From 764ea635f2e980157258d6c3107a4480a6ae12d0 Mon Sep 17 00:00:00 2001 From: Sehong Na Date: Sat, 31 May 2014 13:20:11 +0900 Subject: [PATCH] Initialize Tizen 2.3 --- CMakeLists.txt | 139 + LICENSE | 203 ++ build/Options.cmake | 42 + configuration/CMakeLists.txt | 2 + configuration/access.xsd | 29 + configuration/common.xsd | 79 + configuration/config.xml | 78 + configuration/feature-wrt.properties | 27 + configuration/local.xsd | 8 + configuration/packaging-configuration.xsd | 230 ++ configuration/privilege-wrt.properties | 9 + configuration/signature_schema.xsd | 310 ++ configuration/updates.xsd | 15 + configuration/validate-config.xml.sh | 2 + configuration/widgets.tizen.xsd | 341 ++ configuration/widgets.xsd | 20 + configuration/xml.xsd | 4 + packaging/wrt-installer.spec | 135 + src_mobile/CMakeLists.txt | 185 ++ src_mobile/DESCRIPTION | 2 + src_mobile/commons/installer_log.h | 38 + src_mobile/commons/wrt_common_types.h | 49 + src_mobile/commons/wrt_error.h | 140 + src_mobile/commons/wrt_install_mode.h | 78 + .../configuration_parser/deny_all_parser.cpp | 46 + src_mobile/configuration_parser/deny_all_parser.h | 41 + src_mobile/configuration_parser/element_parser.h | 91 + .../configuration_parser/ignoring_parser.cpp | 55 + src_mobile/configuration_parser/ignoring_parser.h | 43 + src_mobile/configuration_parser/libiriwrapper.cpp | 73 + src_mobile/configuration_parser/libiriwrapper.h | 46 + src_mobile/configuration_parser/parser_runner.cpp | 462 +++ src_mobile/configuration_parser/parser_runner.h | 49 + src_mobile/configuration_parser/root_parser.h | 86 + src_mobile/configuration_parser/widget_parser.cpp | 2949 +++++++++++++++++ src_mobile/configuration_parser/widget_parser.h | 111 + src_mobile/jobs/job.cpp | 104 + src_mobile/jobs/job.h | 62 + src_mobile/jobs/job_base.h | 109 + src_mobile/jobs/job_exception_base.h | 96 + src_mobile/jobs/job_exception_error.h | 87 + src_mobile/jobs/job_types.h | 38 + .../jobs/plugin_install/job_plugin_install.cpp | 96 + .../jobs/plugin_install/job_plugin_install.h | 81 + .../jobs/plugin_install/plugin_install_task.cpp | 389 +++ .../jobs/plugin_install/plugin_install_task.h | 69 + .../jobs/plugin_install/plugin_installer_context.h | 65 + .../jobs/plugin_install/plugin_installer_errors.h | 53 + .../jobs/plugin_install/plugin_installer_struct.h | 45 + .../jobs/plugin_install/plugin_metafile_reader.cpp | 96 + .../jobs/plugin_install/plugin_metafile_reader.h | 54 + src_mobile/jobs/plugin_install/plugin_objects.cpp | 103 + src_mobile/jobs/plugin_install/plugin_objects.h | 58 + .../jobs/widget_install/ace_registration.cpp | 233 ++ src_mobile/jobs/widget_install/ace_registration.h | 35 + src_mobile/jobs/widget_install/directory_api.cpp | 90 + src_mobile/jobs/widget_install/directory_api.h | 32 + .../jobs/widget_install/job_widget_install.cpp | 327 ++ .../jobs/widget_install/job_widget_install.h | 76 + src_mobile/jobs/widget_install/languages.def | 15 + src_mobile/jobs/widget_install/manifest.cpp | 526 +++ src_mobile/jobs/widget_install/manifest.h | 586 ++++ src_mobile/jobs/widget_install/task_ace_check.cpp | 223 ++ src_mobile/jobs/widget_install/task_ace_check.h | 51 + src_mobile/jobs/widget_install/task_certify.cpp | 362 ++ src_mobile/jobs/widget_install/task_certify.h | 68 + .../jobs/widget_install/task_certify_level.cpp | 264 ++ .../jobs/widget_install/task_certify_level.h | 77 + src_mobile/jobs/widget_install/task_commons.cpp | 93 + src_mobile/jobs/widget_install/task_commons.h | 36 + .../jobs/widget_install/task_configuration.cpp | 665 ++++ .../jobs/widget_install/task_configuration.h | 98 + src_mobile/jobs/widget_install/task_database.cpp | 392 +++ src_mobile/jobs/widget_install/task_database.h | 69 + .../jobs/widget_install/task_encrypt_resource.cpp | 349 ++ .../jobs/widget_install/task_encrypt_resource.h | 52 + .../jobs/widget_install/task_file_manipulation.cpp | 318 ++ .../jobs/widget_install/task_file_manipulation.h | 56 + .../jobs/widget_install/task_install_ospsvc.cpp | 114 + .../jobs/widget_install/task_install_ospsvc.h | 56 + .../jobs/widget_install/task_manifest_file.cpp | 1315 ++++++++ .../jobs/widget_install/task_manifest_file.h | 144 + .../jobs/widget_install/task_pkg_info_update.cpp | 289 ++ .../jobs/widget_install/task_pkg_info_update.h | 58 + .../jobs/widget_install/task_prepare_files.cpp | 124 + .../jobs/widget_install/task_prepare_files.h | 51 + .../jobs/widget_install/task_prepare_reinstall.cpp | 255 ++ .../jobs/widget_install/task_prepare_reinstall.h | 65 + .../jobs/widget_install/task_process_config.cpp | 681 ++++ .../jobs/widget_install/task_process_config.h | 108 + src_mobile/jobs/widget_install/task_recovery.cpp | 97 + src_mobile/jobs/widget_install/task_recovery.h | 47 + .../jobs/widget_install/task_remove_backup.cpp | 120 + .../jobs/widget_install/task_remove_backup.h | 50 + src_mobile/jobs/widget_install/task_smack.cpp | 310 ++ src_mobile/jobs/widget_install/task_smack.h | 56 + .../jobs/widget_install/task_update_files.cpp | 140 + src_mobile/jobs/widget_install/task_update_files.h | 56 + .../widget_install/task_user_data_manipulation.cpp | 233 ++ .../widget_install/task_user_data_manipulation.h | 48 + src_mobile/jobs/widget_install/view_mode.h | 38 + .../jobs/widget_install/widget_install_context.h | 108 + .../jobs/widget_install/widget_install_errors.h | 100 + .../jobs/widget_install/widget_installer_struct.h | 80 + src_mobile/jobs/widget_install/widget_security.cpp | 41 + src_mobile/jobs/widget_install/widget_security.h | 113 + src_mobile/jobs/widget_install/widget_unzip.cpp | 375 +++ src_mobile/jobs/widget_install/widget_unzip.h | 58 + .../jobs/widget_install/widget_update_info.cpp | 34 + .../jobs/widget_install/widget_update_info.h | 47 + .../jobs/widget_uninstall/job_widget_uninstall.cpp | 277 ++ .../jobs/widget_uninstall/job_widget_uninstall.h | 78 + src_mobile/jobs/widget_uninstall/task_check.cpp | 132 + src_mobile/jobs/widget_uninstall/task_check.h | 53 + .../jobs/widget_uninstall/task_db_update.cpp | 136 + src_mobile/jobs/widget_uninstall/task_db_update.h | 62 + .../jobs/widget_uninstall/task_delete_pkginfo.cpp | 76 + .../jobs/widget_uninstall/task_delete_pkginfo.h | 51 + .../task_remove_custom_handlers.cpp | 67 + .../widget_uninstall/task_remove_custom_handlers.h | 48 + .../jobs/widget_uninstall/task_remove_files.cpp | 125 + .../jobs/widget_uninstall/task_remove_files.h | 62 + src_mobile/jobs/widget_uninstall/task_smack.cpp | 73 + src_mobile/jobs/widget_uninstall/task_smack.h | 48 + .../widget_uninstall/task_uninstall_ospsvc.cpp | 105 + .../jobs/widget_uninstall/task_uninstall_ospsvc.h | 52 + .../jobs/widget_uninstall/uninstaller_context.h | 70 + .../widget_uninstall/widget_uninstall_errors.h | 52 + .../widget_uninstall/widget_uninstaller_struct.h | 70 + src_mobile/logic/installer_controller.cpp | 82 + src_mobile/logic/installer_controller.h | 140 + src_mobile/logic/installer_logic.cpp | 254 ++ src_mobile/logic/installer_logic.h | 80 + src_mobile/misc/feature_logic.cpp | 118 + src_mobile/misc/feature_logic.h | 105 + src_mobile/misc/libxml_utils.cpp | 51 + src_mobile/misc/libxml_utils.h | 59 + src_mobile/misc/plugin_path.cpp | 53 + src_mobile/misc/plugin_path.h | 62 + src_mobile/misc/wac_widget_id.cpp | 99 + src_mobile/misc/wac_widget_id.h | 42 + src_mobile/misc/widget_install_to_external.cpp | 148 + src_mobile/misc/widget_install_to_external.h | 59 + src_mobile/misc/widget_location.cpp | 277 ++ src_mobile/misc/widget_location.h | 219 ++ src_mobile/pkg-manager/CMakeLists.txt | 77 + src_mobile/pkg-manager/DESCRIPTION | 1 + src_mobile/pkg-manager/backendlib.cpp | 568 ++++ src_mobile/pkg-manager/pkgmgr_signal.cpp | 263 ++ src_mobile/pkg-manager/pkgmgr_signal.h | 72 + src_mobile/pkg-manager/pkgmgr_signal_dummy.h | 76 + src_mobile/pkg-manager/pkgmgr_signal_interface.h | 46 + src_mobile/wrt-installer/CMakeLists.txt | 75 + .../installer_callbacks_translate.cpp | 262 ++ .../wrt-installer/installer_callbacks_translate.h | 81 + src_mobile/wrt-installer/installer_main_thread.cpp | 74 + src_mobile/wrt-installer/installer_main_thread.h | 46 + .../wrt-installer/language_subtag_rst_tree.cpp | 211 ++ .../wrt-installer/language_subtag_rst_tree.h | 47 + src_mobile/wrt-installer/option_parser.cpp | 55 + src_mobile/wrt-installer/option_parser.h | 39 + src_mobile/wrt-installer/plugin_utils.cpp | 150 + src_mobile/wrt-installer/plugin_utils.h | 47 + src_mobile/wrt-installer/wrt-installer.cpp | 1160 +++++++ src_mobile/wrt-installer/wrt-installer.h | 143 + src_mobile/wrt-installer/wrt_installer_api.cpp | 254 ++ src_mobile/wrt-installer/wrt_installer_api.h | 71 + src_mobile/wrt-installer/wrt_type.h | 96 + src_wearable/CMakeLists.txt | 186 ++ src_wearable/DESCRIPTION | 2 + src_wearable/commons/installer_log.h | 69 + src_wearable/commons/wrt_common_types.h | 50 + src_wearable/commons/wrt_error.h | 140 + src_wearable/commons/wrt_install_mode.h | 81 + .../configuration_parser/deny_all_parser.cpp | 46 + .../configuration_parser/deny_all_parser.h | 41 + src_wearable/configuration_parser/element_parser.h | 90 + .../configuration_parser/ignoring_parser.cpp | 56 + .../configuration_parser/ignoring_parser.h | 43 + .../configuration_parser/libiriwrapper.cpp | 73 + src_wearable/configuration_parser/libiriwrapper.h | 46 + .../configuration_parser/parser_runner.cpp | 463 +++ src_wearable/configuration_parser/parser_runner.h | 49 + src_wearable/configuration_parser/root_parser.h | 72 + .../configuration_parser/widget_parser.cpp | 3471 ++++++++++++++++++++ src_wearable/configuration_parser/widget_parser.h | 120 + src_wearable/jobs/job.cpp | 104 + src_wearable/jobs/job.h | 62 + src_wearable/jobs/job_base.h | 109 + src_wearable/jobs/job_exception_base.h | 96 + src_wearable/jobs/job_exception_error.h | 87 + src_wearable/jobs/job_types.h | 38 + .../jobs/plugin_install/job_plugin_install.cpp | 97 + .../jobs/plugin_install/job_plugin_install.h | 81 + .../jobs/plugin_install/plugin_install_task.cpp | 389 +++ .../jobs/plugin_install/plugin_install_task.h | 69 + .../jobs/plugin_install/plugin_installer_context.h | 65 + .../jobs/plugin_install/plugin_installer_errors.h | 53 + .../jobs/plugin_install/plugin_installer_struct.h | 45 + .../jobs/plugin_install/plugin_metafile_reader.cpp | 96 + .../jobs/plugin_install/plugin_metafile_reader.h | 54 + .../jobs/plugin_install/plugin_objects.cpp | 103 + src_wearable/jobs/plugin_install/plugin_objects.h | 58 + .../jobs/widget_install/ace_registration.cpp | 251 ++ .../jobs/widget_install/ace_registration.h | 35 + src_wearable/jobs/widget_install/directory_api.cpp | 90 + src_wearable/jobs/widget_install/directory_api.h | 32 + .../jobs/widget_install/job_widget_install.cpp | 402 +++ .../jobs/widget_install/job_widget_install.h | 80 + src_wearable/jobs/widget_install/languages.def | 15 + src_wearable/jobs/widget_install/manifest.cpp | 575 ++++ src_wearable/jobs/widget_install/manifest.h | 688 ++++ .../jobs/widget_install/task_ace_check.cpp | 245 ++ src_wearable/jobs/widget_install/task_ace_check.h | 51 + src_wearable/jobs/widget_install/task_certify.cpp | 403 +++ src_wearable/jobs/widget_install/task_certify.h | 68 + .../jobs/widget_install/task_certify_level.cpp | 280 ++ .../jobs/widget_install/task_certify_level.h | 78 + src_wearable/jobs/widget_install/task_commons.cpp | 93 + src_wearable/jobs/widget_install/task_commons.h | 36 + .../jobs/widget_install/task_configuration.cpp | 610 ++++ .../jobs/widget_install/task_configuration.h | 90 + src_wearable/jobs/widget_install/task_database.cpp | 430 +++ src_wearable/jobs/widget_install/task_database.h | 70 + .../jobs/widget_install/task_encrypt_resource.cpp | 347 ++ .../jobs/widget_install/task_encrypt_resource.h | 52 + .../jobs/widget_install/task_file_manipulation.cpp | 326 ++ .../jobs/widget_install/task_file_manipulation.h | 56 + .../jobs/widget_install/task_install_ospsvc.cpp | 129 + .../jobs/widget_install/task_install_ospsvc.h | 57 + .../jobs/widget_install/task_manifest_file.cpp | 1614 +++++++++ .../jobs/widget_install/task_manifest_file.h | 167 + .../jobs/widget_install/task_pkg_info_update.cpp | 281 ++ .../jobs/widget_install/task_pkg_info_update.h | 58 + .../jobs/widget_install/task_prepare_files.cpp | 124 + .../jobs/widget_install/task_prepare_files.h | 51 + .../jobs/widget_install/task_prepare_reinstall.cpp | 258 ++ .../jobs/widget_install/task_prepare_reinstall.h | 65 + .../jobs/widget_install/task_process_config.cpp | 695 ++++ .../jobs/widget_install/task_process_config.h | 108 + src_wearable/jobs/widget_install/task_recovery.cpp | 149 + src_wearable/jobs/widget_install/task_recovery.h | 47 + .../jobs/widget_install/task_remove_backup.cpp | 124 + .../jobs/widget_install/task_remove_backup.h | 50 + src_wearable/jobs/widget_install/task_smack.cpp | 320 ++ src_wearable/jobs/widget_install/task_smack.h | 57 + .../jobs/widget_install/task_status_check.cpp | 159 + .../jobs/widget_install/task_status_check.h | 52 + .../jobs/widget_install/task_update_files.cpp | 140 + .../jobs/widget_install/task_update_files.h | 56 + .../widget_install/task_user_data_manipulation.cpp | 274 ++ .../widget_install/task_user_data_manipulation.h | 48 + src_wearable/jobs/widget_install/view_mode.h | 38 + .../jobs/widget_install/widget_install_context.h | 109 + .../jobs/widget_install/widget_install_errors.h | 102 + .../jobs/widget_install/widget_installer_struct.h | 80 + .../jobs/widget_install/widget_security.cpp | 47 + src_wearable/jobs/widget_install/widget_security.h | 121 + src_wearable/jobs/widget_install/widget_unzip.cpp | 383 +++ src_wearable/jobs/widget_install/widget_unzip.h | 58 + .../jobs/widget_install/widget_update_info.cpp | 36 + .../jobs/widget_install/widget_update_info.h | 50 + .../jobs/widget_uninstall/job_widget_uninstall.cpp | 271 ++ .../jobs/widget_uninstall/job_widget_uninstall.h | 80 + src_wearable/jobs/widget_uninstall/task_check.cpp | 138 + src_wearable/jobs/widget_uninstall/task_check.h | 53 + .../jobs/widget_uninstall/task_db_update.cpp | 143 + .../jobs/widget_uninstall/task_db_update.h | 62 + .../jobs/widget_uninstall/task_delete_pkginfo.cpp | 76 + .../jobs/widget_uninstall/task_delete_pkginfo.h | 51 + .../task_remove_custom_handlers.cpp | 68 + .../widget_uninstall/task_remove_custom_handlers.h | 48 + .../jobs/widget_uninstall/task_remove_files.cpp | 125 + .../jobs/widget_uninstall/task_remove_files.h | 62 + src_wearable/jobs/widget_uninstall/task_smack.cpp | 79 + src_wearable/jobs/widget_uninstall/task_smack.h | 48 + .../widget_uninstall/task_uninstall_ospsvc.cpp | 105 + .../jobs/widget_uninstall/task_uninstall_ospsvc.h | 52 + .../jobs/widget_uninstall/uninstaller_context.h | 75 + .../widget_uninstall/widget_uninstall_errors.h | 52 + .../widget_uninstall/widget_uninstaller_struct.h | 70 + src_wearable/logic/installer_controller.cpp | 82 + src_wearable/logic/installer_controller.h | 139 + src_wearable/logic/installer_logic.cpp | 261 ++ src_wearable/logic/installer_logic.h | 74 + src_wearable/misc/feature_logic.cpp | 118 + src_wearable/misc/feature_logic.h | 105 + src_wearable/misc/libxml_utils.cpp | 51 + src_wearable/misc/libxml_utils.h | 59 + src_wearable/misc/plugin_path.cpp | 53 + src_wearable/misc/plugin_path.h | 62 + src_wearable/misc/wac_widget_id.cpp | 99 + src_wearable/misc/wac_widget_id.h | 42 + src_wearable/misc/widget_install_to_external.cpp | 148 + src_wearable/misc/widget_install_to_external.h | 59 + src_wearable/misc/widget_location.cpp | 290 ++ src_wearable/misc/widget_location.h | 225 ++ src_wearable/pkg-manager/CMakeLists.txt | 77 + src_wearable/pkg-manager/DESCRIPTION | 1 + src_wearable/pkg-manager/backendlib.cpp | 570 ++++ src_wearable/pkg-manager/pkgmgr_signal.cpp | 297 ++ src_wearable/pkg-manager/pkgmgr_signal.h | 75 + src_wearable/pkg-manager/pkgmgr_signal_dummy.h | 76 + src_wearable/pkg-manager/pkgmgr_signal_interface.h | 46 + src_wearable/wrt-installer/CMakeLists.txt | 75 + src_wearable/wrt-installer/command_parser.cpp | 140 + src_wearable/wrt-installer/command_parser.h | 61 + .../installer_callbacks_translate.cpp | 262 ++ .../wrt-installer/installer_callbacks_translate.h | 96 + .../wrt-installer/installer_main_thread.cpp | 74 + src_wearable/wrt-installer/installer_main_thread.h | 46 + .../wrt-installer/language_subtag_rst_tree.cpp | 211 ++ .../wrt-installer/language_subtag_rst_tree.h | 47 + src_wearable/wrt-installer/plugin_utils.cpp | 150 + src_wearable/wrt-installer/plugin_utils.h | 47 + src_wearable/wrt-installer/wrt-installer.cpp | 1497 +++++++++ src_wearable/wrt-installer/wrt-installer.h | 132 + src_wearable/wrt-installer/wrt_type.h | 88 + tests/CMakeLists.txt | 20 + tests/general/AceRegistrationTests.cpp | 150 + tests/general/BackgroundPageTests.cpp | 75 + tests/general/CMakeLists.txt | 181 + tests/general/InstallerWrapper.cpp | 150 + tests/general/InstallerWrapper.h | 54 + tests/general/LanguageSubtagRstTreeTests.cpp | 55 + tests/general/ManifestFile.cpp | 106 + tests/general/ManifestFile.h | 54 + tests/general/ManifestTests.cpp | 117 + tests/general/NPluginsInstallTests.cpp | 58 + tests/general/ParserRunnerTests.cpp | 172 + tests/general/ParsingAccountTests.cpp | 69 + tests/general/ParsingAllowNavigationTests.cpp | 194 ++ tests/general/ParsingAppWidgetTests.cpp | 1043 ++++++ tests/general/ParsingCategoryTests.cpp | 139 + tests/general/ParsingContentTests.cpp | 197 ++ tests/general/ParsingCspTests.cpp | 149 + tests/general/ParsingMetadataTests.cpp | 162 + tests/general/ParsingSplashImgTests.cpp | 134 + tests/general/ParsingTizenAppcontrolTests.cpp | 91 + tests/general/ParsingTizenPrivilegeTests.cpp | 261 ++ tests/general/PluginsInstallation.cpp | 75 + tests/general/TaskConfigurationTests.cpp | 645 ++++ tests/general/TestInit.cpp | 43 + tests/general/WidgetInstallManifestTests.cpp | 463 +++ tests/general/WidgetLocationTests.cpp | 186 ++ tests/general/WidgetUpdateTests.cpp | 223 ++ tests/general/configs/AllowNavigationEmpty.xml | 6 + .../configs/AllowNavigationMultipleHosts.xml | 5 + .../AllowNavigationMultipleHostsMultiline.xml | 10 + tests/general/configs/AppWidgetAutoLaunchEmpty.xml | 10 + .../configs/AppWidgetAutoLaunchWrongValue.xml | 10 + tests/general/configs/AppWidgetFull.xml | 12 + tests/general/configs/AppWidgetIdEmpty.xml | 10 + tests/general/configs/AppWidgetIdTooLong.xml | 10 + tests/general/configs/AppWidgetIdTooShort.xml | 10 + tests/general/configs/AppWidgetIdWrongChar.xml | 10 + tests/general/configs/AppWidgetMinimal.xml | 10 + .../configs/AppWidgetMultipleBoxContent.xml | 13 + tests/general/configs/AppWidgetMultipleBoxIcon.xml | 12 + .../general/configs/AppWidgetMultipleBoxLabel.xml | 12 + tests/general/configs/AppWidgetMultiplePrimary.xml | 22 + tests/general/configs/AppWidgetNoBoxContent.xml | 7 + tests/general/configs/AppWidgetNoBoxLabel.xml | 9 + tests/general/configs/AppWidgetNoId.xml | 10 + tests/general/configs/AppWidgetNoPrimary.xml | 10 + tests/general/configs/AppWidgetPrimaryEmpty.xml | 10 + .../general/configs/AppWidgetPrimaryWrongValue.xml | 10 + .../general/configs/AppWidgetUpdatePeriodEmpty.xml | 10 + tests/general/configs/AppWidgetUpdatePeriodLow.xml | 10 + .../configs/AppWidgetUpdatePeriodWrongFormat.xml | 10 + tests/general/configs/BoxContentEmpty.xml | 8 + .../general/configs/BoxContentMouseEventEmpty.xml | 10 + .../configs/BoxContentMouseEventWrongValue.xml | 10 + .../general/configs/BoxContentMultipleBoxSize.xml | 12 + tests/general/configs/BoxContentMultiplePd.xml | 12 + tests/general/configs/BoxContentNoMouseEvent.xml | 10 + tests/general/configs/BoxContentNoSrc.xml | 10 + tests/general/configs/BoxContentNoTouchEfect.xml | 10 + tests/general/configs/BoxContentSrcEmpty.xml | 10 + .../general/configs/BoxContentTouchEfectEmpty.xml | 10 + .../configs/BoxContentTouchEfectWrongValue.xml | 10 + tests/general/configs/BoxIconEmpty.xml | 11 + tests/general/configs/BoxIconSrcEmpty.xml | 11 + tests/general/configs/BoxLabelEmpty.xml | 10 + tests/general/configs/BoxSizeEmpty.xml | 10 + tests/general/configs/BoxSizeNoUserDecoration.xml | 10 + tests/general/configs/BoxSizePreviewEmpty.xml | 10 + .../general/configs/BoxSizeUserDecorationEmpty.xml | 10 + tests/general/configs/ContentEmpty.xml | 5 + tests/general/configs/ContentSrcCorrect.xml | 5 + tests/general/configs/ContentSrcEmpty.xml | 5 + tests/general/configs/CspEmpty.xml | 6 + tests/general/configs/CspReportOnlyEmpty.xml | 6 + tests/general/configs/InstallConfig.xml | 25 + tests/general/configs/MetadataDuplicatedKey.xml | 6 + tests/general/configs/MetadataEmpty.xml | 5 + tests/general/configs/MultipleAllowNavigation.xml | 6 + tests/general/configs/MultipleContentCorrect.xml | 8 + tests/general/configs/MultipleContentIncorrect.xml | 8 + tests/general/configs/MultipleCsp.xml | 6 + tests/general/configs/MultipleCspReportOnly.xml | 6 + tests/general/configs/MultipleMetadata.xml | 7 + tests/general/configs/NoAllowNavigation.xml | 4 + tests/general/configs/NoContent.xml | 4 + tests/general/configs/NoCsp.xml | 4 + tests/general/configs/NoMetadata.xml | 4 + tests/general/configs/PdHeightEmpty.xml | 11 + tests/general/configs/PdHeightExcessive.xml | 11 + tests/general/configs/PdHeightTooLow.xml | 11 + tests/general/configs/PdHeightWrongValue.xml | 11 + tests/general/configs/PdNoHeight.xml | 11 + tests/general/configs/PdNoSrc.xml | 11 + tests/general/configs/PdNoWidth.xml | 11 + tests/general/configs/PdSrcEmpty.xml | 11 + tests/general/configs/PdWidthEmpty.xml | 11 + tests/general/configs/PdWidthNegative.xml | 11 + tests/general/configs/PdWidthWrongValue.xml | 11 + tests/general/configs/PdWidthZero.xml | 11 + tests/general/configs/config_category1.xml | 6 + tests/general/configs/config_category2.xml | 5 + tests/general/configs/config_category3.xml | 5 + tests/general/configs/config_category4.xml | 7 + tests/general/configs/config_privilege1.xml | 12 + tests/general/configs/config_privilege2.xml | 8 + tests/general/configs/config_privilege3.xml | 8 + tests/general/configs/config_privilege4.xml | 8 + tests/general/configs/config_privilege5.xml | 10 + tests/general/configs/config_privilege6.xml | 10 + tests/general/configs/config_splash1.xml | 5 + tests/general/configs/config_splash2.xml | 5 + tests/general/configs/config_splash3.xml | 5 + tests/general/configs/config_splash4.xml | 5 + tests/general/widgets/account.wgt | Bin 0 -> 14064 bytes tests/general/widgets/allowNavigation.wgt | Bin 0 -> 5053 bytes tests/general/widgets/app-control.wgt | Bin 0 -> 5290 bytes tests/general/widgets/appWidgetFull.wgt | Bin 0 -> 14164 bytes tests/general/widgets/appWidgetIncorrect.wgt | Bin 0 -> 5100 bytes tests/general/widgets/appWidgetMinimal.wgt | Bin 0 -> 5098 bytes tests/general/widgets/bg-00-with_bg.wgt | Bin 0 -> 727 bytes tests/general/widgets/bg-01-missing_file.wgt | Bin 0 -> 628 bytes tests/general/widgets/bg-02-without_bg.wgt | Bin 0 -> 611 bytes tests/general/widgets/category.wgt | Bin 0 -> 5031 bytes tests/general/widgets/contentCorrect.wgt | Bin 0 -> 5030 bytes tests/general/widgets/contentIncorrect.wgt | Bin 0 -> 5030 bytes tests/general/widgets/csp.wgt | Bin 0 -> 5062 bytes tests/general/widgets/inst_nplug_1.wgt | Bin 0 -> 1293 bytes tests/general/widgets/inst_nplug_2.wgt | Bin 0 -> 925 bytes tests/general/widgets/inst_nplug_3.wgt | Bin 0 -> 1161 bytes tests/general/widgets/inst_nplug_4.wgt | Bin 0 -> 1045 bytes tests/general/widgets/manifest.wgt | Bin 0 -> 1149 bytes tests/general/widgets/metadata.wgt | Bin 0 -> 5038 bytes tests/general/widgets/privilege.wgt | Bin 0 -> 5122 bytes tests/general/widgets/splash.wgt | Bin 0 -> 5027 bytes tests/general/widgets/widgetFakeConfig.wgt | Bin 0 -> 38773 bytes tests/general/widgets/widgetInDir.tar | Bin 0 -> 266240 bytes tests/general/widgets/widgetUpdateVer100Signed.wgt | Bin 0 -> 23379 bytes .../general/widgets/widgetUpdateVer100Unsigned.wgt | Bin 0 -> 19629 bytes tests/general/widgets/widgetUpdateVer220Signed.wgt | Bin 0 -> 23398 bytes .../widgetUpdateVer220SignedAnotherAuthor.wgt | Bin 0 -> 23430 bytes .../general/widgets/widgetUpdateVer220Unsigned.wgt | Bin 0 -> 19647 bytes .../widgetUpdateVer220UnsignedDifferentId.wgt | Bin 0 -> 19649 bytes uncrustify.cfg | 170 + uncrustify.sh | 1 + wrt-installer.manifest | 14 + wrt-installer.rule | 1 + 465 files changed, 57767 insertions(+) create mode 100755 CMakeLists.txt create mode 100644 LICENSE create mode 100644 build/Options.cmake create mode 100644 configuration/CMakeLists.txt create mode 100644 configuration/access.xsd create mode 100644 configuration/common.xsd create mode 100755 configuration/config.xml create mode 100644 configuration/feature-wrt.properties create mode 100644 configuration/local.xsd create mode 100755 configuration/packaging-configuration.xsd create mode 100644 configuration/privilege-wrt.properties create mode 100644 configuration/signature_schema.xsd create mode 100644 configuration/updates.xsd create mode 100755 configuration/validate-config.xml.sh create mode 100755 configuration/widgets.tizen.xsd create mode 100644 configuration/widgets.xsd create mode 100644 configuration/xml.xsd create mode 100755 packaging/wrt-installer.spec create mode 100644 src_mobile/CMakeLists.txt create mode 100644 src_mobile/DESCRIPTION create mode 100644 src_mobile/commons/installer_log.h create mode 100644 src_mobile/commons/wrt_common_types.h create mode 100644 src_mobile/commons/wrt_error.h create mode 100644 src_mobile/commons/wrt_install_mode.h create mode 100644 src_mobile/configuration_parser/deny_all_parser.cpp create mode 100644 src_mobile/configuration_parser/deny_all_parser.h create mode 100644 src_mobile/configuration_parser/element_parser.h create mode 100644 src_mobile/configuration_parser/ignoring_parser.cpp create mode 100644 src_mobile/configuration_parser/ignoring_parser.h create mode 100644 src_mobile/configuration_parser/libiriwrapper.cpp create mode 100644 src_mobile/configuration_parser/libiriwrapper.h create mode 100644 src_mobile/configuration_parser/parser_runner.cpp create mode 100644 src_mobile/configuration_parser/parser_runner.h create mode 100644 src_mobile/configuration_parser/root_parser.h create mode 100644 src_mobile/configuration_parser/widget_parser.cpp create mode 100644 src_mobile/configuration_parser/widget_parser.h create mode 100644 src_mobile/jobs/job.cpp create mode 100644 src_mobile/jobs/job.h create mode 100644 src_mobile/jobs/job_base.h create mode 100644 src_mobile/jobs/job_exception_base.h create mode 100644 src_mobile/jobs/job_exception_error.h create mode 100644 src_mobile/jobs/job_types.h create mode 100644 src_mobile/jobs/plugin_install/job_plugin_install.cpp create mode 100644 src_mobile/jobs/plugin_install/job_plugin_install.h create mode 100644 src_mobile/jobs/plugin_install/plugin_install_task.cpp create mode 100644 src_mobile/jobs/plugin_install/plugin_install_task.h create mode 100644 src_mobile/jobs/plugin_install/plugin_installer_context.h create mode 100644 src_mobile/jobs/plugin_install/plugin_installer_errors.h create mode 100644 src_mobile/jobs/plugin_install/plugin_installer_struct.h create mode 100644 src_mobile/jobs/plugin_install/plugin_metafile_reader.cpp create mode 100644 src_mobile/jobs/plugin_install/plugin_metafile_reader.h create mode 100644 src_mobile/jobs/plugin_install/plugin_objects.cpp create mode 100644 src_mobile/jobs/plugin_install/plugin_objects.h create mode 100644 src_mobile/jobs/widget_install/ace_registration.cpp create mode 100644 src_mobile/jobs/widget_install/ace_registration.h create mode 100644 src_mobile/jobs/widget_install/directory_api.cpp create mode 100644 src_mobile/jobs/widget_install/directory_api.h create mode 100644 src_mobile/jobs/widget_install/job_widget_install.cpp create mode 100644 src_mobile/jobs/widget_install/job_widget_install.h create mode 100644 src_mobile/jobs/widget_install/languages.def create mode 100644 src_mobile/jobs/widget_install/manifest.cpp create mode 100644 src_mobile/jobs/widget_install/manifest.h create mode 100644 src_mobile/jobs/widget_install/task_ace_check.cpp create mode 100644 src_mobile/jobs/widget_install/task_ace_check.h create mode 100644 src_mobile/jobs/widget_install/task_certify.cpp create mode 100644 src_mobile/jobs/widget_install/task_certify.h create mode 100644 src_mobile/jobs/widget_install/task_certify_level.cpp create mode 100644 src_mobile/jobs/widget_install/task_certify_level.h create mode 100644 src_mobile/jobs/widget_install/task_commons.cpp create mode 100644 src_mobile/jobs/widget_install/task_commons.h create mode 100644 src_mobile/jobs/widget_install/task_configuration.cpp create mode 100644 src_mobile/jobs/widget_install/task_configuration.h create mode 100644 src_mobile/jobs/widget_install/task_database.cpp create mode 100644 src_mobile/jobs/widget_install/task_database.h create mode 100644 src_mobile/jobs/widget_install/task_encrypt_resource.cpp create mode 100644 src_mobile/jobs/widget_install/task_encrypt_resource.h create mode 100644 src_mobile/jobs/widget_install/task_file_manipulation.cpp create mode 100644 src_mobile/jobs/widget_install/task_file_manipulation.h create mode 100644 src_mobile/jobs/widget_install/task_install_ospsvc.cpp create mode 100644 src_mobile/jobs/widget_install/task_install_ospsvc.h create mode 100755 src_mobile/jobs/widget_install/task_manifest_file.cpp create mode 100644 src_mobile/jobs/widget_install/task_manifest_file.h create mode 100644 src_mobile/jobs/widget_install/task_pkg_info_update.cpp create mode 100644 src_mobile/jobs/widget_install/task_pkg_info_update.h create mode 100644 src_mobile/jobs/widget_install/task_prepare_files.cpp create mode 100644 src_mobile/jobs/widget_install/task_prepare_files.h create mode 100644 src_mobile/jobs/widget_install/task_prepare_reinstall.cpp create mode 100644 src_mobile/jobs/widget_install/task_prepare_reinstall.h create mode 100755 src_mobile/jobs/widget_install/task_process_config.cpp create mode 100755 src_mobile/jobs/widget_install/task_process_config.h create mode 100644 src_mobile/jobs/widget_install/task_recovery.cpp create mode 100644 src_mobile/jobs/widget_install/task_recovery.h create mode 100644 src_mobile/jobs/widget_install/task_remove_backup.cpp create mode 100644 src_mobile/jobs/widget_install/task_remove_backup.h create mode 100644 src_mobile/jobs/widget_install/task_smack.cpp create mode 100644 src_mobile/jobs/widget_install/task_smack.h create mode 100644 src_mobile/jobs/widget_install/task_update_files.cpp create mode 100644 src_mobile/jobs/widget_install/task_update_files.h create mode 100644 src_mobile/jobs/widget_install/task_user_data_manipulation.cpp create mode 100644 src_mobile/jobs/widget_install/task_user_data_manipulation.h create mode 100644 src_mobile/jobs/widget_install/view_mode.h create mode 100644 src_mobile/jobs/widget_install/widget_install_context.h create mode 100644 src_mobile/jobs/widget_install/widget_install_errors.h create mode 100644 src_mobile/jobs/widget_install/widget_installer_struct.h create mode 100644 src_mobile/jobs/widget_install/widget_security.cpp create mode 100644 src_mobile/jobs/widget_install/widget_security.h create mode 100644 src_mobile/jobs/widget_install/widget_unzip.cpp create mode 100644 src_mobile/jobs/widget_install/widget_unzip.h create mode 100644 src_mobile/jobs/widget_install/widget_update_info.cpp create mode 100644 src_mobile/jobs/widget_install/widget_update_info.h create mode 100644 src_mobile/jobs/widget_uninstall/job_widget_uninstall.cpp create mode 100644 src_mobile/jobs/widget_uninstall/job_widget_uninstall.h create mode 100644 src_mobile/jobs/widget_uninstall/task_check.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_check.h create mode 100644 src_mobile/jobs/widget_uninstall/task_db_update.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_db_update.h create mode 100644 src_mobile/jobs/widget_uninstall/task_delete_pkginfo.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_delete_pkginfo.h create mode 100644 src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.h create mode 100644 src_mobile/jobs/widget_uninstall/task_remove_files.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_remove_files.h create mode 100644 src_mobile/jobs/widget_uninstall/task_smack.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_smack.h create mode 100644 src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.cpp create mode 100644 src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.h create mode 100644 src_mobile/jobs/widget_uninstall/uninstaller_context.h create mode 100644 src_mobile/jobs/widget_uninstall/widget_uninstall_errors.h create mode 100644 src_mobile/jobs/widget_uninstall/widget_uninstaller_struct.h create mode 100644 src_mobile/logic/installer_controller.cpp create mode 100644 src_mobile/logic/installer_controller.h create mode 100644 src_mobile/logic/installer_logic.cpp create mode 100644 src_mobile/logic/installer_logic.h create mode 100644 src_mobile/misc/feature_logic.cpp create mode 100644 src_mobile/misc/feature_logic.h create mode 100644 src_mobile/misc/libxml_utils.cpp create mode 100644 src_mobile/misc/libxml_utils.h create mode 100644 src_mobile/misc/plugin_path.cpp create mode 100644 src_mobile/misc/plugin_path.h create mode 100644 src_mobile/misc/wac_widget_id.cpp create mode 100644 src_mobile/misc/wac_widget_id.h create mode 100644 src_mobile/misc/widget_install_to_external.cpp create mode 100644 src_mobile/misc/widget_install_to_external.h create mode 100644 src_mobile/misc/widget_location.cpp create mode 100644 src_mobile/misc/widget_location.h create mode 100755 src_mobile/pkg-manager/CMakeLists.txt create mode 100644 src_mobile/pkg-manager/DESCRIPTION create mode 100644 src_mobile/pkg-manager/backendlib.cpp create mode 100644 src_mobile/pkg-manager/pkgmgr_signal.cpp create mode 100644 src_mobile/pkg-manager/pkgmgr_signal.h create mode 100644 src_mobile/pkg-manager/pkgmgr_signal_dummy.h create mode 100644 src_mobile/pkg-manager/pkgmgr_signal_interface.h create mode 100644 src_mobile/wrt-installer/CMakeLists.txt create mode 100644 src_mobile/wrt-installer/installer_callbacks_translate.cpp create mode 100644 src_mobile/wrt-installer/installer_callbacks_translate.h create mode 100644 src_mobile/wrt-installer/installer_main_thread.cpp create mode 100644 src_mobile/wrt-installer/installer_main_thread.h create mode 100644 src_mobile/wrt-installer/language_subtag_rst_tree.cpp create mode 100644 src_mobile/wrt-installer/language_subtag_rst_tree.h create mode 100644 src_mobile/wrt-installer/option_parser.cpp create mode 100644 src_mobile/wrt-installer/option_parser.h create mode 100644 src_mobile/wrt-installer/plugin_utils.cpp create mode 100644 src_mobile/wrt-installer/plugin_utils.h create mode 100644 src_mobile/wrt-installer/wrt-installer.cpp create mode 100644 src_mobile/wrt-installer/wrt-installer.h create mode 100644 src_mobile/wrt-installer/wrt_installer_api.cpp create mode 100644 src_mobile/wrt-installer/wrt_installer_api.h create mode 100644 src_mobile/wrt-installer/wrt_type.h create mode 100755 src_wearable/CMakeLists.txt create mode 100644 src_wearable/DESCRIPTION create mode 100755 src_wearable/commons/installer_log.h create mode 100644 src_wearable/commons/wrt_common_types.h create mode 100644 src_wearable/commons/wrt_error.h create mode 100644 src_wearable/commons/wrt_install_mode.h create mode 100644 src_wearable/configuration_parser/deny_all_parser.cpp create mode 100644 src_wearable/configuration_parser/deny_all_parser.h create mode 100644 src_wearable/configuration_parser/element_parser.h create mode 100644 src_wearable/configuration_parser/ignoring_parser.cpp create mode 100644 src_wearable/configuration_parser/ignoring_parser.h create mode 100644 src_wearable/configuration_parser/libiriwrapper.cpp create mode 100644 src_wearable/configuration_parser/libiriwrapper.h create mode 100644 src_wearable/configuration_parser/parser_runner.cpp create mode 100644 src_wearable/configuration_parser/parser_runner.h create mode 100755 src_wearable/configuration_parser/root_parser.h create mode 100755 src_wearable/configuration_parser/widget_parser.cpp create mode 100755 src_wearable/configuration_parser/widget_parser.h create mode 100644 src_wearable/jobs/job.cpp create mode 100644 src_wearable/jobs/job.h create mode 100644 src_wearable/jobs/job_base.h create mode 100644 src_wearable/jobs/job_exception_base.h create mode 100644 src_wearable/jobs/job_exception_error.h create mode 100644 src_wearable/jobs/job_types.h create mode 100755 src_wearable/jobs/plugin_install/job_plugin_install.cpp create mode 100644 src_wearable/jobs/plugin_install/job_plugin_install.h create mode 100644 src_wearable/jobs/plugin_install/plugin_install_task.cpp create mode 100644 src_wearable/jobs/plugin_install/plugin_install_task.h create mode 100644 src_wearable/jobs/plugin_install/plugin_installer_context.h create mode 100644 src_wearable/jobs/plugin_install/plugin_installer_errors.h create mode 100644 src_wearable/jobs/plugin_install/plugin_installer_struct.h create mode 100644 src_wearable/jobs/plugin_install/plugin_metafile_reader.cpp create mode 100644 src_wearable/jobs/plugin_install/plugin_metafile_reader.h create mode 100644 src_wearable/jobs/plugin_install/plugin_objects.cpp create mode 100644 src_wearable/jobs/plugin_install/plugin_objects.h create mode 100644 src_wearable/jobs/widget_install/ace_registration.cpp create mode 100644 src_wearable/jobs/widget_install/ace_registration.h create mode 100644 src_wearable/jobs/widget_install/directory_api.cpp create mode 100644 src_wearable/jobs/widget_install/directory_api.h create mode 100644 src_wearable/jobs/widget_install/job_widget_install.cpp create mode 100644 src_wearable/jobs/widget_install/job_widget_install.h create mode 100644 src_wearable/jobs/widget_install/languages.def create mode 100755 src_wearable/jobs/widget_install/manifest.cpp create mode 100755 src_wearable/jobs/widget_install/manifest.h create mode 100755 src_wearable/jobs/widget_install/task_ace_check.cpp create mode 100644 src_wearable/jobs/widget_install/task_ace_check.h create mode 100644 src_wearable/jobs/widget_install/task_certify.cpp create mode 100644 src_wearable/jobs/widget_install/task_certify.h create mode 100755 src_wearable/jobs/widget_install/task_certify_level.cpp create mode 100755 src_wearable/jobs/widget_install/task_certify_level.h create mode 100644 src_wearable/jobs/widget_install/task_commons.cpp create mode 100644 src_wearable/jobs/widget_install/task_commons.h create mode 100755 src_wearable/jobs/widget_install/task_configuration.cpp create mode 100644 src_wearable/jobs/widget_install/task_configuration.h create mode 100755 src_wearable/jobs/widget_install/task_database.cpp create mode 100755 src_wearable/jobs/widget_install/task_database.h create mode 100644 src_wearable/jobs/widget_install/task_encrypt_resource.cpp create mode 100644 src_wearable/jobs/widget_install/task_encrypt_resource.h create mode 100644 src_wearable/jobs/widget_install/task_file_manipulation.cpp create mode 100644 src_wearable/jobs/widget_install/task_file_manipulation.h create mode 100644 src_wearable/jobs/widget_install/task_install_ospsvc.cpp create mode 100644 src_wearable/jobs/widget_install/task_install_ospsvc.h create mode 100755 src_wearable/jobs/widget_install/task_manifest_file.cpp create mode 100755 src_wearable/jobs/widget_install/task_manifest_file.h create mode 100644 src_wearable/jobs/widget_install/task_pkg_info_update.cpp create mode 100644 src_wearable/jobs/widget_install/task_pkg_info_update.h create mode 100644 src_wearable/jobs/widget_install/task_prepare_files.cpp create mode 100644 src_wearable/jobs/widget_install/task_prepare_files.h create mode 100644 src_wearable/jobs/widget_install/task_prepare_reinstall.cpp create mode 100644 src_wearable/jobs/widget_install/task_prepare_reinstall.h create mode 100755 src_wearable/jobs/widget_install/task_process_config.cpp create mode 100755 src_wearable/jobs/widget_install/task_process_config.h create mode 100644 src_wearable/jobs/widget_install/task_recovery.cpp create mode 100644 src_wearable/jobs/widget_install/task_recovery.h create mode 100755 src_wearable/jobs/widget_install/task_remove_backup.cpp create mode 100644 src_wearable/jobs/widget_install/task_remove_backup.h create mode 100644 src_wearable/jobs/widget_install/task_smack.cpp create mode 100644 src_wearable/jobs/widget_install/task_smack.h create mode 100644 src_wearable/jobs/widget_install/task_status_check.cpp create mode 100644 src_wearable/jobs/widget_install/task_status_check.h create mode 100644 src_wearable/jobs/widget_install/task_update_files.cpp create mode 100644 src_wearable/jobs/widget_install/task_update_files.h create mode 100644 src_wearable/jobs/widget_install/task_user_data_manipulation.cpp create mode 100644 src_wearable/jobs/widget_install/task_user_data_manipulation.h create mode 100644 src_wearable/jobs/widget_install/view_mode.h create mode 100644 src_wearable/jobs/widget_install/widget_install_context.h create mode 100755 src_wearable/jobs/widget_install/widget_install_errors.h create mode 100644 src_wearable/jobs/widget_install/widget_installer_struct.h create mode 100644 src_wearable/jobs/widget_install/widget_security.cpp create mode 100644 src_wearable/jobs/widget_install/widget_security.h create mode 100644 src_wearable/jobs/widget_install/widget_unzip.cpp create mode 100644 src_wearable/jobs/widget_install/widget_unzip.h create mode 100644 src_wearable/jobs/widget_install/widget_update_info.cpp create mode 100644 src_wearable/jobs/widget_install/widget_update_info.h create mode 100755 src_wearable/jobs/widget_uninstall/job_widget_uninstall.cpp create mode 100755 src_wearable/jobs/widget_uninstall/job_widget_uninstall.h create mode 100755 src_wearable/jobs/widget_uninstall/task_check.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_check.h create mode 100755 src_wearable/jobs/widget_uninstall/task_db_update.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_db_update.h create mode 100644 src_wearable/jobs/widget_uninstall/task_delete_pkginfo.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_delete_pkginfo.h create mode 100755 src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.h create mode 100644 src_wearable/jobs/widget_uninstall/task_remove_files.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_remove_files.h create mode 100644 src_wearable/jobs/widget_uninstall/task_smack.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_smack.h create mode 100644 src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.cpp create mode 100644 src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.h create mode 100755 src_wearable/jobs/widget_uninstall/uninstaller_context.h create mode 100644 src_wearable/jobs/widget_uninstall/widget_uninstall_errors.h create mode 100644 src_wearable/jobs/widget_uninstall/widget_uninstaller_struct.h create mode 100644 src_wearable/logic/installer_controller.cpp create mode 100644 src_wearable/logic/installer_controller.h create mode 100755 src_wearable/logic/installer_logic.cpp create mode 100755 src_wearable/logic/installer_logic.h create mode 100755 src_wearable/misc/feature_logic.cpp create mode 100644 src_wearable/misc/feature_logic.h create mode 100644 src_wearable/misc/libxml_utils.cpp create mode 100644 src_wearable/misc/libxml_utils.h create mode 100644 src_wearable/misc/plugin_path.cpp create mode 100644 src_wearable/misc/plugin_path.h create mode 100644 src_wearable/misc/wac_widget_id.cpp create mode 100644 src_wearable/misc/wac_widget_id.h create mode 100644 src_wearable/misc/widget_install_to_external.cpp create mode 100644 src_wearable/misc/widget_install_to_external.h create mode 100755 src_wearable/misc/widget_location.cpp create mode 100755 src_wearable/misc/widget_location.h create mode 100755 src_wearable/pkg-manager/CMakeLists.txt create mode 100644 src_wearable/pkg-manager/DESCRIPTION create mode 100644 src_wearable/pkg-manager/backendlib.cpp create mode 100644 src_wearable/pkg-manager/pkgmgr_signal.cpp create mode 100644 src_wearable/pkg-manager/pkgmgr_signal.h create mode 100644 src_wearable/pkg-manager/pkgmgr_signal_dummy.h create mode 100644 src_wearable/pkg-manager/pkgmgr_signal_interface.h create mode 100644 src_wearable/wrt-installer/CMakeLists.txt create mode 100644 src_wearable/wrt-installer/command_parser.cpp create mode 100755 src_wearable/wrt-installer/command_parser.h create mode 100644 src_wearable/wrt-installer/installer_callbacks_translate.cpp create mode 100644 src_wearable/wrt-installer/installer_callbacks_translate.h create mode 100644 src_wearable/wrt-installer/installer_main_thread.cpp create mode 100644 src_wearable/wrt-installer/installer_main_thread.h create mode 100644 src_wearable/wrt-installer/language_subtag_rst_tree.cpp create mode 100644 src_wearable/wrt-installer/language_subtag_rst_tree.h create mode 100644 src_wearable/wrt-installer/plugin_utils.cpp create mode 100644 src_wearable/wrt-installer/plugin_utils.h create mode 100755 src_wearable/wrt-installer/wrt-installer.cpp create mode 100755 src_wearable/wrt-installer/wrt-installer.h create mode 100644 src_wearable/wrt-installer/wrt_type.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/general/AceRegistrationTests.cpp create mode 100644 tests/general/BackgroundPageTests.cpp create mode 100644 tests/general/CMakeLists.txt create mode 100644 tests/general/InstallerWrapper.cpp create mode 100644 tests/general/InstallerWrapper.h create mode 100644 tests/general/LanguageSubtagRstTreeTests.cpp create mode 100644 tests/general/ManifestFile.cpp create mode 100644 tests/general/ManifestFile.h create mode 100644 tests/general/ManifestTests.cpp create mode 100644 tests/general/NPluginsInstallTests.cpp create mode 100644 tests/general/ParserRunnerTests.cpp create mode 100644 tests/general/ParsingAccountTests.cpp create mode 100644 tests/general/ParsingAllowNavigationTests.cpp create mode 100644 tests/general/ParsingAppWidgetTests.cpp create mode 100644 tests/general/ParsingCategoryTests.cpp create mode 100644 tests/general/ParsingContentTests.cpp create mode 100644 tests/general/ParsingCspTests.cpp create mode 100644 tests/general/ParsingMetadataTests.cpp create mode 100644 tests/general/ParsingSplashImgTests.cpp create mode 100644 tests/general/ParsingTizenAppcontrolTests.cpp create mode 100644 tests/general/ParsingTizenPrivilegeTests.cpp create mode 100644 tests/general/PluginsInstallation.cpp create mode 100644 tests/general/TaskConfigurationTests.cpp create mode 100644 tests/general/TestInit.cpp create mode 100644 tests/general/WidgetInstallManifestTests.cpp create mode 100644 tests/general/WidgetLocationTests.cpp create mode 100644 tests/general/WidgetUpdateTests.cpp create mode 100644 tests/general/configs/AllowNavigationEmpty.xml create mode 100644 tests/general/configs/AllowNavigationMultipleHosts.xml create mode 100644 tests/general/configs/AllowNavigationMultipleHostsMultiline.xml create mode 100644 tests/general/configs/AppWidgetAutoLaunchEmpty.xml create mode 100644 tests/general/configs/AppWidgetAutoLaunchWrongValue.xml create mode 100644 tests/general/configs/AppWidgetFull.xml create mode 100644 tests/general/configs/AppWidgetIdEmpty.xml create mode 100644 tests/general/configs/AppWidgetIdTooLong.xml create mode 100644 tests/general/configs/AppWidgetIdTooShort.xml create mode 100644 tests/general/configs/AppWidgetIdWrongChar.xml create mode 100644 tests/general/configs/AppWidgetMinimal.xml create mode 100644 tests/general/configs/AppWidgetMultipleBoxContent.xml create mode 100644 tests/general/configs/AppWidgetMultipleBoxIcon.xml create mode 100644 tests/general/configs/AppWidgetMultipleBoxLabel.xml create mode 100644 tests/general/configs/AppWidgetMultiplePrimary.xml create mode 100644 tests/general/configs/AppWidgetNoBoxContent.xml create mode 100644 tests/general/configs/AppWidgetNoBoxLabel.xml create mode 100644 tests/general/configs/AppWidgetNoId.xml create mode 100644 tests/general/configs/AppWidgetNoPrimary.xml create mode 100644 tests/general/configs/AppWidgetPrimaryEmpty.xml create mode 100644 tests/general/configs/AppWidgetPrimaryWrongValue.xml create mode 100644 tests/general/configs/AppWidgetUpdatePeriodEmpty.xml create mode 100644 tests/general/configs/AppWidgetUpdatePeriodLow.xml create mode 100644 tests/general/configs/AppWidgetUpdatePeriodWrongFormat.xml create mode 100644 tests/general/configs/BoxContentEmpty.xml create mode 100644 tests/general/configs/BoxContentMouseEventEmpty.xml create mode 100644 tests/general/configs/BoxContentMouseEventWrongValue.xml create mode 100644 tests/general/configs/BoxContentMultipleBoxSize.xml create mode 100644 tests/general/configs/BoxContentMultiplePd.xml create mode 100644 tests/general/configs/BoxContentNoMouseEvent.xml create mode 100644 tests/general/configs/BoxContentNoSrc.xml create mode 100644 tests/general/configs/BoxContentNoTouchEfect.xml create mode 100644 tests/general/configs/BoxContentSrcEmpty.xml create mode 100644 tests/general/configs/BoxContentTouchEfectEmpty.xml create mode 100644 tests/general/configs/BoxContentTouchEfectWrongValue.xml create mode 100644 tests/general/configs/BoxIconEmpty.xml create mode 100644 tests/general/configs/BoxIconSrcEmpty.xml create mode 100644 tests/general/configs/BoxLabelEmpty.xml create mode 100644 tests/general/configs/BoxSizeEmpty.xml create mode 100644 tests/general/configs/BoxSizeNoUserDecoration.xml create mode 100644 tests/general/configs/BoxSizePreviewEmpty.xml create mode 100644 tests/general/configs/BoxSizeUserDecorationEmpty.xml create mode 100644 tests/general/configs/ContentEmpty.xml create mode 100644 tests/general/configs/ContentSrcCorrect.xml create mode 100644 tests/general/configs/ContentSrcEmpty.xml create mode 100644 tests/general/configs/CspEmpty.xml create mode 100644 tests/general/configs/CspReportOnlyEmpty.xml create mode 100644 tests/general/configs/InstallConfig.xml create mode 100644 tests/general/configs/MetadataDuplicatedKey.xml create mode 100644 tests/general/configs/MetadataEmpty.xml create mode 100644 tests/general/configs/MultipleAllowNavigation.xml create mode 100644 tests/general/configs/MultipleContentCorrect.xml create mode 100644 tests/general/configs/MultipleContentIncorrect.xml create mode 100644 tests/general/configs/MultipleCsp.xml create mode 100644 tests/general/configs/MultipleCspReportOnly.xml create mode 100644 tests/general/configs/MultipleMetadata.xml create mode 100644 tests/general/configs/NoAllowNavigation.xml create mode 100644 tests/general/configs/NoContent.xml create mode 100644 tests/general/configs/NoCsp.xml create mode 100644 tests/general/configs/NoMetadata.xml create mode 100644 tests/general/configs/PdHeightEmpty.xml create mode 100644 tests/general/configs/PdHeightExcessive.xml create mode 100644 tests/general/configs/PdHeightTooLow.xml create mode 100644 tests/general/configs/PdHeightWrongValue.xml create mode 100644 tests/general/configs/PdNoHeight.xml create mode 100644 tests/general/configs/PdNoSrc.xml create mode 100644 tests/general/configs/PdNoWidth.xml create mode 100644 tests/general/configs/PdSrcEmpty.xml create mode 100644 tests/general/configs/PdWidthEmpty.xml create mode 100644 tests/general/configs/PdWidthNegative.xml create mode 100644 tests/general/configs/PdWidthWrongValue.xml create mode 100644 tests/general/configs/PdWidthZero.xml create mode 100644 tests/general/configs/config_category1.xml create mode 100644 tests/general/configs/config_category2.xml create mode 100644 tests/general/configs/config_category3.xml create mode 100644 tests/general/configs/config_category4.xml create mode 100644 tests/general/configs/config_privilege1.xml create mode 100644 tests/general/configs/config_privilege2.xml create mode 100644 tests/general/configs/config_privilege3.xml create mode 100644 tests/general/configs/config_privilege4.xml create mode 100644 tests/general/configs/config_privilege5.xml create mode 100644 tests/general/configs/config_privilege6.xml create mode 100644 tests/general/configs/config_splash1.xml create mode 100644 tests/general/configs/config_splash2.xml create mode 100644 tests/general/configs/config_splash3.xml create mode 100644 tests/general/configs/config_splash4.xml create mode 100644 tests/general/widgets/account.wgt create mode 100644 tests/general/widgets/allowNavigation.wgt create mode 100644 tests/general/widgets/app-control.wgt create mode 100644 tests/general/widgets/appWidgetFull.wgt create mode 100644 tests/general/widgets/appWidgetIncorrect.wgt create mode 100644 tests/general/widgets/appWidgetMinimal.wgt create mode 100644 tests/general/widgets/bg-00-with_bg.wgt create mode 100644 tests/general/widgets/bg-01-missing_file.wgt create mode 100644 tests/general/widgets/bg-02-without_bg.wgt create mode 100644 tests/general/widgets/category.wgt create mode 100644 tests/general/widgets/contentCorrect.wgt create mode 100644 tests/general/widgets/contentIncorrect.wgt create mode 100644 tests/general/widgets/csp.wgt create mode 100644 tests/general/widgets/inst_nplug_1.wgt create mode 100644 tests/general/widgets/inst_nplug_2.wgt create mode 100644 tests/general/widgets/inst_nplug_3.wgt create mode 100644 tests/general/widgets/inst_nplug_4.wgt create mode 100644 tests/general/widgets/manifest.wgt create mode 100644 tests/general/widgets/metadata.wgt create mode 100644 tests/general/widgets/privilege.wgt create mode 100644 tests/general/widgets/splash.wgt create mode 100644 tests/general/widgets/widgetFakeConfig.wgt create mode 100644 tests/general/widgets/widgetInDir.tar create mode 100644 tests/general/widgets/widgetUpdateVer100Signed.wgt create mode 100644 tests/general/widgets/widgetUpdateVer100Unsigned.wgt create mode 100644 tests/general/widgets/widgetUpdateVer220Signed.wgt create mode 100644 tests/general/widgets/widgetUpdateVer220SignedAnotherAuthor.wgt create mode 100644 tests/general/widgets/widgetUpdateVer220Unsigned.wgt create mode 100644 tests/general/widgets/widgetUpdateVer220UnsignedDifferentId.wgt create mode 100644 uncrustify.cfg create mode 100755 uncrustify.sh create mode 100644 wrt-installer.manifest create mode 100644 wrt-installer.rule diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..350168c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,139 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @brief +# + +############################# Check minimum CMake version ##################### + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT("wrt-installer") + +############################# cmake packages ################################## + +INCLUDE(FindPkgConfig) + +############################# build type ###################################### + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release") +ENDIF(NOT CMAKE_BUILD_TYPE) + +############################# compilation defines ############################# + +OPTION(DPL_LOG "DPL logs status" ON) +OPTION(WITH_TESTS "Build tests" OFF) +OPTION(MULTIPROCESS_SERVICE_SUPPORT "Process per service" OFF) +OPTION(MULTIPROCESS_SERVICE_SUPPORT_INLINE "Process per service - inline mode support" OFF) +OPTION(SCHEMA_VALIDATION_SUPPORT "Support for XML schema validation" OFF) +OPTION(NFC_APP_CONTROL_EXCEPTION "Enable exception handling for NFC app-control" ON) + +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/build") +INCLUDE(Options) + +############################# compiler flags ################################## + +SET(CMAKE_C_FLAGS_PROFILING "-O2") +SET(CMAKE_CXX_FLAGS_PROFILING "-O2 -std=c++0x") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") +SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -std=c++0x -g") +SET(CMAKE_C_FLAGS_RELEASE "-O2 -g") +SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -std=c++0x -g -fvisibility-inlines-hidden") +SET(CMAKE_CXX_FLAGS_CCOV "-O0 -std=c++0x -g --coverage") + +IF(DPL_LOG AND NOT CMAKE_BUILD_TYPE MATCHES "profiling") + MESSAGE(STATUS "Logging enabled for DPL") + ADD_DEFINITIONS("-DDPL_LOGS_ENABLED") +ELSE(DPL_LOG AND NOT CMAKE_BUILD_TYPE MATCHES "profiling") + MESSAGE(STATUS "Logging disabled for DPL") +ENDIF(DPL_LOG AND NOT CMAKE_BUILD_TYPE MATCHES "profiling") +MESSAGE(STATUS "WITH_TESTS: " ${WITH_TESTS}) +IF(MULTIPROCESS_SERVICE_SUPPORT) + ADD_DEFINITIONS("-DMULTIPROCESS_SERVICE_SUPPORT") + IF (MULTIPROCESS_SERVICE_SUPPORT_INLINE) + ADD_DEFINITIONS("-DMULTIPROCESS_SERVICE_SUPPORT_INLINE") + ENDIF(MULTIPROCESS_SERVICE_SUPPORT_INLINE) +ENDIF(MULTIPROCESS_SERVICE_SUPPORT) +IF(SCHEMA_VALIDATION_SUPPORT) + MESSAGE(STATUS "XML Schema validation of installed app enabled") + ADD_DEFINITIONS("-DSCHEMA_VALIDATION_ENABLED") +ENDIF(SCHEMA_VALIDATION_SUPPORT) +IF(NFC_APP_CONTROL_EXCEPTION) + MESSAGE(STATUS "Exception handling for NFC app-control is enabled") + ADD_DEFINITIONS("-DNFC_EXCEPTION_HANDLING_FOR_TIZEN_2_2_ONLY") +ENDIF(NFC_APP_CONTROL_EXCEPTION) + +IF(DEVICE_PROFILE STREQUAL "wearable") + MESSAGE(STATUS "Device Profile: wearable") + ADD_DEFINITIONS("-DDEVICE_PROFILE_WEARABLE") +ELSE(DEVICE_PROFILE STREQUAL "wearable") + MESSAGE(STATUS "Device Profile: mobile") + ADD_DEFINITIONS("-DDEVICE_PROFILE_MOBILE") + ADD_DEFINITIONS("-DWRT_SMACK_ENABLED") # enable smack + + OPTION(CSP_SUPPORT "Support for csp policy" ON) + OPTION(ALLOW_NAVIGATION_SUPPORT "Support for allow-navigation" ON) + + IF(CSP_SUPPORT) + ADD_DEFINITIONS("-DCSP_ENABLED") + ENDIF(CSP_SUPPORT) + IF(ALLOW_NAVIGATION_SUPPORT) + ADD_DEFINITIONS("-DALLOW_NAVIGATION_ENABLED") + ENDIF(ALLOW_NAVIGATION_SUPPORT) +ENDIF(DEVICE_PROFILE STREQUAL "wearable") + +# If supported for the target machine, emit position-independent code,suitable +# for dynamic linking and avoiding any limit on the size of the global offset +# table. This option makes a difference on the m68k, PowerPC and SPARC. +# (BJ: our ARM too?) +ADD_DEFINITIONS("-fPIC") + +# Set the default ELF image symbol visibility to hidden - all symbols will be +# marked with this unless overridden within the code. +#ADD_DEFINITIONS("-fvisibility=hidden") + +# Set compiler warning flags +#ADD_DEFINITIONS("-Werror") # Make all warnings into errors. +ADD_DEFINITIONS("-Wall") # Generate all warnings +ADD_DEFINITIONS("-Wextra") # Generate even more extra warnings +ADD_DEFINITIONS("-Wno-variadic-macros") # Inhibit variadic macros warnings (needed for ORM) +ADD_DEFINITIONS("-Wno-deprecated") # No warnings about deprecated features +ADD_DEFINITIONS("-std=c++0x") # accept C++11x standard +ADD_DEFINITIONS("-DWRT_INSTALLER_LOG") # enable installer log +ADD_DEFINITIONS("-DDBOX_ENABLED") # enable dynamic box features +ADD_DEFINITIONS("-DIME_ENABLED") # enable Ime features +ADD_DEFINITIONS("-DSERVICE_ENABLED") # enable Service features + +############################# Targets names ################################### + +SET(TARGET_INSTALLER_STATIC "wrt-installer_static") +SET(TARGET_INSTALLER "wrt-installer") +SET(TARGET_BACKEND_LIB "wgt") + +############################# subdirectories ################################## +ADD_SUBDIRECTORY(src) +ADD_SUBDIRECTORY(configuration) + +IF(WITH_TESTS) + ADD_SUBDIRECTORY(tests) +ENDIF(WITH_TESTS) + +########################## smack rule install ################################# +INSTALL(FILES ${CMAKE_SOURCE_DIR}/wrt-installer.rule + DESTINATION /etc/smack/accesses2.d/ +) + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..247c97d --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/build/Options.cmake b/build/Options.cmake new file mode 100644 index 0000000..05087e0 --- /dev/null +++ b/build/Options.cmake @@ -0,0 +1,42 @@ +# Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file Options.cmake +# @author Tae-Jeong Lee (taejeong.lee@samsung.com) +# + +# Macro declaration +# - WRT_OPTION() : Wrapper omitting description argument from OPTION() command. +MACRO(WRT_OPTION feature_name on_off) + OPTION(${feature_name} "" ${on_off}) +ENDMACRO(WRT_OPTION) + +# Use Feature +# Use a particular optional platform service or third-party library +# +# Description : +# +# Author : () - +# WRT_OPTION(_USE_ ON/OFF) + + +# Enable Feature +# Turn on a specific feature of WRT +# +# Description : +# +# Author : () - +# WRT_OPTION(_ENABLE_ ON/OFF) + diff --git a/configuration/CMakeLists.txt b/configuration/CMakeLists.txt new file mode 100644 index 0000000..380f512 --- /dev/null +++ b/configuration/CMakeLists.txt @@ -0,0 +1,2 @@ +FILE(GLOB XSD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.xsd") +INSTALL(FILES ${XSD_FILES} DESTINATION /usr/etc/wrt-installer/) diff --git a/configuration/access.xsd b/configuration/access.xsd new file mode 100644 index 0000000..f2404e4 --- /dev/null +++ b/configuration/access.xsd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/configuration/common.xsd b/configuration/common.xsd new file mode 100644 index 0000000..2b9a0d2 --- /dev/null +++ b/configuration/common.xsd @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/config.xml b/configuration/config.xml new file mode 100755 index 0000000..492e3df --- /dev/null +++ b/configuration/config.xml @@ -0,0 +1,78 @@ + + + hello + hello + + + + + + + + + + + + + + + + + + + + 6135122a-a428-40d2-8feb-a75f462c202c + + en-us + ko-kr + + + + + + + WebService + + WebService + + + + "img-src http://test.com 'unsafe-inline'; script-src 'unsafe-inline';" + + + + + + + + + + + + + + + + + diff --git a/configuration/feature-wrt.properties b/configuration/feature-wrt.properties new file mode 100644 index 0000000..5b7fb11 --- /dev/null +++ b/configuration/feature-wrt.properties @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/local.xsd b/configuration/local.xsd new file mode 100644 index 0000000..4aecb7d --- /dev/null +++ b/configuration/local.xsd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/configuration/packaging-configuration.xsd b/configuration/packaging-configuration.xsd new file mode 100755 index 0000000..0990be2 --- /dev/null +++ b/configuration/packaging-configuration.xsd @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/privilege-wrt.properties b/configuration/privilege-wrt.properties new file mode 100644 index 0000000..dc36b93 --- /dev/null +++ b/configuration/privilege-wrt.properties @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/configuration/signature_schema.xsd b/configuration/signature_schema.xsd new file mode 100644 index 0000000..1ca3bd3 --- /dev/null +++ b/configuration/signature_schema.xsd @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/updates.xsd b/configuration/updates.xsd new file mode 100644 index 0000000..6e6d88a --- /dev/null +++ b/configuration/updates.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/configuration/validate-config.xml.sh b/configuration/validate-config.xml.sh new file mode 100755 index 0000000..a242910 --- /dev/null +++ b/configuration/validate-config.xml.sh @@ -0,0 +1,2 @@ +#!/bin/sh +xmllint --schema widgets.xsd config.xml diff --git a/configuration/widgets.tizen.xsd b/configuration/widgets.tizen.xsd new file mode 100755 index 0000000..e849e30 --- /dev/null +++ b/configuration/widgets.tizen.xsd @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/widgets.xsd b/configuration/widgets.xsd new file mode 100644 index 0000000..8de2f3d --- /dev/null +++ b/configuration/widgets.xsd @@ -0,0 +1,20 @@ + + + + + + diff --git a/configuration/xml.xsd b/configuration/xml.xsd new file mode 100644 index 0000000..715330a --- /dev/null +++ b/configuration/xml.xsd @@ -0,0 +1,4 @@ + + + + diff --git a/packaging/wrt-installer.spec b/packaging/wrt-installer.spec new file mode 100755 index 0000000..11630ec --- /dev/null +++ b/packaging/wrt-installer.spec @@ -0,0 +1,135 @@ +#git:framework/web/wrt-installer +Name: wrt-installer +Summary: Installer for tizen Webruntime +Version: 0.1.175_w11 +Release: 1 +Group: Development/Libraries +License: Apache License, Version 2.0 +URL: N/A +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: edje-tools +BuildRequires: pkgconfig(appsvc) +BuildRequires: pkgconfig(libxml-2.0) +BuildRequires: pkgconfig(openssl) +BuildRequires: pkgconfig(dpl-efl) +BuildRequires: pkgconfig(cert-svc-vcore) +BuildRequires: pkgconfig(dpl-event-efl) +BuildRequires: pkgconfig(dpl-utils-efl) +BuildRequires: pkgconfig(dpl-wrt-dao-ro) +BuildRequires: pkgconfig(dpl-wrt-dao-rw) +BuildRequires: pkgconfig(wrt-commons-i18n-dao-ro) +BuildRequires: pkgconfig(wrt-commons-widget-interface-dao) +BuildRequires: pkgconfig(security-install) +BuildRequires: pkgconfig(ecore-x) +BuildRequires: pkgconfig(xmlsec1) +BuildRequires: pkgconfig(libidn) +BuildRequires: pkgconfig(libiri) +BuildRequires: pkgconfig(libpcrecpp) +BuildRequires: pkgconfig(pkgmgr-installer) +BuildRequires: pkgconfig(pkgmgr-parser) +BuildRequires: pkgconfig(pkgmgr-types) +BuildRequires: pkgconfig(pkgmgr-info) +BuildRequires: pkgconfig(pkgmgr) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(cert-svc) +BuildRequires: pkgconfig(utilX) +BuildRequires: pkgconfig(wrt-plugins-types) +BuildRequires: pkgconfig(tapi) +BuildRequires: pkgconfig(shortcut) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(capi-system-power) +BuildRequires: pkgconfig(app2sd) +BuildRequires: pkgconfig(web-provider) +BuildRequires: pkgconfig(libprivilege-control) +BuildRequires: pkgconfig(libsmack) +BuildRequires: libss-client-devel +BuildRequires: boost-devel +Requires: libss-client +Requires: xmlsec1 +Requires: wrt-plugins-tizen + +%description +Description: Wrt Installer for Tizen apps and Wac apps + +%prep +%setup -q + +%define with_tests 0 +%if "%{WITH_TESTS}" == "ON" || "%{WITH_TESTS}" == "Y" || "%{WITH_TESTS}" == "YES" || "%{WITH_TESTS}" == "TRUE" || "%{WITH_TESTS}" == "1" + %define with_tests 1 +%endif + +%build +%if 0%{?sec_build_binary_debug_enable} +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" +%endif + +%if "%{_repository}" == "wearable" + %define device_profile wearable +%else + %define device_profile mobile +%endif + +export LDFLAGS+="-Wl,--rpath=/usr/lib -Wl,--hash-style=both -Wl,--as-needed" +LDFLAGS="$LDFLAGS" + +%if "%{_repository}" == "wearable" + ln -sf src_wearable src +%else + ln -sf src_mobile src +%endif + +cmake . -DCMAKE_INSTALL_PREFIX=/usr \ + -DDPL_LOG=ON \ + -DLB_SUPPORT=ON \ + -DCMAKE_BUILD_TYPE=%{?build_type:%build_type} \ + -DDEVICE_PROFILE=%{?device_profile:%device_profile} \ + %{?WITH_TESTS:-DWITH_TESTS=%WITH_TESTS} +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp LICENSE %{buildroot}/usr/share/license/%{name} +%make_install + +%clean +rm -rf %{buildroot} + +%post +/sbin/ldconfig + +#symlink for package manager +%define pkg_manager_backend_path "/usr/etc/package-manager/backend" +ln -sf /usr/bin/wrt-installer %{pkg_manager_backend_path}/wgt +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/Wgt +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/wGt +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/wgT +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/WGt +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/wGT +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/WgT +ln -sf %{pkg_manager_backend_path}/wgt %{pkg_manager_backend_path}/WGT + +#for booting recovery +mkdir -p /opt/share/widget/temp_info + +# for downloadable Application icons path +mkdir -p /opt/share/icons/default/small + +%postun -p /sbin/ldconfig + +%files +%manifest wrt-installer.manifest +%attr(755,root,root) %{_bindir}/wrt-installer +/usr/etc/package-manager/backendlib/libwgt.so +%attr(644,root,root) /usr/etc/wrt-installer/*.xsd +%{_datadir}/license/%{name} +%if %{with_tests} + %attr(755,root,root) %{_bindir}/wrt-installer-tests-* + /opt/share/widget/tests/installer/widgets/* + /opt/share/widget/tests/installer/configs/* +%endif +%{_sysconfdir}/smack/accesses2.d/wrt-installer.rule diff --git a/src_mobile/CMakeLists.txt b/src_mobile/CMakeLists.txt new file mode 100644 index 0000000..bd52dcf --- /dev/null +++ b/src_mobile/CMakeLists.txt @@ -0,0 +1,185 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Lukasz Wrzosek (l.wrzosek@samsung.com) +# @version 1.0 +# + +SET(TARGET_INSTALLER "wrt-installer") + +OPTION(LB_SUPPORT "lb support" OFF) + +SET(INSTALLER_SRC_DIR + ${PROJECT_SOURCE_DIR}/src_mobile + ) + +SET(INSTALLER_CONFIG_PARSER + ${INSTALLER_SRC_DIR}/configuration_parser + ) + +SET(INSTALLER_JOBS + ${INSTALLER_SRC_DIR}/jobs + ) + +SET(INSTALLER_INCLUDES + ${INSTALLER_SRC_DIR} + ${INSTALLER_SRC_DIR}/logic + ${INSTALLER_SRC_DIR}/jobs + ${INSTALLER_SRC_DIR}/jobs/plugin_install + ${INSTALLER_SRC_DIR}/jobs/widget_install + ${INSTALLER_SRC_DIR}/jobs/widget_uninstall + ${INSTALLER_SRC_DIR}/misc + ${INSTALLER_SRC_DIR}/configuration_parser + ${INSTALLER_SRC_DIR}/wrt-installer + ${INSTALLER_SRC_DIR}/commons + ${INSTALLER_SRC_DIR}/pkg-manager +) + +SET(INSTALLER_SOURCES + ${INSTALLER_CONFIG_PARSER}/widget_parser.cpp + ${INSTALLER_CONFIG_PARSER}/parser_runner.cpp + ${INSTALLER_CONFIG_PARSER}/ignoring_parser.cpp + ${INSTALLER_CONFIG_PARSER}/deny_all_parser.cpp + ${INSTALLER_CONFIG_PARSER}/libiriwrapper.cpp + ${INSTALLER_JOBS}/job.cpp + ${INSTALLER_JOBS}/plugin_install/job_plugin_install.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_install_task.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_objects.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_metafile_reader.cpp + ${INSTALLER_JOBS}/widget_install/job_widget_install.cpp + ${INSTALLER_JOBS}/widget_install/manifest.cpp + ${INSTALLER_JOBS}/widget_install/task_commons.cpp + ${INSTALLER_JOBS}/widget_install/task_process_config.cpp + ${INSTALLER_JOBS}/widget_install/task_database.cpp + ${INSTALLER_JOBS}/widget_install/task_configuration.cpp + ${INSTALLER_JOBS}/widget_install/ace_registration.cpp + ${INSTALLER_JOBS}/widget_install/task_file_manipulation.cpp + ${INSTALLER_JOBS}/widget_install/task_user_data_manipulation.cpp + ${INSTALLER_JOBS}/widget_install/task_smack.cpp + ${INSTALLER_JOBS}/widget_install/task_ace_check.cpp + ${INSTALLER_JOBS}/widget_install/task_manifest_file.cpp + ${INSTALLER_JOBS}/widget_install/task_certify.cpp + ${INSTALLER_JOBS}/widget_install/task_certify_level.cpp + ${INSTALLER_JOBS}/widget_install/task_prepare_files.cpp + ${INSTALLER_JOBS}/widget_install/task_recovery.cpp + ${INSTALLER_JOBS}/widget_install/task_install_ospsvc.cpp + ${INSTALLER_JOBS}/widget_install/task_update_files.cpp + ${INSTALLER_JOBS}/widget_install/task_remove_backup.cpp + ${INSTALLER_JOBS}/widget_install/task_encrypt_resource.cpp + ${INSTALLER_JOBS}/widget_install/task_prepare_reinstall.cpp + ${INSTALLER_JOBS}/widget_install/task_pkg_info_update.cpp + ${INSTALLER_JOBS}/widget_install/widget_security.cpp + ${INSTALLER_JOBS}/widget_install/widget_update_info.cpp + ${INSTALLER_JOBS}/widget_install/directory_api.cpp + ${INSTALLER_JOBS}/widget_install/widget_unzip.cpp + ${INSTALLER_JOBS}/widget_uninstall/job_widget_uninstall.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_check.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_remove_files.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_remove_custom_handlers.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_db_update.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_smack.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_uninstall_ospsvc.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_delete_pkginfo.cpp + ${INSTALLER_SRC_DIR}/logic/installer_logic.cpp + ${INSTALLER_SRC_DIR}/logic/installer_controller.cpp + ${INSTALLER_SRC_DIR}/misc/wac_widget_id.cpp + ${INSTALLER_SRC_DIR}/misc/feature_logic.cpp + ${INSTALLER_SRC_DIR}/misc/libxml_utils.cpp + ${INSTALLER_SRC_DIR}/misc/widget_location.cpp + ${INSTALLER_SRC_DIR}/misc/widget_install_to_external.cpp + ${INSTALLER_SRC_DIR}/misc/plugin_path.cpp + ${INSTALLER_SRC_DIR}/pkg-manager/pkgmgr_signal.cpp + ) + +IF(LB_SUPPORT) + SET(INSTALLER_SOURCES + ${INSTALLER_SOURCES} + ) + MESSAGE(STATUS "adding definition -DLB_SUPPORT") + ADD_DEFINITIONS("-DLB_SUPPORT") +ENDIF(LB_SUPPORT) + +MESSAGE(STATUS "add -DSEP_INSTALLER") +ADD_DEFINITIONS("-DSEP_INSTALLER") + + +PKG_CHECK_MODULES(INSTALLER_STATIC_DEP + dpl-efl + dpl-event-efl + dpl-utils-efl + dpl-wrt-dao-ro + dpl-wrt-dao-rw + wrt-commons-custom-handler-dao-rw + wrt-commons-security-origin-dao + wrt-commons-widget-interface-dao + wrt-plugins-types + pkgmgr-installer + pkgmgr-parser + pkgmgr-info + web-provider + libsmack + REQUIRED +) + +PKG_CHECK_MODULES(SYS_INSTALLER_STATIC_DEP + appsvc + libxml-2.0 + openssl + cert-svc-vcore + security-install + ecore-x + xmlsec1 + libidn + libiri + libpcrecpp + ail + elementary + tapi + shortcut + capi-appfw-app-manager + app2sd + libprivilege-control + REQUIRED +) + +INCLUDE_DIRECTORIES( SYSTEM ${SYS_INSTALLER_STATIC_DEP_INCLUDE_DIRS}) + +INCLUDE_DIRECTORIES( + ${INSTALLER_DEP_INCLUDES} + ${INSTALLER_INCLUDES} + ${INSTALLER_STATIC_DEP_INCLUDE_DIRS} + ${OSP_APPFW_INCLUDES} + ) + +ADD_LIBRARY(${TARGET_INSTALLER_STATIC} STATIC + ${INSTALLER_SOURCES} + ) + +ADD_DEFINITIONS(${INSTALLER_STATIC_DEP_CFLAGS}) +ADD_DEFINITIONS(${INSTALLER_STATIC_DEP_CFLAGS_OTHERS}) +ADD_DEFINITIONS(${SYS_INSTALLER_STATIC_DEP_CFLAGS}) +ADD_DEFINITIONS(${SYS_INSTALLER_STATIC_DEP_CFLAGS_OTHERS}) + +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER_STATIC} + ${INSTALLER_STATIC_DEP_LIBRARIES} "-ldl" + ${SYS_INSTALLER_STATIC_DEP_LIBRARIES} "-ldl" + ) + +#for encryption +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER_STATIC} "-lss-client" ) + +ADD_SUBDIRECTORY(pkg-manager) +ADD_SUBDIRECTORY(wrt-installer) diff --git a/src_mobile/DESCRIPTION b/src_mobile/DESCRIPTION new file mode 100644 index 0000000..0e8c571 --- /dev/null +++ b/src_mobile/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +Widget (un)installer, plugin (un)installer diff --git a/src_mobile/commons/installer_log.h b/src_mobile/commons/installer_log.h new file mode 100644 index 0000000..6ba4137 --- /dev/null +++ b/src_mobile/commons/installer_log.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_log.h + * @author Sungsu Kim(sung-su.kim@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef INSTALLER_LOG_H +#define INSTALLER_LOG_H + +#include + +#ifdef WRT_INSTALLER_LOG + +#undef COLOR_WARNING +#define COLOR_WARNING "\e[0m" +#undef COLOR_TAG +#define COLOR_TAG "\e[0m" + +#endif + +#endif // INSTALLER_LOG_H + diff --git a/src_mobile/commons/wrt_common_types.h b/src_mobile/commons/wrt_common_types.h new file mode 100644 index 0000000..d9ea0f0 --- /dev/null +++ b/src_mobile/commons/wrt_common_types.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * plugin_common_types.h + * + * Author: Soyoung Kim(sy037.kim@samsung.com) + */ + +#ifndef PLUGIN_COMMON_TYPES_H +#define PLUGIN_COMMON_TYPES_H + +#include +#include + +/** + * Widget version is optional + */ +typedef DPL::Optional OptionalWidgetVersion; + +/* Define db type */ +typedef WrtDB::DbWidgetFeature WidgetFeature; +typedef WrtDB::DbWidgetFeatureSet WidgetFeatureSet; + +typedef WrtDB::DbWidgetSize WidgetSize; + +typedef WrtDB::DbPluginHandle PluginHandle; + +enum InstallLocationType +{ + INSTALL_LOCATION_TYPE_UNKNOWN = 0, + INSTALL_LOCATION_TYPE_INTERNAL_ONLY, + INSTALL_LOCATION_TYPE_AUTO, + INSTALL_LOCATION_TYPE_PREFER_EXTERNAL, +}; + +#endif /* PLUGIN_COMMON_TYPES_H */ diff --git a/src_mobile/commons/wrt_error.h b/src_mobile/commons/wrt_error.h new file mode 100644 index 0000000..ae4f2de --- /dev/null +++ b/src_mobile/commons/wrt_error.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of the error codes of Widget. + * + * @file wrt_error.h + * @author MaQuan (jason.ma@samsung.com) + * @version 0.7 + * @brief This file contains the declaration of the error codes of Widget. + */ + +#ifndef _WRT_ERROR_H_ +#define _WRT_ERROR_H_ + +#ifndef WRT_ERROR_MASKL8 +#define WRT_ERROR_MASKL8 0xFF +#endif + +#ifndef WRT_SET_IDENT +#define WRT_SET_IDENT(X) (X & WRT_ERROR_MASKL8) +#endif + +#ifndef WRT_ERROR_SET +#define WRT_ERROR_SET(X) ((X & WRT_ERROR_MASKL8) << 8) +#endif + +#define WRT_MID_ERRCODE 0x10000 + WRT_SET_IDENT(5) + +/*typedef */ enum +{ + WRT_GENERAL_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(0), + WRT_CONFIG_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(1), + WRT_DOMAIN_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(2), + WRT_JS_EXT_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(3), + WRT_WM_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(4), + WRT_PLUGIN_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(5), + //_ACE support + WRT_SAI_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(6) +}; + +/** + * WRT error code description + * + * @ WRT_SUCCESS + * There is no error with WRT operations. + * + * @ WRT_ERR_UNKNOW + * An unknow error happened to WRT. + * + * @ WRT_ERR_INVALID_ARG + * Invalid arguments are passed into WRT functions. + * + * @ WRT_ERR_OUT_MEMORY + * No memory space available for WRT. + * + * @ WRT_ERR_NO_DISK_SPACE + * There is no disk space for widget applications. + * + * + * + * + */ +enum WrtError +{ + /* General errors */ + WRT_SUCCESS = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x01), + WRT_ERR_UNKNOWN = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x02), + WRT_ERR_INVALID_ARG = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x03), + WRT_ERR_OUT_OF_MEMORY = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x04), + WRT_ERR_NO_DISK_SPACE = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x05), + + /* Configuration */ + WRT_CONF_ERR_GCONF_FAILURE = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x01), + WRT_CONF_ERR_OBJ_MISSING = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x02), + WRT_CONF_ERR_OBJ_EXIST = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x03), + WRT_CONF_ERR_START_FILE_MISSING = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x04), + WRT_CONF_ERR_EMDB_FAILURE = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x05), + WRT_CONF_ERR_EMDB_NO_RECORD = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x06), + + /* Domain */ + WRT_DOMAIN_ERR_CREATE_JS_RT = WRT_DOMAIN_ERRCODE + WRT_ERROR_SET(0x01), + WRT_DOMAIN_ERR_MSG_QUEUE = WRT_DOMAIN_ERRCODE + WRT_ERROR_SET(0x02), + + /* Widget manager*/ + WRT_WM_ERR_NOT_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x01), + WRT_WM_ERR_HIGH_VER_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x02), + WRT_WM_ERR_LOW_VER_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x03), + WRT_WM_ERR_INVALID_ARCHIVE = WRT_WM_ERRCODE + WRT_ERROR_SET(0x04), + WRT_WM_ERR_INVALID_CERTIFICATION = WRT_WM_ERRCODE + WRT_ERROR_SET(0x05), + WRT_WM_ERR_NULL_CERTIFICATION = WRT_WM_ERRCODE + WRT_ERROR_SET(0x06), + WRT_WM_ERR_INSTALLATION_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x07), + WRT_WM_ERR_ALREADY_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x08), + WRT_WM_ERR_INSTALL_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x09), + WRT_WM_ERR_DELETE_BY_SERVER = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0a), + WRT_WM_ERR_DEINSTALLATION_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0b), + WRT_WM_ERR_INCORRECT_UPDATE_INFO = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0c), + WRT_WM_ERR_UNREG_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0d), + WRT_WM_ERR_REMOVE_FILES_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0e), + WRT_WM_ERR_ALREADY_LATEST = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0f), + WRT_WM_ERR_UPDATE_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x10), + WRT_WM_ERR_INVALID_APP_ID = WRT_WM_ERRCODE + WRT_ERROR_SET(0x11), + + /* Access Control Manager */ + WRT_SAI_ERR_INIT_ACE_FAILED = WRT_SAI_ERRCODE + WRT_ERROR_SET(0x01) +}; + +namespace CommonError { +enum Type +{ + WrtSuccess, ///< Success + + HandleNotFound, ///< Widget handle was not found + AlreadyRunning, ///< Widget is already running + AlreadyStopped, ///< Widget is already stopped + InvalidLanguage, ///< Widget is invalid in current locales + StillAuthorizing, ///< Widget is still autorizing and has not yet + // finished it + EarlyKilled, ///< Widget was early killed during launch + AccessDenied, ///< Access denied from ACE + CertificateRevoked, ///< Some certificate was revoked. + /// Widget is not allowed to run. + + Unknown ///< Temporary error. Try to not use this. +}; +} +#endif /* _WRT_ERROR_H_ */ + diff --git a/src_mobile/commons/wrt_install_mode.h b/src_mobile/commons/wrt_install_mode.h new file mode 100644 index 0000000..714d3c3 --- /dev/null +++ b/src_mobile/commons/wrt_install_mode.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_install_mode.h + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief Definition file of widget install mode class + */ + +#ifndef WRT_INSTALL_MODE_H +#define WRT_INSTALL_MODE_H + +class InstallMode +{ + public: + enum class Command + { + INSTALL, + REINSTALL + }; + enum class Location + { + INTERNAL, + EXTERNAL + }; + enum class RootPath + { + RW, + RO + }; + enum class ExtensionType + { + WGT, + DIR + }; + enum class InstallTime + { + NORMAL, + CSC, + PRELOAD, + }; + + InstallMode(Command cmd = Command::INSTALL, + Location lo = Location::INTERNAL, + RootPath root = RootPath::RW, + ExtensionType extensionType = ExtensionType::WGT, + InstallTime time = InstallTime::NORMAL) : + command(cmd), + location(lo), + rootPath(root), + extension(extensionType), + installTime(time), + removable(true) + {}; + + Command command; + Location location; + RootPath rootPath; + ExtensionType extension; + InstallTime installTime; + bool removable; +}; + +#endif // WRT_INSTALL_MODE_H + diff --git a/src_mobile/configuration_parser/deny_all_parser.cpp b/src_mobile/configuration_parser/deny_all_parser.cpp new file mode 100644 index 0000000..e807d5e --- /dev/null +++ b/src_mobile/configuration_parser/deny_all_parser.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "deny_all_parser.h" +#include + +DenyAllParser::DenyAllParser() : ElementParser() +{} + +ElementParserPtr DenyAllParser::Create() +{ + ThrowMsg(Exception::ParseError, "There must not be any subelement"); +} + +ElementParser::ActionFunc DenyAllParser::GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any subelement"); +} + +void DenyAllParser::Accept(const Element& /*element*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any element"); +} + +void DenyAllParser::Accept(const XmlAttribute& /*attribute*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any attribute"); +} + +void DenyAllParser::Accept(const Text& /*text*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any text element"); +} diff --git a/src_mobile/configuration_parser/deny_all_parser.h b/src_mobile/configuration_parser/deny_all_parser.h new file mode 100644 index 0000000..d9dfe56 --- /dev/null +++ b/src_mobile/configuration_parser/deny_all_parser.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file deny_all_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef DENY_ALL_PARSER_H +#define DENY_ALL_PARSER_H + +#include "element_parser.h" + +struct DenyAllParser : public ElementParser +{ + static ElementParserPtr Create(); + virtual void Accept(const Element& /*element*/); + virtual void Accept(const XmlAttribute& /*attribute*/); + virtual void Accept(const Text& /*text*/); + virtual void Verify() + {} + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + + DenyAllParser(); +}; + +#endif // DENY_ALL_PARSER_H diff --git a/src_mobile/configuration_parser/element_parser.h b/src_mobile/configuration_parser/element_parser.h new file mode 100644 index 0000000..a44f94a --- /dev/null +++ b/src_mobile/configuration_parser/element_parser.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file element_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef ELEMENT_PARSER_H_ +#define ELEMENT_PARSER_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct XmlAttribute +{ + DPL::String prefix; + DPL::String name; + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +struct Element +{ + DPL::String name; + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +struct Text +{ + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +class ElementParser; + +typedef std::shared_ptr ElementParserPtr; + +class ElementParser : public std::enable_shared_from_this +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ParseError) + }; + typedef DPL::FastDelegate0 ActionFunc; + typedef std::map FuncMap; + + virtual void Accept(const Element&) = 0; + virtual void Accept(const XmlAttribute&) = 0; + virtual void Accept(const Text&) = 0; + virtual void Verify() = 0; + virtual ActionFunc GetElementParser(const DPL::String &ns, + const DPL::String &name) = 0; + virtual ~ElementParser() + {} + + protected: + ElementParser() + {} +}; + +#endif // ELEMENT_PARSER_H_ diff --git a/src_mobile/configuration_parser/ignoring_parser.cpp b/src_mobile/configuration_parser/ignoring_parser.cpp new file mode 100644 index 0000000..29520d5 --- /dev/null +++ b/src_mobile/configuration_parser/ignoring_parser.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ignoring_parser.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include "ignoring_parser.h" + +#include + +IgnoringParser::IgnoringParser() : ElementParser() +{} + +ElementParserPtr IgnoringParser::Create() +{ + return ElementParserPtr(new IgnoringParser()); +} + +ElementParserPtr IgnoringParser::Reuse() +{ + return shared_from_this(); +} + +ElementParser::ActionFunc IgnoringParser::GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) +{ + return DPL::MakeDelegate(this, &IgnoringParser::Reuse); +} + +void IgnoringParser::Accept(const Element& /*element*/) +{} + +void IgnoringParser::Accept(const Text& /*text*/) +{} + +void IgnoringParser::Accept(const XmlAttribute& /*attribute*/) +{} + +void IgnoringParser::Verify() +{} diff --git a/src_mobile/configuration_parser/ignoring_parser.h b/src_mobile/configuration_parser/ignoring_parser.h new file mode 100644 index 0000000..b14f1ad --- /dev/null +++ b/src_mobile/configuration_parser/ignoring_parser.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ignoring_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef IGNORING_PARSER_H_ +#define IGNORING_PARSER_H_ + +#include "element_parser.h" + +struct IgnoringParser : public ElementParser +{ + static ElementParserPtr Create(); + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + virtual void Accept(const Element&); + virtual void Accept(const Text&); + virtual void Accept(const XmlAttribute&); + virtual void Verify(); + + IgnoringParser(); + + private: + ElementParserPtr Reuse(); +}; + +#endif // IGNORING_PARSER_H_ diff --git a/src_mobile/configuration_parser/libiriwrapper.cpp b/src_mobile/configuration_parser/libiriwrapper.cpp new file mode 100644 index 0000000..6d8de2a --- /dev/null +++ b/src_mobile/configuration_parser/libiriwrapper.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libiriwrapper.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com + * @version 0.1 + * @brief Libiri parser wrapper + */ +#include +#include +#include "libiriwrapper.h" + +//TODO: Design and implement new IRI manager class + +namespace LibIri { +Wrapper::Wrapper(const char* aIri) : m_Iri(iri_parse(aIri)) +{} +Wrapper::~Wrapper() +{ + iri_destroy(m_Iri); +} +//! \brief Returns true if iri is valid +bool Wrapper::Validate() +{ + return + m_Iri != NULL && + m_Iri->scheme != NULL && ( + m_Iri->display != NULL || + m_Iri->user != NULL || + m_Iri->auth != NULL || + m_Iri->password != NULL || + m_Iri->host != NULL || + m_Iri->path != NULL || + m_Iri->query != NULL || + m_Iri->anchor != NULL || + m_Iri->qparams != NULL || + m_Iri->schemelist != NULL); +} + +std::ostream & operator<<(std::ostream& a_stream, + const Wrapper& a_wrapper) +{ + iri_t& iri = *a_wrapper.m_Iri; +#define PRINT_FIELD(field) "] " #field " [" << (iri.field ? iri.field : "null") + a_stream << + " display [" << (iri.display ? iri.display : "null") << + PRINT_FIELD(scheme) << + PRINT_FIELD(user) << + PRINT_FIELD(auth) << + PRINT_FIELD(password) << + PRINT_FIELD(host) << + "] port [" << (iri.port ? iri.port : -1) << + PRINT_FIELD(path) << + PRINT_FIELD(query) << + PRINT_FIELD(anchor) << + "]"; + return a_stream; +#undef PRINT_FIELD +} +} //namespace LibIri diff --git a/src_mobile/configuration_parser/libiriwrapper.h b/src_mobile/configuration_parser/libiriwrapper.h new file mode 100644 index 0000000..b0b3e86 --- /dev/null +++ b/src_mobile/configuration_parser/libiriwrapper.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libiriwrapper.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com + * @version 0.1 + * @brief Libiri parser wrapper + */ + +#ifndef _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ +#define _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ + +#include +#include + +//TODO: Design and implement new IRI manager class +// +namespace LibIri { +struct Wrapper +{ + Wrapper(const char* aIri); + ~Wrapper(); + iri_t *m_Iri; + //! \brief Returns true if iri is valid + bool Validate(); +}; + +std::ostream & operator<<(std::ostream& a_stream, + const Wrapper& a_wrapper); +} //namespace LibIri + +#endif // _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ + diff --git a/src_mobile/configuration_parser/parser_runner.cpp b/src_mobile/configuration_parser/parser_runner.cpp new file mode 100644 index 0000000..725cd77 --- /dev/null +++ b/src_mobile/configuration_parser/parser_runner.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file parser_runner.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include "parser_runner.h" +#include "root_parser.h" + +#include "stdio.h" + +#include +#include +#include +#include +#include + +#include + +class ParserRunner::Impl +{ + static void logErrorLibxml2(void *, const char *msg, ...) + { + char buffer[300]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, 300, msg, args); + va_end(args); + _E("%s", buffer); + } + + static void logWarningLibxml2(void *, const char *msg, ...) + { + char buffer[300]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, 300, msg, args); + va_end(args); + _W("%s", buffer); + } + + public: + bool Validate(const std::string& filename, const std::string& schema) + { + int ret = -1; + xmlSchemaParserCtxtPtr ctx; + xmlSchemaValidCtxtPtr vctx; + xmlSchemaPtr xschema; + ctx = xmlSchemaNewParserCtxt(schema.c_str()); + if (ctx == NULL) { + _E("xmlSchemaNewParserCtxt() Failed"); + return false; + } + xschema = xmlSchemaParse(ctx); + if (xschema == NULL) { + _E("xmlSchemaParse() Failed"); + return false; + } + vctx = xmlSchemaNewValidCtxt(xschema); + if (vctx == NULL) { + _E("xmlSchemaNewValidCtxt() Failed"); + return false; + } + xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&logErrorLibxml2, (xmlSchemaValidityWarningFunc) &logWarningLibxml2, NULL); + ret = xmlSchemaValidateFile(vctx, filename.c_str(), 0); + if (ret == -1) { + _E("xmlSchemaValidateFile() failed"); + return false; + } else if (ret == 0) { + _E("Config is Valid"); + return true; + } else { + _E("Config Validation Failed with error code %d", ret); + return false; + } + return true; + } + + void Parse(const std::string& filename, + const ElementParserPtr& root) + { + DPL::FileInput input(filename); + Parse(&input, root); + } + + void Parse (DPL::AbstractInput *input, + const ElementParserPtr& root) + { + Try + { + m_reader = xmlReaderForIO(&IoRead, + &IoClose, + input, + NULL, + NULL, + XML_PARSE_NOENT); + + xmlTextReaderSetErrorHandler(m_reader, + &xmlTextReaderErrorHandler, + this); + xmlTextReaderSetStructuredErrorHandler( + m_reader, + &xmlTextReaderStructuredErrorHandler, + this); + SetCurrentElementParser(root); + + while (xmlTextReaderRead(m_reader) == 1) { + switch (xmlTextReaderNodeType(m_reader)) { + case XML_READER_TYPE_END_ELEMENT: + VerifyAndRemoveCurrentElementParser(); + break; + + case XML_READER_TYPE_ELEMENT: + { + // Elements without closing tag don't receive + // XML_READER_TYPE_END_ELEMENT event. + if (IsNoClosingTagElementLeft()) { + VerifyAndRemoveCurrentElementParser(); + } + + DPL::String elementName = GetNameWithoutNamespace(); + DPL::String nameSpace = GetNamespace(); + ElementParserPtr parser = GetCurrentElementParser(); + parser = parser->GetElementParser(nameSpace, + elementName) (); + Assert(!!parser); + SetCurrentElementParser(parser); + ParseNodeElement(parser); + break; + } + case XML_READER_TYPE_TEXT: + case XML_READER_TYPE_CDATA: + { + ParseNodeText(GetCurrentElementParser()); + break; + } + default: + _W("Ignoring Node of Type: %d", xmlTextReaderNodeType(m_reader)); + break; + } + + if (m_parsingError) { + _E("Parsing error occured: %ls", m_errorMsg.c_str()); + ThrowMsg(ElementParser::Exception::ParseError, m_errorMsg); + } + } + + if (m_parsingError) { + _E("Parsing error occured: %ls", m_errorMsg.c_str()); + ThrowMsg(ElementParser::Exception::ParseError, m_errorMsg); + } + + while (!m_stack.empty()) { + VerifyAndRemoveCurrentElementParser(); + } + } + Catch(ElementParser::Exception::Base) + { + CleanupParserRunner(); + _E("%s", _rethrown_exception.DumpToString().c_str()); + ReThrow(ElementParser::Exception::ParseError); + } + CleanupParserRunner(); + } + + Impl() : + m_reader(NULL), + m_parsingError(false) + {} + + ~Impl() + { + CleanupParserRunner(); + } + + private: + typedef std::stack ElementStack; + + private: + static void xmlTextReaderErrorHandler(void* arg, + const char* msg, + xmlParserSeverities /* severity */, + xmlTextReaderLocatorPtr /* locator */) + { + ParserRunner::Impl* impl = static_cast(arg); + impl->ErrorHandler(DPL::FromASCIIString(msg)); + } + + static void xmlTextReaderStructuredErrorHandler(void* arg, + xmlErrorPtr error) + { + ParserRunner::Impl* impl = static_cast(arg); + impl->StructuredErrorHandler(error); + } + + static int XMLCALL IoRead(void *context, + char *buffer, + int len) + { + DPL::AbstractInput *input = static_cast(context); + DPL::BinaryQueueAutoPtr data = input->Read(static_cast(len)); + if (!data.get()) { + return -1; + } + data->Flatten(buffer, data->Size()); + return static_cast(data->Size()); + } + + static int XMLCALL IoClose(void */* context */) + { + // NOOP + return 0; + } + + private: + void SetCurrentElementParser(const ElementParserPtr& elementParser) + { + Assert(elementParser); + + m_stack.push(elementParser); + } + + const ElementParserPtr& GetCurrentElementParser() const + { + Assert(!m_stack.empty()); + + return m_stack.top(); + } + + void VerifyAndRemoveCurrentElementParser() + { + Assert(!m_stack.empty()); + + m_stack.top()->Verify(); + m_stack.pop(); + } + + bool IsNoClosingTagElementLeft() const + { + Assert(m_reader); + + int depth = xmlTextReaderDepth(m_reader); + return (static_cast(m_stack.size()) - 2 == depth); + } + + void ParseNodeElement(const ElementParserPtr& parser) + { + Assert(m_reader); + + Element element; + element.value = GetValue(); + element.lang = GetLanguageTag(); + element.ns = GetNamespace(); + + _D("value: %ls, lang: %ls, ns: %ls)", + element.value.c_str(), element.lang.c_str(), element.ns.c_str()); + + parser->Accept(element); + ParseNodeElementAttributes(parser); + } + + void ParseNodeElementAttributes(const ElementParserPtr& parser) + { + Assert(m_reader); + int count = xmlTextReaderAttributeCount(m_reader); + for (int i = 0; i < count; ++i) { + xmlTextReaderMoveToAttributeNo(m_reader, i); + + XmlAttribute attribute; + attribute.ns = GetAttributeNamespace(); + attribute.prefix = GetNamePrefix(); + attribute.name = GetNameWithoutNamespace(); + attribute.value = GetValue(); + attribute.lang = GetLanguageTag(); + _D("Attribute name: %ls, value: %ls, prefix: %ls, namespace: %ls, lang: %ls", + attribute.name.c_str(), attribute.value.c_str(), attribute.prefix.c_str(), + attribute.ns.c_str(), attribute.lang.c_str()); + parser->Accept(attribute); + } + } + + void ParseNodeText(const ElementParserPtr& parser) + { + Text text; + text.value = GetValue(); + text.lang = GetLanguageTag(); + parser->Accept(text); + } + + DPL::String GetValue() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstValue(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetAttributeValue(int pos) const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderGetAttributeNo(m_reader, pos); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + xmlFree(const_cast(value)); + + return ret_value; + } + + DPL::String GetAttributeNamespace() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderLookupNamespace(m_reader, NULL); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + xmlFree(const_cast(value)); + + return ret_value; + } + + DPL::String GetName() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstName(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNamePrefix() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderPrefix(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNameWithoutNamespace() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderLocalName(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNamespace() const + { + DPL::String ret_value; + + const xmlChar* value = xmlTextReaderConstNamespaceUri(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetLanguageTag() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstXmlLang(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + void ErrorHandler(const DPL::String& msg) + { + _E("LibXML: %ls", msg.c_str()); + m_parsingError = true; + m_errorMsg = m_errorMsg + DPL::FromASCIIString("\n"); + m_errorMsg = m_errorMsg + msg; + } + + void StructuredErrorHandler(xmlErrorPtr error) + { + _E("LibXML: %s", error->message); + m_parsingError = true; + m_errorMsg = m_errorMsg + DPL::FromASCIIString("\n"); + m_errorMsg = m_errorMsg + DPL::FromUTF8String(error->message); + } + + void CleanupParserRunner() + { + while (!m_stack.empty()) { + m_stack.pop(); + } + if (m_reader) { + xmlFreeTextReader(m_reader); + } + m_reader = NULL; + } + + private: + xmlTextReaderPtr m_reader; + ElementStack m_stack; + bool m_parsingError; + DPL::String m_errorMsg; +}; + +ParserRunner::ParserRunner() : + m_impl(new ParserRunner::Impl()) +{} + +bool ParserRunner::Validate(const std::string& filename, const std::string& schema) +{ + return m_impl->Validate(filename, schema); +} + +void ParserRunner::Parse(const std::string& filename, + ElementParserPtr root) +{ + m_impl->Parse(filename, root); +} + +void ParserRunner::Parse(DPL::AbstractInput *input, + ElementParserPtr root) +{ + m_impl->Parse(input, root); +} + +ParserRunner::~ParserRunner() +{ + delete m_impl; +} diff --git a/src_mobile/configuration_parser/parser_runner.h b/src_mobile/configuration_parser/parser_runner.h new file mode 100644 index 0000000..c5c0714 --- /dev/null +++ b/src_mobile/configuration_parser/parser_runner.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file parser_runner.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef PARSER_RUNNER_H_ +#define PARSER_RUNNER_H_ + +#include +#include +#include +#include "element_parser.h" + +class ParserRunner : private DPL::Noncopyable +{ + public: + bool Validate(const std::string& filename, const std::string& schema); + + void Parse(const std::string& filename, + ElementParserPtr root); + void Parse(DPL::AbstractInput *input, + ElementParserPtr root); + + ParserRunner(); + ~ParserRunner(); + + private: + class Impl; + Impl* m_impl; +}; + +#endif // PARSER_RUNNER_H_ + diff --git a/src_mobile/configuration_parser/root_parser.h b/src_mobile/configuration_parser/root_parser.h new file mode 100644 index 0000000..65bae87 --- /dev/null +++ b/src_mobile/configuration_parser/root_parser.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file root_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ +#define _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ + +#include +#include "element_parser.h" + +template +class RootParser : public ElementParser +{ + public: + typedef typename ta_Parser::Data Data; + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == m_tag) { + return DPL::MakeDelegate(this, + &RootParser::OnWidgetElement); + } else { + ThrowMsg(Exception::ParseError, + name << " != " << m_tag); + } + } + + RootParser(Data data, + const DPL::String& tag) : + m_data(data), + m_tag(tag) + {} + + virtual ~RootParser() + {} + + virtual void Accept(const Element& /*element*/) + { + _D("element"); + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + { + _D("attribute"); + } + + virtual void Accept(const Text& /*text*/) + { + _D("text"); + } + + virtual void Verify() + { + _D(""); + } + + private: + + ElementParserPtr OnWidgetElement() + { + typedef ta_Parser Parser; + return ElementParserPtr(new Parser(this->m_data)); + } + + Data m_data; + const DPL::String& m_tag; +}; + +#endif // _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ diff --git a/src_mobile/configuration_parser/widget_parser.cpp b/src_mobile/configuration_parser/widget_parser.cpp new file mode 100644 index 0000000..e00b84e --- /dev/null +++ b/src_mobile/configuration_parser/widget_parser.cpp @@ -0,0 +1,2949 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file have been implemented in compliance with W3C WARP SPEC. + * but there are some patent issue between W3C WARP SPEC and APPLE. + * so if you want to use this file, refer to the README file in root directory + */ +/** + * @file widget_parser.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace WrtDB; + +namespace Unicode { +static const DPL::String UTF_LRE = L"\x0202a"; +static const DPL::String UTF_LRO = L"\x0202d"; +static const DPL::String UTF_RLE = L"\x0202b"; +static const DPL::String UTF_RLO = L"\x0202e"; +static const DPL::String UTF_PDF = L"\x0202c"; + +Direction ParseDirAttribute(const XmlAttribute& attribute) +{ + Assert(L"dir" == attribute.name); + if (L"ltr" == attribute.value) { + return LRE; + } else if (L"rtl" == attribute.value) { + return RLE; + } else if (L"lro" == attribute.value) { + return LRO; + } else if (L"rlo" == attribute.value) { + return RLO; + } else { + _W("dir attribute has wrong value: %ls ", attribute.value.c_str()); + return EMPTY; + } +} + +void UpdateTextWithDirectionMark(Direction direction, + DPL::String* text) +{ + Assert(text); + switch (direction) { + case RLO: + *text = UTF_RLO + *text + UTF_PDF; + break; + case RLE: + *text = UTF_RLE + *text + UTF_PDF; + break; + case LRE: + *text = UTF_LRE + *text + UTF_PDF; + break; + case LRO: + *text = UTF_LRO + *text + UTF_PDF; + break; + case EMPTY: + break; + default: + Assert(false); + break; + } +} +} // namespace Unicode + +class InnerElementsParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &InnerElementsParser::Other); + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& text) + { + if (m_text.IsNull()) { + m_text = text; + } else { + m_text->value += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + if (!m_text.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &m_text->value); + m_parentParser->Accept(*m_text); + } + } + + InnerElementsParser(ElementParserPtr parent) : + m_parentParser(parent), + m_textDirection(Unicode::EMPTY) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + DPL::Optional m_text; + ElementParserPtr m_parentParser; + Unicode::Direction m_textDirection; +}; + +class NameParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &NameParser::Other); + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_name = L""; + } + + virtual void Accept(const Text& text) + { + if (m_name.IsNull()) { + m_name = text.value; + } else { + *m_name += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"short") { + if (m_shortName.IsNull()) { + m_shortName = attribute.value; + } + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (data.name.IsNull()) { + NormalizeString(m_name); + NormalizeString(m_shortName); + if (!m_name.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, &*m_name); + } + data.name = m_name; + if (!m_shortName.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_shortName); + } + data.shortName = m_shortName; + } + } + + NameParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_textDirection(direction) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_name; + DPL::OptionalString m_shortName; + DPL::OptionalString m_dir; + DPL::String m_lang; + Unicode::Direction m_textDirection; +}; + +class AccessParser : public ElementParser +{ + public: + enum StandardType + { + STANDARD_TYPE_NONE, + STANDARD_TYPE_WARP + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &AccessParser::Other); + } + + virtual void Accept(const Element& element) + { + // for tizen web apps WARP should be used + if (element.ns == ConfigurationNamespace::W3CWidgetNamespaceName || + element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_standardType = STANDARD_TYPE_WARP; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + void AcceptWac(const XmlAttribute& attribute) + { + if (attribute.name == L"origin") { + m_strIRIOrigin = attribute.value; + NormalizeString(m_strIRIOrigin); + } else if (attribute.name == L"subdomains") { + DPL::String normalizedValue = attribute.value; + NormalizeString(normalizedValue); + + if (normalizedValue == L"true") { + m_bSubDomainAccess = true; + } else if (normalizedValue == L"false") { + m_bSubDomainAccess = false; + } + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + switch (m_standardType) { + case STANDARD_TYPE_WARP: + AcceptWac(attribute); + break; + default: + _E("Error in Access tag - unknown standard."); + break; + } + } + + void VerifyWac() + { + WarpIRI iri; + iri.set(m_strIRIOrigin, false); + + if (!iri.isAccessDefinition()) { + _W("Access list element: %ls is not acceptable by WARP standard and will be ignored!", + m_strIRIOrigin.c_str()); + return; + } + + if(m_strIRIOrigin == L"*") //wildcard match means yes for subdomains + { + m_bSubDomainAccess = true; + } + + ConfigParserData::AccessInfo accessInfo(m_strIRIOrigin, + m_bSubDomainAccess); + //std::pair ret = + m_data.accessInfoSet.insert(accessInfo); + } + + virtual void Verify() + { + switch (m_standardType) { + case STANDARD_TYPE_WARP: + VerifyWac(); + break; + default: + _E("Error in Access tag - unknown standard."); + break; + } + } + + AccessParser(ConfigParserData& data) : + ElementParser(), + m_bSubDomainAccess(false), + m_standardType(STANDARD_TYPE_NONE), + m_network(false), + m_data(data) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + ElementParserPtr(shared_from_this()))); + } + + private: + DPL::String m_strIRIOrigin; + bool m_bSubDomainAccess; + StandardType m_standardType; + bool m_network; + ConfigParserData& m_data; +}; + +class DescriptionParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &DescriptionParser::Other); + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_description = L""; + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + virtual void Accept(const Text& text) + { + if (m_description.IsNull()) { + m_description = text.value; + } else { + *m_description += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (data.description.IsNull()) { + if (!m_description.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_description); + } + data.description = m_description; + } + } + + DescriptionParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_lang(), + m_description(), + m_textDirection(direction) + {} + + private: + ConfigParserData& m_data; + DPL::String m_lang; + DPL::OptionalString m_description; + Unicode::Direction m_textDirection; +}; + +class AuthorParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &AuthorParser::Other); + } + + AuthorParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_textDirection(direction) + {} + + virtual void Accept(const Element& /*element*/) + { + m_authorName = L""; + } + + virtual void Accept(const Text& text) + { + *(m_authorName) += text.value; + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"href") { + //Validate href IRI and ignore it if invalid + //See also test: ta-argMozRiC-an + LibIri::Wrapper iri(DPL::ToUTF8String(attribute.value).c_str()); + if (iri.Validate()) { + m_authorHref = attribute.value; + } + } else if (attribute.name == L"email") { + m_authorEmail = attribute.value; + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + if (!m_data.authorName && !m_data.authorHref && !m_data.authorEmail) { + NormalizeString(m_authorName); + NormalizeString(m_authorHref); + NormalizeString(m_authorEmail); + if (!!m_authorName) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_authorName); + m_data.authorName = m_authorName; + } + if (!!m_authorHref) { + m_data.authorHref = m_authorHref; + } + if (!!m_authorEmail) { + m_data.authorEmail = m_authorEmail; + } + } + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_authorEmail; + DPL::OptionalString m_authorHref; + DPL::OptionalString m_authorName; + Unicode::Direction m_textDirection; +}; + +class LicenseParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return DPL::MakeDelegate(this, &LicenseParser::Other); + } + + LicenseParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_ignore(true), + m_textDirection(direction) + {} + + virtual void Accept(const Element& element) + { + if (m_license.IsNull()) { + m_lang = element.lang; + m_license = L""; + m_ignore = false; + } + } + + virtual void Accept(const Text& text) + { + if (!m_ignore) { + *m_license += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (!m_ignore) { + if (attribute.name == L"href" && m_licenseHref.IsNull()) { + m_licenseHref = attribute.value; + } else if (attribute.name == L"file" && m_licenseFile.IsNull()) { + m_licenseFile = attribute.value; + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (data.license.IsNull()) { + if (!m_license.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_license); + } + data.license = m_license; + data.licenseHref = m_licenseHref; + data.licenseFile = m_licenseFile; + } + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + ElementParserPtr(shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::String m_lang; + bool m_ignore; + + DPL::OptionalString m_license; + DPL::OptionalString m_licenseFile; + DPL::OptionalString m_licenseHref; + Unicode::Direction m_textDirection; +}; + +class IconParser : public ElementParser +{ + DECLARE_EXCEPTION_TYPE(ElementParser::Exception::ParseError, BadSrcError) + + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + IconParser(ConfigParserData& data) : ElementParser(), + m_data(data) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } else if (attribute.name == L"width") { + m_width = ParseSizeAttributeValue(attribute.value); + } else if (attribute.name == L"height") { + m_height = ParseSizeAttributeValue(attribute.value); + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "Icon element must be empty"); + } + + virtual void Verify() + { + if (m_src.IsNull()) { + _W("src attribute of icon element is mandatory - ignoring"); + return; + } + + Try + { + ConfigParserData::Icon icon(delocalizeSrcPath(*m_src)); + icon.width = m_width; + icon.height = m_height; + + ConfigParserData::IconsList::iterator it = std::find( + m_data.iconsList.begin(), m_data.iconsList.end(), icon); + if (it == m_data.iconsList.end()) { + m_data.iconsList.push_front(icon); + } + } + Catch(BadSrcError) + { + _W("src attribute is invalid: %ls", (*m_src).c_str()); + } + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_src; + DPL::OptionalInt m_width; + DPL::OptionalInt m_height; + + static DPL::OptionalInt ParseSizeAttributeValue(const DPL::String& value) + { + DPL::OptionalString normalizedValue = value; + NormalizeString(normalizedValue); + if (!(*normalizedValue).empty()) { + char* reterr = NULL; + errno = 0; + long int valueInt = + strtol(DPL::ToUTF8String(value).c_str(), &reterr, 10); + if (errno != 0 || + std::string(reterr) == DPL::ToUTF8String(value) || + valueInt <= 0) + { + return DPL::OptionalInt::Null; + } else { + return valueInt; + } + } + return DPL::OptionalInt::Null; + } + + /** + * @brief delocalizePath removes locales folder from relative path if + * neccessary + * @param source source string + * + * @throw BadSrcError if string is bad value of src attribute + * + * @return corrected string + */ + static DPL::String delocalizeSrcPath(const DPL::String & source) + { + static const DPL::String localeFolder(L"locales/"); + static const int index = localeFolder.size(); + + DPL::String result = source; + + if (source.substr(0, index) == localeFolder) { + size_t pos = result.find_first_of('/', index); + if (pos != std::string::npos && pos + 1 < source.size()) { + result = result.substr(pos + 1, source.size()); + } else { + Throw(BadSrcError); + } + } + return result; + } +}; + +class ContentParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + ContentParser(ConfigParserData& data) : + ElementParser(), + m_data(data) + {} + + virtual void Accept(const Element& element) + { + m_namespace = element.ns; + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + DPL::String value = attribute.value; + NormalizeString(value); + + if (attribute.name == L"src") { + m_src = value; + } else if (attribute.name == L"type") { + m_type = value; + MimeTypeUtils::MimeAttributes mimeAttributes = + MimeTypeUtils::getMimeAttributes(value); + if ((mimeAttributes.count(L"charset") > 0) && m_encoding.IsNull()) + { + m_encoding = mimeAttributes[L"charset"]; + } + } else if (attribute.name == L"encoding") { + if (!value.empty()) { + m_encoding = value; + } + } + } + + virtual void Verify() + { + if(!!m_data.startFileEncountered) + { + if(m_data.startFileNamespace == m_namespace + || m_namespace != ConfigurationNamespace::TizenWebAppNamespaceName) + { + return; + } + //else continue -> if previous item was not in tizen namespace + } + + m_data.startFileEncountered = true; + m_data.startFileNamespace = m_namespace; + + //we're consciously setting startFile even if m_src is null or invalid. + //WidgetConfigurationManager will deal with this. + m_data.startFile = m_src; + + if (!!m_src) { + m_data.startFileContentType = m_type; + if (!!m_encoding) { + m_data.startFileEncoding = m_encoding; + } else { + m_data.startFileEncoding = L"UTF-8"; + } + } + } + + private: + DPL::OptionalString m_src; + DPL::OptionalString m_type; + DPL::OptionalString m_encoding; + ConfigParserData& m_data; + DPL::String m_namespace; +}; + +class PreferenceParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + m_name = attribute.value; + } else if (attribute.name == L"value") { + m_value = attribute.value; + } else if (attribute.name == L"readonly") { + if (attribute.value == L"true") { + m_required = true; + } else { + m_required = false; + } + } + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_name.IsNull()) { + _W("preference element must have name attribute"); + return; + } + NormalizeString(m_name); + NormalizeString(m_value); + ConfigParserData::Preference preference(*m_name, m_required); + preference.value = m_value; + if (m_data.preferencesList.find(preference) == + m_data.preferencesList.end()) + { + m_data.preferencesList.insert(preference); + } + } + + PreferenceParser(ConfigParserData& data) : + ElementParser(), + m_required(false), + m_data(data) + {} + + private: + DPL::OptionalString m_name; + DPL::OptionalString m_value; + bool m_required; + ConfigParserData& m_data; +}; + +class SettingParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + m_setting.m_name = attribute.name; + m_setting.m_value = attribute.value; + m_data.settingsList.insert(m_setting); + } + + virtual void Verify() + {} + + SettingParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_setting(L"", L"") + {} + + private: + ConfigParserData& m_data; + ConfigParserData::Setting m_setting; +}; + +class AppControlParser : public ElementParser +{ + public: + struct SourceParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + m_data.m_src = *m_value; + } + + SourceParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct OperationParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + m_data.m_operation = *m_value; + } + + OperationParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct UriParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + // exception + DPL::String ignoreUri(L"file"); + + if (!m_value.IsNull() && *m_value == ignoreUri) + { + _D("exception : '%ls' scheme will be ignored.", (*m_value).c_str()); + m_value = DPL::OptionalString::Null; + } + + if (m_value.IsNull() || *m_value == L"") { + return; + } + + DPL::String wildString(L"*/*"); + if ((m_data.m_uriList.find(wildString) == m_data.m_uriList.end()) + && (m_data.m_uriList.find(*m_value) == m_data.m_uriList.end())) + { + m_data.m_uriList.insert(*m_value); + } else { + _D("Ignoring uri with name %ls", (*m_value).c_str()); + } + } + + UriParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct MimeParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + DPL::String wildString(L"*/*"); + if ((m_data.m_mimeList.find(wildString) == + m_data.m_mimeList.end()) + && (m_data.m_mimeList.find(*m_value) == + m_data.m_mimeList.end())) + { + m_data.m_mimeList.insert(*m_value); + } else { + _D("Ignoring mime with name %ls", (*m_value).c_str()); + } + } + + MimeParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct DispositionParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + DPL::String windowString(L"window"); + DPL::String inlineString(L"inline"); + + if (*m_value == L"window") { + m_data.m_disposition = + ConfigParserData::AppControlInfo::Disposition::WINDOW; + } else if (*m_value == L"inline") { + m_data.m_disposition = + ConfigParserData::AppControlInfo::Disposition::INLINE; + } else { + _D("Ignoring dispostion value %ls", (*m_value).c_str()); + } + } + + DispositionParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"src") { + return DPL::MakeDelegate(this, &AppControlParser::OnSourceElement); + } else if (name == L"operation") { + return DPL::MakeDelegate(this, + &AppControlParser::OnOperationElement); + } else if (name == L"uri") { + return DPL::MakeDelegate(this, &AppControlParser::OnUriElement); + } else if (name == L"mime") { + return DPL::MakeDelegate(this, &AppControlParser::OnMimeElement); + } else if (name == L"disposition") { + return DPL::MakeDelegate(this, + &AppControlParser::OnDispositionElement); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Element& element) + { + _W("namespace for app service = %ls", element.ns.c_str()); + if (element.ns == ConfigurationNamespace::W3CWidgetNamespaceName) { + ThrowMsg(Exception::ParseError, + "Wrong xml namespace for widget element"); + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_appControl.m_src == L"") { + ThrowMsg(Exception::ParseError, "service element must have src element"); + } + + if (m_appControl.m_operation == L"") { + ThrowMsg(Exception::ParseError, "service element must have operation element"); + } + + auto res = std::find(m_data.appControlList.begin(), m_data.appControlList.end(), m_appControl); + if(res != m_data.appControlList.end()) { + ThrowMsg(Exception::ParseError, "service element must be unique"); + } + +#ifdef NFC_EXCEPTION_HANDLING_FOR_TIZEN_2_2_ONLY + // XXX This feature should be retained to Tizen 2.2 only. + // NFC exception handling which was requested from Tizen Device API team. + + const DPL::String exceptionNfcOperation = + L"http://tizen.org/appcontrol/operation/nfc/transaction"; + const DPL::String exceptionNfcUri = L"nfc://secure/aid/"; + const DPL::String divertingNfcUri1 = L"nfc://secure/SIM1/aid/"; + const DPL::String divertingNfcUri2 = L"nfc://secure/eSE/aid/"; + + if (m_appControl.m_operation == exceptionNfcOperation + && m_appControl.m_mimeList.empty() + && m_appControl.m_uriList.size() == 1 + && (m_appControl.m_uriList.begin())->compare(0, exceptionNfcUri.length(), exceptionNfcUri) == 0) + { + DPL::String originalUri = *m_appControl.m_uriList.begin(); + DPL::String newUri = originalUri; + + newUri.replace(0, exceptionNfcUri.length(), divertingNfcUri1); + m_appControl.m_uriList.erase(m_appControl.m_uriList.begin()); + m_appControl.m_uriList.insert(newUri); + m_data.appControlList.push_back(m_appControl); + _D("NFC exception : %ls -> %ls", originalUri.c_str(), newUri.c_str()); + + newUri = originalUri; + newUri.replace(0, exceptionNfcUri.length(), divertingNfcUri2); + m_appControl.m_uriList.erase(m_appControl.m_uriList.begin()); + m_appControl.m_uriList.insert(newUri); + m_data.appControlList.push_back(m_appControl); + _D("NFC exception : %ls -> %ls", originalUri.c_str(), newUri.c_str()); + + return; + } +#endif // NFC_EXCEPTION_HANDLING_FOR_TIZEN_2_2_ONLY + + m_data.appControlList.push_back(m_appControl); + } + + ElementParserPtr OnSourceElement() + { + return ElementParserPtr(new SourceParser(m_appControl)); + } + + ElementParserPtr OnOperationElement() + { + return ElementParserPtr(new OperationParser(m_appControl)); + } + + ElementParserPtr OnUriElement() + { + return ElementParserPtr(new UriParser(m_appControl)); + } + + ElementParserPtr OnMimeElement() + { + return ElementParserPtr(new MimeParser(m_appControl)); + } + + ElementParserPtr OnDispositionElement() + { + return ElementParserPtr(new DispositionParser(m_appControl)); + } + + AppControlParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_appControl(L"") + {} + + private: + ConfigParserData& m_data; + ConfigParserData::AppControlInfo m_appControl; +}; + +class ApplicationParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + { + if (m_properNamespace) { + ThrowMsg(Exception::ParseError, "application element must be empty"); + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"id") { + m_id = attribute.value; + NormalizeAndTrimSpaceString(m_id); + } else if (attribute.name == L"package") { + m_package = attribute.value; + } else if (attribute.name == L"required_version") { + m_version = attribute.value; + NormalizeString(m_version); + } else { + ThrowMsg(Exception::ParseError, + "unknown attribute '" + + DPL::ToUTF8String(attribute.name) + + "' in application element"); + } + } + } + + virtual void Verify() + { + VerifyIdAndPackage(); + VerifyVersion(); + } + + ApplicationParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_id(DPL::OptionalString::Null), + m_version(DPL::OptionalString::Null), + m_properNamespace(false) + {} + + static const char* const REGEXP_ID; + + private: + void VerifyIdAndPackage() + { + if (!m_package) + { + ThrowMsg(Exception::ParseError, + "application element must have package attribute"); + } + else + { + pcrecpp::RE re(REGEXP_PACKAGE); + if (!re.FullMatch(DPL::ToUTF8String(*m_package))) + { + ThrowMsg(Exception::ParseError, + "invalid format of package attribute"); + } + } + + if (!m_id) { + ThrowMsg(Exception::ParseError, + "application element must have id attribute"); + } + else + { + std::string package; + pcrecpp::RE re(REGEXP_ID); + if (!re.FullMatch(DPL::ToUTF8String(*m_id), &package)) + { + ThrowMsg(Exception::ParseError, + "invalid format of id attribute"); + } + if (package != DPL::ToUTF8String(*m_package)) + { + ThrowMsg(Exception::ParseError, + "invalid package prefix in id attribute"); + } + } + + m_data.tizenAppId = m_id; + m_data.tizenPkgId = m_package; + } + + void VerifyVersion() + { + if (!m_version) + { + ThrowMsg(Exception::ParseError, + "application element must have required_version attribute"); + } + else + { + pcrecpp::RE re(REGEXP_VERSION); + if (!re.FullMatch(DPL::ToUTF8String(*m_version))) + { + ThrowMsg(Exception::ParseError, + "invalid format of version attribute"); + } + } + + m_data.tizenMinVersionRequired = m_version; + } + + static const char* const REGEXP_PACKAGE; + static const char* const REGEXP_VERSION; + + ConfigParserData& m_data; + DPL::OptionalString m_id; + DPL::OptionalString m_package; + DPL::OptionalString m_version; + bool m_properNamespace; +}; + +const char* const ApplicationParser::REGEXP_PACKAGE = "[0-9A-Za-z]{10}"; +const char* const ApplicationParser::REGEXP_ID = "([0-9A-Za-z]{10})\\.[0-9A-Za-z]{1,52}"; +const char* const ApplicationParser::REGEXP_VERSION = "\\d+\\.\\d+(\\.\\d+)?"; + +class SplashParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_src.IsNull()) + { + _W("src attribute of splash element is mandatory - ignoring"); + return; + } + + m_data.splashImgSrc = m_src; + } + + SplashParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + private: + DPL::OptionalString m_src; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +class BackgroundParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_src.IsNull()) { + _W("src attribute of background element is mandatory - ignoring"); + return; + } + + m_data.backgroundPage = m_src; + } + + explicit BackgroundParser(ConfigParserData& data) : + m_data(data) + {} + + private: + DPL::OptionalString m_src; + ConfigParserData& m_data; +}; + +class PrivilegeParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + _D("element"); + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"name") { + m_feature.name = attribute.value; + m_privilege.name = attribute.value; + } + } + } + + virtual void Verify() + { + LibIri::Wrapper iri(DPL::ToUTF8String(m_feature.name).c_str()); + + if (m_feature.name != L"") { + if (iri.Validate()) { + if (m_data.featuresList.find(m_feature) == + m_data.featuresList.end()) + { + m_data.featuresList.insert(m_feature); + } else { + _D("Ignoring feature with name %ls", m_feature.name.c_str()); + } + } + } + + LibIri::Wrapper iriPrivilege( + DPL::ToUTF8String(m_privilege.name).c_str()); + + if (m_privilege.name != L"") { + if (iriPrivilege.Validate()) { + if (m_data.privilegeList.find(m_privilege) == + m_data.privilegeList.end()) + { + m_data.privilegeList.insert(m_privilege); + } else { + _D("Ignoring privilege with name %ls", m_privilege.name.c_str()); + } + } + } + } + + PrivilegeParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_feature(L""), + m_privilege(L""), + m_properNamespace(false) + {} + + private: + ConfigParserData& m_data; + ConfigParserData::Feature m_feature; + ConfigParserData::Privilege m_privilege; + bool m_properNamespace; +}; + +class CategoryParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_name = attribute.value; + } + } + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_name.IsNull()) { + _W("name attribute of category element is mandatory - ignoring"); + return; + } + + if (m_data.categoryList.find(*m_name) == + m_data.categoryList.end()) + { + m_data.categoryList.insert(*m_name); + } + } + + explicit CategoryParser(ConfigParserData& data) : + m_data(data) + {} + + private: + DPL::OptionalString m_name; + ConfigParserData& m_data; +}; + +class AppWidgetParser : public ElementParser +{ + public: + + struct BoxLabelParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + m_lang = attribute.lang; + } + } + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_label = text.value; + } + } + + virtual void Verify() + { + std::pair boxLabel; + if (m_label.empty()) { + _W("box-label element is empty"); + boxLabel.first = DPL::FromUTF8String(""); + boxLabel.second = DPL::FromUTF8String(""); + m_data.m_label.push_back(boxLabel); + } + else { + boxLabel.first = m_lang; + boxLabel.second = m_label; + m_data.m_label.push_back(boxLabel); + } + } + + BoxLabelParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_lang; + DPL::String m_label; + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + }; + + struct BoxIconParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_icon = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_icon.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of box-icon element is mandatory - ignoring"); + } + if (!m_data.m_icon.empty()) { + ThrowMsg(Exception::ParseError, + " element should occur as 0 or 1 time"); + } + m_data.m_icon = m_icon; + } + + explicit BoxIconParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_icon; + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + }; + + struct BoxContentParser : public ElementParser + { + struct BoxSizeParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"preview") { + m_preview = attribute.value; + } + if (attribute.name == L"use-decoration") { + m_useDecoration = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_size = text.value; + } + } + + virtual void Verify() + { + if(m_size.empty()) { + ThrowMsg(Exception::ParseError, + "size is mandatory - ignoring"); + } + + ConfigParserData::LiveboxInfo::BoxSizeInfo boxSizeInfo; + boxSizeInfo.m_size = m_size; + boxSizeInfo.m_preview = m_preview; + boxSizeInfo.m_useDecoration = m_useDecoration; + m_data.m_boxSize.push_back(boxSizeInfo); + } + + explicit BoxSizeParser( + ConfigParserData::LiveboxInfo::BoxContentInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_size; + DPL::String m_preview; + DPL::String m_useDecoration; + bool m_properNamespace; + ConfigParserData::LiveboxInfo::BoxContentInfo& m_data; + }; + + struct PdParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_src = attribute.value; + } else if (attribute.name == L"width") { + m_width = attribute.value; + } else if (attribute.name == L"height") { + m_height = attribute.value; + } else if (attribute.name == L"fast-open") { + m_fastOpen= attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_src.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of pd element is mandatory - ignoring"); + } + + if (m_width.empty()) { + ThrowMsg(Exception::ParseError, + "width attribute of pd element is mandatory - ignoring"); + } + + if (m_height.empty()) { + ThrowMsg(Exception::ParseError, + "height attribute of pd element is mandatory - ignoring"); + } + + if (ConvertToInt(m_width).IsNull()) { + ThrowMsg(Exception::ParseError, + "width attribute of pd element cannot be converted to int - ignoring. value: " << m_width); + } + + + DPL::OptionalInt height = ConvertToInt(m_height); + + if (height.IsNull()) { + ThrowMsg(Exception::ParseError, + "height attribute of pd element cannot be converted to int - ignoring. value: " << m_height); + } + + if (*height < 1) { + m_height = L"1"; + _D("height attribute of pd element shouldn't be less than 1. Changed to 1 from %d", *height); + } else if (*height > 380){ + m_height = L"380"; + _D("height attribute of pd element shouldn't be greater than 380. Changed to 380 from %d", *height); + } + + m_data.m_pdSrc = m_src; + m_data.m_pdWidth = m_width; + m_data.m_pdHeight = m_height; + m_data.m_pdFastOpen = m_fastOpen; + } + + explicit PdParser( + ConfigParserData::LiveboxInfo::BoxContentInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::OptionalInt ConvertToInt(const DPL::String& intAsString) + { + char * endptr; + std::string tempStr = DPL::ToUTF8String(intAsString); + const char * intAsString_c = tempStr.c_str(); + errno = 0; + long int intAsString_i = strtol(intAsString_c, &endptr, 10); + + if ((errno == ERANGE && (intAsString_i == LONG_MAX || intAsString_i == LONG_MIN)) + || intAsString_i > INT_MAX || intAsString_i < INT_MIN + || *endptr != '\0') { + return DPL::OptionalInt::Null; + } + + return static_cast(intAsString_i); + } + + DPL::String m_src; + DPL::String m_width; + DPL::String m_height; + DPL::String m_fastOpen; + + bool m_properNamespace; + ConfigParserData::LiveboxInfo::BoxContentInfo& m_data; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"box-size") { + return DPL::MakeDelegate( + this, + &AppWidgetParser::BoxContentParser:: + OnBoxSizeElement); + } else if (name == L"pd") { + return DPL::MakeDelegate( + this, + &AppWidgetParser::BoxContentParser:: + OnPdElement); + } else { + ThrowMsg(Exception::ParseError, + "No element parser for name: " << name); + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_box.m_boxSrc = attribute.value; + } + if (attribute.name == L"mouse-event") { + m_box.m_boxMouseEvent = attribute.value; + } + if (attribute.name == L"touch-effect") { + m_box.m_boxTouchEffect = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_box.m_boxSrc.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of box-content element is mandatory - ignoring"); + } + + if (!m_box.m_boxMouseEvent.empty() && + CheckIfNotTrueNorFalse(m_box.m_boxMouseEvent)) + { + ThrowMsg(Exception::ParseError, + "mouse-event attribute of box-content element should be true or false - ignoring"); + } + + if (!m_box.m_boxTouchEffect.empty() && + CheckIfNotTrueNorFalse(m_box.m_boxTouchEffect)) + { + ThrowMsg(Exception::ParseError, + "touch-effect attribute of box-content element should be true or false - ignoring"); + } + + if (m_box.m_boxMouseEvent.empty()) { + m_box.m_boxMouseEvent = L"false"; + } + + if (m_box.m_boxTouchEffect.empty()) { + m_box.m_boxTouchEffect = L"true"; + } + + if (m_box.m_boxSize.empty()) { + ThrowMsg(Exception::ParseError, + "box-size element of box-content element not found - ignoring"); + } + + m_data.m_boxInfo = m_box; + } + + explicit BoxContentParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + ElementParserPtr OnBoxSizeElement() + { + return ElementParserPtr(new BoxSizeParser(m_box)); + } + + ElementParserPtr OnPdElement() + { + return ElementParserPtr(new PdParser(m_box)); + } + + private: + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + ConfigParserData::LiveboxInfo::BoxContentInfo m_box; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"box-label") { + return DPL::MakeDelegate(this, &AppWidgetParser::OnBoxLabelElement); + } else if (name == L"box-icon") { + return DPL::MakeDelegate(this, &AppWidgetParser::OnBoxIconElement); + } else if (name == L"box-content") { + return DPL::MakeDelegate(this, &AppWidgetParser::OnBoxContentElement); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"id") { + m_liveboxId = attribute.value; + } else if (attribute.name == L"primary") { + m_primary = attribute.value; + } else if (attribute.name == L"auto-launch") { + m_autoLaunch = attribute.value; + } else if (attribute.name == L"update-period") { + m_updatePeriod = attribute.value; + } else if (attribute.name == L"type") { + m_type = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_liveboxId.empty()) { + ThrowMsg(Exception::ParseError, + "app-widget element must have id attribute"); + } + else + { + pcrecpp::RE re(REGEXP_ID_STRING.c_str()); + if (!re.FullMatch(DPL::ToUTF8String(m_liveboxId))) + { + ThrowMsg(Exception::ParseError, + "invalid format of app-widget id attribute"); + } + } + + if (m_primary.empty()) + { + ThrowMsg(Exception::ParseError, + "app-widget element must have primary attribute"); + } else if (CheckIfNotTrueNorFalse(m_primary)) + { + ThrowMsg(Exception::ParseError, + "auto-launch attribute of app-widget element should be true or false - ignoring"); + } + + if (!m_autoLaunch.empty() && + CheckIfNotTrueNorFalse(m_autoLaunch)) + { + ThrowMsg(Exception::ParseError, + "auto-launch attribute of app-widget element should be true or false - ignoring"); + } + + if (!m_updatePeriod.empty()) + { + char * endptr; + errno = 0; + std::string tempStr = DPL::ToUTF8String(m_updatePeriod); + + //set standard locale to fix decimal point mark - '.' + std::string currentLocale = setlocale(LC_NUMERIC, NULL); + if (NULL == setlocale(LC_NUMERIC, "C")) + _W("Failed to change locale to \"C\""); + double updatePeriod = strtod(tempStr.c_str(), &endptr); + + //go back to previous locale + if (NULL == setlocale(LC_NUMERIC, currentLocale.c_str())) + _W("Failed to set previous locale"); + + if ((errno == ERANGE && (updatePeriod == -HUGE_VAL || updatePeriod == HUGE_VAL)) + || *endptr != '\0') { + ThrowMsg(Exception::ParseError, + "update-period attribute of app-widget element should be a number - ignoring. current value: " << m_updatePeriod); + } else if (updatePeriod < 1800.0) { + _D("update-period attribute of app-widget element shouldn't be less than 1800.0 - changed to 1800 from value: %ls", m_updatePeriod.c_str()); + m_updatePeriod = L"1800.0"; + } + } + + if (m_autoLaunch.empty()) { + m_autoLaunch = L"false"; + } + + if(m_livebox.m_label.empty()) { + ThrowMsg(Exception::ParseError, + "box-label element of app-widget element not found - ignoring"); + } + + if(!m_boxContentFound) { + ThrowMsg(Exception::ParseError, + "box-content element of app-widget element not found - ignoring"); + } + + m_livebox.m_liveboxId = m_liveboxId; + m_livebox.m_primary = m_primary; + m_livebox.m_autoLaunch = m_autoLaunch; + m_livebox.m_updatePeriod = m_updatePeriod; + m_livebox.m_type = m_type; + + m_data.m_livebox.push_back(m_livebox); + } + + explicit AppWidgetParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false), + m_boxContentFound(false) + { + m_livebox = ConfigParserData::LiveboxInfo(); + } + + ElementParserPtr OnBoxLabelElement() + { + + return ElementParserPtr(new BoxLabelParser(m_livebox)); + } + + ElementParserPtr OnBoxIconElement() + { + return ElementParserPtr(new BoxIconParser(m_livebox)); + } + + ElementParserPtr OnBoxContentElement() + { + m_boxContentFound = true; + return ElementParserPtr(new BoxContentParser(m_livebox)); + } + + private: + static std::string REGEXP_ID_STRING; + ConfigParserData& m_data; + ConfigParserData::LiveboxInfo m_livebox; + DPL::String m_liveboxId; + DPL::String m_primary; + DPL::String m_autoLaunch; + DPL::String m_updatePeriod; + DPL::String m_type; + bool m_properNamespace; + bool m_boxContentFound; + + static bool CheckIfNotTrueNorFalse(const DPL::String &stringToCheck) + { + return stringToCheck.compare(L"true") != 0 && stringToCheck.compare(L"false") != 0; + } +}; + +std::string AppWidgetParser::REGEXP_ID_STRING = std::string(ApplicationParser::REGEXP_ID) + "\\.[0-9A-Za-z]+"; + +class AllowNavigationParser : public ElementParser +{ + public: + AllowNavigationParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) + { + m_origin = text.value; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + { + } + + virtual void Verify() + { + if (m_data.allowNavigationEncountered || !m_properNamespace) + { + return; + } + m_data.allowNavigationEncountered = true; + + if (m_origin.IsNull()) { + _W("data is empty"); + return; + } + + char* data = strdup(DPL::ToUTF8String(*m_origin).c_str()); + char* ptr = strtok(data," "); + while (ptr != NULL) { + std::string origin = ptr; + ptr = strtok(NULL," "); + if(origin == "*") { + ConfigParserData::AllowNavigationInfo info(L"*", L"*"); + m_data.allowNavigationInfoList.push_back(info); + continue; + } + + std::unique_ptr iri(iri_parse(origin.c_str()), iri_destroy); + if (!iri->host || strlen(iri->host) == 0) { + // input origin should has schem and host + // in case of file scheme path is filled + // "http://" + _W("input origin isn't verified"); + continue; + } + DPL::String scheme = L"*"; + if (iri->scheme && strlen(iri->scheme) != 0) { + scheme = DPL::FromUTF8String(iri->scheme); + } + ConfigParserData::AllowNavigationInfo info( + scheme, + DPL::FromUTF8String(iri->host)); + m_data.allowNavigationInfoList.push_back(info); + } + free(data); + } + + private: + DPL::OptionalString m_origin; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +class CspParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + CspParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_policy = text.value; + } + } + + virtual void Verify() + { + if (!m_policy.IsNull()) { + m_data.cspPolicy = *m_policy; + } + } + + private: + ConfigParserData& m_data; + bool m_properNamespace; + DPL::OptionalString m_policy; +}; + +class CspReportOnlyParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + CspReportOnlyParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_policy = text.value; + } + } + + virtual void Verify() + { + if (!m_policy.IsNull()) { + m_data.cspPolicyReportOnly = *m_policy; + } + } + + private: + ConfigParserData& m_data; + bool m_properNamespace; + DPL::OptionalString m_policy; +}; + +class AccountParser : public ElementParser +{ + public: + struct IconParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"section") { + if (attribute.value == L"Account") { + m_type = ConfigParserData::IconSectionType::DefaultIcon; + } else if (attribute.value == L"AccountSmall") { + m_type = ConfigParserData::IconSectionType::SmallIcon; + } + } + } + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + std::pair icon; + icon.first = m_type; + icon.second = *m_value; + + m_data.m_iconSet.insert(icon); + } + + IconParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_type(ConfigParserData::DefaultIcon), + m_data(data) + {} + + private: + bool m_properNamespace; + ConfigParserData::IconSectionType m_type; + ConfigParserData::AccountProvider& m_data; + DPL::OptionalString m_value; + }; + + struct DisplayNameParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_value= L""; + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + + std::pair name; + name.first = *m_lang; + name.second = *m_value; + + m_data.m_displayNameSet.insert(name); + } + + DisplayNameParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_lang; + DPL::OptionalString m_value; + ConfigParserData::AccountProvider& m_data; + }; + + struct CapabilityParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Verify() + { + if (m_value.IsNull() || *m_value == L"") { + return; + } + m_data.m_capabilityList.push_back(*m_value); + } + + CapabilityParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AccountProvider& m_data; + }; + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"icon") { + return DPL::MakeDelegate(this, &AccountParser::OnIconElement); + } else if (name == L"display-name") { + return DPL::MakeDelegate(this, + &AccountParser::OnDisplayNameElement); + } else if (name == L"capability") { + return DPL::MakeDelegate(this, &AccountParser::OnCapabilityElement); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"multiple-account-support") { + if (attribute.value == L"true") { + m_account.m_multiAccountSupport = true; + } + } + } + + virtual void Verify() + { + } + + ElementParserPtr OnIconElement() + { + return ElementParserPtr(new IconParser(m_account)); + } + + ElementParserPtr OnDisplayNameElement() + { + return ElementParserPtr(new DisplayNameParser(m_account)); + } + + ElementParserPtr OnCapabilityElement() + { + return ElementParserPtr(new CapabilityParser(m_account)); + } + + AccountParser(ConfigParserData& data) : + ElementParser(), + m_properNamespace(false), + m_multiSupport(false), + m_data(data), + m_account(data.accountProvider) + { + } + + private: + bool m_properNamespace; + bool m_multiSupport; + ConfigParserData& m_data; + ConfigParserData::AccountProvider& m_account; +}; + +class MetadataParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"key") { + m_key = attribute.value; + } else if (attribute.name == L"value") { + m_value = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_key.IsNull()) { + _W("metadata element must have key attribute"); + return; + } + NormalizeString(m_key); + NormalizeString(m_value); + ConfigParserData::Metadata metaData(m_key, m_value); + FOREACH(it, m_data.metadataList) { + if (!DPL::StringCompare(*it->key, *m_key)) { + _E("Key isn't unique"); + return; + } + } + m_data.metadataList.push_back(metaData); + } + + MetadataParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + private: + DPL::OptionalString m_key; + DPL::OptionalString m_value; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +ElementParser::ActionFunc WidgetParser::GetElementParser( + const DPL::String& /*ns*/, + const DPL::String& name) +{ + FuncMap::const_iterator it = m_map.find(name); + if (it != m_map.end()) { + return it->second; + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } +} + +WidgetParser::WidgetParser(ConfigParserData& data) : + m_data(data), + m_textDirection(Unicode::EMPTY) +{ + m_map[L"name"] = DPL::MakeDelegate(this, &WidgetParser::OnNameElement); + m_map[L"access"] = DPL::MakeDelegate(this, &WidgetParser::OnAccessElement); + m_map[L"description"] = + DPL::MakeDelegate(this, &WidgetParser::OnDescriptionElement); + m_map[L"author"] = DPL::MakeDelegate(this, &WidgetParser::OnAuthorElement); + m_map[L"license"] = + DPL::MakeDelegate(this, &WidgetParser::OnLicenseElement); + m_map[L"icon"] = DPL::MakeDelegate(this, &WidgetParser::OnIconElement); + m_map[L"content"] = + DPL::MakeDelegate(this, &WidgetParser::OnContentElement); + m_map[L"preference"] = + DPL::MakeDelegate(this, &WidgetParser::OnPreferenceElement); + m_map[L"setting"] = + DPL::MakeDelegate(this, &WidgetParser::OnSettingElement); + m_map[L"application"] = DPL::MakeDelegate( + this, + &WidgetParser:: + OnApplicationElement); + m_map[L"splash"] = DPL::MakeDelegate(this, &WidgetParser::OnSplashElement); + m_map[L"background"] = DPL::MakeDelegate(this, + &WidgetParser::OnBackgroundElement); + m_map[L"privilege"] = DPL::MakeDelegate(this, + &WidgetParser::OnPrivilegeElement); + m_map[L"app-control"] = DPL::MakeDelegate( + this, + &WidgetParser:: + OnAppControlElement); + m_map[L"category"] = DPL::MakeDelegate(this, + &WidgetParser::OnCategoryElement); + m_map[L"app-widget"] = DPL::MakeDelegate(this, &WidgetParser::OnAppWidgetElement); +#ifdef CSP_ENABLED + m_map[L"content-security-policy"] = DPL::MakeDelegate( + this, + &WidgetParser:: + OnCspElement); + m_map[L"content-security-policy-report-only"] = DPL::MakeDelegate( + this, + &WidgetParser:: + OnCspReportOnlyElement); +#endif +#ifdef ALLOW_NAVIGATION_ENABLED + m_map[L"allow-navigation"] = + DPL::MakeDelegate(this, &WidgetParser::OnAllowNavigationElement); +#endif + m_map[L"account"] = DPL::MakeDelegate(this, &WidgetParser::OnAccountElement); + m_map[L"metadata"] = DPL::MakeDelegate(this, &WidgetParser::OnMetadataElement); +} + +ElementParserPtr WidgetParser::OnNameElement() +{ + return ElementParserPtr(new NameParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnAccessElement() +{ + return ElementParserPtr(new AccessParser(m_data)); +} + +ElementParserPtr WidgetParser::OnDescriptionElement() +{ + return ElementParserPtr(new DescriptionParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnAuthorElement() +{ + return ElementParserPtr(new AuthorParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnLicenseElement() +{ + return ElementParserPtr(new LicenseParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnIconElement() +{ + return ElementParserPtr(new IconParser(m_data)); +} + +ElementParserPtr WidgetParser::OnContentElement() +{ + return ElementParserPtr(new ContentParser(m_data)); +} + +ElementParserPtr WidgetParser::OnPreferenceElement() +{ + return ElementParserPtr(new PreferenceParser(m_data)); +} + +ElementParserPtr WidgetParser::OnSettingElement() +{ + return ElementParserPtr(new SettingParser(m_data)); +} + +ElementParserPtr WidgetParser::OnApplicationElement() +{ + return ElementParserPtr(new ApplicationParser(m_data)); +} + +ElementParserPtr WidgetParser::OnSplashElement() +{ + return ElementParserPtr(new SplashParser(m_data)); +} + +ElementParserPtr WidgetParser::OnBackgroundElement() +{ + return ElementParserPtr(new BackgroundParser(m_data)); +} + +ElementParserPtr WidgetParser::OnPrivilegeElement() +{ + return ElementParserPtr(new PrivilegeParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAppControlElement() +{ + return ElementParserPtr(new AppControlParser(m_data)); +} + +ElementParserPtr WidgetParser::OnCategoryElement() +{ + return ElementParserPtr(new CategoryParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAppWidgetElement() +{ + return ElementParserPtr(new AppWidgetParser(m_data)); +} + +ElementParserPtr WidgetParser::OnCspElement() +{ + return ElementParserPtr(new CspParser(m_data)); +} + +ElementParserPtr WidgetParser::OnCspReportOnlyElement() +{ + return ElementParserPtr(new CspReportOnlyParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAllowNavigationElement() +{ + return ElementParserPtr(new AllowNavigationParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAccountElement() +{ + return ElementParserPtr(new AccountParser(m_data)); +} + +ElementParserPtr WidgetParser::OnMetadataElement() +{ + return ElementParserPtr(new MetadataParser(m_data)); +} + +void WidgetParser::Accept(const Element& element) +{ + if (element.ns != ConfigurationNamespace::W3CWidgetNamespaceName && + element.ns != ConfigurationNamespace::TizenWebAppNamespaceName) + { + ThrowMsg(Exception::ParseError, + "Wrong xml namespace for widget element"); + } +} + +void WidgetParser::Accept(const Text& /*text*/) +{ + ThrowMsg(Exception::ParseError, "widged element must be empty"); +} + +void WidgetParser::Accept(const XmlAttribute& attribute) +{ + if (attribute.name == L"id") { + LibIri::Wrapper iri(DPL::ToUTF8String(attribute.value).c_str()); + //If may important tests starts to fail this test we will have + //to consider commenting this test out again. + if (iri.Validate()) { + m_data.widget_id = attribute.value; + NormalizeString(m_data.widget_id); + } else { + _W("Widget id validation failed: %ls", attribute.value.c_str()); + } + } else if (attribute.name == L"version") { + m_version = attribute.value; + NormalizeString(m_version); + } else if (attribute.name == L"min-version") { + _D("min-version attribute was found. Value: %ls", attribute.value.c_str()); + m_minVersion = attribute.value; + NormalizeString(m_minVersion); + m_data.minVersionRequired = m_minVersion; + } else if (attribute.name == L"height") { + DPL::OptionalString value = attribute.value; + NormalizeString(value); + std::string v = DPL::ToUTF8String(*value); + + if (!v.empty()) { + unsigned char c = v.c_str()[0]; + if (isdigit(c)) { + int val = 0; + for (size_t i = 0; i < v.size(); ++i) { + c = v.c_str()[i]; + if (isdigit(c)) { + val *= 10; + val += (c - '0'); + } else { + break; + } + } + m_data.height = val; + } + } + } else if (attribute.name == L"width") { + DPL::OptionalString value = attribute.value; + NormalizeString(value); + std::string v = DPL::ToUTF8String(*value); + + if (!v.empty()) { + unsigned char c = v.c_str()[0]; + if (c >= '0' && c <= '9') { + int val = 0; + for (size_t i = 0; i < v.size(); ++i) { + c = v.c_str()[i]; + if (c >= '0' && c <= '9') { + val *= 10; + val += (c - '0'); + } else { + break; + } + } + m_data.width = val; + } + } + } else if (attribute.name == L"viewmodes") { + DPL::Tokenize(attribute.value, + L" ", + std::inserter(m_windowModes, + m_windowModes.end()), + true); + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } else if (L"defaultlocale" == attribute.name) { + if (!m_defaultlocale) { + m_defaultlocale = attribute.value; + NormalizeString(m_defaultlocale); + std::string dl = DPL::ToUTF8String(*m_defaultlocale); + + if (!LanguageSubtagRstTreeSingleton::Instance(). + ValidateLanguageTag(dl)) { + _W("Language tag: %s is not valid", dl.c_str()); + m_defaultlocale = DPL::OptionalString::Null; + } else { + _D("Default locale found %s", dl.c_str()); + } + } else { + _W("Ignoring subsequent default locale"); + } + //Any other value consider as a namespace definition + } else if (attribute.name == L"xmlns" || attribute.prefix == L"xmlns") { + _D("Namespace domain: %ls", attribute.name.c_str()); + _D("Namespace value: %ls", attribute.value.c_str()); + m_nameSpaces[attribute.name] = attribute.value; + } else { + _E("Unknown attirbute: namespace=%ls, name=%ls, value=%ls", + attribute.ns.c_str(), attribute.name.c_str(), attribute.value.c_str()); + } +} + +void WidgetParser::Verify() +{ + FOREACH(mode, m_windowModes) { + if (L"windowed" == *mode || L"floating" == *mode || + L"fullscreen" == *mode || L"maximized" == *mode || + L"minimized" == *mode) + { + m_data.windowModes.insert(*mode); + } + } + if (!m_version.IsNull()) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, &*m_version); + m_data.version = m_version; + } + m_data.defaultlocale = m_defaultlocale; + FOREACH(ns, m_nameSpaces) { + m_data.nameSpaces.insert(ns->second); + } +} + diff --git a/src_mobile/configuration_parser/widget_parser.h b/src_mobile/configuration_parser/widget_parser.h new file mode 100644 index 0000000..f59b725 --- /dev/null +++ b/src_mobile/configuration_parser/widget_parser.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file have been implemented in compliance with W3C WARP SPEC. + * but there are some patent issue between W3C WARP SPEC and APPLE. + * so if you want to use this file, refer to the README file in root directory + */ +/** + * @file widget_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef WIDGET_PARSER_H_ +#define WIDGET_PARSER_H_ + +#include "element_parser.h" +#include +#include +#include +#include + +namespace ConfigurationNamespace { +static const DPL::String W3CWidgetNamespaceName = + L"http://www.w3.org/ns/widgets"; +static const DPL::String TizenWebAppNamespaceName = + L"http://tizen.org/ns/widgets"; +} + +namespace PluginsPrefix { +const char * const W3CPluginsPrefix = "http://www.w3.org/"; +const char * const TIZENPluginsPrefix = "http://tizen.org/api/"; +} + +namespace Unicode { +enum Direction +{ + LRE, + RLE, + LRO, + RLO, + EMPTY +}; +} + +class WidgetParser : public ElementParser +{ + public: + ElementParserPtr OnNameElement(); + ElementParserPtr OnDescriptionElement(); + ElementParserPtr OnAuthorElement(); + ElementParserPtr OnLicenseElement(); + ElementParserPtr OnIconElement(); + ElementParserPtr OnContentElement(); + ElementParserPtr OnPreferenceElement(); + ElementParserPtr OnAccessElement(); + ElementParserPtr OnSettingElement(); + ElementParserPtr OnApplicationElement(); + ElementParserPtr OnSplashElement(); + ElementParserPtr OnBackgroundElement(); + ElementParserPtr OnPrivilegeElement(); + ElementParserPtr OnAppControlElement(); + ElementParserPtr OnCategoryElement(); + ElementParserPtr OnAppWidgetElement(); + ElementParserPtr OnCspElement(); + ElementParserPtr OnCspReportOnlyElement(); + ElementParserPtr OnAllowNavigationElement(); + ElementParserPtr OnAccountElement(); + ElementParserPtr OnMetadataElement(); + + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + + virtual void Accept(const Element&); + virtual void Accept(const Text&); + virtual void Accept(const XmlAttribute&); + virtual void Verify(); + + //Typedef used by RootParser + typedef WrtDB::ConfigParserData& Data; + + WidgetParser(Data&); + + private: + Data& m_data; + Unicode::Direction m_textDirection; + FuncMap m_map; + DPL::Optional m_version; + DPL::Optional m_minVersion; + std::list m_windowModes; + DPL::Optional m_defaultlocale; + std::map m_nameSpaces; +}; + +struct IconParser; +struct ContentParser; + +#endif // WIDGET_PARSER_H_ diff --git a/src_mobile/jobs/job.cpp b/src_mobile/jobs/job.cpp new file mode 100644 index 0000000..ec2bb7f --- /dev/null +++ b/src_mobile/jobs/job.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +namespace Jobs { +Job::Job(InstallationType installType) : + m_handle(0), + m_installationType(installType), + m_abortStarted(false), + m_paused(false) +{} + +InstallationType Job::GetInstallationType() const +{ + return m_installationType; +} + +bool Job::GetAbortStarted() const +{ + return m_abortStarted; +} + +void Job::SetAbortStarted(bool flag) +{ + m_abortStarted = flag; +} + +bool Job::IsPaused() const +{ + return m_paused; +} + +void Job::SetPaused(bool paused) +{ + if (paused) { + Pause(); + } else { + Resume(); + } +} + +void Job::Pause() +{ + if (m_paused) { + return; + } + + // Pause + m_paused = true; +} + +void Job::Resume() +{ + if (!m_paused) { + return; + } + + // Continue + m_paused = false; + + // Trigger next steps + CONTROLLER_POST_EVENT(Logic::InstallerController, + InstallerControllerEvents::NextStepEvent(this)); +} + +void Job::SetJobHandle(JobHandle handle) +{ + m_handle = handle; +} + +JobHandle Job::GetJobHandle() const +{ + return m_handle; +} + +void Job::SendProgress() +{} + +void Job::SendFinishedSuccess() +{} + +void Job::SendFinishedFailure() +{} + +void Job::SendProgressIconPath(const std::string &/*path*/) +{} + +void Job::SaveExceptionData(const Jobs::JobExceptionBase&) +{} +} //namespace Jobs diff --git a/src_mobile/jobs/job.h b/src_mobile/jobs/job.h new file mode 100644 index 0000000..3963fc6 --- /dev/null +++ b/src_mobile/jobs/job.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INSTALLER_MODEL_H +#define INSTALLER_MODEL_H + +#include + +#include + +namespace Jobs { +class JobExceptionBase; + +typedef int JobHandle; + +class Job : + public DPL::MutableTaskList +{ + public: + Job(InstallationType installType); + + InstallationType GetInstallationType() const; + + // Undo + void SetAbortStarted(bool flag); + bool GetAbortStarted() const; + + // Pause/resume support + bool IsPaused() const; + void SetPaused(bool paused); + void Pause(); + void Resume(); + void SetJobHandle(JobHandle handle); + JobHandle GetJobHandle() const; + virtual void SendProgress(); + virtual void SendFinishedSuccess(); + virtual void SendFinishedFailure(); + virtual void SendProgressIconPath(const std::string &path); + + virtual void SaveExceptionData(const Jobs::JobExceptionBase&); + + private: + JobHandle m_handle; + InstallationType m_installationType; + bool m_abortStarted; + bool m_paused; +}; +} //namespace Jobs + +#endif // INSTALLER_MODEL_H diff --git a/src_mobile/jobs/job_base.h b/src_mobile/jobs/job_base.h new file mode 100644 index 0000000..edcf0f5 --- /dev/null +++ b/src_mobile/jobs/job_base.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SRC_INSTALLER_CORE_JOBS_JOB_BASE_H +#define SRC_INSTALLER_CORE_JOBS_JOB_BASE_H + +#include + +typedef std::string ProgressDescription; +typedef float ProgressPercent; + +namespace Jobs { +template +class JobProgressBase +{ + protected: + bool m_progressFlag; + ProgressDescription m_progresDescription; + ProgressPercent m_progresPercent; + + public: + JobProgressBase() : m_progressFlag(false), + m_progresPercent(0.0) + {} + + void SetProgressFlag(bool flag) + { + m_progressFlag = flag; + } + bool GetProgressFlag() const + { + return m_progressFlag; + } + + ProgressDescription GetProgressDescription() const + { + return m_progresDescription; + } + + ProgressPercent GetProgressPercent() const + { + return m_progresPercent; + } + + void UpdateProgress(T_InstallationStep step, + ProgressDescription const &description) + { + m_progresPercent = + ((static_cast(step)) / + static_cast(lastElement)) * 100; + m_progresDescription = description; + } +}; + +template +class JobContextBase +{ + public: + JobContextBase(const T_JobStruct& jobStruct) : + m_jobStruct(jobStruct) + {} + + T_JobStruct GetInstallerStruct() const + { + return m_jobStruct; + } + + protected: + T_JobStruct m_jobStruct; +}; + +template +struct JobCallbacksBase +{ + T_finishedCb finishedCallback; + T_progressCb progressCallback; + void *userParam; + + // It must be empty-constructible as a parameter of generic event + JobCallbacksBase() : + finishedCallback(0), + progressCallback(0), + userParam(0) + {} + + JobCallbacksBase(T_finishedCb finished, + T_progressCb progress, + void *param) : + finishedCallback(finished), + progressCallback(progress), + userParam(param) + {} +}; +} //namespace Jobs + +#endif // SRC_INSTALLER_CORE_JOBS_JOB_BASE_H diff --git a/src_mobile/jobs/job_exception_base.h b/src_mobile/jobs/job_exception_base.h new file mode 100644 index 0000000..4d35420 --- /dev/null +++ b/src_mobile/jobs/job_exception_base.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_exception_base.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#include + +#ifndef SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ +#define SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ + +#define DECLARE_JOB_EXCEPTION_BASE(Base, Class, Param) \ + class Class : \ + public Base { \ + public: \ + Class(const char *path, \ + const char *function, \ + int line, \ + const std::string & message = std::string()) : \ + Base(path, function, line, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + Class(const char *path, \ + const char *function, \ + int line, \ + const Exception &reason, \ + const std::string & message = std::string()) : \ + Base(path, function, line, reason, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + virtual int getParam() const \ + { \ + return m_param; \ + } \ + protected: \ + int m_param; \ + }; + +#define DECLARE_JOB_EXCEPTION(Base, Class, Param) \ + class Class : \ + public Base { \ + public: \ + Class(const char *path, \ + const char *function, \ + int line, \ + const std::string & message = std::string()) : \ + Base(path, function, line, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + Class(const char *path, \ + const char *function, \ + int line, \ + const Exception &reason, \ + const std::string & message = std::string()) : \ + Base(path, function, line, reason, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + virtual int getParam() const \ + { \ + return m_param; \ + } \ + }; + +namespace Jobs { +DECLARE_JOB_EXCEPTION_BASE(DPL::Exception, JobExceptionBase, 0) +} + +#endif /* SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ */ diff --git a/src_mobile/jobs/job_exception_error.h b/src_mobile/jobs/job_exception_error.h new file mode 100644 index 0000000..1caba99 --- /dev/null +++ b/src_mobile/jobs/job_exception_error.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_exception_error.h + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief This file contains declarations of wrt api + */ + +/* + * @defgroup wrt_engine_group WebRunTime engine Library + * @ingroup internet_FW + * Functions to APIs to access wrt-engine + */ + +#ifndef JOB_EXCEPTION_ERROR_H +#define JOB_EXCEPTION_ERROR_H + +#include +#include + +namespace Jobs { +namespace Exceptions { +enum Type +{ + Success = 0, ///< Success + + /* pkgmgr error */ + ErrorPackageNotFound, ///< + ErrorPackageInvalid, ///< invalid widget package + ErrorPackageLowerVersion, ///< given version is lower + ErrorPackageExecutableNotFound, + + ErrorManifestNotFound = 11, ///< + ErrorManifestInvalid, ///< + ErrorConfigNotFound, ///< couldn't find config.xml + ErrorConfigInvalid, ///< invalid config.xml + + ErrorSignatureNotFound = 21, ///< signature file not exist. + ErrorSignatureInvalid, ///< invalid signature file + ErrorSignatureVerificationFailed, ///< failure in verificate + ///< signature + ErrorRootCertificateNotFound = 31, ///< couldn't find root + ErrorCertificationInvaid, ///< invalid certification + ErrorCertificateChainVerificationFailed, ///< failure in verificate + ErrorCertificateExpired, ///< expire cerification. + + ErrorInvalidPrivilege = 41, ///< invalid privilege. + ErrorPrivilegeLevelViolation, + + ErrorMenuIconNotFound = 51, ///< + + ErrorFatalError = 61, ///< failure in db operation + ErrorOutOfStorage, ///< failure in shortage of memory + ErrorOutOfMemory, ///< failure in shortage of RAM + ErrorArgumentInvalid, + + /* wrt-installer error */ + /* 121-140 : reserved for Web installer */ + ErrorPackageAlreadyInstalled = 121, ///< package already in target. + ErrorAceCheckFailed, ///< failure in ace check. + ErrorManifestCreateFailed, ///< failure in creating manifest + ErrorEncryptionFailed, ///< failure in encryption resource + ErrorInstallOspServcie, ///< Failure in installing osp service + ErrorPluginInstallationFailed, ///< failure in plugin installation + ErrorWidgetUninstallationFailed, ///< failure in uninstallation + ErrorNotSupportRDSUpdate, ///< failure in rds update + + ErrorUnknown = 140, ///< do not use this error code. +}; +} +} + +#endif /* JOB_EXCEPTION_ERROR_H */ diff --git a/src_mobile/jobs/job_types.h b/src_mobile/jobs/job_types.h new file mode 100644 index 0000000..5f845bf --- /dev/null +++ b/src_mobile/jobs/job_types.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_types.h + * @author Tomasz Iwanek () + */ +#ifndef JOB_TYPES_H +#define JOB_TYPES_H + +namespace Jobs { +/** + * @brief Defines installation and uninstallation type. + */ +enum InstallationType +{ + UnknownInstallation, ///< defines installation of yet unknown type + NewInstallation, ///< defines install process + UpdateInstallation, ///< defines update installation + Uninstallation, ///< defines uninstall process + PluginInstallation ///< defines plugin installation process +}; + +} + +#endif // JOB_TYPES_H diff --git a/src_mobile/jobs/plugin_install/job_plugin_install.cpp b/src_mobile/jobs/plugin_install/job_plugin_install.cpp new file mode 100644 index 0000000..2bcd9de --- /dev/null +++ b/src_mobile/jobs/plugin_install/job_plugin_install.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_plugin_install.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ +#include +#include +#include "plugin_objects.h" +#include +#include + +namespace Jobs { +namespace PluginInstall { +JobPluginInstall::JobPluginInstall(PluginPath const &pluginPath, + const PluginInstallerStruct &installerStruct) + : + Job(PluginInstallation), + JobContextBase(installerStruct), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + // + // Init installer context + // + m_context.pluginFilePath = pluginPath; + m_context.pluginHandle = INVALID_HANDLE; + m_context.installationCompleted = false; + + m_context.installerTask = this; + // + // Create main installation tasks + // + AddTask(new PluginInstallTask(&m_context)); +} + +void JobPluginInstall::SendProgress() +{ + if (GetProgressFlag() && GetInstallerStruct().progressCallback != NULL) { + _D("Call Plugin install progressCallback"); + GetInstallerStruct().progressCallback(GetInstallerStruct().userParam, + GetProgressPercent(), + GetProgressDescription()); + } +} + +void JobPluginInstall::SendFinishedSuccess() +{ + PluginHandle handle = getNewPluginHandle(); + + if (handle != Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE && + isReadyToInstall()) + { + _D("Call Plugin install success finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + Jobs::Exceptions::Success); + } else { + _D("Call Plugin install waiting finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + Jobs::Exceptions::ErrorPluginInstallationFailed); + + _D("Installation: %s NOT possible", getFilePath().c_str()); + } +} + +void JobPluginInstall::SendFinishedFailure() +{ + _E("Error in plugin installation step: %d", m_exceptionCaught); + _E("Message: %s", m_exceptionMessage.c_str()); + + _D("Call Plugin install failure finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + m_exceptionCaught); +} + +void JobPluginInstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} +} //namespace Jobs +} //namespace PluginInstall diff --git a/src_mobile/jobs/plugin_install/job_plugin_install.h b/src_mobile/jobs/plugin_install/job_plugin_install.h new file mode 100644 index 0000000..a8d3386 --- /dev/null +++ b/src_mobile/jobs/plugin_install/job_plugin_install.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_plugin_install.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +namespace Jobs { +namespace PluginInstall { +class JobPluginInstall : + public Job, + public JobProgressBase, + public JobContextBase +{ + public: + static const WrtDB::DbPluginHandle INVALID_HANDLE = -1; + + public: + /** + * @brief Automaticaly sets installation process + */ + JobPluginInstall(PluginPath const &pluginPath, + const PluginInstallerStruct &installerStruct); + + WrtDB::DbPluginHandle getNewPluginHandle() const + { + return m_context.pluginHandle; + } + std::string getFilePath() const + { + return m_context.pluginFilePath.Fullpath(); + } + bool isReadyToInstall() const + { + return m_context.installationCompleted; + } + + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SaveExceptionData(const Jobs::JobExceptionBase &e); + + private: + PluginInstallerContext m_context; + + //TODO move it to base class of all jobs + //maybe separate JobBase class for this? + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; +}; +} //namespace Jobs +} //namespace PluginInstall + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ */ diff --git a/src_mobile/jobs/plugin_install/plugin_install_task.cpp b/src_mobile/jobs/plugin_install/plugin_install_task.cpp new file mode 100644 index 0000000..5f42730 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_install_task.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file install_one_task.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include "plugin_install_task.h" +#include "job_plugin_install.h" +#include "plugin_installer_errors.h" +#include "plugin_metafile_reader.h" +#include +//#include +#include +#include +#include +#include "plugin_objects.h" +#include +#include +#include + +using namespace WrtDB; + +#define SET_PLUGIN_INSTALL_PROGRESS(step, desc) \ + m_context->installerTask->UpdateProgress( \ + PluginInstallerContext::step, desc); + +#define DISABLE_IF_PLUGIN_WITHOUT_LIB() \ + if (m_pluginInfo.m_libraryName.empty()) \ + { \ + _W("Plugin without library."); \ + return; \ + } + +namespace Jobs { +namespace PluginInstall { +PluginInstallTask::PluginInstallTask(PluginInstallerContext *inCont) : + DPL::TaskDecl(this), + m_context(inCont), + m_pluginHandle(0), + m_dataFromConfigXML(true) +{ + AddStep(&PluginInstallTask::stepCheckPluginPath); + AddStep(&PluginInstallTask::stepParseConfigFile); + AddStep(&PluginInstallTask::stepFindPluginLibrary); + AddStep(&PluginInstallTask::stepCheckIfAlreadyInstalled); + AddStep(&PluginInstallTask::stepLoadPluginLibrary); + AddStep(&PluginInstallTask::stepRegisterPlugin); + AddStep(&PluginInstallTask::stepRegisterFeatures); + AddStep(&PluginInstallTask::stepRegisterPluginObjects); + AddStep(&PluginInstallTask::stepResolvePluginDependencies); + + SET_PLUGIN_INSTALL_PROGRESS(START, "Installation initialized"); +} + +PluginInstallTask::~PluginInstallTask() +{} + +void PluginInstallTask::stepCheckPluginPath() +{ + _D("Plugin installation: step CheckPluginPath"); + + if(!m_context->pluginFilePath.Exists()){ + ThrowMsg(Exceptions::PluginPathFailed, + "No such path"); + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_PATH, "Path to plugin verified"); +} + +void PluginInstallTask::stepParseConfigFile() +{ + _D("Plugin installation: step parse config file"); + + if(!m_context->pluginFilePath.getMetaFile().Exists()){ + m_dataFromConfigXML = false; + return; + } + + _D("Plugin Config file::%s", m_context->pluginFilePath.getMetaFile().Fullpath().c_str()); + + Try + { + PluginMetafileReader reader; + reader.initialize(m_context->pluginFilePath.getMetaFile()); + reader.read(m_pluginInfo); + + FOREACH(it, m_pluginInfo.m_featureContainer) + { + _D("Parsed feature : %s", it->m_name.c_str()); + FOREACH(devCap, it->m_deviceCapabilities) { + _D(" | DevCap : %s", (*devCap).c_str()); + } + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_PATH, "Config file analyzed"); + } + Catch(ValidationCore::ParserSchemaException::Base) + { + _E("Error during file processing %s", m_context->pluginFilePath.getMetaFile().Fullpath().c_str()); + ThrowMsg(Exceptions::PluginMetafileFailed, + "Metafile error"); + } +} + +void PluginInstallTask::stepFindPluginLibrary() +{ + if (m_dataFromConfigXML) { + return; + } + _D("Plugin installation: step find plugin library"); + _D("Plugin .so: %s", m_context->pluginFilePath.getLibraryName().c_str()); + m_pluginInfo.m_libraryName = m_context->pluginFilePath.getLibraryName(); +} + +void PluginInstallTask::stepCheckIfAlreadyInstalled() +{ + if (PluginDAO::isPluginInstalled(m_pluginInfo.m_libraryName)) { + ThrowMsg(Exceptions::PluginAlreadyInstalled, + "Plugin already installed"); + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_EXISTS_CHECK, "Check if plugin exist"); +} + +void PluginInstallTask::stepLoadPluginLibrary() +{ + _D("Plugin installation: step load library"); + + DISABLE_IF_PLUGIN_WITHOUT_LIB() + + _D("Loading plugin: %s", m_context->pluginFilePath.getLibraryName().c_str()); + + fprintf(stderr, " - Try to dlopen() : [%s] ", m_context->pluginFilePath.getLibraryPath().Fullpath().c_str()); + + void *dlHandle = dlopen( m_context->pluginFilePath.getLibraryPath().Fullpath().c_str(), RTLD_LAZY); + if (dlHandle == NULL) { + const char* error = (const char*)dlerror(); + fprintf(stderr, + "-> Failed!\n %s\n", + (error != NULL ? error : "unknown")); + _E("Failed to load plugin: %s. Reason: %s", + m_context->pluginFilePath.getLibraryName().c_str(), (error != NULL ? error : "unknown")); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + fprintf(stderr, "-> Done.\n"); + + const js_entity_definition_t *rawEntityList = NULL; + get_widget_entity_map_proc *getWidgetEntityMapProcPtr = NULL; + + getWidgetEntityMapProcPtr = + reinterpret_cast(dlsym(dlHandle, + PLUGIN_GET_CLASS_MAP_PROC_NAME)); + + if (getWidgetEntityMapProcPtr) { + rawEntityList = (*getWidgetEntityMapProcPtr)(); + } else { + rawEntityList = + static_cast(dlsym(dlHandle, + PLUGIN_CLASS_MAP_NAME)); + } + + if (rawEntityList == NULL) { + dlclose(dlHandle); + _E("Failed to read class name %s", m_context->pluginFilePath.getLibraryName().c_str()); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + if (!m_dataFromConfigXML) { + on_widget_init_proc *onWidgetInitProc = + reinterpret_cast( + dlsym(dlHandle, PLUGIN_WIDGET_INIT_PROC_NAME)); + + if (NULL == onWidgetInitProc) { + dlclose(dlHandle); + _E("Failed to read onWidgetInit symbol %s", m_context->pluginFilePath.getLibraryName().c_str()); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + // obtain feature -> dev-cap mapping + feature_mapping_interface_t mappingInterface = { NULL, NULL, NULL }; + (*onWidgetInitProc)(&mappingInterface); + + if (!mappingInterface.featGetter || !mappingInterface.release || + !mappingInterface.dcGetter) + { + _E("Failed to obtain mapping interface from .so"); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + feature_mapping_t* devcapMapping = mappingInterface.featGetter(); + + _D("Getting mapping from features to device capabilities"); + + for (size_t i = 0; i < devcapMapping->featuresCount; ++i) { + PluginMetafileData::Feature feature; + feature.m_name = devcapMapping->features[i].feature_name; + + _D("Feature: %s", feature.m_name.c_str()); + + const devcaps_t* dc = + mappingInterface.dcGetter( + devcapMapping, + devcapMapping->features[i]. + feature_name); + + if (dc) { + _D("devcaps count: %d", dc->devCapsCount); + + for (size_t j = 0; j < dc->devCapsCount; ++j) { + _D("devcap: %s", dc->deviceCaps[j]); + feature.m_deviceCapabilities.insert(dc->deviceCaps[j]); + } + } + + m_pluginInfo.m_featureContainer.insert(feature); + } + + mappingInterface.release(devcapMapping); + } + + m_libraryObjects = PluginObjectsPtr(new PluginObjects()); + const js_entity_definition_t *rawEntityListIterator = rawEntityList; + + _D("#####"); + _D("##### Plugin: %s supports new plugin API", + m_context->pluginFilePath.getLibraryName().c_str()); + _D("#####"); + + while (rawEntityListIterator->parent_name != NULL && + rawEntityListIterator->object_name != NULL) + { + _D("##### [%s]: ", rawEntityListIterator->object_name); + _D("##### Parent: %s", rawEntityListIterator->parent_name); + _D("#####"); + + m_libraryObjects->addObjects(rawEntityListIterator->parent_name, + rawEntityListIterator->object_name); + + ++rawEntityListIterator; + } + + // Unload library + if (dlclose(dlHandle) != 0) { + _E("Cannot close plugin handle"); + } else { + _D("Library is unloaded"); + } + + // Load export table + _D("Library successfuly loaded and parsed"); + + SET_PLUGIN_INSTALL_PROGRESS(LOADING_LIBRARY, "Library loaded and analyzed"); +} + +void PluginInstallTask::stepRegisterPlugin() +{ + _D("Plugin installation: step register Plugin"); + + m_pluginHandle = + PluginDAO::registerPlugin(m_pluginInfo, m_context->pluginFilePath.Fullpath()); + + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_PLUGIN, "Plugin registered"); +} + +void PluginInstallTask::stepRegisterFeatures() +{ + _D("Plugin installation: step register features"); + + FOREACH(it, m_pluginInfo.m_featureContainer) + { + _D("PluginHandle: %d", m_pluginHandle); + FeatureDAO::RegisterFeature(*it, m_pluginHandle); + } + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_FEATURES, "Features registered"); +} + +void PluginInstallTask::stepRegisterPluginObjects() +{ + _D("Plugin installation: step register objects"); + + DISABLE_IF_PLUGIN_WITHOUT_LIB() + + //register implemented objects + PluginObjects::ObjectsPtr objects = + m_libraryObjects->getImplementedObject(); + + FOREACH(it, *objects) + { + PluginDAO::registerPluginImplementedObject(*it, m_pluginHandle); + } + + //register requiredObjects + objects = m_libraryObjects->getDependentObjects(); + + FOREACH(it, *objects) + { + if (m_libraryObjects->hasObject(*it)) { + _D("Dependency from the same library. ignored"); + continue; + } + + PluginDAO::registerPluginRequiredObject(*it, m_pluginHandle); + } + + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_OBJECTS, "Plugin Objects registered"); +} + +void PluginInstallTask::stepResolvePluginDependencies() +{ + _D("Plugin installation: step resolve dependencies "); + + //DISABLE_IF_PLUGIN_WITHOUT_LIB + if (m_pluginInfo.m_libraryName.empty()) { + PluginDAO::setPluginInstallationStatus( + m_pluginHandle, + PluginDAO:: + INSTALLATION_COMPLETED); + //Installation completed + m_context->pluginHandle = m_pluginHandle; + m_context->installationCompleted = true; + _W("Plugin without library."); + return; + } + + PluginHandleSetPtr handles = PluginHandleSetPtr(new PluginHandleSet); + + DbPluginHandle handle = INVALID_PLUGIN_HANDLE; + + //register requiredObjects + FOREACH(it, *(m_libraryObjects->getDependentObjects())) + { + if (m_libraryObjects->hasObject(*it)) { + _D("Dependency from the same library. ignored"); + continue; + } + + handle = PluginDAO::getPluginHandleForImplementedObject(*it); + if (handle == INVALID_PLUGIN_HANDLE) { + _E("Library implementing: %s NOT FOUND", (*it).c_str()); + PluginDAO::setPluginInstallationStatus( + m_pluginHandle, + PluginDAO::INSTALLATION_WAITING); + return; + } + + handles->insert(handle); + } + + PluginDAO::registerPluginLibrariesDependencies(m_pluginHandle, handles); + + PluginDAO::setPluginInstallationStatus(m_pluginHandle, + PluginDAO::INSTALLATION_COMPLETED); + + //Installation completed + m_context->pluginHandle = m_pluginHandle; + m_context->installationCompleted = true; + + SET_PLUGIN_INSTALL_PROGRESS(RESOLVE_DEPENDENCIES, "Dependencies resolved"); +} + +#undef SET_PLUGIN_INSTALL_PROGRESS +} //namespace Jobs +} //namespace PluginInstall diff --git a/src_mobile/jobs/plugin_install/plugin_install_task.h b/src_mobile/jobs/plugin_install/plugin_install_task.h new file mode 100644 index 0000000..c7a5d8c --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_install_task.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file install.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALL_H_ +#define INSTALL_H_ + +//WRT INCLUDES +#include +#include "plugin_installer_context.h" +#include "plugin_objects.h" + +namespace Jobs { +namespace PluginInstall { +class PluginInstallTask : + public DPL::TaskDecl +{ + public: + PluginInstallTask(PluginInstallerContext *inCont); + virtual ~PluginInstallTask(); + + private: + //data + PluginInstallerContext *m_context; + + //PluginMetafile + WrtDB::PluginMetafileData m_pluginInfo; + + //Plugin LibraryObjects + PluginObjectsPtr m_libraryObjects; + + WrtDB::DbPluginHandle m_pluginHandle; + + bool m_dataFromConfigXML; + + //steps + void stepCheckPluginPath(); + void stepFindPluginLibrary(); + void stepParseConfigFile(); + void stepCheckIfAlreadyInstalled(); + void stepLoadPluginLibrary(); + void stepRegisterPlugin(); + void stepRegisterFeatures(); + void stepRegisterPluginObjects(); + void stepResolvePluginDependencies(); +}; +} //namespace Jobs +} //namespace PluginInstall + +#endif /* INSTALL_H_ */ diff --git a/src_mobile/jobs/plugin_install/plugin_installer_context.h b/src_mobile/jobs/plugin_install/plugin_installer_context.h new file mode 100644 index 0000000..1efd504 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_installer_context.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file plugin_installer_structs.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief Definition file of plugin installer tasks data structures + */ +#ifndef WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ +#define WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ + +#include +#include +#include +//#include + +using namespace WrtDB; + +namespace Jobs { +namespace PluginInstall { +class JobPluginInstall; +} +} + +struct PluginInstallerContext +{ + enum PluginInstallStep + { + START, + PLUGIN_PATH, + CONFIG_FILE, + PLUGIN_EXISTS_CHECK, + LOADING_LIBRARY, + REGISTER_PLUGIN, + REGISTER_FEATURES, + REGISTER_OBJECTS, + RESOLVE_DEPENDENCIES, + PLUGIN_INSTALL_END + }; + + PluginPath pluginFilePath; ///< plugin directory + PluginPath metaFilePath; + bool m_dataFromConfigXML; + WrtDB::DbPluginHandle pluginHandle; + // if this value is true the plugin model may be created + // if not plugin installation has failed from some reason + bool installationCompleted; + + //used to set installation progress + Jobs::PluginInstall::JobPluginInstall* installerTask; +}; +#endif // WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ diff --git a/src_mobile/jobs/plugin_install/plugin_installer_errors.h b/src_mobile/jobs/plugin_install/plugin_installer_errors.h new file mode 100644 index 0000000..11c7f88 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_installer_errors.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_installer_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +#ifndef \ + WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ +#define \ + WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ + +#include +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace PluginInstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) +DECLARE_JOB_EXCEPTION(Base, PluginPathFailed, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginMetafileFailed, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginAlreadyInstalled, + ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginLibraryError, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, InstallationWaitingError, + ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, UnknownError, ErrorUnknown) +} //namespace +} //namespace +} //namespace + +#endif +//WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ + diff --git a/src_mobile/jobs/plugin_install/plugin_installer_struct.h b/src_mobile/jobs/plugin_install/plugin_installer_struct.h new file mode 100644 index 0000000..c253897 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_installer_struct.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_installer_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ + +#include +#include + +//Plugin Installer typedefs +typedef void (*PluginInstallerFinishedCallback)( + void *userParam, + Jobs::Exceptions::Type); + +//installer progress +typedef void (*PluginInstallerProgressCallback)( + void *userParam, + ProgressPercent percent, + const ProgressDescription &description); + +//Plugin Installetion Struct +typedef Jobs::JobCallbacksBase +PluginInstallerStruct; + +#endif // WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ diff --git a/src_mobile/jobs/plugin_install/plugin_metafile_reader.cpp b/src_mobile/jobs/plugin_install/plugin_metafile_reader.cpp new file mode 100644 index 0000000..c7df516 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_metafile_reader.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_metafile_reader.cpp + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#include "plugin_metafile_reader.h" +#include + +using namespace WrtDB; + +namespace { +const std::string XML_NAMESPACE = ""; + +const std::string TOKEN_LIBRARY_NAME = "library-name"; +const std::string TOKEN_API_FEATURE = "api-feature"; +const std::string TOKEN_NAME = "name"; +const std::string TOKEN_DEVICECAPABILITY = "device-capability"; +} + +PluginMetafileReader::PluginMetafileReader() : m_parserSchema(this) +{ + m_parserSchema.addEndTagCallback( + TOKEN_LIBRARY_NAME, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndLibraryName); + + m_parserSchema.addEndTagCallback( + TOKEN_API_FEATURE, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndApiFeature); + + m_parserSchema.addEndTagCallback( + TOKEN_NAME, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndName); + + m_parserSchema.addEndTagCallback( + TOKEN_DEVICECAPABILITY, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndDeviceCapability); +} + +void PluginMetafileReader::initialize(const PluginPath &filename) +{ + m_parserSchema.initialize(filename.Fullpath(), + true, + ValidationCore::SaxReader::VALIDATION_DTD, + std::string()); +} + +void PluginMetafileReader::read(WrtDB::PluginMetafileData &data) +{ + m_parserSchema.read(data); +} + +void PluginMetafileReader::blankFunction(PluginMetafileData & /* data */) +{} + +void PluginMetafileReader::tokenEndLibraryName(PluginMetafileData &data) +{ + data.m_libraryName = m_parserSchema.getText(); +} + +void PluginMetafileReader::tokenEndApiFeature(PluginMetafileData &data) +{ + data.m_featureContainer.insert(m_feature); + m_feature.m_deviceCapabilities.clear(); +} + +void PluginMetafileReader::tokenEndName(PluginMetafileData & /* data */) +{ + m_feature.m_name = m_parserSchema.getText(); +} + +void PluginMetafileReader::tokenEndDeviceCapability(PluginMetafileData& /*data*/) +{ + m_feature.m_deviceCapabilities.insert(m_parserSchema.getText()); +} + diff --git a/src_mobile/jobs/plugin_install/plugin_metafile_reader.h b/src_mobile/jobs/plugin_install/plugin_metafile_reader.h new file mode 100644 index 0000000..a3a3068 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_metafile_reader.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_metafile_reader.h + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_METAFILE_READER_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_METAFILE_READER_H_ + +#include +#include + +class PluginPath; + +class PluginMetafileReader +{ + public: + PluginMetafileReader(); + + void initialize(const PluginPath &filename); + + void read(WrtDB::PluginMetafileData &data); + + private: + void blankFunction(WrtDB::PluginMetafileData &data); + + void tokenEndLibraryName(WrtDB::PluginMetafileData &data); + void tokenEndApiFeature(WrtDB::PluginMetafileData &data); + void tokenEndName(WrtDB::PluginMetafileData &data); + void tokenEndDeviceCapability(WrtDB::PluginMetafileData &data); + + WrtDB::PluginMetafileData::Feature m_feature; + + ValidationCore::ParserSchema m_parserSchema; +}; + +#endif diff --git a/src_mobile/jobs/plugin_install/plugin_objects.cpp b/src_mobile/jobs/plugin_install/plugin_objects.cpp new file mode 100644 index 0000000..9235f47 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_objects.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_objects.h + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ +#include +#include +#include "plugin_objects.h" + +namespace { +const char* SEPARATOR = "."; +const std::string GLOBAL_OBJECT_NAME = "GLOBAL_OBJECT"; + +std::string normalizeName(const std::string& objectName) +{ + if (objectName.empty()) { + _E("Normalize name, name size is 0"); + return objectName; + } + + if (!objectName.compare(0, GLOBAL_OBJECT_NAME.size(), + GLOBAL_OBJECT_NAME)) + { + return objectName; + } + + //each object in storage has name started from $GLOBAL_OBJECT_NAME$ + return GLOBAL_OBJECT_NAME + std::string(SEPARATOR) + objectName; +} + +std::string normalizeName(const std::string& objectName, + const std::string& parentName) +{ + if (objectName.empty() || parentName.empty()) { + _E("Normalize name, name size or parent name size is 0"); + return std::string(); + } + + std::string normalizedName; + normalizedName = normalizeName(parentName) + + std::string(SEPARATOR) + objectName; + + return normalizedName; +} +} + +PluginObjects::PluginObjects() +{ + m_implemented = ObjectsPtr(new Objects()); + m_dependent = ObjectsPtr(new Objects()); +} + +PluginObjects::ObjectsPtr PluginObjects::getImplementedObject() const +{ + return m_implemented; +} + +PluginObjects::ObjectsPtr PluginObjects::getDependentObjects() const +{ + return m_dependent; +} + +void PluginObjects::addObjects(const std::string& parentName, + const std::string& name) +{ + addImplementedObject(normalizeName(name, parentName)); + addDependentObject(normalizeName(parentName)); +} + +void PluginObjects::addDependentObject(const std::string& value) +{ + if (!value.compare(GLOBAL_OBJECT_NAME)) { + //dont add dependency to GLOBAL_OBJECT + return; + } + m_dependent->insert(value); +} + +bool PluginObjects::hasObject(const std::string& name) const +{ + return m_implemented->find(name) != m_implemented->end(); +} + +void PluginObjects::addImplementedObject(const std::string& value) +{ + m_implemented->insert(value); +} diff --git a/src_mobile/jobs/plugin_install/plugin_objects.h b/src_mobile/jobs/plugin_install/plugin_objects.h new file mode 100644 index 0000000..0b27a14 --- /dev/null +++ b/src_mobile/jobs/plugin_install/plugin_objects.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_objects.h + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_OBJECTS_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_OBJECTS_H_ + +#include +#include +#include +#include + +#include +//TODO TO BE MOVED SOMEWHERE ELSE +// AS OTHER MODULES (LIKE DAO) USE IT + +class PluginObjects : public WrtDB::PluginObjectsDAO +{ + public: + explicit PluginObjects(); + + //getters for objects from library + ObjectsPtr getImplementedObject() const; + ObjectsPtr getDependentObjects() const; + + //add object declaration + void addObjects(const std::string& parentName, + const std::string& name); + + //check if library implemements object given as name + bool hasObject(const std::string& name) const; + + private: + void addImplementedObject(const std::string& value); + void addDependentObject(const std::string& value); +}; + +typedef std::shared_ptr PluginObjectsPtr; + +#endif diff --git a/src_mobile/jobs/widget_install/ace_registration.cpp b/src_mobile/jobs/widget_install/ace_registration.cpp new file mode 100644 index 0000000..6596d47 --- /dev/null +++ b/src_mobile/jobs/widget_install/ace_registration.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ace_registration.cpp + * @author Andrzej Surdej (a.surdej@gmail.com) + * @version 1.0 + * @brief Translate structures to ace api - implementation file + */ + +#include +#include +#include + +#include + +namespace { +char* toAceString(const DPL::OptionalString& os) +{ + if (!os.IsNull()) { + return strdup(DPL::ToUTF8String(*os).c_str()); + } else { + return NULL; + } +} + +char* toAceString(const std::string& str) +{ + if (!str.empty()) { + return strdup(str.c_str()); + } else { + return NULL; + } +} +} //anonymous namespace + +namespace AceApi { +bool registerAceWidget(const WrtDB::DbWidgetHandle& widgetHandle, + const WrtDB::WidgetRegisterInfo& widgetConfig, + const WrtDB::WidgetCertificateDataList& certList) +{ + _D("Updating Ace database"); + struct widget_info wi; + + switch (widgetConfig.webAppType.appType) { + case WrtDB::APP_TYPE_TIZENWEBAPP: + wi.type = Tizen; + break; + default: + _E("Unknown application type"); + return false; + } + + wi.id = toAceString(widgetConfig.configInfo.widget_id); + wi.version = toAceString(widgetConfig.configInfo.version); + wi.author = toAceString(widgetConfig.configInfo.authorName); + wi.shareHerf = strdup(widgetConfig.shareHref.c_str()); + _D("Basic data converted. Certificates begin."); + + //one more element for NULL termination + _D("Found: %d certificates", certList.size()); + ace_certificate_data** certData = new ace_certificate_data * + [certList.size() + 1]; + certData[certList.size()] = NULL; // last element set to NULL + + int i = 0; + FOREACH(it, certList) + { + certData[i] = new ace_certificate_data; + switch (it->owner) { + case WrtDB::WidgetCertificateData::AUTHOR: + certData[i]->owner = AUTHOR; + break; + case WrtDB::WidgetCertificateData::DISTRIBUTOR: + certData[i]->owner = DISTRIBUTOR; + break; + default: + _D("Unknown owner type of cert"); + certData[i]->owner = UNKNOWN; + break; + } + switch (it->type) { + case WrtDB::WidgetCertificateData::ENDENTITY: + certData[i]->type = ENDENTITY; + break; + case WrtDB::WidgetCertificateData::ROOT: + certData[i]->type = ROOT; + break; + default: + _E("Unknown type of cert"); + certData[i]->type = ENDENTITY; + break; + } + certData[i]->chain_id = it->chainId; + + certData[i]->md5_fp = toAceString(it->strMD5Fingerprint); + certData[i]->sha1_fp = toAceString(it->strSHA1Fingerprint); + certData[i]->common_name = + toAceString(DPL::ToUTF8String(it->strCommonName)); + ++i; + } + + _D("Registerign widget in ace"); + ace_return_t retval = ace_register_widget( + static_cast(widgetHandle), &wi, certData); + + //clean up - WidgetInfo + free(wi.author); + free(wi.id); + free(wi.shareHerf); + free(wi.version); + + //free cert list + i = 0; + while (certData[i] != NULL) { + free(certData[i]->common_name); + free(certData[i]->md5_fp); + free(certData[i]->sha1_fp); + delete certData[i]; + ++i; + } + delete[] certData; + return retval == ACE_OK; +} +bool registerAceWidgetFromDB(const WrtDB::DbWidgetHandle& widgetHandle) +{ + using namespace WrtDB; + _D("Updating Ace database from Widget DB"); + struct widget_info wi; + DPL::OptionalString os; + WrtDB::WidgetCertificateDataList certList; + + Try { + WidgetDAOReadOnly dao(widgetHandle); + + WidgetType type = dao.getWidgetType(); + if (type == WrtDB::APP_TYPE_TIZENWEBAPP) { + wi.type = Tizen; + } else { + _E("Unknown application type"); + return false; + } + + wi.id = toAceString(dao.getGUID()); + wi.version = toAceString(dao.getVersion()); + wi.author = toAceString(dao.getAuthorName()); + wi.shareHerf = strdup(dao.getShareHref().c_str()); + _D("Basic data converted. Certificates begin."); + certList = dao.getCertificateDataList(); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + _E("Widget does not exist"); + return false; + } + + //one more element for NULL termination + _D("Found: %d certificates", certList.size()); + ace_certificate_data** certData = new ace_certificate_data * + [certList.size() + 1]; + certData[certList.size()] = NULL; // last element set to NULL + + int i = 0; + FOREACH(it, certList) + { + certData[i] = new ace_certificate_data; + switch (it->owner) { + case WrtDB::WidgetCertificateData::AUTHOR: + certData[i]->owner = AUTHOR; + break; + case WrtDB::WidgetCertificateData::DISTRIBUTOR: + certData[i]->owner = DISTRIBUTOR; + break; + default: + _D("Unknown owner type of cert"); + certData[i]->owner = UNKNOWN; + break; + } + switch (it->type) { + case WrtDB::WidgetCertificateData::ENDENTITY: + certData[i]->type = ENDENTITY; + break; + case WrtDB::WidgetCertificateData::ROOT: + certData[i]->type = ROOT; + break; + default: + _E("Unknown type of cert"); + certData[i]->type = ENDENTITY; + break; + } + certData[i]->chain_id = it->chainId; + + certData[i]->md5_fp = toAceString(it->strMD5Fingerprint); + certData[i]->sha1_fp = toAceString(it->strSHA1Fingerprint); + certData[i]->common_name = + toAceString(DPL::ToUTF8String(it->strCommonName)); + ++i; + } + + _D("Registerign widget in ace"); + ace_return_t retval = ace_register_widget( + static_cast(widgetHandle), &wi, certData); + + //clean up - WidgetInfo + free(wi.author); + free(wi.id); + free(wi.shareHerf); + free(wi.version); + + //free cert list + i = 0; + while (certData[i] != NULL) { + free(certData[i]->common_name); + free(certData[i]->md5_fp); + free(certData[i]->sha1_fp); + delete certData[i]; + ++i; + } + delete[] certData; + return retval == ACE_OK; +} +} diff --git a/src_mobile/jobs/widget_install/ace_registration.h b/src_mobile/jobs/widget_install/ace_registration.h new file mode 100644 index 0000000..f98c3ce --- /dev/null +++ b/src_mobile/jobs/widget_install/ace_registration.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ace_registration.h + * @author Andrzej Surdej (a.surdej@gmail.com) + * @version 1.0 + * @brief Translate structures to ace api - header file + */ +#ifndef WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ +#define WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ + +#include + +namespace AceApi { +bool registerAceWidget(const WrtDB::DbWidgetHandle& widgetHandle, + const WrtDB::WidgetRegisterInfo& widgetConfig, + const WrtDB::WidgetCertificateDataList& certList); +bool registerAceWidgetFromDB(const WrtDB::DbWidgetHandle& widgetHandle); +} + +#endif /* WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ */ + diff --git a/src_mobile/jobs/widget_install/directory_api.cpp b/src_mobile/jobs/widget_install/directory_api.cpp new file mode 100644 index 0000000..dd8aa07 --- /dev/null +++ b/src_mobile/jobs/widget_install/directory_api.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file directory_api.cpp + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief directory api - implementation file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DirectoryApi { +bool DirectoryCopy(std::string source, std::string dest) +{ + DIR* dir = opendir(source.c_str()); + if (NULL == dir) { + return false; + } + + struct dirent dEntry; + struct dirent *dEntryResult; + int return_code; + + do { + struct stat statInfo; + return_code = readdir_r(dir, &dEntry, &dEntryResult); + if (dEntryResult != NULL && return_code == 0) { + std::string fileName = dEntry.d_name; + std::string fullName = source + "/" + fileName; + + if (stat(fullName.c_str(), &statInfo) != 0) { + closedir(dir); + return false; + } + + if (S_ISDIR(statInfo.st_mode)) { + if (("." == fileName) || (".." == fileName)) { + continue; + } + std::string destFolder = dest + "/" + fileName; + WrtUtilMakeDir(destFolder); + + if (!DirectoryCopy(fullName, destFolder)) { + closedir(dir); + return false; + } + } + + std::string destFile = dest + "/" + fileName; + std::ifstream infile(fullName); + std::ofstream outfile(destFile); + outfile << infile.rdbuf(); + outfile.close(); + infile.close(); + + errno = 0; + if (-1 == TEMP_FAILURE_RETRY(chown(destFile.c_str(), + statInfo.st_uid, + statInfo.st_gid))) { + int error = errno; + _E("Failed to change owner [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } while (dEntryResult != NULL && return_code == 0); + closedir(dir); + return true; +} +} diff --git a/src_mobile/jobs/widget_install/directory_api.h b/src_mobile/jobs/widget_install/directory_api.h new file mode 100644 index 0000000..1632528 --- /dev/null +++ b/src_mobile/jobs/widget_install/directory_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file directory_api.h + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief directory api - header file + */ +#ifndef WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ +#define WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ + +#include + +namespace DirectoryApi { +bool DirectoryCopy(std::string source, std::string dest); +} + +#endif /* WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ */ + diff --git a/src_mobile/jobs/widget_install/job_widget_install.cpp b/src_mobile/jobs/widget_install/job_widget_install.cpp new file mode 100644 index 0000000..58e2068 --- /dev/null +++ b/src_mobile/jobs/widget_install/job_widget_install.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file job_widget_install.cpp + * @author Radoslaw Wicik r.wicik@samsung.com + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for main installer task + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace WrtDB; +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { + +JobWidgetInstall::JobWidgetInstall( + std::string const &widgetPath, + std::string const &tzPkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct) : + Job(UnknownInstallation), + JobContextBase(installerStruct), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + m_installerContext.mode = m_jobStruct.m_installMode; + m_installerContext.requestedPath = widgetPath; + m_jobStruct.pkgmgrInterface->setPkgname(tzPkgId); + + //start configuration of installation + AddTask(new TaskConfiguration(m_installerContext)); + + // Init installer context + m_installerContext.installStep = InstallerContext::INSTALL_START; + m_installerContext.job = this; + + m_installerContext.callerPkgId + = DPL::FromUTF8String(m_jobStruct.pkgmgrInterface->getCallerId()); + _D("Caller Package Id : %s", DPL::ToUTF8String(m_installerContext.callerPkgId).c_str()); +} + +void JobWidgetInstall::appendNewInstallationTaskList() +{ + _D("Configure installation succeeded"); + m_installerContext.job->SetProgressFlag(true); + + AddTask(new TaskRecovery(m_installerContext)); + + AddTask(new TaskFileManipulation(m_installerContext)); + AddTask(new TaskProcessConfig(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + AddTask(new TaskPrepareFiles(m_installerContext)); + } + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) + { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::appendUpdateInstallationTaskList() +{ + _D("Configure installation updated"); + _D("Widget Update"); + m_installerContext.job->SetProgressFlag(true); + + if (m_installerContext.mode.command == + InstallMode::Command::REINSTALL) + { + AddTask(new TaskPrepareReinstall(m_installerContext)); + } + + if (m_installerContext.mode.extension != + InstallMode::ExtensionType::DIR) { + AddTask(new TaskUpdateFiles(m_installerContext)); + AddTask(new TaskFileManipulation(m_installerContext)); + } + + AddTask(new TaskProcessConfig(m_installerContext)); + + if (m_installerContext.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + AddTask(new TaskPrepareFiles(m_installerContext)); + } + + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) + { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + //TODO: remove widgetHandle from this task and move before database task + // by now widget handle is needed in ace check + // Any error in acecheck while update will break widget + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskRemoveBackupFiles(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::SendProgress() +{ + using namespace PackageManager; + if (GetProgressFlag() != false) { + if (GetInstallerStruct().progressCallback != NULL) { + // send progress signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->sendProgress(GetProgressPercent()); + + _D("Call widget install progressCallback"); + GetInstallerStruct().progressCallback( + GetInstallerStruct().userParam, + GetProgressPercent(), + GetProgressDescription()); + } + } +} + +void JobWidgetInstall::SendProgressIconPath(const std::string &path) +{ + using namespace PackageManager; + if (GetProgressFlag() != false) { + if (GetInstallerStruct().progressCallback != NULL) { + // send progress signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->sendIconPath(path); + } + } +} + +void JobWidgetInstall::SendFinishedSuccess() +{ + using namespace PackageManager; + // TODO : sync should move to separate task. + sync(); + + if (INSTALL_LOCATION_TYPE_PREFER_EXTERNAL == m_installerContext.locationType) { + if (m_installerContext.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().postUpgrade(true); + } else { + WidgetInstallToExtSingleton::Instance().postInstallation(true); + } + WidgetInstallToExtSingleton::Instance().deinitialize(); + } + + // remove widget install information file + unlink(m_installerContext.installInfo.c_str()); + + //inform widget info + JobWidgetInstall::displayWidgetInfo(); + + TizenAppId& tizenId = m_installerContext.widgetConfig.tzAppid; + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget install successfinishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + DPL::ToUTF8String( + tizenId), Jobs::Exceptions::Success); +} + +void JobWidgetInstall::SendFinishedFailure() +{ + using namespace PackageManager; + // remove widget install information file + unlink(m_installerContext.installInfo.c_str()); + + _E("Error number: %d", m_exceptionCaught); + _E("Message: %s", m_exceptionMessage.c_str()); + TizenAppId & tizenId = m_installerContext.widgetConfig.tzAppid; + + _D("Call widget install failure finishedCallback"); + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + DPL::ToUTF8String( + tizenId), m_exceptionCaught); +} + +void JobWidgetInstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} + +void JobWidgetInstall::displayWidgetInfo() +{ + WidgetDAOReadOnly dao(m_installerContext.widgetConfig.tzAppid); + + std::ostringstream out; + WidgetLocalizedInfo localizedInfo = + W3CFileLocalization::getLocalizedInfo(dao.getTzAppId()); + + out << std::endl << + "===================================== INSTALLED WIDGET INFO =========" \ + "============================"; + out << std::endl << "Name: " << localizedInfo.name; + out << std::endl << "AppId: " << dao.getTzAppId(); + WidgetSize size = dao.getPreferredSize(); + out << std::endl << "Width: " << size.width; + out << std::endl << "Height: " << size.height; + out << std::endl << "Start File: " << + W3CFileLocalization::getStartFile(dao.getTzAppId()); + out << std::endl << "Version: " << dao.getVersion(); + out << std::endl << "Licence: " << + localizedInfo.license; + out << std::endl << "Licence Href: " << + localizedInfo.licenseHref; + out << std::endl << "Description: " << + localizedInfo.description; + out << std::endl << "Widget Id: " << dao.getGUID(); + out << std::endl << "Widget recognized: " << dao.isRecognized(); + out << std::endl << "Widget distributor signed: " << + dao.isDistributorSigned(); + out << std::endl << "Widget trusted: " << dao.isTrusted(); + + OptionalWidgetIcon icon = W3CFileLocalization::getIcon(dao.getTzAppId()); + DPL::OptionalString iconSrc = + !!icon ? icon->src : DPL::OptionalString::Null; + out << std::endl << "Icon: " << iconSrc; + + out << std::endl << "Preferences:"; + { + PropertyDAOReadOnly::WidgetPreferenceList list = dao.getPropertyList(); + FOREACH(it, list) + { + out << std::endl << " Key: " << + it->key_name; + out << std::endl << " Readonly: " << + it->readonly; + } + } + + out << std::endl << "Features:"; + { + WidgetFeatureSet list = dao.getFeaturesList(); + FOREACH(it, list) + { + out << std::endl << " Name: " << it->name; + } + } + + out << std::endl; + + _D("%s", out.str().c_str()); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/job_widget_install.h b/src_mobile/jobs/widget_install/job_widget_install.h new file mode 100644 index 0000000..a87337d --- /dev/null +++ b/src_mobile/jobs/widget_install/job_widget_install.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_widget_install.h + * @author Radoslaw Wicik r.wicik@samsung.com + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for main installer task + */ +#ifndef WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ + +#include +#include +#include +#include "widget_installer_struct.h" +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { + +typedef JobProgressBase InstallerBase; +typedef JobContextBase WidgetInstallationBase; + +class JobWidgetInstall : + public Job, + public InstallerBase, + public WidgetInstallationBase + +{ + private: + InstallerContext m_installerContext; + + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; + + public: + /** + * @brief Automaticaly sets installation process + */ + JobWidgetInstall(std::string const & widgetPath, + std::string const &tzPkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct &installerStruct); + + //overrides + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SendProgressIconPath(const std::string &path); + + void SaveExceptionData(const Jobs::JobExceptionBase&); + void displayWidgetInfo(); + + //execution paths + void appendNewInstallationTaskList(); + void appendUpdateInstallationTaskList(); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ diff --git a/src_mobile/jobs/widget_install/languages.def b/src_mobile/jobs/widget_install/languages.def new file mode 100644 index 0000000..e9439b6 --- /dev/null +++ b/src_mobile/jobs/widget_install/languages.def @@ -0,0 +1,15 @@ +ADD(de, de-de) +ADD(el, el-gr) +ADD(en, en-us) +ADD(es, es-es) +ADD(fr, fr-fr) +ADD(it, it-it) +ADD(ja, ja-jp) +ADD(ko, ko-kr) +ADD(nl, nl-nl) +ADD(pt, pt-pt) +ADD(ru, ru-ru) +ADD(tr, tr-tr) +ADD(zh, zh-cn) +ADD(zh, zh-hk) +ADD(zh, zh-tw) diff --git a/src_mobile/jobs/widget_install/manifest.cpp b/src_mobile/jobs/widget_install/manifest.cpp new file mode 100644 index 0000000..1e02cfa --- /dev/null +++ b/src_mobile/jobs/widget_install/manifest.cpp @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file manifest.cpp + * @author Mariusz Domanski (m.domanski@samsung.com) + */ + +#include "manifest.h" +#include "libxml_utils.h" +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +void writeElement(xmlTextWriterPtr writer, const char * name, DPL::String body) +{ + int state = xmlTextWriterWriteElement(writer, BAD_CAST name, + BAD_CAST DPL::ToUTF8String( + body).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteElement failed"); + } +} + +void writeText(xmlTextWriterPtr writer, DPL::String text) +{ + int state = xmlTextWriterWriteString(writer, + BAD_CAST DPL::ToUTF8String(text).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteText failed"); + } +} + +void writeElement(xmlTextWriterPtr writer, const char * name, const char * body) +{ + int state = xmlTextWriterWriteElement(writer, BAD_CAST name, BAD_CAST body); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteElement failed"); + } +} + +void writeElementWithOneAttribute(xmlTextWriterPtr writer, + const char * name, + DPL::String body, + const char * nameAttr, + DPL::String bodyAttr, + bool condition = true) +{ + startElement(writer, name); + writeAttribute(writer, nameAttr, bodyAttr, condition); + writeText(writer, body); + endElement(writer); +} + +void startElement(xmlTextWriterPtr writer, const char * name) +{ + int state = xmlTextWriterStartElement(writer, BAD_CAST name); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterStartElement failed"); + } +} + +void endElement(xmlTextWriterPtr writer) +{ + int state = xmlTextWriterEndElement(writer); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterEndElement failed"); + } +} + +void writeAttribute(xmlTextWriterPtr writer, + const char * name, + DPL::String body, + bool condition = true) +{ + if (!condition) { + return; + } + int state = xmlTextWriterWriteAttribute(writer, BAD_CAST name, + BAD_CAST DPL::ToUTF8String( + body).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, + "xmlTextWriterWriteAttribute failed"); + } +} + +void writeAttribute(xmlTextWriterPtr writer, + const char * name, + const char * body, + bool condition = true) +{ + if (!condition) { + return; + } + int state = xmlTextWriterWriteAttribute(writer, + BAD_CAST name, + BAD_CAST body); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, + "xmlTextWriterWriteAttribute failed"); + } +} + +void Manifest::generate(DPL::String filename) +{ + xmlTextWriterPtr writer; + int state; + + //compression set to 0 + writer = xmlNewTextWriterFilename(DPL::ToUTF8String(filename).c_str(), 0); + + if (writer == NULL) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlNewTextWriterFilename failed"); + } + state = xmlTextWriterSetIndent(writer, 1); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterSetIndent failed"); + } + + state = xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterStartDocument failed"); + } + this->serialize(writer); + state = xmlTextWriterEndDocument(writer); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterEndDocument failed"); + } + if (writer != NULL) { + xmlFreeTextWriter(writer); + writer = NULL; + } +} + +void Manifest::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "manifest"); + { + writeAttribute(writer, "xmlns", "http://tizen.org/ns/packages"); + writeAttribute(writer, "package", this->package); + writeAttribute(writer, "type", this->type); + writeAttribute(writer, "version", this->version); + if (!this->installLocation.IsNull()) { + writeAttribute(writer, "install-location", (*this->installLocation)); + } + writeAttribute(writer, "storeclient-id", this->storeClientId, + !this->storeClientId.empty()); + + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", l->getString(), + "xml:lang", l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), + "xml:lang", i->getLang(), i->hasLang()); + } + FOREACH(a, this->author) + { + a->serialize(writer); + } + FOREACH(d, this->description) + { + writeElementWithOneAttribute(writer, "description", d->getString(), + "xml:lang", d->getLang(), d->hasLang()); + } + //FOREACH(c, this->compatibility) { c->serialize(writer); } + //FOREACH(d, this->deviceProfile) { d->serialize(writer); } + FOREACH(s, this->serviceApplication) { + s->serialize(writer); + } + FOREACH(u, this->uiApplication) { + u->serialize(writer); + } + FOREACH(i, this->imeApplication) { + i->serialize(writer); + } + //FOREACH(f, this->font) { f->serialize(writer); } + FOREACH(l, this->livebox) { + l->serialize(writer); + } + FOREACH(acc, this->account) + { + acc->serialize(writer); + } + + if (!this->privileges.isEmpty()) { + this->privileges.serialize(writer); + } + } + endElement(writer); +} + +void Author::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "author"); + writeAttribute(writer, "email", this->email, !this->email.empty()); + writeAttribute(writer, "href", this->href, !this->href.empty()); + writeAttribute(writer, "xml:lang", this->lang, !this->lang.empty()); + writeText(writer, body); + endElement(writer); +} + +void ServiceApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "service-application"); + writeAttribute(writer, "appid", this->appid); + writeAttribute(writer, "auto-restart", + (!this->autoRestart.IsNull() && + (*this->autoRestart)) ? "true" : + "false"); + writeAttribute(writer, "exec", this->exec); + writeAttribute(writer, "on-boot", (!this->onBoot.IsNull() && + (*this->onBoot)) ? "true" : "false"); + writeAttribute(writer, "type", this->type); + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", + l->getString(), "xml:lang", + l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), "xml:lang", + i->getLang(), i->hasLang()); + } + FOREACH(a, this->appControl) + { + a->serialize(writer); + } + endElement(writer); +} + +void UiApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "ui-application"); + writeAttribute(writer, "appid", this->appid); + writeAttribute(writer, "exec", this->exec); + if (!this->multiple.IsNull()) { + writeAttribute(writer, "multiple", (*this->multiple) ? "true" : "false"); + } + if (!this->nodisplay.IsNull()) { + writeAttribute(writer, + "nodisplay", + (*this->nodisplay) ? "true" : "false"); + } + if (!this->taskmanage.IsNull()) { + writeAttribute(writer, + "taskmanage", + (*this->taskmanage) ? "true" : "false"); + } + writeAttribute(writer, "type", this->type); + writeAttribute(writer, "extraid", this->extraid); + if (!this->categories.IsNull()) { + writeAttribute(writer, "categories", (*this->categories)); + } + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", + l->getString(), "xml:lang", + l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), "xml:lang", + i->getLang(), i->hasLang()); + } + FOREACH(a, this->appControl) + { + a->serialize(writer); + } + FOREACH(c, this->appCategory) + { + startElement(writer, "category"); + writeAttribute(writer, "name", *c); + endElement(writer); + } + FOREACH(m, this->metadata) { + m->serialize(writer); + } + endElement(writer); +} + +void ImeApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "ime-application"); + writeAttribute(writer, "appid", this->appid); + writeAttribute(writer, "exec", this->exec); + if (!this->multiple.IsNull()) { + writeAttribute(writer, "multiple", (*this->multiple) ? "true" : "false"); + } + if (!this->nodisplay.IsNull()) { + writeAttribute(writer, + "nodisplay", + (*this->nodisplay) ? "true" : "false"); + } + writeAttribute(writer, "type", this->type); + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", + l->getString(), "xml:lang", + l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), "xml:lang", + i->getLang(), i->hasLang()); + } + endElement(writer); +} + +void AppControl::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "app-control"); + FOREACH(o, this->operation) + { + startElement(writer, "operation"); + writeAttribute(writer, "name", *o); + endElement(writer); + } + FOREACH(u, this->uri) + { + startElement(writer, "uri"); + writeAttribute(writer, "name", *u); + endElement(writer); + } + FOREACH(m, this->mime) + { + startElement(writer, "mime"); + writeAttribute(writer, "name", *m); + endElement(writer); + } + endElement(writer); +} + +void LiveBox::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "livebox"); + if (!this->liveboxId.empty()) { + writeAttribute(writer, "appid", this->liveboxId); + } + + if (!this->primary.empty()) { + writeAttribute(writer, "primary", this->primary); + } + + if (!this->updatePeriod.empty()) { + writeAttribute(writer, "period", this->updatePeriod); + } + + writeAttribute(writer, "abi", "html"); + writeAttribute(writer, "network", "true"); + writeAttribute(writer, "nodisplay", "false"); + + if (!this->label.empty()) { + int defaultLabelChk = 0; + FOREACH(m, this->label) + { + std::pair boxLabel = *m; + startElement(writer, "label"); + if (!boxLabel.first.empty()) { + writeAttribute(writer, "xml:lang", boxLabel.first); + } else { + defaultLabelChk++; + } + writeText(writer, boxLabel.second); + endElement(writer); + } + if(!defaultLabelChk) { + startElement(writer, "label"); + writeText(writer, DPL::FromUTF8String("NO NAME")); + endElement(writer); + } + } + if (!this->icon.empty()) { + startElement(writer, "icon"); + writeText(writer, this->icon); + endElement(writer); + } + + if (!this->autoLaunch.empty()) { + startElement(writer, "launch"); + writeText(writer, this->autoLaunch); + endElement(writer); + } + + if (!this->box.boxSrc.empty() && + !this->box.boxMouseEvent.empty() && + !this->box.boxSize.empty()) + { + startElement(writer, "box"); + writeAttribute(writer, "type", "buffer"); + writeAttribute(writer, "mouse_event", this->box.boxMouseEvent); + writeAttribute(writer, "touch_effect", this->box.boxTouchEffect); + + FOREACH(it, this->box.boxSize) + { + startElement(writer, "size"); + if (!(*it).m_preview.empty()) { + writeAttribute(writer, "preview", (*it).m_preview); + } + if (!(*it).m_useDecoration.empty()) { + writeAttribute(writer, "need_frame", (*it).m_useDecoration); + } else { + // default value of use-decoration is "true" + writeAttribute(writer, "need_frame", DPL::String(L"true")); + } + + writeText(writer, (*it).m_size); + endElement(writer); + } + + startElement(writer, "script"); + writeAttribute(writer, "src", this->box.boxSrc); + endElement(writer); + + endElement(writer); + + if (!this->box.pdSrc.empty() && + !this->box.pdWidth.empty() && + !this->box.pdHeight.empty()) + { + startElement(writer, "pd"); + writeAttribute(writer, "type", "buffer"); + + startElement(writer, "size"); + DPL::String pdSize = this->box.pdWidth + DPL::String(L"x") + + this->box.pdHeight; + writeText(writer, pdSize); + endElement(writer); + + startElement(writer, "script"); + writeAttribute(writer, "src", this->box.pdSrc); + endElement(writer); + + endElement(writer); + } + } + + endElement(writer); +} + +void Account::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "account"); + { + startElement(writer, "account-provider"); + writeAttribute(writer, "appid", this->provider.appid); + writeAttribute(writer, "multiple-accounts-support", + this->provider.multiAccount); + + FOREACH(i, this->provider.icon) + { + startElement(writer, "icon"); + writeAttribute(writer, "section", i->first); + writeText(writer, i->second); + endElement(writer); + } + + bool setDefaultLang = false; + FOREACH(n, this->provider.name) + { + if (!setDefaultLang && n->getLang() == L"en-gb") { + writeElement(writer, "label", n->getString()); + setDefaultLang = true; + } + writeElementWithOneAttribute(writer, "label", + n->getString(), "xml:lang", + n->getLang(), n->hasLang()); + } + if (!setDefaultLang) { + writeElement(writer, "label", this->provider.name.begin()->getString()); + } + + FOREACH(c, this->provider.capability) + { + startElement(writer, "capability"); + writeText(writer, *c); + endElement(writer); + } + endElement(writer); + } + endElement(writer); +} + +void Privilege::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "privileges"); + { + FOREACH(it, this->name) + { + startElement(writer, "privilege"); + writeText(writer, *it); + endElement(writer); + } + } + endElement(writer); +} + +void Metadata::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "metadata"); + writeAttribute(writer, "key", *this->key); + if (!this->value.IsNull()) { + writeAttribute(writer, "value", *this->value); + } + endElement(writer); +} +} //namespace Jobs +} //namespace WidgetInstall diff --git a/src_mobile/jobs/widget_install/manifest.h b/src_mobile/jobs/widget_install/manifest.h new file mode 100644 index 0000000..67ee3ce --- /dev/null +++ b/src_mobile/jobs/widget_install/manifest.h @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file manifest.h + * @author Mariusz Domanski (m.domanski@samsung.com) + */ + +#ifndef INSTALLER_JOBS_MANIFEST_H +#define INSTALLER_JOBS_MANIFEST_H + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +/** + * @brief string with optional language attribute + */ +class StringWithLang +{ + public: + StringWithLang() { } + StringWithLang(DPL::String s) : string(s) { } + StringWithLang(DPL::String s, DPL::String l) : string(s), lang(l) { } + DPL::String getString() + { + return this->string; + } + DPL::String getLang() + { + return this->lang; + } + bool hasLang() + { + return !this->lang.empty(); + } + int operator==(const StringWithLang &other) + { + return (DPL::ToUTF8String(other.string) == DPL::ToUTF8String(string)) && + (DPL::ToUTF8String(other.lang) == DPL::ToUTF8String(lang)); + } + + private: + DPL::String string; + DPL::String lang; +}; + +typedef StringWithLang LabelType, IconType, DescriptionType; + +/** + * These types are basicaly strings but they should allow usage of different + * range of characters or words (details in XML spec.). + * For simplicity DPL::Strings are used, although this can lead to XML + * validation + * errors (related to usage of not allowed characters in given places). + */ +typedef DPL::String NcnameType, NmtokenType, AnySimpleType, LangType; +typedef DPL::String OperationType, MimeType, UriType, TypeType, PackageType; +typedef DPL::OptionalString InstallLocationType, CategoriesType; +typedef DPL::String AppCategoryType; +typedef DPL::OptionalString KeyType, ValueType; + +/** + * xmllib2 wrappers + */ +void writeElement(xmlTextWriterPtr writer, const char * name, DPL::String body); +void writeText(xmlTextWriterPtr writer, DPL::String text); +void writeElement(xmlTextWriterPtr writer, const char * name, const char * body); +void writeElementWithOneAttribute(xmlTextWriterPtr writer, + const char * name, + const char * body, + const char * nameAttr, + DPL::String bodyAttr, + bool condition = true); +void startElement(xmlTextWriterPtr writer, const char * name); +void endElement(xmlTextWriterPtr writer); +void writeAttribute(xmlTextWriterPtr writer, const char * name, + DPL::String body, bool condition); +void writeAttribute(xmlTextWriterPtr writer, const char * name, + const char * body, bool condition); + +/** + * @brief author element + */ +class Author +{ + public: + Author() {} + Author(AnySimpleType e, + NcnameType h, + LangType l, + DPL::String b) : + email(e), href(h), lang(l), body(b) {} + void serialize(xmlTextWriterPtr writer); + + private: + AnySimpleType email; + NcnameType href; + LangType lang; + DPL::String body; +}; + +typedef Author AuthorType; + +/** + * @brief application-service element + */ +class AppControl +{ + public: + AppControl() {} + void addOperation(const OperationType &x) + { + this->operation.push_back(x); + } + void addUri(const UriType &x) + { + this->uri.push_back(x); + } + void addMime(const MimeType &x) + { + this->mime.push_back(x); + } + void serialize(xmlTextWriterPtr writer); + + private: + std::list operation; //attr name AnySimpleType + std::list uri; //attr name AnySimpleType + std::list mime; //attr name AnySimpleType +}; + +typedef AppControl AppControlType; + +/** + * @brief account element + */ +typedef std::list> IconListType; +typedef std::list DisplayNameListType; +typedef std::list AccountCapabilityType; + +struct AccountProvider +{ + NcnameType appid; + NcnameType multiAccount; + IconListType icon; + DisplayNameListType name; + AccountCapabilityType capability; +}; + +typedef AccountProvider AccountProviderType; + +class Account +{ + public: + Account() {} + void addAccountProvider(const AccountProvider &x) + { + this->provider = x; + } + void serialize(xmlTextWriterPtr writer); + + private: + AccountProviderType provider; +}; + +class Privilege +{ + public: + Privilege() {} + void addPrivilegeName(const DPL::String &x) + { + this->name.push_back(x); + } + bool isEmpty() + { + return this->name.empty(); + } + + void serialize(xmlTextWriterPtr writer); + + private: + std::list name; +}; + +typedef Privilege PrivilegeType; + +class Metadata +{ + public: + Metadata(KeyType k, ValueType v) : + key(k), + value(v) + {} + void serialize(xmlTextWriterPtr writer); + + private: + KeyType key; + ValueType value; +}; + +typedef Metadata MetadataType; + + +/** + * @brief ime-application element + */ +class ImeApplication +{ + public: + ImeApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void setExec(const NcnameType &x) + { + this->exec = x; + } + void setMultiple(bool x) + { + this->multiple = x; + } + void setNodisplay(bool x) + { + this->nodisplay = x; + } + void setType(const TypeType &x) + { + this->type = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType appid; + NcnameType exec; + DPL::OptionalBool multiple; + DPL::OptionalBool nodisplay; + TypeType type; + std::list label; + std::list icon; +}; + +typedef ImeApplication ImeApplicationType; + +/** + * @brief service-application element + */ +class ServiceApplication +{ + public: + ServiceApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void setAutoRestart(bool x) + { + this->autoRestart = x; + } + void setExec(const AnySimpleType &x) + { + this->exec = x; + } + void setOnBoot(bool x) + { + this->onBoot = x; + } + void setType(const TypeType &x) + { + this->type = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAppControl(const AppControlType &x) + { + this->appControl.push_back(x); + } + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType appid; + DPL::OptionalBool autoRestart; + AnySimpleType exec; + DPL::OptionalBool onBoot; + TypeType type; + std::list label; //attr name AnySimpleType + std::list icon; //attr name AnySimpleType + std::list appControl; //attr name AnySimpleType +}; + +typedef ServiceApplication ServiceApplicationType; + +/** + * @brief ui-application element + */ +class UiApplication +{ + public: + UiApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void setExtraid(const NcnameType &x) + { + this->extraid = x; + } + void setExec(const AnySimpleType &x) + { + this->exec = x; + } + void setMultiple(bool x) + { + this->multiple = x; + } + void setNodisplay(bool x) + { + this->nodisplay = x; + } + void setTaskmanage(bool x) + { + this->taskmanage = x; + } + void setType(const TypeType &x) + { + this->type = x; + } + void setCategories(const NcnameType &x) + { + this->categories = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAppControl(const AppControlType &x) + { + this->appControl.push_back(x); + } + void addAppCategory(const AppCategoryType &x) + { + this->appCategory.push_back(x); + } + void addMetadata(const MetadataType &m) + { + this->metadata.push_back(m); + } + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType appid; + NcnameType extraid; + AnySimpleType exec; + DPL::OptionalBool multiple; + DPL::OptionalBool nodisplay; + DPL::OptionalBool taskmanage; + TypeType type; + CategoriesType categories; + std::list label; + std::list icon; + std::list appControl; + std::list appCategory; + std::list metadata; +}; + +typedef UiApplication UiApplicationType; + +/** + * @brief LiveBox element + */ +typedef WrtDB::ConfigParserData::LiveboxInfo::BoxSizeList BoxSizeType; +typedef WrtDB::ConfigParserData::LiveboxInfo::BoxLabelList BoxLabelType; + +struct BoxInfo +{ + NcnameType boxSrc; + NcnameType boxMouseEvent; + NcnameType boxTouchEffect; + BoxSizeType boxSize; + NcnameType pdSrc; + NcnameType pdWidth; + NcnameType pdHeight; +}; +typedef BoxInfo BoxInfoType; + +class LiveBox +{ + public: + LiveBox() { } + void setLiveboxId(const NcnameType &x) + { + this->liveboxId = x; + } + void setPrimary(const NcnameType &x) + { + this->primary = x; + } + void setUpdatePeriod(const NcnameType &x) + { + this->updatePeriod = x; + } + void setLabel(const BoxLabelType &x) + { + this->label = x; + } + void setIcon(const NcnameType &x) + { + this->icon = x; + } + void setBox(const BoxInfoType &x) + { + this->box = x; + } + + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType liveboxId; + NcnameType primary; + NcnameType autoLaunch; + NcnameType updatePeriod; + NcnameType timeout; + BoxLabelType label; + NcnameType icon; + NcnameType lang; + BoxInfoType box; +}; + +typedef LiveBox LiveBoxInfo; + +/** + * @brief manifest element + * + * Manifest xml file representation. + */ +class Manifest +{ + public: + Manifest() {} + void serialize(xmlTextWriterPtr writer); + void generate(DPL::String filename); + + void addLabel(const LabelType &x) + { +#ifdef MULTIPROCESS_SERVICE_SUPPORT + auto pos = std::find(label.begin(), label.end(), x); + if (pos == label.end()) { + this->label.push_back(x); + } +#else + this->label.push_back(x); +#endif + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAuthor(const AuthorType &x) + { + this->author.push_back(x); + } + void addDescription(const DescriptionType &x) + { + this->description.push_back(x); + } + // void addCompatibility(const CompatibilityType &x) + // { + // this->compatibility.push_back(x); + // } + // void addDeviceProfile(const DeviceProfileType &x) + // { + // this->deviceProfile.push_back(x); + // } + void addServiceApplication(const ServiceApplicationType &x) + { + this->serviceApplication.push_back(x); + } + void addUiApplication(const UiApplicationType &x) + { + this->uiApplication.push_back(x); + } + void addImeApplication(const ImeApplicationType &x) + { + this->imeApplication.push_back(x); + } + // void addFont(const FontType &x) { this->font.push_back(x); } + + void addLivebox(const LiveBoxInfo &x) + { + this->livebox.push_back(x); + } + + void addAccount(const Account &x) + { + this->account.push_back(x); + } + + void addPrivileges(const PrivilegeType &x) + { + this->privileges = x; + } + + void setInstallLocation(const InstallLocationType &x) + { + this->installLocation = x; + } + void setPackage(const NcnameType &x) + { + this->package = x; + } + void setType(const PackageType &x) + { + this->type = x; + } + void setVersion(const NmtokenType &x) + { + this->version = x; + } + void setStoreClientId(const NcnameType &x) + { + this->storeClientId= x; + } + + private: + std::list label; + std::list icon; + std::list author; + std::list description; + // std::list compatibility; + // std::list deviceProfile; + std::list serviceApplication; + std::list uiApplication; + std::list imeApplication; + // std::list font; + std::list livebox; + InstallLocationType installLocation; + NcnameType package; + PackageType type; + NmtokenType version; + std::list account; + PrivilegeType privileges; + NcnameType storeClientId; + +}; +} //namespace Jobs +} //namespace WidgetInstall + +#endif //INSTALLER_JOBS_MANIFEST_H diff --git a/src_mobile/jobs/widget_install/task_ace_check.cpp b/src_mobile/jobs/widget_install/task_ace_check.cpp new file mode 100644 index 0000000..cf44fc0 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_ace_check.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_ace_check.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task ace check + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +namespace Jobs { +namespace WidgetInstall { +TaskAceCheck::TaskAceCheck(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskAceCheck::StartStep); + AddStep(&TaskAceCheck::StepPrepareForAce); + AddStep(&TaskAceCheck::StepAceCheck); + AddStep(&TaskAceCheck::StepProcessAceResponse); + AddStep(&TaskAceCheck::StepCheckAceResponse); + AddStep(&TaskAceCheck::EndStep); +} + +void TaskAceCheck::StepPrepareForAce() +{ + m_context.featureLogic = + FeatureLogicPtr(new FeatureLogic(m_context.widgetConfig.tzAppid)); + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ACE_PREPARE, + "Widget Access Control Check Prepared"); +} + +void TaskAceCheck::StepAceCheck() +{ + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); + _D("StepAceCheck!"); + // This widget does not use any device cap + if (m_context.featureLogic->isDone()) { + return; + } + + _D("StepAceCheck!"); + DPL::String deviceCap = m_context.featureLogic->getDevice(); + + _D("StepAceCheck!"); + _D("DevCap is : %ls", deviceCap.c_str()); + + std::string devCapStr = DPL::ToUTF8String(deviceCap); + ace_policy_result_t policyResult = ACE_DENY; + + //TODO: remove dao.getHandle() + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD) { + _D("This widget is preloaded. So ace check will be skiped"); + policyResult = ACE_PERMIT; + } else { + ace_return_t ret = ace_get_policy_result( + const_cast(devCapStr.c_str()), + dao.getHandle(), + &policyResult); + if (ACE_OK != ret) { + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ACE check failure"); + } + } + + _D("PolicyResult is : %d", static_cast(policyResult)); + m_context.staticPermittedDevCaps.insert(std::make_pair(deviceCap, + policyResult == + ACE_PERMIT)); + + m_context.featureLogic->setAceResponse(policyResult != ACE_DENY); +} + +void TaskAceCheck::StepProcessAceResponse() +{ + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); + if (m_context.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + return; + } + + _D("StepProcessAceResponse"); + m_context.featureLogic->next(); + + // No device caps left to process + if (m_context.featureLogic->isDone()) { + _D("All responses has been received from ACE."); + // Data to convert to C API + std::vector devCaps; + std::vector devCapsSmack; + // Saving static dev cap permissions + FOREACH(cap, m_context.staticPermittedDevCaps) { + _D("staticPermittedDevCaps : %ls smack: %d", cap->first.c_str(), cap->second); + std::string devCapStr = DPL::ToUTF8String(cap->first); + devCaps.push_back(devCapStr); + devCapsSmack.push_back(cap->second); + } + ace_requested_dev_cap_list_t list; + list.count = devCaps.size(); + list.items = new ace_requested_dev_cap_t[list.count]; + + for (unsigned int i = 0; i < devCaps.size(); ++i) { + list.items[i].device_capability = + const_cast(devCaps[i].c_str()); + list.items[i].smack_granted = + devCapsSmack[i] ? ACE_TRUE : ACE_FALSE; + } + //TODO: remove dao.getHandle() + ace_return_t ret = ace_set_requested_dev_caps(dao.getHandle(), + &list); + if (ACE_OK != ret) { + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ACE failure"); + } + delete[] list.items; + + std::set acceptedFeature; + auto it = m_context.featureLogic->resultBegin(); + for (; it != m_context.featureLogic->resultEnd(); ++it) { + if (!(it->rejected)) { + acceptedFeature.insert(DPL::ToUTF8String(it->name)); + } + } + ace_feature_list_t featureList; + featureList.count = acceptedFeature.size(); + featureList.items = new ace_string_t[featureList.count]; + + size_t i = 0; + for (std::set::const_iterator iter = acceptedFeature.begin(); + iter != acceptedFeature.end(); ++iter) + { + _D("Accepted feature item: %s", iter->c_str()); + featureList.items[i] = const_cast(iter->c_str()); + i++; + } + + //TODO: remove dao.getHandle() + ret = ace_set_accepted_feature(dao.getHandle(), &featureList); + + delete[] featureList.items; + + if (ACE_OK != ret) { + _E("Error in ace_set_feature"); + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ace_set_feature failure."); + } + return; + } + + _D("Next device cap."); + // Process next device cap + SwitchToStep(&TaskAceCheck::StepAceCheck); +} + +void TaskAceCheck::StepCheckAceResponse() +{ + _D("Checking ACE response"); + if (m_context.featureLogic->isRejected()) { + _E("Installation failure. Some devCap was not accepted by ACE."); + ThrowMsg( + Exceptions::PrivilegeLevelViolation, + "Instalation failure. " + "Some deviceCap was not accepted by ACE."); + } + _D("Updating \"feature reject status\" in database!"); + auto it = m_context.featureLogic->resultBegin(); + auto end = m_context.featureLogic->resultEnd(); + for (; it != end; ++it) { + _D(" |- Feature: %ls has reject status: %d", it->name.c_str(), it->rejected); + if (it->rejected) { + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); + dao.updateFeatureRejectStatus(*it); + } + } + _D("Installation continues..."); +} + +void TaskAceCheck::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskAceCheck::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ACE_CHECK, + "Widget Access Control Check Finished"); + + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_ace_check.h b/src_mobile/jobs/widget_install/task_ace_check.h new file mode 100644 index 0000000..e4ce90d --- /dev/null +++ b/src_mobile/jobs/widget_install/task_ace_check.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_ace_check.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for installer task ace check + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskAceCheck : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepPrepareForAce(); + void StepAceCheck(); + void StepProcessAceResponse(); + void StepCheckAceResponse(); + + void StartStep(); + void EndStep(); + + public: + TaskAceCheck(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H */ diff --git a/src_mobile/jobs/widget_install/task_certify.cpp b/src_mobile/jobs/widget_install/task_certify.cpp new file mode 100644 index 0000000..81f21b0 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_certify.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_certify.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include "wac_widget_id.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ValidationCore; +using namespace WrtDB; + +namespace { + +WidgetCertificateData toWidgetCertificateData(const SignatureData &data, + bool root) +{ + WidgetCertificateData result; + + result.chainId = data.getSignatureNumber(); + _D("result.chainId : %d", result.chainId); + + result.owner = data.isAuthorSignature() ? + WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR; + + result.type = root ? + WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY; + + CertificatePtr certificate; + + if (root) { + certificate = data.getRootCaCertificatePtr(); + } else { + certificate = data.getEndEntityCertificatePtr(); + } + + Assert(certificate && !certificate->getCommonName().IsNull() && + "CommonName is Null"); + + result.strCommonName = *certificate->getCommonName(); + + result.strMD5Fingerprint = std::string("md5 ") + + Certificate::FingerprintToColonHex( + certificate->getFingerprint(Certificate::FINGERPRINT_MD5)); + + result.strSHA1Fingerprint = std::string("sha-1 ") + + Certificate::FingerprintToColonHex( + certificate->getFingerprint(Certificate::FINGERPRINT_SHA1)); + + return result; +} + +CertificatePtr getOldAuthorSignerCertificate(DPL::String appid) +{ + WidgetDAOReadOnly dao(appid); + CertificateChainList chainList = dao.getWidgetCertificate(SIGNATURE_AUTHOR); + + FOREACH(it, chainList) + { + ValidationCore::CertificateCollection chain; + if (false == chain.load(*it)) { + _E("Chain is broken"); + } + + if (!chain.sort()) { + _E("Chain failed at sorting"); + } + + ValidationCore::CertificateList list = chain.getCertificateList(); + + FOREACH(cert, list) + { + if (!(*cert)->isRootCert() && !(*cert)->isCA()) { + return *cert; + } + } + } + return CertificatePtr(NULL); +} +} // namespace anonymous + +namespace Jobs { +namespace WidgetInstall { +TaskCertify::TaskCertify(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_contextData(inCont) +{ + AddStep(&TaskCertify::StartStep); + AddStep(&TaskCertify::stepSignature); + // certi comparison determines whether the update. + if (true == m_contextData.isUpdateMode) { + AddStep(&TaskCertify::stepVerifyUpdate); + } + AddStep(&TaskCertify::EndStep); +} + +void TaskCertify::processDistributorSignature(const SignatureData &data) +{ + // this signature is verified - + // no point in check domain WAC_ROOT and WAC_RECOGNIZED + m_contextData.widgetSecurity.setDistributorSigned(true); + + CertificateCollection collection; + collection.load(data.getCertList()); + Assert(collection.sort() && + "Certificate collection can't sort"); + + Assert(collection.isChain() && + "Certificate collection is not able to create chain. " + "It is not possible to verify this signature."); + + m_contextData.widgetSecurity.getCertificateChainListRef().push_back( + collection); + + if (data.getSignatureNumber() == 1) { + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, true)); + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, false)); + } +} + +void TaskCertify::processAuthorSignature(const SignatureData &data) +{ + using namespace ValidationCore; + _D("DNS Identity match!"); + // this signature is verified or widget is distributor signed + m_contextData.widgetSecurity.setAuthorCertificatePtr(data.getEndEntityCertificatePtr()); + CertificatePtr test = m_contextData.widgetSecurity.getAuthorCertificatePtr(); + + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, true)); + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, false)); + + // match widget_id with one from dns identity set + WacWidgetId widgetId(m_contextData.widgetConfig.configInfo.widget_id); + + CertificatePtr cert = data.getEndEntityCertificatePtr(); + Assert(cert); + Certificate::AltNameSet dnsIdentity = cert->getAlternativeNameDNS(); + + CertificateCollection collection; + collection.load(data.getCertList()); + collection.sort(); + Assert(collection.isChain() && + "Certificate collection is not able to create chain. " + "It is not possible to verify this signature."); + + m_contextData.widgetSecurity.getAuthorsCertificateChainListRef().push_back( + collection); + + FOREACH(it, dnsIdentity){ + if (widgetId.matchHost(*it)) { + m_contextData.widgetSecurity.setRecognized(true); + return; + } + } +} + +void TaskCertify::getSignatureFiles(std::string path, SignatureFileInfoSet& file) +{ + _D("path : %s", path.c_str()); + SignatureFileInfoSet signatureFiles; + SignatureFinder signatureFinder(path); + if (SignatureFinder::NO_ERROR != signatureFinder.find(file)) { + _E("Error in Signature Finder : %s", path.c_str()); + ThrowMsg(Exceptions::SignatureNotFound, + "Error openig temporary widget directory"); + } +} + +void TaskCertify::stepSignature() +{ + _D("================ Step: <> ENTER ==============="); + + std::string widgetPath; + widgetPath = m_contextData.locations->getPackageInstallationDir() + "/"; + + SignatureFileInfoSet signatureFiles; + + Try { + getSignatureFiles(widgetPath, signatureFiles); + + if (signatureFiles.size() <= 0) { + widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath()) + + "/"; + if (0 == access(widgetPath.c_str(), F_OK)) { + getSignatureFiles(widgetPath, signatureFiles); + } + } + } Catch(Exceptions::SignatureNotFound) { + ReThrowMsg(Exceptions::SignatureNotFound, widgetPath); + } + + SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin(); + _D("Number of signatures: %d", signatureFiles.size()); + + for (; iter != signatureFiles.rend(); ++iter) { + _D("Checking signature with id=%d", iter->getFileNumber()); + SignatureData data(widgetPath + iter->getFileName(), + iter->getFileNumber()); + + Try { + SignatureReader xml; + xml.initialize(data, GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + WrtSignatureValidator::Result result; + + WrtSignatureValidator validator( + WrtSignatureValidator::TIZEN, + !GlobalSettings:: + OCSPTestModeEnabled(), + !GlobalSettings:: + CrlTestModeEnabled(), + false); + + result = validator.check(data, widgetPath); + + if (m_contextData.mode.installTime + == InstallMode::InstallTime::PRELOAD) + { + result = WrtSignatureValidator::SIGNATURE_VERIFIED; + } + + if (result == WrtSignatureValidator::SIGNATURE_REVOKED) { + _W("Certificate is REVOKED"); + ThrowMsg(Exceptions::CertificateExpired, + "Certificate is REVOKED"); + } + + if (result == WrtSignatureValidator::SIGNATURE_INVALID && + iter->getFileNumber() <= 1) { + _W("Signature is INVALID"); + // TODO change exception name + ThrowMsg(Exceptions::SignatureInvalid, + "Invalid Package"); + } + + if (data.isAuthorSignature()) { + if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ) { + processAuthorSignature(data); + } + } else { + if (result != WrtSignatureValidator::SIGNATURE_INVALID) { + processDistributorSignature(data); + } + } + } Catch(ParserSchemaException::Base) { + _E("Error occured in ParserSchema."); + ReThrowMsg(Exceptions::SignatureInvalid, + "Error occured in ParserSchema."); + } + } + + if (signatureFiles.empty()) { + _D("No signature files has been found."); + } + + _D("================ Step: <> DONE ================"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_DIGSIG_CHECK, + "Widget Signature checked"); +} + +bool TaskCertify::isTizenWebApp() const +{ + bool ret = FALSE; + if (m_contextData.widgetConfig.webAppType.appType + == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) + { + ret = TRUE; + } + + return ret; +} + +void TaskCertify::stepVerifyUpdate() +{ + _D("Step: <>"); + CertificatePtr newCertificate = + m_contextData.widgetSecurity.getAuthorCertificatePtr(); + CertificatePtr oldCertificate = + getOldAuthorSignerCertificate(m_contextData.widgetConfig.tzAppid); + + if (!!newCertificate && !!oldCertificate) { + if (0 != newCertificate->getBase64().compare(oldCertificate->getBase64())) { + _D("old widget's author signer certificate : %s", (oldCertificate->getBase64()).c_str()); + _D("new widget's author signer certificate : %s", (newCertificate->getBase64()).c_str()); + ThrowMsg(Exceptions::NotMatchedCertification, + "Author signer certificates doesn't match \ + between old widget and installing widget"); + } + } else { + if (!(NULL == newCertificate.Get() && NULL == oldCertificate.Get())) { + ThrowMsg(Exceptions::NotMatchedCertification, + "Author signer certificates doesn't match \ + between old widget and installing widget"); + } + } +} + +void TaskCertify::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskCertify::EndStep() +{ + _D("Step: <>"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_CERT_CHECK, + "Widget Certification Check Finished"); + + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs + diff --git a/src_mobile/jobs/widget_install/task_certify.h b/src_mobile/jobs/widget_install/task_certify.h new file mode 100644 index 0000000..6a59781 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_certify.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_certify.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include + +class InstallerContext; + +namespace ValidationCore { +class SignatureData; +} + +namespace Jobs { +namespace WidgetInstall { +class TaskCertify : + public DPL::TaskDecl +{ + public: + TaskCertify(InstallerContext &inCont); + + private: + //data + InstallerContext& m_contextData; + + //steps + void stepSignature(); + void stepVerifyUpdate(); + + void StartStep(); + void EndStep(); + + void processDistributorSignature(const ValidationCore::SignatureData &data); + void processAuthorSignature(const ValidationCore::SignatureData &data); + void getSignatureFiles(std::string path, + ValidationCore::SignatureFileInfoSet& file); + + bool isTizenWebApp() const; +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H diff --git a/src_mobile/jobs/widget_install/task_certify_level.cpp b/src_mobile/jobs/widget_install/task_certify_level.cpp new file mode 100644 index 0000000..9d1bc50 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_certify_level.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_certify_level.cpp + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace ValidationCore; +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskCertifyLevel::TaskCertifyLevel(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_contextData(inCont) +{ + AddStep(&TaskCertifyLevel::StartStep); + AddStep(&TaskCertifyLevel::stepCertifyLevel); + AddStep(&TaskCertifyLevel::EndStep); +} + +void TaskCertifyLevel::stepCertifyLevel() +{ + _D("================ Step: <> ENTER ==============="); + if (!checkConfigurationLevel(getCertifyLevel())) { + ThrowMsg(Exceptions::PrivilegeLevelViolation, "setting level violate"); + } + _D("================ Step: <> DONE ================"); +} + +void TaskCertifyLevel::getSignatureFiles(const std::string& path, + SignatureFileInfoSet& file) +{ + SignatureFileInfoSet signatureFiles; + SignatureFinder signatureFinder(path); + if (SignatureFinder::NO_ERROR != signatureFinder.find(file)) { + _E("Error in Signature Finder : %s", path.c_str()); + ThrowMsg(Exceptions::SignatureNotFound, "Signature not found"); + } +} + +TaskCertifyLevel::Level TaskCertifyLevel::getCertifyLevel() +{ + std::string widgetPath = m_contextData.locations->getPackageInstallationDir() + "/"; + SignatureFileInfoSet signatureFiles; + + Try { + getSignatureFiles(widgetPath, signatureFiles); + + if (signatureFiles.size() <= 0) { + widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath()) + + "/"; + if (0 == access(widgetPath.c_str(), F_OK)) { + getSignatureFiles(widgetPath, signatureFiles); + } + } + } Catch(Exceptions::SignatureNotFound) { + ReThrowMsg(Exceptions::SignatureNotFound, widgetPath); + } + + SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin(); + _D("Number of signatures: %d", signatureFiles.size()); + + Level level = Level::UNKNOWN; + for (; iter != signatureFiles.rend(); ++iter) { + _D("Checking signature with id=%d", iter->getFileNumber()); + SignatureData data(widgetPath + iter->getFileName(), + iter->getFileNumber()); + + Try { + SignatureReader xml; + xml.initialize(data, GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + WrtSignatureValidator validator( + WrtSignatureValidator::TIZEN, + !GlobalSettings:: + OCSPTestModeEnabled(), + !GlobalSettings:: + CrlTestModeEnabled(), + false); + + WrtSignatureValidator::Result result = + validator.check(data, widgetPath); + + if (m_contextData.mode.installTime + == InstallMode::InstallTime::PRELOAD) + { + result = WrtSignatureValidator::SIGNATURE_VERIFIED; + } + + if (result == WrtSignatureValidator::SIGNATURE_REVOKED) { + ThrowMsg(Exceptions::CertificateExpired, + "Certificate is REVOKED"); + } + + if (result == WrtSignatureValidator::SIGNATURE_INVALID && + iter->getFileNumber() <= 1) + { + ThrowMsg(Exceptions::SignatureInvalid, "Invalid Package"); + } + + if (data.isAuthorSignature()) { + _D("Skip author signature"); + } else { + Level currentCertLevel = + certTypeToLevel(data.getVisibilityLevel()); + if (currentCertLevel == Level::UNKNOWN) { + continue; + } + if (currentCertLevel > level) { + level = currentCertLevel; + _D("level %s", enumToString(level).c_str()); + } + } + } Catch(ParserSchemaException::Base) { + _E("Error occured in ParserSchema."); + ReThrowMsg(Exceptions::SignatureInvalid, + "Error occured in ParserSchema."); + } + } + + m_contextData.certLevel = level; + return level; +} + +bool TaskCertifyLevel::checkConfigurationLevel( + TaskCertifyLevel::Level level) +{ + if (!checkSettingLevel(level)) { + return false; + } + if (!checkAppcontrolHasDisposition(level)) { + return false; + } + return true; +} + +bool TaskCertifyLevel::checkSettingLevel( + TaskCertifyLevel::Level level) +{ + secureSettingMap data = { + {"sound-mode", Level::PARTNER}, + {"nodisplay", Level::PARTNER} + }; + FOREACH(it, m_contextData.widgetConfig.configInfo.settingsList) { + secureSettingIter ret = data.find(DPL::ToUTF8String(it->m_name)); + if (ret != data.end()) { + if (level < ret->second) { + _E("\"%ls\" needs \"%s\" level", it->m_name.c_str(), enumToString(ret->second).c_str()); + return false; + } + } + } + return true; +} + +bool TaskCertifyLevel::checkAppcontrolHasDisposition( + TaskCertifyLevel::Level level) +{ + // tizen:disposition -> platform + FOREACH(it, m_contextData.widgetConfig.configInfo.appControlList) { + if (ConfigParserData::AppControlInfo::Disposition::UNDEFINE != + it->m_disposition) + { + if (level < Level::PLATFORM) { + _E("\"tizen:disposition\" needs \"%s \" level", enumToString(Level::PLATFORM).c_str()); + return false; + } + } + } + return true; +} + +std::string TaskCertifyLevel::enumToString( + TaskCertifyLevel::Level level) +{ + switch (level) { +#define X(x, y) case x: return #y; + X(Level::UNKNOWN, UNKNOWN) + X(Level::PUBLIC, PUBLIC) + X(Level::PARTNER, PARTNER) + X(Level::PLATFORM, PLATFORM) +#undef X + default: + return "UNKNOWN"; + } +} + +TaskCertifyLevel::Level TaskCertifyLevel::certTypeToLevel( + CertStoreId::Type type) +{ + // CertStoreType.h (framework/security/cert-svc) + // RootCA's visibility level : public + // const Type VIS_PUBLIC = 1 << 6; + // RootCA's visibility level : partner + // const Type VIS_PARTNER = 1 << 7; + // RootCA's visibility level : platform + // const Type VIS_PLATFORM = 1 << 10; + if (type == CertStoreId::VIS_PUBLIC) { + return Level::PUBLIC; + } else if (type == CertStoreId::VIS_PARTNER) { + return Level::PARTNER; + } else if (type == CertStoreId::VIS_PLATFORM) { + return Level::PLATFORM; + } + return Level::UNKNOWN; +} + +void TaskCertifyLevel::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskCertifyLevel::EndStep() +{ + _D("--------- : END ----------"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_CERTIFY_LEVEL_CHECK, + "Application Certificate level check Finished"); +} +} //namespace WidgetInstall +} //namespace Jobs + diff --git a/src_mobile/jobs/widget_install/task_certify_level.h b/src_mobile/jobs/widget_install/task_certify_level.h new file mode 100644 index 0000000..ad8d427 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_certify_level.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_certify_level.h + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskCertifyLevel : + public DPL::TaskDecl +{ + public: + TaskCertifyLevel(InstallerContext &inCont); + + private: + //data + InstallerContext& m_contextData; + + enum Level { + UNKNOWN = 0, + PUBLIC = 1, + PARTNER = 2, + PLATFORM = 3 + }; + typedef std::map secureSettingMap; + typedef std::map::iterator secureSettingIter; + + //steps + void stepCertifyLevel(); + void StartStep(); + void EndStep(); + + //util + void getSignatureFiles(const std::string& path, + ValidationCore::SignatureFileInfoSet& file); + Level getCertifyLevel(); + bool checkConfigurationLevel(Level level); + bool checkSettingLevel(Level level); + bool checkAppcontrolHasDisposition(Level level); + std::string enumToString(Level level); + Level certTypeToLevel(ValidationCore::CertStoreId::Type type); + +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H diff --git a/src_mobile/jobs/widget_install/task_commons.cpp b/src_mobile/jobs/widget_install/task_commons.cpp new file mode 100644 index 0000000..c5b5b67 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_commons.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_commons.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "task_commons.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const char * const TEMPORARY_PATH_POSTFIX = "temp"; +const mode_t TEMPORARY_PATH_MODE = 0775; +} // namespace + +std::string createTempPath(bool preload) +{ + _D("Step: Creating temporary path"); + + // Temporary path + std::ostringstream tempPathBuilder; + + if (preload) { + tempPathBuilder << WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + tempPathBuilder << WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + tempPathBuilder << WrtDB::GlobalConfig::GetTmpDirPath(); + tempPathBuilder << "/"; + tempPathBuilder << TEMPORARY_PATH_POSTFIX; + tempPathBuilder << "_"; + + timeval tv; + gettimeofday(&tv, NULL); + tempPathBuilder << + (static_cast(tv.tv_sec) * 1000000ULL + + static_cast(tv.tv_usec)); + + std::string tempPath = tempPathBuilder.str(); + + // Remove old path if any + struct stat fileInfo; + + if (stat(tempPath.c_str(), &fileInfo) == 0) { + if (!WrtUtilRemove(tempPath)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Failed to to remove temporary directory"); + } + } + // Create new path + if (!WrtUtilMakeDir(tempPath, TEMPORARY_PATH_MODE)) { + ThrowMsg(Exceptions::FileOperationFailed, + "Failed to create temporary directory"); + } + + return tempPath; +} + +void createTempPath(const std::string& path) +{ + if (!WrtUtilMakeDir(path, TEMPORARY_PATH_MODE)) { + ThrowMsg(Exceptions::FileOperationFailed, + "Failed to create temporary directory"); + } +} +} // WidgetInstall +} // Jobs diff --git a/src_mobile/jobs/widget_install/task_commons.h b/src_mobile/jobs/widget_install/task_commons.h new file mode 100644 index 0000000..caf9660 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_commons.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_commons.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ + +#include + +namespace Jobs { +namespace WidgetInstall { +//TODO make directory like jobs common? + +std::string createTempPath(bool isReadOnly = false); +void createTempPath(const std::string& path); +} // WidgetInstall +} // Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ */ diff --git a/src_mobile/jobs/widget_install/task_configuration.cpp b/src_mobile/jobs/widget_install/task_configuration.cpp new file mode 100644 index 0000000..c336848 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_configuration.cpp @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_configuration.cpp + * @version 1.0 + * @author Tomasz Iwanek + * @brief implementation file for configuration task + */ +#include "task_configuration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +const char* const CONFIG_XML = "config.xml"; +const char* const WITH_OSP_XML = "res/wgt/config.xml"; +const char* const OSP_MANIFEST_XML = "info/manifest.xml"; + +//allowed: a-z, A-Z, 0-9 +const char* REG_TIZENID_PATTERN = "^[a-zA-Z0-9]{10}.{1,}$"; +const char* REG_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; +const char* REG_NAME_PATTERN = "^[a-zA-Z0-9._-]{1,}$"; +const size_t PACKAGE_ID_LENGTH = 10; + +static const DPL::String SETTING_VALUE_ENCRYPTION = L"encryption"; +static const DPL::String SETTING_VALUE_ENCRYPTION_ENABLE = L"enable"; +static const DPL::String SETTING_VALUE_ENCRYPTION_DISABLE = L"disable"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_NAME = L"install-location"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_PREPER_EXT = L"prefer-external"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_AUTO = L"auto"; +const std::string XML_EXTENSION = ".xml"; + +bool hasExtension(const std::string& filename, const std::string& extension) +{ + _D("Looking for extension %s in %s", extension.c_str(), filename.c_str()); + size_t fileLen = filename.length(); + size_t extLen = extension.length(); + if (fileLen < extLen) { + _E("Filename %s is shorter than extension %s", filename.c_str(), extension.c_str()); + return false; + } + return (0 == filename.compare(fileLen - extLen, extLen, extension)); +} +} // namespace anonymous + +namespace Jobs { +namespace WidgetInstall { + +TaskConfiguration::TaskConfiguration(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_widgetConfig(m_context.widgetConfig.configInfo) +{ + AddStep(&TaskConfiguration::StartStep); + + AddStep(&TaskConfiguration::SetupTempDirStep); + AddStep(&TaskConfiguration::UnzipConfigurationStep); + AddStep(&TaskConfiguration::ParseXMLConfigStep); + + AddStep(&TaskConfiguration::TizenIdStep); + AddStep(&TaskConfiguration::CheckAppRunningStateStep); + AddStep(&TaskConfiguration::ApplicationTypeStep); + AddStep(&TaskConfiguration::ResourceEncryptionStep); + AddStep(&TaskConfiguration::InstallationFSLocationStep); + + AddStep(&TaskConfiguration::DetectUpdateInstallationStep); + AddStep(&TaskConfiguration::CheckRDSSupportStep); + AddStep(&TaskConfiguration::ConfigureWidgetLocationStep); + AddStep(&TaskConfiguration::PkgmgrStartStep); + + AddStep(&TaskConfiguration::AppendTasklistStep); + + AddStep(&TaskConfiguration::EndStep); +} + +void TaskConfiguration::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskConfiguration::EndStep() +{ + m_context.job->UpdateProgress(InstallerContext::INSTALL_PARSE_CONFIG, + "Parse config.xml and set structure"); + _D("--------- : END ----------"); +} + +void TaskConfiguration::PkgmgrStartStep() +{ + pkgMgrInterface()->sendProgress(0); +} + +void TaskConfiguration::AppendTasklistStep() +{ + if (!m_context.isUpdateMode) { + _D("TaskConfiguration -> new installation task list"); + m_context.job->appendNewInstallationTaskList(); + } else { + _D("TaskConfiguration -> update installation task list"); + m_context.job->appendUpdateInstallationTaskList(); + } +} + +std::shared_ptr TaskConfiguration::pkgMgrInterface() +{ + return m_context.job->GetInstallerStruct().pkgmgrInterface; +} + +void TaskConfiguration::SetupTempDirStep() +{ + _D("widgetPath: %s", m_context.requestedPath.c_str()); + _D("tempPath: %s", m_tempDir.c_str()); + if (m_context.mode.extension == InstallMode::ExtensionType::DIR) { + if (m_context.mode.command == + InstallMode::Command::REINSTALL) { + std::ostringstream tempPathBuilder; + tempPathBuilder << WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + tempPathBuilder << WrtDB::GlobalConfig::GetTmpDirPath(); + tempPathBuilder << "/"; + tempPathBuilder << m_context.requestedPath; + m_tempDir = tempPathBuilder.str(); + } else { + m_tempDir = m_context.requestedPath; + } + } else { + m_tempDir = + Jobs::WidgetInstall::createTempPath( + m_context.mode.rootPath == + InstallMode::RootPath::RO); + } +} + +void TaskConfiguration::UnzipConfigurationStep() +{ + _D("UnzipConfigurationStep"); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) { + if(!hasExtension(m_context.requestedPath, XML_EXTENSION)) //unzip everything except xml files + { + WidgetUnzip wgtUnzip(m_context.requestedPath); + wgtUnzip.unzipConfiguration(m_tempDir, &m_context.widgetConfig.packagingType); + m_configuration += m_tempDir + "/" + CONFIG_XML; + } else{ + m_context.widgetConfig.packagingType = PKG_TYPE_HOSTED_WEB_APP; + m_configuration += m_context.requestedPath; + } + } else { + if (m_context.mode.command == InstallMode::Command::REINSTALL) { + std::string configFile = m_tempDir + "/" + CONFIG_XML; + if (!WrtUtilFileExists(configFile)) { + configFile = m_tempDir + "/" + WITH_OSP_XML; + if (!WrtUtilFileExists(configFile)) { + std::string tzAppId = m_context.requestedPath. + substr(m_context.requestedPath.find_last_of("/")+1); + WidgetDAOReadOnly dao(WidgetDAOReadOnly::getTzAppId( + DPL::FromUTF8String(tzAppId))); + configFile = DPL::ToUTF8String(*dao.getWidgetInstalledPath()); + configFile += "/"; + configFile += WITH_OSP_XML; + } + } else { + m_context.widgetConfig.packagingType = PKG_TYPE_NOMAL_WEB_APP; + } + m_configuration = configFile; + } else { + m_configuration = m_tempDir + "/" + WITH_OSP_XML; + } + } + _D("m_configuration : %s", m_configuration.c_str()); + _D("Package Type : %s", m_context.widgetConfig.packagingType.getPkgtypeToString().c_str()); +} + +void TaskConfiguration::ParseXMLConfigStep() +{ + _D("ParseXMLConfigStep"); + // Parse config + ParserRunner parser; + Try + { + if(!DPL::Utils::Path(m_configuration).Exists()) + { + ThrowMsg(Exceptions::MissingConfig, "Config file not exists"); + } + +#ifdef SCHEMA_VALIDATION_ENABLED + if(!parser.Validate(configFilePath, WRT_WIDGETS_XML_SCHEMA)) + { + _E("Invalid configuration file - schema validation failed"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Failed to parse config.xml file"); + } +#endif + parser.Parse(m_configuration, + ElementParserPtr( + new RootParser(m_widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + } + Catch(ElementParser::Exception::ParseError) + { + _E("Failed to parse config.xml file"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Parser exeption"); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Failed to find installed widget - give proper tizenId"); + ThrowMsg(Exceptions::RDSDeltaFailure, "WidgetNotExist"); + } + Catch(Exceptions::WidgetConfigFileNotFound){ + _E("Failed to find config.xml"); + ThrowMsg(Exceptions::MissingConfig, "Parser exeption"); + } + + if (!WrtUtilRemove(m_configuration)) { + _E("Error occurs during removing %s", m_configuration.c_str()); + } + +} + +void TaskConfiguration::TizenIdStep() +{ + bool shouldMakeAppid = false; + using namespace PackageManager; + + if (!!m_widgetConfig.tizenAppId) { + _D("Setting tizenAppId provided in config.xml: %s", DPL::ToUTF8String(*m_widgetConfig.tizenAppId).c_str()); + + m_context.widgetConfig.tzAppid = *m_widgetConfig.tizenAppId; + //check package id. + if (!!m_widgetConfig.tizenPkgId) { + _D("Setting tizenPkgId provided in config.xml: %s", DPL::ToUTF8String(*m_widgetConfig.tizenPkgId).c_str()); + + m_context.widgetConfig.tzPkgid = *m_widgetConfig.tizenPkgId; + } else { + DPL::String appid = *m_widgetConfig.tizenAppId; + if (appid.length() > PACKAGE_ID_LENGTH) { + m_context.widgetConfig.tzPkgid = + appid.substr(0, PACKAGE_ID_LENGTH); + } else { + //old version appid only has 10byte random character is able to install for a while. + //this case appid equal pkgid. + m_context.widgetConfig.tzPkgid = + *m_widgetConfig.tizenAppId; + shouldMakeAppid = true; + } + } + } else { + shouldMakeAppid = true; + TizenPkgId pkgId = WidgetDAOReadOnly::generatePkgId(); + _D("Checking if pkg id is unique"); + while (true) { + if (!validateTizenPackageID(pkgId)) { + //path exist, chose another one + pkgId = WidgetDAOReadOnly::generatePkgId(); + continue; + } + break; + } + m_context.widgetConfig.tzPkgid = pkgId; + _D("tizen_id name was generated by WRT: %ls", m_context.widgetConfig.tzPkgid.c_str()); + } + + if (shouldMakeAppid == true) { + DPL::OptionalString name; + DPL::OptionalString defaultLocale = m_widgetConfig.defaultlocale; + + FOREACH(localizedData, m_widgetConfig.localizedDataSet) + { + Locale i = localizedData->first; + if (!!defaultLocale) { + if (defaultLocale == i) { + name = localizedData->second.name; + break; + } + } else { + name = localizedData->second.name; + break; + } + } + regex_t regx; + if (regcomp(®x, REG_NAME_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + } + + _D("Name : %ls", (*name).c_str()); + if (!name || (regexec(®x, DPL::ToUTF8String(*name).c_str(), + static_cast(0), NULL, 0) != REG_NOERROR)) + { + const std::string allowedString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + std::ostringstream genName; + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned int seed = time(NULL) + tv.tv_usec; + + genName << "_" << allowedString[rand_r(&seed) % allowedString.length()]; + name = DPL::FromUTF8String(genName.str()); + _D("name was generated by WRT"); + } + regfree(®x); + _D("Name : %ls", (*name).c_str()); + std::ostringstream genid; + genid << m_context.widgetConfig.tzPkgid << "." << name; + _D("tizen appid was generated by WRT : %s", genid.str().c_str()); + + DPL::OptionalString appid = DPL::FromUTF8String(genid.str()); + NormalizeAndTrimSpaceString(appid); + m_context.widgetConfig.tzAppid = *appid; + } + + // send start signal of pkgmgr + pkgMgrInterface()->setPkgname(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid)); + + _D("Tizen App Id : %ls", (m_context.widgetConfig.tzAppid).c_str()); + _D("Tizen Pkg Id : %ls", (m_context.widgetConfig.tzPkgid).c_str()); +} + +void TaskConfiguration::CheckAppRunningStateStep() +{ + bool isRunning = false; + int ret = + app_manager_is_running(DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str(), + &isRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + + if (true == isRunning) { + // get app_context for running application + // app_context must be released with app_context_destroy + app_context_h appCtx = NULL; + ret = + app_manager_get_app_context( + DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str(), + &appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get app_context"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + + // terminate app_context_h + ret = app_manager_terminate_app(appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to terminate running application"); + app_context_destroy(appCtx); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } else { + app_context_destroy(appCtx); + // app_manager_terminate_app isn't sync API + // wait until application isn't running (50ms * 100) + bool isStillRunning = true; + int checkingloop = 100; + struct timespec duration = { 0, 50 * 1000 * 1000 }; + while (--checkingloop >= 0) { + nanosleep(&duration, NULL); + int ret = app_manager_is_running( + DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str(), + &isStillRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + if (!isStillRunning) { + break; + } + } + if (isStillRunning) { + _E("Fail to terminate running application"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + _D("terminate application"); + } + } +} + +void TaskConfiguration::ConfigureWidgetLocationStep() +{ + m_context.locations = + WidgetLocation(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid), + m_context.requestedPath, m_tempDir, + m_context.widgetConfig.packagingType, + m_context.mode.rootPath == + InstallMode::RootPath::RO, + m_context.mode.extension); + m_context.locations->registerAppid( + DPL::ToUTF8String(m_context.widgetConfig.tzAppid)); + + _D("widgetSource %s", m_context.requestedPath.c_str()); +} + +void TaskConfiguration::DetectUpdateInstallationStep() +{ + WidgetUpdateInfo update; + // checking installed web application + Try { + // no excpetion means, it isn't update mode + update = detectWidgetUpdate(m_widgetConfig, + m_context.widgetConfig.tzAppid); + checkWidgetUpdate(update); + + m_context.isUpdateMode = true; + + //if update, notify pkgmgr that this is update + pkgMgrInterface()->startJob(InstallationType::UpdateInstallation); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + pkgMgrInterface()->startJob(InstallationType::NewInstallation); + + m_context.isUpdateMode = false; + + if (!validateTizenApplicationID( + m_context.widgetConfig.tzAppid)) + { + _E("tizen application ID is already used"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid, + "invalid config"); + } + if (!validateTizenPackageID(m_context.widgetConfig.tzPkgid)) { + _E("tizen package ID is already used"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::PackageAlreadyInstalled, + "package is already installed"); + } + } +} + +void TaskConfiguration::CheckRDSSupportStep() +{ + //update needs RDS support to go ahead if REINSTALL command is given + if(m_context.isUpdateMode) + { + if (!checkSupportRDSUpdateIfReinstall(m_widgetConfig)) { + ThrowMsg(Jobs::WidgetInstall::Exceptions::NotSupportRDSUpdate, + "RDS update failed"); + } + } +} + +bool TaskConfiguration::validateTizenApplicationID( + const WrtDB::TizenAppId &tizenAppId) +{ + _D("tizen application ID = [%ls]", tizenAppId.c_str()); + + regex_t reg; + if (regcomp(®, REG_TIZENID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + return false; + } + + if (regexec(®, DPL::ToUTF8String(tizenAppId).c_str(), 0, NULL, 0) + == REG_NOMATCH) + { + regfree(®); + return false; + } + regfree(®); + return true; +} + +bool TaskConfiguration::validateTizenPackageID( + const WrtDB::TizenPkgId &tizenPkgId) +{ + _D("tizen application ID = [%ls]", tizenPkgId.c_str()); + + regex_t reg; + if (regcomp(®, REG_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) + { + _D("Regcomp failed"); + return false; + } + if (regexec(®, DPL::ToUTF8String(tizenPkgId).c_str(), 0, NULL, 0) == REG_NOMATCH) + { + regfree(®); + return false; + } + regfree(®); + return true; +} + +bool TaskConfiguration::checkWidgetUpdate( + const WidgetUpdateInfo &update) +{ + if (update.existingVersion.IsNull() || update.incomingVersion.IsNull()) { + return false; + } + + _D("existing version = '%ls", update.existingVersion->Raw().c_str()); + _D("incoming version = '%ls", update.incomingVersion->Raw().c_str()); + _D("Tizen AppID = %ls", update.tzAppId.c_str()); + + m_context.widgetConfig.tzAppid = update.tzAppId; + + if (!!update.existingVersion || + m_context.mode.extension == + InstallMode::ExtensionType::DIR) { + return true; + } + + return false; +} + +WidgetUpdateInfo TaskConfiguration::detectWidgetUpdate( + const ConfigParserData &configInfo, + const WrtDB::TizenAppId &tizenId) +{ + _D("Checking up widget package for config.xml..."); + OptionalWidgetVersion incomingVersion; + + if (!configInfo.version.IsNull()) { + incomingVersion = + DPL::Optional( + WidgetVersion(*configInfo.version)); + } + + WidgetDAOReadOnly dao(tizenId); + + OptionalWidgetVersion optVersion; + DPL::OptionalString version = dao.getVersion(); + if (!version.IsNull()) { + optVersion = OptionalWidgetVersion(WidgetVersion(*version)); + } + + return WidgetUpdateInfo( + dao.getTzAppId(), + optVersion, + incomingVersion); +} + +void TaskConfiguration::ApplicationTypeStep() //TODO: is this really needed as WAC is not supported? +{ + AppType widgetAppType = APP_TYPE_UNKNOWN; + FOREACH(iterator, m_widgetConfig.nameSpaces) { + _D("namespace = [%ls]", (*iterator).c_str()); + + if (*iterator == ConfigurationNamespace::TizenWebAppNamespaceName) { + widgetAppType = APP_TYPE_TIZENWEBAPP; + break; + } + } + + m_context.widgetConfig.webAppType = widgetAppType; + + _D("type = [%s]", m_context.widgetConfig.webAppType.getApptypeToString().c_str()); +} + +void TaskConfiguration::ResourceEncryptionStep() +{ + m_context.needEncryption = false; + FOREACH(it, m_widgetConfig.settingsList) + { + if (it->m_name == SETTING_VALUE_ENCRYPTION && + it->m_value == SETTING_VALUE_ENCRYPTION_ENABLE) + { + _D("resource need encryption"); + m_context.needEncryption = true; + } + } +} + +void TaskConfiguration::InstallationFSLocationStep() +{ + if (m_context.mode.installTime != InstallMode::InstallTime::PRELOAD) { + FOREACH(it, m_widgetConfig.settingsList) { + if (it->m_name == SETTING_VALUE_INSTALLTOEXT_NAME) { + if (it->m_value == SETTING_VALUE_INSTALLTOEXT_AUTO) { + m_context.locationType = INSTALL_LOCATION_TYPE_AUTO; + } else if (it->m_value == SETTING_VALUE_INSTALLTOEXT_PREPER_EXT) { + m_context.locationType = + INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } else { + m_context.locationType = + INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + } + break; + } + } + } +} + +bool TaskConfiguration::checkSupportRDSUpdateIfReinstall(const WrtDB::ConfigParserData + &configInfo) +{ + if (m_context.mode.command == + InstallMode::Command::REINSTALL) + { + DPL::String configValue = SETTING_VALUE_ENCRYPTION_DISABLE; + DPL::String dbValue = SETTING_VALUE_ENCRYPTION_DISABLE; + + WidgetDAOReadOnly dao(m_context.widgetConfig.tzAppid); + WrtDB::WidgetSettings widgetSettings; + dao.getWidgetSettings(widgetSettings); + + FOREACH(it, widgetSettings) { + if (it->settingName == SETTING_VALUE_ENCRYPTION) { + dbValue = it->settingValue; + } + } + + FOREACH(data, configInfo.settingsList) + { + if (data->m_name == SETTING_VALUE_ENCRYPTION) + { + configValue = data->m_value; + } + } + if (configValue != dbValue) { + _E("Not Support RDS mode because of encryption setting"); + return false; + } + } + + return true; +} + +} +} diff --git a/src_mobile/jobs/widget_install/task_configuration.h b/src_mobile/jobs/widget_install/task_configuration.h new file mode 100644 index 0000000..39e4701 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_configuration.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_configuration.h + * @version 1.0 + * @author Tomasz Iwanek + * @brief header file for configuration task + */ +#ifndef TASK_CONFIGURATION_H +#define TASK_CONFIGURATION_H + +#include + +#include +#include + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { + +class TaskConfiguration : public DPL::TaskDecl +{ + InstallerContext& m_context; + std::string m_tempDir; + WrtDB::ConfigParserData &m_widgetConfig; + std::string m_configuration; + + WidgetUpdateInfo m_widgetUpdateInfo; + + void parseWidgetXMLConfig( + const std::string &widgetSource, + const std::string &tempPath, + WrtDB::PackagingType pkgType, + bool isReinstall); + + static WidgetUpdateInfo detectWidgetUpdate( + const WrtDB::ConfigParserData &configInfo, + const WrtDB::TizenAppId &tizenId); + + bool validateTizenApplicationID(const WrtDB::TizenAppId &tizenAppId); + bool validateTizenPackageID(const WrtDB::TizenPkgId &tizenPkgId); + bool checkWidgetUpdate(const WidgetUpdateInfo &update); + void ApplicationTypeStep(const WrtDB::ConfigParserData &configInfo); + bool checkSupportRDSUpdateIfReinstall(const WrtDB::ConfigParserData &configInfo); + bool getDefaultExternalStorage(); + bool getMMCStatus(); + + std::shared_ptr pkgMgrInterface(); + + //steps + void StartStep(); + + void SetupTempDirStep(); + void UnzipConfigurationStep(); + void ParseXMLConfigStep(); + + void TizenIdStep(); + void CheckAppRunningStateStep(); + void DetectUpdateInstallationStep(); + void PkgmgrStartStep(); + + void ApplicationTypeStep(); + void ResourceEncryptionStep(); + void InstallationFSLocationStep(); + + void ConfigureWidgetLocationStep(); + void CheckRDSSupportStep(); + + void AppendTasklistStep(); + void EndStep(); + +public: + TaskConfiguration(InstallerContext& context); +}; + +} +} + +#endif // TASK_CONFIGURATION_H diff --git a/src_mobile/jobs/widget_install/task_database.cpp b/src_mobile/jobs/widget_install/task_database.cpp new file mode 100644 index 0000000..9dc4e15 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_database.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_new_db_insert.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task database updating for widget + * update + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskDatabase::TaskDatabase(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_handleToRemove(INVALID_WIDGET_HANDLE), + m_handle(INVALID_WIDGET_HANDLE) +{ + AddStep(&TaskDatabase::StartStep); + AddStep(&TaskDatabase::StepRegisterExternalFiles); + AddStep(&TaskDatabase::StepWrtDBInsert); + AddStep(&TaskDatabase::StepAceDBInsert); + AddStep(&TaskDatabase::StepSecurityOriginDBInsert); + AddStep(&TaskDatabase::StepWidgetInterfaceDBInsert); + AddStep(&TaskDatabase::StepRemoveExternalFiles); + AddStep(&TaskDatabase::StepLiveboxDBInsert); + AddStep(&TaskDatabase::EndStep); + + AddAbortStep(&TaskDatabase::StepAbortDBInsert); + AddAbortStep(&TaskDatabase::StepAbortAceDBInsert); + AddAbortStep(&TaskDatabase::StepAbortWidgetInterfaceDBInsert); +} + +void TaskDatabase::StepWrtDBInsert() +{ + Try + { + /* Set install Time */ + time(&m_context.widgetConfig.installedTime); + + if (m_context.isUpdateMode) { //update + _D("Registering widget... (update)"); + Try + { + m_handleToRemove = WidgetDAOReadOnly::getHandle( + m_context.widgetConfig.tzAppid); + + std::string makeAppid = + DPL::ToUTF8String(m_context.widgetConfig.tzAppid) + "." + + "backup"; + m_backAppId = DPL::FromUTF8String(makeAppid); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Given tizenId not found for update installation (Same GUID?)"); + ThrowMsg(Exceptions::DatabaseFailure, + "Given tizenId not found for update installation"); + } + + WidgetDAO::updateTizenAppId(m_context.widgetConfig.tzAppid, + m_backAppId); + WidgetDAO::registerWidget(m_context.widgetConfig.tzAppid, + m_context.widgetConfig, + m_context.widgetSecurity); + m_handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + } else { //new installation + _D("Registering widget..."); + WidgetDAO::registerWidget( + m_context.widgetConfig.tzAppid, + m_context.widgetConfig, + m_context.widgetSecurity); + m_handle = WidgetDAOReadOnly::getHandle( + m_context.widgetConfig.tzAppid); + } + + FOREACH(cap, m_context.staticPermittedDevCaps) { + _D("staticPermittedDevCaps : %ls smack status: %d", cap->first.c_str(), cap->second); + } + + _D("Widget registered"); + } + Catch(WidgetDAO::Exception::DatabaseError) + { + _E("Database failure!"); + ReThrowMsg(Exceptions::InsertNewWidgetFailed, "Database failure!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Database failure!"); + ReThrowMsg(Exceptions::InsertNewWidgetFailed, "Database failure!"); + } +} + +void TaskDatabase::StepAceDBInsert() +{ + _D("Inserting Ace database entry. New handle: %d", m_handle); + if (INVALID_WIDGET_HANDLE != m_handleToRemove) { + _D("Removing old insallation. Handle: %d", m_handleToRemove); + if (ACE_OK != ace_unregister_widget( + static_cast(m_handleToRemove))) + { + _W("Error while removing ace entry for previous insallation"); + } + } + + if (!AceApi::registerAceWidget(m_handle, m_context.widgetConfig, + m_context.widgetSecurity.getCertificateList())) + { + _E("ace database insert failed"); + ThrowMsg(Exceptions::UpdateFailed, + "Update failure. ace_register_widget failed"); + } + _D("Ace data inserted"); +} + +void TaskDatabase::StepSecurityOriginDBInsert() +{ + _D("Create Security origin database"); + // automatically create security origin database + using namespace SecurityOriginDB; + using namespace WrtDB; + + SecurityOriginDAO dao(m_context.locations->getPkgId()); + + // Checking privilege list for setting security origin exception data + FOREACH(it, m_context.widgetConfig.configInfo.privilegeList) { + std::map::const_iterator result = + g_W3CPrivilegeTextMap.find(DPL::ToUTF8String(it->name)); + if (result != g_W3CPrivilegeTextMap.end()) { + if (result->second == FEATURE_USER_MEDIA) { + dao.setPrivilegeSecurityOriginData(result->second, false); + } else if (result->second == FEATURE_FULLSCREEN_MODE) { + continue; + } else { + dao.setPrivilegeSecurityOriginData(result->second); + } + } + } +} + +void TaskDatabase::StepWidgetInterfaceDBInsert() +{ + _D("Create Widget Interface database"); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + + // backup database + if (m_context.isUpdateMode) { + std::string dbPath = WidgetInterfaceDAO::databaseFileName(handle); + std::string backupDbPath = dbPath; + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + _D("\"%s\" to \"%s\"", dbPath.c_str(), backupDbPath.c_str()); + if (0 != std::rename(dbPath.c_str(), backupDbPath.c_str())) { + _E("widget interface database backup failed"); + ThrowMsg(Exceptions::UpdateFailed, + "widget interface database backup failed"); + } + } + + Try + { + // automatically create widget interface database + WidgetInterfaceDAO dao(handle); + } + Catch(WidgetInterfaceDAO::Exception::DatabaseError) + { + _E("widget interface database create failed"); + ThrowMsg(Exceptions::UpdateFailed, + "widget interface database create failed"); + } +} + +void TaskDatabase::StepRegisterExternalFiles() +{ + WrtDB::ExternalLocationList externalLocationsUpdate = + m_context.locations->listExternalLocations(); + if (m_context.isUpdateMode) { //update + Try + { + WidgetDAO dao(m_context.widgetConfig.tzAppid); + WrtDB::ExternalLocationList externalLocationsDB = + dao.getWidgetExternalLocations(); + FOREACH(file, externalLocationsDB) + { + if (std::find(externalLocationsUpdate.begin(), + externalLocationsUpdate.end(), + *file) == externalLocationsUpdate.end()) + { + m_externalLocationsToRemove.push_back(*file); + } + } + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Given tizenId not found for update installation (Same GUID?)"); + ThrowMsg(Exceptions::UpdateFailed, + "Given tizenId not found for update installation"); + } + } + _D("Registering external files:"); + FOREACH(file, externalLocationsUpdate) + { + _D(" -> %s", (*file).c_str()); + } + + //set external locations to be registered + m_context.widgetConfig.externalLocations = externalLocationsUpdate; +} + +void TaskDatabase::StepRemoveExternalFiles() +{ + if (!m_externalLocationsToRemove.empty()) { + _D("Removing external files:"); + } + + FOREACH(file, m_externalLocationsToRemove) + { + if (WrtUtilFileExists(*file)) { + _D(" -> %s", (*file).c_str()); + if (-1 == TEMP_FAILURE_RETRY(remove(file->c_str()))) { + ThrowMsg(Exceptions::RemovingFileFailure, + "Failed to remove external file"); + } + } else if (WrtUtilDirExists(*file)) { + _D(" -> %s", (*file).c_str()); + if (!WrtUtilRemove(*file)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Failed to remove external directory"); + } + } else { + _W(" -> %s(no such a path)", (*file).c_str()); + } + } +} + +void TaskDatabase::StepAbortDBInsert() +{ + _W("[DB Update Task] Aborting... (DB Clean)"); + Try + { + if (m_context.isUpdateMode) { + WidgetDAO::unregisterWidget(m_context.widgetConfig.tzAppid); + WidgetDAO::updateTizenAppId(m_backAppId, + m_context.widgetConfig.tzAppid); + } else { + WidgetDAO::unregisterWidget(m_context.widgetConfig.tzAppid); + } + _D("Cleaning DB successful!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Failed to handle StepAbortDBClean!"); + } +} + +void TaskDatabase::StepAbortAceDBInsert() +{ + _W("[DB Update Task] ACE DB Aborting... (DB Clean)"); + + ace_unregister_widget(static_cast(m_handle)); + // Remove also old one. If it was already updated nothing wrong will happen, + // but if not old widget will be removed. + if (INVALID_WIDGET_HANDLE != m_handleToRemove) { + ace_unregister_widget(static_cast(m_handle)); + } + + if (!AceApi::registerAceWidgetFromDB(m_handleToRemove)) + { + _E("ace database restore failed"); + } + _D("Ace data inserted"); +} + +void TaskDatabase::StepAbortWidgetInterfaceDBInsert() +{ + _D("[DB Update Task] Widget interface Aborting..."); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + std::string dbPath = WidgetInterfaceDAO::databaseFileName(handle); + + // remove database + if (remove(dbPath.c_str()) != 0) { + _W("Fail to remove"); + } + + // rollback database + if (m_context.isUpdateMode) { + std::string backupDbPath = dbPath; + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + _D("\"%s\" to \"%s\"", dbPath.c_str(), backupDbPath.c_str()); + if (0 != std::rename(backupDbPath.c_str(), dbPath.c_str())) { + _W("Fail to rollback"); + } + } +} + +void TaskDatabase::StepLiveboxDBInsert() +{ + if (m_context.widgetConfig.configInfo.m_livebox.size() <= 0) { + return; + } + + std::string tizenId = DPL::ToUTF8String(m_context.widgetConfig.tzAppid); + + // insert specific information to web livebox db + for (auto it = m_context.widgetConfig.configInfo.m_livebox.begin(); + it != m_context.widgetConfig.configInfo.m_livebox.end(); ++it) + { + std::string boxId = DPL::ToUTF8String((**it).m_liveboxId); + std::string boxType; + if ((**it).m_type.empty()) { + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String((**it).m_type); + } + _D("livebox id: %s", boxId.c_str()); + _D("livebox type: %s", boxType.c_str()); + + int autoLaunch = (**it).m_autoLaunch == L"true" ? 1 : 0; + _D("livebox auto-launch: %d", autoLaunch); + + int mouseEvent = (**it).m_boxInfo.m_boxMouseEvent == L"true" ? 1 : 0; + _D("livebox mouse-event: %d", mouseEvent); + + int pdFastOpen = (**it).m_boxInfo.m_pdFastOpen == L"true" ? 1 : 0; + _D("livebox pd fast-open: %d", pdFastOpen); + + web_provider_livebox_insert_box_info( + boxId.c_str(), tizenId.c_str(), boxType.c_str(), + autoLaunch, mouseEvent, pdFastOpen); + } +} + +void TaskDatabase::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskDatabase::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_database.h b/src_mobile/jobs/widget_install/task_database.h new file mode 100644 index 0000000..1291c36 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_database.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_database.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task database updating + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskDatabase : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + WrtDB::ExternalLocationList m_externalLocationsToRemove; + + //TODO: temporary needed until security-server start to use pkgName instead + //of widget handle + WrtDB::DbWidgetHandle m_handleToRemove; + WrtDB::DbWidgetHandle m_handle; + WrtDB::TizenAppId m_backAppId; + + void StepRegisterExternalFiles(); + void StepWrtDBInsert(); + void StepAceDBInsert(); + void StepSecurityOriginDBInsert(); + void StepWidgetInterfaceDBInsert(); + void StepRemoveExternalFiles(); + void StepLiveboxDBInsert(); + + void StepAbortDBInsert(); + void StepAbortAceDBInsert(); + void StepAbortWidgetInterfaceDBInsert(); + + void StartStep(); + void EndStep(); + + public: + TaskDatabase(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H diff --git a/src_mobile/jobs/widget_install/task_encrypt_resource.cpp b/src_mobile/jobs/widget_install/task_encrypt_resource.cpp new file mode 100644 index 0000000..3966ca8 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_encrypt_resource.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_ecnrypt_resource.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task encrypt resource + */ +#include "task_encrypt_resource.h" + +#undef __USE_FILE_OFFSET64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 8192; // bytes +const std::size_t ENCRYPTION_DEC_CHUNK_SIZE = 4; // bytes + +std::set& getSupportedForEncryption() +{ + static std::set encryptSet; + if (encryptSet.empty()) { + encryptSet.insert(".html"); + encryptSet.insert(".htm"); + encryptSet.insert(".css"); + encryptSet.insert(".js"); + } + return encryptSet; +} + +bool isSupportedForEncryption(const std::string &file) +{ + size_t foundKey = file.rfind("."); + if (std::string::npos != foundKey) { + std::string mimeType = file.substr(foundKey); + std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(), + ::tolower); + + return getSupportedForEncryption().count(mimeType) > 0; + } + return false; +} + +/** + * Opens a file. + * + * @param path Path to a file. + * @param mode Mode. + * @return Stream handle. + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +FILE* openFile(const std::string& path, const std::string& mode) +{ + FILE* result = NULL; + + do + { + result = fopen(path.c_str(), mode.c_str()); + } while ((NULL == result) && (EINTR == errno)); + + if (NULL == result) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed, + "Could not open file " << path); + } + + return result; +} + +/** + * Reads bytes from a stream. + * + * @param buffer Buffer to read the bytes into. + * @param count Number of bytes to read. + * @param stream Stream to read from. + * @return Number of bytes read + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream) +{ + std::size_t result = std::fread(buffer, + sizeof(unsigned char), + count, + stream); + + if (result != count) + { + int error = errno; + if (0 != std::ferror(stream)) + { + if (EINTR != error) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::ErrorExternalInstallingFailure, + "Error while reading data" << + " [" << DPL::GetErrnoString(error) << "]"); + } + } + } + + return result; +} + +/** + * Writes bytes to a stream. + * + * @param buffer Data to write. + * @param count Number of bytes. + * @param stream Stream to write to. + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream) +{ + std::size_t bytesWritten = 0; + std::size_t bytesToWrite = 0; + do + { + bytesToWrite = count - bytesWritten; + bytesWritten = std::fwrite(buffer + bytesWritten, + sizeof(unsigned char), + count - bytesWritten, + stream); + if ((bytesWritten != bytesToWrite) && (EINTR != errno)) + { + int error = errno; + ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed, + "Error while writing data" << + " [" << DPL::GetErrnoString(error) << "]"); + } + } while ((bytesWritten != bytesToWrite) && (EINTR == errno)); +} + +int ssmEncrypt(InstallMode::InstallTime time, std::string pkgId, const char* + inChunk, int inBytes, char** outChunk, int *outBytes) +{ + if (time == InstallMode::InstallTime::PRELOAD) { + return ssm_encrypt_preloaded_application(inChunk, inBytes, + outChunk, outBytes); + } else { + return ssm_encrypt(pkgId.c_str(), pkgId.length(), + inChunk, inBytes, + outChunk, outBytes); + } +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskEncryptResource::TaskEncryptResource(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskEncryptResource::StartStep); + AddStep(&TaskEncryptResource::StepEncryptResource); + AddStep(&TaskEncryptResource::EndStep); +} + +void TaskEncryptResource::StepEncryptResource() +{ + _D("Step Encrypt resource"); + + EncryptDirectory(m_context.locations->getSourceDir()); +} + +void TaskEncryptResource::EncryptDirectory(std::string path) +{ + FTS *fts; + FTSENT *ftsent; + char * const paths[] = { const_cast(path.c_str()), NULL }; + + if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) { + //ERROR + int error = errno; + _W("%s: fts_open failed with error: %s", __PRETTY_FUNCTION__, strerror(error)); + ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: " + << path); + } + + while ((ftsent = fts_read(fts)) != NULL) { + switch (ftsent->fts_info) { + case FTS_DP: + case FTS_DC: + case FTS_D: + case FTS_DEFAULT: + case FTS_SLNONE: + //directories, non-regular files, dangling symbolic links + break; + case FTS_F: + case FTS_NSOK: + case FTS_SL: + //regular files and other objects that can be counted + if (isSupportedForEncryption(ftsent->fts_path)) { + EncryptFile(ftsent->fts_path); + } + break; + case FTS_NS: + case FTS_DOT: + case FTS_DNR: + case FTS_ERR: + default: + _W("%s: traversal failed on file: %s with error: %s", __PRETTY_FUNCTION__, ftsent->fts_path, strerror(ftsent->fts_errno)); + ThrowMsg(Exceptions::EncryptionFailed, "Error reading file"); + break; + } + } + + if (fts_close(fts) == -1) { + int error = errno; + _W("%s: fts_close failed with error: %s", __PRETTY_FUNCTION__, strerror(error)); + } +} + +void TaskEncryptResource::EncryptFile(const std::string &fileName) +{ + _D("Encrypt file: %s", fileName.c_str()); + std::string encFile = fileName + ".enc"; + + struct stat info; + memset(&info, 0, sizeof(info)); + if (stat(fileName.c_str(), &info) != 0) + { + int error = errno; + ThrowMsg(Exceptions::EncryptionFailed, + "Could not access file " << fileName << + "[" << DPL::GetErrnoString(error) << "]"); + } + const std::size_t fileSize = info.st_size; + if (0 == fileSize) { + _D("%s size is 0, so encryption is skiped", fileName.c_str()); + return; + } + + // If update installed preload web, should skip encryption. + if (!(m_context.mode.rootPath == InstallMode::RootPath::RO && + m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + + DPL::ScopedFClose inFile(openFile(fileName, "r")); + DPL::ScopedFClose outFile(openFile(encFile, "w")); + + const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE + ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize); + + std::unique_ptr inChunk(new unsigned char[chunkSize]); + std::size_t bytesRead = 0; + /* TODO : pkgId should change to appId after wrt-client label changed. */ + std::string pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + do + { + bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get()); + if (0 != bytesRead) { + int outDecSize = 0; + char *outChunk = NULL; + if (0 != ssmEncrypt(m_context.mode.installTime, pkgId, + (char*)inChunk.get(), (int)bytesRead, + &outChunk, &outDecSize)) { + ThrowMsg(Exceptions::EncryptionFailed, + "Encryption Failed using TrustZone"); + } + + std::stringstream toString; + toString << outDecSize; + + writeBytes((unsigned char*)toString.str().c_str(), + sizeof(int), outFile.Get()); + writeBytes((unsigned char*)outChunk, outDecSize, outFile.Get()); + delete outChunk; + } + inChunk.reset(new unsigned char[chunkSize]); + + } while (0 == std::feof(inFile.Get())); + + outFile.Reset(); + inFile.Reset(); + + _D("File encrypted successfully"); + _D("Remove plain-text file: %s", fileName.c_str()); + if (0 != unlink(fileName.c_str())) + { + Throw(Exceptions::EncryptionFailed); + } + + _D("Rename encrypted file"); + if (0 != std::rename(encFile.c_str(), fileName.c_str())) + { + Throw(Exceptions::EncryptionFailed); + } + } + + WrtDB::EncryptedFileInfo fileInfo; + fileInfo.fileName = DPL::FromUTF8String(fileName); + fileInfo.fileSize = fileSize; + + m_context.widgetConfig.encryptedFiles.insert(fileInfo); +} + +void TaskEncryptResource::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskEncryptResource::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ECRYPTION_FILES, + "Ecrypt resource files"); + + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_encrypt_resource.h b/src_mobile/jobs/widget_install/task_encrypt_resource.h new file mode 100644 index 0000000..b89a992 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_encrypt_resource.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_encrypt_resource.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_RESOURCE_ENCRYPT_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_RESOURCE_ENCRYPT_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskEncryptResource : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + std::string tempInstalledPath; + + void StepEncryptResource(); + + void StartStep(); + void EndStep(); + + void EncryptDirectory(std::string path); + void EncryptFile(const std::string &fileName); + + public: + explicit TaskEncryptResource(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_ENCRYPT_RESOURCE_H_ */ diff --git a/src_mobile/jobs/widget_install/task_file_manipulation.cpp b/src_mobile/jobs/widget_install/task_file_manipulation.cpp new file mode 100644 index 0000000..5e08d28 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_file_manipulation.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2010 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task database updating + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WEBAPP_DEFAULT_UID 5000 +#define WEBAPP_DEFAULT_GID 5000 + +namespace { +const mode_t PRIVATE_STORAGE_MODE = 0700; +const mode_t SHARED_STORAGE_MODE = 0755; +} + +using namespace WrtDB; + +namespace { +const char* GLIST_RES_DIR = "res"; + +bool _FolderCopy(std::string source, std::string dest) +{ + DIR* dir = opendir(source.c_str()); + if (NULL == dir) { + return false; + } + + struct dirent dEntry; + struct dirent *dEntryResult; + int return_code; + + do { + struct stat statInfo; + return_code = readdir_r(dir, &dEntry, &dEntryResult); + if (dEntryResult != NULL && return_code == 0) { + std::string fileName = dEntry.d_name; + std::string fullName = source + "/" + fileName; + + if (stat(fullName.c_str(), &statInfo) != 0) { + closedir(dir); + return false; + } + + if (S_ISDIR(statInfo.st_mode)) { + if (("." == fileName) || (".." == fileName)) { + continue; + } + std::string destFolder = dest + "/" + fileName; + WrtUtilMakeDir(destFolder); + + if (!_FolderCopy(fullName, destFolder)) { + closedir(dir); + return false; + } + } + + std::string destFile = dest + "/" + fileName; + std::ifstream infile(fullName); + std::ofstream outfile(destFile); + outfile << infile.rdbuf(); + outfile.close(); + infile.close(); + } + } while (dEntryResult != NULL && return_code == 0); + closedir(dir); + return true; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskFileManipulation::TaskFileManipulation(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_extHandle(NULL) +{ + AddStep(&TaskFileManipulation::StartStep); + AddStep(&TaskFileManipulation::StepCheckInstallLocation); + AddStep(&TaskFileManipulation::StepPrepareRootDirectory); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) + { + AddStep(&TaskFileManipulation::StepUnzipWgtFile); + } + AddStep(&TaskFileManipulation::EndStep); + + AddAbortStep(&TaskFileManipulation::StepAbortPrepareRootDirectory); +} + +void TaskFileManipulation::StepCheckInstallLocation() +{ + _D("StepCheckInstallLocation"); + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + return; + } + + std::string installedPath = WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + WidgetUnzip wgtUnzip(m_context.requestedPath); + + if (m_context.locationType == INSTALL_LOCATION_TYPE_AUTO) { + int storage = 0; + // vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT) + // 0 : phone internal memory + // 1 : SD card + if (vconf_get_int("db/setting/default_memory/download_application", + &storage)) { + _E("vconf_get_int(db/setting/default_memory/download_application) \ + failed."); + } + _D("default setting : storage [%d]", storage); + if (storage) { + m_context.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } else { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + if(!wgtUnzip.checkAvailableSpace(installedPath)) { + m_context.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } + } + } + + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + int mmcStatus; + if (vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS, &mmcStatus)) { + _E("vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS) failed."); + mmcStatus = VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED; + } + + if (VCONFKEY_SYSMAN_MMC_MOUNTED != mmcStatus) { + _D("mmcStatus is MMC_REMOVED or NOT_MOUNTED."); + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + } + } + + if (m_context.locationType == INSTALL_LOCATION_TYPE_INTERNAL_ONLY) { + if(!wgtUnzip.checkAvailableSpace(installedPath)) { + ThrowMsg(Exceptions::OutOfStorageFailed, "There is no space for installation"); + } + } +} + +void TaskFileManipulation::StepPrepareRootDirectory() +{ + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + prepareExternalDir(); + } else { + std::string widgetPath = m_context.locations->getPackageInstallationDir(); + std::string widgetBinPath = m_context.locations->getBinaryDir(); + std::string widgetSrcPath = m_context.locations->getSourceDir(); + + WrtUtilMakeDir(widgetPath); + + _D("Create resource directory"); + WrtUtilMakeDir(widgetBinPath); + WrtUtilMakeDir(widgetSrcPath); + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD) { + std::string userWidgetDir = m_context.locations->getUserDataRootDir(); + WrtUtilMakeDir(userWidgetDir); + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_DIR_CREATE, + "Widget Directory Created"); +} + +void TaskFileManipulation::StepUnzipWgtFile() +{ + if (m_context.widgetConfig.packagingType != PKG_TYPE_HOSTED_WEB_APP) { + std::string instDir; + if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) { + instDir = m_context.locations->getPackageInstallationDir(); + } else { + instDir = m_context.locations->getSourceDir(); + } + + _D("unzip file to %s", instDir.c_str()); + + WidgetUnzip wgtUnzip(m_context.requestedPath); + wgtUnzip.unzipWgtFile(instDir); + } else { + _D("From browser installation - unzip is not done"); + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_UNZIP_WGT, + "Unzip Wgt file"); +} + +void TaskFileManipulation::StepAbortPrepareRootDirectory() +{ + _D("[Create Root Directory] Aborting.... (Rename path)"); + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + if (m_context.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().postUpgrade(false); + } else { + WidgetInstallToExtSingleton::Instance().postInstallation(false); + } + WidgetInstallToExtSingleton::Instance().deinitialize(); + } else { + std::string widgetPath; + widgetPath = m_context.locations->getPackageInstallationDir(); + if (!WrtUtilRemove(widgetPath)) { + _E("Error occurs during removing existing folder"); + } + // Remove user data directory if preload web app. + std::string userData = m_context.locations->getUserDataRootDir(); + if (0 == access(userData.c_str(), F_OK)) { + if (!WrtUtilRemove(userData)) { + _E("Error occurs during removing user data directory"); + } + } + } +} + +void TaskFileManipulation::prepareExternalDir() +{ + _D("Step prepare to install in exernal directory"); + Try { + std::string pkgid = + DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + WidgetInstallToExtSingleton::Instance().initialize(pkgid); + + std::unique_ptr zipFile(new + DPL::ZipInput(m_context.requestedPath)); + double unzipSize = zipFile->GetTotalUncompressedSize(); + int folderSize = (int)(unzipSize / (1024 * 1024)) + 1; + + GList *list = NULL; + app2ext_dir_details* dirDetail = NULL; + + dirDetail = (app2ext_dir_details*) calloc(1, + sizeof( + app2ext_dir_details)); + if (NULL == dirDetail) { + ThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "error in app2ext"); + } + dirDetail->name = strdup(GLIST_RES_DIR); + dirDetail->type = APP2EXT_DIR_RO; + list = g_list_append(list, dirDetail); + + if (m_context.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().preUpgrade(list, + folderSize); + } else { + WidgetInstallToExtSingleton::Instance().preInstallation(list, + folderSize); + } + free(dirDetail); + g_list_free(list); + + /* make bin directory */ + std::string widgetBinPath = m_context.locations->getBinaryDir(); + WrtUtilMakeDir(widgetBinPath); + std::string sourceDir = m_context.locations->getSourceDir(); + WrtUtilMakeDir(sourceDir); + } + Catch(DPL::ZipInput::Exception::OpenFailed) { + ReThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "Error during \ + create external folder "); + } + Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) + { + ReThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "Error during create external folder "); + } +} + +void TaskFileManipulation::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskFileManipulation::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_file_manipulation.h b/src_mobile/jobs/widget_install/task_file_manipulation.h new file mode 100644 index 0000000..113ca72 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_file_manipulation.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_db_update.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for installer task database updating + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_FILE_MANIPULATION_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_FILE_MANIPULATION_UPDATE_H + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskFileManipulation : + public DPL::TaskDecl +{ + InstallerContext& m_context; + app2ext_handle *m_extHandle; + + // install internal location + void StepCheckInstallLocation(); + void StepPrepareRootDirectory(); + void StepUnzipWgtFile(); + void StepAbortPrepareRootDirectory(); + void StepLinkForPreload(); + void StartStep(); + void EndStep(); + + // install external location + void prepareExternalDir(); + + public: + TaskFileManipulation(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_FILE_MANIPULATION_H diff --git a/src_mobile/jobs/widget_install/task_install_ospsvc.cpp b/src_mobile/jobs/widget_install/task_install_ospsvc.cpp new file mode 100644 index 0000000..15923f9 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_install_ospsvc.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_install_ospsvc.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task install osp service + */ +#include "task_install_ospsvc.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +const int MAX_BUF_SIZE = 128; +const char* OSP_INSTALL_STR1 = "/usr/etc/package-manager/backend/tpk -iv "; +const char* OSP_INSTALL_STR2 = " -p "; +} + +namespace Jobs { +namespace WidgetInstall { +TaskInstallOspsvc::TaskInstallOspsvc(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskInstallOspsvc::StartStep); + AddStep(&TaskInstallOspsvc::StepInstallOspService); + AddStep(&TaskInstallOspsvc::EndStep); +} + +void TaskInstallOspsvc::StepInstallOspService() +{ + _D("Step: installation for osp service"); + + std::ostringstream commStr; + commStr << OSP_INSTALL_STR1<< BashUtils::escape_arg( + m_context.locations->getPackageInstallationDir()) + << OSP_INSTALL_STR2 << m_context.certLevel; + _D("osp install command : %s", commStr.str().c_str()); + + char readBuf[MAX_BUF_SIZE]; + FILE *fd; + fd = popen(commStr.str().c_str(), "r"); + if (NULL == fd) { + _E("Failed to installtion osp service"); + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + + if (fgets(readBuf, MAX_BUF_SIZE, fd) == NULL) + { + _E("Failed to installtion osp service.\ + Inability of reading file."); + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + _D("return value : %s", readBuf); + + int result = atoi(readBuf); + if (0 != result) { + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + + pclose(fd); +} + +void TaskInstallOspsvc::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskInstallOspsvc::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_INSTALL_OSPSVC, + "Installed Osp servcie"); + + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_install_ospsvc.h b/src_mobile/jobs/widget_install/task_install_ospsvc.h new file mode 100644 index 0000000..3e3a7e6 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_install_ospsvc.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_install_ospsvc.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskInstallOspsvc : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void StepInstallOspService(); + + void StepAbortInstall(); + + void StartStep(); + void EndStep(); + + // return callback + static int StatusCallback( + int req_id, const char *pkg_type, const char *pkg_name, + const char *key, const char *val, const void *pmsg, + void *priv_data); + + public: + explicit TaskInstallOspsvc(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ */ diff --git a/src_mobile/jobs/widget_install/task_manifest_file.cpp b/src_mobile/jobs/widget_install/task_manifest_file.cpp new file mode 100755 index 0000000..12f19bf --- /dev/null +++ b/src_mobile/jobs/widget_install/task_manifest_file.cpp @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_manifest_file.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEFAULT_ICON_NAME "icon.png" +#define DEFAULT_PREVIEW_NAME "preview.png" + +using namespace WrtDB; + +namespace { +typedef std::map LanguageTagMap; + +const char* const STR_TRUE = "true"; +const char* const STR_FALSE = "false"; +const char* const STR_NODISPLAY = "nodisplay"; + +LanguageTagMap getLanguageTagMap() +{ + LanguageTagMap map; + +#define ADD(tag, l_tag) map.insert(std::make_pair(L###tag, L###l_tag)); +#include "languages.def" +#undef ADD + + return map; +} + +DPL::OptionalString getLangTag(const DPL::String& tag) +{ + static LanguageTagMap TagsMap = + getLanguageTagMap(); + + DPL::String langTag = tag; + + _D("Trying to map language tag: %ls", langTag.c_str()); + size_t pos = langTag.find_first_of(L'_'); + if (pos != DPL::String::npos) { + langTag.erase(pos); + } + DPL::OptionalString ret; + + LanguageTagMap::iterator it = TagsMap.find(langTag); + if (it != TagsMap.end()) { + ret = it->second; + _D("Mapping IANA Language tag to language tag: %ls -> %ls", langTag.c_str(), (*ret).c_str()); + } + + return ret; +} +} + +namespace Jobs { +namespace WidgetInstall { +const char * TaskManifestFile::encoding = "UTF-8"; + +TaskManifestFile::TaskManifestFile(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_context(inCont), + writer(NULL) +{ + if (m_context.isUpdateMode) { + // for widget update. + AddStep(&TaskManifestFile::stepBackupIconFiles); + AddStep(&TaskManifestFile::stepCopyIconFiles); + AddStep(&TaskManifestFile::stepCopyLiveboxFiles); + AddStep(&TaskManifestFile::stepCopyAccountIconFiles); + AddStep(&TaskManifestFile::stepCreateExecFile); + AddStep(&TaskManifestFile::stepCreateLinkNPPluginsFile); + AddStep(&TaskManifestFile::stepGenerateManifest); + AddAbortStep(&TaskManifestFile::stepAbortIconFiles); + } else { + AddStep(&TaskManifestFile::stepCopyIconFiles); + AddStep(&TaskManifestFile::stepCopyLiveboxFiles); + AddStep(&TaskManifestFile::stepCopyAccountIconFiles); + AddStep(&TaskManifestFile::stepCreateExecFile); + AddStep(&TaskManifestFile::stepCreateLinkNPPluginsFile); + AddStep(&TaskManifestFile::stepGenerateManifest); + } +} + +TaskManifestFile::~TaskManifestFile() +{} + +void TaskManifestFile::stepCreateExecFile() +{ + std::string exec = m_context.locations->getExecFile(); + std::string clientExeStr = GlobalConfig::GetWrtClientExec(); + +#ifdef MULTIPROCESS_SERVICE_SUPPORT + //default widget + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << 0; + std::string controlExec = exec; + controlExec.append(postfix.str()); + + errno = 0; + if (symlink(clientExeStr.c_str(), controlExec.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + + // app-control widgets + unsigned int indexMax = 0; + FOREACH(it, m_context.widgetConfig.configInfo.appControlList) { + if (it->m_index > indexMax) { + indexMax = it->m_index; + } + } + + for (std::size_t i = 1; i <= indexMax; ++i) { + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << i; + std::string controlExec = exec; + controlExec.append(postfix.str()); + errno = 0; + if (symlink(clientExeStr.c_str(), controlExec.c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } +#else + //default widget + _D("link -s %s %s", clientExeStr.c_str(), exec.c_str()); + errno = 0; + if (symlink(clientExeStr.c_str(), exec.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } +#endif + // creation of box symlink + ConfigParserData::LiveboxList& liveboxList = + m_context.widgetConfig.configInfo.m_livebox; + if (!liveboxList.empty()) { + std::string boxExec = "/usr/bin/WebProcess"; + std::string boxSymlink = m_context.locations->getExecFile(); + boxSymlink += ".d-box"; + + errno = 0; + if (symlink(boxExec.c_str(), boxSymlink.c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_EXECFILE, + "Widget execfile creation Finished"); +} + +void TaskManifestFile::stepCreateLinkNPPluginsFile() +{ + _D("stepCreateLinkNPPluginsFile"); + if (0 == access(m_context.locations->getNPPluginsDir().c_str(), F_OK)) { + _D("This webapp has NPPlugins"); + std::string pluginsExec = "/usr/bin/PluginProcess"; + errno = 0; + if (symlink(pluginsExec.c_str(), + m_context.locations->getNPPluginsExecFile().c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to create symbolic link for npplugins : %ls", + DPL::GetErrnoString(error).c_str()); + } + } + } +} + +void TaskManifestFile::stepCopyIconFiles() +{ + _D("CopyIconFiles"); + + //This function copies icon to desktop icon path. For each locale avaliable + //which there is at least one icon in widget for, icon file is copied. + //Coping prioritize last positions when coping. If there is several icons + //with given locale, the one, that will be copied, will be icon + //which is declared by tag later than the others in config.xml of + // widget + + std::vector generatedLocales; + + WrtDB::WidgetRegisterInfo::LocalizedIconList & icons = + m_context.widgetConfig.localizationData.icons; + + for (WrtDB::WidgetRegisterInfo::LocalizedIconList::const_iterator + icon = icons.begin(); + icon != icons.end(); + ++icon) + { + DPL::String src = icon->src; + FOREACH(locale, icon->availableLocales) + { + _D("Icon for locale: %ls is: %ls", (*locale).c_str(), src.c_str()); + + if (std::find(generatedLocales.begin(), generatedLocales.end(), + *locale) != generatedLocales.end()) + { + _D("Skipping - has that locale"); + continue; + } else { + generatedLocales.push_back(*locale); + } + + DPL::Utils::Path sourceFile(m_context.locations->getSourceDir()); + if (!locale->empty()) { + sourceFile /= "locales"; + sourceFile /= *locale; + } + sourceFile /= src; + + DPL::Utils::Path targetFile(GlobalConfig::GetUserWidgetDesktopIconPath()); + targetFile /= getIconTargetFilename(*locale, sourceFile.Extension()); + + if (m_context.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + m_context.locations->setIconTargetFilenameForLocale( + targetFile.Fullpath()); + } + + _D("Copying icon: %s -> %s", sourceFile.Filename().c_str(), targetFile.Filename().c_str()); + + icon_list.push_back(targetFile.Fullpath()); + + Try + { + DPL::FileInput input(sourceFile.Fullpath()); + DPL::FileOutput output(targetFile.Fullpath()); + DPL::Copy(&input, &output); + } + + Catch(DPL::FileInput::Exception::Base) + { + // Error while opening or closing source file + //ReThrowMsg(InstallerException::CopyIconFailed, + // sourceFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + + Catch(DPL::FileOutput::Exception::Base) + { + // Error while opening or closing target file + //ReThrowMsg(InstallerException::CopyIconFailed, + // targetFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + + Catch(DPL::CopyFailed) + { + // Error while copying + //ReThrowMsg(InstallerException::CopyIconFailed, + // targetFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_COPY_ICONFILE, + "Widget iconfile copy Finished"); +} + +void TaskManifestFile::stepCopyLiveboxFiles() +{ + _D("Copy Livebox Files"); + + using namespace WrtDB; + ConfigParserData &data = m_context.widgetConfig.configInfo; + ConfigParserData::LiveboxList liveBoxList = data.m_livebox; + + if (liveBoxList.size() <= 0) { + return; + } + + std::ostringstream sourceFile; + std::ostringstream targetFile; + + FOREACH (boxIt, liveBoxList) { + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + (**boxIt).m_boxInfo.m_boxSize; + FOREACH (sizeIt, boxSizeList) { + std::string preview = DPL::ToUTF8String((*sizeIt).m_preview); + if (preview.empty()) { + continue; + } + sourceFile << m_context.locations->getSourceDir() << "/"; + sourceFile << preview; + targetFile << m_context.locations->getSharedDataDir() << "/"; + targetFile << (**boxIt).m_liveboxId << "."; + targetFile << DPL::ToUTF8String((*sizeIt).m_size) << "." << DEFAULT_PREVIEW_NAME; + + copyFile(sourceFile.str(), targetFile.str()); + + // clear stream objects + sourceFile.str(""); + targetFile.str(""); + } + // check this livebox has icon element + std::string icon = DPL::ToUTF8String((**boxIt).m_icon); + if (icon.empty()) { + continue; + } + sourceFile << m_context.locations->getSourceDir() << "/"; + sourceFile << icon; + targetFile << m_context.locations->getSharedDataDir() << "/"; + targetFile << (**boxIt).m_liveboxId << "." << DEFAULT_ICON_NAME; + + copyFile(sourceFile.str(), targetFile.str()); + + // clear stream objects + sourceFile.str(""); + targetFile.str(""); + } + m_context.job->UpdateProgress( + InstallerContext::INSTALL_COPY_LIVEBOX_FILES, + "Livebox files copy Finished"); +} + +void TaskManifestFile::stepCopyAccountIconFiles() +{ + _D("Copy Account icon files"); + WrtDB::ConfigParserData::AccountProvider account = + m_context.widgetConfig.configInfo.accountProvider; + + if (account.m_iconSet.empty()) { + _D("Widget doesn't contain Account"); + return; + } + + FOREACH(it, account.m_iconSet) { + std::string sourceFile = m_context.locations->getSourceDir() + + '/' + + DPL::ToUTF8String(it->second); + std::string targetFile = m_context.locations->getSharedResourceDir() + + '/' + + DPL::ToUTF8String(it->second); + copyFile(sourceFile, targetFile); + } +} + +void TaskManifestFile::copyFile(const std::string& sourceFile, + const std::string& targetFile) +{ + Try + { + DPL::FileInput input(sourceFile); + DPL::FileOutput output(targetFile); + DPL::Copy(&input, &output); + } + Catch(DPL::Exception) + { + _E("Failed to file copy. %s to %s", sourceFile.c_str(), targetFile.c_str()); + ReThrowMsg(Exceptions::CopyIconFailed, "Error during file copy."); + } +} + +bool TaskManifestFile::addBoxUiApplication(Manifest& manifest) +{ + UiApplication uiApp; + std::string postfix = ".d-box"; + static bool isAdded = false; + + Try + { + if (isAdded) { + _D("UiApplication for d-box is already added"); + return false; + } + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + uiApp.setMultiple(false); + setWidgetName(manifest, uiApp); + setWidgetIcons(uiApp); + + // appid for box is like [webapp id].d-box + setWidgetIds(manifest, uiApp, postfix); + // executable path for box is like [app path]/bin/[webapp id].d-box + setWidgetExecPath(uiApp, postfix); + manifest.addUiApplication(uiApp); + isAdded = true; + + return true; + } + Catch(DPL::Exception) + { + _E("Adding UiApplication on xml is failed."); + isAdded = false; + return false; + } +} + +void TaskManifestFile::stepBackupIconFiles() +{ + _D("Backup Icon Files"); + + backup_dir << m_context.locations->getBackupDir() << "/"; + + backupIconFiles(); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_BACKUP_ICONFILE, + "New Widget icon file backup Finished"); +} + +void TaskManifestFile::stepAbortIconFiles() +{ + _D("Abrot Icon Files"); + FOREACH(it, icon_list) + { + _D("Remove Update Icon : %s", (*it).c_str()); + unlink((*it).c_str()); + } + + std::ostringstream b_icon_dir; + b_icon_dir << backup_dir.str() << "icons"; + + std::list fileList; + getFileList(b_icon_dir.str().c_str(), fileList); + + FOREACH(back_icon, fileList) + { + std::ostringstream res_file; + res_file << GlobalConfig::GetUserWidgetDesktopIconPath(); + res_file << "/" << (*back_icon); + + std::ostringstream backup_file; + backup_file << b_icon_dir.str() << "/" << (*back_icon); + + Try + { + DPL::FileInput input(backup_file.str()); + DPL::FileOutput output(res_file.str()); + DPL::Copy(&input, &output); + } + Catch(DPL::FileInput::Exception::Base) + { + _E("Restoration icon File Failed. %s to %s", backup_file.str().c_str(), res_file.str().c_str()); + } + + Catch(DPL::FileOutput::Exception::Base) + { + _E("Restoration icon File Failed. %s to %s", backup_file.str().c_str(), res_file.str().c_str()); + } + Catch(DPL::CopyFailed) + { + _E("Restoration icon File Failed. %s to %s", backup_file.str().c_str(), res_file.str().c_str()); + } + } +} + +DPL::String TaskManifestFile::getIconTargetFilename( + const DPL::String& languageTag, const std::string & ext) const +{ + DPL::OStringStream filename; + TizenAppId appid = m_context.widgetConfig.tzAppid; + + filename << DPL::ToUTF8String(appid).c_str(); + + if (!languageTag.empty()) { + DPL::OptionalString tag = getLangTag(languageTag); // translate en -> + // en_US etc + if (tag.IsNull()) { + tag = languageTag; + } + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + + if (locale.empty()) { + filename << L"." << languageTag; + } else { + filename << L"." << locale; + } + } + + if(!ext.empty()) + { + filename << L"." + DPL::FromUTF8String(ext); + } + return filename.str(); +} + +void TaskManifestFile::saveLocalizedKey(std::ofstream &file, + const DPL::String& key, + const DPL::String& languageTag) +{ + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(languageTag); + + file << key; + if (!locale.empty()) { + file << "[" << locale << "]"; + } + file << "="; +} + +void TaskManifestFile::backupIconFiles() +{ + _D("Backup Icon Files"); + + std::ostringstream b_icon_dir; + b_icon_dir << backup_dir.str() << "icons"; + + _D("Create icon backup folder : %s", b_icon_dir.str().c_str()); + WrtUtilMakeDir(b_icon_dir.str()); + + std::list fileList; + getFileList(GlobalConfig::GetUserWidgetDesktopIconPath(), fileList); + std::string appid = DPL::ToUTF8String(m_context.widgetConfig.tzAppid); + + FOREACH(it, fileList) + { + if (0 == (strncmp((*it).c_str(), appid.c_str(), + strlen(appid.c_str())))) + { + std::ostringstream icon_file, backup_icon; + icon_file << GlobalConfig::GetUserWidgetDesktopIconPath(); + icon_file << "/" << (*it); + + backup_icon << b_icon_dir.str() << "/" << (*it); + + _D("Backup icon file %s to %s", icon_file.str().c_str(), backup_icon.str().c_str()); + Try + { + DPL::FileInput input(icon_file.str()); + DPL::FileOutput output(backup_icon.str()); + DPL::Copy(&input, &output); + } + Catch(DPL::FileInput::Exception::Base) + { + _E("Backup Desktop File Failed."); + ReThrowMsg(Exceptions::BackupFailed, icon_file.str()); + } + + Catch(DPL::FileOutput::Exception::Base) + { + _E("Backup Desktop File Failed."); + ReThrowMsg(Exceptions::BackupFailed, backup_icon.str()); + } + Catch(DPL::CopyFailed) + { + _E("Backup Desktop File Failed."); + ReThrowMsg(Exceptions::BackupFailed, backup_icon.str()); + } + unlink((*it).c_str()); + } + } +} + +void TaskManifestFile::getFileList(const char* path, + std::list &list) +{ + DIR* dir = opendir(path); + if (!dir) { + _E("icon directory doesn't exist"); + ThrowMsg(Exceptions::FileOperationFailed, path); + } + + struct dirent entry; + struct dirent *result; + int return_code; + errno = 0; + for (return_code = readdir_r(dir, &entry, &result); + result != NULL && return_code == 0; + return_code = readdir_r(dir, &entry, &result)) + { + if (strcmp(entry.d_name, ".") == 0 || + strcmp(entry.d_name, "..") == 0) + { + continue; + } + std::string file_name = entry.d_name; + list.push_back(file_name); + } + + if (return_code != 0 || errno != 0) { + _E("readdir_r() failed with %s", DPL::GetErrnoString().c_str()); + } + + if (-1 == closedir(dir)) { + _E("Failed to close dir: %s with error: %s", path, DPL::GetErrnoString().c_str()); + } +} + +void TaskManifestFile::stepGenerateManifest() +{ + TizenPkgId pkgid = m_context.widgetConfig.tzPkgid; + manifest_name = pkgid + L".xml"; + manifest_file += L"/tmp/" + manifest_name; + + //libxml - init and check + LibxmlSingleton::Instance().init(); + + writeManifest(manifest_file); + + std::ostringstream destFile; + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + destFile << WrtDB::GlobalConfig::GetPreloadManifestPath() << "/"; + } else { + destFile << WrtDB::GlobalConfig::GetManifestPath() << "/"; + } + + destFile << DPL::ToUTF8String(manifest_name); + commit_manifest = destFile.str(); + _D("Commiting manifest file : %s", commit_manifest.c_str()); + + commitManifest(); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_MANIFEST, + "Widget Manifest Creation Finished"); +} + +void TaskManifestFile::commitManifest() +{ + + if (!(m_context.mode.rootPath == InstallMode::RootPath::RO && + m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + _D("cp %ls %s", manifest_file.c_str(), commit_manifest.c_str()); + + DPL::FileInput input(DPL::ToUTF8String(manifest_file)); + DPL::FileOutput output(commit_manifest); + DPL::Copy(&input, &output); + _D("Manifest writen to: %s", commit_manifest.c_str()); + + //removing temp file + unlink((DPL::ToUTF8String(manifest_file)).c_str()); + manifest_file = DPL::FromUTF8String(commit_manifest); + } +} + +void TaskManifestFile::writeManifest(const DPL::String & path) +{ + _D("Generating manifest file : %ls", path.c_str()); + Manifest manifest; + UiApplication uiApp; + +#ifdef MULTIPROCESS_SERVICE_SUPPORT + //default widget content + std::stringstream postfix; + // index 0 is reserved + postfix << AppControlPrefix::PROCESS_PREFIX << 0; + setWidgetExecPath(uiApp, postfix.str()); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setWidgetDescription(manifest); + setWidgetManifest(manifest); + setWidgetOtherInfo(uiApp); + setAppCategory(uiApp); + setMetadata(uiApp); + // move to the last of this procedure + //setLiveBoxInfo(manifest); + setAccount(manifest); + setPrivilege(manifest); + manifest.addUiApplication(uiApp); + + //app-control content + ConfigParserData::AppControlInfoList appControlList = + m_context.widgetConfig.configInfo.appControlList; + FOREACH(it, appControlList) { + UiApplication uiApp; + + uiApp.setTaskmanage(true); + uiApp.setNodisplay(true); +#ifdef MULTIPROCESS_SERVICE_SUPPORT_INLINE + uiApp.setTaskmanage(ConfigParserData::AppControlInfo::Disposition::INLINE != it->m_disposition); + uiApp.setMultiple(ConfigParserData::AppControlInfo::Disposition::INLINE == it->m_disposition); +#endif + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << it->m_index; + setWidgetExecPath(uiApp, postfix.str()); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setAppControlInfo(uiApp, *it); + setAppCategory(uiApp); + setMetadata(uiApp); + manifest.addUiApplication(uiApp); + } + // TODO: Must fix again with right method + // The mainapp attiribute must be set + // when there are multiple uiapps in mainfest + setLiveBoxInfo(manifest); +#else + //default widget content + setWidgetExecPath(uiApp); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setWidgetDescription(manifest); + setWidgetManifest(manifest); + setWidgetOtherInfo(uiApp); + setAppControlsInfo(uiApp); + setAppCategory(uiApp); + setMetadata(uiApp); + // move to the last of this procedure + //setLiveBoxInfo(manifest); + setAccount(manifest); + setPrivilege(manifest); + + manifest.addUiApplication(uiApp); + // TODO: Must fix again with right method + // The mainapp attiribute must be set + // when there are multiple uiapps in mainfest + setLiveBoxInfo(manifest); +#endif + + manifest.generate(path); + _D("Manifest file serialized"); +} + +void TaskManifestFile::setWidgetExecPath(UiApplication & uiApp, + const std::string &postfix) +{ + std::string exec = m_context.locations->getExecFile(); + if (!postfix.empty()) { + exec.append(postfix); + } + _D("exec = %s", exec.c_str()); + uiApp.setExec(DPL::FromASCIIString(exec)); +} + +void TaskManifestFile::setWidgetName(Manifest & manifest, + UiApplication & uiApp) +{ + bool defaultNameSaved = false; + + DPL::OptionalString defaultLocale = + m_context.widgetConfig.configInfo.defaultlocale; + std::pair defaultLocalizedData; + //labels + FOREACH(localizedData, m_context.widgetConfig.configInfo.localizedDataSet) + { + Locale i = localizedData->first; + DPL::OptionalString tag = getLangTag(i); // translate en -> en_US etc + if (tag.IsNull()) { + tag = i; + } + DPL::OptionalString name = localizedData->second.name; + generateWidgetName(manifest, uiApp, tag, name, defaultNameSaved); + + //store default locale localized data + if (!!defaultLocale && defaultLocale == i) { + defaultLocalizedData = *localizedData; + } + } + + if (!!defaultLocale && !defaultNameSaved) { + DPL::OptionalString name = defaultLocalizedData.second.name; + generateWidgetName(manifest, + uiApp, + DPL::OptionalString::Null, + name, + defaultNameSaved); + } +} + +void TaskManifestFile::setWidgetIds(Manifest & manifest, + UiApplication & uiApp, + const std::string &postfix) +{ + //appid + TizenAppId appid = m_context.widgetConfig.tzAppid; + if (!postfix.empty()) { + appid = DPL::FromUTF8String(DPL::ToUTF8String(appid).append(postfix)); + } + uiApp.setAppid(appid); + + //extraid + if (!!m_context.widgetConfig.guid) { + uiApp.setExtraid(*m_context.widgetConfig.guid); + } else { + if (!appid.empty()) { + uiApp.setExtraid(DPL::String(L"http://") + appid); + } + } + + //type + uiApp.setType(DPL::FromASCIIString("webapp")); + manifest.setType(L"wgt"); +} + +void TaskManifestFile::generateWidgetName(Manifest & manifest, + UiApplication &uiApp, + const DPL::OptionalString& tag, + DPL::OptionalString name, + bool & defaultNameSaved) +{ + if (!!name) { + if (!!tag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + + if (!locale.empty()) { + uiApp.addLabel(LabelType(*name, *tag)); + } else { + uiApp.addLabel(LabelType(*name)); + manifest.addLabel(LabelType(*name)); + } + } else { + defaultNameSaved = true; + uiApp.addLabel(LabelType(*name)); + manifest.addLabel(LabelType(*name)); + } + } +} + +void TaskManifestFile::setWidgetIcons(UiApplication & uiApp) +{ + //TODO this file will need to be updated when user locale preferences + //changes. + bool defaultIconSaved = false; + + DPL::OptionalString defaultLocale = + m_context.widgetConfig.configInfo.defaultlocale; + + std::vector generatedLocales; + WrtDB::WidgetRegisterInfo::LocalizedIconList & icons = + m_context.widgetConfig.localizationData.icons; + + for (WrtDB::WidgetRegisterInfo::LocalizedIconList::const_iterator + icon = icons.begin(); + icon != icons.end(); + ++icon) + { + FOREACH(locale, icon->availableLocales) + { + if (std::find(generatedLocales.begin(), generatedLocales.end(), + *locale) != generatedLocales.end()) + { + _D("Skipping - has that locale - already in manifest"); + continue; + } else { + generatedLocales.push_back(*locale); + } + + DPL::OptionalString tag = getLangTag(*locale); // translate en -> + // en_US etc + if (tag.IsNull()) { + tag = *locale; + } + + generateWidgetIcon(uiApp, tag, *locale, DPL::Utils::Path(icon->src).Extension(), defaultIconSaved); + } + } + if (!!defaultLocale && !defaultIconSaved) { + generateWidgetIcon(uiApp, DPL::OptionalString::Null, + DPL::String(), + std::string(), + defaultIconSaved); + } +} + +void TaskManifestFile::generateWidgetIcon(UiApplication & uiApp, + const DPL::OptionalString& tag, + const DPL::String& language, + const std::string & extension, + bool & defaultIconSaved) +{ + DPL::String locale; + if (!!tag) { + locale = LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + } else { + defaultIconSaved = true; + } + + DPL::String iconText; + iconText += getIconTargetFilename(language, extension); + + if (!locale.empty()) { + uiApp.addIcon(IconType(iconText, locale)); + } else { + uiApp.addIcon(IconType(iconText)); + } + std::ostringstream iconPath; + iconPath << GlobalConfig::GetUserWidgetDesktopIconPath() << "/"; + iconPath << getIconTargetFilename(locale, extension); + m_context.job->SendProgressIconPath(iconPath.str()); +} + +void TaskManifestFile::setWidgetDescription(Manifest & manifest) +{ + FOREACH(localizedData, m_context.widgetConfig.configInfo.localizedDataSet) + { + Locale i = localizedData->first; + DPL::OptionalString tag = getLangTag(i); // translate en -> en_US etc + if (tag.IsNull()) { + tag = i; + } + DPL::OptionalString description = localizedData->second.description; + generateWidgetDescription(manifest, tag, description); + } +} + +void TaskManifestFile::generateWidgetDescription(Manifest & manifest, + const DPL::OptionalString& tag, + DPL::OptionalString description) +{ + if (!!description) { + if (!!tag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + if (!locale.empty()) { + manifest.addDescription(DescriptionType(*description, locale)); + } else { + manifest.addDescription(DescriptionType(*description)); + } + } else { + manifest.addDescription(DescriptionType(*description)); + } + } +} + +void TaskManifestFile::setWidgetManifest(Manifest & manifest) +{ + manifest.setPackage(m_context.widgetConfig.tzPkgid); + + if (!!m_context.widgetConfig.version) { + manifest.setVersion(*m_context.widgetConfig.version); + } + DPL::String email = (!!m_context.widgetConfig.configInfo.authorEmail ? + *m_context.widgetConfig.configInfo.authorEmail : L""); + DPL::String href = (!!m_context.widgetConfig.configInfo.authorHref ? + *m_context.widgetConfig.configInfo.authorHref : L""); + DPL::String name = (!!m_context.widgetConfig.configInfo.authorName ? + *m_context.widgetConfig.configInfo.authorName : L""); + manifest.addAuthor(Author(email, href, L"", name)); + + if (!m_context.callerPkgId.empty()) { + manifest.setStoreClientId(m_context.callerPkgId); + } +} + +void TaskManifestFile::setWidgetOtherInfo(UiApplication & uiApp) +{ + FOREACH(it, m_context.widgetConfig.configInfo.settingsList) + { + if (!strcmp(DPL::ToUTF8String(it->m_name).c_str(), STR_NODISPLAY)) { + if (!strcmp(DPL::ToUTF8String(it->m_value).c_str(), STR_TRUE)) { + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + } else { + uiApp.setNodisplay(false); + uiApp.setTaskmanage(true); + } + } + } + //TODO + //There is no "X-TIZEN-PackageType=wgt" + //There is no X-TIZEN-PackageID in manifest "X-TIZEN-PackageID=" << + // DPL::ToUTF8String(*widgetID).c_str() + //There is no Comment in pkgmgr "Comment=Widget application" + //that were in desktop file +} + +void TaskManifestFile::setAppControlsInfo(UiApplication & uiApp) +{ + WrtDB::ConfigParserData::AppControlInfoList appControlList = + m_context.widgetConfig.configInfo.appControlList; + + if (appControlList.empty()) { + _D("Widget doesn't contain app control"); + return; + } + + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + FOREACH(it, appControlList) { + setAppControlInfo(uiApp, *it); + } +} + +void TaskManifestFile::setAppControlInfo(UiApplication & uiApp, + const WrtDB::ConfigParserData::AppControlInfo & service) +{ + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + AppControl appControl; + if (!service.m_operation.empty()) { + appControl.addOperation(service.m_operation); //TODO: encapsulation? + } + if (!service.m_uriList.empty()) { + FOREACH(uri, service.m_uriList) { + appControl.addUri(*uri); + } + } + if (!service.m_mimeList.empty()) { + FOREACH(mime, service.m_mimeList) { + appControl.addMime(*mime); + } + } + uiApp.addAppControl(appControl); +} + +void TaskManifestFile::setAppCategory(UiApplication &uiApp) +{ + WrtDB::ConfigParserData::CategoryList categoryList = + m_context.widgetConfig.configInfo.categoryList; + + if (categoryList.empty()) { + _D("Widget doesn't contain application category"); + return; + } + FOREACH(it, categoryList) { + if (!(*it).empty()) { + uiApp.addAppCategory(*it); + } + } +} + +void TaskManifestFile::setMetadata(UiApplication &uiApp) +{ + WrtDB::ConfigParserData::MetadataList metadataList = + m_context.widgetConfig.configInfo.metadataList; + + if (metadataList.empty()) { + _D("Web application doesn't contain metadata"); + return; + } + FOREACH(it, metadataList) { + MetadataType metadataType(it->key, it->value); + uiApp.addMetadata(metadataType); + } +} + +void TaskManifestFile::setLiveBoxInfo(Manifest& manifest) +{ + ConfigParserData::LiveboxList& liveboxList = + m_context.widgetConfig.configInfo.m_livebox; + + if (liveboxList.empty()) { + _D("no livebox"); + return; + } + + if (!addBoxUiApplication(manifest)) { + _D("error during adding UiApplication for d-box"); + return; + } + + FOREACH(it, liveboxList) { + _D("setLiveBoxInfo"); + LiveBoxInfo liveBox; + DPL::Optional ConfigInfo = *it; + DPL::String appid = m_context.widgetConfig.tzAppid; + + if (ConfigInfo->m_liveboxId != L"") { + size_t found = ConfigInfo->m_liveboxId.find_last_of(L"."); + if (found != std::string::npos) { + if (0 == ConfigInfo->m_liveboxId.compare(0, found, appid)) { + liveBox.setLiveboxId(ConfigInfo->m_liveboxId); + } else { + DPL::String liveboxId = + appid + DPL::String(L".") + ConfigInfo->m_liveboxId; + liveBox.setLiveboxId(liveboxId); + } + } else { + DPL::String liveboxId = + appid + DPL::String(L".") + ConfigInfo->m_liveboxId; + liveBox.setLiveboxId(liveboxId); + } + } + + if (ConfigInfo->m_primary != L"") { + liveBox.setPrimary(ConfigInfo->m_primary); + } + + if (ConfigInfo->m_updatePeriod != L"") { + liveBox.setUpdatePeriod(ConfigInfo->m_updatePeriod); + } + + std::list > boxLabelList; + if (!ConfigInfo->m_label.empty()) { + FOREACH(im, ConfigInfo->m_label) { + std::pair boxSize; + Locale i = (*im).first; + // translate en -> en_US etc + DPL::OptionalString tag = getLangTag(i); + if (tag.IsNull()) { + tag = i; + } + boxSize.first = (*tag); + boxSize.second = (*im).second; + boxLabelList.push_back(boxSize); + } + liveBox.setLabel(boxLabelList); + } + + DPL::String defaultLocale = + DPL::FromUTF8String(m_context.locations->getPackageInstallationDir()) + + DPL::String(L"/res/wgt/"); + + if (ConfigInfo->m_icon != L"") { + DPL::String icon = + DPL::FromUTF8String(m_context.locations->getSharedDataDir()) + + DPL::String(L"/") + + ConfigInfo->m_liveboxId + DPL::String(L".icon.png"); + liveBox.setIcon(icon); + } + + if (ConfigInfo->m_boxInfo.m_boxSrc.empty() || + ConfigInfo->m_boxInfo.m_boxSize.empty()) + { + _D("Widget doesn't contain box"); + return; + } else { + BoxInfoType box; + if (!ConfigInfo->m_boxInfo.m_boxSrc.empty()) { + if ((0 == ConfigInfo->m_boxInfo.m_boxSrc.compare(0, 4, L"http")) + || (0 == + ConfigInfo->m_boxInfo.m_boxSrc.compare(0, 5, L"https"))) + { + box.boxSrc = ConfigInfo->m_boxInfo.m_boxSrc; + } else { + box.boxSrc = defaultLocale + ConfigInfo->m_boxInfo.m_boxSrc; + } + } + + if (ConfigInfo->m_boxInfo.m_boxMouseEvent == L"true") { + std::string boxType; + if (ConfigInfo->m_type == L"") { + // in case of default livebox + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String(ConfigInfo->m_type); + } + + int box_scrollable = + web_provider_plugin_get_box_scrollable(boxType.c_str()); + + if (box_scrollable) { + box.boxMouseEvent = L"true"; + } else { + box.boxMouseEvent = L"false"; + } + } else { + box.boxMouseEvent = L"false"; + } + + if (ConfigInfo->m_boxInfo.m_boxTouchEffect == L"true") { + box.boxTouchEffect = L"true"; + } else { + box.boxTouchEffect= L"false"; + } + + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + ConfigInfo->m_boxInfo.m_boxSize; + FOREACH(it, boxSizeList) { + if (!(*it).m_preview.empty()) { + (*it).m_preview = + DPL::FromUTF8String(m_context.locations->getSharedDataDir()) + + DPL::String(L"/") + + ConfigInfo->m_liveboxId + DPL::String(L".") + + (*it).m_size + DPL::String(L".preview.png"); + } + box.boxSize.push_back((*it)); + } + + if (!ConfigInfo->m_boxInfo.m_pdSrc.empty() + && !ConfigInfo->m_boxInfo.m_pdWidth.empty() + && !ConfigInfo->m_boxInfo.m_pdHeight.empty()) + { + if ((0 == ConfigInfo->m_boxInfo.m_pdSrc.compare(0, 4, L"http")) + || (0 == ConfigInfo->m_boxInfo.m_pdSrc.compare(0, 5, L"https"))) + { + box.pdSrc = ConfigInfo->m_boxInfo.m_pdSrc; + } else { + box.pdSrc = defaultLocale + ConfigInfo->m_boxInfo.m_pdSrc; + } + box.pdWidth = ConfigInfo->m_boxInfo.m_pdWidth; + box.pdHeight = ConfigInfo->m_boxInfo.m_pdHeight; + } + liveBox.setBox(box); + } + manifest.addLivebox(liveBox); + } +} + +void TaskManifestFile::setAccount(Manifest& manifest) +{ + WrtDB::ConfigParserData::AccountProvider account = + m_context.widgetConfig.configInfo.accountProvider; + + AccountProviderType provider; + + if (account.m_iconSet.empty()) { + _D("Widget doesn't contain Account"); + return; + } + if (account.m_multiAccountSupport) { + provider.multiAccount = L"true"; + } else { + provider.multiAccount = L"false"; + } + provider.appid = m_context.widgetConfig.tzAppid; + + FOREACH(it, account.m_iconSet) { + std::pair icon; + + if (it->first == ConfigParserData::IconSectionType::DefaultIcon) { + icon.first = L"account"; + } else if (it->first == ConfigParserData::IconSectionType::SmallIcon) { + icon.first = L"account-small"; + } + + // account manifest requires absolute path for icon + // /opt/apps/[package]/shared/res/[icon_path] + icon.second = DPL::FromUTF8String(m_context.locations->getSharedResourceDir()) + + DPL::String(L"/") + + it->second; + provider.icon.push_back(icon); + } + + FOREACH(it, account.m_displayNameSet) { + provider.name.push_back(LabelType(it->second, it->first)); + } + + FOREACH(it, account.m_capabilityList) { + provider.capability.push_back(*it); + } + + Account accountInfo; + accountInfo.addAccountProvider(provider); + manifest.addAccount(accountInfo); +} + +void TaskManifestFile::setPrivilege(Manifest& manifest) +{ + WrtDB::ConfigParserData::PrivilegeList privileges = + m_context.widgetConfig.configInfo.privilegeList; + + PrivilegeType privilege; + + FOREACH(it, privileges) + { + privilege.addPrivilegeName(it->name); + } + + manifest.addPrivileges(privilege); +} + +void TaskManifestFile::StartStep() +{ + +} + +void TaskManifestFile::EndStep() +{ + +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_manifest_file.h b/src_mobile/jobs/widget_install/task_manifest_file.h new file mode 100644 index 0000000..afcb00d --- /dev/null +++ b/src_mobile/jobs/widget_install/task_manifest_file.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_manifest_file.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include + +#include + +#include +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskManifestFile : + public DPL::TaskDecl +{ + public: + + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ManifestValidationError) + DECLARE_EXCEPTION_TYPE(Base, ManifestParsingError) + + TaskManifestFile(InstallerContext &inCont); + virtual ~TaskManifestFile(); + + private: + //context data + InstallerContext &m_context; + + //TODO stepAbort + //steps + void stepCreateExecFile(); + void stepCopyIconFiles(); + void stepCopyLiveboxFiles(); + void stepCopyAccountIconFiles(); + void stepGenerateManifest(); + void stepCreateLinkNPPluginsFile(); + + void stepAbortParseManifest(); + + //For widget update + void stepBackupIconFiles(); + void stepAbortIconFiles(); + + void StartStep(); + void EndStep(); + + //private data + std::list icon_list; //TODO: this should be registered as + // external files + std::ostringstream backup_dir; + xmlTextWriterPtr writer; + DPL::String manifest_name; + DPL::String manifest_file; + std::string commit_manifest; + + //private methods + + void writeManifest(const DPL::String & path); + void commitManifest(); + + void setWidgetExecPath(UiApplication & uiApp, + const std::string &postfix = std::string()); + void setWidgetName(Manifest & manifest, + UiApplication & uiApp); + void setWidgetIds(Manifest & manifest, + UiApplication & uiApp, + const std::string &postfix = std::string()); + void setWidgetIcons(UiApplication & uiApp); + void setWidgetDescription(Manifest & manifest); + void setWidgetManifest(Manifest & manifest); + void setWidgetOtherInfo(UiApplication & uiApp); + void setAppControlsInfo(UiApplication & uiApp); + void setAppControlInfo(UiApplication & uiApp, + const WrtDB::ConfigParserData::AppControlInfo & service); + void setAppCategory(UiApplication & uiApp); + void setMetadata(UiApplication & uiApp); + void setLiveBoxInfo(Manifest& manifest); + void setAccount(Manifest& uiApp); + void setPrivilege(Manifest& manifest); + + void generateWidgetName(Manifest & manifest, + UiApplication &uiApp, + const DPL::OptionalString& tag, + DPL::OptionalString name, + bool & defaultNameSaved); + void generateWidgetDescription(Manifest & manifest, + const DPL::OptionalString& tag, + DPL::OptionalString description); + void generateWidgetIcon(UiApplication & uiApp, + const DPL::OptionalString& tag, + const DPL::String& language, const std::string &extension, + bool & defaultIconSaved); + void copyFile(const std::string& sourceFile, + const std::string& targetFile); + bool addBoxUiApplication(Manifest& manifest); + + //for widget update + void backupIconFiles(); + void getFileList(const char* path, std::list &list); + DPL::String getIconTargetFilename(const DPL::String& languageTag, + const std::string & ext) const; + + static void saveLocalizedKey(std::ofstream &file, + const DPL::String& key, + const DPL::String& languageTag); + + static const char * encoding; +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H */ diff --git a/src_mobile/jobs/widget_install/task_pkg_info_update.cpp b/src_mobile/jobs/widget_install/task_pkg_info_update.cpp new file mode 100644 index 0000000..4bd0898 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_pkg_info_update.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_pkg_info_update.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task information about package + * update + */ +#include "task_pkg_info_update.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +} + +namespace Jobs { +namespace WidgetInstall { +TaskPkgInfoUpdate::TaskPkgInfoUpdate(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskPkgInfoUpdate::StartStep); + AddStep(&TaskPkgInfoUpdate::StepPkgInfo); + AddStep(&TaskPkgInfoUpdate::StepSetCertiInfo); + AddStep(&TaskPkgInfoUpdate::EndStep); + AddStep(&TaskPkgInfoUpdate::StepSetEndofInstallation); + + AddAbortStep(&TaskPkgInfoUpdate::StepAbortCertiInfo); + AddAbortStep(&TaskPkgInfoUpdate::stepAbortParseManifest); +} + +void TaskPkgInfoUpdate::StepPkgInfo() +{ + int code = 0; + char* updateTags[3] = {NULL, }; + + char preloadTrue[] = "preload=true"; + char removableTrue[] = "removable=true"; + char removableFalse[] = "removable=false"; + + if (InstallMode::InstallTime::CSC == m_context.mode.installTime) { + updateTags[0] = preloadTrue; + if (m_context.mode.removable) { + updateTags[1] = removableTrue; + } else { + updateTags[1] = removableFalse; + } + updateTags[2] = NULL; + } + + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + m_manifest += "/usr/share/packages/"; + } else { + m_manifest += "/opt/share/packages/"; + } + m_manifest += DPL::ToUTF8String(m_context.widgetConfig.tzPkgid) + ".xml"; + _D("manifest file : %s", m_manifest.c_str()); + + if (m_context.isUpdateMode || ( + m_context.mode.rootPath == InstallMode::RootPath::RO + && m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + + code = pkgmgr_parser_parse_manifest_for_upgrade( + m_manifest.c_str(), (updateTags[0] == NULL) ? NULL : updateTags); + + if (code != 0) { + _E("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + } else { + code = pkgmgr_parser_parse_manifest_for_installation( + m_manifest.c_str(), (updateTags[0] == NULL) ? NULL : updateTags); + + if (code != 0) { + _E("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_PKGINFO_UPDATE, + "Manifest Update Finished"); + _D("Manifest parsed"); +} + +void TaskPkgInfoUpdate::StepSetCertiInfo() +{ + _D("StepSetCertiInfo"); + + if (pkgmgr_installer_create_certinfo_set_handle(&m_pkgHandle) < 0) { + _E("pkgmgrInstallerCreateCertinfoSetHandle fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to create certificate handle"); + } + + SetCertiInfo(SIGNATURE_AUTHOR); + SetCertiInfo(SIGNATURE_DISTRIBUTOR); + + if ((pkgmgr_installer_save_certinfo( + const_cast(DPL::ToUTF8String( + m_context.widgetConfig.tzPkgid).c_str()), + m_pkgHandle)) < 0) + { + _E("pkgmgrInstallerSaveCertinfo fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Installer Save Certinfo"); + } else { + _D("Succeed to save Certinfo"); + } + + if (pkgmgr_installer_destroy_certinfo_set_handle(m_pkgHandle) < 0) { + _E("pkgmgrInstallerDestroyCertinfoSetHandle fail"); + } +} + +void TaskPkgInfoUpdate::SetCertiInfo(int source) +{ + _D("Set CertiInfo to pkgmgr : %d", source); + CertificateChainList certificateChainList; + m_context.widgetSecurity.getCertificateChainList(certificateChainList, + (CertificateSource)source); + + FOREACH(it, certificateChainList) + { + _D("Insert certinfo to pkgmgr structure"); + + ValidationCore::CertificateCollection chain; + + if (false == chain.load(*it)) { + _E("Chain is broken"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Installer Save Certinfo"); + } + + if (!chain.sort()) { + _E("Chain failed at sorting"); + } + + ValidationCore::CertificateList list = chain.getCertificateList(); + + FOREACH(certIt, list) + { + pkgmgr_instcert_type instCertType; + + if (source == SIGNATURE_DISTRIBUTOR) { + bool distributor1 = false; + if (!(*certIt)->getCommonName().IsNull()) { + std::string + Name(DPL::ToUTF8String(*(*certIt)->getCommonName())); + std::string tizenStr("Tizen"); + if (0 == Name.compare(0, tizenStr.length(), tizenStr)) { + distributor1 = true; + } + } + + if (distributor1) { + _D("Set SIGNATURE_DISTRIBUTOR"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_DISTRIBUTOR_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_DISTRIBUTOR_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_DISTRIBUTOR_SIGNER_CERT; + } + } + } else { + _D("Set SIGNATURE_DISTRIBUTOR2"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_DISTRIBUTOR2_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_DISTRIBUTOR2_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_DISTRIBUTOR2_SIGNER_CERT; + } + } + } + } else { + _D("set SIGNATURE_AUTHOR"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_AUTHOR_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_AUTHOR_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_AUTHOR_SIGNER_CERT; + } + } + } + _D("cert type : %d", instCertType); + if ((pkgmgr_installer_set_cert_value( + m_pkgHandle, + instCertType, + const_cast(((*certIt)->getBase64()).c_str()))) < 0) + { + _E("pkgmgrInstallerSetCertValue fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Set CertValue"); + } + } + } +} + +void TaskPkgInfoUpdate::StepAbortCertiInfo() +{ + if ((pkgmgr_installer_delete_certinfo( + const_cast(DPL::ToUTF8String( + m_context.widgetConfig.tzPkgid).c_str()))) < + 0) + { + _E("pkgmgr_installer_delete_certinfo fail"); + } +} + +void TaskPkgInfoUpdate::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskPkgInfoUpdate::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_SET_CERTINFO, + "Save certinfo to pkgmgr"); + + _D("--------- : END ----------"); +} + +void TaskPkgInfoUpdate::stepAbortParseManifest() +{ + _E("[Parse Manifest] Abroting...."); + + int code = pkgmgr_parser_parse_manifest_for_uninstallation( + m_manifest.c_str(), NULL); + + if (0 != code) { + _W("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + int ret = unlink(m_manifest.c_str()); + if (0 != ret) { + _W("No manifest file found: %s", m_manifest.c_str()); + } +} + +void TaskPkgInfoUpdate::StepSetEndofInstallation() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_END, + "End installation"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_pkg_info_update.h b/src_mobile/jobs/widget_install/task_pkg_info_update.h new file mode 100644 index 0000000..e1e9235 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_pkg_info_update.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_pkg_info_update.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ + +#include +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPkgInfoUpdate : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void StepPkgInfo(); + void StepSetCertiInfo(); + void SetCertiInfo(int source); + void StepSetEndofInstallation(); + + void stepAbortParseManifest(); + void StepAbortCertiInfo(); + + void StartStep(); + void EndStep(); + + pkgmgr_instcertinfo_h m_pkgHandle; + std::string m_manifest; + + public: + explicit TaskPkgInfoUpdate(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ */ diff --git a/src_mobile/jobs/widget_install/task_prepare_files.cpp b/src_mobile/jobs/widget_install/task_prepare_files.cpp new file mode 100644 index 0000000..55791f4 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_prepare_files.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_generate_config.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "task_prepare_files.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +TaskPrepareFiles::TaskPrepareFiles(InstallerContext &installerContext) : + DPL::TaskDecl(this), + m_installerContext(installerContext) +{ + AddStep(&TaskPrepareFiles::StartStep); + AddStep(&TaskPrepareFiles::StepCopyFiles); + AddStep(&TaskPrepareFiles::EndStep); +} + +void TaskPrepareFiles::CopyFile(const std::string& source) +{ + if (source.empty()) { + _W("No source file specified"); + return; + } + + std::string filename = source; + size_t last = source.find_last_of("\\/"); + if (last != std::string::npos) { + filename = source.substr(last + 1); + } + std::string target = + m_installerContext.locations->getSourceDir() + '/' + + filename; + _D("source %s", source.c_str()); + _D("target %s", target.c_str()); + + Try + { + DPL::FileInput input(source); + DPL::FileOutput output(target); + DPL::Copy(&input, &output); + } + Catch(DPL::FileInput::Exception::Base) + { + _E("File input error"); + // Error while opening or closing source file + ReThrowMsg(Exceptions::CopyIconFailed, source); + } + Catch(DPL::FileOutput::Exception::Base) + { + _E("File output error"); + // Error while opening or closing target file + ReThrowMsg(Exceptions::CopyIconFailed, target); + } + Catch(DPL::CopyFailed) + { + _E("File copy error"); + // Error while copying + ReThrowMsg(Exceptions::CopyIconFailed, target); + } +} + +void TaskPrepareFiles::StepCopyFiles() +{ + CopyFile(m_installerContext.locations->getWidgetSource()); + + size_t last = m_installerContext.locations->getWidgetSource().find_last_of( + "\\/"); + std::string sourceDir = ""; + if (last != std::string::npos) { + sourceDir = m_installerContext.locations->getWidgetSource().substr( + 0, + last + + 1); + } + + _D("Icons copy..."); + FOREACH(it, m_installerContext.widgetConfig.configInfo.iconsList) { + std::ostringstream os; + _D("Coping: %s%ls", sourceDir.c_str(), (it->src).c_str()); + os << sourceDir << DPL::ToUTF8String(it->src); + CopyFile(os.str()); + } +} + +void TaskPrepareFiles::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskPrepareFiles::EndStep() +{ + _D("--------- : END ----------"); +} +} // namespace WidgetInstall +} // namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_prepare_files.h b/src_mobile/jobs/widget_install/task_prepare_files.h new file mode 100644 index 0000000..99458e8 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_prepare_files.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_prepare_files.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPrepareFiles : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_installerContext; + + void CopyFile(const std::string& source); + + // Steps + void StepCopyFiles(); + + void StartStep(); + void EndStep(); + + public: + explicit TaskPrepareFiles(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ */ diff --git a/src_mobile/jobs/widget_install/task_prepare_reinstall.cpp b/src_mobile/jobs/widget_install/task_prepare_reinstall.cpp new file mode 100644 index 0000000..4980e4d --- /dev/null +++ b/src_mobile/jobs/widget_install/task_prepare_reinstall.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_prepare_reinstall.cpp + * @author Jihoon Chung(jihoon.chung@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task prepare reinstalling + */ + +#include "task_prepare_reinstall.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const char* const KEY_DELETE = "#delete"; +const char* const KEY_ADD = "#add"; +const char* const KEY_MODIFY = "#modify"; +std::list keyList = {KEY_DELETE, KEY_ADD, KEY_MODIFY}; + +void verifyFile(const std::string &filePath) +{ + if (access(filePath.c_str(), F_OK) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, "File is missed " << filePath); + } +} + +std::string parseSubPath(const std::string& filePath) +{ + std::string subPath(""); + size_t pos = filePath.find_last_of('/') + 1; + + if (pos != std::string::npos) { + subPath = filePath.substr(0, pos); + } + return subPath; +} + +void createDir(const std::string& path) +{ + if (WrtUtilMakeDir(path)) { + _D("Create directory : %s", path.c_str()); + } else { + ThrowMsg(Exceptions::RDSDeltaFailure, "Fail to create dir" << path); + } +} +} // namespace anonymous + +TaskPrepareReinstall::TaskPrepareReinstall(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskPrepareReinstall::StartStep); + AddStep(&TaskPrepareReinstall::StepPrepare); + AddStep(&TaskPrepareReinstall::StepParseRDSDelta); + AddStep(&TaskPrepareReinstall::StepVerifyRDSDelta); + AddStep(&TaskPrepareReinstall::StepAddFile); + AddStep(&TaskPrepareReinstall::StepDeleteFile); + AddStep(&TaskPrepareReinstall::StepModifyFile); + AddStep(&TaskPrepareReinstall::EndStep); +} + +void TaskPrepareReinstall::StepPrepare() +{ + _D("Prepare"); + m_sourcePath = m_context.locations->getTemporaryPackageDir(); + m_sourcePath += "/"; + + m_installedPath = m_context.locations->getPackageInstallationDir(); + m_installedPath += "/"; +} + +void TaskPrepareReinstall::StepParseRDSDelta() +{ + _D("parse RDS delta"); + std::string rdsDeltaPath = m_sourcePath; + rdsDeltaPath += ".rds_delta"; + std::ifstream delta(rdsDeltaPath); + + if (!delta.is_open()) { + ThrowMsg(Exceptions::RDSDeltaFailure, "rds_delta file is missed"); + return; + } + + std::string line; + std::string key; + while (std::getline(delta, line) &&!delta.eof()) { + FOREACH(keyIt, keyList) { + if (line == *keyIt) { + _D("find key = [%s]", line.c_str()); + key = line; + break; + } + } + if (key == line || line.empty() || line == "\n") { + continue; + } + if (key == KEY_DELETE) { + m_deleteFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } else if (key == KEY_ADD) { + m_addFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } else if (key == KEY_MODIFY) { + m_modifyFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } + } +} + +void TaskPrepareReinstall::StepVerifyRDSDelta() +{ + _D("verify RDS delta"); + // Verify ADD file + FOREACH(file, m_addFileList) { + std::string addFilePath = m_sourcePath; + addFilePath += *file; + verifyFile(addFilePath); + } + // Verify DELETE file + FOREACH(file, m_deleteFileList) { + std::string deleteFilePath = m_installedPath; + deleteFilePath += *file; + verifyFile(deleteFilePath); + } + // Verify MODIFY file + FOREACH(file, m_modifyFileList) { + std::string newFilePath = m_sourcePath; + newFilePath += *file; + verifyFile(newFilePath); + + std::string existingFilePath = m_installedPath; + existingFilePath += *file; + verifyFile(existingFilePath); + } + _D("Finished veify RDS Delta"); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_RDS_DELTA_CHECK, + "RDS delta verify finished"); +} + +void TaskPrepareReinstall::StepAddFile() +{ + _D("Add file"); + FOREACH(file, m_addFileList) { + std::string newfile = m_sourcePath; + newfile += *file; + std::string destPath = m_installedPath; + destPath += *file; + + if (WrtUtilDirExists(newfile)) { + // In case of a new directory + createDir(destPath); + } else { + // In case of a new file + + // Parse directory and file separately + std::string subPath = parseSubPath(destPath); + if (subPath.empty()) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Invalid path given" << destPath); + } + + // Create a new directory + createDir(subPath); + + // Add file + if (rename(newfile.c_str(), destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to add file " << newfile); + } + _D("Add %s to %s", newfile.c_str(), destPath.c_str()); + } + } +} + +void TaskPrepareReinstall::StepDeleteFile() +{ + _D("Delete file"); + FOREACH(file, m_deleteFileList) { + std::string deleteFilePath = m_installedPath; + deleteFilePath += *file; + if (remove(deleteFilePath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to DELETE file " << deleteFilePath); + } + _D("Delete %s", deleteFilePath.c_str()); + } +} + +void TaskPrepareReinstall::StepModifyFile() +{ + _D("Modify file"); + FOREACH(file, m_modifyFileList) { + std::string destPath = m_installedPath; + destPath += *file; + if (remove(destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to delete existing file " << destPath); + } + + std::string newfile = m_sourcePath; + newfile += *file; + if (rename(newfile.c_str(), destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to move new file" << destPath); + } + _D("Replace %s to %s", newfile.c_str(), destPath.c_str()); + } +} + +void TaskPrepareReinstall::StartStep() +{ + _D("---------- : START ----------"); +} + +void TaskPrepareReinstall::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_RDS_PREPARE, + "RDS prepare finished"); + + _D("---------- : END ----------"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_prepare_reinstall.h b/src_mobile/jobs/widget_install/task_prepare_reinstall.h new file mode 100644 index 0000000..2828f27 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_prepare_reinstall.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_prepare_reinstall.h + * @author Jihoon Chung(jihoon.chung@samsung.com) + * @version 1.0 + * @brief Header file for installer task prepare reinstalling + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPrepareReinstall : + public DPL::TaskDecl +{ + public: + TaskPrepareReinstall(InstallerContext& context); + + private: + // install internal location + void StepPrepare(); + void StepParseRDSDelta(); + void StepVerifyRDSDelta(); + void StepAddFile(); + void StepDeleteFile(); + void StepModifyFile(); + + void StepAbortPrepareReinstall(); + + void StartStep(); + void EndStep(); + + InstallerContext& m_context; + // TODO : replace multimap + std::list m_addFileList; + std::list m_deleteFileList; + std::list m_modifyFileList; + std::string m_sourcePath; + std::string m_installedPath; +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H diff --git a/src_mobile/jobs/widget_install/task_process_config.cpp b/src_mobile/jobs/widget_install/task_process_config.cpp new file mode 100755 index 0000000..2c92f2c --- /dev/null +++ b/src_mobile/jobs/widget_install/task_process_config.cpp @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_process_config.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task widget config + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { // anonymous +const DPL::String BR = DPL::FromUTF8String("
"); +const std::string WINDGET_INSTALL_NETWORK_ACCESS = "network access"; +} + +namespace Jobs { +namespace WidgetInstall { + +TaskProcessConfig::TaskProcessConfig(InstallerContext& installContext) : + DPL::TaskDecl(this), + m_installContext(installContext) +{ + AddStep(&TaskProcessConfig::StartStep); + AddStep(&TaskProcessConfig::ReadLocaleFolders); + AddStep(&TaskProcessConfig::StepFillWidgetConfig); + AddStep(&TaskProcessConfig::ProcessLocalizedStartFiles); + AddStep(&TaskProcessConfig::ProcessBackgroundPageFile); + AddStep(&TaskProcessConfig::ProcessLocalizedIcons); + AddStep(&TaskProcessConfig::ProcessWidgetInstalledPath); + AddStep(&TaskProcessConfig::ProcessAppControlInfo); + AddStep(&TaskProcessConfig::ProcessSecurityModel); + AddStep(&TaskProcessConfig::StepVerifyFeatures); + AddStep(&TaskProcessConfig::StepVerifyLivebox); + AddStep(&TaskProcessConfig::StepCheckMinVersionInfo); + AddStep(&TaskProcessConfig::EndStep); +} + +void TaskProcessConfig::StepFillWidgetConfig() +{ + if (!fillWidgetConfig(m_installContext.widgetConfig, + m_installContext.widgetConfig.configInfo)) + { + _E("Widget configuration is illformed"); + ThrowMsg(Exception::ConfigParseFailed, "Widget configuration is illformed"); + } +} + +void TaskProcessConfig::ReadLocaleFolders() +{ + _D("Reading locale"); + //Adding default locale + m_localeFolders.insert(L""); + + std::string localePath = + m_installContext.locations->getSourceDir() + "/locales"; + + DIR* localeDir = opendir(localePath.c_str()); + if (!localeDir) { + _D("No /locales directory in the widget package."); + return; + } + + struct stat statStruct; + struct dirent dirent; + struct dirent *result; + int return_code; + errno = 0; + for (return_code = readdir_r(localeDir, &dirent, &result); + result != NULL && return_code == 0; + return_code = readdir_r(localeDir, &dirent, &result)) + { + DPL::String dirName = DPL::FromUTF8String(dirent.d_name); + std::string absoluteDirName = localePath + "/"; + absoluteDirName += dirent.d_name; + + if (stat(absoluteDirName.c_str(), &statStruct) != 0) { + _E("stat() failed with %s", DPL::GetErrnoString().c_str()); + continue; + } + + if (S_ISDIR(statStruct.st_mode)) { + //Yes, we ignore current, parent & hidden directories + if (dirName[0] != L'.') { + _D("Adding locale directory \"%ls\"", dirName.c_str()); + m_localeFolders.insert(dirName); + } + } + } + + if (return_code != 0 || errno != 0) { + _E("readdir_r() failed with %s", DPL::GetErrnoString().c_str()); + } + + if (-1 == closedir(localeDir)) { + _E("Failed to close dir: %s with error: %s", localePath.c_str(), DPL::GetErrnoString().c_str()); + } + + m_installContext.job->UpdateProgress(InstallerContext::INSTALL_WIDGET_CONFIG1, "Read locale folders"); +} + +void TaskProcessConfig::ProcessLocalizedStartFiles() +{ + typedef DPL::String S; + ProcessStartFile( + m_installContext.widgetConfig.configInfo.startFile, + m_installContext.widgetConfig.configInfo. + startFileContentType, + m_installContext.widgetConfig.configInfo.startFileEncoding, + true); + ProcessStartFile(S(L"index.htm"), S(L"text/html")); + ProcessStartFile(S(L"index.html"), S(L"text/html")); + ProcessStartFile(S(L"index.svg"), S(L"image/svg+xml")); + ProcessStartFile(S(L"index.xhtml"), S(L"application/xhtml+xml")); + ProcessStartFile(S(L"index.xht"), S(L"application/xhtml+xml")); + // TODO: we need better check if in current locales widget is valid + FOREACH(it, m_installContext.widgetConfig.localizationData.startFiles) { + if (it->propertiesForLocales.size() > 0) { + return; + } + } + ThrowMsg(Exceptions::InvalidStartFile, + "The Widget has no valid start file"); +} + +void TaskProcessConfig::ProcessStartFile(const DPL::OptionalString& path, + const DPL::OptionalString& type, + const DPL::OptionalString& encoding, + bool typeForcedInConfig) +{ + using namespace WrtDB; + + if (!!path) { + WidgetRegisterInfo::LocalizedStartFile startFileData; + startFileData.path = *path; + + FOREACH(i, m_localeFolders) { + DPL::String pathPrefix = *i; + if (!pathPrefix.empty()) { + pathPrefix = L"locales/" + pathPrefix + L"/"; + } + + DPL::String relativePath = pathPrefix + *path; + DPL::String absolutePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + relativePath; + _D("absolutePath : %ls", absolutePath.c_str()); + + // get property data from packaged app + if (WrtUtilFileExists(DPL::ToUTF8String(absolutePath))) { + WidgetRegisterInfo::StartFileProperties startFileProperties; + if (!!type) { + startFileProperties.type = *type; + } else { + startFileProperties.type = + MimeTypeUtils::identifyFileMimeType(absolutePath); + } + + //proceed only if MIME type is supported + if (MimeTypeUtils::isMimeTypeSupportedForStartFile( + startFileProperties.type)) + { + if (!!encoding) { + startFileProperties.encoding = *encoding; + } else { + MimeTypeUtils::MimeAttributes attributes = + MimeTypeUtils::getMimeAttributes( + startFileProperties.type); + if (attributes.count(L"charset") > 0) { + startFileProperties.encoding = + attributes[L"charset"]; + } else { + startFileProperties.encoding = L"UTF-8"; + } + } + + startFileData.propertiesForLocales[*i] = + startFileProperties; + } else { + //9.1.16.5.content.8 + //(there seems to be no similar requirement in .6, + //so let's throw only when mime type is + // provided explcitly in config.xml) + if (typeForcedInConfig) { + ThrowMsg(Exceptions::WidgetConfigFileInvalid, + "Unsupported MIME type for start file."); + } + } + } else { + // set property data for hosted start url + // Hosted start url only support TIZEN WebApp + if (m_installContext.widgetConfig.webAppType == + APP_TYPE_TIZENWEBAPP) + { + std::string startPath = DPL::ToUTF8String( + startFileData.path); + + if (strstr(startPath.c_str(), + "http") == startPath.c_str()) + { + WidgetRegisterInfo::StartFileProperties + startFileProperties; + if (!!type) { + startFileProperties.type = *type; + } + if (!!encoding) { + startFileProperties.encoding = *encoding; + } + startFileData.propertiesForLocales[*i] = + startFileProperties; + } + } + } + } + + m_installContext.widgetConfig.localizationData.startFiles.push_back( + startFileData); + } +} + +void TaskProcessConfig::ProcessBackgroundPageFile() +{ + if (!!m_installContext.widgetConfig.configInfo.backgroundPage) { + // check whether file exists + DPL::String backgroundPagePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + *m_installContext.widgetConfig.configInfo.backgroundPage; + //if no then cancel installation + if (!WrtUtilFileExists(DPL::ToUTF8String(backgroundPagePath))) { + ThrowMsg(Exceptions::WidgetConfigFileInvalid, + L"Given background page file not found in archive"); + } + } +} + +void TaskProcessConfig::ProcessLocalizedIcons() +{ + using namespace WrtDB; + FOREACH(i, m_installContext.widgetConfig.configInfo.iconsList) + { + ProcessIcon(*i); + } + ProcessIcon(ConfigParserData::Icon(L"icon.svg")); + ProcessIcon(ConfigParserData::Icon(L"icon.ico")); + ProcessIcon(ConfigParserData::Icon(L"icon.png")); + ProcessIcon(ConfigParserData::Icon(L"icon.gif")); + ProcessIcon(ConfigParserData::Icon(L"icon.jpg")); +} + +void TaskProcessConfig::ProcessIcon(const WrtDB::ConfigParserData::Icon& icon) +{ + _D("enter"); + bool isAnyIconValid = false; + //In case a default filename is passed as custom filename in config.xml, we + //need to keep a set of already processed filenames to avoid icon + // duplication + //in database. + + using namespace WrtDB; + + if (m_processedIconSet.count(icon.src) > 0) { + return; + } + m_processedIconSet.insert(icon.src); + + LocaleSet localesAvailableForIcon; + + FOREACH(i, m_localeFolders) + { + DPL::String pathPrefix = *i; + if (!pathPrefix.empty()) { + pathPrefix = L"locales/" + pathPrefix + L"/"; + } + + DPL::String relativePath = pathPrefix + icon.src; + DPL::String absolutePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + relativePath; + + if (WrtUtilFileExists(DPL::ToUTF8String(absolutePath))) { + DPL::String type = MimeTypeUtils::identifyFileMimeType(absolutePath); + + if (MimeTypeUtils::isMimeTypeSupportedForIcon(type)) { + isAnyIconValid = true; + localesAvailableForIcon.insert(*i); + _D("Icon absolutePath: %ls, assigned locale: %ls, type: %ls", + absolutePath.c_str(), (*i).c_str(), type.c_str()); + } + } + } + + if (isAnyIconValid) { + WidgetRegisterInfo::LocalizedIcon localizedIcon(icon, + localesAvailableForIcon); + m_installContext.widgetConfig.localizationData.icons.push_back( + localizedIcon); + } +} + +void TaskProcessConfig::ProcessWidgetInstalledPath() +{ + _D("ProcessWidgetInstalledPath"); + m_installContext.widgetConfig.widgetInstalledPath = + DPL::FromUTF8String( + m_installContext.locations->getPackageInstallationDir()); +} + +void TaskProcessConfig::ProcessAppControlInfo() +{ + _D("ProcessAppControlInfo"); + using namespace WrtDB; + + // In case of dispostion is inline, set the seperate execute + int index = 1; + // 0 index is reserved by default execute + FOREACH(it, m_installContext.widgetConfig.configInfo.appControlList) { + if (it->m_disposition == + ConfigParserData::AppControlInfo::Disposition::INLINE) + { + it->m_index = index++; + } else { + it->m_index = 0; + } + } +} + +void TaskProcessConfig::ProcessSecurityModel() +{ + // 0104. If the "required_version" specified in the Web Application's + // configuration is 2.2 or higher and if the Web Application's + // configuration is "CSP-compatible configuration", then the WRT MUST be + // set to "CSP-based security mode". Otherwise, the WRT MUST be set to + // "WARP-based security mode". + // 0105. A Web Application configuration is "CSP-compatible configuration" + // if the configuration includes one or more of + // / + // / + // elements. + + bool isSecurityModelV1 = false; + bool isSecurityModelV2 = false; + WrtDB::ConfigParserData &data = m_installContext.widgetConfig.configInfo; + + if (!data.cspPolicy.IsNull() || + !data.cspPolicyReportOnly.IsNull() || + !data.allowNavigationInfoList.empty()) + { + data.accessInfoSet.clear(); + } + + // WARP is V1 + if (!data.accessInfoSet.empty()) { + isSecurityModelV1 = true; + } + + // CSP & allow-navigation is V2 + if (!data.cspPolicy.IsNull() || + !data.cspPolicyReportOnly.IsNull() || + !data.allowNavigationInfoList.empty()) + { + isSecurityModelV2 = true; + } + + if (isSecurityModelV1 && isSecurityModelV2) { + _E("Security model is conflict"); + ThrowMsg(Exceptions::NotAllowed, "Security model is conflict"); + } else if (isSecurityModelV1) { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V1; + } else if (isSecurityModelV2) { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V2; + } else { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V1; + } + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Finished process security model"); +} + +void TaskProcessConfig::StepCheckMinVersionInfo() +{ + if (!isMinVersionCompatible( + m_installContext.widgetConfig.webAppType.appType, + m_installContext.widgetConfig.minVersion)) + { + _E("Platform version lower than required -> cancelling installation"); + ThrowMsg(Exceptions::NotAllowed, + "Platform version does not meet requirements"); + } + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Check MinVersion Finished"); +} + +void TaskProcessConfig::StepVerifyFeatures() +{ + using namespace WrtDB; + ConfigParserData &data = m_installContext.widgetConfig.configInfo; + ConfigParserData::FeaturesList list = data.featuresList; + ConfigParserData::FeaturesList newList; + + //in case of tests, this variable is unused + std::string featureInfo; + FOREACH(it, list) + { + // check feature vender for permission + // WAC, TIZEN WebApp cannot use other feature + + if (!isFeatureAllowed(m_installContext.widgetConfig.webAppType.appType, + it->name)) + { + _D("This application type not allowed to use this feature"); + ThrowMsg( + Exceptions::WidgetConfigFileInvalid, + "This app type [" << + m_installContext.widgetConfig.webAppType.getApptypeToString() + << + "] cannot be allowed to use [" << + DPL::ToUTF8String(it->name) + "] feature"); + } else { + newList.insert(*it); + featureInfo += DPL::ToUTF8String(it->name); + featureInfo += DPL::ToUTF8String(BR); + } + } + if (!data.accessInfoSet.empty()) { + featureInfo += WINDGET_INSTALL_NETWORK_ACCESS; + featureInfo += DPL::ToUTF8String(BR); + } + data.featuresList = newList; + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Widget Config step2 Finished"); +} + +void TaskProcessConfig::StepVerifyLivebox() +{ + using namespace WrtDB; + ConfigParserData &data = m_installContext.widgetConfig.configInfo; + ConfigParserData::LiveboxList liveBoxList = data.m_livebox; + + if (liveBoxList.size() <= 0) { + return; + } + + FOREACH (it, liveBoxList) { + std::string boxType; + + if ((**it).m_liveboxId.find(m_installContext.widgetConfig.tzAppid) != 0) { + _E("Invalid app-widget id (doesn't begin with application id)"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Invalid app-widget id (doesn't begin with application id)"); + } + + if ((**it).m_type.empty()) { + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String((**it).m_type); + } + + _D("livebox type: %s", boxType.c_str()); + + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + (**it).m_boxInfo.m_boxSize; + char** boxSize = static_cast( + malloc(sizeof(char*)* boxSizeList.size())); + + int boxSizeCnt = 0; + FOREACH (m, boxSizeList) { + boxSize[boxSizeCnt++] = strdup(DPL::ToUTF8String((*m).m_size).c_str()); + } + + bool chkSize = web_provider_plugin_check_supported_size( + boxType.c_str(), boxSize, boxSizeCnt); + + for(int i = 0; i < boxSizeCnt; i++) { + free(boxSize[i]); + } + free(boxSize); + + if(!chkSize) { + _E("Invalid boxSize"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Invalid boxSize"); + } + } +} + +bool TaskProcessConfig::isFeatureAllowed(WrtDB::AppType appType, + DPL::String featureName) +{ + using namespace WrtDB; + _D("AppType = [%s]", WidgetType(appType).getApptypeToString().c_str()); + _D("FetureName = [%ls]", featureName.c_str()); + + AppType featureType = APP_TYPE_UNKNOWN; + std::string featureStr = DPL::ToUTF8String(featureName); + const char* feature = featureStr.c_str(); + + // check prefix of feature name + if (strstr(feature, PluginsPrefix::TIZENPluginsPrefix) == feature) { + // Tizen WebApp feature + featureType = APP_TYPE_TIZENWEBAPP; + } else if (strstr(feature, PluginsPrefix::W3CPluginsPrefix) == feature) { + // W3C standard feature + // Both WAC and TIZEN WebApp are possible to use W3C plugins + return true; + } else { + // unknown feature + // unknown feature will be checked next step + return true; + } + + if (appType == featureType) { + return true; + } + return false; +} + +bool TaskProcessConfig::parseVersionString(const std::string &version, + long &majorVersion, + long &minorVersion, + long µVersion) const +{ + std::istringstream inputString(version); + inputString >> majorVersion; + if (inputString.bad() || inputString.fail()) { + _W("Invalid minVersion format."); + return false; + } + inputString.get(); // skip period + inputString >> minorVersion; + if (inputString.bad() || inputString.fail()) { + _W("Invalid minVersion format"); + return false; + } else { + inputString.get(); // skip period + if (inputString.bad() || inputString.fail()) { + inputString >> microVersion; + } + } + return true; +} + +bool TaskProcessConfig::isMinVersionCompatible( + WrtDB::AppType appType, + const DPL::OptionalString & + widgetVersion) const +{ + if (widgetVersion.IsNull() || (*widgetVersion).empty()) { + if (appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) { + return false; + } else { + _W("minVersion attribute is empty. WRT assumes platform " + "supports this widget."); + return true; + } + } + + //Parse widget version + long majorWidget = 0, minorWidget = 0, microWidget = 0; + if (!parseVersionString(DPL::ToUTF8String(*widgetVersion), majorWidget, + minorWidget, microWidget)) + { + _W("Invalid format of widget version string."); + return false; + } + + //Parse supported version + long majorSupported = 0, minorSupported = 0, microSupported = 0; + std::string version; + if (appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) { + version = WrtDB::GlobalConfig::GetTizenVersion(); + } else { + _W("Invaild AppType"); + return false; + } + + if (!parseVersionString(version, + majorSupported, minorSupported, microSupported)) + { + _W("Invalid format of platform version string."); + return true; + } + + if (majorWidget > majorSupported || + (majorWidget == majorSupported && minorWidget > minorSupported) || + (majorWidget == majorSupported && minorWidget == minorSupported + && microWidget > microSupported)) + { + _D("Platform doesn't support this widget."); + return false; + } + return true; +} + +bool TaskProcessConfig::isTizenWebApp() const +{ + if (m_installContext.widgetConfig.webAppType.appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) + { + return true; + } + return false; +} + +bool TaskProcessConfig::fillWidgetConfig( + WrtDB::WidgetRegisterInfo& pWidgetConfigInfo, + WrtDB::ConfigParserData& configInfo) +{ + pWidgetConfigInfo.guid = configInfo.widget_id; + + if (!!configInfo.version) { + if (!pWidgetConfigInfo.version) { + pWidgetConfigInfo.version = configInfo.version; + } else { + if (pWidgetConfigInfo.version != configInfo.version) { + _E("Invalid archive"); + return false; + } + } + } + if (!!configInfo.minVersionRequired) { + pWidgetConfigInfo.minVersion = configInfo.minVersionRequired; + } else if (!!configInfo.tizenMinVersionRequired) { + pWidgetConfigInfo.minVersion = configInfo.tizenMinVersionRequired; + } + return true; +} + +void TaskProcessConfig::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskProcessConfig::EndStep() +{ + _D("--------- : END ----------"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_process_config.h b/src_mobile/jobs/widget_install/task_process_config.h new file mode 100755 index 0000000..8d844d6 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_process_config.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_process_config.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task widget config + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { + +class TaskProcessConfig : + public DPL::TaskDecl +{ + private: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ConfigParseFailed) + }; + + typedef std::list > StringPairList; + + InstallerContext& m_installContext; + WrtDB::LocaleSet m_localeFolders; + std::set m_processedIconSet; + + void StepFillWidgetConfig(); + void ReadLocaleFolders(); + void ProcessLocalizedStartFiles(); + void ProcessStartFile( + const DPL::OptionalString& path, + const DPL::OptionalString& type, + const DPL::OptionalString& encoding = + DPL::OptionalString::Null, + bool typeForcedInConfig = false); + void ProcessBackgroundPageFile(); + void ProcessLocalizedIcons(); + void ProcessIcon(const WrtDB::ConfigParserData::Icon& icon); + void ProcessWidgetInstalledPath(); + void ProcessAppControlInfo(); + void ProcessSecurityModel(); + void StepVerifyFeatures(); + void StepVerifyLivebox(); + void StepCheckMinVersionInfo(); + + template + void StepCancelInstallation(); + + void StartStep(); + void EndStep(); + + DPL::String createAuthorWidgetInfo() const; + bool isFeatureAllowed( + WrtDB::AppType appType, DPL::String featureName); + bool isMinVersionCompatible( + WrtDB::AppType appType, + const DPL::OptionalString &widgetVersion) const; + /** + * @brief Parses version string in format "major.minor.micro anything" + * Returns false if format is invalid + */ + bool isTizenWebApp() const; + bool parseVersionString(const std::string &version, long &majorVersion, + long &minorVersion, long µVersion) const; + + bool fillWidgetConfig(WrtDB::WidgetRegisterInfo& pWidgetConfigInfo, + WrtDB::ConfigParserData& configInfo); + + public: + TaskProcessConfig(InstallerContext& installTaskContext); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H diff --git a/src_mobile/jobs/widget_install/task_recovery.cpp b/src_mobile/jobs/widget_install/task_recovery.cpp new file mode 100644 index 0000000..cf9d952 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_recovery.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_recovery.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task recovery + */ +#include "task_recovery.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskRecovery::TaskRecovery(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRecovery::StartStep); + AddStep(&TaskRecovery::StepCreateCheckFile); + AddStep(&TaskRecovery::EndStep); +} + +void TaskRecovery::StartStep() +{ + _D("---------- : START ----------"); +} + +void TaskRecovery::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CHECK_FILE, + "Create information file for recovery"); + + _D("---------- : END ----------"); +} + +void TaskRecovery::StepCreateCheckFile() +{ + _D("Step: create information file for recovery"); + + size_t pos = m_context.locations->getWidgetSource().rfind("/"); + std::ostringstream infoPath; + infoPath << GlobalConfig::GetTempInstallInfoPath(); + infoPath << "/"; + infoPath << m_context.locations->getWidgetSource().substr(pos + 1); + + FILE *temp = fopen(infoPath.str().c_str(), "w+"); + if (temp != NULL) { + fputs(m_context.locations->getWidgetSource().c_str(), temp); + int ret = fsync(temp->_fileno); + fclose(temp); + if (-1 == ret) { + ThrowMsg(Exceptions::FileOperationFailed, "Fail to fsync for recovery."); + } + + m_context.installInfo = infoPath.str(); + + _D("Create file : %s", m_context.installInfo.c_str()); + } else { + ThrowMsg(Exceptions::FileOperationFailed, "Fail to create file for recovery."); + } +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_recovery.h b/src_mobile/jobs/widget_install/task_recovery.h new file mode 100644 index 0000000..22503a3 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_recovery.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_recovery.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskRecovery : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void StepCreateCheckFile(); + void StartStep(); + void EndStep(); + + public: + explicit TaskRecovery(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ */ diff --git a/src_mobile/jobs/widget_install/task_remove_backup.cpp b/src_mobile/jobs/widget_install/task_remove_backup.cpp new file mode 100644 index 0000000..10caf9e --- /dev/null +++ b/src_mobile/jobs/widget_install/task_remove_backup.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_backup.cpp + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task backup files remove + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskRemoveBackupFiles::TaskRemoveBackupFiles(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveBackupFiles::StartStep); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) + { + AddStep(&TaskRemoveBackupFiles::StepRemoveBackupFiles); + } + AddStep(&TaskRemoveBackupFiles::StepDeleteBackupDB); + AddStep(&TaskRemoveBackupFiles::StepDeleteBackupWidgetInterfaceDB); + AddStep(&TaskRemoveBackupFiles::EndStep); +} + +void TaskRemoveBackupFiles::StepRemoveBackupFiles() +{ + std::ostringstream backupDir; + backupDir << m_context.locations->getBackupDir(); + + if (WrtUtilRemove(backupDir.str())) { + _D("Success to remove backup files : %s", backupDir.str().c_str()); + } else { + _E("Failed to remove backup directory : %s", backupDir.str().c_str()); + ThrowMsg(Exceptions::RemoveBackupFailed, + "Error occurs during removing existing folder"); + } + + std::string tmp = m_context.locations->getTemporaryPackageDir(); + if (WrtUtilRemove(tmp)) { + _D("Success to remove temp directory : %s", tmp.c_str()); + } else { + _E("Failed to remove temp directory : %s", tmp.c_str()); + } +} + +void TaskRemoveBackupFiles::StepDeleteBackupDB() +{ + _D("StepDeleteBackupDB"); + std::string oldAppid = + DPL::ToUTF8String(m_context.widgetConfig.tzAppid) + ".backup"; + + Try + { + WidgetDAO::unregisterWidget(DPL::FromUTF8String(oldAppid)); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Fail to delete old version db information"); + } +} + +void TaskRemoveBackupFiles::StepDeleteBackupWidgetInterfaceDB() +{ + _D("StepDeleteBackupWidgetInterfaceDB"); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + std::string backupDbPath = WidgetInterfaceDAO::databaseFileName(handle); + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + + // remove backup database + if (remove(backupDbPath.c_str()) != 0) { + _W("Fail to remove"); + } +} + +void TaskRemoveBackupFiles::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskRemoveBackupFiles::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_remove_backup.h b/src_mobile/jobs/widget_install/task_remove_backup.h new file mode 100644 index 0000000..1dcdf5b --- /dev/null +++ b/src_mobile/jobs/widget_install/task_remove_backup.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_backup.h + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task backup files remove + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskRemoveBackupFiles : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepRemoveBackupFiles(); + void StepDeleteBackupDB(); + void StepDeleteBackupWidgetInterfaceDB(); + + void StartStep(); + void EndStep(); + + public: + TaskRemoveBackupFiles(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H diff --git a/src_mobile/jobs/widget_install/task_smack.cpp b/src_mobile/jobs/widget_install/task_smack.cpp new file mode 100644 index 0000000..0114c4e --- /dev/null +++ b/src_mobile/jobs/widget_install/task_smack.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.cpp + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task smack + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WRT_SMACK_ENABLED +#include +#include +#endif +#include +#include +#include + +using namespace WrtDB; +using namespace ValidationCore; + +namespace { +const int MAX_BUF_SIZE = 128; +void freeList(const char** list) { + for (int i = 0; list[i] != NULL; i++) + { + delete(list[i]); + } + delete[] list; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskSmack::TaskSmack(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_pkgId(NULL) +{ + AddStep(&TaskSmack::StartStep); + AddStep(&TaskSmack::StepSetInstall); + AddStep(&TaskSmack::StepSmackFolderLabeling); + AddStep(&TaskSmack::StepSmackPrivilege); + AddStep(&TaskSmack::StepAddLabelNPRuntime); + AddStep(&TaskSmack::EndStep); + + AddAbortStep(&TaskSmack::StepAbortSmack); +} + +void TaskSmack::StepSetInstall() +{ + _D("----------------> SMACK: StepStartSetSmack()"); +#ifdef WRT_SMACK_ENABLED + std::string pkg = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + m_pkgId = (char*)calloc(1, pkg.length() + 1); + snprintf(m_pkgId, pkg.length() + 1, "%s", pkg.c_str()); + + if (m_context.widgetConfig.packagingType != + WrtDB::PkgType::PKG_TYPE_HYBRID_WEB_APP) + { + if (PC_OPERATION_SUCCESS != perm_app_install(m_pkgId)) { + free(m_pkgId); + ThrowMsg(Exceptions::NotAllowed, "Instalation failure. " + "failure in creating smack rules file."); + } + } +#endif +} + +void TaskSmack::StepSmackFolderLabeling() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::SmackFolderLabelingStep()"); +#ifdef WRT_SMACK_ENABLED + /* /opt/usr/apps/[pkgid] directory's label is "_" */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, + m_context.locations->getPackageInstallationDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getPackageInstallationDir().c_str()); + } + + /* for prelaod */ + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD && + m_context.mode.extension != InstallMode::ExtensionType::DIR) + { + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, + m_context.locations->getUserDataRootDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + } + } + + /* res directory */ + std::string resDir = m_context.locations->getPackageInstallationDir() + + "/res"; + + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, resDir.c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", resDir.c_str()); + } + + /* data directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, + m_context.locations->getPrivateStorageDir().c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", m_context.locations->getPrivateStorageDir().c_str()); + } + + /* tmp directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, + m_context.locations->getPrivateTempStorageDir().c_str(), + APP_PATH_PRIVATE)) + { + _W("Add label to %s", m_context.locations->getPrivateTempStorageDir().c_str()); + } + + /* bin directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId, + m_context.locations->getBinaryDir().c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", m_context.locations->getBinaryDir().c_str()); + } + + if(!setLabelForSharedDir(m_pkgId)) { + _W("Add label to shared directory"); + } + + free(m_pkgId); + + /* TODO : set label at wrt-client */ +#endif +} + +void TaskSmack::StepSmackPrivilege() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::SmackPrivilegeStep()"); +#ifdef WRT_SMACK_ENABLED + /* TODO : + std::string id = DPL::ToUTF8String(m_context.widgetConfig.tzAppid); + */ + std::string id = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + char* appId = NULL; + appId = (char*)calloc(1, id.length() + 1); + snprintf(appId, id.length() + 1, "%s", id.c_str()); + + WrtDB::ConfigParserData::PrivilegeList privileges = + m_context.widgetConfig.configInfo.privilegeList; + + char** perm_list = new char*[privileges.size() + 1]; + int index = 0; + FOREACH(it, privileges) { + _D("Permission : %ls", it->name.c_str()); + int length = DPL::ToUTF8String(it->name).length(); + char *priv = new char[length + 1]; + snprintf(priv, length + 1, "%s", + DPL::ToUTF8String(it->name).c_str()); + perm_list[index++] = priv; + } + perm_list[index] = NULL; + + if (PC_OPERATION_SUCCESS != perm_app_enable_permissions(appId, APP_TYPE_WGT, + const_cast(perm_list), true)) { + _W("failure in contructing smack rules based on perm_list"); + } + + free(appId); + index = 0; + while (NULL != perm_list[index]) { + delete [] perm_list[index++]; + } + delete [] perm_list; + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_SMACK_ENABLE, + "Widget SMACK Enabled"); +#endif +} + +void TaskSmack::StepAddLabelNPRuntime() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepAddLabelNPRuntime()"); + if (0 == access(m_context.locations->getNPPluginsDir().c_str(), F_OK)) { + if (PC_OPERATION_SUCCESS != + perm_app_setup_path(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid).c_str(), + m_context.locations->getNPPluginsExecFile().c_str(), + PERM_APP_PATH_NPRUNTIME)) { + _E("failed to set smack execute label to %s", + m_context.locations->getNPPluginsExecFile().c_str()); + } + } +} + +void TaskSmack::StepRevokeForUpdate() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepRevokePrivilegeForUpdate()"); +#ifdef WRT_SMACK_ENABLED + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(m_pkgId)) { + _W("failure in revoking smack permissions"); + } +#endif +} + +void TaskSmack::StepAbortSmack() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepAbortSmack()"); +#ifdef WRT_SMACK_ENABLED + + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(m_pkgId)) { + _W("failure in revoking smack permissions"); + } + + if (PC_OPERATION_SUCCESS != perm_app_uninstall(m_pkgId)) { + _W("failure in removing smack rules file"); + } + free(m_pkgId); +#endif +} + +bool TaskSmack::setLabelForSharedDir(const char* pkgId) +{ + /* /shared directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedRootDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getUserDataRootDir().c_str()); + } + + /* /shared/res directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedResourceDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getSharedResourceDir().c_str()); + } + + /* /shared/trusted directory */ + CertificatePtr rootCert = m_context.widgetSecurity.getAuthorCertificatePtr(); + if (rootCert.Get() != NULL) { + ValidationCore::Crypto::Hash::SHA1 sha1; + sha1.Append(rootCert->getDER()); + sha1.Finish(); + std::string sha1String = sha1.ToBase64String(); + size_t iPos = sha1String.find("/"); + while(iPos < std::string::npos) { + sha1String.replace(iPos, 1, "#"); + iPos = sha1String.find("/"); + } + + _D("sha1 label string : %s", sha1String.c_str()); + + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedTrustedDir().c_str(), + APP_PATH_GROUP_RW, sha1String.c_str())) { + _W("Add label to %s", m_context.locations->getBinaryDir().c_str()); + } + } + + /* /shared/data directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedDataDir().c_str(), + APP_PATH_PUBLIC_RO)) { + _W("Add label to %s", m_context.locations->getSharedDataDir().c_str()); + } + + return true; +} + +void TaskSmack::StartStep() +{ + _D("--------- : START ----------"); + if (PC_OPERATION_SUCCESS != perm_begin()) { + _E("Failed to smack transaction begin."); + ThrowMsg(Exceptions::SmackTransactionFailed, "Failed to smack transaction begin"); + } +} + +void TaskSmack::EndStep() +{ + _D("--------- : END ----------"); + if (PC_OPERATION_SUCCESS != perm_end()) { + _E("Failed to smack transaction end."); + ThrowMsg(Exceptions::SmackTransactionFailed, "Failed to smack transaction end"); + } +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_smack.h b/src_mobile/jobs/widget_install/task_smack.h new file mode 100644 index 0000000..d1895c2 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_smack.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.h + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Header file for installer task smack + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskSmack : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + char* m_pkgId; + + void StepSetInstall(); + void StepSmackFolderLabeling(); + void StepSmackPrivilege(); + void StepAddLabelNPRuntime(); + void StepRevokeForUpdate(); + void StepAbortSmack(); + + bool setLabelForSharedDir(const char* pkgId); + + void StartStep(); + void EndStep(); + + public: + TaskSmack(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H */ diff --git a/src_mobile/jobs/widget_install/task_update_files.cpp b/src_mobile/jobs/widget_install/task_update_files.cpp new file mode 100644 index 0000000..ef27d30 --- /dev/null +++ b/src_mobile/jobs/widget_install/task_update_files.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_update_files.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task update files + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +inline const char* GetWidgetBackupDirPath() +{ + return "backup"; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskUpdateFiles::TaskUpdateFiles(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUpdateFiles::StartStep); + AddStep(&TaskUpdateFiles::StepBackupDirectory); + AddStep(&TaskUpdateFiles::EndStep); + + AddAbortStep(&TaskUpdateFiles::StepAbortBackupDirectory); +} + +void TaskUpdateFiles::StepBackupDirectory() +{ + _D("StepCreateBackupFolder"); + + Try { + std::string pkgid = + DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + if (APP2EXT_SD_CARD == app2ext_get_app_location(pkgid.c_str())) { + _D("Installed external directory"); + WidgetInstallToExtSingleton::Instance().initialize(pkgid); + WidgetInstallToExtSingleton::Instance().disable(); + } + } Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) { + _E("Failed disable app2sd"); + ReThrowMsg(Exceptions::BackupFailed, "Error occurs during disable app2sd"); + } + + std::string backPath = m_context.locations->getBackupDir(); + _D("Backup resource directory path : %s", backPath.c_str()); + std::string pkgPath = m_context.locations->getPackageInstallationDir(); + + if (0 == access(backPath.c_str(), F_OK)) { + if (!WrtUtilRemove(backPath)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Error occurs during removing backup resource directory"); + } + } + _D("copy : %s to %s", pkgPath.c_str(), backPath.c_str()); + if ((rename(pkgPath.c_str(), backPath.c_str())) != 0) { + _E("Failed to rename %s to %s", pkgPath.c_str(), backPath.c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs during rename file"); + } + + WrtUtilMakeDir(pkgPath); +} + +void TaskUpdateFiles::StepAbortBackupDirectory() +{ + _D("StepAbortCopyFiles"); + + std::string backPath = m_context.locations->getBackupDir(); + std::string pkgPath = m_context.locations->getPackageInstallationDir(); + + _D("Backup Folder %s to %s", backPath.c_str(), pkgPath.c_str()); + + if (!WrtUtilRemove(pkgPath)) { + _E("Failed to remove %s", pkgPath.c_str()); + } + + if (rename(backPath.c_str(), pkgPath.c_str()) != 0) { + _E("Failed to rename %s to %s", backPath.c_str(), pkgPath.c_str()); + } +} + +void TaskUpdateFiles::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskUpdateFiles::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_BACKUP_DIR, + "Backup directory created for update"); + + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_update_files.h b/src_mobile/jobs/widget_install/task_update_files.h new file mode 100644 index 0000000..1d02e0e --- /dev/null +++ b/src_mobile/jobs/widget_install/task_update_files.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_update_files.h + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task update files + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H + +#include +#include +#include + +class InstallerContext; + +namespace { +typedef std::set ExistFileList; +} + +namespace Jobs { +namespace WidgetInstall { +class TaskUpdateFiles : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepBackupDirectory(); + + void StepAbortBackupDirectory(); + + void StartStep(); + void EndStep(); + + public: + TaskUpdateFiles(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H */ diff --git a/src_mobile/jobs/widget_install/task_user_data_manipulation.cpp b/src_mobile/jobs/widget_install/task_user_data_manipulation.cpp new file mode 100644 index 0000000..808529e --- /dev/null +++ b/src_mobile/jobs/widget_install/task_user_data_manipulation.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_user_data_manipulation.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task user data folder + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WEBAPP_DEFAULT_UID 5000 +#define WEBAPP_DEFAULT_GID 5000 + +namespace { +const mode_t PRIVATE_STORAGE_MODE = 0700; +const mode_t SHARED_STORAGE_MODE = 0755; +} + +using namespace WrtDB; + +namespace { +void changeOwnerForDirectory(std::string storagePath, mode_t mode) { + if (euidaccess(storagePath.c_str(), F_OK) != 0) { + if (!WrtUtilMakeDir(storagePath, mode)) { + _E("Failed to create directory : %s", storagePath.c_str()); + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Failed to create directory : " << storagePath); + } + // '5000' is default uid, gid for applications. + // So installed applications should be launched as process of uid + // '5000'. + // the process can access private directory 'data' of itself. + if (chown(storagePath.c_str(), + WEBAPP_DEFAULT_UID, + WEBAPP_DEFAULT_GID) != 0) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Chown to invaild user"); + } + } else if (euidaccess(storagePath.c_str(), W_OK | R_OK | X_OK) == 0) { + _D("%s already exists.", storagePath.c_str()); + // Even if private directory already is created, private dircetory + // should change owner (recursively). + if (chown(storagePath.c_str(), + WEBAPP_DEFAULT_UID, + WEBAPP_DEFAULT_GID) != 0) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Chown to invaild user"); + } + if (chmod(storagePath.c_str(), mode) != 0) { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "chmod to 0700"); + } + } else { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "No access to private storage."); + } +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskUserDataManipulation::TaskUserDataManipulation(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUserDataManipulation::StartStep); + AddStep(&TaskUserDataManipulation::StepCreatePrivateStorageDir); + AddStep(&TaskUserDataManipulation::StepCreateSharedFolder); + AddStep(&TaskUserDataManipulation::StepLinkForPreload); + AddStep(&TaskUserDataManipulation::EndStep); +} + +void TaskUserDataManipulation::StepCreatePrivateStorageDir() +{ + std::string storagePath = m_context.locations->getPrivateStorageDir(); + _D("Create private storage directory : %s", m_context.locations->getPrivateStorageDir().c_str()); + + changeOwnerForDirectory(storagePath, PRIVATE_STORAGE_MODE); + + if (m_context.isUpdateMode) { //update + std::string backData = m_context.locations->getBackupPrivateDir(); + _D("copy private storage %s to %s", backData.c_str(), storagePath.c_str()); + if (!DirectoryApi::DirectoryCopy(backData, storagePath)) { + _E("Failed to rename %s to %s", backData.c_str(), storagePath.c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy private strage files"); + } + } + + std::string tempStoragePath = m_context.locations->getPrivateTempStorageDir(); + _D("Create temp private storage directory : %s", tempStoragePath.c_str()); + changeOwnerForDirectory(tempStoragePath, PRIVATE_STORAGE_MODE); +} + +void TaskUserDataManipulation::StepLinkForPreload() +{ + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + std::string optRes = m_context.locations->getUserDataRootDir() + + WrtDB::GlobalConfig::GetWidgetResPath(); + std::string usrRes = m_context.locations->getPackageInstallationDir() + + WrtDB::GlobalConfig::GetWidgetResPath(); + + if (0 != access(optRes.c_str(), F_OK)) { + _D("Make symbolic name for preload app %s to %s", usrRes.c_str(), optRes.c_str()); + + if (symlink(usrRes.c_str(), optRes.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + } + + /* link for data directory */ + std::string storagePath = m_context.locations->getPrivateStorageDir(); + std::string dataDir = m_context.locations->getPackageInstallationDir() + + "/" + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); + if (0 != access(dataDir.c_str(), F_OK)) { + _D("Make symbolic name for preload app %s to %s", storagePath.c_str(), dataDir.c_str()); + + if (symlink(storagePath.c_str(), dataDir.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + changeOwnerForDirectory(dataDir, PRIVATE_STORAGE_MODE); + } + + if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) { + std::string widgetBinPath = m_context.locations->getBinaryDir(); + std::string userBinPath = m_context.locations->getUserBinaryDir(); + _D("Make symbolic link for preload app %s to %s", widgetBinPath.c_str(), userBinPath.c_str()); + if (symlink(widgetBinPath.c_str(), userBinPath.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + + } + } +} + +void TaskUserDataManipulation::StepCreateSharedFolder() +{ + _D("StepCreateSharedFolder"); + std::string sharedPath = m_context.locations->getSharedRootDir(); + _D("Create shared directory : %s", m_context.locations->getSharedRootDir().c_str()); + + WrtUtilMakeDir(sharedPath); + WrtUtilMakeDir(m_context.locations->getSharedResourceDir()); + + changeOwnerForDirectory(m_context.locations->getSharedDataDir(), + SHARED_STORAGE_MODE); + changeOwnerForDirectory(m_context.locations->getSharedTrustedDir(), + SHARED_STORAGE_MODE); + + + // additional check for rootPath installation + // If this app is preloaded, "shared" diretory is already on place and do not needs to be moved + // TODO: why "shared" is on RW partion always but "data" and "tmp" are linked + if (m_context.isUpdateMode + && !(m_context.mode.rootPath == InstallMode::RootPath::RO + && m_context.mode.installTime == InstallMode::InstallTime::PRELOAD)) { + + /* Restore /shared/data */ + _D("copy %s to %s", m_context.locations->getBackupSharedDataDir().c_str(), m_context.locations->getSharedDataDir().c_str()); + if (!DirectoryApi::DirectoryCopy( + m_context.locations->getBackupSharedDataDir(), + m_context.locations->getSharedDataDir())) { + _E("Failed to rename %s to %s", m_context.locations->getBackupSharedDataDir().c_str(), m_context.locations->getSharedDataDir().c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy shared strage files"); + } + + /* Restore /shared/trusted */ + _D("copy %s to %s", m_context.locations->getBackupSharedTrustedDir().c_str(), m_context.locations->getSharedTrustedDir().c_str()); + if (!DirectoryApi::DirectoryCopy( + m_context.locations->getBackupSharedTrustedDir(), + m_context.locations->getSharedTrustedDir())) { + _E("Failed to rename %s to %s", m_context.locations->getBackupSharedTrustedDir().c_str(), m_context.locations->getSharedTrustedDir().c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy shared strage files"); + } + } +} + +void TaskUserDataManipulation::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskUserDataManipulation::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/task_user_data_manipulation.h b/src_mobile/jobs/widget_install/task_user_data_manipulation.h new file mode 100644 index 0000000..fbfabcc --- /dev/null +++ b/src_mobile/jobs/widget_install/task_user_data_manipulation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_user_data_manipulation.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task user data folder + */ +#ifndef JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H +#define JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskUserDataManipulation : + public DPL::TaskDecl +{ + InstallerContext& m_context; + + void StartStep(); + void EndStep(); + void StepCreatePrivateStorageDir(); + void StepCreateSharedFolder(); + void StepLinkForPreload(); + + public: + TaskUserDataManipulation(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif //JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H diff --git a/src_mobile/jobs/widget_install/view_mode.h b/src_mobile/jobs/widget_install/view_mode.h new file mode 100644 index 0000000..a4acc39 --- /dev/null +++ b/src_mobile/jobs/widget_install/view_mode.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file view_mode.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ +#define SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ + +namespace Jobs { +namespace WidgetInstall { +enum ViewMode +{ + WINDOWED = 0, + FLOATING, + FULLSCREEN, + MAXIMIZED, + MINIMIZED +}; +} // WidgetInstall +} // Jobs + +#endif /* SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ */ diff --git a/src_mobile/jobs/widget_install/widget_install_context.h b/src_mobile/jobs/widget_install/widget_install_context.h new file mode 100644 index 0000000..967a737 --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_install_context.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_structs.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief Definition file of installer tasks data structures + */ +#ifndef INSTALLER_CONTEXT_H +#define INSTALLER_CONTEXT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class JobWidgetInstall; +} //namespace Jobs +} //namespace WidgetInstall + +class WidgetModel; + +typedef std::map RequestedDevCapsMap; + +struct InstallerContext +{ + typedef enum InstallStepEnum + { + INSTALL_START = 0, + INSTALL_PARSE_CONFIG, + INSTALL_CHECK_FILE, + + INSTALL_RDS_DELTA_CHECK, + INSTALL_RDS_PREPARE, + + INSTALL_CREATE_BACKUP_DIR, /* For Update */ + INSTALL_DIR_CREATE, + INSTALL_UNZIP_WGT, + INSTALL_WIDGET_CONFIG1, + INSTALL_WIDGET_CONFIG2, + INSTALL_DIGSIG_CHECK, + INSTALL_CERT_CHECK, + INSTALL_CERTIFY_LEVEL_CHECK, + INSTALL_ECRYPTION_FILES, + INSTALL_BACKUP_ICONFILE, /* For Update */ + INSTALL_COPY_ICONFILE, + INSTALL_COPY_LIVEBOX_FILES, + INSTALL_CREATE_EXECFILE, + INSTALL_CREATE_MANIFEST, + INSTALL_INSTALL_OSPSVC, + INSTALL_NEW_DB_INSERT, + INSTALL_ACE_PREPARE, + INSTALL_ACE_CHECK, + INSTALL_SMACK_ENABLE, + INSTALL_PKGINFO_UPDATE, + INSTALL_SET_CERTINFO, + + INSTALL_END + } InstallStep; + + // Installation state variables + WrtDB::WidgetRegisterInfo widgetConfig; ///< WidgetConfigInfo + DPL::Optional locations; + Jobs::WidgetInstall::WidgetSecurity widgetSecurity; ///< Widget Domain + // information. + InstallStep installStep; ///< current step of installation + Jobs::WidgetInstall::JobWidgetInstall *job; + ///< Whether this is an update or normal installation + Jobs::WidgetInstall::FeatureLogicPtr featureLogic; + /** List of dev-caps that are requested in widget config file. + * Additional flag tells whether dev cap gets "static" permission + * (will always have PERMIT from ACE Policy). They will therefore receive + * static SMACK permission. (They may be forbidden because + * of ACE User Settings, but for now we do not protect this + * case with SMACK). */ + RequestedDevCapsMap staticPermittedDevCaps; + std::string installInfo; /// + InstallLocationType locationType; + bool isUpdateMode; + InstallMode mode; + DPL::String callerPkgId; + + std::string requestedPath; ///input path of widget + bool needEncryption; ///for configuring right task if encryption needed + int certLevel; +}; + +#endif // INSTALLER_CONTEXT_H diff --git a/src_mobile/jobs/widget_install/widget_install_errors.h b/src_mobile/jobs/widget_install/widget_install_errors.h new file mode 100644 index 0000000..ed0e6fb --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_install_errors.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALLER_ERRORS_H_ +#define INSTALLER_ERRORS_H_ + +#include +#include +#include + +//TODO SafeException(...) + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) + +DECLARE_JOB_EXCEPTION(Base, OpenZipFailed, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, ZipEmpty, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, ExtractFileFailed, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, EmptyPluginsDirectory, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, PluginsSubdirectory, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, RDSDeltaFailure, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, MissingConfig, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, InvalidStartFile, ErrorPackageInvalid) + +DECLARE_JOB_EXCEPTION(Base, PackageLowerVersion, ErrorPackageLowerVersion) + +DECLARE_JOB_EXCEPTION(Base, ManifestInvalid, ErrorManifestInvalid) + +DECLARE_JOB_EXCEPTION(Base, WidgetConfigFileNotFound, ErrorConfigNotFound) +DECLARE_JOB_EXCEPTION(Base, WidgetConfigFileInvalid, ErrorConfigInvalid) + +DECLARE_JOB_EXCEPTION(Base, SignatureNotFound, ErrorSignatureNotFound) + +DECLARE_JOB_EXCEPTION(Base, SignatureInvalid, ErrorSignatureInvalid) + +DECLARE_JOB_EXCEPTION(Base, SignatureVerificationFailed, ErrorSignatureVerificationFailed) + +DECLARE_JOB_EXCEPTION(Base, RootCertificateNotFound, ErrorRootCertificateNotFound) + +DECLARE_JOB_EXCEPTION(Base, CertificationInvaid, ErrorCertificationInvaid) +DECLARE_JOB_EXCEPTION(Base, NotMatchedCertification, ErrorCertificationInvaid) + +DECLARE_JOB_EXCEPTION(Base, CertificateChainVerificationFailed, ErrorCertificateChainVerificationFailed) + +DECLARE_JOB_EXCEPTION(Base, CertificateExpired, ErrorCertificateExpired) + +DECLARE_JOB_EXCEPTION(Base, NotAllowed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, WidgetRunningError, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, DrmDecryptFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, DatabaseFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemovingFolderFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemovingFileFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, CreateVconfFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, CopyIconFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, FileOperationFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, InstallToExternalFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, BackupFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, InsertNewWidgetFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemoveBackupFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, UpdateFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, SetCertificateInfoFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, ErrorExternalInstallingFailure, ErrorFatalError) + +DECLARE_JOB_EXCEPTION(Base, PackageAlreadyInstalled, ErrorPackageAlreadyInstalled) +DECLARE_JOB_EXCEPTION(Base, AceCheckFailed, ErrorAceCheckFailed) +DECLARE_JOB_EXCEPTION(Base, EncryptionFailed, ErrorEncryptionFailed) +DECLARE_JOB_EXCEPTION(Base, InstallOspsvcFailed, ErrorInstallOspServcie) +DECLARE_JOB_EXCEPTION(Base, PrivilegeLevelViolation, ErrorPrivilegeLevelViolation) +DECLARE_JOB_EXCEPTION(Base, NotSupportRDSUpdate, ErrorNotSupportRDSUpdate) +DECLARE_JOB_EXCEPTION(Base, SmackTransactionFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, OutOfStorageFailed, ErrorOutOfStorage) +} //namespace +} //namespace +} //namespace + +#endif /* INSTALLER_ERRORS_H_ */ diff --git a/src_mobile/jobs/widget_install/widget_installer_struct.h b/src_mobile/jobs/widget_install/widget_installer_struct.h new file mode 100644 index 0000000..1fd865e --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_installer_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_installer_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include + +//Widget Installer typedefs +typedef void (*InstallerFinishedCallback)( + void *userParam, + std::string tizenId, + Jobs::Exceptions::Type); + +typedef void (*InstallerProgressCallback)(void *userParam, + ProgressPercent percent, + const ProgressDescription &); + +namespace Jobs { +namespace WidgetInstall { +//InstallationStruct +typedef Jobs::JobCallbacksBase +WidgetInstallCallbackBase; + +//Widget Installation Struct +struct WidgetInstallationStruct : public WidgetInstallCallbackBase +{ + InstallMode m_installMode; + std::shared_ptr pkgmgrInterface; + + // It must be empty-constructible as a parameter of generic event + WidgetInstallationStruct() {}; + WidgetInstallationStruct( + InstallerFinishedCallback finished, + InstallerProgressCallback progress, + void *param, + InstallMode mode, + std::shared_ptr + _pkgmgrInterface + ) : + WidgetInstallCallbackBase(finished, progress, param), + m_installMode(mode), + pkgmgrInterface(_pkgmgrInterface) + {} +}; +} // namespace WidgetInstall +} // namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ diff --git a/src_mobile/jobs/widget_install/widget_security.cpp b/src_mobile/jobs/widget_install/widget_security.cpp new file mode 100644 index 0000000..01644ab --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_security.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_security.cpp + * @author Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + * @version 1.0 + * @brief + */ + +#include "widget_security.h" +#include + +namespace Jobs { +namespace WidgetInstall { +void WidgetSecurity::getCertificateChainList( + WrtDB::CertificateChainList& list, + WrtDB::CertificateSource source) const +{ + if (source == WrtDB::CertificateSource::SIGNATURE_DISTRIBUTOR) { + FOREACH(certIter, mCertificateChainList) + list.push_back(certIter->toBase64String()); + } else { + FOREACH(certIter, mAuthorsCertificateChainList) + list.push_back(certIter->toBase64String()); + } +} +} // namespace WidgetInstall +} // namespace Jobs diff --git a/src_mobile/jobs/widget_install/widget_security.h b/src_mobile/jobs/widget_install/widget_security.h new file mode 100644 index 0000000..0bc04ef --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_security.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_security.h + * @author Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WACSECURITY_H_ +#define WACSECURITY_H_ + +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class WidgetSecurity : public WrtDB::IWidgetSecurity +{ + public: + WidgetSecurity() : + mRecognized(false), + mDistributorSigned(false) + {} + + // from IWidgetSecurity + virtual const WrtDB::WidgetCertificateDataList& getCertificateList() const + { + return mCertificateList; + } + + virtual bool isRecognized() const + { + return mRecognized; + } + + virtual bool isDistributorSigned() const + { + return mDistributorSigned; + } + + virtual void getCertificateChainList( + WrtDB::CertificateChainList& list, + WrtDB::CertificateSource source) const; + + void setRecognized(bool recognized) + { + mRecognized = recognized; + } + void setDistributorSigned(bool distributorSigned) + { + mDistributorSigned = distributorSigned; + } + void setAuthorCertificatePtr(ValidationCore::CertificatePtr certPtr) + { + mAuthorCertificate = certPtr; + } + + ValidationCore::CertificatePtr getAuthorCertificatePtr() const + { + return mAuthorCertificate; + } + ValidationCore::CertificateCollectionList& getCertificateChainListRef() + { + return mCertificateChainList; + } + + ValidationCore::CertificateCollectionList& + getAuthorsCertificateChainListRef() + { + return mAuthorsCertificateChainList; + } + + WrtDB::WidgetCertificateDataList& getCertificateListRef() + { + return mCertificateList; + } + + private: + // This data are used to evaluate policy + WrtDB::WidgetCertificateDataList mCertificateList; + + // author signature verified + bool mRecognized; + // known distribuor + bool mDistributorSigned; + // Author end entity certificate. + // Information from this certificate are shown to user + // during installation process. + ValidationCore::CertificatePtr mAuthorCertificate; + // This certificates are used by OCSP/CRL + ValidationCore::CertificateCollectionList mCertificateChainList; + // This authors certificates are used by tizen + ValidationCore::CertificateCollectionList mAuthorsCertificateChainList; +}; +} // namespace WidgetInstall +} // namespace Jobs + +#endif /* WACSECURITY_H_ */ diff --git a/src_mobile/jobs/widget_install/widget_unzip.cpp b/src_mobile/jobs/widget_install/widget_unzip.cpp new file mode 100644 index 0000000..057b6c3 --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_unzip.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_unzip.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer widget unzip + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const char *const DRM_LIB_PATH = "/usr/lib/libdrm-service-core-tizen.so"; +const size_t SPACE_SIZE = 1024 * 1024; +const char *const WEB_APP_CONFIG_XML= "config.xml"; +const char *const HYBRID_CONFIG_XML = "res/wgt/config.xml"; + +struct PathAndFilePair +{ + std::string path; + std::string file; + + PathAndFilePair(const std::string &p, + const std::string &f) : + path(p), + file(f) + {} +}; + +PathAndFilePair SplitFileAndPath(const std::string &filePath) +{ + std::string::size_type position = filePath.rfind('/'); + + // Is this only a file without a path ? + if (position == std::string::npos) { + return PathAndFilePair(std::string(), filePath); + } + + // This is full file-path pair + return PathAndFilePair(filePath.substr(0, + position), + filePath.substr(position + 1)); +} +} + +namespace Jobs { +namespace WidgetInstall { + +WidgetUnzip::WidgetUnzip(const std::string &source) +{ + Try { + m_requestFile = getDecryptedPackage(source); + m_zip.reset(new DPL::ZipInput(m_requestFile)); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, source); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, source); + } +} + +void WidgetUnzip::ExtractFile(DPL::ZipInput::File *input, + const std::string &destFileName) +{ + Try + { + DPL::AbstractWaitableInputAdapter inputAdapter(input); + DPL::FileOutput output(destFileName); + + DPL::Copy(&inputAdapter, &output); + } + Catch(DPL::FileOutput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, destFileName); + } + Catch(DPL::CopyFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, destFileName); + } +} + +void WidgetUnzip::unzipProgress(const std::string &destination) +{ + // Show file info + _D("Unzipping: '%s', Comment: '%s', Compressed size: %lld, Uncompressed size: %lld", + m_zipIterator->name.c_str(), m_zipIterator->comment.c_str(), m_zipIterator->compressedSize, m_zipIterator->uncompressedSize); + + // Normalize file paths + // FIXME: Implement checking for invalid characters + + // Extract file or path + std::string fileName = m_zipIterator->name; + + if (fileName[fileName.size() - 1] == '/') { + // This is path + std::string newPath = destination + "/" + + fileName.substr(0, fileName.size() - 1); + _D("Path to extract: %s", newPath.c_str()); + + // Create path in case of it is empty + createTempPath(newPath); + } else { + // This is regular file + std::string fileExtractPath = destination + "/" + fileName; + + _D("File to extract: %s", fileExtractPath.c_str()); + + // Split into pat & file pair + PathAndFilePair pathAndFile = SplitFileAndPath(fileExtractPath); + + _D("Path and file: %s : %s", pathAndFile.path.c_str(), pathAndFile.file.c_str()); + + // First, ensure that path exists + createTempPath(pathAndFile.path); + + Try + { + // Open file + std::unique_ptr file( + m_zip->OpenFile(fileName)); + + // Extract single file + ExtractFile(file.get(), fileExtractPath); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, fileName); + } + } + + // Check whether there are more files to extract + if (++m_zipIterator == m_zip->end()) { + _D("Unzip progress finished successfuly"); + } else { + unzipProgress(destination); + } +} + +bool WidgetUnzip::isDRMPackage(const std::string &source) +{ + _D("Enter : isDRMPackage()"); + int ret = 0; + void* pHandle = NULL; + char* pErrorMsg = NULL; + int (*drm_oem_sapps_is_drm_file)(const char* pDcfPath, int dcfPathLen); + + pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL); + if (!pHandle) { + _E("dlopen failed : %s [%s]", source.c_str(), dlerror()); + return false; + } + + // clear existing error + dlerror(); + + drm_oem_sapps_is_drm_file = reinterpret_cast + (dlsym(pHandle, "drm_oem_sapps_is_drm_file")); + + if ((pErrorMsg = dlerror()) != NULL) { + _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg); + dlclose(pHandle); + return false; + } + + if (drm_oem_sapps_is_drm_file == NULL) { + _E("drm_oem_sapps_is_drm_file is NULL : %s", source.c_str()); + dlclose(pHandle); + return false; + } + + ret = drm_oem_sapps_is_drm_file(source.c_str(), source.length()); + dlclose(pHandle); + if (1 == ret) { + _D("%s is DRM file", source.c_str()); + return true; + } + _D("%s isn't DRM file", source.c_str()); + return false; +} + +bool WidgetUnzip::decryptDRMPackage(const std::string &source, const std::string + &decryptedSource) +{ + _D("Enter : decryptDRMPackage()"); + int ret = 0; + void* pHandle = NULL; + char* pErrorMsg = NULL; + int (*drm_oem_sapps_decrypt_package)(const char* pDcfPath, int dcfPathLen, + const char* pDecryptedFile, int decryptedFileLen); + + pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL); + if (!pHandle) { + _E("dlopen failed : %s [%s]", source.c_str(), dlerror()); + return false; + } + + // clear existing error + dlerror(); + + drm_oem_sapps_decrypt_package = reinterpret_cast + (dlsym(pHandle, "drm_oem_sapps_decrypt_package")); + + if ((pErrorMsg = dlerror()) != NULL) { + _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg); + dlclose(pHandle); + return false; + } + + if (drm_oem_sapps_decrypt_package == NULL) { + _E("drm_oem_sapps_decrypt_package is NULL : %s", source.c_str()); + dlclose(pHandle); + return false; + } + + ret = drm_oem_sapps_decrypt_package(source.c_str(), source.length(), + decryptedSource.c_str(), decryptedSource.length()); + dlclose(pHandle); + if (1 == ret) { + _D("%s is decrypted : %s", source.c_str(), decryptedSource.c_str()); + return true; + } + return false; +} + +std::string WidgetUnzip::getDecryptedPackage(const std::string &source) +{ + _D("Check DRM..."); + if (isDRMPackage(source)) { + std::string decryptedFile; + size_t found = source.find_last_of(".wgt"); + if (found == std::string::npos) { + decryptedFile += source + "_tmp.wgt"; + } else { + decryptedFile += source.substr(0, source.find_last_not_of(".wgt") + + 1) + "_tmp.wgt"; + } + + _D("decrypted file name : %s", decryptedFile.c_str()); + if (!decryptDRMPackage(source, decryptedFile)) { + _E("Failed decrypt drm file"); + ThrowMsg(Exceptions::DrmDecryptFailed, source); + } + return decryptedFile; + } + return source; +} + +void WidgetUnzip::unzipWgtFile(const std::string &destination) +{ + _D("Prepare to unzip..."); + Try + { + _D("wgtFile : %s", m_requestFile.c_str()); + _D("Widget package comment: %s", m_zip->GetGlobalComment().c_str()); + + // Widget package must not be empty + if (m_zip->empty()) { + ThrowMsg(Exceptions::ZipEmpty, m_requestFile); + } + + // Set iterator to first file + m_zipIterator = m_zip->begin(); + + unzipProgress(destination); + + // Unzip finished, close internal structures + m_zip.reset(); + + // Done + _D("Unzip finished"); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile); + } + Catch(DPL::ZipInput::Exception::SeekFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } +} + +bool WidgetUnzip::checkAvailableSpace(const std::string &destination) +{ + _D("checkAvailableSpace ... "); + + double unCompressedSize = m_zip->GetTotalUncompressedSize(); + _D("unCompressedSize : %ld", unCompressedSize); + + struct statvfs vfs; + if (-1 == statvfs(destination.c_str(), &vfs)) { + _E("There is no space for installation"); + return false; + } + + double freeSize = (double)vfs.f_bsize * vfs.f_bavail; + _D("Space Size : %ld", freeSize); + + if (unCompressedSize + SPACE_SIZE >= freeSize) { + _E("There is no space for installation"); + return false; + } + return true; +} + +void WidgetUnzip::unzipConfiguration(const std::string &destination, + WrtDB::PackagingType* type) +{ + _D("unzipConfiguration"); + + Try { + _D("wgtFile : %s", m_requestFile.c_str()); + + std::unique_ptr configFile; + + Try { + configFile.reset(m_zip->OpenFile(HYBRID_CONFIG_XML)); + *type = PKG_TYPE_HYBRID_WEB_APP; + } Catch(DPL::ZipInput::Exception::OpenFileFailed) { + configFile.reset(m_zip->OpenFile(WEB_APP_CONFIG_XML)); + *type = PKG_TYPE_NOMAL_WEB_APP; + } + + std::string extractPath = destination + "/" + WEB_APP_CONFIG_XML; + ExtractFile(configFile.get(), extractPath); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, "config.xml"); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_install/widget_unzip.h b/src_mobile/jobs/widget_install/widget_unzip.h new file mode 100644 index 0000000..204cde7 --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_unzip.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_unzip.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task unzip + */ +#ifndef WIDGET_UNZIP_H +#define WIDGET_UNZIP_H + +#include + +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class WidgetUnzip +{ + public: + WidgetUnzip(const std::string &source); + void unzipWgtFile(const std::string &destination); + void unzipConfiguration(const std::string &destination, WrtDB::PackagingType *type); + bool checkAvailableSpace(const std::string &destination); + + private: + // Unzip state + std::unique_ptr m_zip; + DPL::ZipInput::const_iterator m_zipIterator; + std::string m_requestFile; + + void unzipProgress(const std::string &destination); + void ExtractFile(DPL::ZipInput::File *input, const std::string + &destFileName); + bool isDRMPackage(const std::string &source); + bool decryptDRMPackage(const std::string &source, const std::string + &decryptedSource); + std::string getDecryptedPackage(const std::string &source); +}; + +} //namespace WidgetInstall +} //namespace Jobs + +#endif // WIDGET_UNZIP_H diff --git a/src_mobile/jobs/widget_install/widget_update_info.cpp b/src_mobile/jobs/widget_install/widget_update_info.cpp new file mode 100644 index 0000000..621907b --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_update_info.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_update_info.cpp + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief Implementation file for WidgetUpdateInfo + */ + +#include "widget_update_info.h" + +WidgetUpdateInfo::WidgetUpdateInfo( + const WrtDB::TizenAppId & appId, + const DPL::Optional &existingversion, + const DPL::Optional &incomingversion) : + tzAppId(appId), + existingVersion(existingversion), + incomingVersion(incomingversion) +{ +} + diff --git a/src_mobile/jobs/widget_install/widget_update_info.h b/src_mobile/jobs/widget_install/widget_update_info.h new file mode 100644 index 0000000..21004bd --- /dev/null +++ b/src_mobile/jobs/widget_install/widget_update_info.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_update_info.h + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief Header file for WidgetUpdateInfo + */ +#ifndef SRC_DOMAIN_WIDGET_UPDATE_INFO_H +#define SRC_DOMAIN_WIDGET_UPDATE_INFO_H + +#include +#include + +/** + * WidgetUpdateInfo + * A structure to hold widget's information needed to be registered. + * @see WidgetConfigurationInfo + */ +struct WidgetUpdateInfo +{ + WrtDB::TizenAppId tzAppId; + // Existing widget + DPL::Optional existingVersion; + // Incoming widget + DPL::Optional incomingVersion; + + WidgetUpdateInfo() {}; + WidgetUpdateInfo(const WrtDB::TizenAppId & tzAppid, + const DPL::Optional &existringversion, + const DPL::Optional &incomingVersion); +}; + +#endif // SRC_DOMAIN_WIDGET_UPDATE_INFO_H diff --git a/src_mobile/jobs/widget_uninstall/job_widget_uninstall.cpp b/src_mobile/jobs/widget_uninstall/job_widget_uninstall.cpp new file mode 100644 index 0000000..71de26f --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/job_widget_uninstall.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { //anonymous +const char* REG_TIZEN_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; +const int PKGID_LENTH = 10; +const DPL::Utils::Path PRELOAD_INSTALLED_PATH("/usr/apps"); + +bool checkDirectoryExist(const std::string& pkgId) +{ + DPL::Utils::Path installPath(GlobalConfig::GetUserInstalledWidgetPath()); + installPath /= pkgId; + return installPath.Exists(); +} +} + +namespace Jobs { +namespace WidgetUninstall { + +class UninstallerTaskFail : + public DPL::TaskDecl +{ + private: + WidgetStatus m_status; + + void StepFail() + { + if (WidgetStatus::NOT_INSTALLED == m_status) { + ThrowMsg(Jobs::WidgetUninstall::Exceptions::WidgetNotExist, + "Widget does not exist"); + } else if (WidgetStatus::PREALOAD == m_status) { + ThrowMsg(Jobs::WidgetUninstall::Exceptions::Unremovable, + "Widget cann't uninstall"); + } else { + Throw(Jobs::WidgetUninstall::Exceptions::Base); + } + } + + public: + UninstallerTaskFail(WidgetStatus status) : + DPL::TaskDecl(this), + m_status(status) + + { + AddStep(&UninstallerTaskFail::StepFail); + } +}; + +JobWidgetUninstall::JobWidgetUninstall( + const std::string & tizenAppId, + const WidgetUninstallationStruct & + uninstallerStruct) : + Job(Uninstallation), + JobContextBase(uninstallerStruct), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + using namespace PackageManager; + m_context.removeStarted = false; + m_context.removeFinished = false; + m_context.removeAbnormal = false; + m_context.uninstallStep = UninstallerContext::UNINSTALL_START; + m_context.job = this; + + Try + { + WidgetStatus status = getWidgetStatus(tizenAppId); + + if (WidgetStatus::Ok == status) { + WrtDB::WidgetDAOReadOnly dao(DPL::FromUTF8String(m_context.tzAppid)); + m_context.tzPkgid = DPL::ToUTF8String(dao.getTizenPkgId()); + m_context.locations = WidgetLocation(m_context.tzPkgid); + m_context.locations->registerAppid(m_context.tzAppid); + m_context.installedPath = + DPL::Utils::Path(*dao.getWidgetInstalledPath()); + m_context.manifestFile = getManifestFile(); + + _D("Widget model exists. App id : %s", m_context.tzAppid.c_str()); + + // send start signal of pkgmgr + if (GetInstallerStruct().pkgmgrInterface->setPkgname(m_context.tzPkgid)) + { + GetInstallerStruct().pkgmgrInterface->startJob(InstallationType::Uninstallation); + } + + AddTask(new TaskCheck(m_context)); + if (dao.getPackagingType() == PKG_TYPE_HYBRID_WEB_APP) { + AddTask(new TaskUninstallOspsvc(m_context)); + } + AddTask(new TaskDeletePkgInfo(m_context)); + AddTask(new TaskDbUpdate(m_context)); + AddTask(new TaskSmack(m_context)); + + AddTask(new TaskRemoveCustomHandlers(m_context)); + AddTask(new TaskRemoveFiles(m_context)); + } else if (WidgetStatus::NOT_INSTALLED == status || + WidgetStatus::PREALOAD == status) { + AddTask(new UninstallerTaskFail(status)); + } else if (WidgetStatus::ABNORMAL == status) { + m_context.locations = WidgetLocation(m_context.tzPkgid); + m_context.removeAbnormal = true; + AddTask(new TaskRemoveFiles(m_context)); + } else { + AddTask(new UninstallerTaskFail(WidgetStatus::UNRECOGNIZED)); + } + } Catch(WidgetDAOReadOnly::Exception::Base) { + AddTask(new UninstallerTaskFail(WidgetStatus::UNRECOGNIZED)); + } +} + +WidgetStatus JobWidgetUninstall::getWidgetStatus(const std::string &id) +{ + regex_t regx; + if(regcomp(®x, REG_TIZEN_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED)!=0){ + _D("Regcomp failed"); + } + std::string pkgId; + DPL::Utils::Path installPath; + + Try { + if ((regexec(®x, id.c_str(), + static_cast(0), NULL, 0) == REG_NOERROR)) { + pkgId = id; + + TizenAppId appid = + WrtDB::WidgetDAOReadOnly::getTzAppId( + DPL::FromUTF8String(id)); + _D("Get appid from pkgid : %ls", appid.c_str()); + m_context.tzAppid = DPL::ToUTF8String(appid); + WrtDB::WidgetDAOReadOnly dao(appid); + installPath = DPL::Utils::Path(*dao.getWidgetInstalledPath()); + } else { + pkgId = id.substr(0, PKGID_LENTH); + WrtDB::WidgetDAOReadOnly dao(DPL::FromUTF8String(id)); + m_context.tzAppid = id; + installPath = DPL::Utils::Path(*dao.getWidgetInstalledPath()); + } + if(installPath.isSubPath(PRELOAD_INSTALLED_PATH)){ + _D("This widget is preloaded."); + } + } Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + _D("package id : %s", pkgId.c_str()); + m_context.tzPkgid = pkgId; + if (!pkgId.empty()) { + if(checkDirectoryExist(pkgId)) { + _E("installed widget status is abnormal"); + return WidgetStatus::ABNORMAL; + } + } + return WidgetStatus::NOT_INSTALLED; + } + return WidgetStatus::Ok; +} + +std::string JobWidgetUninstall::getRemovedTizenId() const +{ + return m_context.tzAppid; +} + +bool JobWidgetUninstall::getRemoveStartedFlag() const +{ + return m_context.removeStarted; +} + +bool JobWidgetUninstall::getRemoveFinishedFlag() const +{ + return m_context.removeFinished; +} + +DPL::Utils::Path JobWidgetUninstall::getManifestFile() const +{ + std::ostringstream manifest_name; + manifest_name << m_context.tzPkgid << ".xml"; + DPL::Utils::Path manifestFile; + + const DPL::Utils::Path PRELOAD_INSTALLED_PATH("/usr/apps/" + m_context.tzPkgid); + const DPL::Utils::Path USR_PACKAGES_PATH("/usr/share/packages"); + const DPL::Utils::Path OPT_PACKAGES_PATH("/opt/share/packages"); + + if (PRELOAD_INSTALLED_PATH == m_context.installedPath) { + _D("This widget is preloaded."); + manifestFile = USR_PACKAGES_PATH; + } else { + manifestFile = OPT_PACKAGES_PATH; + } + + manifestFile /= manifest_name.str(); + _D("Manifest file : %s", manifestFile.Fullpath().c_str()); + + return manifestFile; +} + +void JobWidgetUninstall::SendProgress() +{ + using namespace PackageManager; + if (!getRemoveStartedFlag() || + (getRemoveStartedFlag() && getRemoveFinishedFlag())) + { + if (NULL != GetInstallerStruct().progressCallback) { + // send progress signal of pkgmgr + std::ostringstream percent; + percent << static_cast(GetProgressPercent()); + + _D("Call widget uninstall progressCallback"); + GetInstallerStruct().progressCallback( + GetInstallerStruct().userParam, + GetProgressPercent(), GetProgressDescription()); + } + } +} + +void JobWidgetUninstall::SendFinishedSuccess() +{ + using namespace PackageManager; + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget uninstall success finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + getRemovedTizenId(), + Jobs::Exceptions::Success); +} + +void JobWidgetUninstall::SendFinishedFailure() +{ + using namespace PackageManager; + _E("Error in uninstallation step: %d", m_exceptionCaught); + _E("Message: %s", m_exceptionMessage.c_str()); + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget uninstall failure finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + getRemovedTizenId(), + m_exceptionCaught); + _D("[JobWidgetUninstall] Asynchronous failure callback status sent"); +} + +void JobWidgetUninstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/job_widget_uninstall.h b/src_mobile/jobs/widget_uninstall/job_widget_uninstall.h new file mode 100644 index 0000000..21bb764 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/job_widget_uninstall.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_widet_uninstall.h + * @brief Uninstaller header file. + * @author Radoslaw Wicik r.wicik@samsung.com + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ + +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { + +enum class WidgetStatus +{ + Ok, NOT_INSTALLED, PREALOAD, ABNORMAL, UNRECOGNIZED +}; + +typedef JobContextBase WidgetUnistallStructBase; +typedef JobProgressBase UninstallContextBase; + +class JobWidgetUninstall : + public Job, + public UninstallContextBase, + public WidgetUnistallStructBase +{ + private: + UninstallerContext m_context; + + //TODO move it to base class of all jobs + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; + + public: + /** + * @brief Uninstaller must to know which widget to uninstall. + * + * @param[in] WrtDB::TizenAppId tzAppId - widget to uninstall + */ + JobWidgetUninstall(const std::string &tizenAppId, + const WidgetUninstallationStruct& uninstallerStruct); + + std::string getRemovedTizenId() const; + bool getRemoveStartedFlag() const; + bool getRemoveFinishedFlag() const; + DPL::Utils::Path getManifestFile() const; + + WidgetStatus getWidgetStatus(const std::string &appId); + + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SaveExceptionData(const Jobs::JobExceptionBase &e); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ diff --git a/src_mobile/jobs/widget_uninstall/task_check.cpp b/src_mobile/jobs/widget_uninstall/task_check.cpp new file mode 100644 index 0000000..22a7e91 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_check.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_check.cpp + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task check + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskCheck::TaskCheck(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskCheck::StartStep); + AddStep(&TaskCheck::StepUninstallPreCheck); + AddStep(&TaskCheck::StepCheckMDM); + AddStep(&TaskCheck::EndStep); +} + +TaskCheck::~TaskCheck() +{} + +void TaskCheck::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskCheck::EndStep() +{ + m_context.job->UpdateProgress(UninstallerContext::UNINSTALL_PRECHECK, + "Uninstall pre-checking Finished"); + _D("--------- : END ----------"); +} + +void TaskCheck::StepUninstallPreCheck() +{ + bool isRunning = false; + int ret = app_manager_is_running(m_context.tzAppid.c_str(), &isRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Exceptions::PlatformAPIFailure, + "Fail to get widget state"); + } + + if (true == isRunning) { + // get app_context for running application + // app_context must be released with app_context_destroy + app_context_h appCtx = NULL; + ret = app_manager_get_app_context(m_context.tzAppid.c_str(), &appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get app_context"); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } + + // terminate app_context_h + ret = app_manager_terminate_app(appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to terminate running application"); + app_context_destroy(appCtx); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } else { + app_context_destroy(appCtx); + // app_manager_terminate_app isn't sync API + // wait until application isn't running (50ms * 100) + bool isStillRunning = true; + int checkingloop = 100; + struct timespec duration = { 0, 50 * 1000 * 1000 }; + while (--checkingloop >= 0) { + nanosleep(&duration, NULL); + int ret = app_manager_is_running(m_context.tzAppid.c_str(), &isStillRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Exceptions::PlatformAPIFailure, + "Fail to get widget state"); + } + if (!isStillRunning) { + break; + } + } + if (isStillRunning) { + _E("Fail to terminate running application"); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } + _D("terminate application"); + } + } + + _D("Widget Can be uninstalled, Pkgname : %s", m_context.tzAppid.c_str()); +} + +void TaskCheck::StepCheckMDM() +{ + _D("StepCheckMDM"); + + if (PMINFO_R_OK != pkgmgr_parser_check_mdm_policy_for_uninstallation( + m_context.manifestFile.Fullpath().c_str())) { + _E("Failed to check mdm policy"); + ThrowMsg(Exceptions::CheckMDMPolicyFailure, "Can't uninstall! Because of MDM policy"); + } +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_check.h b/src_mobile/jobs/widget_uninstall/task_check.h new file mode 100644 index 0000000..29c00db --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_check.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_check.h + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task check + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ + +#include + +struct UninstallerContext; //forward declaration +class WidgetModel; + +namespace Jobs { +namespace WidgetUninstall { +class TaskCheck : + public DPL::TaskDecl +{ + private: + //context + UninstallerContext& m_context; + + //steps + void StepUninstallPreCheck(); + void StepCheckMDM(); + void StartStep(); + void EndStep(); + + public: + TaskCheck(UninstallerContext& context); + virtual ~TaskCheck(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ */ diff --git a/src_mobile/jobs/widget_uninstall/task_db_update.cpp b/src_mobile/jobs/widget_uninstall/task_db_update.cpp new file mode 100644 index 0000000..f38280d --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_db_update.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller task database updating + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetUninstall { +TaskDbUpdate::TaskDbUpdate(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskDbUpdate::StartStep); + AddStep(&TaskDbUpdate::StepRemoveExternalLocations); + AddStep(&TaskDbUpdate::StepDbUpdate); + AddStep(&TaskDbUpdate::StepLiveboxDBDelete); + AddStep(&TaskDbUpdate::EndStep); +} + +TaskDbUpdate::~TaskDbUpdate() +{} + +void TaskDbUpdate::StepDbUpdate() +{ + Try + { + //TODO: widget handle should not be used any more + ace_unregister_widget(static_cast( + WidgetDAOReadOnly::getHandle(DPL:: + FromUTF8String( + m_context. + tzAppid)))); + WidgetDAO::unregisterWidget(DPL::FromUTF8String(m_context.tzAppid)); + + _D("Unregistered widget successfully!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Failed to handle StepDbUpdate!"); + ReThrowMsg(Exceptions::DatabaseFailure, + "Failed to handle StepDbUpdate!"); + } +} + +void TaskDbUpdate::StepLiveboxDBDelete() +{ + int ret = + web_provider_livebox_delete_by_app_id(m_context.tzAppid.c_str()); + + if (ret < 0) { + _D("failed to delete box info"); + } else { + _D("delete box info: %s", m_context.tzAppid.c_str()); + } +} + +void TaskDbUpdate::StepRemoveExternalLocations() +{ + if (!m_context.removeAbnormal) { + WidgetDAO dao(DPL::FromUTF8String(m_context.tzAppid)); + _D("Removing external locations:"); + WrtDB::ExternalLocationList externalPaths = dao.getWidgetExternalLocations(); + FOREACH(file, externalPaths) + { + DPL::Utils::Path path(*file); + if(path.Exists()){ + if(path.IsFile()){ + _D(" -> %s", path.Fullpath().c_str()); + Try{ + DPL::Utils::Remove(path); + }Catch(DPL::Utils::Path::BaseException){ + _E("Failed to remove the file: %s", path.Fullpath().c_str()); + } + } else if (path.IsDir()){ + _D(" -> %s", path.Fullpath().c_str()); + Try{ + DPL::Utils::Remove(path); + }Catch(DPL::Utils::Path::BaseException){ + Throw(Jobs::WidgetUninstall::TaskDbUpdate:: + Exception::RemoveFilesFailed); + } + } + }else{ + _W(" -> %s(no such a path)", path.Fullpath().c_str()); + } + } + dao.unregisterAllExternalLocations(); + } +} + +void TaskDbUpdate::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskDbUpdate::EndStep() +{ + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_DB_UPDATE, + "Widget DB Update Finished"); + + _D("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_db_update.h b/src_mobile/jobs/widget_uninstall/task_db_update.h new file mode 100644 index 0000000..3b74ad5 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_db_update.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task database updating + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ + +#include +#include +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskDbUpdate : + public DPL::TaskDecl +{ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DbStepFailed) + DECLARE_EXCEPTION_TYPE(Base, RemoveFilesFailed) + }; + + UninstallerContext& m_context; + + private: + void StepDbUpdate(); + void StepLiveboxDBDelete(); + void StepRemoveExternalLocations(); + + void StartStep(); + void EndStep(); + + public: + TaskDbUpdate(UninstallerContext& context); + virtual ~TaskDbUpdate(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ diff --git a/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.cpp b/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.cpp new file mode 100644 index 0000000..88db8e2 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_delete_pkginfo.cpp + * @author Leerang Song(leerang.song@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller delete package information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskDeletePkgInfo::TaskDeletePkgInfo( + UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskDeletePkgInfo::StartStep); + AddStep(&TaskDeletePkgInfo::StepDeletePkgInfo); + AddStep(&TaskDeletePkgInfo::EndStep); +} + +void TaskDeletePkgInfo::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskDeletePkgInfo::EndStep() +{ + _D("--------- : END ----------"); +} + +void TaskDeletePkgInfo::StepDeletePkgInfo() +{ + std::ostringstream manifest_name; + manifest_name << m_context.tzPkgid << ".xml"; + DPL::Utils::Path pre_manifest("/usr/share/packages"); + pre_manifest /= manifest_name.str(); + + if (!(m_context.manifestFile.Exists() == 0 && pre_manifest.Exists())) { + if (0 != pkgmgr_parser_parse_manifest_for_uninstallation( + m_context.manifestFile.Fullpath().c_str(), NULL)) { + _W("Manifest file failed to parse for uninstallation"); + } + } + if (!DPL::Utils::TryRemove(m_context.manifestFile)) { + _W("No manifest file found: %s", m_context.manifestFile.Fullpath().c_str()); + } else { + _D("Manifest file removed: %s", m_context.manifestFile.Fullpath().c_str()); + } +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.h b/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.h new file mode 100644 index 0000000..4624c4a --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_delete_pkginfo.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_delete_pkginfo.h + * @author Leerang Song(leerang.song@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task delete package infomation + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ + +#include +#include + +struct UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskDeletePkgInfo : + public DPL::TaskDecl +{ + UninstallerContext& m_context; + + private: + void StepDeletePkgInfo(); + + void StartStep(); + void EndStep(); + + public: + TaskDeletePkgInfo(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif +// WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ diff --git a/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.cpp b/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.cpp new file mode 100644 index 0000000..f981b0c --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_custom_handlers.cpp + * @author Przemyslaw Ciezkowski (p.ciezkowski@samsung.com) + * @version 1.0 + * @brief File for uninstaller - remove custom handlers + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskRemoveCustomHandlers::TaskRemoveCustomHandlers(UninstallerContext& context) + : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveCustomHandlers::StartStep); + AddStep(&TaskRemoveCustomHandlers::Step); + AddStep(&TaskRemoveCustomHandlers::EndStep); +} + +void TaskRemoveCustomHandlers::Step() +{ + _D("Removing widget from appsvc"); + int result = appsvc_unset_defapp(m_context.tzAppid.c_str()); + _D("Result: %d", result); + + CustomHandlerDB::Interface::attachDatabaseRW(); + CustomHandlerDB::CustomHandlerDAO handlersDao( + DPL::FromASCIIString(m_context.tzAppid)); + handlersDao.removeWidgetProtocolHandlers(); + handlersDao.removeWidgetContentHandlers(); + CustomHandlerDB::Interface::detachDatabase(); +} + +void TaskRemoveCustomHandlers::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskRemoveCustomHandlers::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.h b/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.h new file mode 100644 index 0000000..e6458b9 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_remove_custom_handlers.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_custom_handlers.h + * @author Przemyslaw Ciezkowski (p.ciezkowski@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller - remove custom handlers + */ +#ifndef INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H +#define INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H + +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskRemoveCustomHandlers : + public DPL::TaskDecl +{ + private: + UninstallerContext& m_context; + + void Step(); + + void StartStep(); + void EndStep(); + + public: + TaskRemoveCustomHandlers(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H */ diff --git a/src_mobile/jobs/widget_uninstall/task_remove_files.cpp b/src_mobile/jobs/widget_uninstall/task_remove_files.cpp new file mode 100644 index 0000000..f9f22de --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_remove_files.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_files.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller task for removing widget files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +using namespace WrtDB; + +TaskRemoveFiles::TaskRemoveFiles(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveFiles::StartStep); + AddStep(&TaskRemoveFiles::StepRemoveInstallationDirectory); + AddStep(&TaskRemoveFiles::StepRemoveFinished); + AddStep(&TaskRemoveFiles::EndStep); +} + +TaskRemoveFiles::~TaskRemoveFiles() +{} + +void TaskRemoveFiles::StepRemoveInstallationDirectory() +{ + _D("StepRemoveInstallationDirectory started"); + Try { + int ret = app2ext_get_app_location(m_context.tzPkgid.c_str()); + + if (APP2EXT_SD_CARD == ret) { + _D("Remove external directory"); + Try { + WidgetInstallToExtSingleton::Instance().initialize(m_context.tzPkgid); + WidgetInstallToExtSingleton::Instance().uninstallation(); + WidgetInstallToExtSingleton::Instance().deinitialize(); + } + Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) + { + // Continue uninstall even fail to remove external directory. + // i.e.) SD card isn't inserted or unmounted. + // This behavior is recommended by platform(app2sd maintainer). + _E("Fail to remove external directory"); + } + } + if (APP2EXT_NOT_INSTALLED != ret) { + _D("Removing directory"); + m_context.removeStarted = true; + DPL::Utils::Path widgetDir= m_context.installedPath; + Try{ + DPL::Utils::Remove(widgetDir); + } Catch(DPL::Utils::Path::BaseException){ + _E("Removing widget installation directory failed : %s", widgetDir.Fullpath().c_str()); + } + DPL::Utils::Path dataDir(m_context.locations->getUserDataRootDir()); + Try{ + DPL::Utils::Remove(dataDir); + } Catch(DPL::Utils::Path::BaseException){ + _W("%s is already removed", dataDir.Fullpath().c_str()); + } + } else { + _E("app is not installed"); + ThrowMsg(Exceptions::WidgetNotExist, "failed to get app location"); + } + } Catch(Exception::RemoveFilesFailed) { + ThrowMsg(Exceptions::RemoveFileFailure, "Cann't remove directory"); + } + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_REMOVE_WIDGETDIR, + "Widget INstallation Directory Removal Finished"); +} + +void TaskRemoveFiles::StepRemoveFinished() +{ + _D("StepRemoveFinished finished"); + + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_REMOVE_FINISHED, + "Widget remove steps Finished"); +} + +void TaskRemoveFiles::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskRemoveFiles::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_remove_files.h b/src_mobile/jobs/widget_uninstall/task_remove_files.h new file mode 100644 index 0000000..276fb24 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_remove_files.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_files.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task remove files + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ + +//forward declaration +struct UninstallerContext; + +#include +#include + +#include + +namespace Jobs { +namespace WidgetUninstall { +class TaskRemoveFiles : + public DPL::TaskDecl +{ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, RemoveFilesFailed) + }; + + UninstallerContext& m_context; + + private: + void StepRemoveInstallationDirectory(); + void StepRemoveFinished(); + + void StartStep(); + void EndStep(); + + public: + explicit TaskRemoveFiles(UninstallerContext& context); + virtual ~TaskRemoveFiles(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ diff --git a/src_mobile/jobs/widget_uninstall/task_smack.cpp b/src_mobile/jobs/widget_uninstall/task_smack.cpp new file mode 100644 index 0000000..e3471b3 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_smack.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.cpp + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task smack + */ + +#include +#include +#include +#include +#include + +#ifdef WRT_SMACK_ENABLED +#include +#endif + +namespace Jobs { +namespace WidgetUninstall { +TaskSmack::TaskSmack(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskSmack::StartStep); + AddStep(&TaskSmack::Step); + AddStep(&TaskSmack::EndStep); +} + +void TaskSmack::Step() +{ + _D("------------------------> SMACK: Jobs::WidgetUninstall::TaskSmack::Step()"); +#ifdef WRT_SMACK_ENABLED + const char* pkgId = m_context.tzPkgid.c_str(); + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(pkgId)) { + _E("failure in revoking smack permissions"); + } + + if (PC_OPERATION_SUCCESS != perm_app_uninstall(pkgId)) { + _E("failure in removing smack rules file"); + } +#endif +} + +void TaskSmack::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskSmack::EndStep() +{ + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_SMACK_DISABLE, + "Widget SMACK Disabled"); + + _D("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_smack.h b/src_mobile/jobs/widget_uninstall/task_smack.h new file mode 100644 index 0000000..c7886b9 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_smack.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.h + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task smack + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H +#define INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H + +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskSmack : + public DPL::TaskDecl +{ + private: + UninstallerContext& m_context; + + void Step(); + + void StartStep(); + void EndStep(); + + public: + TaskSmack(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H */ diff --git a/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.cpp b/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.cpp new file mode 100644 index 0000000..10cf130 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_uninstall_ospsvc.cpp + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task to uninstall ospsvc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const int MAX_BUF_SIZE = 128; +const char* OSP_INSTALL_STR = "/usr/etc/package-manager/backend/tpk -uv "; +} + +namespace Jobs { +namespace WidgetUninstall { +TaskUninstallOspsvc::TaskUninstallOspsvc(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUninstallOspsvc::StartStep); + AddStep(&TaskUninstallOspsvc::StepUninstallOspsvc); + AddStep(&TaskUninstallOspsvc::EndStep); +} + +TaskUninstallOspsvc::~TaskUninstallOspsvc() +{} + +void TaskUninstallOspsvc::StepUninstallOspsvc() +{ + _D("Step : Uninstall Osp service"); + + std::ostringstream commStr; + commStr << OSP_INSTALL_STR << BashUtils::escape_arg(m_context.tzPkgid); + _D("osp uninstall command : %s", commStr.str().c_str()); + + char readBuf[MAX_BUF_SIZE]; + FILE *fd; + fd = popen(commStr.str().c_str(), "r"); + if (NULL == fd) { + _E("Failed to uninstalltion osp service"); + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + uninstall osp service"); + } + + if(fgets(readBuf, MAX_BUF_SIZE, fd) == NULL) + { + _E("Failed to uninstalltion osp service\ + Inability of reading file."); + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + uninstall osp service"); + } + _D("return value : %s", readBuf); + + int result = atoi(readBuf); + if (0 != result) { + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + install osp service"); + } + + pclose(fd); + + _D("Widget Can be uninstalled. Pkgname : %s", m_context.tzPkgid.c_str()); + m_context.job->UpdateProgress(UninstallerContext::UNINSTALL_REMOVE_OSPSVC, + "Uninstall OSP service finished"); +} + +void TaskUninstallOspsvc::StartStep() +{ + _D("--------- : START ----------"); +} + +void TaskUninstallOspsvc::EndStep() +{ + _D("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.h b/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.h new file mode 100644 index 0000000..50ec347 --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/task_uninstall_ospsvc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_uninstall_ospsvc.h + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task to uninstall ospsvc + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H + +#include + +struct UninstallerContext; //forward declaration + +namespace Jobs { +namespace WidgetUninstall { +class TaskUninstallOspsvc : + public DPL::TaskDecl +{ + private: + //context + UninstallerContext& m_context; + + //steps + void StepUninstallOspsvc(); + + void StartStep(); + void EndStep(); + + public: + TaskUninstallOspsvc(UninstallerContext& context); + virtual ~TaskUninstallOspsvc(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H */ diff --git a/src_mobile/jobs/widget_uninstall/uninstaller_context.h b/src_mobile/jobs/widget_uninstall/uninstaller_context.h new file mode 100644 index 0000000..b6cf5bd --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/uninstaller_context.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file uninstaller_context.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version + * @brief Definition file of installer tasks data structures + */ + +#ifndef WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ +#define WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ + +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +class JobWidgetUninstall; +} //namespace WidgetUninstall +} //namespace Jobs + +struct UninstallerContext +{ + enum UninstallStep + { + UNINSTALL_START, + UNINSTALL_PRECHECK, + UNINSTALL_REMOVE_WIDGETDIR, + UNINSTALL_REMOVE_DESKTOP, + UNINSTALL_REMOVE_FINISHED, + UNINSTALL_DB_UPDATE, + UNINSTALL_REMOVE_OSPSVC, + UNINSTALL_SMACK_DISABLE, + UNINSTALL_END + }; + + ///< flag that indicates whether installer starts + //to remove files.rStruct; + bool removeStarted; + ///< flag that indicates whether installer finishes + //to remove files completely. + bool removeFinished; + + DPL::Optional locations; + + UninstallStep uninstallStep; ///< current step of installation + Jobs::WidgetUninstall::JobWidgetUninstall *job; + std::string tzAppid; + std::string tzPkgid; + bool removeAbnormal; + DPL::Utils::Path installedPath; + DPL::Utils::Path manifestFile; +}; + +#endif // WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ diff --git a/src_mobile/jobs/widget_uninstall/widget_uninstall_errors.h b/src_mobile/jobs/widget_uninstall/widget_uninstall_errors.h new file mode 100644 index 0000000..3c5970b --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/widget_uninstall_errors.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_uninstall_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef WIDGET_UNINSTALL_ERRORS_H_ +#define WIDGET_UNINSTALL_ERRORS_H_ + +#include +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetUninstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) + +DECLARE_JOB_EXCEPTION(Base, DatabaseFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, AlreadyUninstalling, + ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, AppIsRunning, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, WidgetNotExist, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, UninstallOspSvcFailed, + ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PlatformAPIFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, RemoveFileFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, Unremovable, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, CheckMDMPolicyFailure, ErrorWidgetUninstallationFailed) +} //namespace +} //namespace +} //namespace + +#endif /* WIDGET_UNINSTALL_ERRORS_H_ */ diff --git a/src_mobile/jobs/widget_uninstall/widget_uninstaller_struct.h b/src_mobile/jobs/widget_uninstall/widget_uninstaller_struct.h new file mode 100644 index 0000000..3f33e4b --- /dev/null +++ b/src_mobile/jobs/widget_uninstall/widget_uninstaller_struct.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_uninstaller_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include + +//Widget Uninstaller typedefs +typedef void (*UninstallerFinishedCallback)( + void *userParam, + std::string tizenId, + Jobs::Exceptions::Type); + +typedef void (*UninstallerProgressCallback)( + void *userParam, + ProgressPercent percent, + const ProgressDescription &description); + +//UninstallationStruct +typedef Jobs::JobCallbacksBase +WidgetUninstallCallbackBase; + +struct WidgetUninstallationStruct : public WidgetUninstallCallbackBase +{ + std::shared_ptr pkgmgrInterface; + + // It must be empty-constructible as a parameter of generic event + WidgetUninstallationStruct() + {} + + WidgetUninstallationStruct( + UninstallerFinishedCallback finished, + UninstallerProgressCallback progress, + void *param, + std::shared_ptr + _pkgmgrInterface + ) : + WidgetUninstallCallbackBase(finished, progress, param), + pkgmgrInterface(_pkgmgrInterface) + {} +}; +#endif // WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ diff --git a/src_mobile/logic/installer_controller.cpp b/src_mobile/logic/installer_controller.cpp new file mode 100644 index 0000000..d9b41d5 --- /dev/null +++ b/src_mobile/logic/installer_controller.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "installer_controller.h" +#include + +IMPLEMENT_SINGLETON(Logic::InstallerController) + +namespace Logic { +InstallerController::InstallerController() +{} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InstallWidgetEvent &event) +{ + std::string fileName = event.GetArg0(); + std::string pkgId = event.GetArg1(); + Jobs::WidgetInstall::WidgetInstallationStruct installerStruct = event.GetArg2(); + m_installerLogic.InstallWidget(fileName, pkgId, installerStruct); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InstallPluginEvent &event) +{ + std::string dirName = event.GetArg0(); + PluginInstallerStruct installerStruct = event.GetArg1(); + m_installerLogic.InstallPlugin(dirName, installerStruct); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::UninstallWidgetEvent &event) +{ + std::string widgetPkgName = event.GetArg0(); + WidgetUninstallationStruct uninstallerStruct = event.GetArg1(); + m_installerLogic.UninstallWidget(widgetPkgName, uninstallerStruct); +} + +Eina_Bool InstallerController::AddNextStep(void *data) +{ + Jobs::Job* model = static_cast(data); + CONTROLLER_POST_EVENT(InstallerController, + InstallerControllerEvents::NextStepEvent(model)); + + return ECORE_CALLBACK_CANCEL; +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::NextStepEvent &event) +{ + Jobs::Job* model = event.GetArg0(); + Assert(model != NULL); + + if (m_installerLogic.NextStep(model)) { + ecore_idler_add(AddNextStep, model); + } +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InitializeEvent & /*event*/) +{ + m_installerLogic.Initialize(); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::TerminateEvent & /*event*/) +{ + m_installerLogic.Terminate(); +} +} //Logic + diff --git a/src_mobile/logic/installer_controller.h b/src_mobile/logic/installer_controller.h new file mode 100644 index 0000000..9f04288 --- /dev/null +++ b/src_mobile/logic/installer_controller.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_CONTROLLER_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_CONTROLLER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief holds events send to InstallControler + */ +namespace InstallerControllerEvents { +/** + * @brief Event for inicieting instalation process. + * + * This event holds std::string witch should be path to widget package + */ +DECLARE_GENERIC_EVENT_3(InstallWidgetEvent, + std::string, // zipFileName + std::string, // package id + Jobs::WidgetInstall::WidgetInstallationStruct) // installerStruct + +/** + * @brief Event for iniciating plugin instalation process. + * This event holds std::string witch should be path to plugin directory + * and PluginInstallerStruct which contain + * StatusCallack, progressCallback and private data for callbacks + */ +DECLARE_GENERIC_EVENT_2(InstallPluginEvent, std::string, PluginInstallerStruct) + +/** + * @brief Event for inicietig widget uninstallation. + * + * tizen id is used to point witch widget shuld be uninstalled + */ +DECLARE_GENERIC_EVENT_2(UninstallWidgetEvent, + std::string, + WidgetUninstallationStruct) + +/** + * @brief Event for pushing installation process forward. + */ +DECLARE_GENERIC_EVENT_1(NextStepEvent, Jobs::Job *) + +DECLARE_GENERIC_EVENT_0(InitializeEvent) +DECLARE_GENERIC_EVENT_0(TerminateEvent) +} // namespace InstallerEvents + +namespace Logic { +/** + * @brief Controls Widget installation + * + * Main Controler of wiget installation/uninstallation, this is also used + * for pushing forward each of processes. + * It waits for three events: + *
    + *
  • InstallWidgetEvent
  • + *
  • UninstallWidgetEvent
  • + *
  • NextStepEvent
  • + *
+ */ + +typedef DPL::TypeListDecl< + InstallerControllerEvents::InstallWidgetEvent, + InstallerControllerEvents::InstallPluginEvent, + InstallerControllerEvents::UninstallWidgetEvent, + InstallerControllerEvents::NextStepEvent, + InstallerControllerEvents::InitializeEvent, + InstallerControllerEvents::TerminateEvent>::Type +InstallerControllerEventsSet; + +class InstallerController : public DPL::Event::Controller< + InstallerControllerEventsSet> +{ + protected: + /** + * @brief Executed on InstallWidgetEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::InstallWidgetEvent &event); + + /** + * @brief Executed on InstallPluginEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::InstallPluginEvent &event); + + /** + * @brief Executed on UninstallWidgetEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::UninstallWidgetEvent &event); + /** + * @brief Executed on NextStepEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::NextStepEvent &event); + + virtual void OnEventReceived( + const InstallerControllerEvents::InitializeEvent &event); + virtual void OnEventReceived( + const InstallerControllerEvents::TerminateEvent &event); + + private: + // Embedded logic + Logic::InstallerLogic m_installerLogic; + + InstallerController(); + + static Eina_Bool AddNextStep(void *data); + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton InstallerControllerSingleton; +} + +#endif // INSTALLER_CONTROLLER_H diff --git a/src_mobile/logic/installer_logic.cpp b/src_mobile/logic/installer_logic.cpp new file mode 100644 index 0000000..df67764 --- /dev/null +++ b/src_mobile/logic/installer_logic.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Logic { +InstallerLogic::InstallerLogic() : + m_job(0), + m_NextHandle(0) +{} + +InstallerLogic::~InstallerLogic() +{ + Assert(!m_job && "There are still running job"); + //FIXME what should be done here? +} + +void InstallerLogic::Initialize() +{ + _D("Done"); +} + +void InstallerLogic::Terminate() +{ + //TODO how to delete, if it is still running, paused and so on + if(m_job) + m_job->SetPaused(true); + + _D("Done"); +} + +Jobs::JobHandle InstallerLogic::AddAndStartJob() +{ + Jobs::JobHandle handle = GetNewJobHandle(); + m_job->SetJobHandle(handle); + //Start job + CONTROLLER_POST_EVENT(InstallerController, + InstallerControllerEvents::NextStepEvent(m_job)); + + return handle; +} + +//InstallWidget, UninstallWidget InstallPlugin method are almost the same +// But each Job has different constructor, so creating new Job is specific +Jobs::JobHandle InstallerLogic::InstallWidget( + const std::string & widgetPath, + const std::string & pkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Widget Installation:"); + + m_job = new Jobs::WidgetInstall::JobWidgetInstall(widgetPath, pkgId, installerStruct); + + return AddAndStartJob(); +} + +Jobs::JobHandle InstallerLogic::UninstallWidget( + const std::string & widgetPkgName, + const + WidgetUninstallationStruct &uninstallerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Widget Uninstallation"); + + m_job = + new Jobs::WidgetUninstall::JobWidgetUninstall(widgetPkgName, + uninstallerStruct); + + return AddAndStartJob(); +} + +Jobs::JobHandle InstallerLogic::InstallPlugin( + std::string const & pluginPath, // TODO change type to PluginPath + const PluginInstallerStruct & + installerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Plugin Installation"); + + // TODO Conversion to PluginPath is temporary + m_job = + new Jobs::PluginInstall::JobPluginInstall(PluginPath(pluginPath), installerStruct); + + // before start install plugin, reset plugin data which is stopped + // during installing. (PluginDAO::INSTALLATION_IN_PROGRESS) + ResetProgressPlugins(); + + return AddAndStartJob(); +} + +#define TRANSLATE_JOB_EXCEPTION() \ + _rethrown_exception.getParam() +#define TRANSLATE_JOB_MESSAGE() \ + _rethrown_exception.GetMessage() + +bool InstallerLogic::NextStep(Jobs::Job *job) +{ + Try { + bool stepSucceded = job->NextStep(); + + job->SendProgress(); + + if (stepSucceded) { + return !job->IsPaused(); + } + + if (!job->GetAbortStarted()) { + //job successfully finished + + //send finished callback + job->SendFinishedSuccess(); + + switch (job->GetInstallationType()) { + case Jobs::PluginInstallation: + InstallWaitingPlugins(); + break; + default: //because of warning + break; + } + } else { + //job abort process completed + job->SendFinishedFailure(); + } + + //clean job + delete job; + m_job=0; + + return false; + } catch (Jobs::JobExceptionBase &exc) { + //start revert job + _D("Exception occured: %d. Reverting job...", exc.getParam()); + bool hasAbortSteps = job->Abort(); + job->SetAbortStarted(true); + job->SaveExceptionData(exc); + + if (!hasAbortSteps) { + //no AbortSteps + job->SendFinishedFailure(); + + //clean job + delete job; + m_job=0; + } + return hasAbortSteps; + } +} + +void InstallerLogic::InstallWaitingPlugins() +{ + PluginHandleSetPtr waitingPlugins; + + waitingPlugins = + PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_WAITING); + + FOREACH(it, *waitingPlugins) + { + resolvePluginDependencies(*it); + } +} + +void InstallerLogic::ResetProgressPlugins() +{ + PluginHandleSetPtr progressPlugins; + + progressPlugins = + PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_IN_PROGRESS); + + FOREACH(it, *progressPlugins) { + FeatureHandleListPtr featureListPtr = + FeatureDAOReadOnly::GetFeatureHandleListForPlugin(*it); + FOREACH(ItFeature, *featureListPtr) { + FeatureDAO::UnregisterFeature(*ItFeature); + } + PluginDAO::unregisterPlugin(*it); + } +} + +bool InstallerLogic::resolvePluginDependencies(PluginHandle handle) +{ + PluginHandleSetPtr dependencies(new PluginHandleSet); + + PluginObjects::ObjectsPtr requiredObjects = + PluginDAO::getRequiredObjectsForPluginHandle(handle); + + PluginHandle depHandle = + Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE; + + FOREACH(requiredObject, *requiredObjects) + { + depHandle = + PluginDAO::getPluginHandleForImplementedObject(*requiredObject); + + if (depHandle == + Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE) + { + _E("Library implementing: %s NOT FOUND", (*requiredObject).c_str()); + + //PluginDAO::SetPluginInstallationStatus(INSTALLATION_WAITING); + return false; + } + dependencies->insert(depHandle); + } + + PluginDAO::registerPluginLibrariesDependencies(handle, dependencies); + PluginDAO::setPluginInstallationStatus(handle, + PluginDAO::INSTALLATION_COMPLETED); + + return true; +} +} + diff --git a/src_mobile/logic/installer_logic.h b/src_mobile/logic/installer_logic.h new file mode 100644 index 0000000..abe1b5c --- /dev/null +++ b/src_mobile/logic/installer_logic.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_LOGIC_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_LOGIC_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Logic { +class InstallerLogic +{ + Jobs::Job* m_job; + + void ResetProgressPlugins(); + void InstallWaitingPlugins(); + bool resolvePluginDependencies(PluginHandle handle); + + Jobs::JobHandle m_NextHandle; + Jobs::JobHandle GetNewJobHandle() + { + return m_NextHandle++; + } + Jobs::JobHandle AddAndStartJob(); + + public: + virtual ~InstallerLogic(); + + void Initialize(); + + void Terminate(); + + Jobs::JobHandle InstallWidget( + const std::string & widgetPath, + const std::string & pkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct); + + Jobs::JobHandle UninstallWidget( + const std::string & widgetPkgName, + const WidgetUninstallationStruct & + uninstallerStruct); + + Jobs::JobHandle InstallPlugin(std::string const & pluginPath, + const PluginInstallerStruct &installerStruct); + + bool NextStep(Jobs::Job* installModel); + + //TODO implement me + bool AbortJob(const Jobs::JobHandle & /*handle*/) + { + _W("Not implemented"); + return true; + } + + private: + InstallerLogic(); + + friend class InstallerController; +}; +} + +#endif // INSTALLER_LOGIC_H diff --git a/src_mobile/misc/feature_logic.cpp b/src_mobile/misc/feature_logic.cpp new file mode 100644 index 0000000..818b0cd --- /dev/null +++ b/src_mobile/misc/feature_logic.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "feature_logic.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const DPL::String PRIVILEGE_TESTAUTOMATION = + L"http://tizen.org/privilege/testautomation"; +const DPL::String DEVICE_CAPABILITY_TESTAUTOMATION = L"testautomation"; +} +FeatureLogic::FeatureLogic(const WrtDB::TizenAppId & tzAppid) : + m_rejected(false) +{ + WrtDB::WidgetDAOReadOnly widgetDao(tzAppid); + WidgetFeatureSet featureSet = widgetDao.getFeaturesList(); + FOREACH(it, featureSet) { + _D("Feature name : %ls", it->name.c_str()); + WrtDB::DeviceCapabilitySet dcs; + if (!DPL::StringCompare(it->name, PRIVILEGE_TESTAUTOMATION)) { + // special privilege + // This privilege doesn't have plugin in the target + // only use to special behavior + dcs.insert(DEVICE_CAPABILITY_TESTAUTOMATION); + } else { + // normal privilege + dcs = WrtDB::FeatureDAOReadOnly::GetDeviceCapability(it->name); + } + FOREACH(devCap, dcs) { + _D("--- dev cap : %ls", (*devCap).c_str()); + } + Feature feature(*it, dcs); + m_featureList.push_back(feature); + } + m_currentFeature = m_featureList.begin(); + + // ok we must set iterator on the first processable node + if (!isProcessable()) { + next(); + } +} + +bool FeatureLogic::isDone() const +{ + return m_currentFeature == m_featureList.end(); +} + +bool FeatureLogic::next() +{ + while (!isDone()) { + if (m_currentFeature->currentCap != + m_currentFeature->devCapSet.end()) + { + m_currentFeature->currentCap++; + } else { + ++m_currentFeature; + } + // we moved pointer + if (isProcessable()) { + return true; + } + } + return false; +} + +void FeatureLogic::setAceResponse(bool allowed) +{ + Assert(isProcessable() && "Wrong usage"); + if (!allowed) { + m_currentFeature->rejected = true; + m_rejected = true; + } +} + +DPL::String FeatureLogic::getDevice() const +{ + return *(m_currentFeature->currentCap); +} + +bool FeatureLogic::isProcessable() const +{ + if (isDone()) { + return false; + } + + if (m_currentFeature->currentCap == m_currentFeature->devCapSet.end()) { + return false; + } + + return true; +} +} // namespace WidgetInstall +} // namespace Jobs + diff --git a/src_mobile/misc/feature_logic.h b/src_mobile/misc/feature_logic.h new file mode 100644 index 0000000..d407cb0 --- /dev/null +++ b/src_mobile/misc/feature_logic.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_INSTALLER_MISC_FEATURE_LOGIC +#define SRC_INSTALLER_MISC_FEATURE_LOGIC + +#include +#include + +#include +#include +#include + +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class FeatureLogic : DPL::Noncopyable +{ + public: + + FeatureLogic(const WrtDB::TizenAppId & tzAppid); + + bool isDone() const; + + bool next(); + + void setAceResponse(bool allowed); + + DPL::String getDevice() const; + + bool isRejected(void) const + { + return m_rejected; + } + + struct Feature : public WidgetFeature { + WrtDB::DeviceCapabilitySet devCapSet; + WrtDB::DeviceCapabilitySet::const_iterator currentCap; + + Feature(const WidgetFeature &wf, + const WrtDB::DeviceCapabilitySet &set) : + WidgetFeature(wf) + , devCapSet(set) + { + currentCap = devCapSet.begin(); + } + + explicit Feature(const Feature &second) : WidgetFeature(second) + { + devCapSet = second.devCapSet; + currentCap = devCapSet.find(*second.currentCap); + rejected = second.rejected; + } + + private: + void operator=(const Feature &second) + { + name = second.name; + devCapSet = second.devCapSet; + rejected = second.rejected; + pluginId = second.pluginId; + currentCap = devCapSet.find(*second.currentCap); + } + }; + + typedef std::list FeatureList; + typedef FeatureList::const_iterator FeatureIterator; + + FeatureIterator resultBegin() + { + return m_featureList.begin(); + } + FeatureIterator resultEnd() + { + return m_featureList.end(); + } + + private: + bool isProcessable() const; + + FeatureList m_featureList; + FeatureList::iterator m_currentFeature; + bool m_rejected; +}; + +typedef std::shared_ptr FeatureLogicPtr; +} // namespace WidgetInstall +} // namespace Jobs + +#endif // SRC_INSTALLER_MISC_FEATURE_LOGIC diff --git a/src_mobile/misc/libxml_utils.cpp b/src_mobile/misc/libxml_utils.cpp new file mode 100644 index 0000000..114e95b --- /dev/null +++ b/src_mobile/misc/libxml_utils.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libxml_utils.cpp + * @author Tomasz Iwanek (t.iwanek@samsung.com) + */ + +#include "libxml_utils.h" + +#include +#include + +IMPLEMENT_SINGLETON(LibxmlUtils) + +LibxmlUtils::LibxmlUtils() : isInitialized(false) +{} + +LibxmlUtils::~LibxmlUtils() +{ + if (isInitialized) { + _D("Libxml - cleaning"); + // Cleanup function for the XML library. + xmlCleanupParser(); + //this is to debug memory for regression tests + xmlMemoryDump(); + } +} + +void LibxmlUtils::init() +{ + if (!isInitialized) { + LIBXML_TEST_VERSION + isInitialized = true; + _D("Libxml have been initialized"); + } + _D("Libxml already initialized"); +} + diff --git a/src_mobile/misc/libxml_utils.h b/src_mobile/misc/libxml_utils.h new file mode 100644 index 0000000..5354bda --- /dev/null +++ b/src_mobile/misc/libxml_utils.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libxml_utils.h + * @author Tomasz Iwanek (t.iwanek@samsung.com) + */ + +#ifndef LIBXML_UTILS_H +#define LIBXML_UTILS_H + +#include +#include + +#include +#include +#include +#include + +/** + * @brief The LibxmlUtils class + * + * Singleton for assurence for libxml2 initialization + * + * Use: LibxmlUtils::Instance().init(); to initialize library + * + */ +class LibxmlUtils +{ + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, Libxml2Error) + + LibxmlUtils(); + ~LibxmlUtils(); + + void init(); + + private: + bool isInitialized; + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton LibxmlSingleton; + +#endif // LIBXML_UTILS_H diff --git a/src_mobile/misc/plugin_path.cpp b/src_mobile/misc/plugin_path.cpp new file mode 100644 index 0000000..2b2ebdb --- /dev/null +++ b/src_mobile/misc/plugin_path.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_path_builder.cpp + * @author Kamil Nować (k.nowac@partner.samgsung.com) + * @version + * @brief + */ + +#include +#include +#include + +using namespace DPL::Utils; + +PluginPath::PluginPath(const Path& fullPath) : Path(fullPath.Fullpath()) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(const std::string& fullPath) : Path(fullPath) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(const DPL::String& fullPath) : Path(fullPath) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(){} + +PluginPath PluginPath::getMetaFile() const +{ + PluginPath metaFile = *this; + return metaFile /= WrtDB::GlobalConfig::GetPluginMetafileName(); +} \ No newline at end of file diff --git a/src_mobile/misc/plugin_path.h b/src_mobile/misc/plugin_path.h new file mode 100644 index 0000000..8ada790 --- /dev/null +++ b/src_mobile/misc/plugin_path.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_path_builder.cpp + * @author Kamil Nować (k.nowac@partner.samgsung.com) + * @version + * @brief + */ + +#ifndef PLUGIN_PATH_H +#define PLUGIN_PATH_H + +#include +#include +#include + +class PluginPath: public DPL::Utils::Path{ +private: + std::string m_library; + +public: + PluginPath(const DPL::Utils::Path& fullPath); + PluginPath(const std::string& fullPath); + PluginPath(const DPL::String& fullPath); + PluginPath(); + + //getMetafile() this function adds metafile to current path. + PluginPath getMetaFile() const; + + //setLibraryCombinedName This function creates name for library by adding + //prefix and suffix to PluginPath object filename. + void setLibraryCombinedName(const std::string& prefix, const std::string& sufix) + { + this->m_library = prefix + this->Filename() + sufix; + } + + //getLibraryName returns library name + const std::string& getLibraryName() const + { + return m_library; + } + //getLibraryPath returns full path to the library + const PluginPath getLibraryPath() const + { + return this->operator /(m_library); + } +}; + +#endif // PLUGIN_PATH_H diff --git a/src_mobile/misc/wac_widget_id.cpp b/src_mobile/misc/wac_widget_id.cpp new file mode 100644 index 0000000..b24679a --- /dev/null +++ b/src_mobile/misc/wac_widget_id.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include "wac_widget_id.h" + +#include +#include + +#include + +#include +#include + +namespace { +const char *SCHEME_HTTP = "http"; +const char *SCHEME_HTTPS = "https"; +} + +WacWidgetId::WacWidgetId(const DPL::OptionalString &widgetId) : + m_schemaMatch(false) +{ + if (!widgetId.IsNull()) { + std::string wid = DPL::ToUTF8String(*widgetId); + parse(wid.c_str()); + } +} + +bool WacWidgetId::matchHost(const DPL::String &second) const +{ + _D("m_schemaMatch is: %d", m_schemaMatch); + if (!m_schemaMatch) { + return false; + } + + _D("Matching DNS identity: %s %ls", m_host.c_str(), second.c_str()); + + return m_host == DPL::ToUTF8String(second); +} + +void WacWidgetId::parse(const char *url) +{ + _D("Widget id to parse: %s", url); + + std::unique_ptr > + iri(iri_parse(url), iri_destroy); + + if (!iri.get()) { + _E("Error in parsing widget id."); + return; // m_schemaMatch == false; + } + + std::string scheme; + + if (iri.get()->scheme) { + scheme = iri.get()->scheme; + } else { + _W("Error. No scheme in widget id."); + return; // m_schemaMatch == false; + } + + // should we support HTTP and HTTPS? wac says nothing + // std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), + // tolower); + + // We only match "http" and "https" schemas + if ((scheme != SCHEME_HTTP) && (scheme != SCHEME_HTTPS)) { + _W("Unknown scheme in widget id. %s", scheme.c_str()); + return; // m_schemaMatch == false; + } else { + m_schemaMatch = true; + } + + if (iri.get()->host) { + m_host = iri.get()->host; + _D("Host has been set to: %s", m_host.c_str()); + } + + // What to do when host is empty? No info in wac documentation. + + // Any post processing algorithm? No info in wac documentation. +} diff --git a/src_mobile/misc/wac_widget_id.h b/src_mobile/misc/wac_widget_id.h new file mode 100644 index 0000000..dba5f36 --- /dev/null +++ b/src_mobile/misc/wac_widget_id.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H +#define WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H + +#include +#include + +class WacWidgetId +{ + public: + explicit WacWidgetId(const DPL::OptionalString &widgetId); + bool matchHost(const DPL::String &second) const; + + private: + void parse(const char *url); + + bool m_schemaMatch; + std::string m_host; +}; + +#endif // WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H + diff --git a/src_mobile/misc/widget_install_to_external.cpp b/src_mobile/misc/widget_install_to_external.cpp new file mode 100644 index 0000000..b0219dd --- /dev/null +++ b/src_mobile/misc/widget_install_to_external.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_install_to_external.cpp + * @author Soyoung Kim (sy037.kim@smasung.com) + */ +#include "widget_install_to_external.h" + +#include +#include +#include + +IMPLEMENT_SAFE_SINGLETON(WidgetInstallToExt) + +WidgetInstallToExt::WidgetInstallToExt() : + m_handle(NULL), + m_appId("") +{} + +WidgetInstallToExt::~WidgetInstallToExt() +{} + +void WidgetInstallToExt::initialize(std::string appId) +{ + _D("WidgetInstallToExt::initialize()"); + m_appId = appId; + + if (NULL == m_handle) { + m_handle = app2ext_init(APP2EXT_SD_CARD); + if (NULL == m_handle) { + ThrowMsg(Exception::ErrorInstallToExt, "initialize failed"); + } + } +} + +void WidgetInstallToExt::deinitialize() +{ + _D("WidgetInstallToExt::deinitialize()"); + if (NULL != m_handle) { + if (0 < app2ext_deinit(m_handle)) { + ThrowMsg(Exception::ErrorInstallToExt, + "app2ext deinitialize \ + failed"); + } + } +} + +void WidgetInstallToExt::preInstallation(GList *dirList, int dSize) +{ + _D("WidgetInstallToExt::preInstallation()"); + Assert(m_handle); + + int ret = m_handle->interface.pre_install(m_appId.c_str(), dirList, dSize); + + if (APP2EXT_SUCCESS == ret) { + _D("App2Ext pre install success"); + } else { + postInstallation(false); + ThrowMsg(Exception::ErrorInstallToExt, "pre-install failed"); + } +} + +void WidgetInstallToExt::postInstallation(bool status) +{ + _D("WidgetInstallToExt::postInstallation()"); + + if (NULL != m_handle) { + if (status) { + m_handle->interface.post_install(m_appId.c_str(), + APP2EXT_STATUS_SUCCESS); + } else { + m_handle->interface.post_install(m_appId.c_str(), + APP2EXT_STATUS_FAILED); + } + } +} + +void WidgetInstallToExt::preUpgrade(GList *dirList, int dSize) +{ + _D("WidgetInstallToExt::preUpgrade()"); + Assert(m_handle); + + int ret = m_handle->interface.pre_upgrade(m_appId.c_str(), dirList, dSize); + if (APP2EXT_SUCCESS == ret) { + _D("App2Ext pre-upgrade success"); + } else { + postUpgrade(false); + ThrowMsg(Exception::ErrorInstallToExt, "pre-upgrade failed"); + } +} + +void WidgetInstallToExt::postUpgrade(bool status) +{ + _D("WidgetInstallToExt::postUpgrade()"); + if (NULL != m_handle) { + if (status) { + m_handle->interface.post_upgrade(m_appId.c_str(), + APP2EXT_STATUS_SUCCESS); + } else { + m_handle->interface.post_upgrade(m_appId.c_str(), + APP2EXT_STATUS_FAILED); + } + } +} + +void WidgetInstallToExt::uninstallation() +{ + _D("WidgetInstallToExt::uninstallation()"); + + Assert(m_handle); + + int ret = m_handle->interface.pre_uninstall(m_appId.c_str()); + if (APP2EXT_SUCCESS == ret) { + if (APP2EXT_SUCCESS == + m_handle->interface.post_uninstall(m_appId.c_str())) + { + _D("App2Ext pre-uninstall success"); + } else { + ThrowMsg(Exception::ErrorInstallToExt, "post-uninstall failed"); + } + } else { + ThrowMsg(Exception::ErrorInstallToExt, "pre-uninstall failed"); + } +} + +void WidgetInstallToExt::disable() +{ + _D("WidgetInstallToExt::disable()"); + if (NULL != m_handle) { + int ret = m_handle->interface.disable(m_appId.c_str()); + if (APP2EXT_SUCCESS != ret && APP2EXT_ERROR_UNMOUNT != ret) { + ThrowMsg(Exception::ErrorInstallToExt, "disable failed"); + } + } +} diff --git a/src_mobile/misc/widget_install_to_external.h b/src_mobile/misc/widget_install_to_external.h new file mode 100644 index 0000000..cc9c4df --- /dev/null +++ b/src_mobile/misc/widget_install_to_external.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_install_to_external.h + * @author Soyoung Kim (sy037.kim@smasung.com) + */ +#ifndef WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H +#define WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H + +#include +#include +#include +#include + +class WidgetInstallToExt +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ErrorInstallToExt) + }; + + void initialize(std::string appId); + void deinitialize(); + void preInstallation(GList* dirList, int dSize); + void postInstallation(bool status); + void preUpgrade(GList* dirList, int dSize); + void postUpgrade(bool status); + void uninstallation(); + void disable(); + + private: + app2ext_handle *m_handle; + std::string m_appId; + + WidgetInstallToExt(); + ~WidgetInstallToExt(); + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton WidgetInstallToExtSingleton; + +#endif // WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H diff --git a/src_mobile/misc/widget_location.cpp b/src_mobile/misc/widget_location.cpp new file mode 100644 index 0000000..182df80 --- /dev/null +++ b/src_mobile/misc/widget_location.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_location.cpp + * @author Iwanek Tomasz (t.iwanek@smasung.com) + */ +#include "widget_location.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +WidgetLocation::DirectoryDeletor::DirectoryDeletor(bool isReadOnly) : + m_dirpath(Jobs::WidgetInstall::createTempPath(isReadOnly)) +{} + +WidgetLocation::DirectoryDeletor::DirectoryDeletor(std::string tempPath) : + m_dirpath(tempPath) +{} + +WidgetLocation::DirectoryDeletor::~DirectoryDeletor() +{ + _D("Removing widget installation temporary directory: %s", m_dirpath.c_str()); + if (!WrtUtilRemove(m_dirpath)) { + _W("Fail at removing directory: %s", m_dirpath.c_str()); + } +} + +std::string WidgetLocation::DirectoryDeletor::getTempPath() const +{ + return m_dirpath; +} + +WidgetLocation::WidgetLocation() +{} + +WidgetLocation::WidgetLocation(const std::string & widgetname) : + m_pkgid(widgetname) +{} + +WidgetLocation::~WidgetLocation() +{} + +WidgetLocation::WidgetLocation(const std::string & widgetname, + std::string sourcePath, + WrtDB::PackagingType t, + bool isReadonly, + InstallMode::ExtensionType eType) : + m_pkgid(widgetname), + m_widgetSource(sourcePath), + m_type(t), + m_temp( + new WidgetLocation::DirectoryDeletor(isReadonly)), + m_extensionType(eType) +{ + if (isReadonly) { + m_installedPath += WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + m_installedPath += WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + if (access(m_widgetSource.c_str(), F_OK) != 0) { + m_widgetSource = m_installedPath + "/" + m_pkgid; + } +} + +WidgetLocation::WidgetLocation(const std::string & widgetname, + std::string sourcePath, + std::string dirPath, + WrtDB::PackagingType t, + bool isReadonly, + InstallMode::ExtensionType eType) : + m_pkgid(widgetname), + m_widgetSource(sourcePath), + m_type(t), + m_temp(new WidgetLocation::DirectoryDeletor(dirPath)), + m_extensionType(eType) +{ + if (isReadonly) { + m_installedPath += WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + m_installedPath += WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + if (access(m_widgetSource.c_str(), F_OK) != 0) { + m_widgetSource = m_installedPath + "/" + m_pkgid; + } +} + +// TODO cache all these paths +std::string WidgetLocation::getInstallationDir() const +{ + return m_installedPath; +} + +std::string WidgetLocation::getPackageInstallationDir() const +{ + return m_installedPath + "/" + m_pkgid; +} + +std::string WidgetLocation::getSourceDir() const +{ + return m_installedPath + "/" + + m_pkgid + WrtDB::GlobalConfig::GetWidgetSrcPath(); +} + +std::string WidgetLocation::getBinaryDir() const +{ + return m_installedPath + "/" + + m_pkgid + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getUserBinaryDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getExecFile() const +{ + return getBinaryDir() + "/" + m_appid; +} + +std::string WidgetLocation::getBackupDir() const +{ + return getPackageInstallationDir() + ".backup"; +} + +std::string WidgetLocation::getBackupSourceDir() const +{ + return getBackupDir() + WrtDB::GlobalConfig::GetWidgetSrcPath(); +} + +std::string WidgetLocation::getBackupBinaryDir() const +{ + return getBackupDir() + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getBackupExecFile() const +{ + return getBackupBinaryDir() + "/" + m_appid; +} + +std::string WidgetLocation::getBackupPrivateDir() const +{ + return getBackupDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); +} + +std::string WidgetLocation::getUserDataRootDir() const +{ + return std::string(WrtDB::GlobalConfig::GetWidgetUserDataPath()) + + "/" + m_pkgid; +} + +std::string WidgetLocation::getPrivateStorageDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); +} + +std::string WidgetLocation::getPrivateTempStorageDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateTempStoragePath(); +} + + +std::string WidgetLocation::getTemporaryPackageDir() const +{ + return m_temp->getTempPath(); +} + +std::string WidgetLocation::getTemporaryRootDir() const +{ + if (m_extensionType == InstallMode::ExtensionType::DIR) { + return getWidgetSource() + WrtDB::GlobalConfig::GetWidgetSrcPath(); + } + return getSourceDir(); +} + +DPL::String WidgetLocation::getPkgId() const +{ + return DPL::FromUTF8String(m_pkgid); +} + +std::string WidgetLocation::getInstalledIconPath() const +{ + return m_iconPath; +} + +std::string WidgetLocation::getWidgetSource() const +{ + return m_widgetSource; +} + +void WidgetLocation::setIconTargetFilenameForLocale(const std::string & icon) +{ + m_iconPath = icon; +} + +void WidgetLocation::registerExternalLocation(const std::string & file) +{ + m_externals.push_back(file); +} + +WrtDB::ExternalLocationList WidgetLocation::listExternalLocations() const +{ + return m_externals; +} + +void WidgetLocation::registerAppid(const std::string & appid) +{ + m_appid = appid; +} + +std::string WidgetLocation::getSharedRootDir() const +{ + /* TODO : add wrt-commons*/ + return getUserDataRootDir() + "/shared"; +} + +std::string WidgetLocation::getSharedResourceDir() const +{ + return getSharedRootDir() + "/res"; +} + +std::string WidgetLocation::getSharedDataDir() const +{ + return getSharedRootDir() + "/data"; +} + +std::string WidgetLocation::getSharedTrustedDir() const +{ + return getSharedRootDir() + "/trusted"; +} + +std::string WidgetLocation::getBackupSharedDir() const +{ + return getBackupDir() + "/shared"; +} + +std::string WidgetLocation::getBackupSharedDataDir() const +{ + return getBackupSharedDir() + "/data"; +} + +std::string WidgetLocation::getBackupSharedTrustedDir() const +{ + return getBackupSharedDir() + "/trusted"; +} + +std::string WidgetLocation::getNPPluginsExecFile() const +{ + return getBinaryDir() + "/" + m_appid + ".npruntime"; +} + +std::string WidgetLocation::getNPPluginsDir() const +{ + return getSourceDir() + "/plugins"; +} diff --git a/src_mobile/misc/widget_location.h b/src_mobile/misc/widget_location.h new file mode 100644 index 0000000..7208170 --- /dev/null +++ b/src_mobile/misc/widget_location.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_location.h + * @author Iwanek Tomasz (t.iwanek@smasung.com) + */ +#ifndef WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H +#define WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H + +#include +#include + +#include +#include +#include +#include + +/** + * @brief The WidgetLocation class + * + * Object that stores locations of several files/directories according + * to package name + * + * Current package layout (of installed package) is following: + * + * /opt/apps/[package_name] + * \_____________ /data + * \_____________ /share + * \_____________ /bin + * \_____________ /bin/[id_of_installed_package] + * \_____________ /res/wgt/ + * \___ config.xml + * \___ [widgets_archive_content] + * + * 1) Normal Widget + * Developer provides content of res/wgt directory (package contains that + * directory as root). + * + * 2) For OSP Service Hybrid App is actually a bit different: + * Root is OSP Service directory, WebApp content is located in [root]/res/wgt + * + * Temporary directory is directory when widget is placed at the begining + * of installation process. After parsing process of config.xml, destination + * directory is created. + */ +class WidgetLocation +{ + class DirectoryDeletor + { + public: + DirectoryDeletor(); + DirectoryDeletor(std::string tempPath); + DirectoryDeletor(bool isPreload); + + ~DirectoryDeletor(); + std::string getTempPath() const; + + private: + std::string m_dirpath; + }; + + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, NoTemporaryPath) + /** + * @brief WidgetLocation + * + * Creates useless object. Needed by DPL::Optional + */ + WidgetLocation(); + /** + * @brief WidgetLocation Builds paths for widget location during + * uninstallation + * + * Uninstallation process needs only installed package directory. + * + * @param widgetname name of widget + */ + explicit WidgetLocation(const std::string & widgetname); + /** + * @brief WidgetLocation Builds paths for widget location during + * installation + * + * @param widgetname name of widget + * @param sourcePath given source path + * @param t declaraced type of widget if type is needed + * + * In destruction removes temporary directory + */ + WidgetLocation(const std::string & widgetname, std::string sourcePath, + WrtDB::PackagingType t = WrtDB::PKG_TYPE_NOMAL_WEB_APP, + bool isReadonly = false, + InstallMode::ExtensionType eType = + InstallMode::ExtensionType::WGT); + + WidgetLocation(const std::string & widgetname, std::string sourcePath, + std::string dirPath, + WrtDB::PackagingType t = WrtDB::PKG_TYPE_NOMAL_WEB_APP, + bool isReadonly = false, + InstallMode::ExtensionType eType = + InstallMode::ExtensionType::WGT); + + ~WidgetLocation(); + + // Installed paths + std::string getInstallationDir() const; // /opt/apps or /usr/apps + std::string getPackageInstallationDir() const; // /opt/apps/[package] + std::string getSourceDir() const; // /opt/apps/[package]/res/wgt + std::string getBinaryDir() const; // /opt/apps/[package]/bin or /usr/apps/[package]/bin + std::string getUserBinaryDir() const; // /opt/apps/[package]/bin + std::string getExecFile() const; // /opt/apps/[package]/bin/[package] + std::string getBackupDir() const; // /opt/apps/[package].backup + std::string getBackupSourceDir() const; // /opt/apps/[pkg].backup/res/wgt + std::string getBackupBinaryDir() const; // /opt/apps/[pkg].backup/bin + std::string getBackupExecFile() const; // /opt/apps/[pkg].backup/bin/[pkg] + std::string getBackupPrivateDir() const; // /opt/apps/[pkg].backup/data + std::string getUserDataRootDir() const; // /opt/usr/apps/[package] + std::string getPrivateStorageDir() const; // /opt/usr/apps/[package]/data + std::string getPrivateTempStorageDir() const; // /opt/usr/apps/[package]/tmp + std::string getSharedRootDir() const; // /opt/usr/apps/[package]/shared + std::string getSharedResourceDir() const; // /opt/usr/apps/[package]/shared/res + std::string getSharedDataDir() const; // /opt/usr/apps/[package]/shared/data + std::string getSharedTrustedDir() const; // /opt/usr/apps/[package]/shared/trusted + std::string getBackupSharedDir() const; // /opt/usr/apps/[package].backup/shared + std::string getBackupSharedDataDir() const; // /opt/usr/apps/[package].backup/shared/data + std::string getBackupSharedTrustedDir() const; // /opt/usr/apps/[package].backup/shared/trusted + std::string getNPPluginsDir() const; // /opt/usr/apps/[package]/res/wgt/plugins + std::string getNPPluginsExecFile() const; // /opt/usr/apps/[package]/bin/{execfile} + + // Temporary paths + /** + * @brief getTemporaryRootDir + * @return value of root for developer's provide package (root of unpacked + * .wgt file) + */ + std::string getTemporaryPackageDir() const; + /** + * @brief getTemporaryRootDir + * + * Value of this will differs according to type of installed widget. + * + * @return value of root for content in temporary directory to be copied + * into 'res/wgt' + */ + std::string getTemporaryRootDir() const; + + //icons + /** + * @brief setIconTargetFilenameForLocale set installed ion path according to + * locale + * @param icon path of application icon + */ + void setIconTargetFilenameForLocale(const std::string &icon); + + /** + * @brief getIconTargetFilename gets icon full path + * @param languageTag language tag + * @return value of full path + */ + std::string getInstalledIconPath() const; + + /** + * @brief getWidgetSourcePath return widget's source path given to installer + * @return value of source path + */ + std::string getWidgetSource() const; + /** + * @brief pkgid Returns pkgid + * @return pkgid + */ + DPL::String getPkgId() const; + + //external files + /** + * @brief registerExternalFile Registers file for database registration + * @param file + * + * Registered file will be stored in database and removed automatically a + * + * @return + */ + void registerExternalLocation(const std::string & file); + /** + * @brief listExternalFile list all file to be registered + */ + WrtDB::ExternalLocationList listExternalLocations() const; + + /* + * @brief set appid + */ + void registerAppid(const std::string & appid); + + private: + std::string m_pkgid; //id of package + std::string m_widgetSource; // Source widget zip + // file/widget url + std::string m_appid; //id of app + std::string m_iconPath; //installed icon path + WrtDB::PackagingType m_type; + std::shared_ptr m_temp; //directory + WrtDB::ExternalLocationList m_externals; + std::string m_installedPath; + InstallMode::ExtensionType m_extensionType; +}; + +#endif // WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H diff --git a/src_mobile/pkg-manager/CMakeLists.txt b/src_mobile/pkg-manager/CMakeLists.txt new file mode 100755 index 0000000..74fd837 --- /dev/null +++ b/src_mobile/pkg-manager/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +SET(BACKLIB_SRCS + backendlib.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/configuration_parser/widget_parser.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/configuration_parser/parser_runner.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/configuration_parser/ignoring_parser.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/configuration_parser/deny_all_parser.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/configuration_parser/libiriwrapper.cpp + ${PROJECT_SOURCE_DIR}/src_mobile/wrt-installer/language_subtag_rst_tree.cpp +) + +PKG_CHECK_MODULES(WRT_BACKLIB_PKGS + dpl-efl + dpl-wrt-dao-ro + dpl-wrt-dao-rw + dpl-utils-efl + pkgmgr-installer + pkgmgr-types + pkgmgr + dlog + libpcrecpp + wrt-commons-i18n-dao-ro + REQUIRED) + +INCLUDE_DIRECTORIES( + ${WRT_BACKLIB_PKGS_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/configuration_parser +) + +ADD_LIBRARY(${TARGET_BACKEND_LIB} SHARED + ${BACKLIB_SRCS} +) + +TARGET_LINK_LIBRARIES(${TARGET_BACKEND_LIB} + ${WRT_BACKLIB_PKGS_LIBRARIES} +) + +SET_TARGET_PROPERTIES(${TARGET_BACKEND_LIB} PROPERTIES + LINK_FLAGS "-Wl,--as-needed -Wl,--hash-style=both" +) + +INSTALL(TARGETS ${TARGET_BACKEND_LIB} + DESTINATION etc/package-manager/backendlib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +#SYMLINK +set(SYMLINK_USE OFF) +set(SYMLINK_DEST "${CMAKE_INSTALL_PREFIX}/etc/package-manager") + +IF(SYMLINK_USE) + ADD_CUSTOM_COMMAND(OUTPUT ${SYMLINK_DEST}/backend/wgt + COMMAND mkdir + ARGS -p ${SYMLINK_DEST}/backend + COMMAND ln + ARGS -sf ${CMAKE_INSTALL_PREFIX}/bin/${BACKEND} ${SYMLINK_DEST}/backend/wgt + DEPENDS ${BACKEND} + ) + ADD_CUSTOM_TARGET(test_symlinks ALL + DEPENDS ${SYMLINK_DEST}/backend/wgt + ) +ENDIF(SYMLINK_USE) diff --git a/src_mobile/pkg-manager/DESCRIPTION b/src_mobile/pkg-manager/DESCRIPTION new file mode 100644 index 0000000..a9d3696 --- /dev/null +++ b/src_mobile/pkg-manager/DESCRIPTION @@ -0,0 +1 @@ +Executables for interfacing with the package manager diff --git a/src_mobile/pkg-manager/backendlib.cpp b/src_mobile/pkg-manager/backendlib.cpp new file mode 100644 index 0000000..8367714 --- /dev/null +++ b/src_mobile/pkg-manager/backendlib.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file backendlib.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 0.1 + * @brief This is implementation file for providing widget information + * to package manager + */ +#include "package-manager-plugin.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" +#include + +using namespace WrtDB; + +#undef TRUE +#undef FALSE +#define TRUE 0 +#define FALSE -1 +#define GET_DIRECTORY_SIZE_KB(x) (x) / 1024 + +#ifdef __cplusplus +extern "C" +{ +#endif + +static void pkg_native_plugin_on_unload(); +static int pkg_plugin_app_is_installed(const char *pkg_name); +static int pkg_plugin_get_installed_apps_list(const char *category, + const char *option, + package_manager_pkg_info_t **list, + int *count); +static int pkg_plugin_get_app_detail_info( + const char *pkg_name, + package_manager_pkg_detail_info_t * + pkg_detail_info); +static int pkg_plugin_get_app_detail_info_from_package( + const char *pkg_path, + package_manager_pkg_detail_info_t + *pkg_detail_info); + +pkgmgr_info *pkgmgr_client_check_pkginfo_from_file(const char *pkg_path); + +static void pkg_native_plugin_on_unload() +{ + _D("pkg_native_plugin_unload() is called"); +} + +static int pkg_plugin_app_is_installed(const char *pkg_name) +{ + const char* REG_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; + _D("pkg_plugin_app_is_installed() is called"); + + WrtDB::WrtDatabase::attachToThreadRO(); + + regex_t reg; + if (regcomp(®, REG_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + } + + WrtDB::TizenAppId appid; + + if (!(regexec(®, pkg_name, + static_cast(0), NULL, 0) == 0)) + { + _E("Invalid argument : %s", pkg_name); + return FALSE; + } + + Try { + WrtDB::TizenPkgId pkgid(DPL::FromUTF8String(pkg_name)); + appid = WidgetDAOReadOnly::getTzAppId(pkgid); + _D("appid : %ls", appid.c_str()); + } Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + WrtDB::WrtDatabase::detachFromThread(); + return FALSE; + } + WrtDB::WrtDatabase::detachFromThread(); + return TRUE; +} + +static int pkg_plugin_get_installed_apps_list(const char * /*category*/, + const char * /*option*/, + package_manager_pkg_info_t **list, + int *count) +{ + _D("pkg_plugin_get_installed_apps_list() is called"); + + package_manager_pkg_info_t *pkg_list = NULL; + package_manager_pkg_info_t *pkg_last = NULL; + + WrtDB::WrtDatabase::attachToThreadRO(); + TizenAppIdList tizenAppidList = WidgetDAOReadOnly::getTizenAppidList(); + *count = 0; + + FOREACH(iterator, tizenAppidList) { + package_manager_pkg_info_t *pkg_info = + static_cast + (malloc(sizeof(package_manager_pkg_info_t))); + + if (NULL == pkg_list) { + pkg_list = pkg_info; + pkg_last = pkg_info; + } else { + pkg_last->next = pkg_info; + } + + TizenAppId tzAppid = *iterator; + WidgetDAOReadOnly widget(tzAppid); + strncpy(pkg_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + snprintf(pkg_info->pkg_name, PKG_NAME_STRING_LEN_MAX, "%s", + DPL::ToUTF8String(tzAppid).c_str()); + + DPL::Optional version = widget.getVersion(); + if (!version.IsNull()) { + strncpy(pkg_info->version, + DPL::ToUTF8String(*version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + (*count)++; + pkg_last = pkg_info; + } + *list = pkg_list; + WrtDB::WrtDatabase::detachFromThread(); + + return TRUE; +} + +static int pkg_plugin_get_app_detail_info( + const char *pkg_name, + package_manager_pkg_detail_info_t * + pkg_detail_info) +{ + _D("pkg_plugin_get_app_detail_info() is called"); + + WrtDB::WrtDatabase::attachToThreadRO(); + + WrtDB::TizenAppId appid; + Try { + WrtDB::TizenPkgId pkgid(DPL::FromUTF8String(pkg_name)); + appid = WidgetDAOReadOnly::getTzAppId(pkgid); + _D("appid : %ls", appid.c_str()); + } Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + WrtDB::WrtDatabase::detachFromThread(); + return FALSE; + } + + WidgetDAOReadOnly widget(appid); + + DPL::Optional version = widget.getVersion(); + DPL::Optional id = widget.getGUID(); + DPL::Optional locale = widget.getDefaultlocale(); + + if (!version.IsNull()) { + strncpy(pkg_detail_info->version, + DPL::ToUTF8String(*version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + snprintf(pkg_detail_info->pkgid, PKG_NAME_STRING_LEN_MAX, "%s", + pkg_name); + snprintf(pkg_detail_info->optional_id, PKG_NAME_STRING_LEN_MAX, "%s", + DPL::ToUTF8String(appid).c_str()); + WidgetLocalizedInfo localizedInfo; + + if (locale.IsNull()) { + _D("locale is NULL"); + DPL::String languageTag(L""); + localizedInfo = widget.getLocalizedInfo(languageTag); + } else { + localizedInfo = widget.getLocalizedInfo(*locale); + } + DPL::Optional desc(localizedInfo.description); + + if (!desc.IsNull()) { + strncpy(pkg_detail_info->pkg_description, + DPL::ToUTF8String(*desc).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + strncpy(pkg_detail_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + strncpy(pkg_detail_info->pkg_name, pkg_name, PKG_NAME_STRING_LEN_MAX - 1); + + std::string min_version = DPL::ToUTF8String((*widget.getMinimumWacVersion())); + + strncpy(pkg_detail_info->min_platform_version, min_version.c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + + /* set installed time */ + pkg_detail_info->installed_time = widget.getInstallTime(); + + /* set Widget size */ + DPL::String pkgName = DPL::FromUTF8String(pkg_name); + std::string installPath = WidgetConfig::GetWidgetBasePath(pkgName); + std::string persistentPath = + WidgetConfig::GetWidgetPersistentStoragePath(pkgName); + std::string tempPath = + WidgetConfig::GetWidgetTemporaryStoragePath(pkgName); + installPath += "/"; + tempPath += "/"; + persistentPath += "/"; + + size_t installedSize = Utils::getFolderSize(installPath); + size_t persistentSize = Utils::getFolderSize(persistentPath); + size_t appSize = installedSize - persistentSize; + size_t dataSize = persistentSize + Utils::getFolderSize(tempPath); + + pkg_detail_info->installed_size = GET_DIRECTORY_SIZE_KB(installedSize); + pkg_detail_info->app_size = GET_DIRECTORY_SIZE_KB(appSize); + pkg_detail_info->data_size = GET_DIRECTORY_SIZE_KB(dataSize); + + WrtDB::WrtDatabase::detachFromThread(); + return TRUE; +} + +int getConfigParserData(const std::string &widgetPath, ConfigParserData& configInfo) +{ + const char* CONFIG_XML = "config.xml"; + const char* WITH_OSP_XML = "res/wgt/config.xml"; + + Try { + ParserRunner parser; + + std::unique_ptr zipFile( + new DPL::ZipInput(widgetPath)); + + std::unique_ptr configFile; + + // Open config.xml file + Try { + configFile.reset(zipFile->OpenFile(CONFIG_XML)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + configFile.reset(zipFile->OpenFile(WITH_OSP_XML)); + } + + // Extract config + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(configFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + parser.Parse(&buffer, + ElementParserPtr( + new RootParser(configInfo, + DPL:: + FromUTF32String( + L"widget")))); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _E("Failed to open widget package"); + return FALSE; + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _E("Failed to open config.xml file"); + return FALSE; + } + Catch(DPL::CopyFailed) + { + _E("Failed to extract config.xml file"); + return FALSE; + } + Catch(DPL::FileInput::Exception::OpenFailed) + { + _E("Failed to open config.xml file"); + return FALSE; + } + Catch(ElementParser::Exception::ParseError) + { + _E("Failed to parse config.xml file"); + return FALSE; + } + Catch(DPL::ZipInput::Exception::SeekFileFailed) + { + _E("Failed to seek widget archive - corrupted package?"); + return FALSE; + } + + return TRUE; +} + +char* getIconInfo(const std::string &widgetPath, + const std::string &icon_name, int &icon_size) +{ + Try { + std::unique_ptr zipFile( + new DPL::ZipInput(widgetPath)); + + std::unique_ptr iconFile; + + Try { + iconFile.reset(zipFile->OpenFile(icon_name)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _D("This web app is hybrid web app"); + std::string hybrid_icon = "res/wgt/" + icon_name; + iconFile.reset(zipFile->OpenFile(hybrid_icon)); + } + + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(iconFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + icon_size = buffer.Size(); + char *getBuffer = (char*) calloc(1, (sizeof(char) * icon_size) + 1); + buffer.Flatten(getBuffer, buffer.Size()); + return getBuffer; + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _D("Failed to open widget package"); + return NULL; + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _D("Not found icon file %s", icon_name.c_str()); + return NULL; + } +} + +char* getIconForLocale(const std::string& bp, const std::string& tag, + const std::string& icon, int & size) +{ + std::string iconPath; + if (!tag.empty()) { + iconPath += std::string("locales/") + tag; + } + if (!iconPath.empty()) { + iconPath += "/"; + } + + iconPath += icon; + return getIconInfo(bp, iconPath, size); +} + +char* getIcon(const std::string & basepath, const WrtDB::ConfigParserData & config, int & size) +{ + const std::list defaultIcons{ "icon.svg", "icon.ico", "icon.png", "icon.gif", "icon.jpg" }; + LanguageTags tagsList = + LanguageTagsProviderSingleton::Instance().getLanguageTags(); + + char * result = NULL; + + //for each locale tag - searching for icon presence and returning raw data + //first found is best as locale tags are ordered + FOREACH(tag, tagsList) + { + FOREACH(icon, config.iconsList) + { + std::string src = DPL::ToUTF8String(icon->src); + result = getIconForLocale(basepath, DPL::ToUTF8String(*tag), src, size); + if(result) { + return result; + } + } + FOREACH(i, defaultIcons) + { + result = getIconForLocale(basepath, DPL::ToUTF8String(*tag), *i, size); + if(result) { + return result; + } + } + } + return NULL; +} + +int getWidgetDetailInfoFromPackage(const char* pkgPath, + package_manager_pkg_detail_info_t* pkg_detail_info) +{ + const std::string widget_path(pkgPath); + ConfigParserData configInfo; + + if (FALSE == getConfigParserData(widget_path, configInfo)) { + return FALSE; + } + + strncpy(pkg_detail_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + if (!configInfo.tizenPkgId.IsNull()) { + strncpy(pkg_detail_info->pkgid, + DPL::ToUTF8String(*configInfo.tizenPkgId).c_str(), PKG_TYPE_STRING_LEN_MAX - 1); + } + if (!configInfo.tizenAppId.IsNull()) { + strncpy(pkg_detail_info->pkg_name, + DPL::ToUTF8String(*configInfo.tizenAppId).c_str(), + PKG_NAME_STRING_LEN_MAX - 1); + } + if (!configInfo.version.IsNull()) { + strncpy(pkg_detail_info->version, + DPL::ToUTF8String(*configInfo.version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + DPL::Optional name; + DPL::Optional desc; + + LanguageTags tags = LanguageTagsProviderSingleton::Instance().getLanguageTags(); + + auto toLowerCase = [](const DPL::String & r) + { + DPL::String result; + std::transform(r.begin(), r.end(), std::inserter(result, result.begin()), ::tolower); + return result; + }; + + if (!!configInfo.defaultlocale) + { + Locale & dl = *configInfo.defaultlocale; + configInfo.defaultlocale = toLowerCase(dl); + } + + bool found = false; + FOREACH(tag, tags) + { + *tag = toLowerCase(*tag); + FOREACH(localizedData, configInfo.localizedDataSet) + { + Locale i = localizedData->first; + i = toLowerCase(i); + + if (!!configInfo.defaultlocale && *configInfo.defaultlocale == i) + { + name = localizedData->second.name; + desc = localizedData->second.description; + } + if (*tag == i) + { + name = localizedData->second.name; + desc = localizedData->second.description; + found = true; + break; + } + } + if(found) break; + } + + if( !name.IsNull()) { + strncpy(pkg_detail_info->label, DPL::ToUTF8String(*name).c_str(), + PKG_LABEL_STRING_LEN_MAX - 1); + } + + if (!desc.IsNull()) { + strncpy(pkg_detail_info->pkg_description, + DPL::ToUTF8String(*desc).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + + if (!configInfo.tizenMinVersionRequired.IsNull()) { + strncpy(pkg_detail_info->min_platform_version, + DPL::ToUTF8String(*configInfo.tizenMinVersionRequired).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + if (!configInfo.authorName.IsNull()) { + strncpy(pkg_detail_info->author, + DPL::ToUTF8String(*configInfo.authorName).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + + + pkg_detail_info->privilege_list = NULL; + FOREACH(it, configInfo.featuresList) { + std::string featureInfo = DPL::ToUTF8String(it->name); + _D("privilege : %s", featureInfo.c_str()); + int length = featureInfo.size(); + char *privilege = (char*) calloc(1, (sizeof(char) * (length + 1))); + snprintf(privilege, length + 1, "%s", featureInfo.c_str()); + pkg_detail_info->privilege_list = + g_list_append(pkg_detail_info->privilege_list, privilege); + } + + char* icon_buf = getIcon(widget_path, configInfo, pkg_detail_info->icon_size); + + if (icon_buf) { + _D("icon size : %d", pkg_detail_info->icon_size); + pkg_detail_info->icon_buf = icon_buf; + } else { + _D("No icon"); + pkg_detail_info->icon_size = 0; + pkg_detail_info->icon_buf = NULL; + } + + return TRUE; +} + +static int pkg_plugin_get_app_detail_info_from_package( + const char * pkg_path, + package_manager_pkg_detail_info_t * pkg_detail_info) +{ + _D("pkg_plugin_get_app_detail_info_from_package() is called"); + return getWidgetDetailInfoFromPackage(pkg_path, pkg_detail_info); +} + +pkgmgr_info *pkgmgr_client_check_pkginfo_from_file(const char *pkg_path) +{ + _D("pkgmgr_client_check_pkginfo_from_file() is called"); + package_manager_pkg_detail_info_t *pkg_detail_info; + pkg_detail_info = (package_manager_pkg_detail_info_t*)malloc( + sizeof(package_manager_pkg_detail_info_t)); + int ret = getWidgetDetailInfoFromPackage(pkg_path, pkg_detail_info); + if (FALSE == ret) { + _E("Failed to get package detail info "); + return NULL; + } + return reinterpret_cast(pkg_detail_info); +} + +__attribute__ ((visibility("default"))) +int pkg_plugin_on_load(pkg_plugin_set *set) +{ + DPL::Log::LogSystemSingleton::Instance().SetTag("WGT-BACKLIB"); + if (NULL == set) { + return FALSE; + } + memset(set, 0x00, sizeof(pkg_plugin_set)); + + set->plugin_on_unload = pkg_native_plugin_on_unload; + set->pkg_is_installed = pkg_plugin_app_is_installed; + set->get_installed_pkg_list = pkg_plugin_get_installed_apps_list; + set->get_pkg_detail_info = pkg_plugin_get_app_detail_info; + set->get_pkg_detail_info_from_package = + pkg_plugin_get_app_detail_info_from_package; + + return TRUE; +} + +#ifdef __cplusplus +} +#endif diff --git a/src_mobile/pkg-manager/pkgmgr_signal.cpp b/src_mobile/pkg-manager/pkgmgr_signal.cpp new file mode 100644 index 0000000..140e224 --- /dev/null +++ b/src_mobile/pkg-manager/pkgmgr_signal.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Yunchan Cho (yunchan.cho@samsung.com) + * @version 0.1 + * @brief + */ + +#include + +#include +#include +#include + +namespace { +// package type sent in every signal +const char PKGMGR_WEBAPP_TYPE[] = "wgt"; + +// notification about opoeration start +const char PKGMGR_START_KEY[] = "start"; + +// value for new installation +const char PKGMGR_START_INSTALL[] = "install"; + +// value for update installation +const char PKGMGR_START_UPDATE[] = "update"; + +// value for uninstallation +const char PKGMGR_START_UNINSTALL[] = "uninstall"; + +// notification about progress of installation with percentage number +const char PKGMGR_PROGRESS_KEY[] = "install_percent"; + +// notification about icon path for installation frontend +const char PKGMGR_ICON_PATH[] = "icon_path"; + +// notification about error before end with given error code +// (currently, same as backend exit status) +const char PKGMGR_ERROR[] = "error"; + +// notification about end of installation with status +const char PKGMGR_END_KEY[] = "end"; + +// success value of end of installation +const char PKGMGR_END_SUCCESS[] = "ok"; + +// failure value of end of installation +const char PKGMGR_END_FAILURE[] = "fail"; +} + +namespace PackageManager { +PkgmgrSignal::PkgmgrSignal() : + m_initialized(false), + m_handle(NULL), + m_reqType(RequestType::UNSUPPORTED) +{} + +PkgmgrSignal::~PkgmgrSignal() +{} + +bool PkgmgrSignal::initialize(int argc, char* argv[]) +{ + if (m_handle) { + _D("Release already allocated pkgmgr handle"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + } + + m_handle = pkgmgr_installer_new(); + if (!m_handle) { + _E("Fail to get pkgmgr installer handle"); + return false; + } + + // set information from pkgmgr + if (!pkgmgr_installer_receive_request( + m_handle, argc, argv)) + { + auto pkgmgrtype = pkgmgr_installer_get_request_type(m_handle); + switch(pkgmgrtype) + { + case PKGMGR_REQ_INSTALL: + m_reqType = RequestType::INSTALL; + break; + case PKGMGR_REQ_UNINSTALL: + m_reqType = RequestType::UNINSTALL; + break; + case PKGMGR_REQ_REINSTALL: + m_reqType = RequestType::REINSTALL; + break; + default: + m_reqType = RequestType::UNSUPPORTED; + break; + } + + if (m_reqType == RequestType::UNSUPPORTED) + { + _E("Fail to get request type of pkgmgr"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + return false; + } + const char *callerId = pkgmgr_installer_get_caller_pkgid(m_handle); + if(callerId) + m_callerId = callerId; + + } else { + _E("Fail to get information of pkgmgr's request"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + return false; + } + + m_type = PKGMGR_WEBAPP_TYPE; + m_initialized = true; + return true; +} + +bool PkgmgrSignal::deinitialize() +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + pkgmgr_installer_free(m_handle); + m_handle = NULL; + m_initialized = false; + return true; +} + +bool PkgmgrSignal::setPkgname(const std::string& name) +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + if (name.empty()) { + _E("name is empty"); + return false; + } + + m_pkgname = name; + _D("Success to set tizen package name: %s", m_pkgname.c_str()); + + return true; +} + +bool PkgmgrSignal::startJob(Jobs::InstallationType type) +{ + switch(type) + { + case Jobs::InstallationType::NewInstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_INSTALL); + break; + case Jobs::InstallationType::UpdateInstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_UPDATE); + break; + case Jobs::InstallationType::Uninstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_UNINSTALL); + break; + default: + _E("Trying to send unknown installation type to pkgmgr"); + return false; + } + return true; +} + +bool PkgmgrSignal::endJob(Jobs::Exceptions::Type ecode) +{ + if(ecode == Jobs::Exceptions::Type::Success) + { + return sendSignal(PKGMGR_END_KEY, PKGMGR_END_SUCCESS); + } + else + { + sendSignal(PKGMGR_ERROR, DPL::lexical_cast(ecode)); + return sendSignal(PKGMGR_END_KEY, PKGMGR_END_FAILURE); + } +} + +bool PkgmgrSignal::sendProgress(int percent) +{ + return sendSignal(PKGMGR_PROGRESS_KEY, DPL::lexical_cast(percent)); +} + +bool PkgmgrSignal::sendIconPath(const std::string & iconpath) +{ + return sendSignal(PKGMGR_ICON_PATH, iconpath); +} + +bool PkgmgrSignal::sendSignal(const std::string& key, + const std::string& value) const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + if (key.empty() || value.empty()) { + _D("key or value is empty"); + return false; + } + + if (m_handle == NULL || m_type.empty()) { + _E("Some data of PkgmgrSignal is empty"); + return false; + } + + // send pkgmgr signal + if (pkgmgr_installer_send_signal( + m_handle, m_type.c_str(), m_pkgname.c_str(), + key.c_str(), value.c_str())) + { + _E("Fail to send pkgmgr signal"); + return false; + } + + _D("Success to send pkgmgr signal: %s - %s", key.c_str(), value.c_str()); + return true; +} + +std::string PkgmgrSignal::getPkgname() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_pkgname; +} + +PkgmgrSignal::RequestType PkgmgrSignal::getRequestedType() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_reqType; +} + +std::string PkgmgrSignal::getCallerId() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_callerId; +} +} // PackageManager diff --git a/src_mobile/pkg-manager/pkgmgr_signal.h b/src_mobile/pkg-manager/pkgmgr_signal.h new file mode 100644 index 0000000..abd756b --- /dev/null +++ b/src_mobile/pkg-manager/pkgmgr_signal.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Yunchan Cho (yunchan.cho@samsung.com) + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.2 + * @brief + */ + +#ifndef WRT_PKGMGR_SIGNAL_H_ +#define WRT_PKGMGR_SIGNAL_H_ + +#include + +struct pkgmgr_installer; + +namespace PackageManager { + +class PkgmgrSignal : public IPkgmgrSignal +{ +public: + enum class RequestType + { + UNSUPPORTED, + INSTALL, + UNINSTALL, + REINSTALL + }; + + bool initialize(int argc, char* argv[]); + bool deinitialize(); + bool setPkgname(const std::string& name); + std::string getPkgname() const; + RequestType getRequestedType() const; + std::string getCallerId() const; + + bool startJob(Jobs::InstallationType type); + bool endJob(Jobs::Exceptions::Type ecode); + bool sendProgress(int percent); + bool sendIconPath(const std::string & iconpath); + + PkgmgrSignal(); + virtual ~PkgmgrSignal(); + +protected: + bool sendSignal(const std::string& key, const std::string& value) const; + +private: + bool m_initialized; + pkgmgr_installer* m_handle; + std::string m_type; + std::string m_pkgname; + RequestType m_reqType; + std::string m_callerId; +}; +} // PackageManager + +#endif // WRT_PKGMGR_SIGNAL_H_ + diff --git a/src_mobile/pkg-manager/pkgmgr_signal_dummy.h b/src_mobile/pkg-manager/pkgmgr_signal_dummy.h new file mode 100644 index 0000000..42b0aa4 --- /dev/null +++ b/src_mobile/pkg-manager/pkgmgr_signal_dummy.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.1 + * @brief Dummy version of PkgmgrSignal. + */ + +#ifndef WRT_PKGMGR_SIGNAL_DUMMY_H_ +#define WRT_PKGMGR_SIGNAL_DUMMY_H_ + +#include + +#include + +namespace PackageManager { +class PkgmgrSignalDummy : public IPkgmgrSignal +{ + public: + PkgmgrSignalDummy() + {} + + virtual ~PkgmgrSignalDummy() + {} + + bool setPkgname(const std::string& /*name*/) + { + return false; + } + + std::string getPkgname() const + { + return ""; + } + + std::string getCallerId() const + { + return ""; + } + + bool startJob(Jobs::InstallationType type DPL_UNUSED) + { + return false; + } + + bool endJob(Jobs::Exceptions::Type ecode DPL_UNUSED) + { + return false; + } + + bool sendProgress(int percent DPL_UNUSED) + { + return false; + } + + bool sendIconPath(const std::string & iconpath DPL_UNUSED) + { + return false; + } +}; +} // PkgmgrSignalDummy + +#endif // WRT_PKGMGR_SIGNAL_DUMMY_H_ diff --git a/src_mobile/pkg-manager/pkgmgr_signal_interface.h b/src_mobile/pkg-manager/pkgmgr_signal_interface.h new file mode 100644 index 0000000..1e38a17 --- /dev/null +++ b/src_mobile/pkg-manager/pkgmgr_signal_interface.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.1 + * @brief Interface for PkgmgrSignal. + */ + +#ifndef WRT_PKGMGR_SIGNAL_INTERFACE_H_ +#define WRT_PKGMGR_SIGNAL_INTERFACE_H_ + +#include + +#include +#include + +namespace PackageManager { +class IPkgmgrSignal +{ + public: + virtual bool setPkgname(const std::string& name) = 0; + virtual std::string getPkgname() const = 0; + virtual std::string getCallerId() const = 0; + + virtual bool startJob(Jobs::InstallationType type) = 0; + virtual bool endJob(Jobs::Exceptions::Type ecode) = 0; + virtual bool sendProgress(int percent) = 0; + virtual bool sendIconPath(const std::string & iconpath) = 0; + virtual ~IPkgmgrSignal(){} +}; +} // IPkgmgrSignal + +#endif // WRT_PKGMGR_SIGNAL_INTERFACE_H_ diff --git a/src_mobile/wrt-installer/CMakeLists.txt b/src_mobile/wrt-installer/CMakeLists.txt new file mode 100644 index 0000000..e22fdc5 --- /dev/null +++ b/src_mobile/wrt-installer/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Lukasz Wrzosek (l.wrzosek@samsung.com) +# @version 1.0 +# + +SET(WRT_INSTALLER_DIR + ${INSTALLER_SRC_DIR}/wrt-installer + ) + +SET(PKG_MANAGER_DIR + ${INSTALLER_SRC_DIR}/pkg-manager + ) + +SET(WRT_INSTALLER_SOURCES + ${WRT_INSTALLER_DIR}/wrt-installer.cpp + ${WRT_INSTALLER_DIR}/wrt_installer_api.cpp + ${WRT_INSTALLER_DIR}/installer_callbacks_translate.cpp + ${WRT_INSTALLER_DIR}/plugin_utils.cpp + ${WRT_INSTALLER_DIR}/language_subtag_rst_tree.cpp + ${WRT_INSTALLER_DIR}/installer_main_thread.cpp + ${WRT_INSTALLER_DIR}/option_parser.cpp + ${PKG_MANAGER_DIR}/pkgmgr_signal.cpp +) + +PKG_CHECK_MODULES(WRT_INSTALLER_DEPS + pkgmgr-installer + libpcrecpp + pkgmgr-info + pkgmgr + security-install + wrt-commons-i18n-dao-ro + REQUIRED) + +INCLUDE_DIRECTORIES( + ${PKG_MANAGER_DIR} + ${WRT_INSTALLER_DEP_INCLUDES} + ${WRT_INSTALLER_INCLUDES} + ${WRT_INSTALLER_DEPS_INCLUDE_DIRS} +) + +ADD_EXECUTABLE(${TARGET_INSTALLER} + ${TARGET_INSTALLER_STATIC_SRC} + ${WRT_INSTALLER_SOURCES} +) + +ADD_DEFINITIONS(${WRT_INSTALLER_DEPS_CFLAGS}) + +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER} + ${TARGET_INSTALLER_STATIC} + ${WRT_INSTALLER_DEPS_LIBRARIES} +) + + +SET_TARGET_PROPERTIES(${TARGET_INSTALLER} PROPERTIES + LINK_FLAGS "-Wl,--as-needed -Wl,--hash-style=both" + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_INSTALLER} DESTINATION bin) diff --git a/src_mobile/wrt-installer/installer_callbacks_translate.cpp b/src_mobile/wrt-installer/installer_callbacks_translate.cpp new file mode 100644 index 0000000..ca52dd6 --- /dev/null +++ b/src_mobile/wrt-installer/installer_callbacks_translate.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file api_callbacks_translate.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Source file for api callbacks translate functions + */ + +#include +#include +#include + +namespace InstallerCallbacksTranslate { + +// callback for finished install +void installFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->status_callback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + + case Jobs::Exceptions::ErrorPackageNotFound: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorPackageInvalid: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_INVALID; + break; + + case Jobs::Exceptions::ErrorPackageLowerVersion: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION; + break; + + case Jobs::Exceptions::ErrorPackageExecutableNotFound: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_EXCUTABLE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorManifestNotFound: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorManifestInvalid: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_INVALID; + break; + + case Jobs::Exceptions::ErrorConfigNotFound: + errorStatus = WRT_INSTALLER_CONFIG_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorConfigInvalid: + errorStatus = WRT_INSTALLER_ERROR_CONFIG_INVALID; + break; + + case Jobs::Exceptions::ErrorSignatureNotFound: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorSignatureInvalid: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_INVALID; + break; + + case Jobs::Exceptions::ErrorSignatureVerificationFailed: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED; + break; + + case Jobs::Exceptions::ErrorRootCertificateNotFound: + errorStatus = WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorCertificationInvaid: + errorStatus = WRT_INSTALLER_ERROR_CERTIFICATION_INVAID; + break; + + case + Jobs::Exceptions::ErrorCertificateChainVerificationFailed: + errorStatus = + WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED; + break; + + case Jobs::Exceptions::ErrorCertificateExpired: + errorStatus = WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED; + break; + + case Jobs::Exceptions::ErrorInvalidPrivilege: + errorStatus = WRT_INSTALLER_ERROR_INVALID_PRIVILEGE; + break; + + case Jobs::Exceptions::ErrorPrivilegeLevelViolation: + errorStatus = WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION; + break; + + case Jobs::Exceptions::ErrorMenuIconNotFound: + errorStatus = WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorFatalError: + errorStatus = WRT_INSTALLER_ERROR_FATAL_ERROR; + break; + + case Jobs::Exceptions::ErrorOutOfStorage: + errorStatus = WRT_INSTALLER_ERROR_OUT_OF_STORAGE; + break; + + case Jobs::Exceptions::ErrorOutOfMemory: + errorStatus = WRT_INSTALLER_ERROR_OUT_OF_MEMORY; + break; + + case Jobs::Exceptions::ErrorArgumentInvalid: + errorStatus = WRT_INSTALLER_ERROR_ARGUMENT_INVALID; + break; + + case Jobs::Exceptions::ErrorPackageAlreadyInstalled: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED; + break; + + case Jobs::Exceptions::ErrorAceCheckFailed: + errorStatus = WRT_INSTALLER_ERROR_ACE_CHECK_FAILED; + break; + + case Jobs::Exceptions::ErrorManifestCreateFailed: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED; + break; + + case Jobs::Exceptions::ErrorEncryptionFailed: + errorStatus = WRT_INSTALLER_ERROR_ENCRYPTION_FAILED; + break; + + case Jobs::Exceptions::ErrorInstallOspServcie: + errorStatus = WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE; + break; + + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + // Callback + apiStr->status_callback(tizenId, errorStatus, apiStr->userdata); + } else { + _D("installFinishedCallback: No callback"); + } +} + +// callback for finished install +void uninstallFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->status_callback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + + case Jobs::Exceptions::ErrorWidgetUninstallationFailed: + errorStatus = WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED; + break; + + case Jobs::Exceptions::ErrorUnknown: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + // Callback + apiStr->status_callback(tizenId, errorStatus, apiStr->userdata); + } else { + _D("uninstallFinishedCallback: No callback"); + } +} + +void pluginInstallFinishedCallback(void *userParam, + Jobs::Exceptions::Type status) +{ + Assert(userParam); + + PluginStatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->statusCallback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + case Jobs::Exceptions::ErrorPluginInstallationFailed: + errorStatus = WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED; + break; + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + apiStr->statusCallback(errorStatus, apiStr->userdata); + } else { + _D("PluginInstallFinishedCallback: No callback"); + } + + delete apiStr; +} + +// callback for progress of install OR uninstall +void installProgressCallback(void *userParam, + ProgressPercent percent, + const ProgressDescription &description) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->progress_callback) { + //CALLBACK EXEC + _D("Entered %2.0f%% %s", percent, description.c_str()); + apiStr->progress_callback(static_cast(percent), + description.c_str(), + apiStr->userdata); + } else { + _D("installProgressCallback: ignoring NULL callback pointer"); + } +} +} //namespace + diff --git a/src_mobile/wrt-installer/installer_callbacks_translate.h b/src_mobile/wrt-installer/installer_callbacks_translate.h new file mode 100644 index 0000000..5322078 --- /dev/null +++ b/src_mobile/wrt-installer/installer_callbacks_translate.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file api_callbacks_translate.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for api callbacks translate functions + */ +#ifndef WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ +#define WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace InstallerCallbacksTranslate { +struct StatusCallbackStruct +{ + void* userdata; + WrtInstallerStatusCallback status_callback; + WrtProgressCallback progress_callback; + + StatusCallbackStruct(void* u, + WrtInstallerStatusCallback s, + WrtProgressCallback p) : + userdata(u), + status_callback(s), + progress_callback(p) + {} +}; + +struct PluginStatusCallbackStruct +{ + void* userdata; + WrtPluginInstallerStatusCallback statusCallback; + WrtProgressCallback progressCallback; + + PluginStatusCallbackStruct(void* u, + WrtPluginInstallerStatusCallback s, + WrtProgressCallback p) : + userdata(u), + statusCallback(s), + progressCallback(p) + {} +}; + +void installFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status); + +void uninstallFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status); + +void pluginInstallFinishedCallback(void *userParam, + Jobs::Exceptions::Type status); + +// callback for progress of install OR uninstall +void installProgressCallback(void *userParam, + ProgressPercent percent, + const ProgressDescription &description); +} //namespace + +#endif /* WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ */ diff --git a/src_mobile/wrt-installer/installer_main_thread.cpp b/src_mobile/wrt-installer/installer_main_thread.cpp new file mode 100644 index 0000000..b398d3c --- /dev/null +++ b/src_mobile/wrt-installer/installer_main_thread.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file installer_main_thread.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "installer_main_thread.h" +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(InstallerMainThread) + +using namespace WrtDB; + +InstallerMainThread::InstallerMainThread() : m_attached(false) {} + +InstallerMainThread::~InstallerMainThread() +{ + Assert(!m_attached); +} + +void InstallerMainThread::AttachDatabases() +{ + Assert(!m_attached); + // Attach databases + ValidationCore::AttachToThreadRW(); + ace_return_t ret = ace_install_initialize(); + Assert(ACE_OK == ret); // to be changed to exception in the future + WrtDB::WrtDatabase::attachToThreadRW(); + m_attached = true; +} + +void InstallerMainThread::DetachDatabases() +{ + Assert(m_attached); + m_attached = false; + // Detach databases + ValidationCore::DetachFromThread(); + ace_return_t ret = ace_install_shutdown(); + Assert(ACE_OK == ret); // to be changed to exception in the future + WrtDB::WrtDatabase::detachFromThread(); +} + +void InstallerMainThread::TouchArchitecture() +{ + // Touch controller + Logic::InstallerControllerSingleton::Instance().Touch(); +} + +void InstallerMainThread::TouchArchitectureOnlyInstaller() +{ + // Touch controller + Logic::InstallerControllerSingleton::Instance().Touch(); +} diff --git a/src_mobile/wrt-installer/installer_main_thread.h b/src_mobile/wrt-installer/installer_main_thread.h new file mode 100644 index 0000000..bd70b16 --- /dev/null +++ b/src_mobile/wrt-installer/installer_main_thread.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file installer_main_thread.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef INSTALLER_MAINTHREAD_H_ +#define INSTALLER_MAINTHREAD_H_ + +#include + +class InstallerMainThread +{ + public: + void AttachDatabases(); + void DetachDatabases(); + void TouchArchitecture(); + void TouchArchitectureOnlyInstaller(); + + private: + friend class DPL::Singleton; + + InstallerMainThread(); + virtual ~InstallerMainThread(); + + bool m_attached; +}; + +typedef DPL::Singleton InstallerMainThreadSingleton; + +#endif /* INSTALLER_MAINTHREAD_H_ */ diff --git a/src_mobile/wrt-installer/language_subtag_rst_tree.cpp b/src_mobile/wrt-installer/language_subtag_rst_tree.cpp new file mode 100644 index 0000000..a2bfaf5 --- /dev/null +++ b/src_mobile/wrt-installer/language_subtag_rst_tree.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file language_subtag_rst_tree.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(LanguageSubtagRstTree) + +namespace I18nDAOReadOnly = I18n::DB::I18nDAOReadOnly; + +bool LanguageSubtagRstTree::ValidateLanguageTag(const std::string &tag_input) +{ + std::string tag = tag_input; + std::transform(tag.begin(), tag.end(), tag.begin(), &tolower); + + std::vector parts; + DPL::Tokenize(DPL::FromUTF8String(tag), + '-', + std::back_inserter(parts), + false); + std::vector::iterator token = parts.begin(); + if (token == parts.end()) + { + return false; + } + + I18n::DB::Interface::attachDatabaseRO(); + DPL_SCOPE_EXIT() + { + I18n::DB::Interface::detachDatabase(); + }; + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_LANGUAGE)) + { + ++token; + } + else + { + return false; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_EXTLANG)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_SCRIPT)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_REGION)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + while (token != parts.end()) + { + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_VARIANT)) + { + ++token; + } + else + { + break; + } + } + + //'u' - unicode extension - only one BCP47 extension is registered. + //TODO: unicode extension should be also validated (l.wrzosek) + if (token == parts.end()) + { + return true; + } + + if (*token == L"u") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + token->size() > 1 && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + if (!one_or_more) + { + return false; + } + } + + //'x' - privateuse + if (token == parts.end()) + { + return true; + } + + if (*token == L"x") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + !token->empty() && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + if (!one_or_more) + { + return false; + } + } + + if (token == parts.end()) + { + return true; + } + + //Try private use now: + token = parts.begin(); + if (*token == L"x") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + !token->empty() && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + return one_or_more; + } + + //grandfathered is always rejected + return false; +} + +#define TEST_LANG(str, cond) \ + if (LanguageSubtagRstTreeSingleton::Instance(). \ + ValidateLanguageTag(str) == cond) { \ + _D("Good validate status for lang: %s", str); \ + } else { \ + _E("Wrong validate status for lang: %s, should be %d", str, cond); \ + } + +void LanguageSubtagRstTree::Initialize() +{ + /* Temporarily added unit test. Commented out due to performance drop. + * TEST_LANG("zh", true); + * TEST_LANG("esx-al", true); + * TEST_LANG("zh-Hant", true); + * TEST_LANG("zh-Hant-CN", true); + * TEST_LANG("zh-Hant-CN-x-private1-private2", true); + * TEST_LANG("plxxx", false); + * TEST_LANG("pl-x-private111", false); + * TEST_LANG("x-private1", false); //do not support pure private ones + * TEST_LANG("x-private22", false); + * TEST_LANG("i-private22", false); //do not support i-* + */ +} + +#undef TEST_LANG diff --git a/src_mobile/wrt-installer/language_subtag_rst_tree.h b/src_mobile/wrt-installer/language_subtag_rst_tree.h new file mode 100644 index 0000000..b057059 --- /dev/null +++ b/src_mobile/wrt-installer/language_subtag_rst_tree.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file language_subtag_rst_tree.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ +#ifndef LANGUAGE_SUBTAG_RST_TREE_H +#define LANGUAGE_SUBTAG_RST_TREE_H + +#include +#include +#include +class LanguageSubtagRstTree : DPL::Noncopyable +{ + public: + void Initialize(); + bool ValidateLanguageTag(const std::string &tag); +}; + +typedef DPL::Singleton LanguageSubtagRstTreeSingleton; + +enum iana_record_types_e +{ + RECORD_TYPE_LANGUAGE, + RECORD_TYPE_SCRIPT, + RECORD_TYPE_REGION, + RECORD_TYPE_VARIANT, + RECORD_TYPE_GRANDFATHERED, + RECORD_TYPE_REDUNDANT, + RECORD_TYPE_EXTLANG +}; + +#endif //LANGUAGE_SUBTAG_RST_TREE_H diff --git a/src_mobile/wrt-installer/option_parser.cpp b/src_mobile/wrt-installer/option_parser.cpp new file mode 100644 index 0000000..982d86f --- /dev/null +++ b/src_mobile/wrt-installer/option_parser.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file option_parser.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @brief Implementation file for OptionParser. + */ + +#include +#include +#include +#include "option_parser.h" + +DPL::OptionalString OptionParser::QueryOption(int argc, + char* argv[], + const std::string& name) +{ + DPL::OptionalString result; + + std::vector args(argv, (argv + argc)); + + auto it = std::find_if(args.begin(), + args.end(), + [&name](const std::string & option){ + return (option == name); + }); + + if (it != args.end()) { + std::string value; + while ((++it != args.end()) && !IsOption(*it)) { + value += *it + " "; + } + result = DPL::FromUTF8String(value); + } + + return result; +} + +bool OptionParser::IsOption(const std::string& name) +{ + return (name.find('-') == 0); +} diff --git a/src_mobile/wrt-installer/option_parser.h b/src_mobile/wrt-installer/option_parser.h new file mode 100644 index 0000000..7fa8c3a --- /dev/null +++ b/src_mobile/wrt-installer/option_parser.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file option_parser.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @brief Header file for OptionParser. + */ + +#ifndef WRT_INSTALLER_SRC_WRT_INSTALLER_OPTION_PARSER_H_ +#define WRT_INSTALLER_SRC_WRT_INSTALLER_OPTION_PARSER_H_ + +#include +#include + +class OptionParser +{ + public: + static DPL::OptionalString QueryOption(int argc, + char* argv[], + const std::string & name); + + private: + static bool IsOption(const std::string& name); +}; + +#endif diff --git a/src_mobile/wrt-installer/plugin_utils.cpp b/src_mobile/wrt-installer/plugin_utils.cpp new file mode 100644 index 0000000..cabda19 --- /dev/null +++ b/src_mobile/wrt-installer/plugin_utils.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin-utils.cpp + * @author + * @version 1.0 + * @brief Header file for plugin util + */ + +#include +#include "plugin_utils.h" +#include +#include +#include +#include + +using namespace WrtDB; + +namespace PluginUtils { +const char* PLUGIN_INSTALL_LOCK_FILE = "/tmp/.wrt_plugin_install_lock"; + +static int s_plugin_install_lock_fd = -1; + +bool lockPluginInstallation(bool isPreload) +{ + if (isPreload) { + fprintf(stderr, "Skip create lock file.. \n"); + return true; + } + + int ret = 0; + + _D("Try to lock for plugins installation."); + + s_plugin_install_lock_fd = + open(PLUGIN_INSTALL_LOCK_FILE, O_RDONLY | O_CREAT, 0666); + + if (s_plugin_install_lock_fd == -1) { + _E("Lock file open failed!"); + + return false; + } + + ret = flock(s_plugin_install_lock_fd, LOCK_EX); //lock with waiting + + if (ret == -1) { + _E("Lock failed!"); + + close(s_plugin_install_lock_fd); + s_plugin_install_lock_fd = -1; + + return false; + } + + return true; +} + +bool unlockPluginInstallation(bool isPreload) +{ + _D("Unlock for plugins installation."); + if (isPreload) { + fprintf(stderr, "Skip plugin unlock.. \n"); + return true; + } + + if (s_plugin_install_lock_fd != -1) { + int ret = 0; + + ret = flock(s_plugin_install_lock_fd, LOCK_UN); //unlock + + if (ret == -1) { + _E("Unlock failed!"); + } + + close(s_plugin_install_lock_fd); + s_plugin_install_lock_fd = -1; + + return true; + } else { + _E("Lock file was not created!"); + } + + return false; +} + +bool checkPluginInstallationRequired() +{ + std::string installRequest = + std::string(GlobalConfig::GetPluginInstallInitializerName()); + + FileState::Type installationRequest = + checkFile(installRequest); + + switch (installationRequest) { + case FileState::FILE_EXISTS: + return true; + case FileState::FILE_NOT_EXISTS: + return false; + default: + _W("Opening installation request file failed"); + return false; + } +} + +bool removeInstallationRequiredFlag() +{ + std::string installRequest = + std::string(GlobalConfig::GetPluginInstallInitializerName()); + + return removeFile(installRequest); +} + +//checks if file exists and is regular file +FileState::Type checkFile(const std::string& filename) +{ + struct stat tmp; + + if (-1 == stat(filename.c_str(), &tmp)) { + if (ENOENT == errno) { + return FileState::FILE_NOT_EXISTS; + } + return FileState::FILE_READ_DATA_ERROR; + } else if (!S_ISREG(tmp.st_mode)) { + return FileState::FILE_EXISTS_NOT_REGULAR; + } + return FileState::FILE_EXISTS; +} + +bool removeFile(const std::string& filename) +{ + if (0 != unlink(filename.c_str())) { + return false; + } + + return true; +} +} //namespace PluginUtils diff --git a/src_mobile/wrt-installer/plugin_utils.h b/src_mobile/wrt-installer/plugin_utils.h new file mode 100644 index 0000000..8659f20 --- /dev/null +++ b/src_mobile/wrt-installer/plugin_utils.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin-utils.h + * @author + * @version 1.0 + * @brief Header file for plugin util + */ +#ifndef PLUGIN_UTILS_H +#define PLUGIN_UTILS_H + +#include +#include + +namespace PluginUtils { +struct FileState +{ + enum Type + { + FILE_EXISTS, + FILE_EXISTS_NOT_REGULAR, + FILE_NOT_EXISTS, + FILE_READ_DATA_ERROR + }; +}; + +bool lockPluginInstallation(bool isPreload); +bool unlockPluginInstallation(bool isPreload); +bool checkPluginInstallationRequired(); +bool removeInstallationRequiredFlag(); +FileState::Type checkFile(const std::string& filename); +bool removeFile(const std::string& filename); +} +#endif // PLUGIN_UTILS_H diff --git a/src_mobile/wrt-installer/wrt-installer.cpp b/src_mobile/wrt-installer/wrt-installer.cpp new file mode 100644 index 0000000..61a0c2a --- /dev/null +++ b/src_mobile/wrt-installer/wrt-installer.cpp @@ -0,0 +1,1160 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* @file wrt-installer.cpp + * @version 1.0 + * @brief Implementation file for installer + */ + +#include "wrt-installer.h" +#include "plugin_utils.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace WrtDB; + +namespace { // anonymous +const char * const PKGMGR_INSTALL_MSG = "Install widget"; +const char * const PKGMGR_UNINSTALL_MSG = "Uninstall widget"; + +const char * const CONFIG_XML = "config.xml"; +const char * const HYBRID_CONFIG_XML = "res/wgt/config.xml"; + +const unsigned int NOFILE_CNT_FOR_INSTALLER = 9999; + +struct free_deleter +{ + void operator()(void* x) + { + free(x); + } +}; + +struct PluginInstallerData +{ + void* wrtInstaller; + std::string pluginPath; +}; +} // namespace anonymous + +WrtInstaller::WrtInstaller(int argc, char **argv) : + Application(argc, argv, "backend", false), + DPL::TaskDecl(this), + m_packagePath(), + m_initialized(false), + m_numPluginsToInstall(0), + m_totalPlugins(0), + m_returnStatus(-1), + m_installByPkgmgr(false), + m_startupPluginInstallation(false) +{ + Touch(); + _D("App Created"); +} + +WrtInstaller::~WrtInstaller() +{ + _D("App Finished"); +} + +int WrtInstaller::getReturnStatus() const +{ + return m_returnStatus; +} + +void WrtInstaller::OnStop() +{ + _D("Stopping Dummy Client"); +} + +void WrtInstaller::OnCreate() +{ + _D("Creating DummyClient"); + showArguments(); + + AddStep(&WrtInstaller::initStep); + + std::string arg = m_argv[0]; + + pkgmgrSignalInterface = + std::static_pointer_cast( + std::shared_ptr( + new PackageManager::PkgmgrSignalDummy() + ) + ); + + if (arg.empty()) { + return showHelpAndQuit(); + } + + installNewPlugins(); + + if (arg.find("wrt-installer") != std::string::npos) { + if (m_argc <= 1) { + return showHelpAndQuit(); + } + + arg = m_argv[1]; + if (arg == "-h" || arg == "--help") { + if (m_argc != 2) { + return showHelpAndQuit(); + } + + // Just show help + return showHelpAndQuit(); + } else if (arg == "-p" || arg == "--install-plugins") { + if (m_argc != 2) { + return showHelpAndQuit(); + } + + if (!m_startupPluginInstallation) { + AddStep(&WrtInstaller::installPluginsStep); + } else { + _D("Plugin installation alredy started"); + } + } else if (arg == "-i" || arg == "--install") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + + struct stat info; + if (-1 != stat(m_argv[2], &info) && S_ISDIR(info.st_mode)) { + _D("Installing package directly from directory"); + m_installMode.extension = InstallMode::ExtensionType::DIR; + } else { + _D("Installing from regular location"); + m_installMode.extension = InstallMode::ExtensionType::WGT; + } + m_packagePath = m_argv[2]; + + AddStep(&WrtInstaller::installStep); + } else if (arg == "-ip" || arg == "--install-preload") { + _D("Install preload web app"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + m_installMode.installTime = InstallMode::InstallTime::PRELOAD; + m_installMode.rootPath = InstallMode::RootPath::RO; + AddStep(&WrtInstaller::installStep); + } else if (arg == "-ipw" || arg == "--install-preload-writable") { + _D("Install preload web application to writable storage"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + m_installMode.installTime = InstallMode::InstallTime::PRELOAD; + m_installMode.rootPath = InstallMode::RootPath::RW; + AddStep(&WrtInstaller::installStep); + } else if (arg == "-c" || arg == "--csc-update") { + // "path=/opt/system/csc/Ozq2iEG15R-2.0.0-arm.wgt:op=install:removable=true" + _D("Install & uninstall by csc configuration"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_installMode.installTime = InstallMode::InstallTime::CSC; + std::string configuration = m_argv[2]; + m_CSCconfigurationMap = parseCSCConfiguration(configuration); + + CSCConfiguration::dataMap::iterator it; + it = m_CSCconfigurationMap.find(CSCConfiguration::KEY_OP); + if (it == m_CSCconfigurationMap.end()) { + return showHelpAndQuit(); + } + + if (it->second == CSCConfiguration::VALUE_INSTALL) { + _D("operation = %s", it->second.c_str()); + m_installMode.extension = InstallMode::ExtensionType::WGT; + it = m_CSCconfigurationMap.find(CSCConfiguration::KEY_PATH); + if (it == m_CSCconfigurationMap.end()) { + return showHelpAndQuit(); + } + m_packagePath = it->second; + + it = m_CSCconfigurationMap.find( + CSCConfiguration::KEY_REMOVABLE); + if (it == m_CSCconfigurationMap.end()) { + return showHelpAndQuit(); + } + + m_installMode.removable = + (it->second.compare(CSCConfiguration::VALUE_FALSE) == 0) + ? false : true; + + AddStep(&WrtInstaller::installStep); + _D("path = %s", m_packagePath.c_str()); + } else if (it->second == CSCConfiguration::VALUE_UNINSTALL) { + _D("operation = %s", it->second.c_str()); + // uninstall command isn't confirmed yet + it = m_CSCconfigurationMap.find(CSCConfiguration::KEY_PATH); + if (it == m_CSCconfigurationMap.end()) { + return showHelpAndQuit(); + } + m_packagePath = it->second; + AddStep(&WrtInstaller::unistallWgtFileStep); + _D("operation = uninstall"); + _D("path = %s", m_packagePath.c_str()); + } else { + _E("Unknown operation : %s", it->second.c_str()); + _D("operation = %s", it->second.c_str()); + return showHelpAndQuit(); + } + } else if (arg == "-un" || arg == "--uninstall-name") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_name = m_argv[2]; + AddStep(&WrtInstaller::uninstallPkgNameStep); + } else if (arg == "-up" || arg == "--uninstall-packagepath") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + AddStep(&WrtInstaller::unistallWgtFileStep); + } else if (arg == "-r" || arg == "--reinstall") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + _D("Installing package directly from directory"); + m_installMode.command = InstallMode::Command::REINSTALL; + m_installMode.extension = InstallMode::ExtensionType::DIR; + m_packagePath = m_argv[2]; + AddStep(&WrtInstaller::installStep); + } else { + return showHelpAndQuit(); + } + } else if (arg.find("backend") != std::string::npos) { + using namespace PackageManager; + m_installByPkgmgr = true; + + auto pkgmgrSignal = std::shared_ptr( + new PackageManager::PkgmgrSignal() + ); + + pkgmgrSignal->initialize(m_argc, m_argv); + + PackageManager::PkgmgrSignal::RequestType reqType = pkgmgrSignal->getRequestedType(); + + pkgmgrSignalInterface = + std::static_pointer_cast( + pkgmgrSignal); + switch (reqType) { + case PackageManager::PkgmgrSignal::RequestType::INSTALL: + m_packagePath = m_argv[4]; + if (6 < m_argc) { + m_name = std::string(m_argv[6]); + } + + struct stat info; + if (-1 != stat(m_argv[4], &info) && S_ISDIR(info.st_mode)) { + _D("Installing package directly from directory"); + m_installMode.extension = InstallMode::ExtensionType::DIR; + } else { + _D("Installing from regular location"); + m_installMode.extension = InstallMode::ExtensionType::WGT; + } + AddStep(&WrtInstaller::installStep); + break; + case PackageManager::PkgmgrSignal::RequestType::UNINSTALL: + { + m_name = m_argv[4]; + pkgmgrinfo_pkginfo_h handle = NULL; + bool system_app = false; // system app is preloaded and unremovable. + bool update = false; + + if (0 == pkgmgrinfo_pkginfo_get_pkginfo(m_name.c_str(), &handle)) { + if (0 > pkgmgrinfo_pkginfo_is_system(handle, &system_app)) { + _E("Can't get package information : %s", m_name.c_str()); + } + if (0 > pkgmgrinfo_pkginfo_is_update(handle, &update)) { + _E("Can't get package information about update : %s", m_name.c_str()); + } + } + + _D("system app : %d", system_app); + _D("update : %d", update); + + if (system_app && update) { + AddStep(&WrtInstaller::removeUpdateStep); + } else { + AddStep(&WrtInstaller::uninstallPkgNameStep); + } + break; + } + case PackageManager::PkgmgrSignal::RequestType::REINSTALL: + m_packagePath = m_argv[4]; + m_installMode.command = InstallMode::Command::REINSTALL; + m_installMode.extension = InstallMode::ExtensionType::DIR; + AddStep(&WrtInstaller::installStep); + break; + default: + _D("Not available type"); + break; + } + } + + AddStep(&WrtInstaller::shutdownStep); + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::NextStepEvent()); +} + +void WrtInstaller::OnReset(bundle* /*b*/) +{ + _D("OnReset"); +} + +void WrtInstaller::OnTerminate() +{ + _D("Wrt Shutdown now"); + PluginUtils::unlockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD); + if (m_initialized) { + wrt_installer_shutdown(); + } +} + +void WrtInstaller::showHelpAndQuit() +{ + printf("Usage: wrt-installer [OPTION]... [WIDGET: ID/NAME/PATH]...\n" + "Operate with WebRuntime daemon: install, uninstall" + " and launch widgets.\n" + "Query list of installed widgets and setup up debugging support.\n" + "\n" + "Exactly one option must be selected.\n" + "Mandatory arguments to long options are mandatory for short " + "options too.\n" + " -h, --help show this help\n" + " -p, --install-plugins install plugins\n" + " -i, --install " + "install or update widget package for given path\n" + " -c, --csc-update " + "install or uninstall by CSC configuration \n" + " -un, --uninstall-name " + "uninstall widget for given package name\n" + " -up, --uninstall-packagepath " + "uninstall widget for given package file path\n" + " -r, --reinstall " + "reinstall web application\n" + "\n"); + + Quit(); +} + +void WrtInstaller::showArguments() +{ + fprintf(stderr, + "===========================================================\n"); + fprintf(stderr, "# wrt-installer #\n"); + fprintf(stderr, "# argc [%d]\n", m_argc); + for (int i = 0; i < m_argc; i++) { + fprintf(stderr, "# argv[%d] = [%s]\n", i, m_argv[i]); + } + fprintf(stderr, + "===========================================================\n"); + // for dlog + _D("==========================================================="); + _D("# wrt-installer #"); + _D("# argc %d", m_argc); + for (int i = 0; i < m_argc; i++) { + _D("# argv[%d] = %s", i, m_argv[i]); + } + _D("==========================================================="); + +} + +void WrtInstaller::OnEventReceived(const WRTInstallerNS::QuitEvent& /*event*/) +{ + _D("Quiting"); + + if (m_initialized) { + _D("Wrt Shutdown now"); + SwitchToStep(&WrtInstaller::shutdownStep); + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::NextStepEvent()); + } else { + _D("Quiting application"); + return Quit(); + } +} + +void WrtInstaller::OnEventReceived( + const WRTInstallerNS::NextStepEvent& /*event*/) +{ + _D("Executing next step"); + NextStep(); +} + +void WrtInstaller::OnEventReceived( + const WRTInstallerNS::InstallPluginEvent& /*event*/) +{ + PluginInstallerData* privateData = new PluginInstallerData; + privateData->wrtInstaller = this; + + if (!(*m_pluginsPaths).empty()) { + privateData->pluginPath = (*m_pluginsPaths).front(); + (*m_pluginsPaths).pop_front(); + + wrt_install_plugin(privateData->pluginPath.c_str(), + static_cast(privateData), + &staticWrtPluginInstallationCallback, + &staticWrtPluginInstallProgressCb); + } else { + delete privateData; + } +} + +void WrtInstaller::initStep() +{ + wrt_installer_init(this, staticWrtInitCallback); +} + +void WrtInstaller::installStep() +{ + _D("Installing widget ..."); + std::unique_ptr packagePath(canonicalize_file_name( + m_packagePath.c_str())); + + wrt_install_widget(packagePath ? packagePath.get() : m_packagePath.c_str(), + m_name.c_str(), this, &staticWrtStatusCallback, + (m_installByPkgmgr) + ? &staticWrtInstallProgressCallback : NULL, + m_installMode, + pkgmgrSignalInterface); +} + +void WrtInstaller::installPluginsStep() +{ + _D("Installing plugins ..."); + fprintf(stderr, "Installing plugins ...\n"); + + if (m_startupPluginInstallation) { + _D("Plugin installation started because new plugin package found"); + } else if (!PluginUtils::lockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _E("Failed to open plugin installation lock file" + " Plugins are currently installed by other process"); + staticWrtPluginInstallationCallback(WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED, + this); + return; + } + + std::string PLUGIN_PATH = std::string(GlobalConfig::GetDevicePluginPath()); + + DIR *dir; + dir = opendir(PLUGIN_PATH.c_str()); + + if (!dir) { + return; + } + + _D("Plugin DIRECTORY IS %s", PLUGIN_PATH.c_str()); + + std::list pluginsPaths; + struct dirent libdir; + struct dirent *result; + int return_code; + errno = 0; + for (return_code = readdir_r(dir, &libdir, &result); + result != NULL && return_code == 0; + return_code = readdir_r(dir, &libdir, &result)) + { + if (strcmp(libdir.d_name, ".") == 0 || + strcmp(libdir.d_name, "..") == 0) + { + continue; + } + + std::string path = PLUGIN_PATH; + path += "/"; + path += libdir.d_name; + + struct stat tmp; + + if (stat(path.c_str(), &tmp) == -1) { + _E("Failed to open file %s", path.c_str()); + continue; + } + + if (!S_ISDIR(tmp.st_mode)) { + _E("Not a directory %s", path.c_str()); + continue; + } + + pluginsPaths.push_back(path); + } + + if (return_code != 0 || errno != 0) { + _E("readdir_r() failed with %s", DPL::GetErrnoString().c_str()); + } + + //set nb of plugins to install + //this value indicate how many callbacks are expected + m_numPluginsToInstall = pluginsPaths.size(); + _D("Plugins to install: %d", m_numPluginsToInstall); + m_pluginsPaths = pluginsPaths; + + m_totalPlugins = m_numPluginsToInstall; + DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::InstallPluginEvent()); + + if (-1 == TEMP_FAILURE_RETRY(closedir(dir))) { + _E("Failed to close dir: %s with error: %s", PLUGIN_PATH.c_str(), DPL::GetErrnoString().c_str()); + } +} + +void WrtInstaller::uninstallPkgNameStep() +{ + _D("Uninstalling widget ..."); + _D("Package name : %s", m_name.c_str()); + + wrt_uninstall_widget(m_name.c_str(), this, + &staticWrtStatusCallback, + (m_installByPkgmgr) + ? &staticWrtUninstallProgressCallback : NULL, + pkgmgrSignalInterface); +} + +void WrtInstaller::removeUpdateStep() +{ + _D("This web app need to initialize preload app"); + _D("Package name : %s", m_name.c_str()); + + wrt_uninstall_widget(m_name.c_str(), this, + &staticWrtInitializeToPreloadCallback, + (m_installByPkgmgr) + ? &staticWrtUninstallProgressCallback : NULL, + pkgmgrSignalInterface); + +} + +void WrtInstaller::unistallWgtFileStep() +{ + _D("Uninstalling widget ..."); + + Try { + // Parse config + ParserRunner parser; + ConfigParserData configInfo; + + // Open zip file + std::unique_ptr zipFile( + new DPL::ZipInput(m_packagePath)); + std::unique_ptr configFile; + + Try { + // Open config.xml file + configFile.reset(zipFile->OpenFile(CONFIG_XML)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + // Open config.xml file for hybrid + configFile.reset(zipFile->OpenFile(HYBRID_CONFIG_XML)); + } + + // Extract config + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(configFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + parser.Parse(&buffer, + ElementParserPtr( + new RootParser(configInfo, + DPL::FromUTF32String( + L"widget")))); + + DPL::OptionalString pkgId = configInfo.tizenPkgId; + if (!pkgId.IsNull()) { + _D("Pkgid from packagePath : %ls", (*pkgId).c_str()); + wrt_uninstall_widget( + DPL::ToUTF8String(*pkgId).c_str(), this, &staticWrtStatusCallback, + !m_installByPkgmgr ? &staticWrtUninstallProgressCallback + : NULL, + pkgmgrSignalInterface); + } else { + _E("Fail to uninstalling widget... "); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _E("Failed to open widget package"); + printf("failed: widget package does not exist\n"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + printf("failed: widget config file does not exist\n"); + _E("Failed to open config.xml file"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + Catch(ElementParser::Exception::ParseError) + { + printf("failed: can not parse config file\n"); + _E("Failed to parse config.xml file"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::shutdownStep() +{ + _D("Closing Wrt connection ..."); + if (m_initialized) { + wrt_installer_shutdown(); + m_initialized = false; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::staticWrtInitCallback(WrtErrStatus status, + void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + if (status == WRT_SUCCESS) { + _D("Init succesfull"); + This->m_initialized = true; + This->m_returnStatus = 0; + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } else { + _E("Init unsuccesfull"); + This->m_returnStatus = -1; + This->DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::staticWrtInitializeToPreloadCallback(std::string tizenId, WrtErrStatus + status, void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + std::string printMsg = "uninstallation"; + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = 1; + + This->showErrorMsg(status, tizenId, printMsg); + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + } else { + InstallMode mode; + mode.extension = InstallMode::ExtensionType::DIR; + mode.installTime = InstallMode::InstallTime::PRELOAD; + mode.rootPath = InstallMode::RootPath::RO; + std::string packagePath = + std::string(WrtDB::GlobalConfig::GetUserPreloadedWidgetPath()) + "/" + + This->m_name; + + wrt_install_widget(packagePath.c_str(), tizenId.c_str(), + This, &staticWrtInitPreloadStatusCallback, + NULL, + mode, + This->pkgmgrSignalInterface); + } +} + +void WrtInstaller::staticWrtInitPreloadStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + std::string printMsg = "initialization"; + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = status; + + This->showErrorMsg(status, tizenId, printMsg); + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + } else { + fprintf(stderr, + "## wrt-installer : %s %s was successful.\n", + tizenId.c_str(), + printMsg.c_str()); + _D("Status succesfull"); + This->m_returnStatus = 0; + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } +} + +void WrtInstaller::staticWrtStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + Step current = This->GetCurrentStep(); + std::string printMsg; + + if (current == &WrtInstaller::installStep) { + printMsg = "installation"; + } else if (current == &WrtInstaller::uninstallPkgNameStep || + current == &WrtInstaller::unistallWgtFileStep) + { + printMsg = "uninstallation"; + } + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = status; + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + + This->showErrorMsg(status, tizenId, printMsg); + } else { + fprintf(stderr, + "## wrt-installer : %s %s was successful.\n", + tizenId.c_str(), + printMsg.c_str()); + _D("Status succesfull"); + This->m_returnStatus = 0; + + if (This->m_installMode.installTime == InstallMode::InstallTime::PRELOAD && + !This->m_packagePath.empty()) + { + _D("This widget is preloaded so it will be removed : %s", This->m_packagePath.c_str()); + if (!WrtUtilRemove(This->m_packagePath)) { + _E("Failed to remove %s", This->m_packagePath.c_str()); + } + } + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } +} + +void WrtInstaller::showErrorMsg(WrtErrStatus status, std::string tizenId, + std::string printMsg) +{ + switch (status) { + case WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - widget package does not exist\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - invalid widget package\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION: + fprintf(stderr, "## wrt-installer : %s %s has failed - given" + " version is lower than existing version\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - manifest" + " file doesn't find in package.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid manifestx.xml\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_CONFIG_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "config.xml does not exist\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CONFIG_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid config.xml\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "signature doesn't exist in package.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid signature.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "signature verification failed.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "root certificate could not find.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATION_INVAID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid certification.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "certificate chain verification failed.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "certificate expired.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_INVALID_PRIVILEGE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid privilege\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "privilege level violation\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "menu icon could not find\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_FATAL_ERROR: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "fatal error\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_OUT_OF_STORAGE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "out of storage\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_OUT_OF_MEMORY: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "out of memory\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ARGUMENT_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid argument\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "package already installed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ACE_CHECK_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "ace check failure\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "to create manifest failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ENCRYPTION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "encryption of resource failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "installation of osp service failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "widget uninstallation failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + + case WRT_INSTALLER_ERROR_UNKNOWN: + fprintf(stderr,"## wrt-installer : %s %s has failed - unknown error\n", + tizenId.c_str(), printMsg.c_str()); + break; + + default: + break; + } + +} + +void WrtInstaller::staticWrtPluginInstallationCallback(WrtErrStatus status, + void* userdata) +{ + Assert(userdata); + + PluginInstallerData* data = static_cast(userdata); + + WrtInstaller *This = static_cast(data->wrtInstaller); + + std::string path = std::string(data->pluginPath); + delete data; + + This->m_numPluginsToInstall--; + _D("Plugins to install: %d", This->m_numPluginsToInstall); + + if (This->m_numPluginsToInstall < 1) { + _D("All plugins installation completed"); + fprintf(stderr, "All plugins installation completed.\n"); + + //remove installation request + if (!PluginUtils::removeInstallationRequiredFlag()) { + _D("Failed to remove file initializing plugin installation"); + } + + //remove lock file + if (!PluginUtils::unlockPluginInstallation( + This->m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _D("Failed to remove installation lock"); + } + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } else { + This->DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::InstallPluginEvent()); + } + + if (WRT_SUCCESS == status) { + This->m_returnStatus = 0; + fprintf(stderr, + "## wrt-installer : plugin installation successfull [%s]\n", + path.c_str()); + _D("One plugin Installation succesfull: %s", path.c_str()); + return; + } + + // Failure + _W("One of the plugins installation failed!: %s", path.c_str()); + + switch (status) { + case WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED: + _E("failed: plugin installation failed\n"); + break; + + case WRT_INSTALLER_ERROR_UNKNOWN: + _E("failed: unknown error\n"); + break; + + default: + break; + } +} + +void WrtInstaller::staticWrtPluginInstallProgressCb(float percent, + const char* description, + void* userdata) +{ + PluginInstallerData* data = static_cast(userdata); + + std::string path = std::string(data->pluginPath); + + _D("Plugin Installation: %s progress: %2.0f description: %s", path.c_str(), percent, description); +} + +void WrtInstaller::staticWrtInstallProgressCallback(float percent, + const char* description, + void* /*userdata*/) +{ + //WrtInstaller *This = static_cast(userdata); + _D(" progress: %2.0f description: %s", percent, description); +} +void WrtInstaller::staticWrtUninstallProgressCallback(float percent, + const char* description, + void* /*userdata*/) +{ + //WrtInstaller *This = static_cast(userdata); + _D(" progress: %2.0f description: %s", percent, description); +} + +void WrtInstaller::installNewPlugins() +{ + _D("Install new plugins"); + + if (!PluginUtils::lockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _D("Lock NOT created"); + return; + } + + if (!PluginUtils::checkPluginInstallationRequired()) { + _D("Plugin installation not required"); + PluginUtils::unlockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD); + return; + } + + m_startupPluginInstallation = true; + AddStep(&WrtInstaller::installPluginsStep); +} + +CSCConfiguration::dataMap WrtInstaller::parseCSCConfiguration( + std::string str) +{ + // path=/opt/system/csc/Ozq2iEG15R-2.0.0-arm.wgt:op=install:removable=true + // parsing CSC configuration string + _D("parseConfiguration"); + CSCConfiguration::dataMap result; + + if (str.empty()) { + _D("Input argument is empty"); + return result; + } + + char* buf = strdup(str.c_str()); + const char* ptr = strtok(buf,":"); + while (ptr != NULL) { + std::string string = ptr; + ptr = strtok (NULL, ":"); + size_t pos = string.find('='); + if (pos == std::string::npos) { + continue; + } + result.insert( + CSCConfiguration::dataPair(string.substr(0, pos), + string.substr(pos+1))); + } + free(buf); + return result; +} + +int main(int argc, char *argv[]) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + DPL::Log::LogSystemSingleton::Instance().SetTag("WRT_INSTALLER"); + + // Output on stdout will be flushed after every newline character, + // even if it is redirected to a pipe. This is useful for running + // from a script and parsing output. + // (Standard behavior of stdlib is to use full buffering when + // redirected to a pipe, which means even after an end of line + // the output may not be flushed). + setlinebuf(stdout); + + // Check and re-set the file open limitation + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim) != -1) { + _D("RLIMIT_NOFILE sft(%d)", rlim.rlim_cur); + _D("RLIMIT_NOFILE hrd(%d)", rlim.rlim_max); + + if (rlim.rlim_cur < NOFILE_CNT_FOR_INSTALLER) { + rlim.rlim_cur = NOFILE_CNT_FOR_INSTALLER; + rlim.rlim_max = NOFILE_CNT_FOR_INSTALLER; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + _E("setrlimit is fail!!"); + } + } + } else { + _E("getrlimit is fail!!"); + } + + WrtInstaller app(argc, argv); + int ret = app.Exec(); + _D("App returned: %d", ret); + ret = app.getReturnStatus(); + _D("WrtInstaller returned: %d", ret); + return ret; + } + UNHANDLED_EXCEPTION_HANDLER_END +} diff --git a/src_mobile/wrt-installer/wrt-installer.h b/src_mobile/wrt-installer/wrt-installer.h new file mode 100644 index 0000000..e8feefb --- /dev/null +++ b/src_mobile/wrt-installer/wrt-installer.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt-installer.h + * @version 1.0 + * @brief Implementation file for installer + */ +#ifndef WRT_INSTALLER_H +#define WRT_INSTALLER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WRTInstallerNS { //anonymous +DECLARE_GENERIC_EVENT_0(QuitEvent) +DECLARE_GENERIC_EVENT_0(NextStepEvent) +DECLARE_GENERIC_EVENT_0(InstallPluginEvent) +} + +typedef void (*ShowResultCallback)(void *data, Evas_Object *obj, + void *event_info); +namespace CSCConfiguration { +typedef std::map dataMap; +typedef std::pair dataPair; +const char* const KEY_OP = "op"; +const char* const KEY_PATH = "path"; +const char* const KEY_REMOVABLE = "removable"; +const char* const VALUE_INSTALL = "install"; +const char* const VALUE_UNINSTALL = "uninstall"; +const char* const VALUE_TRUE = "true"; +const char* const VALUE_FALSE = "false"; +} + +class WrtInstaller : + public DPL::Application, + private DPL::Event::Controller:: + Type>, + public DPL::TaskDecl +{ + public: + WrtInstaller(int argc, + char **argv); + virtual ~WrtInstaller(); + + int getReturnStatus() const; + + protected: + virtual void OnStop(); + virtual void OnCreate(); + virtual void OnReset(bundle *b); + virtual void OnTerminate(); + + private: + void showHelpAndQuit(); + void showArguments(); + + // Events + virtual void OnEventReceived(const WRTInstallerNS::QuitEvent &event); + virtual void OnEventReceived(const WRTInstallerNS::NextStepEvent& event); + virtual void OnEventReceived( + const WRTInstallerNS::InstallPluginEvent& event); + + // Installation steps + void initStep(); + void installStep(); + void installPluginsStep(); + void uninstallPkgNameStep(); + void unistallWgtFileStep(); + void removeUpdateStep(); + void shutdownStep(); + + // Static callbacks + static void staticWrtInitCallback(WrtErrStatus status, + void* userdata); + static void staticWrtStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + static void staticWrtPluginInstallationCallback(WrtErrStatus status, + void* userdata); + static void staticWrtPluginInstallProgressCb(float percent, + const char* description, + void* userdata); + static void staticWrtInstallProgressCallback(float percent, + const char* description, + void* userdata); + + static void staticWrtUninstallProgressCallback(float percent, + const char* description, + void* userdata); + + static void staticWrtInitializeToPreloadCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + + static void staticWrtInitPreloadStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + + void installNewPlugins(); + CSCConfiguration::dataMap parseCSCConfiguration(std::string str); + void showErrorMsg(WrtErrStatus status, std::string tizenId, std::string + printMsg); + + // Private data + std::shared_ptr pkgmgrSignalInterface; + InstallMode m_installMode; + std::string m_packagePath; + std::string m_name; + bool m_initialized; + size_t m_numPluginsToInstall; + size_t m_totalPlugins; + int m_returnStatus; + bool m_installByPkgmgr; + bool m_startupPluginInstallation; + CSCConfiguration::dataMap m_CSCconfigurationMap; + + typedef std::list PluginPathList; + DPL::Optional m_pluginsPaths; +}; +#endif // WRT_INSTALLER_H diff --git a/src_mobile/wrt-installer/wrt_installer_api.cpp b/src_mobile/wrt-installer/wrt_installer_api.cpp new file mode 100644 index 0000000..ba1ee19 --- /dev/null +++ b/src_mobile/wrt-installer/wrt_installer_api.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_installer_api.cpp + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief This file contains definitions of wrt installer api + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +static std::string cutOffFileName(const std::string& path) +{ + size_t found = path.find_last_of("/"); + if (found == std::string::npos) { + return path; + } else { + return path.substr(0, found); + } +} + +static bool checkPath(const std::string& path) +{ + struct stat st; + if (0 == stat(path.c_str(), &st) && S_ISDIR(st.st_mode)) { + return true; + } + _E("Cannot access directory [ %s ]", path.c_str()); + return false; +} + +static bool checkPaths() +{ + bool if_ok = true; + + if_ok &= (checkPath(cutOffFileName(GlobalConfig::GetWrtDatabaseFilePath()))); + if_ok &= (checkPath(GlobalConfig::GetDevicePluginPath())); + if_ok &= (checkPath(GlobalConfig::GetUserInstalledWidgetPath())); + if_ok &= (checkPath(GlobalConfig::GetUserPreloadedWidgetPath())); + + return if_ok; +} + +void wrt_installer_init(void *userdata, + WrtInstallerInitCallback callback) +{ + try { + _D("[WRT-API] INITIALIZING WRT INSTALLER..."); + _D("[WRT-API] BUILD: %s", __TIMESTAMP__); + + // Touch InstallerController Singleton + InstallerMainThreadSingleton::Instance().TouchArchitecture(); + + // Check paths + if (!checkPaths()) { + if (callback) { + callback(WRT_INSTALLER_ERROR_FATAL_ERROR, userdata); + } + return; + } + + // Initialize ValidationCore - this must be done before AttachDatabases + ValidationCore::VCoreInit( + std::string(GlobalConfig::GetFingerprintListFile()), + std::string(GlobalConfig::GetFingerprintListSchema()), + std::string(GlobalConfig::GetVCoreDatabaseFilePath())); + + InstallerMainThreadSingleton::Instance().AttachDatabases(); + + _D("Prepare libxml2 to work in multithreaded program."); + xmlInitParser(); + + // Initialize Language Subtag registry + LanguageSubtagRstTreeSingleton::Instance().Initialize(); + + // Installer init + CONTROLLER_POST_SYNC_EVENT( + Logic::InstallerController, + InstallerControllerEvents:: + InitializeEvent()); + + if (callback) { + _D("[WRT-API] WRT INSTALLER INITIALIZATION CALLBACK"); + callback(WRT_SUCCESS, userdata); + } + } catch (const DPL::Exception& ex) { + _E("Internal Error during Init:"); + DPL::Exception::DisplayKnownException(ex); + if (callback) { + callback(WRT_INSTALLER_ERROR_FATAL_ERROR, userdata); + return; + } + } + return; +} + +void wrt_installer_shutdown() +{ + try { + _D("[WRT-API] DEINITIALIZING WRT INSTALLER..."); + + // Installer termination + CONTROLLER_POST_SYNC_EVENT( + Logic::InstallerController, + InstallerControllerEvents:: + TerminateEvent()); + + InstallerMainThreadSingleton::Instance().DetachDatabases(); + + // This must be done after DetachDatabase + ValidationCore::VCoreDeinit(); + + // Global deinit check + _D("Cleanup libxml2 global values."); + xmlCleanupParser(); + } catch (const DPL::Exception& ex) { + _E("Internal Error during Shutdown:"); + DPL::Exception::DisplayKnownException(ex); + } +} + +void wrt_install_widget( + const char *path, + const char *tzPkgId, + void* userdata, + WrtInstallerStatusCallback status_cb, + WrtProgressCallback progress_cb, + InstallMode installMode, + std::shared_ptr pkgmgrInterface + ) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + if (InstallMode::InstallTime::PRELOAD == installMode.installTime) { + DPL::Log::OldStyleLogProvider *oldStyleProvider = + new DPL::Log::OldStyleLogProvider(false, false, false, true, + false, true); + DPL::Log::LogSystemSingleton::Instance().AddProvider(oldStyleProvider); + } + + _D("[WRT-API] INSTALL WIDGET: %s", path); + // Post installation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::InstallWidgetEvent( + path, tzPkgId, Jobs::WidgetInstall::WidgetInstallationStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + userdata, status_cb, progress_cb), + installMode, + pkgmgrInterface))); + } + UNHANDLED_EXCEPTION_HANDLER_END +} + +void wrt_uninstall_widget( + const char * const tzAppid, + void* userdata, + WrtInstallerStatusCallback status_cb, + WrtProgressCallback progress_cb, + std::shared_ptr pkgmgrSignalInterface) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + std::string tizenAppid(tzAppid); + _D("[WRT-API] UNINSTALL WIDGET: %s", tizenAppid.c_str()); + // Post uninstallation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::UninstallWidgetEvent( + tizenAppid, + WidgetUninstallationStruct( + InstallerCallbacksTranslate::uninstallFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + userdata, status_cb, progress_cb), + pkgmgrSignalInterface + ) + ) + ); + } + UNHANDLED_EXCEPTION_HANDLER_END +} + +void wrt_install_plugin( + const char *pluginDir, + void *user_param, + WrtPluginInstallerStatusCallback status_cb, + WrtProgressCallback progress_cb) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + _D("[WRT-API] INSTALL PLUGIN: %s", pluginDir); + //Private data for status callback + //Resource is free in pluginInstallFinishedCallback + InstallerCallbacksTranslate::PluginStatusCallbackStruct* + callbackStruct = + new InstallerCallbacksTranslate::PluginStatusCallbackStruct( + user_param, status_cb, progress_cb); + + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::InstallPluginEvent( + std::string(pluginDir), + PluginInstallerStruct( + InstallerCallbacksTranslate:: + pluginInstallFinishedCallback, + InstallerCallbacksTranslate:: + installProgressCallback, callbackStruct))); + } + UNHANDLED_EXCEPTION_HANDLER_END +} diff --git a/src_mobile/wrt-installer/wrt_installer_api.h b/src_mobile/wrt-installer/wrt_installer_api.h new file mode 100644 index 0000000..eb2be3c --- /dev/null +++ b/src_mobile/wrt-installer/wrt_installer_api.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_installer_api.h + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief This file contains declarations of wrt_installer_api + */ + +#ifndef WRT_INSTALLER_API_H_ +#define WRT_INSTALLER_API_H_ + +#include +#include +#include +#include +#include +#include +#include + +typedef void (*WrtInstallerInitCallback)(WrtErrStatus status, + void *data); +typedef void (*WrtPluginInstallerStatusCallback)(WrtErrStatus status, + void *data); +typedef void (*WrtInstallerStatusCallback)(std::string tizenId, + WrtErrStatus status, + void *data); +typedef void (*WrtProgressCallback)(float percent, + const char *description, + void *data); + +void wrt_installer_init( + void *userdata, + WrtInstallerInitCallback callback); +void wrt_installer_shutdown(void); +void wrt_install_widget( + const char *path, + const char *tzPkgId, + void *user_parameter, + WrtInstallerStatusCallback status_callback, + WrtProgressCallback progress_callback, + InstallMode install_mode, + std::shared_ptr + pkgmgrInterface + ); +void wrt_uninstall_widget ( + const char * const tzAppid, + void* userdata, + WrtInstallerStatusCallback status_cb, + WrtProgressCallback progress_cb, + std::shared_ptr + pkgmgrSignalInterface); +void wrt_install_plugin(const char *pluginDirectory, + void *userData, + WrtPluginInstallerStatusCallback statusCallback, + WrtProgressCallback progressCallback); + +#endif /* WRT_INSTALLER_API_H_ */ diff --git a/src_mobile/wrt-installer/wrt_type.h b/src_mobile/wrt-installer/wrt_type.h new file mode 100644 index 0000000..2cfb584 --- /dev/null +++ b/src_mobile/wrt-installer/wrt_type.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_type.h + * @author jihoon Chung (jihoon.Chung@samsung.com) + * @version 1.0 + * @brief This file contains declarations of wrt api + */ + +/* + * @defgroup wrt_engine_group WebRunTime engine Library + * @ingroup internet_FW + * Functions to APIs to access wrt-engine + */ + +#ifndef WRT_TYPE_H_ +#define WRT_TYPE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define WRT_DEPRECATED __attribute__((deprecated)) + +typedef enum +{ + /* Generic success */ + WRT_SUCCESS = 0, /*< Success*/ + + /* pkgmgr error */ + WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND, ///< + WRT_INSTALLER_ERROR_PACKAGE_INVALID, ///< invalid widget package + WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION, ///< given version is lower than existing version + WRT_INSTALLER_ERROR_PACKAGE_EXCUTABLE_NOT_FOUND, + + WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND = 11,///< + WRT_INSTALLER_ERROR_MANIFEST_INVALID, ///< + WRT_INSTALLER_CONFIG_NOT_FOUND, ///< couldn't find config.xml + ///< in package. + WRT_INSTALLER_ERROR_CONFIG_INVALID, ///< invalid config.xml + + WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND = 21, ///< signature file not exist. + WRT_INSTALLER_ERROR_SIGNATURE_INVALID, ///< invalid signature file + WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED, ///< failure in verificate signature + WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND = 31, ///< couldn't find root certificate. + WRT_INSTALLER_ERROR_CERTIFICATION_INVAID, ///< invalid certification + WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED, ///< failure in verificate certification chain. + WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED, ///< expire cerification. + + WRT_INSTALLER_ERROR_INVALID_PRIVILEGE = 41, ///< invalid privilege. + WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION, + + WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND = 51, ///< + + WRT_INSTALLER_ERROR_FATAL_ERROR = 61, ///< failure in db operation or file opertion.. + WRT_INSTALLER_ERROR_OUT_OF_STORAGE, ///< failure in shortage of memory + WRT_INSTALLER_ERROR_OUT_OF_MEMORY, ///< failure in shortage of RAM + WRT_INSTALLER_ERROR_ARGUMENT_INVALID, + + /* wrt-installer error */ + /* 121-140 : reserved for Web installer */ + + /* installation */ + WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED = 121, + WRT_INSTALLER_ERROR_ACE_CHECK_FAILED, + WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED, ///< + WRT_INSTALLER_ERROR_ENCRYPTION_FAILED, ///< Failure in reousrce encrypttion + WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE, ///< Failure in installing osp service + WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED, + WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED, + + WRT_INSTALLER_ERROR_UNKNOWN = 140, ///< do not use this error code. + +} WrtErrStatus; + +#ifdef __cplusplus +} +#endif + +#endif /* WRT_TYPE_H_ */ diff --git a/src_wearable/CMakeLists.txt b/src_wearable/CMakeLists.txt new file mode 100755 index 0000000..ee25b20 --- /dev/null +++ b/src_wearable/CMakeLists.txt @@ -0,0 +1,186 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Lukasz Wrzosek (l.wrzosek@samsung.com) +# @version 1.0 +# + +SET(TARGET_INSTALLER "wrt-installer") + +OPTION(LB_SUPPORT "lb support" OFF) + +SET(INSTALLER_SRC_DIR + ${PROJECT_SOURCE_DIR}/src + ) + +SET(INSTALLER_CONFIG_PARSER + ${INSTALLER_SRC_DIR}/configuration_parser + ) + +SET(INSTALLER_JOBS + ${INSTALLER_SRC_DIR}/jobs + ) + +SET(INSTALLER_INCLUDES + ${INSTALLER_SRC_DIR} + ${INSTALLER_SRC_DIR}/logic + ${INSTALLER_SRC_DIR}/jobs + ${INSTALLER_SRC_DIR}/jobs/plugin_install + ${INSTALLER_SRC_DIR}/jobs/widget_install + ${INSTALLER_SRC_DIR}/jobs/widget_uninstall + ${INSTALLER_SRC_DIR}/misc + ${INSTALLER_SRC_DIR}/configuration_parser + ${INSTALLER_SRC_DIR}/wrt-installer + ${INSTALLER_SRC_DIR}/commons + ${INSTALLER_SRC_DIR}/pkg-manager +) + +SET(INSTALLER_SOURCES + ${INSTALLER_CONFIG_PARSER}/widget_parser.cpp + ${INSTALLER_CONFIG_PARSER}/parser_runner.cpp + ${INSTALLER_CONFIG_PARSER}/ignoring_parser.cpp + ${INSTALLER_CONFIG_PARSER}/deny_all_parser.cpp + ${INSTALLER_CONFIG_PARSER}/libiriwrapper.cpp + ${INSTALLER_JOBS}/job.cpp + ${INSTALLER_JOBS}/plugin_install/job_plugin_install.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_install_task.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_objects.cpp + ${INSTALLER_JOBS}/plugin_install/plugin_metafile_reader.cpp + ${INSTALLER_JOBS}/widget_install/job_widget_install.cpp + ${INSTALLER_JOBS}/widget_install/manifest.cpp + ${INSTALLER_JOBS}/widget_install/task_commons.cpp + ${INSTALLER_JOBS}/widget_install/task_process_config.cpp + ${INSTALLER_JOBS}/widget_install/task_database.cpp + ${INSTALLER_JOBS}/widget_install/task_configuration.cpp + ${INSTALLER_JOBS}/widget_install/ace_registration.cpp + ${INSTALLER_JOBS}/widget_install/task_file_manipulation.cpp + ${INSTALLER_JOBS}/widget_install/task_user_data_manipulation.cpp + ${INSTALLER_JOBS}/widget_install/task_smack.cpp + ${INSTALLER_JOBS}/widget_install/task_ace_check.cpp + ${INSTALLER_JOBS}/widget_install/task_manifest_file.cpp + ${INSTALLER_JOBS}/widget_install/task_certify.cpp + ${INSTALLER_JOBS}/widget_install/task_certify_level.cpp + ${INSTALLER_JOBS}/widget_install/task_prepare_files.cpp + ${INSTALLER_JOBS}/widget_install/task_recovery.cpp + ${INSTALLER_JOBS}/widget_install/task_install_ospsvc.cpp + ${INSTALLER_JOBS}/widget_install/task_update_files.cpp + ${INSTALLER_JOBS}/widget_install/task_remove_backup.cpp + ${INSTALLER_JOBS}/widget_install/task_encrypt_resource.cpp + ${INSTALLER_JOBS}/widget_install/task_prepare_reinstall.cpp + ${INSTALLER_JOBS}/widget_install/task_pkg_info_update.cpp + ${INSTALLER_JOBS}/widget_install/task_status_check.cpp + ${INSTALLER_JOBS}/widget_install/task_recovery.cpp + ${INSTALLER_JOBS}/widget_install/widget_security.cpp + ${INSTALLER_JOBS}/widget_install/directory_api.cpp + ${INSTALLER_JOBS}/widget_install/widget_unzip.cpp + ${INSTALLER_JOBS}/widget_uninstall/job_widget_uninstall.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_check.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_remove_files.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_remove_custom_handlers.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_db_update.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_smack.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_uninstall_ospsvc.cpp + ${INSTALLER_JOBS}/widget_uninstall/task_delete_pkginfo.cpp + ${INSTALLER_SRC_DIR}/logic/installer_logic.cpp + ${INSTALLER_SRC_DIR}/logic/installer_controller.cpp + ${INSTALLER_SRC_DIR}/misc/wac_widget_id.cpp + ${INSTALLER_SRC_DIR}/misc/feature_logic.cpp + ${INSTALLER_SRC_DIR}/misc/libxml_utils.cpp + ${INSTALLER_SRC_DIR}/misc/widget_location.cpp + ${INSTALLER_SRC_DIR}/misc/widget_install_to_external.cpp + ${INSTALLER_SRC_DIR}/misc/plugin_path.cpp + ${INSTALLER_SRC_DIR}/pkg-manager/pkgmgr_signal.cpp + ) + +IF(LB_SUPPORT) + SET(INSTALLER_SOURCES + ${INSTALLER_SOURCES} + ) + MESSAGE(STATUS "adding definition -DLB_SUPPORT") + ADD_DEFINITIONS("-DLB_SUPPORT") +ENDIF(LB_SUPPORT) + +MESSAGE(STATUS "add -DSEP_INSTALLER") +ADD_DEFINITIONS("-DSEP_INSTALLER") + + +PKG_CHECK_MODULES(INSTALLER_STATIC_DEP + dpl-efl + dpl-event-efl + dpl-utils-efl + dpl-wrt-dao-ro + dpl-wrt-dao-rw + wrt-commons-custom-handler-dao-rw + wrt-commons-security-origin-dao + wrt-commons-widget-interface-dao + wrt-plugins-types + pkgmgr-installer + pkgmgr-parser + pkgmgr-info + web-provider + libsmack + REQUIRED +) + +PKG_CHECK_MODULES(SYS_INSTALLER_STATIC_DEP + appsvc + libxml-2.0 + openssl + cert-svc-vcore + security-install + ecore-x + xmlsec1 + libidn + libiri + libpcrecpp + ail + elementary + tapi + shortcut + capi-appfw-app-manager + app2sd + libprivilege-control + REQUIRED +) + +INCLUDE_DIRECTORIES( SYSTEM ${SYS_INSTALLER_STATIC_DEP_INCLUDE_DIRS}) + +INCLUDE_DIRECTORIES( + ${INSTALLER_DEP_INCLUDES} + ${INSTALLER_INCLUDES} + ${INSTALLER_STATIC_DEP_INCLUDE_DIRS} + ${OSP_APPFW_INCLUDES} + ) + +ADD_LIBRARY(${TARGET_INSTALLER_STATIC} STATIC + ${INSTALLER_SOURCES} + ) + +ADD_DEFINITIONS(${INSTALLER_STATIC_DEP_CFLAGS}) +ADD_DEFINITIONS(${INSTALLER_STATIC_DEP_CFLAGS_OTHERS}) +ADD_DEFINITIONS(${SYS_INSTALLER_STATIC_DEP_CFLAGS}) +ADD_DEFINITIONS(${SYS_INSTALLER_STATIC_DEP_CFLAGS_OTHERS}) + +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER_STATIC} + ${INSTALLER_STATIC_DEP_LIBRARIES} "-ldl" + ${SYS_INSTALLER_STATIC_DEP_LIBRARIES} "-ldl" + ) + +#for encryption +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER_STATIC} "-lss-client" ) + +ADD_SUBDIRECTORY(pkg-manager) +ADD_SUBDIRECTORY(wrt-installer) diff --git a/src_wearable/DESCRIPTION b/src_wearable/DESCRIPTION new file mode 100644 index 0000000..0e8c571 --- /dev/null +++ b/src_wearable/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +Widget (un)installer, plugin (un)installer diff --git a/src_wearable/commons/installer_log.h b/src_wearable/commons/installer_log.h new file mode 100755 index 0000000..500f405 --- /dev/null +++ b/src_wearable/commons/installer_log.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_log.h + * @author Sungsu Kim(sung-su.kim@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef INSTALLER_LOG_H +#define INSTALLER_LOG_H + +#include +#include + +#include + +#ifdef WRT_INSTALLER_LOG + +#undef COLOR_WARNING +#define COLOR_WARNING "\e[0m" +#undef COLOR_TAG +#define COLOR_TAG "\e[0m" + +#endif + +// For FOTA debuging +#if 0 +#define PKGMGR_FOTA_PATH "/opt/share/packages/.recovery/fota/" +#define FOTA_RESULT_FILE PKGMGR_FOTA_PATH"result.txt" + +#define _FLOG(prio, fmt, arg...) do { \ + int __fd = 0;\ + FILE* __file = NULL;\ + __file = fopen(FOTA_RESULT_FILE, "a");\ + if (__file == NULL) break;\ + fprintf(__file, "[PKG_FOTA] [wrt-installer] [%s] [%s:%d] "fmt"\n", prio, __FUNCTION__, __LINE__, ##arg); \ + fflush(__file);\ + __fd = fileno(__file);\ + fsync(__fd);\ + fclose(__file);\ +} while (0) + +#undef _D +#define _D(fmt, arg ...) _FLOG("D", fmt, ##arg) + +#undef _W +#define _W(fmt, arg ...) _FLOG("W", fmt, ##arg) + +#undef _E +#define _E(fmt, arg ...) _FLOG("E", fmt, ##arg) +#endif + + +#endif // INSTALLER_LOG_H + diff --git a/src_wearable/commons/wrt_common_types.h b/src_wearable/commons/wrt_common_types.h new file mode 100644 index 0000000..cfed08f --- /dev/null +++ b/src_wearable/commons/wrt_common_types.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * plugin_common_types.h + * + * Author: Soyoung Kim(sy037.kim@samsung.com) + */ + +#ifndef PLUGIN_COMMON_TYPES_H +#define PLUGIN_COMMON_TYPES_H + +#include +#include +#include + +/** + * Widget version is optional + */ +typedef boost::optional OptionalWidgetVersion; + +/* Define db type */ +typedef WrtDB::DbWidgetFeature WidgetFeature; +typedef WrtDB::DbWidgetFeatureSet WidgetFeatureSet; + +typedef WrtDB::DbWidgetSize WidgetSize; + +typedef WrtDB::DbPluginHandle PluginHandle; + +enum InstallLocationType +{ + INSTALL_LOCATION_TYPE_UNKNOWN = 0, + INSTALL_LOCATION_TYPE_INTERNAL_ONLY, + INSTALL_LOCATION_TYPE_AUTO, + INSTALL_LOCATION_TYPE_PREFER_EXTERNAL, +}; + +#endif /* PLUGIN_COMMON_TYPES_H */ diff --git a/src_wearable/commons/wrt_error.h b/src_wearable/commons/wrt_error.h new file mode 100644 index 0000000..ae4f2de --- /dev/null +++ b/src_wearable/commons/wrt_error.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of the error codes of Widget. + * + * @file wrt_error.h + * @author MaQuan (jason.ma@samsung.com) + * @version 0.7 + * @brief This file contains the declaration of the error codes of Widget. + */ + +#ifndef _WRT_ERROR_H_ +#define _WRT_ERROR_H_ + +#ifndef WRT_ERROR_MASKL8 +#define WRT_ERROR_MASKL8 0xFF +#endif + +#ifndef WRT_SET_IDENT +#define WRT_SET_IDENT(X) (X & WRT_ERROR_MASKL8) +#endif + +#ifndef WRT_ERROR_SET +#define WRT_ERROR_SET(X) ((X & WRT_ERROR_MASKL8) << 8) +#endif + +#define WRT_MID_ERRCODE 0x10000 + WRT_SET_IDENT(5) + +/*typedef */ enum +{ + WRT_GENERAL_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(0), + WRT_CONFIG_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(1), + WRT_DOMAIN_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(2), + WRT_JS_EXT_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(3), + WRT_WM_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(4), + WRT_PLUGIN_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(5), + //_ACE support + WRT_SAI_ERRCODE = WRT_MID_ERRCODE + WRT_SET_IDENT(6) +}; + +/** + * WRT error code description + * + * @ WRT_SUCCESS + * There is no error with WRT operations. + * + * @ WRT_ERR_UNKNOW + * An unknow error happened to WRT. + * + * @ WRT_ERR_INVALID_ARG + * Invalid arguments are passed into WRT functions. + * + * @ WRT_ERR_OUT_MEMORY + * No memory space available for WRT. + * + * @ WRT_ERR_NO_DISK_SPACE + * There is no disk space for widget applications. + * + * + * + * + */ +enum WrtError +{ + /* General errors */ + WRT_SUCCESS = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x01), + WRT_ERR_UNKNOWN = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x02), + WRT_ERR_INVALID_ARG = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x03), + WRT_ERR_OUT_OF_MEMORY = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x04), + WRT_ERR_NO_DISK_SPACE = WRT_GENERAL_ERRCODE + WRT_ERROR_SET(0x05), + + /* Configuration */ + WRT_CONF_ERR_GCONF_FAILURE = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x01), + WRT_CONF_ERR_OBJ_MISSING = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x02), + WRT_CONF_ERR_OBJ_EXIST = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x03), + WRT_CONF_ERR_START_FILE_MISSING = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x04), + WRT_CONF_ERR_EMDB_FAILURE = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x05), + WRT_CONF_ERR_EMDB_NO_RECORD = WRT_CONFIG_ERRCODE + WRT_ERROR_SET(0x06), + + /* Domain */ + WRT_DOMAIN_ERR_CREATE_JS_RT = WRT_DOMAIN_ERRCODE + WRT_ERROR_SET(0x01), + WRT_DOMAIN_ERR_MSG_QUEUE = WRT_DOMAIN_ERRCODE + WRT_ERROR_SET(0x02), + + /* Widget manager*/ + WRT_WM_ERR_NOT_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x01), + WRT_WM_ERR_HIGH_VER_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x02), + WRT_WM_ERR_LOW_VER_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x03), + WRT_WM_ERR_INVALID_ARCHIVE = WRT_WM_ERRCODE + WRT_ERROR_SET(0x04), + WRT_WM_ERR_INVALID_CERTIFICATION = WRT_WM_ERRCODE + WRT_ERROR_SET(0x05), + WRT_WM_ERR_NULL_CERTIFICATION = WRT_WM_ERRCODE + WRT_ERROR_SET(0x06), + WRT_WM_ERR_INSTALLATION_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x07), + WRT_WM_ERR_ALREADY_INSTALLED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x08), + WRT_WM_ERR_INSTALL_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x09), + WRT_WM_ERR_DELETE_BY_SERVER = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0a), + WRT_WM_ERR_DEINSTALLATION_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0b), + WRT_WM_ERR_INCORRECT_UPDATE_INFO = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0c), + WRT_WM_ERR_UNREG_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0d), + WRT_WM_ERR_REMOVE_FILES_FAILED = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0e), + WRT_WM_ERR_ALREADY_LATEST = WRT_WM_ERRCODE + WRT_ERROR_SET(0x0f), + WRT_WM_ERR_UPDATE_CANCEL = WRT_WM_ERRCODE + WRT_ERROR_SET(0x10), + WRT_WM_ERR_INVALID_APP_ID = WRT_WM_ERRCODE + WRT_ERROR_SET(0x11), + + /* Access Control Manager */ + WRT_SAI_ERR_INIT_ACE_FAILED = WRT_SAI_ERRCODE + WRT_ERROR_SET(0x01) +}; + +namespace CommonError { +enum Type +{ + WrtSuccess, ///< Success + + HandleNotFound, ///< Widget handle was not found + AlreadyRunning, ///< Widget is already running + AlreadyStopped, ///< Widget is already stopped + InvalidLanguage, ///< Widget is invalid in current locales + StillAuthorizing, ///< Widget is still autorizing and has not yet + // finished it + EarlyKilled, ///< Widget was early killed during launch + AccessDenied, ///< Access denied from ACE + CertificateRevoked, ///< Some certificate was revoked. + /// Widget is not allowed to run. + + Unknown ///< Temporary error. Try to not use this. +}; +} +#endif /* _WRT_ERROR_H_ */ + diff --git a/src_wearable/commons/wrt_install_mode.h b/src_wearable/commons/wrt_install_mode.h new file mode 100644 index 0000000..d6462c6 --- /dev/null +++ b/src_wearable/commons/wrt_install_mode.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_install_mode.h + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief Definition file of widget install mode class + */ + +#ifndef WRT_INSTALL_MODE_H +#define WRT_INSTALL_MODE_H + +class InstallMode +{ + public: + enum class Command + { + INSTALL, + REINSTALL, + RECOVERY + }; + enum class Location + { + INTERNAL, + EXTERNAL + }; + enum class RootPath + { + RW, + RO + }; + enum class ExtensionType + { + WGT, + DIR + }; + enum class InstallTime + { + NORMAL, + CSC, + PRELOAD, + FOTA, + }; + + InstallMode(Command cmd = Command::INSTALL, + Location lo = Location::INTERNAL, + RootPath root = RootPath::RW, + ExtensionType extensionType = ExtensionType::WGT, + InstallTime time = InstallTime::NORMAL) : + command(cmd), + location(lo), + rootPath(root), + extension(extensionType), + installTime(time), + removable(true) + {}; + + Command command; + Location location; + RootPath rootPath; + ExtensionType extension; + InstallTime installTime; + bool removable; + std::string cscPath; +}; + +#endif // WRT_INSTALL_MODE_H + diff --git a/src_wearable/configuration_parser/deny_all_parser.cpp b/src_wearable/configuration_parser/deny_all_parser.cpp new file mode 100644 index 0000000..e807d5e --- /dev/null +++ b/src_wearable/configuration_parser/deny_all_parser.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "deny_all_parser.h" +#include + +DenyAllParser::DenyAllParser() : ElementParser() +{} + +ElementParserPtr DenyAllParser::Create() +{ + ThrowMsg(Exception::ParseError, "There must not be any subelement"); +} + +ElementParser::ActionFunc DenyAllParser::GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any subelement"); +} + +void DenyAllParser::Accept(const Element& /*element*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any element"); +} + +void DenyAllParser::Accept(const XmlAttribute& /*attribute*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any attribute"); +} + +void DenyAllParser::Accept(const Text& /*text*/) +{ + ThrowMsg(Exception::ParseError, "There must not be any text element"); +} diff --git a/src_wearable/configuration_parser/deny_all_parser.h b/src_wearable/configuration_parser/deny_all_parser.h new file mode 100644 index 0000000..d9dfe56 --- /dev/null +++ b/src_wearable/configuration_parser/deny_all_parser.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file deny_all_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef DENY_ALL_PARSER_H +#define DENY_ALL_PARSER_H + +#include "element_parser.h" + +struct DenyAllParser : public ElementParser +{ + static ElementParserPtr Create(); + virtual void Accept(const Element& /*element*/); + virtual void Accept(const XmlAttribute& /*attribute*/); + virtual void Accept(const Text& /*text*/); + virtual void Verify() + {} + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + + DenyAllParser(); +}; + +#endif // DENY_ALL_PARSER_H diff --git a/src_wearable/configuration_parser/element_parser.h b/src_wearable/configuration_parser/element_parser.h new file mode 100644 index 0000000..c4ea5ba --- /dev/null +++ b/src_wearable/configuration_parser/element_parser.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file element_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef ELEMENT_PARSER_H_ +#define ELEMENT_PARSER_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct XmlAttribute +{ + DPL::String prefix; + DPL::String name; + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +struct Element +{ + DPL::String name; + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +struct Text +{ + DPL::String value; + DPL::String ns; + DPL::String lang; +}; + +class ElementParser; + +typedef std::shared_ptr ElementParserPtr; + +class ElementParser : public std::enable_shared_from_this +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ParseError) + }; + typedef std::function ActionFunc; + typedef std::map FuncMap; + + virtual void Accept(const Element&) = 0; + virtual void Accept(const XmlAttribute&) = 0; + virtual void Accept(const Text&) = 0; + virtual void Verify() = 0; + virtual ActionFunc GetElementParser(const DPL::String &ns, + const DPL::String &name) = 0; + virtual ~ElementParser() + {} + + protected: + ElementParser() + {} +}; + +#endif // ELEMENT_PARSER_H_ diff --git a/src_wearable/configuration_parser/ignoring_parser.cpp b/src_wearable/configuration_parser/ignoring_parser.cpp new file mode 100644 index 0000000..087e6d5 --- /dev/null +++ b/src_wearable/configuration_parser/ignoring_parser.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ignoring_parser.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include "ignoring_parser.h" + +#include +#include + +IgnoringParser::IgnoringParser() : ElementParser() +{} + +ElementParserPtr IgnoringParser::Create() +{ + return ElementParserPtr(new IgnoringParser()); +} + +ElementParserPtr IgnoringParser::Reuse() +{ + return shared_from_this(); +} + +ElementParser::ActionFunc IgnoringParser::GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) +{ + return std::bind(&IgnoringParser::Reuse, this); +} + +void IgnoringParser::Accept(const Element& /*element*/) +{} + +void IgnoringParser::Accept(const Text& /*text*/) +{} + +void IgnoringParser::Accept(const XmlAttribute& /*attribute*/) +{} + +void IgnoringParser::Verify() +{} diff --git a/src_wearable/configuration_parser/ignoring_parser.h b/src_wearable/configuration_parser/ignoring_parser.h new file mode 100644 index 0000000..b14f1ad --- /dev/null +++ b/src_wearable/configuration_parser/ignoring_parser.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ignoring_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef IGNORING_PARSER_H_ +#define IGNORING_PARSER_H_ + +#include "element_parser.h" + +struct IgnoringParser : public ElementParser +{ + static ElementParserPtr Create(); + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + virtual void Accept(const Element&); + virtual void Accept(const Text&); + virtual void Accept(const XmlAttribute&); + virtual void Verify(); + + IgnoringParser(); + + private: + ElementParserPtr Reuse(); +}; + +#endif // IGNORING_PARSER_H_ diff --git a/src_wearable/configuration_parser/libiriwrapper.cpp b/src_wearable/configuration_parser/libiriwrapper.cpp new file mode 100644 index 0000000..6d8de2a --- /dev/null +++ b/src_wearable/configuration_parser/libiriwrapper.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libiriwrapper.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com + * @version 0.1 + * @brief Libiri parser wrapper + */ +#include +#include +#include "libiriwrapper.h" + +//TODO: Design and implement new IRI manager class + +namespace LibIri { +Wrapper::Wrapper(const char* aIri) : m_Iri(iri_parse(aIri)) +{} +Wrapper::~Wrapper() +{ + iri_destroy(m_Iri); +} +//! \brief Returns true if iri is valid +bool Wrapper::Validate() +{ + return + m_Iri != NULL && + m_Iri->scheme != NULL && ( + m_Iri->display != NULL || + m_Iri->user != NULL || + m_Iri->auth != NULL || + m_Iri->password != NULL || + m_Iri->host != NULL || + m_Iri->path != NULL || + m_Iri->query != NULL || + m_Iri->anchor != NULL || + m_Iri->qparams != NULL || + m_Iri->schemelist != NULL); +} + +std::ostream & operator<<(std::ostream& a_stream, + const Wrapper& a_wrapper) +{ + iri_t& iri = *a_wrapper.m_Iri; +#define PRINT_FIELD(field) "] " #field " [" << (iri.field ? iri.field : "null") + a_stream << + " display [" << (iri.display ? iri.display : "null") << + PRINT_FIELD(scheme) << + PRINT_FIELD(user) << + PRINT_FIELD(auth) << + PRINT_FIELD(password) << + PRINT_FIELD(host) << + "] port [" << (iri.port ? iri.port : -1) << + PRINT_FIELD(path) << + PRINT_FIELD(query) << + PRINT_FIELD(anchor) << + "]"; + return a_stream; +#undef PRINT_FIELD +} +} //namespace LibIri diff --git a/src_wearable/configuration_parser/libiriwrapper.h b/src_wearable/configuration_parser/libiriwrapper.h new file mode 100644 index 0000000..b0b3e86 --- /dev/null +++ b/src_wearable/configuration_parser/libiriwrapper.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libiriwrapper.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com + * @version 0.1 + * @brief Libiri parser wrapper + */ + +#ifndef _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ +#define _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ + +#include +#include + +//TODO: Design and implement new IRI manager class +// +namespace LibIri { +struct Wrapper +{ + Wrapper(const char* aIri); + ~Wrapper(); + iri_t *m_Iri; + //! \brief Returns true if iri is valid + bool Validate(); +}; + +std::ostream & operator<<(std::ostream& a_stream, + const Wrapper& a_wrapper); +} //namespace LibIri + +#endif // _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_LIBIRIWRAPPER_H_ + diff --git a/src_wearable/configuration_parser/parser_runner.cpp b/src_wearable/configuration_parser/parser_runner.cpp new file mode 100644 index 0000000..237d984 --- /dev/null +++ b/src_wearable/configuration_parser/parser_runner.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file parser_runner.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include "parser_runner.h" +#include "root_parser.h" + +#include "stdio.h" + +#include +#include +#include +#include +#include + +#include + +class ParserRunner::Impl +{ + static void logErrorLibxml2(void *, const char *msg, ...) + { + char buffer[300]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, 300, msg, args); + va_end(args); + _E("%s", buffer); + } + + static void logWarningLibxml2(void *, const char *msg, ...) + { + char buffer[300]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, 300, msg, args); + va_end(args); + _W("%s", buffer); + } + + public: + bool Validate(const std::string& filename, const std::string& schema) + { + int ret = -1; + xmlSchemaParserCtxtPtr ctx; + xmlSchemaValidCtxtPtr vctx; + xmlSchemaPtr xschema; + ctx = xmlSchemaNewParserCtxt(schema.c_str()); + if (ctx == NULL) { + _E("xmlSchemaNewParserCtxt() Failed"); + return false; + } + xschema = xmlSchemaParse(ctx); + if (xschema == NULL) { + _E("xmlSchemaParse() Failed"); + return false; + } + vctx = xmlSchemaNewValidCtxt(xschema); + if (vctx == NULL) { + _E("xmlSchemaNewValidCtxt() Failed"); + return false; + } + xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&logErrorLibxml2, (xmlSchemaValidityWarningFunc) &logWarningLibxml2, NULL); + ret = xmlSchemaValidateFile(vctx, filename.c_str(), 0); + if (ret == -1) { + _E("xmlSchemaValidateFile() failed"); + return false; + } else if (ret == 0) { + _E("Config is Valid"); + return true; + } else { + _E("Config Validation Failed with error code %d", ret); + return false; + } + return true; + } + + void Parse(const std::string& filename, + const ElementParserPtr& root) + { + DPL::FileInput input(filename); + Parse(&input, root); + } + + void Parse (DPL::AbstractInput *input, + const ElementParserPtr& root) + { + Try + { + m_reader = xmlReaderForIO(&IoRead, + &IoClose, + input, + NULL, + NULL, + XML_PARSE_NOENT); + + xmlTextReaderSetErrorHandler(m_reader, + &xmlTextReaderErrorHandler, + this); + xmlTextReaderSetStructuredErrorHandler( + m_reader, + &xmlTextReaderStructuredErrorHandler, + this); + SetCurrentElementParser(root); + + while (xmlTextReaderRead(m_reader) == 1) { + switch (xmlTextReaderNodeType(m_reader)) { + case XML_READER_TYPE_END_ELEMENT: + VerifyAndRemoveCurrentElementParser(); + break; + + case XML_READER_TYPE_ELEMENT: + { + // Elements without closing tag don't receive + // XML_READER_TYPE_END_ELEMENT event. + if (IsNoClosingTagElementLeft()) { + VerifyAndRemoveCurrentElementParser(); + } + + DPL::String elementName = GetNameWithoutNamespace(); + DPL::String nameSpace = GetNamespace(); + ElementParserPtr parser = GetCurrentElementParser(); + parser = parser->GetElementParser(nameSpace, + elementName) (); + Assert(!!parser); + SetCurrentElementParser(parser); + ParseNodeElement(parser); + break; + } + case XML_READER_TYPE_TEXT: + case XML_READER_TYPE_CDATA: + { + ParseNodeText(GetCurrentElementParser()); + break; + } + default: + _W("Ignoring Node of Type: %d", xmlTextReaderNodeType(m_reader)); + break; + } + + if (m_parsingError) { + _E("Parsing error occured: %ls", m_errorMsg.c_str()); + ThrowMsg(ElementParser::Exception::ParseError, m_errorMsg); + } + } + + if (m_parsingError) { + _E("Parsing error occured: %ls", m_errorMsg.c_str()); + ThrowMsg(ElementParser::Exception::ParseError, m_errorMsg); + } + + while (!m_stack.empty()) { + VerifyAndRemoveCurrentElementParser(); + } + } + Catch(ElementParser::Exception::Base) + { + CleanupParserRunner(); + _E("%s", _rethrown_exception.DumpToString().c_str()); + ReThrow(ElementParser::Exception::ParseError); + } + CleanupParserRunner(); + } + + Impl() : + m_reader(NULL), + m_parsingError(false) + {} + + ~Impl() + { + CleanupParserRunner(); + } + + private: + typedef std::stack ElementStack; + + private: + static void xmlTextReaderErrorHandler(void* arg, + const char* msg, + xmlParserSeverities /* severity */, + xmlTextReaderLocatorPtr /* locator */) + { + ParserRunner::Impl* impl = static_cast(arg); + impl->ErrorHandler(DPL::FromASCIIString(msg)); + } + + static void xmlTextReaderStructuredErrorHandler(void* arg, + xmlErrorPtr error) + { + ParserRunner::Impl* impl = static_cast(arg); + impl->StructuredErrorHandler(error); + } + + static int XMLCALL IoRead(void *context, + char *buffer, + int len) + { + DPL::AbstractInput *input = static_cast(context); + DPL::BinaryQueueAutoPtr data = input->Read(static_cast(len)); + if (!data.get()) { + return -1; + } + data->Flatten(buffer, data->Size()); + return static_cast(data->Size()); + } + + static int XMLCALL IoClose(void */* context */) + { + // NOOP + return 0; + } + + private: + void SetCurrentElementParser(const ElementParserPtr& elementParser) + { + Assert(elementParser); + + m_stack.push(elementParser); + } + + const ElementParserPtr& GetCurrentElementParser() const + { + Assert(!m_stack.empty()); + + return m_stack.top(); + } + + void VerifyAndRemoveCurrentElementParser() + { + Assert(!m_stack.empty()); + + m_stack.top()->Verify(); + m_stack.pop(); + } + + bool IsNoClosingTagElementLeft() const + { + Assert(m_reader); + + int depth = xmlTextReaderDepth(m_reader); + return (static_cast(m_stack.size()) - 2 == depth); + } + + void ParseNodeElement(const ElementParserPtr& parser) + { + Assert(m_reader); + + Element element; + element.name = GetName(); + element.value = GetValue(); + element.lang = GetLanguageTag(); + element.ns = GetNamespace(); + + _D("value: %ls, lang: %ls, ns: %ls)", + element.value.c_str(), element.lang.c_str(), element.ns.c_str()); + + parser->Accept(element); + ParseNodeElementAttributes(parser); + } + + void ParseNodeElementAttributes(const ElementParserPtr& parser) + { + Assert(m_reader); + int count = xmlTextReaderAttributeCount(m_reader); + for (int i = 0; i < count; ++i) { + xmlTextReaderMoveToAttributeNo(m_reader, i); + + XmlAttribute attribute; + attribute.ns = GetAttributeNamespace(); + attribute.prefix = GetNamePrefix(); + attribute.name = GetNameWithoutNamespace(); + attribute.value = GetValue(); + attribute.lang = GetLanguageTag(); + _D("Attribute name: %ls, value: %ls, prefix: %ls, namespace: %ls, lang: %ls", + attribute.name.c_str(), attribute.value.c_str(), attribute.prefix.c_str(), + attribute.ns.c_str(), attribute.lang.c_str()); + parser->Accept(attribute); + } + } + + void ParseNodeText(const ElementParserPtr& parser) + { + Text text; + text.value = GetValue(); + text.lang = GetLanguageTag(); + parser->Accept(text); + } + + DPL::String GetValue() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstValue(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetAttributeValue(int pos) const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderGetAttributeNo(m_reader, pos); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + xmlFree(const_cast(value)); + + return ret_value; + } + + DPL::String GetAttributeNamespace() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderLookupNamespace(m_reader, NULL); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + xmlFree(const_cast(value)); + + return ret_value; + } + + DPL::String GetName() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstName(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNamePrefix() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderPrefix(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNameWithoutNamespace() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderLocalName(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetNamespace() const + { + DPL::String ret_value; + + const xmlChar* value = xmlTextReaderConstNamespaceUri(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + DPL::String GetLanguageTag() const + { + DPL::String ret_value; + const xmlChar* value = xmlTextReaderConstXmlLang(m_reader); + if (value) { + ret_value = + DPL::FromUTF8String(reinterpret_cast(value)); + } + + return ret_value; + } + + void ErrorHandler(const DPL::String& msg) + { + _E("LibXML: %ls", msg.c_str()); + m_parsingError = true; + m_errorMsg = m_errorMsg + DPL::FromASCIIString("\n"); + m_errorMsg = m_errorMsg + msg; + } + + void StructuredErrorHandler(xmlErrorPtr error) + { + _E("LibXML: %s", error->message); + m_parsingError = true; + m_errorMsg = m_errorMsg + DPL::FromASCIIString("\n"); + m_errorMsg = m_errorMsg + DPL::FromUTF8String(error->message); + } + + void CleanupParserRunner() + { + while (!m_stack.empty()) { + m_stack.pop(); + } + if (m_reader) { + xmlFreeTextReader(m_reader); + } + m_reader = NULL; + } + + private: + xmlTextReaderPtr m_reader; + ElementStack m_stack; + bool m_parsingError; + DPL::String m_errorMsg; +}; + +ParserRunner::ParserRunner() : + m_impl(new ParserRunner::Impl()) +{} + +bool ParserRunner::Validate(const std::string& filename, const std::string& schema) +{ + return m_impl->Validate(filename, schema); +} + +void ParserRunner::Parse(const std::string& filename, + ElementParserPtr root) +{ + m_impl->Parse(filename, root); +} + +void ParserRunner::Parse(DPL::AbstractInput *input, + ElementParserPtr root) +{ + m_impl->Parse(input, root); +} + +ParserRunner::~ParserRunner() +{ + delete m_impl; +} diff --git a/src_wearable/configuration_parser/parser_runner.h b/src_wearable/configuration_parser/parser_runner.h new file mode 100644 index 0000000..c5c0714 --- /dev/null +++ b/src_wearable/configuration_parser/parser_runner.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file parser_runner.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef PARSER_RUNNER_H_ +#define PARSER_RUNNER_H_ + +#include +#include +#include +#include "element_parser.h" + +class ParserRunner : private DPL::Noncopyable +{ + public: + bool Validate(const std::string& filename, const std::string& schema); + + void Parse(const std::string& filename, + ElementParserPtr root); + void Parse(DPL::AbstractInput *input, + ElementParserPtr root); + + ParserRunner(); + ~ParserRunner(); + + private: + class Impl; + Impl* m_impl; +}; + +#endif // PARSER_RUNNER_H_ + diff --git a/src_wearable/configuration_parser/root_parser.h b/src_wearable/configuration_parser/root_parser.h new file mode 100755 index 0000000..a54b30d --- /dev/null +++ b/src_wearable/configuration_parser/root_parser.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file root_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ +#define _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ + +#include + +#include "element_parser.h" + +template +class RootParser : public ElementParser +{ + public: + typedef typename ta_Parser::Data Data; + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == m_tag) { + return std::bind(&RootParser::OnWidgetElement, this); + } else { + ThrowMsg(Exception::ParseError, + name << " != " << m_tag); + } + } + + RootParser(Data data, const DPL::String& tag) : + m_data(data), + m_tag(tag) + {} + + virtual ~RootParser() {} + + virtual void Accept(const Element& /*element*/) { } + + virtual void Accept(const XmlAttribute& /*attribute*/) { } + + virtual void Accept(const Text& /*text*/) { } + + virtual void Verify() { } + + private: + + ElementParserPtr OnWidgetElement() + { + typedef ta_Parser Parser; + return ElementParserPtr(new Parser(this->m_data)); + } + + Data m_data; + const DPL::String& m_tag; +}; + +#endif // _WRT_ENGINE_SRC_INSTALLERCORE_CONFIGURATION_PARSER_ROOT_PARSER_H_ diff --git a/src_wearable/configuration_parser/widget_parser.cpp b/src_wearable/configuration_parser/widget_parser.cpp new file mode 100755 index 0000000..db5c3fa --- /dev/null +++ b/src_wearable/configuration_parser/widget_parser.cpp @@ -0,0 +1,3471 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file have been implemented in compliance with W3C WARP SPEC. + * but there are some patent issue between W3C WARP SPEC and APPLE. + * so if you want to use this file, refer to the README file in root directory + */ +/** + * @file widget_parser.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include "ignoring_parser.h" +#include "deny_all_parser.h" +#include +#include "libiriwrapper.h" +#include "wrt-commons/i18n-dao-ro/i18n_dao_read_only.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Unicode { +static const DPL::String UTF_LRE = L"\x0202a"; +static const DPL::String UTF_LRO = L"\x0202d"; +static const DPL::String UTF_RLE = L"\x0202b"; +static const DPL::String UTF_RLO = L"\x0202e"; +static const DPL::String UTF_PDF = L"\x0202c"; + +Direction ParseDirAttribute(const XmlAttribute& attribute) +{ + Assert(L"dir" == attribute.name); + if (L"ltr" == attribute.value) { + return LRE; + } else if (L"rtl" == attribute.value) { + return RLE; + } else if (L"lro" == attribute.value) { + return LRO; + } else if (L"rlo" == attribute.value) { + return RLO; + } else { + _W("dir attribute has wrong value: %ls ", attribute.value.c_str()); + return EMPTY; + } +} + +void UpdateTextWithDirectionMark(Direction direction, + DPL::String* text) +{ + Assert(text); + switch (direction) { + case RLO: + *text = UTF_RLO + *text + UTF_PDF; + break; + case RLE: + *text = UTF_RLE + *text + UTF_PDF; + break; + case LRE: + *text = UTF_LRE + *text + UTF_PDF; + break; + case LRO: + *text = UTF_LRO + *text + UTF_PDF; + break; + case EMPTY: + break; + default: + Assert(false); + break; + } +} +} // namespace Unicode + +class InnerElementsParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&InnerElementsParser::Other, this); + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& text) + { + if (!m_text) { + m_text = text; + } else { + m_text->value += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + if (!!m_text) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &m_text->value); + m_parentParser->Accept(*m_text); + } + } + + InnerElementsParser(ElementParserPtr parent) : + m_parentParser(parent), + m_textDirection(Unicode::EMPTY) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + boost::optional m_text; + ElementParserPtr m_parentParser; + Unicode::Direction m_textDirection; +}; + +class NameParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&NameParser::Other, this); + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_name = L""; + } + + virtual void Accept(const Text& text) + { + if (!m_name) { + m_name = text.value; + } else { + *m_name += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"short") { + if (!m_shortName) { + m_shortName = attribute.value; + } + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (!data.name) { + NormalizeString(m_name); + NormalizeString(m_shortName); + if (!!m_name) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, &*m_name); + } + data.name = m_name; + if (!!m_shortName) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_shortName); + } + data.shortName = m_shortName; + } + } + + NameParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_textDirection(direction) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_name; + DPL::OptionalString m_shortName; + DPL::OptionalString m_dir; + DPL::String m_lang; + Unicode::Direction m_textDirection; +}; + +class AccessParser : public ElementParser +{ + public: + enum StandardType + { + STANDARD_TYPE_NONE, + STANDARD_TYPE_WARP + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&AccessParser::Other, this); + } + + virtual void Accept(const Element& element) + { + // for tizen web apps WARP should be used + if (element.ns == ConfigurationNamespace::W3CWidgetNamespaceName || + element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_standardType = STANDARD_TYPE_WARP; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + void AcceptWac(const XmlAttribute& attribute) + { + if (attribute.name == L"origin") { + m_strIRIOrigin = attribute.value; + NormalizeString(m_strIRIOrigin); + } else if (attribute.name == L"subdomains") { + DPL::String normalizedValue = attribute.value; + NormalizeString(normalizedValue); + + if (normalizedValue == L"true") { + m_bSubDomainAccess = true; + } else if (normalizedValue == L"false") { + m_bSubDomainAccess = false; + } + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + switch (m_standardType) { + case STANDARD_TYPE_WARP: + AcceptWac(attribute); + break; + default: + _E("Error in Access tag - unknown standard."); + break; + } + } + + void VerifyWac() + { + WarpIRI iri; + iri.set(m_strIRIOrigin, false); + + if (!iri.isAccessDefinition()) { + _W("Access list element: %ls is not acceptable by WARP standard and will be ignored!", + m_strIRIOrigin.c_str()); + return; + } + + if(m_strIRIOrigin == L"*") //wildcard match means yes for subdomains + { + m_bSubDomainAccess = true; + } + + ConfigParserData::AccessInfo accessInfo(m_strIRIOrigin, + m_bSubDomainAccess); + //std::pair ret = + m_data.accessInfoSet.insert(accessInfo); + } + + virtual void Verify() + { + switch (m_standardType) { + case STANDARD_TYPE_WARP: + VerifyWac(); + break; + default: + _E("Error in Access tag - unknown standard."); + break; + } + } + + AccessParser(ConfigParserData& data) : + ElementParser(), + m_bSubDomainAccess(false), + m_standardType(STANDARD_TYPE_NONE), + m_network(false), + m_data(data) + {} + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + ElementParserPtr(shared_from_this()))); + } + + private: + DPL::String m_strIRIOrigin; + bool m_bSubDomainAccess; + StandardType m_standardType; + bool m_network; + ConfigParserData& m_data; +}; + +class DescriptionParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&DescriptionParser::Other, this); + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_description = L""; + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + virtual void Accept(const Text& text) + { + if (!m_description) { + m_description = text.value; + } else { + *m_description += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (!data.description) { + if (!!m_description) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_description); + } + data.description = m_description; + } + } + + DescriptionParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_lang(), + m_description(), + m_textDirection(direction) + {} + + private: + ConfigParserData& m_data; + DPL::String m_lang; + DPL::OptionalString m_description; + Unicode::Direction m_textDirection; +}; + +class AuthorParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&AuthorParser::Other, this); + } + + AuthorParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_textDirection(direction) + {} + + virtual void Accept(const Element& /*element*/) + { + m_authorName = L""; + } + + virtual void Accept(const Text& text) + { + *(m_authorName) += text.value; + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"href") { + //Validate href IRI and ignore it if invalid + //See also test: ta-argMozRiC-an + LibIri::Wrapper iri(DPL::ToUTF8String(attribute.value).c_str()); + if (iri.Validate()) { + m_authorHref = attribute.value; + } + } else if (attribute.name == L"email") { + m_authorEmail = attribute.value; + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + + virtual void Verify() + { + if (!m_data.authorName && !m_data.authorHref && !m_data.authorEmail) { + NormalizeString(m_authorName); + NormalizeString(m_authorHref); + NormalizeString(m_authorEmail); + if (!!m_authorName) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_authorName); + m_data.authorName = m_authorName; + } + if (!!m_authorHref) { + m_data.authorHref = m_authorHref; + } + if (!!m_authorEmail) { + m_data.authorEmail = m_authorEmail; + } + } + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + std::static_pointer_cast( + shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_authorEmail; + DPL::OptionalString m_authorHref; + DPL::OptionalString m_authorName; + Unicode::Direction m_textDirection; +}; + +class LicenseParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return std::bind(&LicenseParser::Other, this); + } + + LicenseParser(Unicode::Direction direction, + ConfigParserData& data) : + m_data(data), + m_ignore(true), + m_textDirection(direction) + {} + + virtual void Accept(const Element& element) + { + if (!m_license) { + m_lang = element.lang; + m_license = L""; + m_ignore = false; + } + } + + virtual void Accept(const Text& text) + { + if (!m_ignore) { + *m_license += text.value; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (!m_ignore) { + if (attribute.name == L"href" && !m_licenseHref) { + m_licenseHref = attribute.value; + } else if (attribute.name == L"file" && !m_licenseFile) { + m_licenseFile = attribute.value; + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } + } + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_data.localizedDataSet[m_lang]; + if (!data.license) { + if (!!m_license) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, + &*m_license); + } + data.license = m_license; + data.licenseHref = m_licenseHref; + data.licenseFile = m_licenseFile; + } + } + + ElementParserPtr Other() + { + return ElementParserPtr(new InnerElementsParser( + ElementParserPtr(shared_from_this()))); + } + + private: + ConfigParserData& m_data; + DPL::String m_lang; + bool m_ignore; + + DPL::OptionalString m_license; + DPL::OptionalString m_licenseFile; + DPL::OptionalString m_licenseHref; + Unicode::Direction m_textDirection; +}; + +class IconParser : public ElementParser +{ + DECLARE_EXCEPTION_TYPE(ElementParser::Exception::ParseError, BadSrcError) + + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + IconParser(ConfigParserData& data) : ElementParser(), + m_data(data), m_isSmall(false) + {} + + IconParser(ConfigParserData& data, bool isSmall) : ElementParser(), + m_data(data), m_isSmall(isSmall) + {} + + virtual void Accept(const Element& /*element*/) { } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } else if (attribute.name == L"width") { + m_width = ParseSizeAttributeValue(attribute.value); + } else if (attribute.name == L"height") { + m_height = ParseSizeAttributeValue(attribute.value); + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "Icon element must be empty"); + } + + virtual void Verify() + { + if (!m_src) { + _W("src attribute of icon element is mandatory - ignoring"); + return; + } + + Try + { + ConfigParserData::Icon icon(delocalizeSrcPath(*m_src)); + icon.width = m_width; + icon.height = m_height; + icon.isSmall = m_isSmall; + + ConfigParserData::IconsList::iterator it = std::find( + m_data.iconsList.begin(), m_data.iconsList.end(), icon); + if (it == m_data.iconsList.end()) { + m_data.iconsList.push_front(icon); + } + } + Catch(BadSrcError) + { + _W("src attribute is invalid: %ls", (*m_src).c_str()); + } + } + + private: + ConfigParserData& m_data; + DPL::OptionalString m_src; + DPL::OptionalInt m_width; + DPL::OptionalInt m_height; + bool m_isSmall; + + static DPL::OptionalInt ParseSizeAttributeValue(const DPL::String& value) + { + DPL::OptionalString normalizedValue = value; + NormalizeString(normalizedValue); + if (!(*normalizedValue).empty()) { + char* reterr = NULL; + errno = 0; + long int valueInt = + strtol(DPL::ToUTF8String(value).c_str(), &reterr, 10); + if (errno != 0 || + std::string(reterr) == DPL::ToUTF8String(value) || + valueInt <= 0) + { + return DPL::OptionalInt(); + } else { + return valueInt; + } + } + return DPL::OptionalInt(); + } + + /** + * @brief delocalizePath removes locales folder from relative path if + * neccessary + * @param source source string + * + * @throw BadSrcError if string is bad value of src attribute + * + * @return corrected string + */ + static DPL::String delocalizeSrcPath(const DPL::String & source) + { + static const DPL::String localeFolder(L"locales/"); + static const int index = localeFolder.size(); + + DPL::String result = source; + + if (source.substr(0, index) == localeFolder) { + size_t pos = result.find_first_of('/', index); + if (pos != std::string::npos && pos + 1 < source.size()) { + result = result.substr(pos + 1, source.size()); + } else { + Throw(BadSrcError); + } + } + return result; + } +}; + +class ContentParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + ContentParser(ConfigParserData& data) : + ElementParser(), + m_data(data) + {} + + virtual void Accept(const Element& element) + { + m_namespace = element.ns; + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + DPL::String value = attribute.value; + NormalizeString(value); + + if (attribute.name == L"src") { + m_src = value; + } else if (attribute.name == L"type") { + m_type = value; + MimeTypeUtils::MimeAttributes mimeAttributes = + MimeTypeUtils::getMimeAttributes(value); + if ((mimeAttributes.count(L"charset") > 0) && !m_encoding) + { + m_encoding = mimeAttributes[L"charset"]; + } + } else if (attribute.name == L"encoding") { + if (!value.empty()) { + m_encoding = value; + } + } + } + + virtual void Verify() + { + if(!!m_data.startFileEncountered) + { + if(m_data.startFileNamespace == m_namespace + || m_namespace != ConfigurationNamespace::TizenWebAppNamespaceName) + { + return; + } + //else continue -> if previous item was not in tizen namespace + } + + m_data.startFileEncountered = true; + m_data.startFileNamespace = m_namespace; + + if (m_namespace == ConfigurationNamespace::TizenWebAppNamespaceName && + (!m_src || m_src->empty())) { + ThrowMsg(Exception::ParseError, "content element must have correct src element"); + } + + if (!!m_src) { + m_data.startFile = m_src; + m_data.startFileContentType = m_type; + if (!!m_encoding) { + m_data.startFileEncoding = m_encoding; + } else { + m_data.startFileEncoding = L"UTF-8"; + } + } + } + + private: + DPL::OptionalString m_src; + DPL::OptionalString m_type; + DPL::OptionalString m_encoding; + ConfigParserData& m_data; + DPL::String m_namespace; +}; + +class PreferenceParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + m_name = attribute.value; + } else if (attribute.name == L"value") { + m_value = attribute.value; + } else if (attribute.name == L"readonly") { + if (attribute.value == L"true") { + m_required = true; + } else { + m_required = false; + } + } + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (!m_name) { + _W("preference element must have name attribute"); + return; + } + NormalizeString(m_name); + NormalizeString(m_value); + ConfigParserData::Preference preference(*m_name, m_required); + preference.value = m_value; + if (m_data.preferencesList.find(preference) == + m_data.preferencesList.end()) + { + m_data.preferencesList.insert(preference); + } + } + + PreferenceParser(ConfigParserData& data) : + ElementParser(), + m_required(false), + m_data(data) + {} + + private: + DPL::OptionalString m_name; + DPL::OptionalString m_value; + bool m_required; + ConfigParserData& m_data; +}; + +class SettingParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + m_setting.m_name = attribute.name; + m_setting.m_value = attribute.value; + m_data.settingsList.insert(m_setting); + } + + virtual void Verify() + {} + + SettingParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_setting(L"", L"") + {} + + private: + ConfigParserData& m_data; + ConfigParserData::Setting m_setting; +}; + +class AppControlParser : public ElementParser +{ + public: + struct SourceParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + m_data.m_src = *m_value; + } + + SourceParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct OperationParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + m_data.m_operation = *m_value; + } + + OperationParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct UriParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + // exception + DPL::String ignoreUri(L"file"); + + if (!!m_value && *m_value == ignoreUri) + { + _D("exception : '%ls' scheme will be ignored.", (*m_value).c_str()); + m_value = DPL::OptionalString(); + } + + if (!m_value || *m_value == L"") { + return; + } + + DPL::String wildString(L"*/*"); + if ((m_data.m_uriList.find(wildString) == m_data.m_uriList.end()) + && (m_data.m_uriList.find(*m_value) == m_data.m_uriList.end())) + { + m_data.m_uriList.insert(*m_value); + } else { + _D("Ignoring uri with name %ls", (*m_value).c_str()); + } + } + + UriParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct MimeParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + DPL::String wildString(L"*/*"); + if ((m_data.m_mimeList.find(wildString) == + m_data.m_mimeList.end()) + && (m_data.m_mimeList.find(*m_value) == + m_data.m_mimeList.end())) + { + m_data.m_mimeList.insert(*m_value); + } else { + _D("Ignoring mime with name %ls", (*m_value).c_str()); + } + } + + MimeParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + struct DispositionParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_value = attribute.value; + NormalizeString(m_value); + } + } + } + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + DPL::String windowString(L"window"); + DPL::String inlineString(L"inline"); + + if (*m_value == L"window") { + m_data.m_disposition = + ConfigParserData::AppControlInfo::Disposition::WINDOW; + } else if (*m_value == L"inline") { + m_data.m_disposition = + ConfigParserData::AppControlInfo::Disposition::INLINE; + } else { + _D("Ignoring dispostion value %ls", (*m_value).c_str()); + } + } + + DispositionParser(ConfigParserData::AppControlInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AppControlInfo& m_data; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"src") { + return std::bind(&AppControlParser::OnSourceElement, this); + } else if (name == L"operation") { + return std::bind(&AppControlParser::OnOperationElement, this); + } else if (name == L"uri") { + return std::bind(&AppControlParser::OnUriElement, this); + } else if (name == L"mime") { + return std::bind(&AppControlParser::OnMimeElement, this); + } else if (name == L"disposition") { + return std::bind(&AppControlParser::OnDispositionElement, this); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Element& element) + { + _W("namespace for app service = %ls", element.ns.c_str()); + if (element.ns == ConfigurationNamespace::W3CWidgetNamespaceName) { + ThrowMsg(Exception::ParseError, + "Wrong xml namespace for widget element"); + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_appControl.m_src == L"") { + ThrowMsg(Exception::ParseError, "service element must have src element"); + } + + if (m_appControl.m_operation == L"") { + ThrowMsg(Exception::ParseError, "service element must have operation element"); + } + + auto res = std::find(m_data.appControlList.begin(), m_data.appControlList.end(), m_appControl); + if(res != m_data.appControlList.end()) { + ThrowMsg(Exception::ParseError, "service element must be unique"); + } + +#ifdef NFC_EXCEPTION_HANDLING_FOR_TIZEN_2_2_ONLY + // XXX This feature should be retained to Tizen 2.2 only. + // NFC exception handling which was requested from Tizen Device API team. + + const DPL::String exceptionNfcOperation = + L"http://tizen.org/appcontrol/operation/nfc/transaction"; + const DPL::String exceptionNfcUri = L"nfc://secure/aid/"; + const DPL::String divertingNfcUri1 = L"nfc://secure/SIM1/aid/"; + const DPL::String divertingNfcUri2 = L"nfc://secure/eSE/aid/"; + + if (m_appControl.m_operation == exceptionNfcOperation + && m_appControl.m_mimeList.empty() + && m_appControl.m_uriList.size() == 1 + && (m_appControl.m_uriList.begin())->compare(0, exceptionNfcUri.length(), exceptionNfcUri) == 0) + { + DPL::String originalUri = *m_appControl.m_uriList.begin(); + DPL::String newUri = originalUri; + + newUri.replace(0, exceptionNfcUri.length(), divertingNfcUri1); + m_appControl.m_uriList.erase(m_appControl.m_uriList.begin()); + m_appControl.m_uriList.insert(newUri); + m_data.appControlList.push_back(m_appControl); + _D("NFC exception : %ls -> %ls", originalUri.c_str(), newUri.c_str()); + + newUri = originalUri; + newUri.replace(0, exceptionNfcUri.length(), divertingNfcUri2); + m_appControl.m_uriList.erase(m_appControl.m_uriList.begin()); + m_appControl.m_uriList.insert(newUri); + m_data.appControlList.push_back(m_appControl); + _D("NFC exception : %ls -> %ls", originalUri.c_str(), newUri.c_str()); + + return; + } +#endif // NFC_EXCEPTION_HANDLING_FOR_TIZEN_2_2_ONLY + + m_data.appControlList.push_back(m_appControl); + } + + ElementParserPtr OnSourceElement() + { + return ElementParserPtr(new SourceParser(m_appControl)); + } + + ElementParserPtr OnOperationElement() + { + return ElementParserPtr(new OperationParser(m_appControl)); + } + + ElementParserPtr OnUriElement() + { + return ElementParserPtr(new UriParser(m_appControl)); + } + + ElementParserPtr OnMimeElement() + { + return ElementParserPtr(new MimeParser(m_appControl)); + } + + ElementParserPtr OnDispositionElement() + { + return ElementParserPtr(new DispositionParser(m_appControl)); + } + + AppControlParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_appControl(L"") + {} + + private: + ConfigParserData& m_data; + ConfigParserData::AppControlInfo m_appControl; +}; + +class ApplicationParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + { + if (m_properNamespace) { + ThrowMsg(Exception::ParseError, "application element must be empty"); + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"id") { + m_id = attribute.value; + NormalizeAndTrimSpaceString(m_id); + } else if (attribute.name == L"package") { + m_package = attribute.value; + } else if (attribute.name == L"required_version") { + m_version = attribute.value; + NormalizeString(m_version); + } else { + ThrowMsg(Exception::ParseError, + "unknown attribute '" + + DPL::ToUTF8String(attribute.name) + + "' in application element"); + } + } + } + + virtual void Verify() + { + if(m_data.didFoundTizenApplicationElement) + { + ThrowMsg(Exception::ParseError, "tizen:application element must occur only once"); + } + m_data.didFoundTizenApplicationElement = true; + + VerifyIdAndPackage(); + VerifyVersion(); + } + + ApplicationParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_id(DPL::OptionalString()), + m_version(DPL::OptionalString()), + m_properNamespace(false) + {} + + static const char* const REGEXP_ID; + + private: + void VerifyIdAndPackage() + { + if (!m_package) + { + ThrowMsg(Exception::ParseError, + "application element must have package attribute"); + } + else + { + pcrecpp::RE re(REGEXP_PACKAGE); + if (!re.FullMatch(DPL::ToUTF8String(*m_package))) + { + ThrowMsg(Exception::ParseError, + "invalid format of package attribute"); + } + } + + if (!m_id) { + ThrowMsg(Exception::ParseError, + "application element must have id attribute"); + } + else + { + std::string package; + pcrecpp::RE re(REGEXP_ID); + if (!re.FullMatch(DPL::ToUTF8String(*m_id), &package)) + { + ThrowMsg(Exception::ParseError, + "invalid format of id attribute"); + } + if (package != DPL::ToUTF8String(*m_package)) + { + ThrowMsg(Exception::ParseError, + "invalid package prefix in id attribute"); + } + } + + m_data.tizenAppId = m_id; + m_data.tizenPkgId = m_package; + } + + void VerifyVersion() + { + if (!m_version) + { + ThrowMsg(Exception::ParseError, + "application element must have required_version attribute"); + } + else + { + pcrecpp::RE re(REGEXP_VERSION); + if (!re.FullMatch(DPL::ToUTF8String(*m_version))) + { + ThrowMsg(Exception::ParseError, + "invalid format of version attribute"); + } + } + + m_data.tizenMinVersionRequired = m_version; + } + + static const char* const REGEXP_PACKAGE; + static const char* const REGEXP_VERSION; + + ConfigParserData& m_data; + DPL::OptionalString m_id; + DPL::OptionalString m_package; + DPL::OptionalString m_version; + bool m_properNamespace; +}; + +const char* const ApplicationParser::REGEXP_PACKAGE = "[0-9A-Za-z]{10}"; +const char* const ApplicationParser::REGEXP_ID = "([0-9A-Za-z]{10})\\.[0-9A-Za-z]{1,52}"; +const char* const ApplicationParser::REGEXP_VERSION = "\\d+\\.\\d+(\\.\\d+)*"; + +class SplashParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (!m_src) + { + _W("src attribute of splash element is mandatory - ignoring"); + return; + } + + m_data.splashImgSrc = m_src; + } + + SplashParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + private: + DPL::OptionalString m_src; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +class BackgroundParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + if (attribute.value.size() > 0) { + m_src = attribute.value; + } + } + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (!m_src) { + _W("src attribute of background element is mandatory - ignoring"); + return; + } + + m_data.backgroundPage = m_src; + } + + explicit BackgroundParser(ConfigParserData& data) : + m_data(data) + {} + + private: + DPL::OptionalString m_src; + ConfigParserData& m_data; +}; + +class PrivilegeParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + _D("element"); + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"name") { + m_feature.name = attribute.value; + m_privilege.name = attribute.value; + } + } + } + + virtual void Verify() + { + LibIri::Wrapper iri(DPL::ToUTF8String(m_feature.name).c_str()); + + if (m_feature.name != L"") { + if (iri.Validate()) { + if (m_data.featuresList.find(m_feature) == + m_data.featuresList.end()) + { + m_data.featuresList.insert(m_feature); + } else { + _D("Ignoring feature with name %ls", m_feature.name.c_str()); + } + } + } + + LibIri::Wrapper iriPrivilege( + DPL::ToUTF8String(m_privilege.name).c_str()); + + if (m_privilege.name != L"") { + if (iriPrivilege.Validate()) { + if (m_data.privilegeList.find(m_privilege) == + m_data.privilegeList.end()) + { + m_data.privilegeList.insert(m_privilege); + } else { + _D("Ignoring privilege with name %ls", m_privilege.name.c_str()); + } + } + } + } + + PrivilegeParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_feature(L""), + m_privilege(L""), + m_properNamespace(false) + {} + + private: + ConfigParserData& m_data; + ConfigParserData::Feature m_feature; + ConfigParserData::Privilege m_privilege; + bool m_properNamespace; +}; + +class CategoryParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + LogDebug("element"); + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"name") { + if (attribute.value.size() > 0) { + m_name = attribute.value; + } + } + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (!m_name) { + _W("name attribute of category element is mandatory - ignoring"); + return; + } + + if (m_data.categoryList.find(*m_name) == + m_data.categoryList.end()) + { + m_data.categoryList.insert(*m_name); + } + + } + + explicit CategoryParser(ConfigParserData& data) : + m_data(data), + m_properNamespace(false) + {} + + private: + DPL::OptionalString m_name; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +#ifdef DBOX_ENABLED +class AppWidgetParser : public ElementParser +{ + public: + + struct BoxLabelParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + m_lang = attribute.lang; + } + } + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_label = text.value; + } + } + + virtual void Verify() + { + std::pair boxLabel; + if (m_label.empty()) { + _W("box-label element is empty"); + boxLabel.first = DPL::FromUTF8String(""); + boxLabel.second = DPL::FromUTF8String(""); + m_data.m_label.push_back(boxLabel); + } + else { + boxLabel.first = m_lang; + boxLabel.second = m_label; + m_data.m_label.push_back(boxLabel); + } + } + + BoxLabelParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_lang; + DPL::String m_label; + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + }; + + struct BoxIconParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_icon = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_icon.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of box-icon element is mandatory - ignoring"); + } + if (!m_data.m_icon.empty()) { + ThrowMsg(Exception::ParseError, + " element should occur as 0 or 1 time"); + } + m_data.m_icon = m_icon; + } + + explicit BoxIconParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_icon; + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + }; + + struct BoxContentParser : public ElementParser + { + struct BoxSizeParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"preview") { + m_preview = attribute.value; + } + if (attribute.name == L"use-decoration") { + m_useDecoration = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_size = text.value; + } + } + + virtual void Verify() + { + if(m_size.empty()) { + ThrowMsg(Exception::ParseError, + "size is mandatory - ignoring"); + } + + if (m_useDecoration.empty() || CheckIfNotTrueNorFalse(m_useDecoration)) { + m_useDecoration = L"true"; // default value + } + + ConfigParserData::LiveboxInfo::BoxSizeInfo boxSizeInfo; + boxSizeInfo.m_size = m_size; + boxSizeInfo.m_preview = m_preview; + boxSizeInfo.m_useDecoration = m_useDecoration; + m_data.m_boxSize.push_back(boxSizeInfo); + } + + explicit BoxSizeParser( + ConfigParserData::LiveboxInfo::BoxContentInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::String m_size; + DPL::String m_preview; + DPL::String m_useDecoration; + bool m_properNamespace; + ConfigParserData::LiveboxInfo::BoxContentInfo& m_data; + }; + + struct PdParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_src = attribute.value; + } else if (attribute.name == L"width") { + m_width = attribute.value; + } else if (attribute.name == L"height") { + m_height = attribute.value; + } else if (attribute.name == L"fast-open") { + m_fastOpen= attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_src.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of pd element is mandatory - ignoring"); + } + + if (m_width.empty()) { + ThrowMsg(Exception::ParseError, + "width attribute of pd element is mandatory - ignoring"); + } + + if (m_height.empty()) { + ThrowMsg(Exception::ParseError, + "height attribute of pd element is mandatory - ignoring"); + } + + if (!ConvertToInt(m_width)) { + ThrowMsg(Exception::ParseError, + "width attribute of pd element cannot be converted to int - ignoring. value: " << m_width); + } + + + DPL::OptionalInt height = ConvertToInt(m_height); + + if (!height) { + ThrowMsg(Exception::ParseError, + "height attribute of pd element cannot be converted to int - ignoring. value: " << m_height); + } + + if (*height < 1) { + m_height = L"1"; + _D("height attribute of pd element shouldn't be less than 1. Changed to 1 from %d", *height); + } else if (*height > 380){ + m_height = L"380"; + _D("height attribute of pd element shouldn't be greater than 380. Changed to 380 from %d", *height); + } + + if (!m_data.m_pdSrc.empty()) { + ThrowMsg(Exception::ParseError, " element should occur as 0 or 1 time"); + } + + m_data.m_pdSrc = m_src; + m_data.m_pdWidth = m_width; + m_data.m_pdHeight = m_height; + m_data.m_pdFastOpen = m_fastOpen; + } + + explicit PdParser( + ConfigParserData::LiveboxInfo::BoxContentInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + DPL::OptionalInt ConvertToInt(const DPL::String& intAsString) + { + char * endptr; + std::string tempStr = DPL::ToUTF8String(intAsString); + const char * intAsString_c = tempStr.c_str(); + errno = 0; + long int intAsString_i = strtol(intAsString_c, &endptr, 10); + + if ((errno == ERANGE && (intAsString_i == LONG_MAX || intAsString_i == LONG_MIN)) + || intAsString_i > INT_MAX || intAsString_i < INT_MIN + || *endptr != '\0') { + return DPL::OptionalInt(); + } + + return static_cast(intAsString_i); + } + + DPL::String m_src; + DPL::String m_width; + DPL::String m_height; + DPL::String m_fastOpen; + + bool m_properNamespace; + ConfigParserData::LiveboxInfo::BoxContentInfo& m_data; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"box-size") { + return std::bind(&AppWidgetParser::BoxContentParser::OnBoxSizeElement, this); + } else if (name == L"pd") { + return std::bind(&AppWidgetParser::BoxContentParser::OnPdElement, this); + } else { + ThrowMsg(Exception::ParseError, + "No element parser for name: " << name); + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"src") { + m_box.m_boxSrc = attribute.value; + } + if (attribute.name == L"mouse-event") { + m_box.m_boxMouseEvent = attribute.value; + } + if (attribute.name == L"touch-effect") { + m_box.m_boxTouchEffect = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_box.m_boxSrc.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of box-content element is mandatory - ignoring"); + } + + if (m_box.m_boxMouseEvent.empty() || CheckIfNotTrueNorFalse(m_box.m_boxMouseEvent)) { + m_box.m_boxMouseEvent = L"false"; // default value + } + + if (m_box.m_boxTouchEffect.empty() || CheckIfNotTrueNorFalse(m_box.m_boxTouchEffect)) { + m_box.m_boxTouchEffect = L"true"; // default value + } + + if (m_box.m_boxSize.empty()) { + ThrowMsg(Exception::ParseError, + "box-size element of box-content element not found - ignoring"); + } + + if (!m_data.m_boxInfo.m_boxSrc.empty()) { + ThrowMsg(Exception::ParseError, " element must occur exactly 1 time"); + } + + m_data.m_boxInfo = m_box; + } + + explicit BoxContentParser(ConfigParserData::LiveboxInfo& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + ElementParserPtr OnBoxSizeElement() + { + return ElementParserPtr(new BoxSizeParser(m_box)); + } + + ElementParserPtr OnPdElement() + { + return ElementParserPtr(new PdParser(m_box)); + } + + private: + bool m_properNamespace; + ConfigParserData::LiveboxInfo& m_data; + ConfigParserData::LiveboxInfo::BoxContentInfo m_box; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"box-label") { + return std::bind(&AppWidgetParser::OnBoxLabelElement, this); + } else if (name == L"box-icon") { + return std::bind(&AppWidgetParser::OnBoxIconElement, this); + } else if (name == L"box-content") { + return std::bind(&AppWidgetParser::OnBoxContentElement, this); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"id") { + m_liveboxId = attribute.value; + } else if (attribute.name == L"primary") { + m_primary = attribute.value; + } else if (attribute.name == L"auto-launch") { + m_autoLaunch = attribute.value; + } else if (attribute.name == L"update-period") { + m_updatePeriod = attribute.value; + } else if (attribute.name == L"type") { + m_type = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == + ConfigurationNamespace::TizenWebAppNamespaceName) + { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Verify() + { + if (m_liveboxId.empty()) { + ThrowMsg(Exception::ParseError, + "app-widget element must have id attribute"); + } + else + { + pcrecpp::RE re(REGEXP_ID_STRING.c_str()); + if (!re.FullMatch(DPL::ToUTF8String(m_liveboxId))) + { + ThrowMsg(Exception::ParseError, + "invalid format of app-widget id attribute"); + } + } + + if (m_primary.empty() || CheckIfNotTrueNorFalse(m_primary)) + { + m_primary = L"true"; //default value + } + + if (!m_updatePeriod.empty()) + { + char * endptr; + errno = 0; + std::string tempStr = DPL::ToUTF8String(m_updatePeriod); + + //set standard locale to fix decimal point mark - '.' + std::string currentLocale = setlocale(LC_NUMERIC, NULL); + if (NULL == setlocale(LC_NUMERIC, "C")) + _W("Failed to change locale to \"C\""); + double updatePeriod = strtod(tempStr.c_str(), &endptr); + + //go back to previous locale + if (NULL == setlocale(LC_NUMERIC, currentLocale.c_str())) + _W("Failed to set previous locale"); + + if ((errno == ERANGE && (updatePeriod == -HUGE_VAL || updatePeriod == HUGE_VAL)) + || *endptr != '\0') { + ThrowMsg(Exception::ParseError, + "update-period attribute of app-widget element should be a number - ignoring. current value: " << m_updatePeriod); + } else if (updatePeriod < 1800.0) { + _D("update-period attribute of app-widget element shouldn't be less than 1800.0 - changed to 1800 from value: %ls", m_updatePeriod.c_str()); + m_updatePeriod = L"1800.0"; + } + } + + if (m_autoLaunch.empty() || CheckIfNotTrueNorFalse(m_autoLaunch)) + { + m_autoLaunch = L"false"; // default value + } + + if(m_livebox.m_label.empty()) { + ThrowMsg(Exception::ParseError, + "box-label element of app-widget element not found - ignoring"); + } + + if(!m_boxContentFound) { + ThrowMsg(Exception::ParseError, + "box-content element of app-widget element not found - ignoring"); + } + + m_livebox.m_liveboxId = m_liveboxId; + m_livebox.m_primary = m_primary; + m_livebox.m_autoLaunch = m_autoLaunch; + m_livebox.m_updatePeriod = m_updatePeriod; + m_livebox.m_type = m_type; + + m_data.m_livebox.push_back(m_livebox); + } + + explicit AppWidgetParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false), + m_boxContentFound(false) + { + m_livebox = ConfigParserData::LiveboxInfo(); + } + + ElementParserPtr OnBoxLabelElement() + { + + return ElementParserPtr(new BoxLabelParser(m_livebox)); + } + + ElementParserPtr OnBoxIconElement() + { + return ElementParserPtr(new BoxIconParser(m_livebox)); + } + + ElementParserPtr OnBoxContentElement() + { + m_boxContentFound = true; + return ElementParserPtr(new BoxContentParser(m_livebox)); + } + + private: + static std::string REGEXP_ID_STRING; + ConfigParserData& m_data; + ConfigParserData::LiveboxInfo m_livebox; + DPL::String m_liveboxId; + DPL::String m_primary; + DPL::String m_autoLaunch; + DPL::String m_updatePeriod; + DPL::String m_type; + bool m_properNamespace; + bool m_boxContentFound; + + static bool CheckIfNotTrueNorFalse(const DPL::String &stringToCheck) + { + return stringToCheck.compare(L"true") != 0 && stringToCheck.compare(L"false") != 0; + } +}; + +std::string AppWidgetParser::REGEXP_ID_STRING = std::string(ApplicationParser::REGEXP_ID) + "\\.[0-9A-Za-z]+"; +#endif + +class AllowNavigationParser : public ElementParser +{ + public: + AllowNavigationParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& text) + { + if (m_properNamespace) + { + m_origin = text.value; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + { + } + + virtual void Verify() + { + if (m_data.allowNavigationEncountered || !m_properNamespace) + { + return; + } + m_data.allowNavigationEncountered = true; + + if (!m_origin) { + _W("data is empty"); + return; + } + + char* data = strdup(DPL::ToUTF8String(*m_origin).c_str()); + char* ptr = strtok(data," \n\r\t"); + while (ptr != NULL) { + std::string origin = ptr; + ptr = strtok(NULL," \n\r\t"); + if(origin == "*") { + ConfigParserData::AllowNavigationInfo info(L"*", L"*"); + m_data.allowNavigationInfoList.push_back(info); + continue; + } + + std::unique_ptr iri(iri_parse(origin.c_str()), iri_destroy); + if (!iri->host || strlen(iri->host) == 0) { + // input origin should has schem and host + // in case of file scheme path is filled + // "http://" + _W("input origin isn't verified"); + continue; + } + DPL::String scheme = L"*"; + if (iri->scheme && strlen(iri->scheme) != 0) { + scheme = DPL::FromUTF8String(iri->scheme); + } + ConfigParserData::AllowNavigationInfo info( + scheme, + DPL::FromUTF8String(iri->host)); + m_data.allowNavigationInfoList.push_back(info); + } + free(data); + } + + private: + DPL::OptionalString m_origin; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +class CspParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + CspParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_policy = text.value; + } + } + + virtual void Verify() + { + if (m_data.cspPolicyEncountered) { + return; + } + m_data.cspPolicyEncountered = true; + + if (!!m_policy) { + m_data.cspPolicy = *m_policy; + } + } + + private: + ConfigParserData& m_data; + bool m_properNamespace; + DPL::OptionalString m_policy; +}; + +class CspReportOnlyParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + CspReportOnlyParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Accept(const Text& text) + { + if (m_properNamespace) { + m_policy = text.value; + } + } + + virtual void Verify() + { + if (m_data.cspPolicyReportOnlyEncountered) { + return; + } + m_data.cspPolicyReportOnlyEncountered = true; + + if (!!m_policy) { + m_data.cspPolicyReportOnly = *m_policy; + } + } + + private: + ConfigParserData& m_data; + bool m_properNamespace; + DPL::OptionalString m_policy; +}; + +class AccountParser : public ElementParser +{ + public: + struct IconParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"section") { + if (attribute.value == L"Account") { + m_type = ConfigParserData::IconSectionType::DefaultIcon; + } else if (attribute.value == L"AccountSmall") { + m_type = ConfigParserData::IconSectionType::SmallIcon; + } + } + } + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + std::pair icon; + icon.first = m_type; + icon.second = *m_value; + + m_data.m_iconSet.insert(icon); + } + + IconParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_type(ConfigParserData::DefaultIcon), + m_data(data) + {} + + private: + bool m_properNamespace; + ConfigParserData::IconSectionType m_type; + ConfigParserData::AccountProvider& m_data; + DPL::OptionalString m_value; + }; + + struct DisplayNameParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& element) + { + m_lang = element.lang; + m_value= L""; + } + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + + std::pair name; + name.first = *m_lang; + name.second = *m_value; + + m_data.m_displayNameSet.insert(name); + } + + DisplayNameParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_lang; + DPL::OptionalString m_value; + ConfigParserData::AccountProvider& m_data; + }; + + struct CapabilityParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const Text& text) + { + if (text.value == L"") { + return; + } + m_value = text.value; + } + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& /*attribute*/) + {} + + virtual void Verify() + { + if (!m_value || *m_value == L"") { + return; + } + m_data.m_capabilityList.push_back(*m_value); + } + + CapabilityParser(ConfigParserData::AccountProvider& data) : + ElementParser(), + m_properNamespace(false), + m_data(data) + {} + + private: + bool m_properNamespace; + DPL::OptionalString m_value; + ConfigParserData::AccountProvider& m_data; + }; + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"icon") { + return std::bind(&AccountParser::OnIconElement, this); + } else if (name == L"display-name") { + return std::bind(&AccountParser::OnDisplayNameElement, this); + } else if (name == L"capability") { + return std::bind(&AccountParser::OnCapabilityElement, this); + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + } + + virtual void Accept(const Text& /*text*/) + {} + + virtual void Accept(const Element& /*element*/) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"multiple-account-support") { + if (attribute.value == L"true") { + m_account.m_multiAccountSupport = true; + } + } + } + + virtual void Verify() + { + } + + ElementParserPtr OnIconElement() + { + return ElementParserPtr(new IconParser(m_account)); + } + + ElementParserPtr OnDisplayNameElement() + { + return ElementParserPtr(new DisplayNameParser(m_account)); + } + + ElementParserPtr OnCapabilityElement() + { + return ElementParserPtr(new CapabilityParser(m_account)); + } + + AccountParser(ConfigParserData& data) : + ElementParser(), + m_properNamespace(false), + m_multiSupport(false), + m_data(data), + m_account(data.accountProvider) + { + } + + private: + bool m_properNamespace; + bool m_multiSupport; + ConfigParserData& m_data; + ConfigParserData::AccountProvider& m_account; +}; + +class MetadataParser : public ElementParser +{ + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (m_properNamespace) { + if (attribute.name == L"key") { + m_key = attribute.value; + } else if (attribute.name == L"value") { + m_value = attribute.value; + } + } + } + + virtual void Accept(const Element& element) + { + if (element.ns == ConfigurationNamespace::TizenWebAppNamespaceName) { + m_properNamespace = true; + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (!m_key) { + _W("metadata element must have key attribute"); + return; + } + NormalizeString(m_key); + NormalizeString(m_value); + ConfigParserData::Metadata metaData(m_key, m_value); + FOREACH(it, m_data.metadataList) { + if (!DPL::StringCompare(*it->key, *m_key)) { + _E("Key isn't unique"); + return; + } + } + m_data.metadataList.push_back(metaData); + } + + MetadataParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_properNamespace(false) + {} + + private: + DPL::OptionalString m_key; + DPL::OptionalString m_value; + ConfigParserData& m_data; + bool m_properNamespace; +}; + +#ifdef IME_ENABLED +class ImeParser : public ElementParser +{ + public: + struct UuidParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const XmlAttribute& attribute) + {} + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& text) + { + if (m_uuid.empty()) { + m_uuid = text.value; + } else { + m_uuid += text.value; + } + } + + virtual void Verify() + { + if (m_uuid.empty()) { + ThrowMsg(Exception::ParseError, "uuid text is empty"); + } + m_imeAppInfo.uuid = m_uuid; + } + + UuidParser(ConfigParserData::ImeAppInfo& data) : + ElementParser(), + m_imeAppInfo(data) + {} + + private: + ConfigParserData::ImeAppInfo& m_imeAppInfo; + DPL::String m_uuid; + }; + + struct LanguagesParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"language") { + return std::bind(&LanguagesParser::OnLanguageElement, this); + } else { + return &IgnoringParser::Create; + } + } + + virtual void Accept(const XmlAttribute& attribute) + {} + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& text) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + {} + + LanguagesParser(ConfigParserData::ImeAppInfo& data) : + ElementParser(), + m_imeAppInfo(data) + {} + + ElementParserPtr OnLanguageElement() + { + return ElementParserPtr(new LanguageParser(m_imeAppInfo)); + } + + private: + ConfigParserData::ImeAppInfo& m_imeAppInfo; + }; + + struct LanguageParser : public ElementParser + { + public: + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const XmlAttribute& attribute) + {} + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& text) + { + if (m_language.empty()) { + m_language = text.value; + } else { + m_language += text.value; + } + } + + virtual void Verify() + { + if (m_language.empty()) { + ThrowMsg(Exception::ParseError, "language text is empty"); + } + m_imeAppInfo.languageList.insert(m_language); + } + + LanguageParser(ConfigParserData::ImeAppInfo& data) : + ElementParser(), + m_imeAppInfo(data) + {} + + private: + ConfigParserData::ImeAppInfo& m_imeAppInfo; + DPL::String m_language; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"uuid") { + return std::bind(&ImeParser::OnUuidElement, this); + } else if (name == L"languages") { + return std::bind(&ImeParser::OnLanguagesElement, this); + } else { + return &IgnoringParser::Create; + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const XmlAttribute& attribute) + {} + + virtual void Verify() + { + if (m_imeAppInfo.uuid.empty()) { + ThrowMsg(Exception::ParseError, "ime element must have uuid element"); + return; + } + + if (m_imeAppInfo.languageList.empty()) { + ThrowMsg(Exception::ParseError, "ime element must have language element"); + return; + } + m_data.imeAppInfoList.push_back(m_imeAppInfo); + } + + ImeParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_imeAppInfo() + {} + + ElementParserPtr OnLanguagesElement() + { + return ElementParserPtr(new LanguagesParser(m_imeAppInfo)); + } + + ElementParserPtr OnUuidElement() + { + return ElementParserPtr(new UuidParser(m_imeAppInfo)); + } + + private: + ConfigParserData& m_data; + ConfigParserData::ImeAppInfo m_imeAppInfo; +}; +#endif + +#ifdef SERVICE_ENABLED +class ServiceAppParser : public ElementParser +{ + public: + struct ServiceContentParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + m_src = attribute.value; + } + } + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_src.empty()) { + ThrowMsg(Exception::ParseError, "src attribute of service-content element is mandatory"); + } + + if (!m_serviceAppInfo.serviceContent.empty()) { + ThrowMsg(Exception::ParseError, "service-content element occurs more than 1 time"); + } + m_serviceAppInfo.serviceContent = m_src; + } + + explicit ServiceContentParser(ConfigParserData::ServiceAppInfo& data) : + ElementParser(), + m_serviceAppInfo(data) + {} + + private: + DPL::String m_src; + ConfigParserData::ServiceAppInfo& m_serviceAppInfo;; + }; + + struct ServiceNameParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const XmlAttribute& attribute) + { + m_lang = attribute.lang; + } + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& text) + { + m_name = text.value; + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_serviceAppInfo.m_localizedDataSet[m_lang]; + if (data.name.IsNull()) { + NormalizeString(m_name); + data.name = m_name; + } + } + + ServiceNameParser(ConfigParserData::ServiceAppInfo& data) : + ElementParser(), + m_serviceAppInfo(data) + {} + + private: + DPL::String m_lang; + DPL::String m_name; + ConfigParserData::ServiceAppInfo& m_serviceAppInfo; + }; + + struct ServiceIconParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& /*name*/) + { + return &IgnoringParser::Create; + } + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"src") { + m_icon.src = attribute.value; + } + else if (attribute.name == L"width") { + m_icon.width = ConvertToInt(attribute.value); + } + else if (attribute.name == L"height") { + m_icon.height = ConvertToInt(attribute.value); + } + } + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_icon.src.empty()) { + ThrowMsg(Exception::ParseError, + "src attribute of service-icon element is mandatory - ignoring"); + } + m_serviceAppInfo.m_iconsList.push_back(m_icon); + } + + explicit ServiceIconParser(ConfigParserData::ServiceAppInfo& data) : + ElementParser(), + m_icon(L""), + m_serviceAppInfo(data) + {} + + private: + DPL::OptionalInt ConvertToInt(const DPL::String& intAsString) + { + char * endptr; + std::string tempStr = DPL::ToUTF8String(intAsString); + const char * intAsString_c = tempStr.c_str(); + errno = 0; + long int intAsString_i = strtol(intAsString_c, &endptr, 10); + + if ((errno == ERANGE && (intAsString_i == LONG_MAX || intAsString_i == LONG_MIN)) + || intAsString_i > INT_MAX || intAsString_i < INT_MIN + || *endptr != '\0') { + return DPL::OptionalInt(); + } + + return static_cast(intAsString_i); + } + + private: + ConfigParserData::Icon m_icon; + ConfigParserData::ServiceAppInfo& m_serviceAppInfo; + }; + + struct ServiceDescriptionParser : public ElementParser + { + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, const DPL::String& /*name*/) + { + return &IgnoringParser::Create; //ignore unknown according to w3c + } + + virtual void Accept(const XmlAttribute& attribute) + { + m_lang = attribute.lang; + } + virtual void Accept(const Element& element) + {} + + virtual void Accept(const Text& text) + { + m_description = text.value; + } + + virtual void Verify() + { + ConfigParserData::LocalizedData& data = m_serviceAppInfo.m_localizedDataSet[m_lang]; + if (data.description.IsNull()) { + NormalizeString(m_description); + data.description = m_description; + } + } + + ServiceDescriptionParser(ConfigParserData::ServiceAppInfo& data) : + ElementParser(), + m_serviceAppInfo(data) + {} + + private: + DPL::String m_lang; + DPL::String m_description; + ConfigParserData::ServiceAppInfo& m_serviceAppInfo; + }; + + virtual ActionFunc GetElementParser(const DPL::String& /*ns*/, + const DPL::String& name) + { + if (name == L"service-content") { + return std::bind(&ServiceAppParser::OnServiceContentElement, this); + } else if (name == L"service-name") { + return std::bind(&ServiceAppParser::OnServiceNameElement, this); + } else if (name == L"service-icon") { + return std::bind(&ServiceAppParser::OnServiceIconElement, this); + } else if (name == L"service-description") { + return std::bind(&ServiceAppParser::OnServiceDescriptionElement, this); + } else { + return &IgnoringParser::Create; + } + } + + virtual void Accept(const Element& element) + {} + + virtual void Accept(const XmlAttribute& attribute) + { + if (attribute.name == L"id") { + if (attribute.value.size() > 0) { + m_serviceId = attribute.value; + } + } else if (attribute.name == L"auto_restart") { + if (attribute.value == L"true") { + m_autoRestart = true; + } else if (attribute.value == L"false") { + m_autoRestart = false; + } else { + ThrowMsg(Exception::ParseError, "Wrong boolean value"); + } + } else if (attribute.name == L"on_boot") { + if (attribute.value == L"true") { + m_onBoot = true; + } else if (attribute.value == L"false") { + m_onBoot = false; + } else { + ThrowMsg(Exception::ParseError, "Wrong boolean value"); + } + } + } + + virtual void Accept(const Text& /*text*/) + { + ThrowMsg(Exception::ParseError, "param element must be empty"); + } + + virtual void Verify() + { + if (m_serviceAppInfo.serviceContent.empty()) { + ThrowMsg(Exception::ParseError, "service element must have service-content element"); + return; + } + + if (m_serviceId.empty()) { + ThrowMsg(Exception::ParseError, "service attribute must have id attribute"); + return; + } + m_serviceAppInfo.serviceId = m_serviceId; + + m_serviceAppInfo.autoRestart = m_autoRestart; + + m_serviceAppInfo.onBoot = m_onBoot; + + m_data.serviceAppInfoList.push_back(m_serviceAppInfo); + } + + ServiceAppParser(ConfigParserData& data) : + ElementParser(), + m_data(data), + m_serviceAppInfo(), + m_onBoot(false), + m_autoRestart(false) + {} + + ElementParserPtr OnServiceContentElement() + { + return ElementParserPtr(new ServiceContentParser(m_serviceAppInfo)); + } + + ElementParserPtr OnServiceNameElement() + { + return ElementParserPtr(new ServiceNameParser(m_serviceAppInfo)); + } + + ElementParserPtr OnServiceIconElement() + { + return ElementParserPtr(new ServiceIconParser(m_serviceAppInfo)); + } + + ElementParserPtr OnServiceDescriptionElement() + { + return ElementParserPtr(new ServiceDescriptionParser(m_serviceAppInfo)); + } + + private: + DPL::String m_serviceId; + ConfigParserData& m_data; + ConfigParserData::ServiceAppInfo m_serviceAppInfo; + bool m_autoRestart; + bool m_onBoot; +}; +#endif + +ElementParser::ActionFunc WidgetParser::GetElementParser( + const DPL::String& /*ns*/, + const DPL::String& name) +{ + FuncMap::const_iterator it = m_map.find(name); + if (it != m_map.end()) { + return it->second; + } else { + return &IgnoringParser::Create; //ignore unknown according to w3c + } +} + +WidgetParser::WidgetParser(ConfigParserData& data) : + m_data(data), + m_textDirection(Unicode::EMPTY) +{ + m_map[L"name"] = std::bind(&WidgetParser::OnNameElement, this); + m_map[L"access"] = std::bind(&WidgetParser::OnAccessElement, this); + m_map[L"description"] = std::bind(&WidgetParser::OnDescriptionElement, this); + m_map[L"author"] = std::bind(&WidgetParser::OnAuthorElement, this); + m_map[L"license"] = std::bind(&WidgetParser::OnLicenseElement, this); + m_map[L"icon"] = std::bind(&WidgetParser::OnIconElement, this); + m_map[L"small-icon"] = std::bind(&WidgetParser::OnSmallIconElement, this); + m_map[L"content"] = std::bind(&WidgetParser::OnContentElement, this); + m_map[L"preference"] = std::bind(&WidgetParser::OnPreferenceElement, this); + m_map[L"setting"] = std::bind(&WidgetParser::OnSettingElement, this); + m_map[L"application"] = std::bind(&WidgetParser::OnApplicationElement, this); +#ifdef IME_ENABLED + m_map[L"ime"] = std::bind(&WidgetParser::OnImeElement, this); +#endif +#ifdef SERVICE_ENABLED + m_map[L"service"] = std::bind(&WidgetParser::OnServiceAppElement, this); +#endif + m_map[L"splash"] = std::bind(&WidgetParser::OnSplashElement, this); + m_map[L"background"] = std::bind(&WidgetParser::OnBackgroundElement, this); + m_map[L"privilege"] = std::bind(&WidgetParser::OnPrivilegeElement, this); + m_map[L"app-control"] = std::bind(&WidgetParser::OnAppControlElement, this); + m_map[L"category"] = std::bind(&WidgetParser::OnCategoryElement, this); +#ifdef DBOX_ENABLED + m_map[L"app-widget"] = std::bind(&WidgetParser::OnAppWidgetElement, this); +#endif +#if ENABLE(CONTENT_SECURITY_POLICY) + m_map[L"content-security-policy"] = std::bind(&WidgetParser::OnCspElement, this); + m_map[L"content-security-policy-report-only"] = std::bind(&WidgetParser::OnCspReportOnlyElement, this); +#endif +#if ENABLE(ALLOW_NAVIGATION) + m_map[L"allow-navigation"] = std::bind(&WidgetParser::OnAllowNavigationElement, this); +#endif + m_map[L"account"] = std::bind(&WidgetParser::OnAccountElement, this); + m_map[L"metadata"] = std::bind(&WidgetParser::OnMetadataElement, this); +} + +ElementParserPtr WidgetParser::OnNameElement() +{ + return ElementParserPtr(new NameParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnAccessElement() +{ + return ElementParserPtr(new AccessParser(m_data)); +} + +ElementParserPtr WidgetParser::OnDescriptionElement() +{ + return ElementParserPtr(new DescriptionParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnAuthorElement() +{ + return ElementParserPtr(new AuthorParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnLicenseElement() +{ + return ElementParserPtr(new LicenseParser(m_textDirection, m_data)); +} + +ElementParserPtr WidgetParser::OnIconElement() +{ + return ElementParserPtr(new IconParser(m_data)); +} + +ElementParserPtr WidgetParser::OnSmallIconElement() +{ + return ElementParserPtr(new IconParser(m_data, true)); +} + +ElementParserPtr WidgetParser::OnContentElement() +{ + return ElementParserPtr(new ContentParser(m_data)); +} + +ElementParserPtr WidgetParser::OnPreferenceElement() +{ + return ElementParserPtr(new PreferenceParser(m_data)); +} + +ElementParserPtr WidgetParser::OnSettingElement() +{ + return ElementParserPtr(new SettingParser(m_data)); +} + +ElementParserPtr WidgetParser::OnApplicationElement() +{ + return ElementParserPtr(new ApplicationParser(m_data)); +} + +ElementParserPtr WidgetParser::OnSplashElement() +{ + return ElementParserPtr(new SplashParser(m_data)); +} + +ElementParserPtr WidgetParser::OnBackgroundElement() +{ + return ElementParserPtr(new BackgroundParser(m_data)); +} + +ElementParserPtr WidgetParser::OnPrivilegeElement() +{ + return ElementParserPtr(new PrivilegeParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAppControlElement() +{ + return ElementParserPtr(new AppControlParser(m_data)); +} + +ElementParserPtr WidgetParser::OnCategoryElement() +{ + return ElementParserPtr(new CategoryParser(m_data)); +} + +#ifdef DBOX_ENABLED +ElementParserPtr WidgetParser::OnAppWidgetElement() +{ + return ElementParserPtr(new AppWidgetParser(m_data)); +} +#endif + +ElementParserPtr WidgetParser::OnCspElement() +{ + return ElementParserPtr(new CspParser(m_data)); +} + +ElementParserPtr WidgetParser::OnCspReportOnlyElement() +{ + return ElementParserPtr(new CspReportOnlyParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAllowNavigationElement() +{ + return ElementParserPtr(new AllowNavigationParser(m_data)); +} + +ElementParserPtr WidgetParser::OnAccountElement() +{ + return ElementParserPtr(new AccountParser(m_data)); +} + +ElementParserPtr WidgetParser::OnMetadataElement() +{ + return ElementParserPtr(new MetadataParser(m_data)); +} + +#ifdef IME_ENABLED +ElementParserPtr WidgetParser::OnImeElement() +{ + return ElementParserPtr(new ImeParser(m_data)); +} +#endif + +#ifdef SERVICE_ENABLED +ElementParserPtr WidgetParser::OnServiceAppElement() +{ + return ElementParserPtr(new ServiceAppParser(m_data)); +} +#endif + +void WidgetParser::Accept(const Element& element) +{ + if (element.ns != ConfigurationNamespace::W3CWidgetNamespaceName && + element.ns != ConfigurationNamespace::TizenWebAppNamespaceName) + { + ThrowMsg(Exception::ParseError, + "Wrong xml namespace for widget element"); + } +} + +void WidgetParser::Accept(const Text& /*text*/) +{ + ThrowMsg(Exception::ParseError, "widged element must be empty"); +} + +void WidgetParser::Accept(const XmlAttribute& attribute) +{ + if (attribute.name == L"id") { + LibIri::Wrapper iri(DPL::ToUTF8String(attribute.value).c_str()); + //If may important tests starts to fail this test we will have + //to consider commenting this test out again. + if (iri.Validate()) { + m_data.widget_id = attribute.value; + NormalizeString(m_data.widget_id); + } else { + _W("Widget id validation failed: %ls", attribute.value.c_str()); + } + } else if (attribute.name == L"version") { + m_version = attribute.value; + NormalizeString(m_version); + } else if (attribute.name == L"min-version") { + _D("min-version attribute was found. Value: %ls", attribute.value.c_str()); + m_minVersion = attribute.value; + NormalizeString(m_minVersion); + m_data.minVersionRequired = m_minVersion; + } else if (attribute.name == L"height") { + DPL::OptionalString value = attribute.value; + NormalizeString(value); + std::string v = DPL::ToUTF8String(*value); + + if (!v.empty()) { + unsigned char c = v.c_str()[0]; + if (isdigit(c)) { + int val = 0; + for (size_t i = 0; i < v.size(); ++i) { + c = v.c_str()[i]; + if (isdigit(c)) { + val *= 10; + val += (c - '0'); + } else { + break; + } + } + m_data.height = val; + } + } + } else if (attribute.name == L"width") { + DPL::OptionalString value = attribute.value; + NormalizeString(value); + std::string v = DPL::ToUTF8String(*value); + + if (!v.empty()) { + unsigned char c = v.c_str()[0]; + if (c >= '0' && c <= '9') { + int val = 0; + for (size_t i = 0; i < v.size(); ++i) { + c = v.c_str()[i]; + if (c >= '0' && c <= '9') { + val *= 10; + val += (c - '0'); + } else { + break; + } + } + m_data.width = val; + } + } + } else if (attribute.name == L"viewmodes") { + DPL::Tokenize(attribute.value, + L" ", + std::inserter(m_windowModes, + m_windowModes.end()), + true); + } else if (attribute.name == L"dir") { + m_textDirection = Unicode::ParseDirAttribute(attribute); + } else if (L"defaultlocale" == attribute.name) { + if (!m_defaultlocale) { + m_defaultlocale = attribute.value; + NormalizeString(m_defaultlocale); + std::string dl = DPL::ToUTF8String(*m_defaultlocale); + + if (!LanguageSubtagRstTreeSingleton::Instance(). + ValidateLanguageTag(dl)) { + _W("Language tag: %s is not valid", dl.c_str()); + m_defaultlocale = DPL::OptionalString(); + } else { + _D("Default locale found %s", dl.c_str()); + } + } else { + _W("Ignoring subsequent default locale"); + } + //Any other value consider as a namespace definition + } else if (attribute.name == L"xmlns" || attribute.prefix == L"xmlns") { + _D("Namespace domain: %ls", attribute.name.c_str()); + _D("Namespace value: %ls", attribute.value.c_str()); + m_nameSpaces[attribute.name] = attribute.value; + } + else { + _E("Unknown attirbute: namespace=%ls, name=%ls, value=%ls", + attribute.ns.c_str(), attribute.name.c_str(), attribute.value.c_str()); + } +} + +void WidgetParser::Verify() +{ + FOREACH(mode, m_windowModes) { + if (L"windowed" == *mode || L"floating" == *mode || + L"fullscreen" == *mode || L"maximized" == *mode || + L"minimized" == *mode) + { + m_data.windowModes.insert(*mode); + } + } + if (!!m_version) { + Unicode::UpdateTextWithDirectionMark(m_textDirection, &*m_version); + m_data.version = m_version; + } + m_data.defaultlocale = m_defaultlocale; + FOREACH(ns, m_nameSpaces) { + m_data.nameSpaces.insert(ns->second); + } +} diff --git a/src_wearable/configuration_parser/widget_parser.h b/src_wearable/configuration_parser/widget_parser.h new file mode 100755 index 0000000..13cef2f --- /dev/null +++ b/src_wearable/configuration_parser/widget_parser.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file have been implemented in compliance with W3C WARP SPEC. + * but there are some patent issue between W3C WARP SPEC and APPLE. + * so if you want to use this file, refer to the README file in root directory + */ +/** + * @file widget_parser.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef WIDGET_PARSER_H_ +#define WIDGET_PARSER_H_ + +#include "element_parser.h" +#include +#include +#include +#include +#include + +namespace ConfigurationNamespace { +static const DPL::String W3CWidgetNamespaceName = + L"http://www.w3.org/ns/widgets"; +static const DPL::String TizenWebAppNamespaceName = + L"http://tizen.org/ns/widgets"; +} + +namespace PluginsPrefix { +const char * const W3CPluginsPrefix = "http://www.w3.org/"; +const char * const TIZENPluginsPrefix = "http://tizen.org/api/"; +} + +namespace Unicode { +enum Direction +{ + LRE, + RLE, + LRO, + RLO, + EMPTY +}; +} + +class WidgetParser : public ElementParser +{ + public: + ElementParserPtr OnNameElement(); + ElementParserPtr OnDescriptionElement(); + ElementParserPtr OnAuthorElement(); + ElementParserPtr OnLicenseElement(); + ElementParserPtr OnIconElement(); + ElementParserPtr OnSmallIconElement(); + ElementParserPtr OnContentElement(); + ElementParserPtr OnPreferenceElement(); + ElementParserPtr OnAccessElement(); + ElementParserPtr OnSettingElement(); + ElementParserPtr OnApplicationElement(); + ElementParserPtr OnSplashElement(); + ElementParserPtr OnBackgroundElement(); + ElementParserPtr OnPrivilegeElement(); + ElementParserPtr OnAppControlElement(); + ElementParserPtr OnCategoryElement(); + ElementParserPtr OnAppWidgetElement(); + ElementParserPtr OnCspElement(); + ElementParserPtr OnCspReportOnlyElement(); + ElementParserPtr OnAllowNavigationElement(); + ElementParserPtr OnAccountElement(); + ElementParserPtr OnMetadataElement(); + +#ifdef IME_ENABLED + ElementParserPtr OnImeElement(); +#endif +#ifdef SERVICE_ENABLED + ElementParserPtr OnServiceAppElement(); +#endif + + virtual ActionFunc GetElementParser(const DPL::String& ns, + const DPL::String& name); + + virtual void Accept(const Element&); + virtual void Accept(const Text&); + virtual void Accept(const XmlAttribute&); + virtual void Verify(); + + //Typedef used by RootParser + typedef WrtDB::ConfigParserData& Data; + + WidgetParser(Data&); + + private: + Data& m_data; + Unicode::Direction m_textDirection; + FuncMap m_map; + DPL::OptionalString m_version; + DPL::OptionalString m_minVersion; + std::list m_windowModes; + DPL::OptionalString m_defaultlocale; + std::map m_nameSpaces; +}; + +struct IconParser; +struct ContentParser; + +#endif // WIDGET_PARSER_H_ diff --git a/src_wearable/jobs/job.cpp b/src_wearable/jobs/job.cpp new file mode 100644 index 0000000..ec2bb7f --- /dev/null +++ b/src_wearable/jobs/job.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +namespace Jobs { +Job::Job(InstallationType installType) : + m_handle(0), + m_installationType(installType), + m_abortStarted(false), + m_paused(false) +{} + +InstallationType Job::GetInstallationType() const +{ + return m_installationType; +} + +bool Job::GetAbortStarted() const +{ + return m_abortStarted; +} + +void Job::SetAbortStarted(bool flag) +{ + m_abortStarted = flag; +} + +bool Job::IsPaused() const +{ + return m_paused; +} + +void Job::SetPaused(bool paused) +{ + if (paused) { + Pause(); + } else { + Resume(); + } +} + +void Job::Pause() +{ + if (m_paused) { + return; + } + + // Pause + m_paused = true; +} + +void Job::Resume() +{ + if (!m_paused) { + return; + } + + // Continue + m_paused = false; + + // Trigger next steps + CONTROLLER_POST_EVENT(Logic::InstallerController, + InstallerControllerEvents::NextStepEvent(this)); +} + +void Job::SetJobHandle(JobHandle handle) +{ + m_handle = handle; +} + +JobHandle Job::GetJobHandle() const +{ + return m_handle; +} + +void Job::SendProgress() +{} + +void Job::SendFinishedSuccess() +{} + +void Job::SendFinishedFailure() +{} + +void Job::SendProgressIconPath(const std::string &/*path*/) +{} + +void Job::SaveExceptionData(const Jobs::JobExceptionBase&) +{} +} //namespace Jobs diff --git a/src_wearable/jobs/job.h b/src_wearable/jobs/job.h new file mode 100644 index 0000000..3963fc6 --- /dev/null +++ b/src_wearable/jobs/job.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INSTALLER_MODEL_H +#define INSTALLER_MODEL_H + +#include + +#include + +namespace Jobs { +class JobExceptionBase; + +typedef int JobHandle; + +class Job : + public DPL::MutableTaskList +{ + public: + Job(InstallationType installType); + + InstallationType GetInstallationType() const; + + // Undo + void SetAbortStarted(bool flag); + bool GetAbortStarted() const; + + // Pause/resume support + bool IsPaused() const; + void SetPaused(bool paused); + void Pause(); + void Resume(); + void SetJobHandle(JobHandle handle); + JobHandle GetJobHandle() const; + virtual void SendProgress(); + virtual void SendFinishedSuccess(); + virtual void SendFinishedFailure(); + virtual void SendProgressIconPath(const std::string &path); + + virtual void SaveExceptionData(const Jobs::JobExceptionBase&); + + private: + JobHandle m_handle; + InstallationType m_installationType; + bool m_abortStarted; + bool m_paused; +}; +} //namespace Jobs + +#endif // INSTALLER_MODEL_H diff --git a/src_wearable/jobs/job_base.h b/src_wearable/jobs/job_base.h new file mode 100644 index 0000000..edcf0f5 --- /dev/null +++ b/src_wearable/jobs/job_base.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SRC_INSTALLER_CORE_JOBS_JOB_BASE_H +#define SRC_INSTALLER_CORE_JOBS_JOB_BASE_H + +#include + +typedef std::string ProgressDescription; +typedef float ProgressPercent; + +namespace Jobs { +template +class JobProgressBase +{ + protected: + bool m_progressFlag; + ProgressDescription m_progresDescription; + ProgressPercent m_progresPercent; + + public: + JobProgressBase() : m_progressFlag(false), + m_progresPercent(0.0) + {} + + void SetProgressFlag(bool flag) + { + m_progressFlag = flag; + } + bool GetProgressFlag() const + { + return m_progressFlag; + } + + ProgressDescription GetProgressDescription() const + { + return m_progresDescription; + } + + ProgressPercent GetProgressPercent() const + { + return m_progresPercent; + } + + void UpdateProgress(T_InstallationStep step, + ProgressDescription const &description) + { + m_progresPercent = + ((static_cast(step)) / + static_cast(lastElement)) * 100; + m_progresDescription = description; + } +}; + +template +class JobContextBase +{ + public: + JobContextBase(const T_JobStruct& jobStruct) : + m_jobStruct(jobStruct) + {} + + T_JobStruct GetInstallerStruct() const + { + return m_jobStruct; + } + + protected: + T_JobStruct m_jobStruct; +}; + +template +struct JobCallbacksBase +{ + T_finishedCb finishedCallback; + T_progressCb progressCallback; + void *userParam; + + // It must be empty-constructible as a parameter of generic event + JobCallbacksBase() : + finishedCallback(0), + progressCallback(0), + userParam(0) + {} + + JobCallbacksBase(T_finishedCb finished, + T_progressCb progress, + void *param) : + finishedCallback(finished), + progressCallback(progress), + userParam(param) + {} +}; +} //namespace Jobs + +#endif // SRC_INSTALLER_CORE_JOBS_JOB_BASE_H diff --git a/src_wearable/jobs/job_exception_base.h b/src_wearable/jobs/job_exception_base.h new file mode 100644 index 0000000..4d35420 --- /dev/null +++ b/src_wearable/jobs/job_exception_base.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_exception_base.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#include + +#ifndef SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ +#define SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ + +#define DECLARE_JOB_EXCEPTION_BASE(Base, Class, Param) \ + class Class : \ + public Base { \ + public: \ + Class(const char *path, \ + const char *function, \ + int line, \ + const std::string & message = std::string()) : \ + Base(path, function, line, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + Class(const char *path, \ + const char *function, \ + int line, \ + const Exception &reason, \ + const std::string & message = std::string()) : \ + Base(path, function, line, reason, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + virtual int getParam() const \ + { \ + return m_param; \ + } \ + protected: \ + int m_param; \ + }; + +#define DECLARE_JOB_EXCEPTION(Base, Class, Param) \ + class Class : \ + public Base { \ + public: \ + Class(const char *path, \ + const char *function, \ + int line, \ + const std::string & message = std::string()) : \ + Base(path, function, line, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + Class(const char *path, \ + const char *function, \ + int line, \ + const Exception &reason, \ + const std::string & message = std::string()) : \ + Base(path, function, line, reason, message) \ + { \ + m_className = #Class; \ + m_param = Param; \ + } \ + \ + virtual int getParam() const \ + { \ + return m_param; \ + } \ + }; + +namespace Jobs { +DECLARE_JOB_EXCEPTION_BASE(DPL::Exception, JobExceptionBase, 0) +} + +#endif /* SRC_INSTALLER_CORE_JOBS_JOB_EXCEPTION_BASE_H_ */ diff --git a/src_wearable/jobs/job_exception_error.h b/src_wearable/jobs/job_exception_error.h new file mode 100644 index 0000000..1caba99 --- /dev/null +++ b/src_wearable/jobs/job_exception_error.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_exception_error.h + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief This file contains declarations of wrt api + */ + +/* + * @defgroup wrt_engine_group WebRunTime engine Library + * @ingroup internet_FW + * Functions to APIs to access wrt-engine + */ + +#ifndef JOB_EXCEPTION_ERROR_H +#define JOB_EXCEPTION_ERROR_H + +#include +#include + +namespace Jobs { +namespace Exceptions { +enum Type +{ + Success = 0, ///< Success + + /* pkgmgr error */ + ErrorPackageNotFound, ///< + ErrorPackageInvalid, ///< invalid widget package + ErrorPackageLowerVersion, ///< given version is lower + ErrorPackageExecutableNotFound, + + ErrorManifestNotFound = 11, ///< + ErrorManifestInvalid, ///< + ErrorConfigNotFound, ///< couldn't find config.xml + ErrorConfigInvalid, ///< invalid config.xml + + ErrorSignatureNotFound = 21, ///< signature file not exist. + ErrorSignatureInvalid, ///< invalid signature file + ErrorSignatureVerificationFailed, ///< failure in verificate + ///< signature + ErrorRootCertificateNotFound = 31, ///< couldn't find root + ErrorCertificationInvaid, ///< invalid certification + ErrorCertificateChainVerificationFailed, ///< failure in verificate + ErrorCertificateExpired, ///< expire cerification. + + ErrorInvalidPrivilege = 41, ///< invalid privilege. + ErrorPrivilegeLevelViolation, + + ErrorMenuIconNotFound = 51, ///< + + ErrorFatalError = 61, ///< failure in db operation + ErrorOutOfStorage, ///< failure in shortage of memory + ErrorOutOfMemory, ///< failure in shortage of RAM + ErrorArgumentInvalid, + + /* wrt-installer error */ + /* 121-140 : reserved for Web installer */ + ErrorPackageAlreadyInstalled = 121, ///< package already in target. + ErrorAceCheckFailed, ///< failure in ace check. + ErrorManifestCreateFailed, ///< failure in creating manifest + ErrorEncryptionFailed, ///< failure in encryption resource + ErrorInstallOspServcie, ///< Failure in installing osp service + ErrorPluginInstallationFailed, ///< failure in plugin installation + ErrorWidgetUninstallationFailed, ///< failure in uninstallation + ErrorNotSupportRDSUpdate, ///< failure in rds update + + ErrorUnknown = 140, ///< do not use this error code. +}; +} +} + +#endif /* JOB_EXCEPTION_ERROR_H */ diff --git a/src_wearable/jobs/job_types.h b/src_wearable/jobs/job_types.h new file mode 100644 index 0000000..5f845bf --- /dev/null +++ b/src_wearable/jobs/job_types.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_types.h + * @author Tomasz Iwanek () + */ +#ifndef JOB_TYPES_H +#define JOB_TYPES_H + +namespace Jobs { +/** + * @brief Defines installation and uninstallation type. + */ +enum InstallationType +{ + UnknownInstallation, ///< defines installation of yet unknown type + NewInstallation, ///< defines install process + UpdateInstallation, ///< defines update installation + Uninstallation, ///< defines uninstall process + PluginInstallation ///< defines plugin installation process +}; + +} + +#endif // JOB_TYPES_H diff --git a/src_wearable/jobs/plugin_install/job_plugin_install.cpp b/src_wearable/jobs/plugin_install/job_plugin_install.cpp new file mode 100755 index 0000000..36a5d83 --- /dev/null +++ b/src_wearable/jobs/plugin_install/job_plugin_install.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_plugin_install.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ +#include +#include +#include "plugin_objects.h" +#include +#include + +namespace Jobs { +namespace PluginInstall { +JobPluginInstall::JobPluginInstall(PluginPath const &pluginPath, + const PluginInstallerStruct &installerStruct) + : + Job(PluginInstallation), + JobContextBase(installerStruct), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + // + // Init installer context + // + m_context.pluginFilePath = pluginPath; + m_context.pluginHandle = INVALID_HANDLE; + m_context.installationCompleted = false; + + m_context.installerTask = this; + // + // Create main installation tasks + // + AddTask(new PluginInstallTask(&m_context)); +} + +void JobPluginInstall::SendProgress() +{ + if (GetProgressFlag() && GetInstallerStruct().progressCallback != NULL) { + _D("Call Plugin install progressCallback"); + GetInstallerStruct().progressCallback(GetInstallerStruct().userParam, + GetProgressPercent(), + GetProgressDescription()); + } +} + +void JobPluginInstall::SendFinishedSuccess() +{ + PluginHandle handle = getNewPluginHandle(); + + if (handle != Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE && + isReadyToInstall()) + { + _D("Call Plugin install success finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + Jobs::Exceptions::Success); + } else { + _D("Call Plugin install waiting finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + Jobs::Exceptions::ErrorPluginInstallationFailed); + + _D("Installation: %s NOT possible", getFilePath().c_str()); + } +} + +void JobPluginInstall::SendFinishedFailure() +{ + LOGE(COLOR_ERROR "Error in plugin installation step: %d" COLOR_END, m_exceptionCaught); + LOGE(COLOR_ERROR "Message: %s" COLOR_END, m_exceptionMessage.c_str()); + fprintf(stderr, "[Err:%d] %s", m_exceptionCaught, m_exceptionMessage.c_str()); + + _D("Call Plugin install failure finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + m_exceptionCaught); +} + +void JobPluginInstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} +} //namespace Jobs +} //namespace PluginInstall diff --git a/src_wearable/jobs/plugin_install/job_plugin_install.h b/src_wearable/jobs/plugin_install/job_plugin_install.h new file mode 100644 index 0000000..a8d3386 --- /dev/null +++ b/src_wearable/jobs/plugin_install/job_plugin_install.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_plugin_install.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +namespace Jobs { +namespace PluginInstall { +class JobPluginInstall : + public Job, + public JobProgressBase, + public JobContextBase +{ + public: + static const WrtDB::DbPluginHandle INVALID_HANDLE = -1; + + public: + /** + * @brief Automaticaly sets installation process + */ + JobPluginInstall(PluginPath const &pluginPath, + const PluginInstallerStruct &installerStruct); + + WrtDB::DbPluginHandle getNewPluginHandle() const + { + return m_context.pluginHandle; + } + std::string getFilePath() const + { + return m_context.pluginFilePath.Fullpath(); + } + bool isReadyToInstall() const + { + return m_context.installationCompleted; + } + + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SaveExceptionData(const Jobs::JobExceptionBase &e); + + private: + PluginInstallerContext m_context; + + //TODO move it to base class of all jobs + //maybe separate JobBase class for this? + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; +}; +} //namespace Jobs +} //namespace PluginInstall + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_JOB_PLUGIN_INSTALL_H_ */ diff --git a/src_wearable/jobs/plugin_install/plugin_install_task.cpp b/src_wearable/jobs/plugin_install/plugin_install_task.cpp new file mode 100644 index 0000000..5f42730 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_install_task.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file install_one_task.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include "plugin_install_task.h" +#include "job_plugin_install.h" +#include "plugin_installer_errors.h" +#include "plugin_metafile_reader.h" +#include +//#include +#include +#include +#include +#include "plugin_objects.h" +#include +#include +#include + +using namespace WrtDB; + +#define SET_PLUGIN_INSTALL_PROGRESS(step, desc) \ + m_context->installerTask->UpdateProgress( \ + PluginInstallerContext::step, desc); + +#define DISABLE_IF_PLUGIN_WITHOUT_LIB() \ + if (m_pluginInfo.m_libraryName.empty()) \ + { \ + _W("Plugin without library."); \ + return; \ + } + +namespace Jobs { +namespace PluginInstall { +PluginInstallTask::PluginInstallTask(PluginInstallerContext *inCont) : + DPL::TaskDecl(this), + m_context(inCont), + m_pluginHandle(0), + m_dataFromConfigXML(true) +{ + AddStep(&PluginInstallTask::stepCheckPluginPath); + AddStep(&PluginInstallTask::stepParseConfigFile); + AddStep(&PluginInstallTask::stepFindPluginLibrary); + AddStep(&PluginInstallTask::stepCheckIfAlreadyInstalled); + AddStep(&PluginInstallTask::stepLoadPluginLibrary); + AddStep(&PluginInstallTask::stepRegisterPlugin); + AddStep(&PluginInstallTask::stepRegisterFeatures); + AddStep(&PluginInstallTask::stepRegisterPluginObjects); + AddStep(&PluginInstallTask::stepResolvePluginDependencies); + + SET_PLUGIN_INSTALL_PROGRESS(START, "Installation initialized"); +} + +PluginInstallTask::~PluginInstallTask() +{} + +void PluginInstallTask::stepCheckPluginPath() +{ + _D("Plugin installation: step CheckPluginPath"); + + if(!m_context->pluginFilePath.Exists()){ + ThrowMsg(Exceptions::PluginPathFailed, + "No such path"); + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_PATH, "Path to plugin verified"); +} + +void PluginInstallTask::stepParseConfigFile() +{ + _D("Plugin installation: step parse config file"); + + if(!m_context->pluginFilePath.getMetaFile().Exists()){ + m_dataFromConfigXML = false; + return; + } + + _D("Plugin Config file::%s", m_context->pluginFilePath.getMetaFile().Fullpath().c_str()); + + Try + { + PluginMetafileReader reader; + reader.initialize(m_context->pluginFilePath.getMetaFile()); + reader.read(m_pluginInfo); + + FOREACH(it, m_pluginInfo.m_featureContainer) + { + _D("Parsed feature : %s", it->m_name.c_str()); + FOREACH(devCap, it->m_deviceCapabilities) { + _D(" | DevCap : %s", (*devCap).c_str()); + } + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_PATH, "Config file analyzed"); + } + Catch(ValidationCore::ParserSchemaException::Base) + { + _E("Error during file processing %s", m_context->pluginFilePath.getMetaFile().Fullpath().c_str()); + ThrowMsg(Exceptions::PluginMetafileFailed, + "Metafile error"); + } +} + +void PluginInstallTask::stepFindPluginLibrary() +{ + if (m_dataFromConfigXML) { + return; + } + _D("Plugin installation: step find plugin library"); + _D("Plugin .so: %s", m_context->pluginFilePath.getLibraryName().c_str()); + m_pluginInfo.m_libraryName = m_context->pluginFilePath.getLibraryName(); +} + +void PluginInstallTask::stepCheckIfAlreadyInstalled() +{ + if (PluginDAO::isPluginInstalled(m_pluginInfo.m_libraryName)) { + ThrowMsg(Exceptions::PluginAlreadyInstalled, + "Plugin already installed"); + } + + SET_PLUGIN_INSTALL_PROGRESS(PLUGIN_EXISTS_CHECK, "Check if plugin exist"); +} + +void PluginInstallTask::stepLoadPluginLibrary() +{ + _D("Plugin installation: step load library"); + + DISABLE_IF_PLUGIN_WITHOUT_LIB() + + _D("Loading plugin: %s", m_context->pluginFilePath.getLibraryName().c_str()); + + fprintf(stderr, " - Try to dlopen() : [%s] ", m_context->pluginFilePath.getLibraryPath().Fullpath().c_str()); + + void *dlHandle = dlopen( m_context->pluginFilePath.getLibraryPath().Fullpath().c_str(), RTLD_LAZY); + if (dlHandle == NULL) { + const char* error = (const char*)dlerror(); + fprintf(stderr, + "-> Failed!\n %s\n", + (error != NULL ? error : "unknown")); + _E("Failed to load plugin: %s. Reason: %s", + m_context->pluginFilePath.getLibraryName().c_str(), (error != NULL ? error : "unknown")); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + fprintf(stderr, "-> Done.\n"); + + const js_entity_definition_t *rawEntityList = NULL; + get_widget_entity_map_proc *getWidgetEntityMapProcPtr = NULL; + + getWidgetEntityMapProcPtr = + reinterpret_cast(dlsym(dlHandle, + PLUGIN_GET_CLASS_MAP_PROC_NAME)); + + if (getWidgetEntityMapProcPtr) { + rawEntityList = (*getWidgetEntityMapProcPtr)(); + } else { + rawEntityList = + static_cast(dlsym(dlHandle, + PLUGIN_CLASS_MAP_NAME)); + } + + if (rawEntityList == NULL) { + dlclose(dlHandle); + _E("Failed to read class name %s", m_context->pluginFilePath.getLibraryName().c_str()); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + if (!m_dataFromConfigXML) { + on_widget_init_proc *onWidgetInitProc = + reinterpret_cast( + dlsym(dlHandle, PLUGIN_WIDGET_INIT_PROC_NAME)); + + if (NULL == onWidgetInitProc) { + dlclose(dlHandle); + _E("Failed to read onWidgetInit symbol %s", m_context->pluginFilePath.getLibraryName().c_str()); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + // obtain feature -> dev-cap mapping + feature_mapping_interface_t mappingInterface = { NULL, NULL, NULL }; + (*onWidgetInitProc)(&mappingInterface); + + if (!mappingInterface.featGetter || !mappingInterface.release || + !mappingInterface.dcGetter) + { + _E("Failed to obtain mapping interface from .so"); + ThrowMsg(Exceptions::PluginLibraryError, "Library error"); + } + + feature_mapping_t* devcapMapping = mappingInterface.featGetter(); + + _D("Getting mapping from features to device capabilities"); + + for (size_t i = 0; i < devcapMapping->featuresCount; ++i) { + PluginMetafileData::Feature feature; + feature.m_name = devcapMapping->features[i].feature_name; + + _D("Feature: %s", feature.m_name.c_str()); + + const devcaps_t* dc = + mappingInterface.dcGetter( + devcapMapping, + devcapMapping->features[i]. + feature_name); + + if (dc) { + _D("devcaps count: %d", dc->devCapsCount); + + for (size_t j = 0; j < dc->devCapsCount; ++j) { + _D("devcap: %s", dc->deviceCaps[j]); + feature.m_deviceCapabilities.insert(dc->deviceCaps[j]); + } + } + + m_pluginInfo.m_featureContainer.insert(feature); + } + + mappingInterface.release(devcapMapping); + } + + m_libraryObjects = PluginObjectsPtr(new PluginObjects()); + const js_entity_definition_t *rawEntityListIterator = rawEntityList; + + _D("#####"); + _D("##### Plugin: %s supports new plugin API", + m_context->pluginFilePath.getLibraryName().c_str()); + _D("#####"); + + while (rawEntityListIterator->parent_name != NULL && + rawEntityListIterator->object_name != NULL) + { + _D("##### [%s]: ", rawEntityListIterator->object_name); + _D("##### Parent: %s", rawEntityListIterator->parent_name); + _D("#####"); + + m_libraryObjects->addObjects(rawEntityListIterator->parent_name, + rawEntityListIterator->object_name); + + ++rawEntityListIterator; + } + + // Unload library + if (dlclose(dlHandle) != 0) { + _E("Cannot close plugin handle"); + } else { + _D("Library is unloaded"); + } + + // Load export table + _D("Library successfuly loaded and parsed"); + + SET_PLUGIN_INSTALL_PROGRESS(LOADING_LIBRARY, "Library loaded and analyzed"); +} + +void PluginInstallTask::stepRegisterPlugin() +{ + _D("Plugin installation: step register Plugin"); + + m_pluginHandle = + PluginDAO::registerPlugin(m_pluginInfo, m_context->pluginFilePath.Fullpath()); + + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_PLUGIN, "Plugin registered"); +} + +void PluginInstallTask::stepRegisterFeatures() +{ + _D("Plugin installation: step register features"); + + FOREACH(it, m_pluginInfo.m_featureContainer) + { + _D("PluginHandle: %d", m_pluginHandle); + FeatureDAO::RegisterFeature(*it, m_pluginHandle); + } + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_FEATURES, "Features registered"); +} + +void PluginInstallTask::stepRegisterPluginObjects() +{ + _D("Plugin installation: step register objects"); + + DISABLE_IF_PLUGIN_WITHOUT_LIB() + + //register implemented objects + PluginObjects::ObjectsPtr objects = + m_libraryObjects->getImplementedObject(); + + FOREACH(it, *objects) + { + PluginDAO::registerPluginImplementedObject(*it, m_pluginHandle); + } + + //register requiredObjects + objects = m_libraryObjects->getDependentObjects(); + + FOREACH(it, *objects) + { + if (m_libraryObjects->hasObject(*it)) { + _D("Dependency from the same library. ignored"); + continue; + } + + PluginDAO::registerPluginRequiredObject(*it, m_pluginHandle); + } + + SET_PLUGIN_INSTALL_PROGRESS(REGISTER_OBJECTS, "Plugin Objects registered"); +} + +void PluginInstallTask::stepResolvePluginDependencies() +{ + _D("Plugin installation: step resolve dependencies "); + + //DISABLE_IF_PLUGIN_WITHOUT_LIB + if (m_pluginInfo.m_libraryName.empty()) { + PluginDAO::setPluginInstallationStatus( + m_pluginHandle, + PluginDAO:: + INSTALLATION_COMPLETED); + //Installation completed + m_context->pluginHandle = m_pluginHandle; + m_context->installationCompleted = true; + _W("Plugin without library."); + return; + } + + PluginHandleSetPtr handles = PluginHandleSetPtr(new PluginHandleSet); + + DbPluginHandle handle = INVALID_PLUGIN_HANDLE; + + //register requiredObjects + FOREACH(it, *(m_libraryObjects->getDependentObjects())) + { + if (m_libraryObjects->hasObject(*it)) { + _D("Dependency from the same library. ignored"); + continue; + } + + handle = PluginDAO::getPluginHandleForImplementedObject(*it); + if (handle == INVALID_PLUGIN_HANDLE) { + _E("Library implementing: %s NOT FOUND", (*it).c_str()); + PluginDAO::setPluginInstallationStatus( + m_pluginHandle, + PluginDAO::INSTALLATION_WAITING); + return; + } + + handles->insert(handle); + } + + PluginDAO::registerPluginLibrariesDependencies(m_pluginHandle, handles); + + PluginDAO::setPluginInstallationStatus(m_pluginHandle, + PluginDAO::INSTALLATION_COMPLETED); + + //Installation completed + m_context->pluginHandle = m_pluginHandle; + m_context->installationCompleted = true; + + SET_PLUGIN_INSTALL_PROGRESS(RESOLVE_DEPENDENCIES, "Dependencies resolved"); +} + +#undef SET_PLUGIN_INSTALL_PROGRESS +} //namespace Jobs +} //namespace PluginInstall diff --git a/src_wearable/jobs/plugin_install/plugin_install_task.h b/src_wearable/jobs/plugin_install/plugin_install_task.h new file mode 100644 index 0000000..c7a5d8c --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_install_task.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file install.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALL_H_ +#define INSTALL_H_ + +//WRT INCLUDES +#include +#include "plugin_installer_context.h" +#include "plugin_objects.h" + +namespace Jobs { +namespace PluginInstall { +class PluginInstallTask : + public DPL::TaskDecl +{ + public: + PluginInstallTask(PluginInstallerContext *inCont); + virtual ~PluginInstallTask(); + + private: + //data + PluginInstallerContext *m_context; + + //PluginMetafile + WrtDB::PluginMetafileData m_pluginInfo; + + //Plugin LibraryObjects + PluginObjectsPtr m_libraryObjects; + + WrtDB::DbPluginHandle m_pluginHandle; + + bool m_dataFromConfigXML; + + //steps + void stepCheckPluginPath(); + void stepFindPluginLibrary(); + void stepParseConfigFile(); + void stepCheckIfAlreadyInstalled(); + void stepLoadPluginLibrary(); + void stepRegisterPlugin(); + void stepRegisterFeatures(); + void stepRegisterPluginObjects(); + void stepResolvePluginDependencies(); +}; +} //namespace Jobs +} //namespace PluginInstall + +#endif /* INSTALL_H_ */ diff --git a/src_wearable/jobs/plugin_install/plugin_installer_context.h b/src_wearable/jobs/plugin_install/plugin_installer_context.h new file mode 100644 index 0000000..1efd504 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_installer_context.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file plugin_installer_structs.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief Definition file of plugin installer tasks data structures + */ +#ifndef WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ +#define WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ + +#include +#include +#include +//#include + +using namespace WrtDB; + +namespace Jobs { +namespace PluginInstall { +class JobPluginInstall; +} +} + +struct PluginInstallerContext +{ + enum PluginInstallStep + { + START, + PLUGIN_PATH, + CONFIG_FILE, + PLUGIN_EXISTS_CHECK, + LOADING_LIBRARY, + REGISTER_PLUGIN, + REGISTER_FEATURES, + REGISTER_OBJECTS, + RESOLVE_DEPENDENCIES, + PLUGIN_INSTALL_END + }; + + PluginPath pluginFilePath; ///< plugin directory + PluginPath metaFilePath; + bool m_dataFromConfigXML; + WrtDB::DbPluginHandle pluginHandle; + // if this value is true the plugin model may be created + // if not plugin installation has failed from some reason + bool installationCompleted; + + //used to set installation progress + Jobs::PluginInstall::JobPluginInstall* installerTask; +}; +#endif // WRT_SRC_INSTALLERCORE_PLUGININSTALLERTASKS_PLUGININSTALLERCONTEXT_H_ diff --git a/src_wearable/jobs/plugin_install/plugin_installer_errors.h b/src_wearable/jobs/plugin_install/plugin_installer_errors.h new file mode 100644 index 0000000..11c7f88 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_installer_errors.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_installer_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ + +#ifndef \ + WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ +#define \ + WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ + +#include +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace PluginInstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) +DECLARE_JOB_EXCEPTION(Base, PluginPathFailed, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginMetafileFailed, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginAlreadyInstalled, + ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PluginLibraryError, ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, InstallationWaitingError, + ErrorPluginInstallationFailed) +DECLARE_JOB_EXCEPTION(Base, UnknownError, ErrorUnknown) +} //namespace +} //namespace +} //namespace + +#endif +//WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_INSTALLER_ERRORS_H_ + diff --git a/src_wearable/jobs/plugin_install/plugin_installer_struct.h b/src_wearable/jobs/plugin_install/plugin_installer_struct.h new file mode 100644 index 0000000..c253897 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_installer_struct.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_installer_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ + +#include +#include + +//Plugin Installer typedefs +typedef void (*PluginInstallerFinishedCallback)( + void *userParam, + Jobs::Exceptions::Type); + +//installer progress +typedef void (*PluginInstallerProgressCallback)( + void *userParam, + ProgressPercent percent, + const ProgressDescription &description); + +//Plugin Installetion Struct +typedef Jobs::JobCallbacksBase +PluginInstallerStruct; + +#endif // WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_PLUGIN_INSTALLER_STRUCT_H_ diff --git a/src_wearable/jobs/plugin_install/plugin_metafile_reader.cpp b/src_wearable/jobs/plugin_install/plugin_metafile_reader.cpp new file mode 100644 index 0000000..c7df516 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_metafile_reader.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_metafile_reader.cpp + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#include "plugin_metafile_reader.h" +#include + +using namespace WrtDB; + +namespace { +const std::string XML_NAMESPACE = ""; + +const std::string TOKEN_LIBRARY_NAME = "library-name"; +const std::string TOKEN_API_FEATURE = "api-feature"; +const std::string TOKEN_NAME = "name"; +const std::string TOKEN_DEVICECAPABILITY = "device-capability"; +} + +PluginMetafileReader::PluginMetafileReader() : m_parserSchema(this) +{ + m_parserSchema.addEndTagCallback( + TOKEN_LIBRARY_NAME, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndLibraryName); + + m_parserSchema.addEndTagCallback( + TOKEN_API_FEATURE, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndApiFeature); + + m_parserSchema.addEndTagCallback( + TOKEN_NAME, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndName); + + m_parserSchema.addEndTagCallback( + TOKEN_DEVICECAPABILITY, + XML_NAMESPACE, + &PluginMetafileReader::tokenEndDeviceCapability); +} + +void PluginMetafileReader::initialize(const PluginPath &filename) +{ + m_parserSchema.initialize(filename.Fullpath(), + true, + ValidationCore::SaxReader::VALIDATION_DTD, + std::string()); +} + +void PluginMetafileReader::read(WrtDB::PluginMetafileData &data) +{ + m_parserSchema.read(data); +} + +void PluginMetafileReader::blankFunction(PluginMetafileData & /* data */) +{} + +void PluginMetafileReader::tokenEndLibraryName(PluginMetafileData &data) +{ + data.m_libraryName = m_parserSchema.getText(); +} + +void PluginMetafileReader::tokenEndApiFeature(PluginMetafileData &data) +{ + data.m_featureContainer.insert(m_feature); + m_feature.m_deviceCapabilities.clear(); +} + +void PluginMetafileReader::tokenEndName(PluginMetafileData & /* data */) +{ + m_feature.m_name = m_parserSchema.getText(); +} + +void PluginMetafileReader::tokenEndDeviceCapability(PluginMetafileData& /*data*/) +{ + m_feature.m_deviceCapabilities.insert(m_parserSchema.getText()); +} + diff --git a/src_wearable/jobs/plugin_install/plugin_metafile_reader.h b/src_wearable/jobs/plugin_install/plugin_metafile_reader.h new file mode 100644 index 0000000..a3a3068 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_metafile_reader.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_metafile_reader.h + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_METAFILE_READER_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_METAFILE_READER_H_ + +#include +#include + +class PluginPath; + +class PluginMetafileReader +{ + public: + PluginMetafileReader(); + + void initialize(const PluginPath &filename); + + void read(WrtDB::PluginMetafileData &data); + + private: + void blankFunction(WrtDB::PluginMetafileData &data); + + void tokenEndLibraryName(WrtDB::PluginMetafileData &data); + void tokenEndApiFeature(WrtDB::PluginMetafileData &data); + void tokenEndName(WrtDB::PluginMetafileData &data); + void tokenEndDeviceCapability(WrtDB::PluginMetafileData &data); + + WrtDB::PluginMetafileData::Feature m_feature; + + ValidationCore::ParserSchema m_parserSchema; +}; + +#endif diff --git a/src_wearable/jobs/plugin_install/plugin_objects.cpp b/src_wearable/jobs/plugin_install/plugin_objects.cpp new file mode 100644 index 0000000..9235f47 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_objects.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_objects.h + * @author Grzegorz Krawczyk (g.krawczyk@samgsung.com) + * @version + * @brief + */ +#include +#include +#include "plugin_objects.h" + +namespace { +const char* SEPARATOR = "."; +const std::string GLOBAL_OBJECT_NAME = "GLOBAL_OBJECT"; + +std::string normalizeName(const std::string& objectName) +{ + if (objectName.empty()) { + _E("Normalize name, name size is 0"); + return objectName; + } + + if (!objectName.compare(0, GLOBAL_OBJECT_NAME.size(), + GLOBAL_OBJECT_NAME)) + { + return objectName; + } + + //each object in storage has name started from $GLOBAL_OBJECT_NAME$ + return GLOBAL_OBJECT_NAME + std::string(SEPARATOR) + objectName; +} + +std::string normalizeName(const std::string& objectName, + const std::string& parentName) +{ + if (objectName.empty() || parentName.empty()) { + _E("Normalize name, name size or parent name size is 0"); + return std::string(); + } + + std::string normalizedName; + normalizedName = normalizeName(parentName) + + std::string(SEPARATOR) + objectName; + + return normalizedName; +} +} + +PluginObjects::PluginObjects() +{ + m_implemented = ObjectsPtr(new Objects()); + m_dependent = ObjectsPtr(new Objects()); +} + +PluginObjects::ObjectsPtr PluginObjects::getImplementedObject() const +{ + return m_implemented; +} + +PluginObjects::ObjectsPtr PluginObjects::getDependentObjects() const +{ + return m_dependent; +} + +void PluginObjects::addObjects(const std::string& parentName, + const std::string& name) +{ + addImplementedObject(normalizeName(name, parentName)); + addDependentObject(normalizeName(parentName)); +} + +void PluginObjects::addDependentObject(const std::string& value) +{ + if (!value.compare(GLOBAL_OBJECT_NAME)) { + //dont add dependency to GLOBAL_OBJECT + return; + } + m_dependent->insert(value); +} + +bool PluginObjects::hasObject(const std::string& name) const +{ + return m_implemented->find(name) != m_implemented->end(); +} + +void PluginObjects::addImplementedObject(const std::string& value) +{ + m_implemented->insert(value); +} diff --git a/src_wearable/jobs/plugin_install/plugin_objects.h b/src_wearable/jobs/plugin_install/plugin_objects.h new file mode 100644 index 0000000..0b27a14 --- /dev/null +++ b/src_wearable/jobs/plugin_install/plugin_objects.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_objects.h + * @author Grzegorz Krawczyk(g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_OBJECTS_H_ +#define WRT_SRC_INSTALLER_CORE_PLUGIN_INSTALLER_TASKS_PLUGIN_OBJECTS_H_ + +#include +#include +#include +#include + +#include +//TODO TO BE MOVED SOMEWHERE ELSE +// AS OTHER MODULES (LIKE DAO) USE IT + +class PluginObjects : public WrtDB::PluginObjectsDAO +{ + public: + explicit PluginObjects(); + + //getters for objects from library + ObjectsPtr getImplementedObject() const; + ObjectsPtr getDependentObjects() const; + + //add object declaration + void addObjects(const std::string& parentName, + const std::string& name); + + //check if library implemements object given as name + bool hasObject(const std::string& name) const; + + private: + void addImplementedObject(const std::string& value); + void addDependentObject(const std::string& value); +}; + +typedef std::shared_ptr PluginObjectsPtr; + +#endif diff --git a/src_wearable/jobs/widget_install/ace_registration.cpp b/src_wearable/jobs/widget_install/ace_registration.cpp new file mode 100644 index 0000000..31df490 --- /dev/null +++ b/src_wearable/jobs/widget_install/ace_registration.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ace_registration.cpp + * @author Andrzej Surdej (a.surdej@gmail.com) + * @version 1.0 + * @brief Translate structures to ace api - implementation file + */ + +#include +#include +#include +#include + +#include + +namespace { +char* toAceString(const DPL::OptionalString& os) +{ + if (!!os) { + return strdup(DPL::ToUTF8String(*os).c_str()); + } else { + return NULL; + } +} + +char* toAceString(const std::string& str) +{ + if (!str.empty()) { + return strdup(str.c_str()); + } else { + return NULL; + } +} +} //anonymous namespace + +namespace AceApi { +bool registerAceWidget(const WrtDB::DbWidgetHandle& widgetHandle, + const WrtDB::WidgetRegisterInfo& widgetConfig, + const WrtDB::WidgetCertificateDataList& certList) +{ + _D("Updating Ace database"); + struct widget_info wi; + + switch (widgetConfig.webAppType.appType) { + case WrtDB::APP_TYPE_TIZENWEBAPP: + wi.type = Tizen; + break; + default: + _E("Unknown application type"); + return false; + } + + wi.id = toAceString(widgetConfig.configInfo.widget_id); + wi.version = toAceString(widgetConfig.configInfo.version); + wi.author = toAceString(widgetConfig.configInfo.authorName); + // TODO: Need to remove "shareHref" from "widget_info" in wrt-security + wi.shareHerf = NULL; + _D("Basic data converted. Certificates begin."); + + //one more element for NULL termination + _D("Found: %d certificates", certList.size()); + ace_certificate_data** certData = new ace_certificate_data * + [certList.size() + 1]; + certData[certList.size()] = NULL; // last element set to NULL + + int i = 0; + FOREACH(it, certList) + { + certData[i] = new ace_certificate_data; + switch (it->owner) { + case WrtDB::WidgetCertificateData::AUTHOR: + certData[i]->owner = AUTHOR; + break; + case WrtDB::WidgetCertificateData::DISTRIBUTOR: + certData[i]->owner = DISTRIBUTOR; + break; + default: + _D("Unknown owner type of cert"); + certData[i]->owner = UNKNOWN; + break; + } + switch (it->type) { + case WrtDB::WidgetCertificateData::ENDENTITY: + certData[i]->type = ENDENTITY; + break; + case WrtDB::WidgetCertificateData::ROOT: + certData[i]->type = ROOT; + break; + default: + _E("Unknown type of cert"); + certData[i]->type = ENDENTITY; + break; + } + certData[i]->chain_id = it->chainId; + + certData[i]->md5_fp = toAceString(it->strMD5Fingerprint); + certData[i]->sha1_fp = toAceString(it->strSHA1Fingerprint); + certData[i]->common_name = + toAceString(DPL::ToUTF8String(it->strCommonName)); + ++i; + } + + _D("Registerign widget in ace"); + ace_return_t retval = ace_register_widget( + static_cast(widgetHandle), &wi, certData); + + //clean up - WidgetInfo + free(wi.author); + free(wi.id); + free(wi.version); + + //free cert list + i = 0; + while (certData[i] != NULL) { + free(certData[i]->common_name); + free(certData[i]->md5_fp); + free(certData[i]->sha1_fp); + delete certData[i]; + ++i; + } + delete[] certData; + return retval == ACE_OK; +} +bool registerAceWidgetFromDB(const WrtDB::DbWidgetHandle& widgetHandle) +{ + using namespace WrtDB; + _D("Updating Ace database from Widget DB"); + struct widget_info wi; + DPL::OptionalString os; + WrtDB::WidgetCertificateDataList certList; + + wi.id = NULL; + wi.version = NULL; + wi.author = NULL; + wi.shareHerf = NULL; + + Try { + WidgetDAOReadOnly dao(widgetHandle); + + WidgetType type = dao.getWidgetType(); + if (type == WrtDB::APP_TYPE_TIZENWEBAPP) { + wi.type = Tizen; + } else { + _E("Unknown application type"); + return false; + } + + wi.id = toAceString(dao.getGUID()); + wi.version = toAceString(dao.getVersion()); + wi.author = toAceString(dao.getAuthorName()); + // TODO: Need to remove "shareHref" from "widget_info" in wrt-security + wi.shareHerf = NULL; + _D("Basic data converted. Certificates begin."); + certList = dao.getCertificateDataList(); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + _E("Widget does not exist"); + if(wi.id != NULL) { + free(wi.id); + } + if(wi.version != NULL) { + free(wi.version); + } + if(wi.author != NULL) { + free(wi.author); + } + if(wi.shareHerf != NULL) { + free(wi.shareHerf); + } + return false; + } + + //one more element for NULL termination + _D("Found: %d certificates", certList.size()); + ace_certificate_data** certData = new ace_certificate_data * + [certList.size() + 1]; + certData[certList.size()] = NULL; // last element set to NULL + + int i = 0; + FOREACH(it, certList) + { + certData[i] = new ace_certificate_data; + switch (it->owner) { + case WrtDB::WidgetCertificateData::AUTHOR: + certData[i]->owner = AUTHOR; + break; + case WrtDB::WidgetCertificateData::DISTRIBUTOR: + certData[i]->owner = DISTRIBUTOR; + break; + default: + _D("Unknown owner type of cert"); + certData[i]->owner = UNKNOWN; + break; + } + switch (it->type) { + case WrtDB::WidgetCertificateData::ENDENTITY: + certData[i]->type = ENDENTITY; + break; + case WrtDB::WidgetCertificateData::ROOT: + certData[i]->type = ROOT; + break; + default: + _E("Unknown type of cert"); + certData[i]->type = ENDENTITY; + break; + } + certData[i]->chain_id = it->chainId; + + certData[i]->md5_fp = toAceString(it->strMD5Fingerprint); + certData[i]->sha1_fp = toAceString(it->strSHA1Fingerprint); + certData[i]->common_name = + toAceString(DPL::ToUTF8String(it->strCommonName)); + ++i; + } + + _D("Registerign widget in ace"); + ace_return_t retval = ace_register_widget( + static_cast(widgetHandle), &wi, certData); + + //clean up - WidgetInfo + free(wi.author); + free(wi.id); + free(wi.version); + + //free cert list + i = 0; + while (certData[i] != NULL) { + free(certData[i]->common_name); + free(certData[i]->md5_fp); + free(certData[i]->sha1_fp); + delete certData[i]; + ++i; + } + delete[] certData; + return retval == ACE_OK; +} +} diff --git a/src_wearable/jobs/widget_install/ace_registration.h b/src_wearable/jobs/widget_install/ace_registration.h new file mode 100644 index 0000000..f98c3ce --- /dev/null +++ b/src_wearable/jobs/widget_install/ace_registration.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ace_registration.h + * @author Andrzej Surdej (a.surdej@gmail.com) + * @version 1.0 + * @brief Translate structures to ace api - header file + */ +#ifndef WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ +#define WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ + +#include + +namespace AceApi { +bool registerAceWidget(const WrtDB::DbWidgetHandle& widgetHandle, + const WrtDB::WidgetRegisterInfo& widgetConfig, + const WrtDB::WidgetCertificateDataList& certList); +bool registerAceWidgetFromDB(const WrtDB::DbWidgetHandle& widgetHandle); +} + +#endif /* WRT_SRC_INSTALLER_CORE_ACE_REGISTRATION_H_ */ + diff --git a/src_wearable/jobs/widget_install/directory_api.cpp b/src_wearable/jobs/widget_install/directory_api.cpp new file mode 100644 index 0000000..63532ec --- /dev/null +++ b/src_wearable/jobs/widget_install/directory_api.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file directory_api.cpp + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief directory api - implementation file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DirectoryApi { +bool DirectoryCopy(std::string source, std::string dest) +{ + DIR* dir = opendir(source.c_str()); + if (NULL == dir) { + return false; + } + + struct dirent dEntry; + struct dirent *dEntryResult; + int return_code; + + do { + struct stat statInfo; + return_code = readdir_r(dir, &dEntry, &dEntryResult); + if (dEntryResult != NULL && return_code == 0) { + std::string fileName = dEntry.d_name; + std::string fullName = source + "/" + fileName; + + if (stat(fullName.c_str(), &statInfo) != 0) { + closedir(dir); + return false; + } + + if (S_ISDIR(statInfo.st_mode)) { + if (("." == fileName) || (".." == fileName)) { + continue; + } + std::string destFolder = dest + "/" + fileName; + WrtUtilMakeDir(destFolder); + + if (!DirectoryCopy(fullName, destFolder)) { + closedir(dir); + return false; + } + } + + std::string destFile = dest + "/" + fileName; + std::ifstream infile(fullName); + std::ofstream outfile(destFile); + outfile << infile.rdbuf(); + outfile.close(); + infile.close(); + + errno = 0; + if (-1 == chown(destFile.c_str(), + statInfo.st_uid, + statInfo.st_gid)) { + int error = errno; + _D("Failed to change owner [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } while (dEntryResult != NULL && return_code == 0); + closedir(dir); + return true; +} +} diff --git a/src_wearable/jobs/widget_install/directory_api.h b/src_wearable/jobs/widget_install/directory_api.h new file mode 100644 index 0000000..1632528 --- /dev/null +++ b/src_wearable/jobs/widget_install/directory_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file directory_api.h + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief directory api - header file + */ +#ifndef WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ +#define WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ + +#include + +namespace DirectoryApi { +bool DirectoryCopy(std::string source, std::string dest); +} + +#endif /* WRT_SRC_INSTALLER_CORE_DIRECTORY_API_H_ */ + diff --git a/src_wearable/jobs/widget_install/job_widget_install.cpp b/src_wearable/jobs/widget_install/job_widget_install.cpp new file mode 100644 index 0000000..14e01c5 --- /dev/null +++ b/src_wearable/jobs/widget_install/job_widget_install.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file job_widget_install.cpp + * @author Radoslaw Wicik r.wicik@samsung.com + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for main installer task + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace WrtDB; +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { + +JobWidgetInstall::JobWidgetInstall( + std::string const &widgetPath, + std::string const &tzPkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct) : + Job(UnknownInstallation), + JobContextBase(installerStruct), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + m_installerContext.mode = m_jobStruct.m_installMode; + m_installerContext.requestedPath = widgetPath; + m_jobStruct.pkgmgrInterface->setPkgname(tzPkgId); + + if (InstallMode::Command::RECOVERY == m_installerContext.mode.command) { + m_installerContext.widgetConfig.tzPkgid = DPL::FromUTF8String(tzPkgId); + AddTask(new TaskRecovery(m_installerContext)); + } + + //start configuration of installation + AddTask(new TaskConfiguration(m_installerContext)); + + // Init installer context + m_installerContext.installStep = InstallerContext::INSTALL_START; + m_installerContext.job = this; + + m_installerContext.callerPkgId + = DPL::FromUTF8String(m_jobStruct.pkgmgrInterface->getCallerId()); + _D("Caller Package Id : %s", DPL::ToUTF8String(m_installerContext.callerPkgId).c_str()); +} + +void JobWidgetInstall::appendNewInstallationTaskList() +{ + _D("Configure installation succeeded"); + m_installerContext.job->SetProgressFlag(true); + + AddTask(new TaskFileManipulation(m_installerContext)); + AddTask(new TaskProcessConfig(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + AddTask(new TaskPrepareFiles(m_installerContext)); + } + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::appendUpdateInstallationTaskList() +{ + _D("Configure installation updated"); + _D("Widget Update"); + m_installerContext.job->SetProgressFlag(true); + AddTask(new TaskStatusCheck(m_installerContext)); + + if (m_installerContext.mode.command == + InstallMode::Command::REINSTALL) { + AddTask(new TaskPrepareReinstall(m_installerContext)); + } + + if (m_installerContext.mode.extension != + InstallMode::ExtensionType::DIR) { + AddTask(new TaskUpdateFiles(m_installerContext)); + AddTask(new TaskFileManipulation(m_installerContext)); + } + + AddTask(new TaskProcessConfig(m_installerContext)); + + if (m_installerContext.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) { + AddTask(new TaskPrepareFiles(m_installerContext)); + } + + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + //TODO: remove widgetHandle from this task and move before database task + // by now widget handle is needed in ace check + // Any error in acecheck while update will break widget + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskRemoveBackupFiles(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::appendRDSUpdateTaskList() +{ + _D("Configure installation RDS updated"); + m_installerContext.job->SetProgressFlag(true); + AddTask(new TaskStatusCheck(m_installerContext)); + AddTask(new TaskPrepareReinstall(m_installerContext)); + AddTask(new TaskCertify(m_installerContext)); +} + +void JobWidgetInstall::appendRecoveryTaskList() +{ + _D("Recovery Task"); + m_installerContext.job->SetProgressFlag(true); + + AddTask(new TaskProcessConfig(m_installerContext)); + + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + AddTask(new TaskSmack(m_installerContext)); +} + +void JobWidgetInstall::appendFotaInstallationTaskList() +{ + /* TODO */ + _D("Configure installation succeeded"); + m_installerContext.job->SetProgressFlag(true); + + AddTask(new TaskProcessConfig(m_installerContext)); + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) + { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskRemoveBackupFiles(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::appendFotaUpdateTaskList() +{ + _D("Configure installation updated"); + _D("Widget Update"); + m_installerContext.job->SetProgressFlag(true); + + AddTask(new TaskProcessConfig(m_installerContext)); + AddTask(new TaskCertify(m_installerContext)); + AddTask(new TaskCertifyLevel(m_installerContext)); + AddTask(new TaskUserDataManipulation(m_installerContext)); + if (m_installerContext.needEncryption) { + AddTask(new TaskEncryptResource(m_installerContext)); + } + + AddTask(new TaskManifestFile(m_installerContext)); + if (m_installerContext.widgetConfig.packagingType == + PKG_TYPE_HYBRID_WEB_APP) + { + AddTask(new TaskInstallOspsvc(m_installerContext)); + } + + AddTask(new TaskDatabase(m_installerContext)); + AddTask(new TaskAceCheck(m_installerContext)); + //TODO: remove widgetHandle from this task and move before database task + // by now widget handle is needed in ace check + // Any error in acecheck while update will break widget + AddTask(new TaskSmack(m_installerContext)); + AddTask(new TaskRemoveBackupFiles(m_installerContext)); + AddTask(new TaskPkgInfoUpdate(m_installerContext)); +} + +void JobWidgetInstall::SendProgress() +{ + using namespace PackageManager; + if (GetProgressFlag() != false) { + if (GetInstallerStruct().progressCallback != NULL) { + // send progress signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->sendProgress(GetProgressPercent()); + + _D("Call widget install progressCallback"); + GetInstallerStruct().progressCallback( + GetInstallerStruct().userParam, + GetProgressPercent(), + GetProgressDescription()); + } + } +} + +void JobWidgetInstall::SendProgressIconPath(const std::string &path) +{ + using namespace PackageManager; + if (GetProgressFlag() != false) { + if (GetInstallerStruct().progressCallback != NULL) { + // send progress signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->sendIconPath(path); + } + } +} + +void JobWidgetInstall::SendFinishedSuccess() +{ + using namespace PackageManager; + // TODO : sync should move to separate task. + sync(); + + if (INSTALL_LOCATION_TYPE_PREFER_EXTERNAL == m_installerContext.locationType) { + if (m_installerContext.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().postUpgrade(true); + } else { + WidgetInstallToExtSingleton::Instance().postInstallation(true); + } + WidgetInstallToExtSingleton::Instance().deinitialize(); + } + + // remove widget install information file + unlink(m_installerContext.installInfo.c_str()); + + //inform widget info + JobWidgetInstall::displayWidgetInfo(); + + TizenAppId& tizenId = m_installerContext.widgetConfig.tzAppid; + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget install successfinishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + DPL::ToUTF8String( + tizenId), Jobs::Exceptions::Success); +} + +void JobWidgetInstall::SendFinishedFailure() +{ + using namespace PackageManager; + // remove widget install information file + unlink(m_installerContext.installInfo.c_str()); + + // print error message + LOGE(COLOR_ERROR "Error number: %d" COLOR_END, m_exceptionCaught); + LOGE(COLOR_ERROR "Message: %s" COLOR_END, m_exceptionMessage.c_str()); + fprintf(stderr, "[Err:%d] %s", m_exceptionCaught, m_exceptionMessage.c_str()); + + TizenAppId & tizenId = m_installerContext.widgetConfig.tzAppid; + + _D("Call widget install failure finishedCallback"); + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + DPL::ToUTF8String( + tizenId), m_exceptionCaught); +} + +void JobWidgetInstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} +void JobWidgetInstall::displayWidgetInfo() +{ + if (m_installerContext.widgetConfig.webAppType.appType == WrtDB::APP_TYPE_TIZENWEBAPP) + { + WidgetDAOReadOnly dao(m_installerContext.widgetConfig.tzAppid); + + std::ostringstream out; + WidgetLocalizedInfo localizedInfo = + W3CFileLocalization::getLocalizedInfo(dao.getTzAppId()); + + out << std::endl << + "===================================== INSTALLED WIDGET INFO =========" \ + "============================"; + out << std::endl << "Name: " << localizedInfo.name; + out << std::endl << "AppId: " << dao.getTzAppId(); + WidgetSize size = dao.getPreferredSize(); + out << std::endl << "Width: " << size.width; + out << std::endl << "Height: " << size.height; + out << std::endl << "Start File: " << + W3CFileLocalization::getStartFile(dao.getTzAppId()); + out << std::endl << "Version: " << dao.getVersion(); + out << std::endl << "Licence: " << + localizedInfo.license; + out << std::endl << "Licence Href: " << + localizedInfo.licenseHref; + out << std::endl << "Description: " << + localizedInfo.description; + out << std::endl << "Widget Id: " << dao.getGUID(); + + OptionalWidgetIcon icon = W3CFileLocalization::getIcon(dao.getTzAppId()); + DPL::OptionalString iconSrc = + !!icon ? icon->src : DPL::OptionalString::Null; + out << std::endl << "Icon: " << iconSrc; + + out << std::endl << "Preferences:"; + { + PropertyDAOReadOnly::WidgetPreferenceList list = dao.getPropertyList(); + FOREACH(it, list) + { + out << std::endl << " Key: " << + it->key_name; + out << std::endl << " Readonly: " << + it->readonly; + } + } + + out << std::endl; + + _D("%s", out.str().c_str()); + } +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/job_widget_install.h b/src_wearable/jobs/widget_install/job_widget_install.h new file mode 100644 index 0000000..857863c --- /dev/null +++ b/src_wearable/jobs/widget_install/job_widget_install.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_widget_install.h + * @author Radoslaw Wicik r.wicik@samsung.com + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for main installer task + */ +#ifndef WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ + +#include +#include +#include +#include "widget_installer_struct.h" +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { + +typedef JobProgressBase InstallerBase; +typedef JobContextBase WidgetInstallationBase; + +class JobWidgetInstall : + public Job, + public InstallerBase, + public WidgetInstallationBase + +{ + private: + InstallerContext m_installerContext; + + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; + + public: + /** + * @brief Automaticaly sets installation process + */ + JobWidgetInstall(std::string const & widgetPath, + std::string const &tzPkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct &installerStruct); + + //overrides + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SendProgressIconPath(const std::string &path); + + void SaveExceptionData(const Jobs::JobExceptionBase&); + void displayWidgetInfo(); + + //execution paths + void appendNewInstallationTaskList(); + void appendUpdateInstallationTaskList(); + void appendRDSUpdateTaskList(); + void appendRecoveryTaskList(); + void appendFotaInstallationTaskList(); + void appendFotaUpdateTaskList(); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_JOB_WIDGET_INSTALL_H_ diff --git a/src_wearable/jobs/widget_install/languages.def b/src_wearable/jobs/widget_install/languages.def new file mode 100644 index 0000000..e9439b6 --- /dev/null +++ b/src_wearable/jobs/widget_install/languages.def @@ -0,0 +1,15 @@ +ADD(de, de-de) +ADD(el, el-gr) +ADD(en, en-us) +ADD(es, es-es) +ADD(fr, fr-fr) +ADD(it, it-it) +ADD(ja, ja-jp) +ADD(ko, ko-kr) +ADD(nl, nl-nl) +ADD(pt, pt-pt) +ADD(ru, ru-ru) +ADD(tr, tr-tr) +ADD(zh, zh-cn) +ADD(zh, zh-hk) +ADD(zh, zh-tw) diff --git a/src_wearable/jobs/widget_install/manifest.cpp b/src_wearable/jobs/widget_install/manifest.cpp new file mode 100755 index 0000000..0d509ac --- /dev/null +++ b/src_wearable/jobs/widget_install/manifest.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file manifest.cpp + * @author Mariusz Domanski (m.domanski@samsung.com) + */ + +#include "manifest.h" +#include "libxml_utils.h" +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +void writeElement(xmlTextWriterPtr writer, const char * name, DPL::String body) +{ + int state = xmlTextWriterWriteElement(writer, BAD_CAST name, + BAD_CAST DPL::ToUTF8String( + body).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteElement failed"); + } +} + +void writeText(xmlTextWriterPtr writer, DPL::String text) +{ + int state = xmlTextWriterWriteString(writer, + BAD_CAST DPL::ToUTF8String(text).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteText failed"); + } +} + +void writeElement(xmlTextWriterPtr writer, const char * name, const char * body) +{ + int state = xmlTextWriterWriteElement(writer, BAD_CAST name, BAD_CAST body); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterWriteElement failed"); + } +} + +void writeElementWithOneAttribute(xmlTextWriterPtr writer, + const char * name, + DPL::String body, + const char * nameAttr, + DPL::String bodyAttr, + bool condition = true) +{ + startElement(writer, name); + writeAttribute(writer, nameAttr, bodyAttr, condition); + writeText(writer, body); + endElement(writer); +} + +void startElement(xmlTextWriterPtr writer, const char * name) +{ + int state = xmlTextWriterStartElement(writer, BAD_CAST name); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterStartElement failed"); + } +} + +void endElement(xmlTextWriterPtr writer) +{ + int state = xmlTextWriterEndElement(writer); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterEndElement failed"); + } +} + +void writeAttribute(xmlTextWriterPtr writer, + const char * name, + DPL::String body, + bool condition = true) +{ + if (!condition) { + return; + } + int state = xmlTextWriterWriteAttribute(writer, BAD_CAST name, + BAD_CAST DPL::ToUTF8String( + body).c_str()); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, + "xmlTextWriterWriteAttribute failed"); + } +} + +void writeAttribute(xmlTextWriterPtr writer, + const char * name, + const char * body, + bool condition = true) +{ + if (!condition) { + return; + } + int state = xmlTextWriterWriteAttribute(writer, + BAD_CAST name, + BAD_CAST body); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, + "xmlTextWriterWriteAttribute failed"); + } +} + +void Manifest::generate(DPL::String filename) +{ + xmlTextWriterPtr writer; + int state; + + //compression set to 0 + writer = xmlNewTextWriterFilename(DPL::ToUTF8String(filename).c_str(), 0); + + if (writer == NULL) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlNewTextWriterFilename failed"); + } + state = xmlTextWriterSetIndent(writer, 1); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterSetIndent failed"); + } + + state = xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterStartDocument failed"); + } + this->serialize(writer); + state = xmlTextWriterEndDocument(writer); + if (state < 0) { + ThrowMsg(LibxmlUtils::Libxml2Error, "xmlTextWriterEndDocument failed"); + } + if (writer != NULL) { + xmlFreeTextWriter(writer); + writer = NULL; + } +} + +void Manifest::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "manifest"); + { + writeAttribute(writer, "xmlns", "http://tizen.org/ns/packages"); + writeAttribute(writer, "package", this->package); + writeAttribute(writer, "type", this->type); + writeAttribute(writer, "version", this->version); + if (!!this->installLocation) { + writeAttribute(writer, "install-location", (*this->installLocation)); + } + writeAttribute(writer, "storeclient-id", this->storeClientId, + !this->storeClientId.empty()); + writeAttribute(writer, "csc_path", this->cscPath, + !this->cscPath.empty()); + + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", l->getString(), + "xml:lang", l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), + "xml:lang", i->getLang(), i->hasLang()); + } + FOREACH(a, this->author) + { + a->serialize(writer); + } + FOREACH(d, this->description) + { + writeElementWithOneAttribute(writer, "description", d->getString(), + "xml:lang", d->getLang(), d->hasLang()); + } + //FOREACH(c, this->compatibility) { c->serialize(writer); } + //FOREACH(d, this->deviceProfile) { d->serialize(writer); } +#ifdef SERVICE_ENABLED + FOREACH(s, this->serviceApplication) { + s->serialize(writer); + } +#endif + FOREACH(u, this->uiApplication) { + u->serialize(writer); + } +#ifdef IME_ENABLED + FOREACH(i, this->imeApplication) { + i->serialize(writer); + } +#endif + //FOREACH(f, this->font) { f->serialize(writer); } +#ifdef DBOX_ENABLED + FOREACH(l, this->livebox) { + l->serialize(writer); + } +#endif + FOREACH(acc, this->account) + { + acc->serialize(writer); + } + + if (!this->privileges.isEmpty()) { + this->privileges.serialize(writer); + } + } + endElement(writer); +} + +void Author::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "author"); + writeAttribute(writer, "email", this->email, !this->email.empty()); + writeAttribute(writer, "href", this->href, !this->href.empty()); + writeAttribute(writer, "xml:lang", this->lang, !this->lang.empty()); + writeText(writer, body); + endElement(writer); +} + +void UiApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "ui-application"); + writeAttribute(writer, "appid", this->appid); + writeAttribute(writer, "exec", this->exec); + if (!!this->multiple) { + writeAttribute(writer, "multiple", (*this->multiple) ? "true" : "false"); + } + if (!!this->nodisplay) { + writeAttribute(writer, + "nodisplay", + (*this->nodisplay) ? "true" : "false"); + } + if (!!this->taskmanage) { + writeAttribute(writer, + "taskmanage", + (*this->taskmanage) ? "true" : "false"); + } + writeAttribute(writer, "type", this->type); + writeAttribute(writer, "extraid", this->extraid); + if (!!this->categories) { + writeAttribute(writer, "categories", (*this->categories)); + } + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", + l->getString(), "xml:lang", + l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + startElement(writer, "icon"); + { + writeAttribute(writer, "xml:lang", i->getLang(), i->hasLang()); + writeAttribute(writer, "section", "small", i->isSmall()); + writeText(writer, i->getString()); + } + endElement(writer); + } + FOREACH(a, this->appControl) + { + a->serialize(writer); + } + FOREACH(c, this->appCategory) + { + startElement(writer, "category"); + writeAttribute(writer, "name", *c); + endElement(writer); + } + FOREACH(m, this->metadata) { + m->serialize(writer); + } + endElement(writer); +} + +#ifdef IME_ENABLED +void ImeApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "ime"); + writeAttribute(writer, "appid", this->appid); + { + startElement(writer, "uuid"); + writeText(writer, this->uuid); + endElement(writer); + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", l->getString(), "xml:lang", l->getLang(), l->hasLang()); + } + startElement(writer, "languages"); + { + FOREACH(g, this->language) + { + startElement(writer, "language"); + writeText(writer, *g); + endElement(writer); + } + } + endElement(writer); + startElement(writer, "type"); + writeText(writer, this->iseType); + endElement(writer); + startElement(writer, "options"); + { + FOREACH(o, this->option) + { + startElement(writer, "option"); + writeText(writer, *o); + endElement(writer); + } + } + endElement(writer); + } + endElement(writer); +} +#endif + +#ifdef SERVICE_ENABLED +void ServiceApplication::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "ui-application"); + writeAttribute(writer, "component-type", this->component); + writeAttribute(writer, "auto-restart", (!!this->autoRestart && (*this->autoRestart)) ? "true" : "false"); + writeAttribute(writer, "on-boot", (!!this->onBoot && (*this->onBoot)) ? "true" : "false"); + writeAttribute(writer, "appid", this->appid); + writeAttribute(writer, "exec", this->exec); + writeAttribute(writer, "extraid", this->extraid); + if (!!this->nodisplay) { + writeAttribute(writer, "nodisplay", (*this->nodisplay) ? "true" : "false"); + } + if (!!this->multiple) { + writeAttribute(writer, "multiple", (*this->multiple) ? "true" : "false"); + } + writeAttribute(writer, "type", this->type); + if (!!this->taskmanage) { + writeAttribute(writer, "taskmanage", (*this->taskmanage) ? "true" : "false"); + } + if (!!this->categories) { + writeAttribute(writer, "categories", (*this->categories)); + } + FOREACH(l, this->label) + { + writeElementWithOneAttribute(writer, "label", + l->getString(), "xml:lang", + l->getLang(), l->hasLang()); + } + FOREACH(i, this->icon) + { + writeElementWithOneAttribute(writer, "icon", i->getString(), "xml:lang", + i->getLang(), i->hasLang()); + } + FOREACH(a, this->appControl) + { + a->serialize(writer); + } + FOREACH(c, this->appCategory) + { + startElement(writer, "category"); + writeAttribute(writer, "name", *c); + endElement(writer); +} + FOREACH(m, this->metadata) { + m->serialize(writer); + } + endElement(writer); +} +#endif + +void AppControl::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "app-control"); + FOREACH(o, this->operation) + { + startElement(writer, "operation"); + writeAttribute(writer, "name", *o); + endElement(writer); + } + FOREACH(u, this->uri) + { + startElement(writer, "uri"); + writeAttribute(writer, "name", *u); + endElement(writer); + } + FOREACH(m, this->mime) + { + startElement(writer, "mime"); + writeAttribute(writer, "name", *m); + endElement(writer); + } + endElement(writer); +} + +#ifdef DBOX_ENABLED +void LiveBox::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "livebox"); + if (!this->liveboxId.empty()) { + writeAttribute(writer, "appid", this->liveboxId); + } + + if (!this->primary.empty()) { + writeAttribute(writer, "primary", this->primary); + } + + if (!this->updatePeriod.empty()) { + writeAttribute(writer, "period", this->updatePeriod); + } + + writeAttribute(writer, "abi", "html"); + writeAttribute(writer, "network", "true"); + writeAttribute(writer, "nodisplay", "false"); + + if (!this->label.empty()) { + int defaultLabelChk = 0; + FOREACH(m, this->label) + { + std::pair boxLabel = *m; + startElement(writer, "label"); + if (!boxLabel.first.empty()) { + writeAttribute(writer, "xml:lang", boxLabel.first); + } else { + defaultLabelChk++; + } + writeText(writer, boxLabel.second); + endElement(writer); + } + if(!defaultLabelChk) { + startElement(writer, "label"); + writeText(writer, DPL::FromUTF8String("NO NAME")); + endElement(writer); + } + } + if (!this->icon.empty()) { + startElement(writer, "icon"); + writeText(writer, this->icon); + endElement(writer); + } + + if (!this->autoLaunch.empty()) { + startElement(writer, "launch"); + writeText(writer, this->autoLaunch); + endElement(writer); + } + + if (!this->box.boxSrc.empty() && + !this->box.boxMouseEvent.empty() && + !this->box.boxSize.empty()) + { + startElement(writer, "box"); + writeAttribute(writer, "type", "buffer"); + writeAttribute(writer, "mouse_event", this->box.boxMouseEvent); + writeAttribute(writer, "touch_effect", this->box.boxTouchEffect); + + FOREACH(it, this->box.boxSize) + { + startElement(writer, "size"); + if (!(*it).m_preview.empty()) { + writeAttribute(writer, "preview", (*it).m_preview); + } + if (!(*it).m_useDecoration.empty()) { + writeAttribute(writer, "need_frame", (*it).m_useDecoration); + } else { + // default value of use-decoration is "true" + writeAttribute(writer, "need_frame", DPL::String(L"true")); + } + + writeText(writer, (*it).m_size); + endElement(writer); + } + + startElement(writer, "script"); + writeAttribute(writer, "src", this->box.boxSrc); + endElement(writer); + + endElement(writer); + + if (!this->box.pdSrc.empty() && + !this->box.pdWidth.empty() && + !this->box.pdHeight.empty()) + { + startElement(writer, "pd"); + writeAttribute(writer, "type", "buffer"); + + startElement(writer, "size"); + DPL::String pdSize = this->box.pdWidth + DPL::String(L"x") + + this->box.pdHeight; + writeText(writer, pdSize); + endElement(writer); + + startElement(writer, "script"); + writeAttribute(writer, "src", this->box.pdSrc); + endElement(writer); + + endElement(writer); + } + } + + endElement(writer); +} +#endif + +void Account::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "account"); + { + startElement(writer, "account-provider"); + writeAttribute(writer, "appid", this->provider.appid); + writeAttribute(writer, "multiple-accounts-support", + this->provider.multiAccount); + + FOREACH(i, this->provider.icon) + { + startElement(writer, "icon"); + writeAttribute(writer, "section", i->first); + writeText(writer, i->second); + endElement(writer); + } + + bool setDefaultLang = false; + FOREACH(n, this->provider.name) + { + if (!setDefaultLang && n->getLang() == L"en-gb") { + writeElement(writer, "label", n->getString()); + setDefaultLang = true; + } + writeElementWithOneAttribute(writer, "label", + n->getString(), "xml:lang", + n->getLang(), n->hasLang()); + } + if (!setDefaultLang) { + writeElement(writer, "label", this->provider.name.begin()->getString()); + } + + FOREACH(c, this->provider.capability) + { + startElement(writer, "capability"); + writeText(writer, *c); + endElement(writer); + } + endElement(writer); + } + endElement(writer); +} + +void Privilege::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "privileges"); + { + FOREACH(it, this->name) + { + startElement(writer, "privilege"); + writeText(writer, *it); + endElement(writer); + } + } + endElement(writer); +} + +void Metadata::serialize(xmlTextWriterPtr writer) +{ + startElement(writer, "metadata"); + writeAttribute(writer, "key", *this->key); + if (!!this->value) { + writeAttribute(writer, "value", *this->value); + } + endElement(writer); +} +} //namespace Jobs +} //namespace WidgetInstall diff --git a/src_wearable/jobs/widget_install/manifest.h b/src_wearable/jobs/widget_install/manifest.h new file mode 100755 index 0000000..7d132fc --- /dev/null +++ b/src_wearable/jobs/widget_install/manifest.h @@ -0,0 +1,688 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file manifest.h + * @author Mariusz Domanski (m.domanski@samsung.com) + */ + +#ifndef INSTALLER_JOBS_MANIFEST_H +#define INSTALLER_JOBS_MANIFEST_H + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +/** + * @brief string with optional language attribute + */ +class StringWithLang +{ + public: + StringWithLang() { } + StringWithLang(DPL::String s) : string(s) { } + StringWithLang(DPL::String s, DPL::String l) : string(s), lang(l) { } + DPL::String getString() + { + return this->string; + } + DPL::String getLang() + { + return this->lang; + } + bool hasLang() + { + return !this->lang.empty(); + } + int operator==(const StringWithLang &other) + { + return (DPL::ToUTF8String(other.string) == DPL::ToUTF8String(string)) && + (DPL::ToUTF8String(other.lang) == DPL::ToUTF8String(lang)); + } + + private: + DPL::String string; + DPL::String lang; +}; + +class IconType : public StringWithLang +{ + public: + IconType() { } + IconType(DPL::String s) : StringWithLang(s), small(false) { } + IconType(DPL::String s, DPL::String l) : StringWithLang(s, l), small(false) { } + IconType(DPL::String s, bool m) : StringWithLang(s), small(m) { } + IconType(DPL::String s, DPL::String l, bool m) : StringWithLang(s, l), small(m) { } + + bool isSmall() + { + return small; + } + + int operator==(const IconType &other) + { + return (StringWithLang::operator==(other) && (other.small == small)); + } + + private: + bool small; +}; + +typedef StringWithLang LabelType, DescriptionType; + +/** + * These types are basicaly strings but they should allow usage of different + * range of characters or words (details in XML spec.). + * For simplicity DPL::Strings are used, although this can lead to XML + * validation + * errors (related to usage of not allowed characters in given places). + */ +typedef DPL::String NcnameType, NmtokenType, AnySimpleType, LangType; +typedef DPL::String OperationType, MimeType, UriType, TypeType, PackageType; +typedef DPL::OptionalString InstallLocationType, CategoriesType; +typedef DPL::String AppCategoryType; +typedef DPL::OptionalString KeyType, ValueType; + +#ifdef IME_ENABLED +typedef DPL::String UuidType, LanguageType, IseTypeType, OptionType; +#endif + +#ifdef SERVICE_ENABLED +typedef DPL::String ComponentType; +#endif + +/** + * xmllib2 wrappers + */ +void writeElement(xmlTextWriterPtr writer, const char * name, DPL::String body); +void writeText(xmlTextWriterPtr writer, DPL::String text); +void writeElement(xmlTextWriterPtr writer, const char * name, const char * body); +void writeElementWithOneAttribute(xmlTextWriterPtr writer, + const char * name, + const char * body, + const char * nameAttr, + DPL::String bodyAttr, + bool condition = true); +void startElement(xmlTextWriterPtr writer, const char * name); +void endElement(xmlTextWriterPtr writer); +void writeAttribute(xmlTextWriterPtr writer, const char * name, + DPL::String body, bool condition); +void writeAttribute(xmlTextWriterPtr writer, const char * name, + const char * body, bool condition); + +/** + * @brief author element + */ +class Author +{ + public: + Author() {} + Author(AnySimpleType e, + NcnameType h, + LangType l, + DPL::String b) : + email(e), href(h), lang(l), body(b) {} + void serialize(xmlTextWriterPtr writer); + + private: + AnySimpleType email; + NcnameType href; + LangType lang; + DPL::String body; +}; + +typedef Author AuthorType; + +/** + * @brief application-service element + */ +class AppControl +{ + public: + AppControl() {} + void addOperation(const OperationType &x) + { + this->operation.push_back(x); + } + void addUri(const UriType &x) + { + this->uri.push_back(x); + } + void addMime(const MimeType &x) + { + this->mime.push_back(x); + } + void serialize(xmlTextWriterPtr writer); + + private: + std::list operation; //attr name AnySimpleType + std::list uri; //attr name AnySimpleType + std::list mime; //attr name AnySimpleType +}; + +typedef AppControl AppControlType; + +/** + * @brief account element + */ +typedef std::list> IconListType; +typedef std::list DisplayNameListType; +typedef std::list AccountCapabilityType; + +struct AccountProvider +{ + NcnameType appid; + NcnameType multiAccount; + IconListType icon; + DisplayNameListType name; + AccountCapabilityType capability; +}; + +typedef AccountProvider AccountProviderType; + +class Account +{ + public: + Account() {} + void addAccountProvider(const AccountProvider &x) + { + this->provider = x; + } + void serialize(xmlTextWriterPtr writer); + + private: + AccountProviderType provider; +}; + +class Privilege +{ + public: + Privilege() {} + void addPrivilegeName(const DPL::String &x) + { + this->name.push_back(x); + } + bool isEmpty() + { + return this->name.empty(); + } + + void serialize(xmlTextWriterPtr writer); + + private: + std::list name; +}; + +typedef Privilege PrivilegeType; + +class Metadata +{ + public: + Metadata(KeyType k, ValueType v) : + key(k), + value(v) + {} + void serialize(xmlTextWriterPtr writer); + + private: + KeyType key; + ValueType value; +}; + +typedef Metadata MetadataType; + +#ifdef IME_ENABLED +/** + * @brief ime-application element + */ +class ImeApplication +{ + public: + ImeApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addUuid(const UuidType &x) + { + this->uuid = x; + } + void addLanguage(const LanguageType &x) + { + this->language.push_back(x); + } + void addIseType(const IseTypeType &x) + { + this->iseType = x; + } + void addOption(const OptionType &x) + { + this->option.push_back(x); + } + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType appid; + std::list label; + DPL::String uuid; + std::list language; + DPL::String iseType; + std::list option; +}; + +typedef ImeApplication ImeApplicationType; +#endif + +#ifdef SERVICE_ENABLED +/** + * @brief service-application element + */ + +class ServiceApplication +{ + public: + ServiceApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void setComponent(const ComponentType &x) + { + this->component = x; + } + void setAutoRestart(bool x) + { + this->autoRestart = x; + } + void setOnBoot(bool x) + { + this->onBoot = x; + } + void setExec(const AnySimpleType &x) + { + this->exec = x; + } + void setExtraid(const NcnameType &x) + { + this->extraid = x; + } + void setNodisplay(bool x) + { + this->nodisplay = x; + } + void setMultiple(bool x) + { + this->multiple = x; + } + void setType(const TypeType &x) + { + this->type = x; + } + void setTaskmanage(bool x) + { + this->taskmanage = x; + } + void setCategories(const NcnameType &x) + { + this->categories = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAppControl(const AppControlType &x) + { + this->appControl.push_back(x); + } + void addAppCategory(const AppCategoryType &x) + { + this->appCategory.push_back(x); + } + void addMetadata(const MetadataType &m) + { + this->metadata.push_back(m); + } + void serialize(xmlTextWriterPtr writer); + + DPL::OptionalBool isNoDisplay() { + return this->nodisplay; + } + + private: + NcnameType appid; + DPL::String component; + DPL::OptionalBool autoRestart; + DPL::OptionalBool onBoot; + AnySimpleType exec; + NcnameType extraid; + DPL::OptionalBool nodisplay; + DPL::OptionalBool multiple; + TypeType type; + DPL::OptionalBool taskmanage; + CategoriesType categories; + std::list label; + std::list icon; + std::list appControl; + std::list appCategory; + std::list metadata; +}; + +typedef ServiceApplication ServiceApplicationType; + +#endif + +/** + * @brief ui-application element + */ +class UiApplication +{ + public: + UiApplication() {} + void setAppid(const NcnameType &x) + { + this->appid = x; + } + void setExtraid(const NcnameType &x) + { + this->extraid = x; + } + void setExec(const AnySimpleType &x) + { + this->exec = x; + } + void setMultiple(bool x) + { + this->multiple = x; + } + void setNodisplay(bool x) + { + this->nodisplay = x; + } + void setTaskmanage(bool x) + { + this->taskmanage = x; + } + void setType(const TypeType &x) + { + this->type = x; + } + void setCategories(const NcnameType &x) + { + this->categories = x; + } + void addLabel(const LabelType &x) + { + this->label.push_back(x); + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAppControl(const AppControlType &x) + { + this->appControl.push_back(x); + } + void addAppCategory(const AppCategoryType &x) + { + this->appCategory.push_back(x); + } + void addMetadata(const MetadataType &m) + { + this->metadata.push_back(m); + } + void serialize(xmlTextWriterPtr writer); + + DPL::OptionalBool isNoDisplay() { + return this->nodisplay; + } + + private: + NcnameType appid; + NcnameType extraid; + AnySimpleType exec; + DPL::OptionalBool multiple; + DPL::OptionalBool nodisplay; + DPL::OptionalBool taskmanage; + TypeType type; + CategoriesType categories; + std::list label; + std::list icon; + std::list appControl; + std::list appCategory; + std::list metadata; +}; + +typedef UiApplication UiApplicationType; + +#ifdef DBOX_ENABLED +/** + * @brief LiveBox element + */ +typedef WrtDB::ConfigParserData::LiveboxInfo::BoxSizeList BoxSizeType; +typedef WrtDB::ConfigParserData::LiveboxInfo::BoxLabelList BoxLabelType; + +struct BoxInfo +{ + NcnameType boxSrc; + NcnameType boxMouseEvent; + NcnameType boxTouchEffect; + BoxSizeType boxSize; + NcnameType pdSrc; + NcnameType pdWidth; + NcnameType pdHeight; +}; +typedef BoxInfo BoxInfoType; + +class LiveBox +{ + public: + LiveBox() { } + void setLiveboxId(const NcnameType &x) + { + this->liveboxId = x; + } + void setPrimary(const NcnameType &x) + { + this->primary = x; + } + void setAutoLaunch(const NcnameType &x) + { + this->autoLaunch = x; + } + void setUpdatePeriod(const NcnameType &x) + { + this->updatePeriod = x; + } + void setLabel(const BoxLabelType &x) + { + this->label = x; + } + void setIcon(const NcnameType &x) + { + this->icon = x; + } + void setBox(const BoxInfoType &x) + { + this->box = x; + } + + void serialize(xmlTextWriterPtr writer); + + private: + NcnameType liveboxId; + NcnameType primary; + NcnameType autoLaunch; + NcnameType updatePeriod; + NcnameType timeout; + BoxLabelType label; + NcnameType icon; + NcnameType lang; + BoxInfoType box; +}; + +typedef LiveBox LiveBoxInfo; +#endif + +/** + * @brief manifest element + * + * Manifest xml file representation. + */ +class Manifest +{ + public: + Manifest() {} + void serialize(xmlTextWriterPtr writer); + void generate(DPL::String filename); + + void addLabel(const LabelType &x) + { +#ifdef MULTIPROCESS_SERVICE_SUPPORT + auto pos = std::find(label.begin(), label.end(), x); + if (pos == label.end()) { + this->label.push_back(x); + } +#else + this->label.push_back(x); +#endif + } + void addIcon(const IconType &x) + { + this->icon.push_back(x); + } + void addAuthor(const AuthorType &x) + { + this->author.push_back(x); + } + void addDescription(const DescriptionType &x) + { + this->description.push_back(x); + } + // void addCompatibility(const CompatibilityType &x) + // { + // this->compatibility.push_back(x); + // } + // void addDeviceProfile(const DeviceProfileType &x) + // { + // this->deviceProfile.push_back(x); + // } + void addUiApplication(const UiApplicationType &x) + { + this->uiApplication.push_back(x); + } +#ifdef IME_ENABLED + void addImeApplication(const ImeApplicationType &x) + { + this->imeApplication.push_back(x); + } +#endif +#ifdef SERVICE_ENABLED + void addServiceApplication(const ServiceApplicationType &x) + { + this->serviceApplication.push_back(x); + } +#endif + // void addFont(const FontType &x) { this->font.push_back(x); } + +#ifdef DBOX_ENABLED + void addLivebox(const LiveBoxInfo &x) + { + this->livebox.push_back(x); + } +#endif + + void addAccount(const Account &x) + { + this->account.push_back(x); + } + + void addPrivileges(const PrivilegeType &x) + { + this->privileges = x; + } + + void setInstallLocation(const InstallLocationType &x) + { + this->installLocation = x; + } + void setPackage(const NcnameType &x) + { + this->package = x; + } + void setType(const PackageType &x) + { + this->type = x; + } + void setVersion(const NmtokenType &x) + { + this->version = x; + } + void setStoreClientId(const NcnameType &x) + { + this->storeClientId= x; + } + void setCscPath(const NcnameType &x) + { + this->cscPath = x; + } + + private: + std::list label; + std::list icon; + std::list author; + std::list description; + // std::list compatibility; + // std::list deviceProfile; +#ifdef SERVICE_ENABLED + std::list serviceApplication; +#endif + std::list uiApplication; +#ifdef IME_ENABLED + std::list imeApplication; +#endif + // std::list font; +#ifdef DBOX_ENABLED + std::list livebox; +#endif + InstallLocationType installLocation; + NcnameType package; + PackageType type; + NmtokenType version; + std::list account; + PrivilegeType privileges; + NcnameType storeClientId; + NcnameType cscPath; + +}; +} //namespace Jobs +} //namespace WidgetInstall + +#endif //INSTALLER_JOBS_MANIFEST_H diff --git a/src_wearable/jobs/widget_install/task_ace_check.cpp b/src_wearable/jobs/widget_install/task_ace_check.cpp new file mode 100755 index 0000000..7285492 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_ace_check.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_ace_check.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task ace check + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +namespace Jobs { +namespace WidgetInstall { +TaskAceCheck::TaskAceCheck(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskAceCheck::StartStep); + AddStep(&TaskAceCheck::StepPrepareForAce); + AddStep(&TaskAceCheck::StepAceCheck); + AddStep(&TaskAceCheck::StepProcessAceResponse); + AddStep(&TaskAceCheck::StepCheckAceResponse); + AddStep(&TaskAceCheck::EndStep); +} + +void TaskAceCheck::StepPrepareForAce() +{ + m_context.featureLogic = + FeatureLogicPtr(new FeatureLogic(m_context.widgetConfig.tzAppid)); + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ACE_PREPARE, + "Widget Access Control Check Prepared"); +} + +void TaskAceCheck::StepAceCheck() +{ + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); + _D("StepAceCheck!"); + // This widget does not use any device cap + if (m_context.featureLogic->isDone()) { + return; + } + + _D("StepAceCheck!"); + DPL::String deviceCap = m_context.featureLogic->getDevice(); + + _D("StepAceCheck!"); + _D("DevCap is : %ls", deviceCap.c_str()); + + std::string devCapStr = DPL::ToUTF8String(deviceCap); + ace_policy_result_t policyResult = ACE_DENY; + + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD) { + _D("This widget is preloaded. So ace check will be skiped"); + policyResult = ACE_PERMIT; + } else { + ace_return_t ret = ace_get_policy_result( + const_cast(devCapStr.c_str()), + dao.getHandle(), + &policyResult); + if (ACE_OK != ret) { + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ACE check failure"); + } + } + + _D("PolicyResult is : %d", static_cast(policyResult)); + m_context.staticPermittedDevCaps.insert(std::make_pair(deviceCap, + policyResult == + ACE_PERMIT)); + + m_context.featureLogic->setAceResponse(policyResult != ACE_DENY); +} + +void TaskAceCheck::StepProcessAceResponse() +{ + if (m_context.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + return; + } + + _D("StepProcessAceResponse"); + m_context.featureLogic->next(); + + // No device caps left to process + if (m_context.featureLogic->isDone()) { + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); +#ifdef SERVICE_ENABLED + std::list serviceList; + FOREACH(it , m_context.widgetConfig.configInfo.serviceAppInfoList){ + WrtDB::WidgetDAO serviceDao(it->serviceId); + serviceList.push_back(serviceDao.getHandle()); + } +#endif + _D("All responses has been received from ACE."); + // Data to convert to C API + std::vector devCaps; + std::vector devCapsSmack; + // Saving static dev cap permissions + FOREACH(cap, m_context.staticPermittedDevCaps) { + _D("staticPermittedDevCaps : %ls smack: %d", cap->first.c_str(), cap->second); + std::string devCapStr = DPL::ToUTF8String(cap->first); + devCaps.push_back(devCapStr); + devCapsSmack.push_back(cap->second); + } + ace_requested_dev_cap_list_t list; + list.count = devCaps.size(); + list.items = new ace_requested_dev_cap_t[list.count]; + + for (unsigned int i = 0; i < devCaps.size(); ++i) { + list.items[i].device_capability = + const_cast(devCaps[i].c_str()); + list.items[i].smack_granted = + devCapsSmack[i] ? ACE_TRUE : ACE_FALSE; + } + //TODO: remove dao.getHandle() + int ret = ace_set_requested_dev_caps(dao.getHandle(), &list); +#ifdef SERVICE_ENABLED + FOREACH(it, serviceList){ + ret |= ace_set_requested_dev_caps(*it, &list); + } +#endif + delete[] list.items; + + if (ACE_OK != static_cast(ret)) { + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ACE failure"); + } + + std::set acceptedFeature; + auto it = m_context.featureLogic->resultBegin(); + for (; it != m_context.featureLogic->resultEnd(); ++it) { + if (!(it->rejected)) { + acceptedFeature.insert(DPL::ToUTF8String(it->name)); + } + } + ace_feature_list_t featureList; + featureList.count = acceptedFeature.size(); + featureList.items = new ace_string_t[featureList.count]; + + size_t i = 0; + for (std::set::const_iterator iter = acceptedFeature.begin(); + iter != acceptedFeature.end(); ++iter) + { + _D("Accepted feature item: %s", iter->c_str()); + featureList.items[i] = const_cast(iter->c_str()); + i++; + } + + //TODO: remove dao.getHandle() + ret = ace_set_accepted_feature(dao.getHandle(), &featureList); +#ifdef SERVICE_ENABLED + FOREACH(it, serviceList){ + ret |= ace_set_accepted_feature(*it, &featureList); + } +#endif + delete[] featureList.items; + + if (ACE_OK != static_cast(ret)) { + _E("Error in ace_set_feature"); + ThrowMsg(Exceptions::AceCheckFailed, "Instalation failure. " + "ace_set_feature failure."); + } + return; + } + + _D("Next device cap."); + // Process next device cap + SwitchToStep(&TaskAceCheck::StepAceCheck); +} + +void TaskAceCheck::StepCheckAceResponse() +{ + _D("Checking ACE response"); + if (m_context.featureLogic->isRejected()) { + _E("Installation failure. Some devCap was not accepted by ACE."); + ThrowMsg( + Exceptions::PrivilegeLevelViolation, + "Instalation failure. " + "Some deviceCap was not accepted by ACE."); + } + _D("Updating \"feature reject status\" in database!"); + auto it = m_context.featureLogic->resultBegin(); + auto end = m_context.featureLogic->resultEnd(); + for (; it != end; ++it) { + _D(" |- Feature: %ls has reject status: %d", it->name.c_str(), it->rejected); + if (it->rejected) { + WrtDB::WidgetDAO dao(m_context.widgetConfig.tzAppid); + dao.updateFeatureRejectStatus(*it); + +#ifdef SERVICE_ENABLED + FOREACH( svcApp , m_context.widgetConfig.configInfo.serviceAppInfoList){ + WrtDB::WidgetDAO dao(svcApp->serviceId); + dao.updateFeatureRejectStatus(*it); + } +#endif + } + } + _D("Installation continues..."); +} + +void TaskAceCheck::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskAceCheck::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ACE_CHECK, + "Widget Access Control Check Finished"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_ace_check.h b/src_wearable/jobs/widget_install/task_ace_check.h new file mode 100644 index 0000000..e4ce90d --- /dev/null +++ b/src_wearable/jobs/widget_install/task_ace_check.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_ace_check.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for installer task ace check + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskAceCheck : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepPrepareForAce(); + void StepAceCheck(); + void StepProcessAceResponse(); + void StepCheckAceResponse(); + + void StartStep(); + void EndStep(); + + public: + TaskAceCheck(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_ACE_CHECK_H */ diff --git a/src_wearable/jobs/widget_install/task_certify.cpp b/src_wearable/jobs/widget_install/task_certify.cpp new file mode 100644 index 0000000..3e8925c --- /dev/null +++ b/src_wearable/jobs/widget_install/task_certify.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_certify.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include "wac_widget_id.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ValidationCore; +using namespace WrtDB; + +namespace { +const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 = 1; +const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR2 = 2; + +WidgetCertificateData toWidgetCertificateData(const SignatureData &data, + bool root) +{ + WidgetCertificateData result; + + result.chainId = data.getSignatureNumber(); + _D("result.chainId : %d", result.chainId); + + result.owner = data.isAuthorSignature() ? + WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR; + + if (data.isAuthorSignature()) { + result.owner = WidgetCertificateData::AUTHOR; + } else { + if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 == data.getSignatureNumber()) { + result.owner = WidgetCertificateData::DISTRIBUTOR; + } else if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR2 == + data.getSignatureNumber()){ + result.owner = WidgetCertificateData::DISTRIBUTOR2; + } else { + result.owner = WidgetCertificateData::UNKNOWN; + } + } + + result.type = root ? + WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY; + + CertificatePtr certificate; + + if (root) { + certificate = data.getRootCaCertificatePtr(); + } else { + certificate = data.getEndEntityCertificatePtr(); + } + + AssertMsg(certificate && !!certificate->getCommonName(), + "CommonName is Null"); + + result.strCommonName = *certificate->getCommonName(); + + result.strMD5Fingerprint = std::string("md5 ") + + Certificate::FingerprintToColonHex( + certificate->getFingerprint(Certificate::FINGERPRINT_MD5)); + + result.strSHA1Fingerprint = std::string("sha-1 ") + + Certificate::FingerprintToColonHex( + certificate->getFingerprint(Certificate::FINGERPRINT_SHA1)); + + return result; +} + +CertificatePtr getOldAuthorSignerCertificate(DPL::String appid) +{ + CertificateChainList chainList; + WidgetDAOReadOnly dao(appid); + chainList = dao.getWidgetCertificate(SIGNATURE_AUTHOR); + FOREACH(it, chainList) + { + ValidationCore::CertificateCollection chain; + if (false == chain.load(*it)) { + _E("Chain is broken"); + } + + if (!chain.sort()) { + _E("Chain failed at sorting"); + } + + ValidationCore::CertificateList list = chain.getCertificateList(); + + FOREACH(cert, list) + { + if (!(*cert)->isRootCert() && !(*cert)->isCA()) { + return *cert; + } + } + } + return CertificatePtr(); +} +} // namespace anonymous + +namespace Jobs { +namespace WidgetInstall { +TaskCertify::TaskCertify(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_contextData(inCont) +{ + AddStep(&TaskCertify::StartStep); + AddStep(&TaskCertify::stepSignature); + // certi comparison determines whether the update. + if (true == m_contextData.isUpdateMode) { + AddStep(&TaskCertify::stepVerifyUpdate); + } + AddStep(&TaskCertify::EndStep); +} + +void TaskCertify::processDistributorSignature(const SignatureData &data) +{ + // this signature is verified - + // no point in check domain WAC_ROOT and WAC_RECOGNIZED + m_contextData.widgetSecurity.setDistributorSigned(true); + + CertificateCollection collection; + collection.load(data.getCertList()); + AssertMsg(collection.sort(), + "Certificate collection can't sort"); + + AssertMsg(collection.isChain(), + "Certificate collection is not able to create chain. " + "It is not possible to verify this signature."); + + if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 == data.getSignatureNumber()) { + m_contextData.widgetSecurity.getCertificateChainListRef().push_back( + collection); + } else { + m_contextData.widgetSecurity.getCertificateChainList2Ref().push_back( + collection); + } + + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, true)); + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, false)); +} + +void TaskCertify::processAuthorSignature(const SignatureData &data) +{ + using namespace ValidationCore; + _D("DNS Identity match!"); + // this signature is verified or widget is distributor signed + m_contextData.widgetSecurity.setAuthorCertificatePtr(data.getEndEntityCertificatePtr()); + CertificatePtr test = m_contextData.widgetSecurity.getAuthorCertificatePtr(); + + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, true)); + m_contextData.widgetSecurity.getCertificateListRef().push_back( + toWidgetCertificateData(data, false)); + + // match widget_id with one from dns identity set + WacWidgetId widgetId(m_contextData.widgetConfig.configInfo.widget_id); + + CertificatePtr cert = data.getEndEntityCertificatePtr(); + Assert(cert); + Certificate::AltNameSet dnsIdentity = cert->getAlternativeNameDNS(); + + CertificateCollection collection; + collection.load(data.getCertList()); + collection.sort(); + AssertMsg(collection.isChain(), + "Certificate collection is not able to create chain. " + "It is not possible to verify this signature."); + + m_contextData.widgetSecurity.getAuthorsCertificateChainListRef().push_back( + collection); + + FOREACH(it, dnsIdentity){ + if (widgetId.matchHost(*it)) { + m_contextData.widgetSecurity.setRecognized(true); + return; + } + } +} + +void TaskCertify::getSignatureFiles(std::string path, SignatureFileInfoSet& file) +{ + _D("path : %s", path.c_str()); + SignatureFileInfoSet signatureFiles; + SignatureFinder signatureFinder(path); + if (SignatureFinder::NO_ERROR != signatureFinder.find(file)) { + _E("Error in Signature Finder : %s", path.c_str()); + ThrowMsg(Exceptions::SignatureNotFound, + "Error openig temporary widget directory"); + } +} + +void TaskCertify::stepSignature() +{ + LOGD("================ Step: <> ENTER ==============="); + + std::string widgetPath; + widgetPath = m_contextData.locations->getPackageInstallationDir() + "/"; + + SignatureFileInfoSet signatureFiles; + + Try { + getSignatureFiles(widgetPath, signatureFiles); + + if (signatureFiles.size() <= 0) { + widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath()) + + "/"; + if (0 == access(widgetPath.c_str(), F_OK)) { + getSignatureFiles(widgetPath, signatureFiles); + } + } + } Catch(Exceptions::SignatureNotFound) { + ReThrowMsg(Exceptions::SignatureNotFound, widgetPath); + } + + SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin(); + _D("Number of signatures: %d", signatureFiles.size()); + + for (; iter != signatureFiles.rend(); ++iter) { + _D("Checking signature with id=%d", iter->getFileNumber()); + SignatureData data(widgetPath + iter->getFileName(), + iter->getFileNumber()); + + Try { + SignatureReader xml; + xml.initialize(data, GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + WrtSignatureValidator::Result result; + + WrtSignatureValidator validator( + WrtSignatureValidator::TIZEN, + !GlobalSettings:: + OCSPTestModeEnabled(), + !GlobalSettings:: + CrlTestModeEnabled(), + false); + + result = validator.check(data, widgetPath); + + if (m_contextData.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_contextData.mode.command == InstallMode::Command::RECOVERY + || m_contextData.mode.installTime == InstallMode::InstallTime::FOTA) + { + result = WrtSignatureValidator::SIGNATURE_VERIFIED; + } + + if (result == WrtSignatureValidator::SIGNATURE_REVOKED) { + _W("Certificate is REVOKED"); + ThrowMsg(Exceptions::CertificateExpired, + "Certificate is REVOKED"); + } + + if (result == WrtSignatureValidator::SIGNATURE_INVALID && + iter->getFileNumber() <= 1) { + _W("Signature is INVALID"); + // TODO change exception name + ThrowMsg(Exceptions::SignatureInvalid, + "Invalid Package"); + } + + if (data.isAuthorSignature()) { + if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ) { + processAuthorSignature(data); + } + } else { + if (result != WrtSignatureValidator::SIGNATURE_INVALID) { + processDistributorSignature(data); + } + } + } Catch(ParserSchemaException::Base) { + _E("Error occured in ParserSchema."); + ReThrowMsg(Exceptions::SignatureInvalid, + "Error occured in ParserSchema."); + } + } + + if (signatureFiles.empty()) { + _D("No signature files has been found."); + } + + LOGD("================ Step: <> DONE ================"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_DIGSIG_CHECK, + "Widget Signature checked"); +} + +bool TaskCertify::isTizenWebApp() const +{ + bool ret = FALSE; + if (m_contextData.widgetConfig.webAppType.appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) + { + ret = TRUE; + } + + return ret; +} + +void TaskCertify::stepVerifyUpdate() +{ + LOGD("================ Step: <> ENTER ==============="); + + std::string oldAuthorCert; + + int ret = 0; + pkgmgrinfo_certinfo_h handle; + const char *authorCert = NULL; + ret = pkgmgrinfo_pkginfo_create_certinfo(&handle); + if (PMINFO_R_OK == ret) { + ret = pkgmgrinfo_pkginfo_load_certinfo(DPL::ToUTF8String( + m_contextData.widgetConfig.tzPkgid).c_str(), handle); + if (PMINFO_R_OK == ret) { + ret = pkgmgrinfo_pkginfo_get_cert_value(handle, + PMINFO_AUTHOR_SIGNER_CERT, &authorCert); + if (PMINFO_R_OK == ret) { + oldAuthorCert = (NULL != authorCert)? authorCert : ""; + } + } + pkgmgrinfo_pkginfo_destroy_certinfo(handle); + } + + if (oldAuthorCert.empty()) { + _D("Old cert is empty."); + } else { + ValidationCore::CertificatePtr certPtr = m_contextData.widgetSecurity.getAuthorCertificatePtr(); + if (certPtr == NULL) { + ThrowMsg(Exceptions::NotMatchedCertification, "No author certificates"); + } + + DPL::String newAuthorPublicKeyStr = certPtr->getPublicKeyString(); + //compare with public key + ValidationCore::Certificate installedCert(oldAuthorCert , ValidationCore::Certificate::FORM_BASE64); + DPL::String installedPublicKeyStr = installedCert.getPublicKeyString(); + if (0 != installedPublicKeyStr.compare(newAuthorPublicKeyStr)) { + _D("old widget's author public key : %ls", + installedPublicKeyStr.c_str()); + _D("new widget's author public key: %ls", + newAuthorPublicKeyStr.c_str()); + ThrowMsg(Exceptions::NotMatchedCertification, + "Author signer certificates doesn't match \ + between old widget and installing widget"); + } + + } + LOGD("================ Step: <> DONE ==============="); +} + +void TaskCertify::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskCertify::EndStep() +{ + LOGD("Step: <>"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_CERT_CHECK, + "Widget Certification Check Finished"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs + diff --git a/src_wearable/jobs/widget_install/task_certify.h b/src_wearable/jobs/widget_install/task_certify.h new file mode 100644 index 0000000..6a59781 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_certify.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_certify.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include + +class InstallerContext; + +namespace ValidationCore { +class SignatureData; +} + +namespace Jobs { +namespace WidgetInstall { +class TaskCertify : + public DPL::TaskDecl +{ + public: + TaskCertify(InstallerContext &inCont); + + private: + //data + InstallerContext& m_contextData; + + //steps + void stepSignature(); + void stepVerifyUpdate(); + + void StartStep(); + void EndStep(); + + void processDistributorSignature(const ValidationCore::SignatureData &data); + void processAuthorSignature(const ValidationCore::SignatureData &data); + void getSignatureFiles(std::string path, + ValidationCore::SignatureFileInfoSet& file); + + bool isTizenWebApp() const; +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_H diff --git a/src_wearable/jobs/widget_install/task_certify_level.cpp b/src_wearable/jobs/widget_install/task_certify_level.cpp new file mode 100755 index 0000000..1b9bffe --- /dev/null +++ b/src_wearable/jobs/widget_install/task_certify_level.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_certify_level.cpp + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace ValidationCore; +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskCertifyLevel::TaskCertifyLevel(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_contextData(inCont) +{ + AddStep(&TaskCertifyLevel::StartStep); + AddStep(&TaskCertifyLevel::stepCertifyLevel); + AddStep(&TaskCertifyLevel::EndStep); +} + +void TaskCertifyLevel::stepCertifyLevel() +{ + LOGD("================ Step: <> ENTER ==============="); + if (!checkConfigurationLevel(getCertifyLevel())) { + ThrowMsg(Exceptions::PrivilegeLevelViolation, "setting level violate"); + } + LOGD("================ Step: <> DONE ================"); +} + +void TaskCertifyLevel::getSignatureFiles(const std::string& path, + SignatureFileInfoSet& file) +{ + SignatureFileInfoSet signatureFiles; + SignatureFinder signatureFinder(path); + if (SignatureFinder::NO_ERROR != signatureFinder.find(file)) { + _E("Error in Signature Finder : %s", path.c_str()); + ThrowMsg(Exceptions::SignatureNotFound, "Signature not found"); + } +} + +TaskCertifyLevel::Level TaskCertifyLevel::getCertifyLevel() +{ + std::string widgetPath = m_contextData.locations->getPackageInstallationDir() + "/"; + SignatureFileInfoSet signatureFiles; + + Try { + getSignatureFiles(widgetPath, signatureFiles); + + if (signatureFiles.size() <= 0) { + widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath()) + + "/"; + if (0 == access(widgetPath.c_str(), F_OK)) { + getSignatureFiles(widgetPath, signatureFiles); + } + } + } Catch(Exceptions::SignatureNotFound) { + ReThrowMsg(Exceptions::SignatureNotFound, widgetPath); + } + + SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin(); + _D("Number of signatures: %d", signatureFiles.size()); + + Level level = Level::UNKNOWN; + for (; iter != signatureFiles.rend(); ++iter) { + _D("Checking signature with id=%d", iter->getFileNumber()); + SignatureData data(widgetPath + iter->getFileName(), + iter->getFileNumber()); + + Try { + SignatureReader xml; + xml.initialize(data, GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + WrtSignatureValidator validator( + WrtSignatureValidator::TIZEN, + !GlobalSettings:: + OCSPTestModeEnabled(), + !GlobalSettings:: + CrlTestModeEnabled(), + false); + + WrtSignatureValidator::Result result = + validator.check(data, widgetPath); + + if (m_contextData.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_contextData.mode.command == InstallMode::Command::RECOVERY + || m_contextData.mode.installTime == InstallMode::InstallTime::FOTA) + { + result = WrtSignatureValidator::SIGNATURE_VERIFIED; + } + + if (result == WrtSignatureValidator::SIGNATURE_REVOKED) { + ThrowMsg(Exceptions::CertificateExpired, + "Certificate is REVOKED"); + } + + if (result == WrtSignatureValidator::SIGNATURE_INVALID && + iter->getFileNumber() <= 1) + { + ThrowMsg(Exceptions::SignatureInvalid, "Invalid Package"); + } + + if (data.isAuthorSignature()) { + _D("Skip author signature"); + } else { + Level currentCertLevel = + certTypeToLevel(data.getVisibilityLevel()); + if (currentCertLevel == Level::UNKNOWN) { + continue; + } + if (currentCertLevel > level) { + level = currentCertLevel; + _D("level %s", enumToString(level).c_str()); + } + } + } Catch(ParserSchemaException::Base) { + _E("Error occured in ParserSchema."); + ReThrowMsg(Exceptions::SignatureInvalid, + "Error occured in ParserSchema."); + } + } + + m_contextData.certLevel = level; + return level; +} + +bool TaskCertifyLevel::checkConfigurationLevel( + TaskCertifyLevel::Level level) +{ + if (!checkSettingLevel(level)) { + return false; + } + if (!checkAppcontrolHasDisposition(level)) { + return false; + } + if (!checkServiceLevel(level)) { + return false; + } + return true; +} + +bool TaskCertifyLevel::checkSettingLevel( + TaskCertifyLevel::Level level) +{ + secureSettingMap data = { + {"sound-mode", Level::PARTNER}, + {"nodisplay", Level::PARTNER} + }; + FOREACH(it, m_contextData.widgetConfig.configInfo.settingsList) { + secureSettingIter ret = data.find(DPL::ToUTF8String(it->m_name)); + if (ret != data.end()) { + if (level < ret->second) { + _E("\"%ls\" needs \"%s\" level", it->m_name.c_str(), enumToString(ret->second).c_str()); + return false; + } + } + } + return true; +} + +bool TaskCertifyLevel::checkAppcontrolHasDisposition( + TaskCertifyLevel::Level level) +{ + // tizen:disposition -> platform + FOREACH(it, m_contextData.widgetConfig.configInfo.appControlList) { + if (ConfigParserData::AppControlInfo::Disposition::UNDEFINE != + it->m_disposition) + { + if (level < Level::PLATFORM) { + _E("\"tizen:disposition\" needs \"%s \" level", enumToString(Level::PLATFORM).c_str()); + return false; + } + } + } + return true; +} + +bool TaskCertifyLevel::checkServiceLevel( + TaskCertifyLevel::Level level) +{ + if (m_contextData.widgetConfig.configInfo.serviceAppInfoList.size() > 0) { + if (level < Level::PARTNER) { + _E("\"tizen:service\" needs \"%s \" level", enumToString(Level::PARTNER).c_str()); + return false; + } + } + return true; +} + +std::string TaskCertifyLevel::enumToString( + TaskCertifyLevel::Level level) +{ + switch (level) { +#define X(x, y) case x: return #y; + X(Level::UNKNOWN, UNKNOWN) + X(Level::PUBLIC, PUBLIC) + X(Level::PARTNER, PARTNER) + X(Level::PLATFORM, PLATFORM) +#undef X + default: + return "UNKNOWN"; + } +} + +TaskCertifyLevel::Level TaskCertifyLevel::certTypeToLevel( + CertStoreId::Type type) +{ + // CertStoreType.h (framework/security/cert-svc) + // RootCA's visibility level : public + // const Type VIS_PUBLIC = 1 << 6; + // RootCA's visibility level : partner + // const Type VIS_PARTNER = 1 << 7; + // RootCA's visibility level : platform + // const Type VIS_PLATFORM = 1 << 10; + if (type == CertStoreId::VIS_PUBLIC) { + return Level::PUBLIC; + } else if (type == CertStoreId::VIS_PARTNER) { + return Level::PARTNER; + } else if (type == CertStoreId::VIS_PLATFORM) { + return Level::PLATFORM; + } + return Level::UNKNOWN; +} + +void TaskCertifyLevel::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskCertifyLevel::EndStep() +{ + LOGD("--------- : END ----------"); + + m_contextData.job->UpdateProgress( + InstallerContext::INSTALL_CERTIFY_LEVEL_CHECK, + "Application Certificate level check Finished"); +} +} //namespace WidgetInstall +} //namespace Jobs + diff --git a/src_wearable/jobs/widget_install/task_certify_level.h b/src_wearable/jobs/widget_install/task_certify_level.h new file mode 100755 index 0000000..004ecbb --- /dev/null +++ b/src_wearable/jobs/widget_install/task_certify_level.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_certify_level.h + * @author Jihoon Chung (jihoon.chung@samgsung.com) + * @version + * @brief + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H + +//SYSTEM INCLUDES +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskCertifyLevel : + public DPL::TaskDecl +{ + public: + TaskCertifyLevel(InstallerContext &inCont); + + private: + //data + InstallerContext& m_contextData; + + enum Level { + UNKNOWN = 0, + PUBLIC = 1, + PARTNER = 2, + PLATFORM = 3 + }; + typedef std::map secureSettingMap; + typedef std::map::iterator secureSettingIter; + + //steps + void stepCertifyLevel(); + void StartStep(); + void EndStep(); + + //util + void getSignatureFiles(const std::string& path, + ValidationCore::SignatureFileInfoSet& file); + Level getCertifyLevel(); + bool checkConfigurationLevel(Level level); + bool checkSettingLevel(Level level); + bool checkAppcontrolHasDisposition(Level level); + bool checkServiceLevel(Level level); + std::string enumToString(Level level); + Level certTypeToLevel(ValidationCore::CertStoreId::Type type); + +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_CERTIFY_LEVEL_H diff --git a/src_wearable/jobs/widget_install/task_commons.cpp b/src_wearable/jobs/widget_install/task_commons.cpp new file mode 100644 index 0000000..c5b5b67 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_commons.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_commons.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "task_commons.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const char * const TEMPORARY_PATH_POSTFIX = "temp"; +const mode_t TEMPORARY_PATH_MODE = 0775; +} // namespace + +std::string createTempPath(bool preload) +{ + _D("Step: Creating temporary path"); + + // Temporary path + std::ostringstream tempPathBuilder; + + if (preload) { + tempPathBuilder << WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + tempPathBuilder << WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + tempPathBuilder << WrtDB::GlobalConfig::GetTmpDirPath(); + tempPathBuilder << "/"; + tempPathBuilder << TEMPORARY_PATH_POSTFIX; + tempPathBuilder << "_"; + + timeval tv; + gettimeofday(&tv, NULL); + tempPathBuilder << + (static_cast(tv.tv_sec) * 1000000ULL + + static_cast(tv.tv_usec)); + + std::string tempPath = tempPathBuilder.str(); + + // Remove old path if any + struct stat fileInfo; + + if (stat(tempPath.c_str(), &fileInfo) == 0) { + if (!WrtUtilRemove(tempPath)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Failed to to remove temporary directory"); + } + } + // Create new path + if (!WrtUtilMakeDir(tempPath, TEMPORARY_PATH_MODE)) { + ThrowMsg(Exceptions::FileOperationFailed, + "Failed to create temporary directory"); + } + + return tempPath; +} + +void createTempPath(const std::string& path) +{ + if (!WrtUtilMakeDir(path, TEMPORARY_PATH_MODE)) { + ThrowMsg(Exceptions::FileOperationFailed, + "Failed to create temporary directory"); + } +} +} // WidgetInstall +} // Jobs diff --git a/src_wearable/jobs/widget_install/task_commons.h b/src_wearable/jobs/widget_install/task_commons.h new file mode 100644 index 0000000..caf9660 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_commons.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_commons.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ + +#include + +namespace Jobs { +namespace WidgetInstall { +//TODO make directory like jobs common? + +std::string createTempPath(bool isReadOnly = false); +void createTempPath(const std::string& path); +} // WidgetInstall +} // Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_COMMONS_H_ */ diff --git a/src_wearable/jobs/widget_install/task_configuration.cpp b/src_wearable/jobs/widget_install/task_configuration.cpp new file mode 100755 index 0000000..fa3e0fa --- /dev/null +++ b/src_wearable/jobs/widget_install/task_configuration.cpp @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_configuration.cpp + * @version 1.0 + * @author Tomasz Iwanek + * @brief implementation file for configuration task + */ +#include "task_configuration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const char* const CONFIG_XML = "config.xml"; +const char* const WITH_OSP_XML = "res/wgt/config.xml"; +const char* const OSP_MANIFEST_XML = "info/manifest.xml"; +const char* const WRT_WIDGETS_XML_SCHEMA = "/usr/etc/wrt-installer/widgets.xsd"; + +//allowed: a-z, A-Z, 0-9 +const char* REG_TIZENID_PATTERN = "^[a-zA-Z0-9]{10}.{1,}$"; +const char* REG_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; +const char* REG_NAME_PATTERN = "^[a-zA-Z0-9._-]{1,}$"; +const size_t PACKAGE_ID_LENGTH = 10; + +static const DPL::String SETTING_VALUE_ENCRYPTION = L"encryption"; +static const DPL::String SETTING_VALUE_ENCRYPTION_ENABLE = L"enable"; +static const DPL::String SETTING_VALUE_ENCRYPTION_DISABLE = L"disable"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_NAME = L"install-location"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_PREPER_EXT = L"prefer-external"; +const DPL::String SETTING_VALUE_INSTALLTOEXT_AUTO = L"auto"; +const std::string XML_EXTENSION = ".xml"; + +bool hasExtension(const std::string& filename, const std::string& extension) +{ + _D("Looking for extension %s in %s", extension.c_str(), filename.c_str()); + size_t fileLen = filename.length(); + size_t extLen = extension.length(); + if (fileLen < extLen) { + _E("Filename %s is shorter than extension %s", filename.c_str(), extension.c_str()); + return false; + } + return (0 == filename.compare(fileLen - extLen, extLen, extension)); +} +} // namespace anonymous + +namespace Jobs { +namespace WidgetInstall { + +TaskConfiguration::TaskConfiguration(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_widgetConfig(m_context.widgetConfig.configInfo) +{ + AddStep(&TaskConfiguration::StartStep); + + AddStep(&TaskConfiguration::SetupTempDirStep); + AddStep(&TaskConfiguration::UnzipConfigurationStep); + AddStep(&TaskConfiguration::ParseXMLConfigStep); + + AddStep(&TaskConfiguration::TizenIdStep); + AddStep(&TaskConfiguration::ApplicationTypeStep); + AddStep(&TaskConfiguration::ResourceEncryptionStep); + AddStep(&TaskConfiguration::InstallationFSLocationStep); + + AddStep(&TaskConfiguration::DetectUpdateInstallationStep); + AddStep(&TaskConfiguration::CheckRDSSupportStep); + AddStep(&TaskConfiguration::ConfigureWidgetLocationStep); + AddStep(&TaskConfiguration::PkgmgrStartStep); + + AddStep(&TaskConfiguration::AppendTasklistStep); + + AddStep(&TaskConfiguration::EndStep); +} + +void TaskConfiguration::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskConfiguration::EndStep() +{ + m_context.job->UpdateProgress(InstallerContext::INSTALL_PARSE_CONFIG, + "Parse config.xml and set structure"); + LOGD("--------- : END ----------"); +} + +void TaskConfiguration::PkgmgrStartStep() +{ + pkgMgrInterface()->setPkgname(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid)); + pkgMgrInterface()->sendProgress(0); +} + +void TaskConfiguration::AppendTasklistStep() +{ + switch(m_context.widgetConfig.webAppType.appType) + { + case APP_TYPE_TIZENWEBAPP: + if (m_context.mode.installTime == InstallMode::InstallTime::FOTA) { + if (!m_context.isUpdateMode) { + _D("TaskConfiguration -> fota installation task list"); + m_context.job->appendFotaInstallationTaskList(); + } else { + _D("TaskConfiguration -> fota update task list"); + m_context.job->appendFotaUpdateTaskList(); + } + } else { + if (!m_context.isUpdateMode) { + _D("TaskConfiguration -> new installation task list"); + m_context.job->appendNewInstallationTaskList(); + } else { + if (m_context.mode.command == InstallMode::Command::REINSTALL) { + _D("TaskConfiguration -> rds update task list"); + m_context.job->appendRDSUpdateTaskList(); + } else if(m_context.mode.command == InstallMode::Command::RECOVERY) { + _D("TaskConfiguration -> recovery task list"); + m_context.job->appendRecoveryTaskList(); + } else { + _D("TaskConfiguration -> update installation task list"); + m_context.job->appendUpdateInstallationTaskList(); + } + } + } + break; + default: + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid, "Unknown application type"); + } +} + +std::shared_ptr TaskConfiguration::pkgMgrInterface() +{ + return m_context.job->GetInstallerStruct().pkgmgrInterface; +} + +void TaskConfiguration::SetupTempDirStep() +{ + _D("widgetPath: %s", m_context.requestedPath.c_str()); + _D("tempPath: %s", m_tempDir.c_str()); + if (m_context.mode.extension == InstallMode::ExtensionType::DIR) { + if (m_context.mode.command == + InstallMode::Command::REINSTALL) { + std::ostringstream tempPathBuilder; + tempPathBuilder << WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + tempPathBuilder << WrtDB::GlobalConfig::GetTmpDirPath(); + tempPathBuilder << "/"; + tempPathBuilder << m_context.requestedPath; + m_tempDir = tempPathBuilder.str(); + } else if(m_context.mode.command == InstallMode::Command::RECOVERY) { + m_tempDir = Jobs::WidgetInstall::createTempPath(false); + } else { + m_tempDir = m_context.requestedPath; + } + } else { + m_tempDir = + Jobs::WidgetInstall::createTempPath( + m_context.mode.rootPath == + InstallMode::RootPath::RO); + } +} + +void TaskConfiguration::UnzipConfigurationStep() +{ + _D("UnzipConfigurationStep"); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) { + if(!hasExtension(m_context.requestedPath, XML_EXTENSION)) //unzip everything except xml files + { + WidgetUnzip wgtUnzip(m_context.requestedPath); + wgtUnzip.unzipConfiguration(m_tempDir, &m_context.widgetConfig.packagingType); + m_configuration += m_tempDir + "/" + CONFIG_XML; + } else{ + m_context.widgetConfig.packagingType = PKG_TYPE_HOSTED_WEB_APP; + m_configuration += m_context.requestedPath; + } + } else { + std::string configFile = m_tempDir + "/" + CONFIG_XML; + std::string manifestFile = m_tempDir + "/"; + if (!WrtUtilFileExists(configFile)) { + configFile = m_tempDir + "/" + WITH_OSP_XML; + if (!WrtUtilFileExists(configFile)) { + std::string tzAppId = m_context.requestedPath. + substr(m_context.requestedPath.find_last_of("/")+1); + Try { + WidgetDAOReadOnly dao(WidgetDAOReadOnly::getTizenAppId(DPL::FromUTF8String(tzAppId))); + configFile = DPL::ToUTF8String(*dao.getWidgetInstalledPath()); + configFile += "/"; + manifestFile = configFile; + configFile += WITH_OSP_XML; + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Given tizenId not found in database"); + ThrowMsg(Exceptions::DatabaseFailure, "Given tizenId not found in database"); + } + } + } + m_context.widgetConfig.packagingType = PKG_TYPE_NOMAL_WEB_APP; + manifestFile += OSP_MANIFEST_XML; + + if (WrtUtilFileExists(manifestFile)) { + m_context.widgetConfig.packagingType = PKG_TYPE_HYBRID_WEB_APP; + } + m_configuration = configFile; + } + _D("m_configuration : %s", m_configuration.c_str()); + _D("Package Type : %s", m_context.widgetConfig.packagingType.getPkgtypeToString().c_str()); +} + +void TaskConfiguration::ParseXMLConfigStep() +{ + _D("ParseXMLConfigStep"); + // Parse config + ParserRunner parser; + Try + { + _D("m_configuration : %s", m_configuration.c_str()); + if(!DPL::Utils::Path(m_configuration).Exists()) + { + ThrowMsg(Exceptions::MissingConfig, "Config file not exists"); + } + +#ifdef SCHEMA_VALIDATION_ENABLED + if(!parser.Validate(m_configuration, WRT_WIDGETS_XML_SCHEMA)) + { + _E("Invalid configuration file - schema validation failed"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Failed to parse config.xml file"); + } +#endif + parser.Parse(m_configuration, + ElementParserPtr( + new RootParser(m_widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + } + Catch(ElementParser::Exception::ParseError) + { + _E("Failed to parse config.xml file"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Parser exeption"); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Failed to find installed widget - give proper tizenId"); + ThrowMsg(Exceptions::RDSDeltaFailure, "WidgetNotExist"); + } + Catch(Exceptions::WidgetConfigFileNotFound){ + _E("Failed to find config.xml"); + ThrowMsg(Exceptions::MissingConfig, "Parser exeption"); + } + + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) { + if (!WrtUtilRemove(m_configuration)) { + _E("Error occurs during removing %s", m_configuration.c_str()); + } + } + +} + +void TaskConfiguration::TizenIdStep() +{ + bool shouldMakeAppid = false; + using namespace PackageManager; + + if (!!m_widgetConfig.tizenAppId) { + _D("Setting tizenAppId provided in config.xml: %s", DPL::ToUTF8String(*m_widgetConfig.tizenAppId).c_str()); + + m_context.widgetConfig.tzAppid = *m_widgetConfig.tizenAppId; + //check package id. + if (!!m_widgetConfig.tizenPkgId) { + _D("Setting tizenPkgId provided in config.xml: %s", DPL::ToUTF8String(*m_widgetConfig.tizenPkgId).c_str()); + + m_context.widgetConfig.tzPkgid = *m_widgetConfig.tizenPkgId; + } else { + DPL::String appid = *m_widgetConfig.tizenAppId; + if (appid.length() > PACKAGE_ID_LENGTH) { + m_context.widgetConfig.tzPkgid = + appid.substr(0, PACKAGE_ID_LENGTH); + } else { + //old version appid only has 10byte random character is able to install for a while. + //this case appid equal pkgid. + m_context.widgetConfig.tzPkgid = + *m_widgetConfig.tizenAppId; + shouldMakeAppid = true; + } + } + } else { + shouldMakeAppid = true; + TizenPkgId pkgId = WidgetDAOReadOnly::generatePkgId(); + _D("Checking if pkg id is unique"); + while (true) { + if (!validateTizenPackageID(pkgId)) { + //path exist, chose another one + pkgId = WidgetDAOReadOnly::generatePkgId(); + continue; + } + break; + } + m_context.widgetConfig.tzPkgid = pkgId; + _D("tizen_id name was generated by WRT: %ls", m_context.widgetConfig.tzPkgid.c_str()); + } + + if (shouldMakeAppid == true) { + DPL::OptionalString name; + DPL::OptionalString defaultLocale = m_widgetConfig.defaultlocale; + + FOREACH(localizedData, m_widgetConfig.localizedDataSet) + { + Locale i = localizedData->first; + if (!!defaultLocale) { + if (defaultLocale == i) { + name = localizedData->second.name; + break; + } + } else { + name = localizedData->second.name; + break; + } + } + regex_t regx; + if (regcomp(®x, REG_NAME_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + } + + _D("Name : %ls", (*name).c_str()); + if (!name || (regexec(®x, DPL::ToUTF8String(*name).c_str(), + static_cast(0), NULL, 0) != REG_NOERROR)) + { + const std::string allowedString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + std::ostringstream genName; + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned int seed = time(NULL) + tv.tv_usec; + + genName << "_" << allowedString[rand_r(&seed) % allowedString.length()]; + name = DPL::FromUTF8String(genName.str()); + _D("name was generated by WRT"); + } + regfree(®x); + _D("Name : %ls", (*name).c_str()); + std::ostringstream genid; + genid << m_context.widgetConfig.tzPkgid << "." << (*name); + _D("tizen appid was generated by WRT : %s", genid.str().c_str()); + + DPL::OptionalString appid = DPL::FromUTF8String(genid.str()); + NormalizeAndTrimSpaceString(appid); + m_context.widgetConfig.tzAppid = *appid; + } + + // send start signal of pkgmgr + pkgMgrInterface()->setPkgname(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid)); + + _D("Tizen App Id : %ls", (m_context.widgetConfig.tzAppid).c_str()); + _D("Tizen Pkg Id : %ls", (m_context.widgetConfig.tzPkgid).c_str()); +} + +void TaskConfiguration::ConfigureWidgetLocationStep() +{ + m_context.locations = + WidgetLocation(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid), + m_context.requestedPath, m_tempDir, + m_context.widgetConfig.packagingType, + m_context.mode.rootPath == + InstallMode::RootPath::RO, + m_context.mode.extension); + m_context.locations->registerAppid( + DPL::ToUTF8String(m_context.widgetConfig.tzAppid)); +#ifdef SERVICE_ENABLED + FOREACH(it, m_context.widgetConfig.configInfo.serviceAppInfoList) + { + m_context.locations->registerServiceAppid(DPL::ToUTF8String(it->serviceId)); + } +#endif + _D("widgetSource %s", m_context.requestedPath.c_str()); +} + +void TaskConfiguration::DetectUpdateInstallationStep() +{ + pkgmgrinfo_pkginfo_h handle; + + if (PMINFO_R_OK == + pkgmgrinfo_pkginfo_get_pkginfo(DPL::ToUTF8String( + m_context.widgetConfig.tzPkgid).c_str(), &handle)) { + // Update mode + m_context.isUpdateMode = true; + pkgMgrInterface()->startJob(InstallationType::UpdateInstallation); + + } else { + // Install mode + m_context.isUpdateMode = false; + pkgMgrInterface()->startJob(InstallationType::NewInstallation); + + if (!validateTizenApplicationID(m_context.widgetConfig.tzAppid)) { + _E("tizen application ID is invalid"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid, + "invalid config"); + } + if (!validateTizenPackageID(m_context.widgetConfig.tzPkgid)) { + _E("tizen package ID is invalid"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid, + "invalid config"); + } + } + OptionalWidgetVersion incomingVersion; + char *existingVersion = NULL; + + if (!!m_widgetConfig.version) { + incomingVersion = + OptionalWidgetVersion( + WidgetVersion(*m_widgetConfig.version)); + _D("incoming version = '%ls", incomingVersion->Raw().c_str()); + } + + if (m_context.isUpdateMode && + PMINFO_R_OK == pkgmgrinfo_pkginfo_get_version(handle, + &existingVersion)) { + _D("existing version = %s", existingVersion); + } + + pkgmgrinfo_pkginfo_destroy_pkginfo(handle); +} + +void TaskConfiguration::CheckRDSSupportStep() +{ + //update needs RDS support to go ahead if REINSTALL command is given + if(m_context.isUpdateMode) + { + if (!checkSupportRDSUpdateIfReinstall(m_widgetConfig)) { + ThrowMsg(Jobs::WidgetInstall::Exceptions::NotSupportRDSUpdate, + "RDS update failed"); + } + } +} + +bool TaskConfiguration::validateTizenApplicationID( + const WrtDB::TizenAppId &tizenAppId) +{ + _D("tizen application ID = [%ls]", tizenAppId.c_str()); + + regex_t reg; + if (regcomp(®, REG_TIZENID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + return false; + } + + if (regexec(®, DPL::ToUTF8String(tizenAppId).c_str(), 0, NULL, 0) + == REG_NOMATCH) + { + regfree(®); + return false; + } + regfree(®); + return true; +} + +bool TaskConfiguration::validateTizenPackageID( + const WrtDB::TizenPkgId &tizenPkgId) +{ + _D("tizen application ID = [%ls]", tizenPkgId.c_str()); + + regex_t reg; + if (regcomp(®, REG_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) + { + _D("Regcomp failed"); + return false; + } + if (regexec(®, DPL::ToUTF8String(tizenPkgId).c_str(), 0, NULL, 0) == REG_NOMATCH) + { + regfree(®); + return false; + } + regfree(®); + return true; +} + +void TaskConfiguration::ApplicationTypeStep() //TODO: is this really needed as WAC is not supported? +{ + AppType widgetAppType = APP_TYPE_UNKNOWN; + FOREACH(iterator, m_widgetConfig.nameSpaces) { + _D("namespace = [%ls]", (*iterator).c_str()); + + if (*iterator == ConfigurationNamespace::TizenWebAppNamespaceName) { + widgetAppType = APP_TYPE_TIZENWEBAPP; + break; + } + } + + m_context.widgetConfig.webAppType = widgetAppType; + + _D("type = [%s]", m_context.widgetConfig.webAppType.getApptypeToString().c_str()); +} + +void TaskConfiguration::ResourceEncryptionStep() +{ + m_context.needEncryption = false; + FOREACH(it, m_widgetConfig.settingsList) + { + if (it->m_name == SETTING_VALUE_ENCRYPTION && + it->m_value == SETTING_VALUE_ENCRYPTION_ENABLE) + { + _D("resource need encryption"); + m_context.needEncryption = true; + } + } +} + +void TaskConfiguration::InstallationFSLocationStep() +{ + if (m_context.mode.installTime == InstallMode::InstallTime::NORMAL) { + FOREACH(it, m_widgetConfig.settingsList) { + if (it->m_name == SETTING_VALUE_INSTALLTOEXT_NAME) { + if (it->m_value == SETTING_VALUE_INSTALLTOEXT_AUTO) { + m_context.locationType = INSTALL_LOCATION_TYPE_AUTO; + } else if (it->m_value == SETTING_VALUE_INSTALLTOEXT_PREPER_EXT) { + m_context.locationType = + INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } else { + m_context.locationType = + INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + } + break; + } + } + } else { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + } +} + +bool TaskConfiguration::checkSupportRDSUpdateIfReinstall(const WrtDB::ConfigParserData + &configInfo) +{ + if (m_context.mode.command == + InstallMode::Command::REINSTALL) + { + DPL::String configValue = SETTING_VALUE_ENCRYPTION_DISABLE; + DPL::String dbValue = SETTING_VALUE_ENCRYPTION_DISABLE; + + WidgetDAOReadOnly dao(m_context.widgetConfig.tzAppid); + WrtDB::WidgetSettings widgetSettings; + dao.getWidgetSettings(widgetSettings); + + FOREACH(it, widgetSettings) { + if (it->settingName == SETTING_VALUE_ENCRYPTION) { + dbValue = it->settingValue; + } + } + + FOREACH(data, configInfo.settingsList) + { + if (data->m_name == SETTING_VALUE_ENCRYPTION) + { + configValue = data->m_value; + } + } + if (configValue != dbValue) { + _E("Not Support RDS mode because of encryption setting"); + return false; + } + } + + return true; +} + +} +} diff --git a/src_wearable/jobs/widget_install/task_configuration.h b/src_wearable/jobs/widget_install/task_configuration.h new file mode 100644 index 0000000..879a8d3 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_configuration.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_configuration.h + * @version 1.0 + * @author Tomasz Iwanek + * @brief header file for configuration task + */ +#ifndef TASK_CONFIGURATION_H +#define TASK_CONFIGURATION_H + +#include + +#include +#include + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { + +class TaskConfiguration : public DPL::TaskDecl +{ + InstallerContext& m_context; + std::string m_tempDir; + WrtDB::ConfigParserData &m_widgetConfig; + std::string m_configuration; + + void parseWidgetXMLConfig( + const std::string &widgetSource, + const std::string &tempPath, + WrtDB::PackagingType pkgType, + bool isReinstall); + + bool validateTizenApplicationID(const WrtDB::TizenAppId &tizenAppId); + bool validateTizenPackageID(const WrtDB::TizenPkgId &tizenPkgId); + void ApplicationTypeStep(const WrtDB::ConfigParserData &configInfo); + bool checkSupportRDSUpdateIfReinstall(const WrtDB::ConfigParserData &configInfo); + bool getDefaultExternalStorage(); + bool getMMCStatus(); + + std::shared_ptr pkgMgrInterface(); + + //steps + void StartStep(); + + void SetupTempDirStep(); + void UnzipConfigurationStep(); + void ParseXMLConfigStep(); + + void TizenIdStep(); + void DetectUpdateInstallationStep(); + void PkgmgrStartStep(); + + void ApplicationTypeStep(); + void ResourceEncryptionStep(); + void InstallationFSLocationStep(); + + void ConfigureWidgetLocationStep(); + void CheckRDSSupportStep(); + + void AppendTasklistStep(); + void EndStep(); + +public: + TaskConfiguration(InstallerContext& context); +}; + +} +} + +#endif // TASK_CONFIGURATION_H diff --git a/src_wearable/jobs/widget_install/task_database.cpp b/src_wearable/jobs/widget_install/task_database.cpp new file mode 100755 index 0000000..8b9660b --- /dev/null +++ b/src_wearable/jobs/widget_install/task_database.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_new_db_insert.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task database updating for widget + * update + */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DBOX_ENABLED +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskDatabase::TaskDatabase(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskDatabase::StartStep); + AddStep(&TaskDatabase::StepRegisterExternalFiles); + AddStep(&TaskDatabase::StepWrtDBInsert); + AddStep(&TaskDatabase::StepAceDBInsert); + AddStep(&TaskDatabase::StepSecurityOriginDBInsert); + AddStep(&TaskDatabase::StepWidgetInterfaceDBInsert); + AddStep(&TaskDatabase::StepRemoveExternalFiles); +#ifdef DBOX_ENABLED + AddStep(&TaskDatabase::StepLiveboxDBInsert); +#endif + AddStep(&TaskDatabase::EndStep); + + AddAbortStep(&TaskDatabase::StepAbortDBInsert); + AddAbortStep(&TaskDatabase::StepAbortAceDBInsert); + AddAbortStep(&TaskDatabase::StepAbortWidgetInterfaceDBInsert); +} + +void TaskDatabase::StepWrtDBInsert() +{ + Try + { + /* Set install Time */ + time(&m_context.widgetConfig.installedTime); + + if (m_context.isUpdateMode) { //update + _D("Registering widget... (update)"); + Try + { + std::list idList = WidgetDAOReadOnly::getTzAppIdList(m_context.widgetConfig.tzPkgid); + FOREACH(it , idList ){ + //installed AppId list, It need to delete ACE Database corresponding record + m_handleToRemoveList.push_back(WidgetDAOReadOnly::getHandle(*it)); + WrtDB::TizenAppId backAppId = *it + L".backup"; + m_backAppIdList.push_back(backAppId); + //Change all installed tzAppid to .backup + WidgetDAO::updateTizenAppId(*it, backAppId); + } + + WidgetDAO::registerWidget(m_context.widgetConfig.tzAppid, + m_context.widgetConfig, + m_context.widgetSecurity); + m_handleList.push_back(WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid)); + + FOREACH(iterator, m_context.widgetConfig.configInfo.serviceAppInfoList) { + WrtDB::TizenAppId tizenAppId = iterator->serviceId; + WidgetDAO::registerService(*iterator, m_context.widgetConfig, m_context.widgetSecurity); + m_handleList.push_back(WidgetDAOReadOnly::getHandle(tizenAppId)); + } + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + LogError( + "Given tizenId not found for update installation (Same GUID?)"); + ThrowMsg(Exceptions::DatabaseFailure, + "Given tizenId not found for update installation"); + } + } else { //new installation + _D("Registering widget..."); + WidgetDAO::registerWidget( + m_context.widgetConfig.tzAppid, + m_context.widgetConfig, + m_context.widgetSecurity); + + m_handleList.push_back(WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid)); + + FOREACH(iterator, m_context.widgetConfig.configInfo.serviceAppInfoList) { + WidgetDAO::registerService(*iterator, m_context.widgetConfig, m_context.widgetSecurity); + m_handleList.push_back(WidgetDAOReadOnly::getHandle(iterator->serviceId)); + } + } + + FOREACH(cap, m_context.staticPermittedDevCaps) { + _D("staticPermittedDevCaps : %ls smack status: %d", cap->first.c_str(), cap->second); + } + + _D("Widget registered"); + } + Catch(WidgetDAO::Exception::DatabaseError) + { + _E("Database failure!"); + ReThrowMsg(Exceptions::InsertNewWidgetFailed, "Database failure!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Database failure!"); + ReThrowMsg(Exceptions::InsertNewWidgetFailed, "Database failure!"); + } +} + +void TaskDatabase::StepAceDBInsert() +{ + FOREACH(iterHandleToRemove, m_handleToRemoveList) + { + if (INVALID_WIDGET_HANDLE != *iterHandleToRemove) { + _D("Removing old insallation. Handle: %d", *iterHandleToRemove); + if (ACE_OK != ace_unregister_widget( + static_cast(*iterHandleToRemove))) + { + _W("Error while removing ace entry for previous insallation"); + } + } + } + + FOREACH(iterHandle, m_handleList) + { + if (!AceApi::registerAceWidget(*iterHandle, m_context.widgetConfig, + m_context.widgetSecurity.getCertificateList())) + { + _E("ace database insert failed"); + ThrowMsg(Exceptions::UpdateFailed, + "Update failure. ace_register_widget failed"); + } + _D("Ace data inserted"); + } +} + +void TaskDatabase::StepSecurityOriginDBInsert() +{ + _D("Create Security origin database"); + // automatically create security origin database + using namespace SecurityOriginDB; + using namespace WrtDB; + + try{ + SecurityOriginDAO dao(m_context.locations->getPkgId()); + // Checking privilege list for setting security origin exception data + FOREACH(it, m_context.widgetConfig.configInfo.privilegeList) { + std::map::const_iterator result = + g_W3CPrivilegeTextMap.find(DPL::ToUTF8String(it->name)); + if (result != g_W3CPrivilegeTextMap.end()) { + if (result->second == FEATURE_USER_MEDIA) { + dao.setPrivilegeSecurityOriginData(result->second, false); + } else if (result->second == FEATURE_FULLSCREEN_MODE) { + continue; + } else { + dao.setPrivilegeSecurityOriginData(result->second); + } + } + } + }catch(const SecurityOriginDAO::Exception::DatabaseError& err){ + _E("error open SecurityOrigin db %s", err.GetMessage().c_str()); + ThrowMsg(Exceptions::UpdateFailed, "Cannot open SecurityOrigin DB"); + } +} + +void TaskDatabase::StepWidgetInterfaceDBInsert() +{ + _D("Create Widget Interface database"); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + + // backup database + if (m_context.isUpdateMode) { + std::string dbPath = WidgetInterfaceDAO::databaseFileName(handle); + std::string backupDbPath = dbPath; + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + _D("\"%s\" to \"%s\"", dbPath.c_str(), backupDbPath.c_str()); + if (0 != std::rename(dbPath.c_str(), backupDbPath.c_str())) { + _E("widget interface database backup failed"); + ThrowMsg(Exceptions::UpdateFailed, + "widget interface database backup failed"); + } + } + + Try + { + // automatically create widget interface database + WidgetInterfaceDAO dao(handle); + } + Catch(WidgetInterfaceDAO::Exception::DatabaseError) + { + _E("widget interface database create failed"); + ThrowMsg(Exceptions::UpdateFailed, + "widget interface database create failed"); + } +} + +void TaskDatabase::StepRegisterExternalFiles() +{ + WrtDB::ExternalLocationList externalLocationsUpdate = + m_context.locations->listExternalLocations(); + if (m_context.isUpdateMode) { //update + Try + { + WidgetDAOReadOnly dao(WidgetDAOReadOnly::getHandleByPkgId(m_context.widgetConfig.tzPkgid)); + WrtDB::ExternalLocationList externalLocationsDB = + dao.getWidgetExternalLocations(); + FOREACH(file, externalLocationsDB) + { + if (std::find(externalLocationsUpdate.begin(), + externalLocationsUpdate.end(), + *file) == externalLocationsUpdate.end()) + { + m_externalLocationsToRemove.push_back(*file); + } + } + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Given tizenId not found for update installation (Same GUID?)"); + ThrowMsg(Exceptions::UpdateFailed, + "Given tizenId not found for update installation"); + } + } + _D("Registering external files:"); + FOREACH(file, externalLocationsUpdate) + { + _D(" -> %s", (*file).c_str()); + } + + //set external locations to be registered + m_context.widgetConfig.externalLocations = externalLocationsUpdate; +} + +void TaskDatabase::StepRemoveExternalFiles() +{ + if (!m_externalLocationsToRemove.empty()) { + _D("Removing external files:"); + } + + FOREACH(file, m_externalLocationsToRemove) + { + if (WrtUtilFileExists(*file)) { + _D(" -> %s", (*file).c_str()); + if (-1 == remove(file->c_str())) { + ThrowMsg(Exceptions::RemovingFileFailure, + "Failed to remove external file"); + } + } else if (WrtUtilDirExists(*file)) { + _D(" -> %s", (*file).c_str()); + if (!WrtUtilRemove(*file)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Failed to remove external directory"); + } + } else { + _W(" -> %s(no such a path)", (*file).c_str()); + } + } +} + +void TaskDatabase::StepAbortDBInsert() +{ + _W("[DB Update Task] Aborting... (DB Clean)"); + Try + { + WidgetDAO::unregisterWidget(m_context.widgetConfig.tzAppid); + + FOREACH(iter, m_context.widgetConfig.configInfo.serviceAppInfoList) { + WidgetDAO::unregisterWidget(iter->serviceId); + } + + if (m_context.isUpdateMode) { + FOREACH(iter, m_backAppIdList) { + unsigned pos = (*iter).find(L".backup"); + TizenAppId str = (*iter).substr(0, pos); + WidgetDAO::updateTizenAppId(*iter, str); + } + } + _D("Cleaning DB successful!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Failed to handle StepAbortDBClean!"); + } +} + +void TaskDatabase::StepAbortAceDBInsert() +{ + _W("[DB Update Task] ACE DB Aborting... (DB Clean)"); + + FOREACH(iter, m_handleList) { + ace_unregister_widget(static_cast(*iter)); + } + + FOREACH(iter, m_handleToRemoveList) { + // Remove also old one. If it was already updated nothing wrong will happen, + // but if not old widget will be removed. + if (INVALID_WIDGET_HANDLE != *iter) { + ace_unregister_widget(static_cast(*iter)); + } + + if (!AceApi::registerAceWidgetFromDB(*iter)) + { + _E("ace database restore failed"); + } + } + _D("Ace data inserted"); +} + +void TaskDatabase::StepAbortWidgetInterfaceDBInsert() +{ + _D("[DB Update Task] Widget interface Aborting..."); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + std::string dbPath = WidgetInterfaceDAO::databaseFileName(handle); + + // remove database + if (remove(dbPath.c_str()) != 0) { + _W("Fail to remove"); + } + + // rollback database + if (m_context.isUpdateMode) { + std::string backupDbPath = dbPath; + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + _D("\"%s\" to \"%s\"", dbPath.c_str(), backupDbPath.c_str()); + if (0 != std::rename(backupDbPath.c_str(), dbPath.c_str())) { + _W("Fail to rollback"); + } + } +} + +#ifdef DBOX_ENABLED +void TaskDatabase::StepLiveboxDBInsert() +{ + if (m_context.widgetConfig.configInfo.m_livebox.size() <= 0) { + return; + } + + std::string tizenId = DPL::ToUTF8String(m_context.widgetConfig.tzAppid); + + // insert specific information to web livebox db + for (auto it = m_context.widgetConfig.configInfo.m_livebox.begin(); + it != m_context.widgetConfig.configInfo.m_livebox.end(); ++it) + { + std::string boxId = DPL::ToUTF8String((**it).m_liveboxId); + std::string boxType; + if ((**it).m_type.empty()) { + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String((**it).m_type); + } + _D("livebox id: %s", boxId.c_str()); + _D("livebox type: %s", boxType.c_str()); + + int autoLaunch = (**it).m_autoLaunch == L"true" ? 1 : 0; + _D("livebox auto-launch: %d", autoLaunch); + + int mouseEvent = (**it).m_boxInfo.m_boxMouseEvent == L"true" ? 1 : 0; + _D("livebox mouse-event: %d", mouseEvent); + + int pdFastOpen = (**it).m_boxInfo.m_pdFastOpen == L"true" ? 1 : 0; + _D("livebox pd fast-open: %d", pdFastOpen); + + if (m_context.isUpdateMode) { + web_provider_livebox_delete_by_app_id(tizenId.c_str()); + } + web_provider_livebox_insert_box_info( + boxId.c_str(), tizenId.c_str(), boxType.c_str(), + autoLaunch, mouseEvent, pdFastOpen); + } +} +#endif + +void TaskDatabase::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskDatabase::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_database.h b/src_wearable/jobs/widget_install/task_database.h new file mode 100755 index 0000000..9236b1b --- /dev/null +++ b/src_wearable/jobs/widget_install/task_database.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_database.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task database updating + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskDatabase : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + WrtDB::ExternalLocationList m_externalLocationsToRemove; + + //TODO: temporary needed until security-server start to use pkgName instead + //of widget handle + std::list m_handleToRemoveList; + std::list m_handleList; + std::list m_backAppIdList; + WrtDB::TizenAppId m_orginAppId; + + void StepRegisterExternalFiles(); + void StepWrtDBInsert(); + void StepAceDBInsert(); + void StepSecurityOriginDBInsert(); + void StepWidgetInterfaceDBInsert(); + void StepRemoveExternalFiles(); + void StepLiveboxDBInsert(); + + void StepAbortDBInsert(); + void StepAbortAceDBInsert(); + void StepAbortWidgetInterfaceDBInsert(); + + void StartStep(); + void EndStep(); + + public: + TaskDatabase(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DATABASE_H diff --git a/src_wearable/jobs/widget_install/task_encrypt_resource.cpp b/src_wearable/jobs/widget_install/task_encrypt_resource.cpp new file mode 100644 index 0000000..f048904 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_encrypt_resource.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_ecnrypt_resource.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task encrypt resource + */ +#include "task_encrypt_resource.h" + +#undef __USE_FILE_OFFSET64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 8192; // bytes +const std::size_t ENCRYPTION_DEC_CHUNK_SIZE = 4; // bytes + +std::set& getSupportedForEncryption() +{ + static std::set encryptSet; + if (encryptSet.empty()) { + encryptSet.insert(".html"); + encryptSet.insert(".htm"); + encryptSet.insert(".css"); + encryptSet.insert(".js"); + } + return encryptSet; +} + +bool isSupportedForEncryption(const std::string &file) +{ + size_t foundKey = file.rfind("."); + if (std::string::npos != foundKey) { + std::string mimeType = file.substr(foundKey); + std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(), + ::tolower); + + return getSupportedForEncryption().count(mimeType) > 0; + } + return false; +} + +/** + * Opens a file. + * + * @param path Path to a file. + * @param mode Mode. + * @return Stream handle. + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +FILE* openFile(const std::string& path, const std::string& mode) +{ + FILE* result = NULL; + + do + { + result = fopen(path.c_str(), mode.c_str()); + } while ((NULL == result) && (EINTR == errno)); + + if (NULL == result) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed, + "Could not open file " << path); + } + + return result; +} + +/** + * Reads bytes from a stream. + * + * @param buffer Buffer to read the bytes into. + * @param count Number of bytes to read. + * @param stream Stream to read from. + * @return Number of bytes read + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream) +{ + std::size_t result = std::fread(buffer, + sizeof(unsigned char), + count, + stream); + + if (result != count) + { + int error = errno; + if (0 != std::ferror(stream)) + { + if (EINTR != error) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::ErrorExternalInstallingFailure, + "Error while reading data" << + " [" << DPL::GetErrnoString(error) << "]"); + } + } + } + + return result; +} + +/** + * Writes bytes to a stream. + * + * @param buffer Data to write. + * @param count Number of bytes. + * @param stream Stream to write to. + * @throw ExtractFileFailed If error (other than EINTR) occurs. + */ +void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream) +{ + std::size_t bytesWritten = 0; + std::size_t bytesToWrite = 0; + do + { + bytesToWrite = count - bytesWritten; + bytesWritten = std::fwrite(buffer + bytesWritten, + sizeof(unsigned char), + count - bytesWritten, + stream); + if ((bytesWritten != bytesToWrite) && (EINTR != errno)) + { + int error = errno; + ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed, + "Error while writing data" << + " [" << DPL::GetErrnoString(error) << "]"); + } + } while ((bytesWritten != bytesToWrite) && (EINTR == errno)); +} + +int ssmEncrypt(InstallMode::InstallTime time, std::string pkgId, const char* + inChunk, int inBytes, char** outChunk, int *outBytes) +{ + if (time == InstallMode::InstallTime::PRELOAD) { + return ssm_encrypt_preloaded_application(inChunk, inBytes, outChunk, outBytes); + } else { + return ssm_encrypt(pkgId.c_str(),pkgId.length(), inChunk, inBytes, outChunk, outBytes); + } +} + +} + +namespace Jobs { +namespace WidgetInstall { +TaskEncryptResource::TaskEncryptResource(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskEncryptResource::StartStep); + AddStep(&TaskEncryptResource::StepEncryptResource); + AddStep(&TaskEncryptResource::EndStep); +} + +void TaskEncryptResource::StepEncryptResource() +{ + _D("Step Encrypt resource"); + + EncryptDirectory(m_context.locations->getSourceDir()); +} + +void TaskEncryptResource::EncryptDirectory(std::string path) +{ + FTS *fts; + FTSENT *ftsent; + char * const paths[] = { const_cast(path.c_str()), NULL }; + + if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) { + //ERROR + int error = errno; + _W("%s: fts_open failed with error: %s", __PRETTY_FUNCTION__, strerror(error)); + ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: " + << path); + } + + while ((ftsent = fts_read(fts)) != NULL) { + switch (ftsent->fts_info) { + case FTS_DP: + case FTS_DC: + case FTS_D: + case FTS_DEFAULT: + case FTS_SLNONE: + //directories, non-regular files, dangling symbolic links + break; + case FTS_F: + case FTS_NSOK: + case FTS_SL: + //regular files and other objects that can be counted + if (isSupportedForEncryption(ftsent->fts_path)) { + EncryptFile(ftsent->fts_path); + } + break; + case FTS_NS: + case FTS_DOT: + case FTS_DNR: + case FTS_ERR: + default: + _W("%s: traversal failed on file: %s with error: %s", __PRETTY_FUNCTION__, ftsent->fts_path, strerror(ftsent->fts_errno)); + ThrowMsg(Exceptions::EncryptionFailed, "Error reading file"); + break; + } + } + + if (fts_close(fts) == -1) { + int error = errno; + _W("%s: fts_close failed with error: %s", __PRETTY_FUNCTION__, strerror(error)); + } +} + +void TaskEncryptResource::EncryptFile(const std::string &fileName) +{ + _D("Encrypt file: %s", fileName.c_str()); + std::string encFile = fileName + ".enc"; + + struct stat info; + memset(&info, 0, sizeof(info)); + if (stat(fileName.c_str(), &info) != 0) + { + int error = errno; + ThrowMsg(Exceptions::EncryptionFailed, + "Could not access file " << fileName << + "[" << DPL::GetErrnoString(error) << "]"); + } + const std::size_t fileSize = info.st_size; + if (0 == fileSize) { + _D("%s size is 0, so encryption is skiped", fileName.c_str()); + return; + } + + // If update installed preload web, should skip encryption. + if (!(m_context.mode.rootPath == InstallMode::RootPath::RO && + (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_context.mode.installTime == InstallMode::InstallTime::FOTA) + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + + DPL::ScopedFClose inFile(openFile(fileName, "r")); + DPL::ScopedFClose outFile(openFile(encFile, "w")); + + const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE + ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize); + + std::unique_ptr inChunk(new unsigned char[chunkSize]); + std::size_t bytesRead = 0; + std::string pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + do + { + bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get()); + if (0 != bytesRead) { + int outDecSize = 0; + char *outChunk = NULL; + if (0 != ssmEncrypt(m_context.mode.installTime, pkgId, + (char*)inChunk.get(), (int)bytesRead, + &outChunk, &outDecSize)) { + ThrowMsg(Exceptions::EncryptionFailed, + "Encryption Failed using TrustZone"); + } + + std::stringstream toString; + toString << outDecSize; + + writeBytes((unsigned char*)toString.str().c_str(), + sizeof(int), outFile.Get()); + writeBytes((unsigned char*)outChunk, outDecSize, outFile.Get()); + delete outChunk; + } + inChunk.reset(new unsigned char[chunkSize]); + + } while (0 == std::feof(inFile.Get())); + + outFile.Reset(); + inFile.Reset(); + + _D("File encrypted successfully"); + _D("Remove plain-text file: %s", fileName.c_str()); + if (0 != unlink(fileName.c_str())) + { + Throw(Exceptions::EncryptionFailed); + } + + _D("Rename encrypted file"); + if (0 != std::rename(encFile.c_str(), fileName.c_str())) + { + Throw(Exceptions::EncryptionFailed); + } + } + + WrtDB::EncryptedFileInfo fileInfo; + fileInfo.fileName = DPL::FromUTF8String(fileName); + fileInfo.fileSize = fileSize; + + m_context.widgetConfig.encryptedFiles.insert(fileInfo); +} + +void TaskEncryptResource::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskEncryptResource::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_ECRYPTION_FILES, + "Ecrypt resource files"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_encrypt_resource.h b/src_wearable/jobs/widget_install/task_encrypt_resource.h new file mode 100644 index 0000000..b89a992 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_encrypt_resource.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_encrypt_resource.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_RESOURCE_ENCRYPT_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_RESOURCE_ENCRYPT_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskEncryptResource : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + std::string tempInstalledPath; + + void StepEncryptResource(); + + void StartStep(); + void EndStep(); + + void EncryptDirectory(std::string path); + void EncryptFile(const std::string &fileName); + + public: + explicit TaskEncryptResource(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_ENCRYPT_RESOURCE_H_ */ diff --git a/src_wearable/jobs/widget_install/task_file_manipulation.cpp b/src_wearable/jobs/widget_install/task_file_manipulation.cpp new file mode 100644 index 0000000..ae1128e --- /dev/null +++ b/src_wearable/jobs/widget_install/task_file_manipulation.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2010 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task database updating + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WEBAPP_DEFAULT_UID 5000 +#define WEBAPP_DEFAULT_GID 5000 + +namespace { +const mode_t PRIVATE_STORAGE_MODE = 0700; +const mode_t SHARED_STORAGE_MODE = 0755; +} + +using namespace WrtDB; + +namespace { +const char* GLIST_RES_DIR = "res"; + +bool _FolderCopy(std::string source, std::string dest) +{ + DIR* dir = opendir(source.c_str()); + if (NULL == dir) { + return false; + } + + struct dirent dEntry; + struct dirent *dEntryResult; + int return_code; + + do { + struct stat statInfo; + return_code = readdir_r(dir, &dEntry, &dEntryResult); + if (dEntryResult != NULL && return_code == 0) { + std::string fileName = dEntry.d_name; + std::string fullName = source + "/" + fileName; + + if (stat(fullName.c_str(), &statInfo) != 0) { + closedir(dir); + return false; + } + + if (S_ISDIR(statInfo.st_mode)) { + if (("." == fileName) || (".." == fileName)) { + continue; + } + std::string destFolder = dest + "/" + fileName; + WrtUtilMakeDir(destFolder); + + if (!_FolderCopy(fullName, destFolder)) { + closedir(dir); + return false; + } + } + + std::string destFile = dest + "/" + fileName; + std::ifstream infile(fullName); + std::ofstream outfile(destFile); + outfile << infile.rdbuf(); + outfile.close(); + infile.close(); + } + } while (dEntryResult != NULL && return_code == 0); + closedir(dir); + return true; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskFileManipulation::TaskFileManipulation(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context), + m_extHandle(NULL) +{ + AddStep(&TaskFileManipulation::StartStep); + AddStep(&TaskFileManipulation::StepCheckInstallLocation); + AddStep(&TaskFileManipulation::StepPrepareRootDirectory); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) + { + AddStep(&TaskFileManipulation::StepUnzipWgtFile); + } + AddStep(&TaskFileManipulation::EndStep); + + AddAbortStep(&TaskFileManipulation::StepAbortPrepareRootDirectory); +} + +void TaskFileManipulation::StepCheckInstallLocation() +{ + _D("StepCheckInstallLocation"); + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + return; + } + + // If webapp is hybrid app, it should be installed to internal storage. + // Because Service app should be installed to internal. + if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + return; + } + + std::string installedPath = WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + WidgetUnzip wgtUnzip(m_context.requestedPath); + + if (m_context.locationType == INSTALL_LOCATION_TYPE_AUTO) { + int storage = 0; + // vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT) + // 0 : phone internal memory + // 1 : SD card + if (vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT, + &storage)) { + _E("vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT) \ + failed."); + } + _D("default setting : storage [%d]", storage); + if (storage) { + m_context.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } else { + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + if(!wgtUnzip.checkAvailableSpace(installedPath)) { + m_context.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL; + } + } + } + + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + int mmcStatus; + if (vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS, &mmcStatus)) { + _E("vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS) failed."); + mmcStatus = VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED; + } + + if (VCONFKEY_SYSMAN_MMC_MOUNTED != mmcStatus) { + _D("mmcStatus is MMC_REMOVED or NOT_MOUNTED."); + m_context.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY; + } + } + + if (m_context.locationType == INSTALL_LOCATION_TYPE_INTERNAL_ONLY) { + if(!wgtUnzip.checkAvailableSpace(installedPath)) { + ThrowMsg(Exceptions::OutOfStorageFailed, "There is no space for installation"); + } + } +} + +void TaskFileManipulation::StepPrepareRootDirectory() +{ + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + prepareExternalDir(); + } else { + std::string widgetPath = m_context.locations->getPackageInstallationDir(); + std::string widgetBinPath = m_context.locations->getBinaryDir(); + std::string widgetSrcPath = m_context.locations->getSourceDir(); + + if (!m_context.isUpdateMode) { + _D("Remove existing directory : %s", widgetPath.c_str()); + DPL::Utils::TryRemove(DPL::Utils::Path(widgetPath)); + } + WrtUtilMakeDir(widgetPath); + + _D("Create resource directory"); + WrtUtilMakeDir(widgetBinPath); + WrtUtilMakeDir(widgetSrcPath); + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_DIR_CREATE, + "Widget Directory Created"); +} + +void TaskFileManipulation::StepUnzipWgtFile() +{ + if (m_context.widgetConfig.packagingType != PKG_TYPE_HOSTED_WEB_APP) { + std::string instDir; + if (m_context.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) { + instDir = m_context.locations->getPackageInstallationDir(); + } else { + instDir = m_context.locations->getSourceDir(); + } + + _D("unzip file to %s", instDir.c_str()); + + WidgetUnzip wgtUnzip(m_context.requestedPath); + wgtUnzip.unzipWgtFile(instDir); + } else { + _D("From browser installation - unzip is not done"); + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_UNZIP_WGT, + "Unzip Wgt file"); +} + +void TaskFileManipulation::StepAbortPrepareRootDirectory() +{ + _D("[Create Root Directory] Aborting.... (Rename path)"); + if (m_context.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) { + if (m_context.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().postUpgrade(false); + } else { + WidgetInstallToExtSingleton::Instance().postInstallation(false); + } + WidgetInstallToExtSingleton::Instance().deinitialize(); + } else { + std::string widgetPath; + widgetPath = m_context.locations->getPackageInstallationDir(); + if (!WrtUtilRemove(widgetPath)) { + _E("Error occurs during removing existing folder"); + } + // Remove user data directory if preload web app. + std::string userData = m_context.locations->getUserDataRootDir(); + if (0 == access(userData.c_str(), F_OK)) { + if (!WrtUtilRemove(userData)) { + _E("Error occurs during removing user data directory"); + } + } + } +} + +void TaskFileManipulation::prepareExternalDir() +{ + _D("Step prepare to install in exernal directory"); + Try { + std::string pkgid = + DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + WidgetInstallToExtSingleton::Instance().initialize(pkgid); + + std::unique_ptr zipFile(new + DPL::ZipInput(m_context.requestedPath)); + double unzipSize = zipFile->GetTotalUncompressedSize(); + int folderSize = (int)(unzipSize / (1024 * 1024)) + 1; + + GList *list = NULL; + app2ext_dir_details* dirDetail = NULL; + + dirDetail = (app2ext_dir_details*) calloc(1, + sizeof( + app2ext_dir_details)); + if (NULL == dirDetail) { + ThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "error in app2ext"); + } + dirDetail->name = strdup(GLIST_RES_DIR); + dirDetail->type = APP2EXT_DIR_RO; + list = g_list_append(list, dirDetail); + + if (m_context.isUpdateMode) { + WidgetInstallToExtSingleton::Instance().preUpgrade(list, + folderSize); + } else { + WidgetInstallToExtSingleton::Instance().preInstallation(list, + folderSize); + } + free(dirDetail); + g_list_free(list); + + /* make bin directory */ + std::string widgetBinPath = m_context.locations->getBinaryDir(); + WrtUtilMakeDir(widgetBinPath); + std::string sourceDir = m_context.locations->getSourceDir(); + WrtUtilMakeDir(sourceDir); + } + Catch(DPL::ZipInput::Exception::OpenFailed) { + ReThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "Error during \ + create external folder "); + } + Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) + { + ReThrowMsg(Exceptions::ErrorExternalInstallingFailure, + "Error during create external folder "); + } +} + +void TaskFileManipulation::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskFileManipulation::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_file_manipulation.h b/src_wearable/jobs/widget_install/task_file_manipulation.h new file mode 100644 index 0000000..113ca72 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_file_manipulation.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_db_update.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for installer task database updating + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_FILE_MANIPULATION_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_FILE_MANIPULATION_UPDATE_H + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskFileManipulation : + public DPL::TaskDecl +{ + InstallerContext& m_context; + app2ext_handle *m_extHandle; + + // install internal location + void StepCheckInstallLocation(); + void StepPrepareRootDirectory(); + void StepUnzipWgtFile(); + void StepAbortPrepareRootDirectory(); + void StepLinkForPreload(); + void StartStep(); + void EndStep(); + + // install external location + void prepareExternalDir(); + + public: + TaskFileManipulation(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_FILE_MANIPULATION_H diff --git a/src_wearable/jobs/widget_install/task_install_ospsvc.cpp b/src_wearable/jobs/widget_install/task_install_ospsvc.cpp new file mode 100644 index 0000000..4016a6f --- /dev/null +++ b/src_wearable/jobs/widget_install/task_install_ospsvc.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_install_ospsvc.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task install osp service + */ +#include "task_install_ospsvc.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +const int MAX_BUF_SIZE = 128; +const char* OSP_INSTALL_STR1 = "/usr/etc/package-manager/backend/tpk -iv "; +const char* OSP_INSTALL_STR2 = " -p "; +} + +namespace Jobs { +namespace WidgetInstall { +TaskInstallOspsvc::TaskInstallOspsvc(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskInstallOspsvc::StartStep); + AddStep(&TaskInstallOspsvc::StepUninstallSmack); + AddStep(&TaskInstallOspsvc::StepInstallOspService); + AddStep(&TaskInstallOspsvc::EndStep); +} + +void TaskInstallOspsvc::StepUninstallSmack() +{ + std::string pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + if (m_context.isUpdateMode) { + _D("StepUninstallSmack"); + if (PC_OPERATION_SUCCESS != perm_app_uninstall(pkgId.c_str())) { + _E("failure in removing smack rules file"); + ThrowMsg(Exceptions::NotAllowed, "Update failure. " + "failure in delete smack rules file before update."); + } + } +} + +void TaskInstallOspsvc::StepInstallOspService() +{ + _D("Step: installation for osp service"); + + std::ostringstream commStr; + commStr << OSP_INSTALL_STR1<< BashUtils::escape_arg( + m_context.locations->getPackageInstallationDir()) + << OSP_INSTALL_STR2 << m_context.certLevel; + _D("osp install command : %s", commStr.str().c_str()); + + char readBuf[MAX_BUF_SIZE]; + FILE *fd; + fd = popen(commStr.str().c_str(), "r"); + if (NULL == fd) { + _E("Failed to installtion osp service"); + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + + if (fgets(readBuf, MAX_BUF_SIZE, fd) == NULL) + { + _E("Failed to installtion osp service.\ + Inability of reading file."); + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + _D("return value : %s", readBuf); + + int result = atoi(readBuf); + if (0 != result) { + ThrowMsg(Exceptions::InstallOspsvcFailed, + "Error occurs during\ + install osp service"); + } + + pclose(fd); +} + +void TaskInstallOspsvc::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskInstallOspsvc::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_INSTALL_OSPSVC, + "Installed Osp servcie"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_install_ospsvc.h b/src_wearable/jobs/widget_install/task_install_ospsvc.h new file mode 100644 index 0000000..6648d73 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_install_ospsvc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_install_ospsvc.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskInstallOspsvc : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void StepInstallOspService(); + void StepUninstallSmack(); + + void StepAbortInstall(); + + void StartStep(); + void EndStep(); + + // return callback + static int StatusCallback( + int req_id, const char *pkg_type, const char *pkg_name, + const char *key, const char *val, const void *pmsg, + void *priv_data); + + public: + explicit TaskInstallOspsvc(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_INSTALL_OSPSVC_H_ */ diff --git a/src_wearable/jobs/widget_install/task_manifest_file.cpp b/src_wearable/jobs/widget_install/task_manifest_file.cpp new file mode 100755 index 0000000..ac897fa --- /dev/null +++ b/src_wearable/jobs/widget_install/task_manifest_file.cpp @@ -0,0 +1,1614 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_manifest_file.cpp + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +//SYSTEM INCLUDES +#include +#include +#include +#include +#include +#include + +//WRT INCLUDES +#include +#include +#include +#include +#ifdef DBOX_ENABLED +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEFAULT_ICON_NAME "icon.png" +#define DEFAULT_PREVIEW_NAME "preview.png" + +using namespace WrtDB; + +namespace { +typedef std::map LanguageTagMap; + +const char* const STR_TRUE = "true"; +const char* const STR_FALSE = "false"; +const char* const STR_NODISPLAY = "nodisplay"; +const char* const STR_CATEGORY_WATCH_CLOCK = "com.samsung.wmanager.WATCH_CLOCK"; +const char* const STR_CATEGORY_WATCH_APP = "com.samsung.wmanager.WATCH_APP"; + +#ifdef IME_ENABLED +const char* const STR_CATEGORY_IME = "http://tizen.org/category/ime"; +#endif + +#ifdef SERVICE_ENABLED +const char* const STR_CATEGORY_SERVICE = "http://tizen.org/category/service"; +#endif + +LanguageTagMap getLanguageTagMap() +{ + LanguageTagMap map; + +#define ADD(tag, l_tag) map.insert(std::make_pair(L###tag, L###l_tag)); +#include "languages.def" +#undef ADD + + return map; +} + +DPL::OptionalString getLangTag(const DPL::String& tag) +{ + static LanguageTagMap TagsMap = + getLanguageTagMap(); + + DPL::String langTag = tag; + + _D("Trying to map language tag: %ls", langTag.c_str()); + size_t pos = langTag.find_first_of(L'_'); + if (pos != DPL::String::npos) { + langTag.erase(pos); + } + DPL::OptionalString ret; + + LanguageTagMap::iterator it = TagsMap.find(langTag); + if (it != TagsMap.end()) { + ret = it->second; + _D("Mapping IANA Language tag to language tag: %ls -> %ls", langTag.c_str(), (*ret).c_str()); + } + + return ret; +} +} + +namespace Jobs { +namespace WidgetInstall { +const char * TaskManifestFile::encoding = "UTF-8"; + +TaskManifestFile::TaskManifestFile(InstallerContext &inCont) : + DPL::TaskDecl(this), + m_context(inCont), + writer(NULL) +{ + if (InstallMode::Command::RECOVERY == m_context.mode.command) { + AddStep(&TaskManifestFile::stepGenerateManifest); + } else { + AddStep(&TaskManifestFile::stepCopyIconFiles); +#ifdef DBOX_ENABLED + AddStep(&TaskManifestFile::stepCopyLiveboxFiles); +#endif +#ifdef SERVICE_ENABLED + AddStep(&TaskManifestFile::stepCopyServiceIconFiles); +#endif + AddStep(&TaskManifestFile::stepCopyAccountIconFiles); + AddStep(&TaskManifestFile::stepCreateExecFile); + AddStep(&TaskManifestFile::stepCreateLinkNPPluginsFile); + AddStep(&TaskManifestFile::stepGenerateManifest); + } +} + +TaskManifestFile::~TaskManifestFile() +{} + +void TaskManifestFile::stepCreateExecFile() +{ + std::string exec = m_context.locations->getExecFile(); + std::string clientExeStr = GlobalConfig::GetWrtClientExec(); + +#ifdef MULTIPROCESS_SERVICE_SUPPORT + //default widget + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << 0; + std::string controlExec = exec; + controlExec.append(postfix.str()); + + errno = 0; + if (symlink(clientExeStr.c_str(), controlExec.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + + // app-control widgets + unsigned int indexMax = 0; + FOREACH(it, m_context.widgetConfig.configInfo.appControlList) { + if (it->m_index > indexMax) { + indexMax = it->m_index; + } + } + + for (std::size_t i = 1; i <= indexMax; ++i) { + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << i; + std::string controlExec = exec; + controlExec.append(postfix.str()); + errno = 0; + if (symlink(clientExeStr.c_str(), controlExec.c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } +#else + //default widget + _D("link -s %s %s", clientExeStr.c_str(), exec.c_str()); + errno = 0; + if (symlink(clientExeStr.c_str(), exec.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } +#ifdef SERVICE_ENABLED + std::string serviceExeStr = GlobalConfig::GetWrtServiceExec(); + + FOREACH(it, m_context.widgetConfig.configInfo.serviceAppInfoList) { + std::string serviceExec = m_context.locations->getBinaryDir() + "/" + DPL::ToUTF8String(it->serviceId); + errno = 0; + _D("link -s %s %s", serviceExeStr.c_str(), serviceExec.c_str()); + if (symlink(serviceExeStr.c_str(), serviceExec.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + } +#endif +#endif + // creation of box symlink + ConfigParserData::LiveboxList& liveboxList = + m_context.widgetConfig.configInfo.m_livebox; + if (!liveboxList.empty()) { + std::string boxExec = "/usr/bin/WebProcess"; + std::string boxSymlink = m_context.locations->getExecFile(); + boxSymlink += ".d-box"; + + errno = 0; + if (symlink(boxExec.c_str(), boxSymlink.c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to make a symbolic name for a file [%s]", DPL::GetErrnoString(error).c_str()); + } + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_EXECFILE, + "Widget execfile creation Finished"); +} + +void TaskManifestFile::stepCreateLinkNPPluginsFile() +{ + _D("stepCreateLinkNPPluginsFile"); + if (0 == access(m_context.locations->getNPPluginsDir().c_str(), F_OK)) { + _D("This webapp has NPPlugins"); + std::string pluginsExec = "/usr/bin/PluginProcess"; + errno = 0; + if (symlink(pluginsExec.c_str(), + m_context.locations->getNPPluginsExecFile().c_str()) != 0) { + int error = errno; + if (error) { + _E("Failed to create symbolic link for npplugins : %ls", + DPL::GetErrnoString(error).c_str()); + } + } + } +} + +void TaskManifestFile::stepCopyIconFiles() +{ + _D("CopyIconFiles"); + + //This function copies icon to desktop icon path. For each locale avaliable + //which there is at least one icon in widget for, icon file is copied. + //Coping prioritize last positions when coping. If there is several icons + //with given locale, the one, that will be copied, will be icon + //which is declared by tag later than the others in config.xml of + // widget + + std::vector generatedLocales; + + WrtDB::WidgetRegisterInfo::LocalizedIconList & icons = + m_context.widgetConfig.localizationData.icons; + + for (WrtDB::WidgetRegisterInfo::LocalizedIconList::const_iterator + icon = icons.begin(); + icon != icons.end(); + ++icon) + { + DPL::String src = icon->src; + FOREACH(locale, icon->availableLocales) + { + DPL::String tmp = (icon->isSmall ? L"small_" : L"") + (*locale); + _D("Icon for locale: %ls is: %ls", tmp.c_str(), src.c_str()); + + if (std::find(generatedLocales.begin(), generatedLocales.end(), + tmp) != generatedLocales.end()) + { + _D("Skipping - has that locale"); + continue; + } else { + generatedLocales.push_back(tmp); + } + + DPL::Utils::Path sourceFile(m_context.locations->getSourceDir()); + if (!locale->empty()) { + sourceFile /= "locales"; + sourceFile /= *locale; + } + sourceFile /= src; + + DPL::Utils::Path + targetFile(m_context.locations->getSharedResourceDir()); + targetFile /= (icon->isSmall ? L"small_" : L"") + + getIconTargetFilename(*locale, sourceFile.Extension()); + + if (m_context.widgetConfig.packagingType == + WrtDB::PKG_TYPE_HOSTED_WEB_APP) + { + m_context.locations->setIconTargetFilenameForLocale( + targetFile.Fullpath()); + } + + _D("Copying icon: %s -> %s", sourceFile.Filename().c_str(), targetFile.Filename().c_str()); + + icon_list.push_back(targetFile.Fullpath()); + + Try + { + DPL::FileInput input(sourceFile.Fullpath()); + DPL::FileOutput output(targetFile.Fullpath()); + DPL::Copy(&input, &output); + } + + Catch(DPL::FileInput::Exception::Base) + { + // Error while opening or closing source file + //ReThrowMsg(InstallerException::CopyIconFailed, + // sourceFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + + Catch(DPL::FileOutput::Exception::Base) + { + // Error while opening or closing target file + //ReThrowMsg(InstallerException::CopyIconFailed, + // targetFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + + Catch(DPL::CopyFailed) + { + // Error while copying + //ReThrowMsg(InstallerException::CopyIconFailed, + // targetFile.str()); + _E("Copying widget's icon failed. Widget's icon will not be" \ + "available from Main Screen"); + } + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_COPY_ICONFILE, + "Widget iconfile copy Finished"); +} + +#ifdef SERVICE_ENABLED +void TaskManifestFile::stepCopyServiceIconFiles() +{ + _D("Copy Service icon files"); + + WrtDB::ConfigParserData::ServiceAppInfoList service = m_context.widgetConfig.configInfo.serviceAppInfoList; + + if (service.size() <= 0) { + return; + } + + FOREACH(it, service) + { + if (it->m_iconsList.empty()) { + _D("Widget doesn't contain Service icon"); + return; + } + + ConfigParserData::IconsList iconsList = it->m_iconsList; + FOREACH(iconIt, iconsList) { + std::string sourceFile = m_context.locations->getSourceDir() + + '/' + + DPL::ToUTF8String(iconIt->src); + std::string targetFile = m_context.locations->getSharedResourceDir() + + '/' + + DPL::ToUTF8String(it->serviceId) + + ".png"; + copyFile(sourceFile, targetFile); + } + } +} +#endif + +#ifdef DBOX_ENABLED +void TaskManifestFile::stepCopyLiveboxFiles() +{ + _D("Copy Livebox Files"); + + using namespace WrtDB; + ConfigParserData &data = m_context.widgetConfig.configInfo; + ConfigParserData::LiveboxList liveBoxList = data.m_livebox; + + if (liveBoxList.size() <= 0) { + return; + } + + std::ostringstream sourceFile; + std::ostringstream targetFile; + + FOREACH (boxIt, liveBoxList) { + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + (**boxIt).m_boxInfo.m_boxSize; + FOREACH (sizeIt, boxSizeList) { + std::string preview = DPL::ToUTF8String((*sizeIt).m_preview); + if (preview.empty()) { + continue; + } + sourceFile << m_context.locations->getSourceDir() << "/"; + sourceFile << preview; + targetFile << m_context.locations->getSharedDataDir() << "/"; + targetFile << (**boxIt).m_liveboxId << "."; + targetFile << DPL::ToUTF8String((*sizeIt).m_size) << "." << DEFAULT_PREVIEW_NAME; + + copyFile(sourceFile.str(), targetFile.str()); + + // clear stream objects + sourceFile.str(""); + targetFile.str(""); + } + // check this livebox has icon element + std::string icon = DPL::ToUTF8String((**boxIt).m_icon); + if (icon.empty()) { + continue; + } + sourceFile << m_context.locations->getSourceDir() << "/"; + sourceFile << icon; + targetFile << m_context.locations->getSharedDataDir() << "/"; + targetFile << (**boxIt).m_liveboxId << "." << DEFAULT_ICON_NAME; + + copyFile(sourceFile.str(), targetFile.str()); + + // clear stream objects + sourceFile.str(""); + targetFile.str(""); + } + m_context.job->UpdateProgress( + InstallerContext::INSTALL_COPY_LIVEBOX_FILES, + "Livebox files copy Finished"); +} +#endif + +void TaskManifestFile::stepCopyAccountIconFiles() +{ + _D("Copy Account icon files"); + WrtDB::ConfigParserData::AccountProvider account = + m_context.widgetConfig.configInfo.accountProvider; + + if (account.m_iconSet.empty()) { + _D("Widget doesn't contain Account"); + return; + } + + FOREACH(it, account.m_iconSet) { + std::string sourceFile = m_context.locations->getSourceDir() + + '/' + + DPL::ToUTF8String(it->second); + std::string targetFile = m_context.locations->getSharedResourceDir() + + '/' + + DPL::ToUTF8String(it->second); + copyFile(sourceFile, targetFile); + } +} + +void TaskManifestFile::copyFile(const std::string& sourceFile, + const std::string& targetFile) +{ + Try + { + DPL::FileInput input(sourceFile); + DPL::FileOutput output(targetFile); + DPL::Copy(&input, &output); + } + Catch(DPL::Exception) + { + _E("Failed to file copy. %s to %s", sourceFile.c_str(), targetFile.c_str()); + ReThrowMsg(Exceptions::CopyIconFailed, "Error during file copy."); + } +} + +#ifdef DBOX_ENABLED +bool TaskManifestFile::addBoxUiApplication(Manifest& manifest) +{ + UiApplication uiApp; + std::string postfix = ".d-box"; + static bool isAdded = false; + + Try + { + if (isAdded) { + _D("UiApplication for d-box is already added"); + return false; + } + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + uiApp.setMultiple(false); + setWidgetName(manifest, uiApp); + setWidgetIcons(uiApp); + + // appid for box is like [webapp id].d-box + setWidgetIds(manifest, uiApp, postfix); + // executable path for box is like [app path]/bin/[webapp id].d-box + setWidgetExecPath(uiApp, postfix); + manifest.addUiApplication(uiApp); + isAdded = true; + + return true; + } + Catch(DPL::Exception) + { + _E("Adding UiApplication on xml is failed."); + isAdded = false; + return false; + } +} +#endif + +DPL::String TaskManifestFile::getIconTargetFilename( + const DPL::String& languageTag, const std::string & ext) const +{ + DPL::OStringStream filename; + TizenAppId appid = m_context.widgetConfig.tzAppid; + + filename << DPL::ToUTF8String(appid).c_str(); + + if (!languageTag.empty()) { + DPL::OptionalString tag = getLangTag(languageTag); // translate en -> + // en_US etc + if (!tag) { + tag = languageTag; + } + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + + if (locale.empty()) { + filename << L"." << languageTag; + } else { + filename << L"." << locale; + } + } + + if(!ext.empty()) + { + filename << L"." + DPL::FromUTF8String(ext); + } + return filename.str(); +} + +void TaskManifestFile::saveLocalizedKey(std::ofstream &file, + const DPL::String& key, + const DPL::String& languageTag) +{ + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(languageTag); + + file << key; + if (!locale.empty()) { + file << "[" << locale << "]"; + } + file << "="; +} + +void TaskManifestFile::stepGenerateManifest() +{ + TizenPkgId pkgid = m_context.widgetConfig.tzPkgid; + manifest_name = pkgid + L".xml"; + + // In FOTA environment, Use temporary directory created by pkgmgr. + // Becuase /tmp can be read-only filesystem. + if (m_context.mode.installTime == InstallMode::InstallTime::FOTA) { + manifest_file += L"/opt/share/packages/.recovery/wgt/" + manifest_name; + } else { + manifest_file += L"/tmp/" + manifest_name; + } + + //libxml - init and check + LibxmlSingleton::Instance().init(); + + writeManifest(manifest_file); + + std::ostringstream destFile; + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + destFile << WrtDB::GlobalConfig::GetPreloadManifestPath() << "/"; + } else { + destFile << WrtDB::GlobalConfig::GetManifestPath() << "/"; + } + + destFile << DPL::ToUTF8String(manifest_name); + commit_manifest = destFile.str(); + _D("Commiting manifest file : %s", commit_manifest.c_str()); + + commitManifest(); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_MANIFEST, + "Widget Manifest Creation Finished"); +} + +void TaskManifestFile::commitManifest() +{ + + if (!(m_context.mode.rootPath == InstallMode::RootPath::RO && + (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_context.mode.installTime == InstallMode::InstallTime::FOTA) + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + _D("cp %ls %s", manifest_file.c_str(), commit_manifest.c_str()); + + DPL::FileInput input(DPL::ToUTF8String(manifest_file)); + DPL::FileOutput output(commit_manifest); + DPL::Copy(&input, &output); + _D("Manifest writen to: %s", commit_manifest.c_str()); + + //removing temp file + unlink((DPL::ToUTF8String(manifest_file)).c_str()); + manifest_file = DPL::FromUTF8String(commit_manifest); + } +} + +void TaskManifestFile::writeManifest(const DPL::String & path) +{ + _D("Generating manifest file : %ls", path.c_str()); + Manifest manifest; + UiApplication uiApp; + +#ifdef MULTIPROCESS_SERVICE_SUPPORT + //default widget content + std::stringstream postfix; + // index 0 is reserved + postfix << AppControlPrefix::PROCESS_PREFIX << 0; + setWidgetExecPath(uiApp, postfix.str()); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setWidgetDescription(manifest); + setWidgetManifest(manifest); + setWidgetOtherInfo(uiApp); + setAppCategory(uiApp); + setMetadata(uiApp); + // move to the last of this procedure + //setLiveBoxInfo(manifest); + setAccount(manifest); + setPrivilege(manifest); + manifest.addUiApplication(uiApp); + + //app-control content + ConfigParserData::AppControlInfoList appControlList = + m_context.widgetConfig.configInfo.appControlList; + FOREACH(it, appControlList) { + UiApplication uiApp; + + uiApp.setTaskmanage(true); + uiApp.setNodisplay(true); +#ifdef MULTIPROCESS_SERVICE_SUPPORT_INLINE + uiApp.setTaskmanage(ConfigParserData::AppControlInfo::Disposition::INLINE != it->m_disposition); + uiApp.setMultiple(ConfigParserData::AppControlInfo::Disposition::INLINE == it->m_disposition); +#endif + std::stringstream postfix; + postfix << AppControlPrefix::PROCESS_PREFIX << it->m_index; + setWidgetExecPath(uiApp, postfix.str()); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setAppControlInfo(uiApp, *it); + setAppCategory(uiApp); + setMetadata(uiApp); + manifest.addUiApplication(uiApp); + } + // TODO: Must fix again with right method + // The mainapp attiribute must be set + // when there are multiple uiapps in mainfest +#ifdef SERVICE_ENABLED + WrtDB::ConfigParserData::ServiceAppInfoList service = m_context.widgetConfig.configInfo.serviceAppInfoList; + + if (service.size() > 0) { + ConfigParserData &data = m_context.widgetConfig.configInfo; + + FOREACH(it, service) { + ServiceApplication serviceApp; + setServiceInfo(serviceApp, *it); + manifest.addServiceApplication(serviceApp); + } + } else { + _D("Widget doesn't contain service"); + } +#endif + +#ifdef IME_ENABLED + ImeApplication imeApp; + WrtDB::ConfigParserData::ImeAppInfoList ime = m_context.widgetConfig.configInfo.imeAppInfoList; + + if (ime.size() > 0) { + extractImeInfo(imeApp); + manifest.addImeApplication(imeApp); + } else { + _D("Widget doesn't contain ime"); + } +#endif + +#ifdef DBOX_ENABLED + setLiveBoxInfo(manifest); +#endif +#else + //default widget content + setWidgetExecPath(uiApp); + setWidgetName(manifest, uiApp); + setWidgetIds(manifest, uiApp); + setWidgetIcons(uiApp); + setWidgetDescription(manifest); + setWidgetManifest(manifest); + setWidgetOtherInfo(uiApp); + setAppControlsInfo(uiApp); + setAppCategory(uiApp); + setMetadata(uiApp); + // move to the last of this procedure + //setLiveBoxInfo(manifest); + setAccount(manifest); + setPrivilege(manifest); + + manifest.addUiApplication(uiApp); + // TODO: Must fix again with right method + // The mainapp attiribute must be set + // when there are multiple uiapps in mainfest + +#ifdef SERVICE_ENABLED + WrtDB::ConfigParserData::ServiceAppInfoList service = m_context.widgetConfig.configInfo.serviceAppInfoList; + + if (service.size() > 0) { + ConfigParserData &data = m_context.widgetConfig.configInfo; + + FOREACH(it, service) { + ServiceApplication serviceApp; + setServiceInfo(serviceApp, *it); + manifest.addServiceApplication(serviceApp); + } + } else { + _D("Widget doesn't contain service"); + } +#endif + +#ifdef IME_ENABLED + ImeApplication imeApp; + WrtDB::ConfigParserData::ImeAppInfoList ime = m_context.widgetConfig.configInfo.imeAppInfoList; + + if (ime.size() > 0) { + extractImeInfo(imeApp); + manifest.addImeApplication(imeApp); + } else { + _D("Widget doesn't contain ime"); + } +#endif + +#ifdef DBOX_ENABLED + setLiveBoxInfo(manifest); +#endif +#endif + + manifest.generate(path); + _D("Manifest file serialized"); +} + +#ifdef SERVICE_ENABLED +void TaskManifestFile::setServiceInfo(ServiceApplication &serviceApp, WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + setWidgetExecPathService(serviceApp, service); + setWidgetIdsService(serviceApp, service); + setWidgetNameService(serviceApp, service); + setWidgetIconService(serviceApp, service); + setAppControlsInfoService(serviceApp); + setWidgetOtherInfoService(serviceApp); + setWidgetComponentService(serviceApp); + setWidgetAutoRestartService(serviceApp, service); + setWidgetOnBootService(serviceApp, service); +} + +void TaskManifestFile::setWidgetComponentService(ServiceApplication &serviceApp) +{ + serviceApp.setComponent(DPL::FromASCIIString("svcapp")); +} + +void TaskManifestFile::setWidgetAutoRestartService(ServiceApplication &serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + serviceApp.setAutoRestart(service.autoRestart); +} + +void TaskManifestFile::setWidgetOnBootService(ServiceApplication &serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + serviceApp.setOnBoot(service.onBoot); +} + +void TaskManifestFile::setWidgetExecPathService(ServiceApplication &serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + if (service.serviceId.empty()) { + _D("Widget doesn't contain service id"); + return; + } + + std::string serviceExec = m_context.locations->getBinaryDir() + "/" + DPL::ToUTF8String(service.serviceId); + serviceApp.setExec(DPL::FromUTF8String(serviceExec)); +} + +void TaskManifestFile::setWidgetIdsService(ServiceApplication &serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + //appid + if (service.serviceId.empty()) { + _D("Widget doesn't contain service id"); + return; + } + serviceApp.setAppid(service.serviceId); + + //extraid + TizenAppId appid = m_context.widgetConfig.tzAppid; + if (!!m_context.widgetConfig.guid) { + serviceApp.setExtraid(*m_context.widgetConfig.guid); + } else { + if (!appid.empty()) { + serviceApp.setExtraid(DPL::String(L"http://") + appid); + } + } + + //type + serviceApp.setType(DPL::FromASCIIString("capp")); +} + +void TaskManifestFile::setWidgetNameService(ServiceApplication &serviceApp, WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + if (service.m_localizedDataSet.empty()) { + _D("Widget doesn't contain service name"); + return; + } + + ConfigParserData::LocalizedDataSet &localizedDataSet = service.m_localizedDataSet; + FOREACH(localizedData, localizedDataSet) { + Locale i = localizedData->first; + DPL::OptionalString localeTag = getLangTag(i); + if (localeTag.IsNull()) { + localeTag = i; + } + DPL::OptionalString name = localizedData->second.name; + + if (!!name) { + if (!!localeTag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*localeTag); + + if (!locale.empty()) { + serviceApp.addLabel(LabelType(*name, *localeTag)); + } else { + serviceApp.addLabel(LabelType(*name)); + } + } else { + serviceApp.addLabel(LabelType(*name)); + } + } + } +} + +void TaskManifestFile::setWidgetIconService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service) +{ + if (service.m_iconsList.empty()) { + _D("Widget doesn't contain service icon"); + return; + } + + DPL::String icon = + DPL::FromUTF8String(m_context.locations->getSharedResourceDir()) + + DPL::String(L"/") + + DPL::String(service.serviceId) + DPL::String(L".png"); + serviceApp.addIcon(icon); +} + +void TaskManifestFile::setAppControlsInfoService(ServiceApplication & serviceApp) +{ + WrtDB::ConfigParserData::AppControlInfoList appControlList = + m_context.widgetConfig.configInfo.appControlList; + + if (appControlList.empty()) { + _D("Widget doesn't contain app control"); + return; + } + + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + FOREACH(it, appControlList) { + setAppControlInfoService(serviceApp, *it); + } +} + +void TaskManifestFile::setAppControlInfoService(ServiceApplication & serviceApp, + const WrtDB::ConfigParserData::AppControlInfo & service) +{ + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + AppControl appControl; + if (!service.m_operation.empty()) { + appControl.addOperation(service.m_operation); //TODO: encapsulation? + } + if (!service.m_uriList.empty()) { + FOREACH(uri, service.m_uriList) { + appControl.addUri(*uri); + } + } + if (!service.m_mimeList.empty()) { + FOREACH(mime, service.m_mimeList) { + appControl.addMime(*mime); + } + } + serviceApp.addAppControl(appControl); +} + +void TaskManifestFile::setWidgetOtherInfoService(ServiceApplication &serviceApp) +{ + serviceApp.setNodisplay(true); + serviceApp.setTaskmanage(false); + serviceApp.setMultiple(false); + + FOREACH(it, m_context.widgetConfig.configInfo.settingsList) + { + if (!strcmp(DPL::ToUTF8String(it->m_name).c_str(), STR_NODISPLAY)) { + if (!strcmp(DPL::ToUTF8String(it->m_value).c_str(), STR_TRUE)) { + serviceApp.setNodisplay(true); + serviceApp.setTaskmanage(false); + } else { + serviceApp.setNodisplay(false); + serviceApp.setTaskmanage(true); + } + } + } +} +#endif + +#ifdef IME_ENABLED +void TaskManifestFile::extractImeInfo(ImeApplication &imeApp) +{ + setWidgetNameIME(imeApp); + setWidgetIdsIME(imeApp); + setWidgetUuidIME(imeApp); + setWidgetLanguageIME(imeApp); + setWidgetTypeIME(imeApp); + setWidgetOptionIME(imeApp); +} + +void TaskManifestFile::setWidgetUuidIME(ImeApplication &imeApp) +{ + WrtDB::ConfigParserData::ImeAppInfoList ime = m_context.widgetConfig.configInfo.imeAppInfoList; + + FOREACH(it, ime) + { + imeApp.addUuid(it->uuid); + } +} + +void TaskManifestFile::setWidgetLanguageIME(ImeApplication &imeApp) +{ + WrtDB::ConfigParserData::ImeAppInfoList ime = m_context.widgetConfig.configInfo.imeAppInfoList; + + FOREACH(it, ime) + { + FOREACH(lang, it->languageList) { + imeApp.addLanguage(*lang); + } + } +} + +void TaskManifestFile::setWidgetTypeIME(ImeApplication &imeApp) +{ + imeApp.addIseType(DPL::FromASCIIString("SOFTWARE_KEYBOARD_ISE")); +} + +void TaskManifestFile::setWidgetOptionIME(ImeApplication &imeApp) +{ + imeApp.addOption(DPL::FromASCIIString("STAND_ALONE")); + imeApp.addOption(DPL::FromASCIIString("NEED_SCREEN_INFO")); + imeApp.addOption(DPL::FromASCIIString("AUTO_RESTART")); +} + +void TaskManifestFile::setWidgetIdsIME(ImeApplication & imeApp, const std::string &postfix) +{ + //appid + TizenAppId appid = m_context.widgetConfig.tzAppid; + if (!postfix.empty()) { + appid = DPL::FromUTF8String(DPL::ToUTF8String(appid).append(postfix)); + } + imeApp.setAppid(appid); +} + +void TaskManifestFile::setWidgetNameIME(ImeApplication &imeApp) +{ + bool defaultNameSaved = false; + + DPL::OptionalString defaultLocale = + m_context.widgetConfig.configInfo.defaultlocale; + std::pair defaultLocalizedData; + //labels + FOREACH(localizedData, m_context.widgetConfig.configInfo.localizedDataSet) + { + Locale i = localizedData->first; + DPL::OptionalString tag = getLangTag(i); // translate en -> en_US etc + if (!tag) { + tag = i; + } + DPL::OptionalString name = localizedData->second.name; + generateWidgetNameIME(imeApp, tag, name, defaultNameSaved); + + //store default locale localized data + if (!!defaultLocale && defaultLocale == i) { + defaultLocalizedData = *localizedData; + } + } + + if (!!defaultLocale && !defaultNameSaved) { + DPL::OptionalString name = defaultLocalizedData.second.name; + generateWidgetNameIME(imeApp, + DPL::OptionalString(), + name, + defaultNameSaved); + } +} + +void TaskManifestFile::generateWidgetNameIME(ImeApplication &imeApp, + const DPL::OptionalString& tag, + DPL::OptionalString name, + bool & defaultNameSaved) +{ + if (!!name) { + if (!!tag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + + if (!locale.empty()) { + imeApp.addLabel(LabelType(*name, *tag)); + } else { + imeApp.addLabel(LabelType(*name)); + } + } else { + defaultNameSaved = true; + imeApp.addLabel(LabelType(*name)); + } + } +} +#endif + +void TaskManifestFile::setWidgetExecPath(UiApplication & uiApp, + const std::string &postfix) +{ + std::string exec = m_context.locations->getExecFile(); + if (!postfix.empty()) { + exec.append(postfix); + } + _D("exec = %s", exec.c_str()); + uiApp.setExec(DPL::FromASCIIString(exec)); +} + +void TaskManifestFile::setWidgetName(Manifest & manifest, + UiApplication & uiApp) +{ + bool defaultNameSaved = false; + + DPL::OptionalString defaultLocale = + m_context.widgetConfig.configInfo.defaultlocale; + std::pair defaultLocalizedData; + //labels + FOREACH(localizedData, m_context.widgetConfig.configInfo.localizedDataSet) + { + Locale i = localizedData->first; + DPL::OptionalString tag = getLangTag(i); // translate en -> en_US etc + if (!tag) { + tag = i; + } + DPL::OptionalString name = localizedData->second.name; + generateWidgetName(manifest, uiApp, tag, name, defaultNameSaved); + + //store default locale localized data + if (!!defaultLocale && defaultLocale == i) { + defaultLocalizedData = *localizedData; + } + } + + if (!!defaultLocale && !defaultNameSaved) { + DPL::OptionalString name = defaultLocalizedData.second.name; + generateWidgetName(manifest, + uiApp, + DPL::OptionalString(), + name, + defaultNameSaved); + } +} + +void TaskManifestFile::setWidgetIds(Manifest & manifest, + UiApplication & uiApp, + const std::string &postfix) +{ + //appid + TizenAppId appid = m_context.widgetConfig.tzAppid; + if (!postfix.empty()) { + appid = DPL::FromUTF8String(DPL::ToUTF8String(appid).append(postfix)); + } + uiApp.setAppid(appid); + + //extraid + if (!!m_context.widgetConfig.guid) { + uiApp.setExtraid(*m_context.widgetConfig.guid); + } else { + if (!appid.empty()) { + uiApp.setExtraid(DPL::String(L"http://") + appid); + } + } + + //type + uiApp.setType(DPL::FromASCIIString("webapp")); + manifest.setType(L"wgt"); +} + +void TaskManifestFile::generateWidgetName(Manifest & manifest, + UiApplication &uiApp, + const DPL::OptionalString& tag, + DPL::OptionalString name, + bool & defaultNameSaved) +{ + if (!!name) { + if (!!tag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + + if (!locale.empty()) { + uiApp.addLabel(LabelType(*name, *tag)); + } else { + uiApp.addLabel(LabelType(*name)); + manifest.addLabel(LabelType(*name)); + } + } else { + defaultNameSaved = true; + uiApp.addLabel(LabelType(*name)); + manifest.addLabel(LabelType(*name)); + } + } +} + +void TaskManifestFile::setWidgetIcons(UiApplication & uiApp) +{ + //TODO this file will need to be updated when user locale preferences + //changes. + bool defaultIconSaved = false; + + DPL::OptionalString defaultLocale = + m_context.widgetConfig.configInfo.defaultlocale; + + std::vector generatedLocales; + WrtDB::WidgetRegisterInfo::LocalizedIconList & icons = + m_context.widgetConfig.localizationData.icons; + + for (WrtDB::WidgetRegisterInfo::LocalizedIconList::const_iterator + icon = icons.begin(); + icon != icons.end(); + ++icon) + { + FOREACH(locale, icon->availableLocales) + { + DPL::String tmp = (icon->isSmall ? L"small_" : L"") + (*locale); + if (std::find(generatedLocales.begin(), generatedLocales.end(), + tmp) != generatedLocales.end()) + { + _D("Skipping - has that locale - already in manifest"); + continue; + } else { + generatedLocales.push_back(tmp); + } + DPL::OptionalString tag = getLangTag(*locale); // translate en -> + // en_US etc + if (!tag) { + tag = *locale; + } + + generateWidgetIcon(uiApp, tag, *locale, DPL::Utils::Path(icon->src).Extension(), icon->isSmall, defaultIconSaved); + } + } + if (!!defaultLocale && !defaultIconSaved) { + generateWidgetIcon(uiApp, DPL::OptionalString(), + DPL::String(), + std::string(), + false, + defaultIconSaved); + } +} + +void TaskManifestFile::generateWidgetIcon(UiApplication & uiApp, + const DPL::OptionalString& tag, + const DPL::String& language, + const std::string & extension, + bool isSmall, + bool & defaultIconSaved) +{ + DPL::String locale; + if (!!tag) { + locale = LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + } else { + defaultIconSaved = true; + } + + DPL::Utils::Path + iconText(m_context.locations->getSharedResourceDir()); + iconText /= (isSmall ? L"small_" : L"") + getIconTargetFilename(language, extension); + + if (!locale.empty()) { + uiApp.addIcon(IconType(DPL::FromUTF8String(iconText.Fullpath()), locale, isSmall)); + } else { + uiApp.addIcon(IconType(DPL::FromUTF8String(iconText.Fullpath()), isSmall)); + } + + _D("Icon file : %s", iconText.Fullpath().c_str()); + m_context.job->SendProgressIconPath(iconText.Fullpath()); +} + +void TaskManifestFile::setWidgetDescription(Manifest & manifest) +{ + FOREACH(localizedData, m_context.widgetConfig.configInfo.localizedDataSet) + { + Locale i = localizedData->first; + DPL::OptionalString tag = getLangTag(i); // translate en -> en_US etc + if (!tag) { + tag = i; + } + DPL::OptionalString description = localizedData->second.description; + generateWidgetDescription(manifest, tag, description); + } +} + +void TaskManifestFile::generateWidgetDescription(Manifest & manifest, + const DPL::OptionalString& tag, + DPL::OptionalString description) +{ + if (!!description) { + if (!!tag) { + DPL::String locale = + LanguageTagsProvider::BCP47LanguageTagToLocale(*tag); + if (!locale.empty()) { + manifest.addDescription(DescriptionType(*description, locale)); + } else { + manifest.addDescription(DescriptionType(*description)); + } + } else { + manifest.addDescription(DescriptionType(*description)); + } + } +} + +void TaskManifestFile::setWidgetManifest(Manifest & manifest) +{ + manifest.setPackage(m_context.widgetConfig.tzPkgid); + + if (!!m_context.widgetConfig.version) { + manifest.setVersion(*m_context.widgetConfig.version); + } + DPL::String email = (!!m_context.widgetConfig.configInfo.authorEmail ? + *m_context.widgetConfig.configInfo.authorEmail : L""); + DPL::String href = (!!m_context.widgetConfig.configInfo.authorHref ? + *m_context.widgetConfig.configInfo.authorHref : L""); + DPL::String name = (!!m_context.widgetConfig.configInfo.authorName ? + *m_context.widgetConfig.configInfo.authorName : L""); + manifest.addAuthor(Author(email, href, L"", name)); + + if (!m_context.callerPkgId.empty()) { + manifest.setStoreClientId(m_context.callerPkgId); + } + + // set csc path + if (!m_context.mode.cscPath.empty()) { + manifest.setCscPath(DPL::FromUTF8String(m_context.mode.cscPath)); + } +} + +void TaskManifestFile::setWidgetOtherInfo(UiApplication & uiApp) +{ + FOREACH(it, m_context.widgetConfig.configInfo.settingsList) + { + if (!strcmp(DPL::ToUTF8String(it->m_name).c_str(), STR_NODISPLAY)) { + if (!strcmp(DPL::ToUTF8String(it->m_value).c_str(), STR_TRUE)) { + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + } else { + uiApp.setNodisplay(false); + uiApp.setTaskmanage(true); + } + } + } + //TODO + //There is no "X-TIZEN-PackageType=wgt" + //There is no X-TIZEN-PackageID in manifest "X-TIZEN-PackageID=" << + // DPL::ToUTF8String(*widgetID).c_str() + //There is no Comment in pkgmgr "Comment=Widget application" + //that were in desktop file +} + +void TaskManifestFile::setAppControlsInfo(UiApplication & uiApp) +{ + WrtDB::ConfigParserData::AppControlInfoList appControlList = + m_context.widgetConfig.configInfo.appControlList; + + if (appControlList.empty()) { + _D("Widget doesn't contain app control"); + return; + } + + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + FOREACH(it, appControlList) { + setAppControlInfo(uiApp, *it); + } +} + +void TaskManifestFile::setAppControlInfo(UiApplication & uiApp, + const WrtDB::ConfigParserData::AppControlInfo & service) +{ + // x-tizen-svc=http://tizen.org/appcontrol/operation/pick|NULL|image; + AppControl appControl; + if (!service.m_operation.empty()) { + appControl.addOperation(service.m_operation); //TODO: encapsulation? + } + if (!service.m_uriList.empty()) { + FOREACH(uri, service.m_uriList) { + appControl.addUri(*uri); + } + } + if (!service.m_mimeList.empty()) { + FOREACH(mime, service.m_mimeList) { + appControl.addMime(*mime); + } + } + uiApp.addAppControl(appControl); +} + +void TaskManifestFile::setAppCategory(UiApplication &uiApp) +{ + WrtDB::ConfigParserData::CategoryList categoryList = + m_context.widgetConfig.configInfo.categoryList; + + bool hasPredefinedCategory = false; + FOREACH(it, categoryList) { + if (!(*it).empty()) { + uiApp.addAppCategory(*it); + if (DPL::ToUTF8String(*it) == STR_CATEGORY_WATCH_CLOCK) { + // in case of idle clock, + // nodisplay should be set to true + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + hasPredefinedCategory = true; + } +#ifdef IME_ENABLED + else if (DPL::ToUTF8String(*it) == STR_CATEGORY_IME) { + uiApp.setNodisplay(true); + uiApp.setTaskmanage(false); + hasPredefinedCategory = true; + } +#endif +#ifdef SERVICE_ENABLED + else if (DPL::ToUTF8String(*it) == STR_CATEGORY_SERVICE) { + //uiApp.setNodisplay(true); + //uiApp.setTaskmanage(false); + hasPredefinedCategory = true; + } +#endif + else if (DPL::ToUTF8String(*it) == STR_CATEGORY_WATCH_APP) { + hasPredefinedCategory = true; + } + } + } + + // Tizen W feature + // Add default app category (watch_app) except for watch_clock + if(!hasPredefinedCategory) { + // If the nodisplay attribute is true, does not insert any predefined category. + // Some preloaded applications (like font package) shouldn't be visible + // in app-tray and host-manager. + // But, the nodisplay attribute can be set by "partner" or "platform" privilege only. + if (!uiApp.isNoDisplay()) { + uiApp.addAppCategory(DPL::FromASCIIString(STR_CATEGORY_WATCH_APP)); + } + } +} + +void TaskManifestFile::setMetadata(UiApplication &uiApp) +{ + WrtDB::ConfigParserData::MetadataList metadataList = + m_context.widgetConfig.configInfo.metadataList; + + if (metadataList.empty()) { + _D("Web application doesn't contain metadata"); + return; + } + FOREACH(it, metadataList) { + MetadataType metadataType(it->key, it->value); + uiApp.addMetadata(metadataType); + } +} + +#ifdef DBOX_ENABLED +void TaskManifestFile::setLiveBoxInfo(Manifest& manifest) +{ + ConfigParserData::LiveboxList& liveboxList = + m_context.widgetConfig.configInfo.m_livebox; + + if (liveboxList.empty()) { + _D("no livebox"); + return; + } + + if (!addBoxUiApplication(manifest)) { + _D("error during adding UiApplication for d-box"); + return; + } + + FOREACH(it, liveboxList) { + _D("setLiveBoxInfo"); + LiveBoxInfo liveBox; + WrtDB::ConfigParserData::OptionalLiveboxInfo ConfigInfo = *it; + DPL::String appid = m_context.widgetConfig.tzAppid; + + if (ConfigInfo->m_liveboxId != L"") { + liveBox.setLiveboxId(ConfigInfo->m_liveboxId); + } + + if (ConfigInfo->m_primary != L"") { + liveBox.setPrimary(ConfigInfo->m_primary); + } + + if (ConfigInfo->m_autoLaunch != L"") { + liveBox.setAutoLaunch(ConfigInfo->m_autoLaunch); + } + + if (ConfigInfo->m_updatePeriod != L"") { + liveBox.setUpdatePeriod(ConfigInfo->m_updatePeriod); + } + + std::list > boxLabelList; + if (!ConfigInfo->m_label.empty()) { + FOREACH(im, ConfigInfo->m_label) { + std::pair boxSize; + Locale i = (*im).first; + // translate en -> en_US etc + DPL::OptionalString tag = getLangTag(i); + if (!tag) { + tag = i; + } + boxSize.first = (*tag); + boxSize.second = (*im).second; + boxLabelList.push_back(boxSize); + } + liveBox.setLabel(boxLabelList); + } + + DPL::String defaultLocale = + DPL::FromUTF8String(m_context.locations->getPackageInstallationDir()) + + DPL::String(L"/res/wgt/"); + + if (ConfigInfo->m_icon != L"") { + DPL::String icon = + DPL::FromUTF8String(m_context.locations->getSharedDataDir()) + + DPL::String(L"/") + + ConfigInfo->m_liveboxId + DPL::String(L".icon.png"); + liveBox.setIcon(icon); + } + + if (ConfigInfo->m_boxInfo.m_boxSrc.empty() || + ConfigInfo->m_boxInfo.m_boxSize.empty()) + { + _D("Widget doesn't contain box"); + return; + } else { + BoxInfoType box; + if (!ConfigInfo->m_boxInfo.m_boxSrc.empty()) { + if ((0 == ConfigInfo->m_boxInfo.m_boxSrc.compare(0, 4, L"http")) + || (0 == + ConfigInfo->m_boxInfo.m_boxSrc.compare(0, 5, L"https"))) + { + box.boxSrc = ConfigInfo->m_boxInfo.m_boxSrc; + } else { + box.boxSrc = defaultLocale + ConfigInfo->m_boxInfo.m_boxSrc; + } + } + + if (ConfigInfo->m_boxInfo.m_boxMouseEvent == L"true") { + std::string boxType; + if (ConfigInfo->m_type == L"") { + // in case of default livebox + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String(ConfigInfo->m_type); + } + + int box_scrollable = + web_provider_plugin_get_box_scrollable(boxType.c_str()); + + if (box_scrollable) { + box.boxMouseEvent = L"true"; + } else { + box.boxMouseEvent = L"false"; + } + } else { + box.boxMouseEvent = L"false"; + } + + if (ConfigInfo->m_boxInfo.m_boxTouchEffect == L"true") { + box.boxTouchEffect = L"true"; + } else { + box.boxTouchEffect= L"false"; + } + + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + ConfigInfo->m_boxInfo.m_boxSize; + FOREACH(it, boxSizeList) { + if (!(*it).m_preview.empty()) { + (*it).m_preview = + DPL::FromUTF8String(m_context.locations->getSharedDataDir()) + + DPL::String(L"/") + + ConfigInfo->m_liveboxId + DPL::String(L".") + + (*it).m_size + DPL::String(L".preview.png"); + } + box.boxSize.push_back((*it)); + } + + if (!ConfigInfo->m_boxInfo.m_pdSrc.empty() + && !ConfigInfo->m_boxInfo.m_pdWidth.empty() + && !ConfigInfo->m_boxInfo.m_pdHeight.empty()) + { + if ((0 == ConfigInfo->m_boxInfo.m_pdSrc.compare(0, 4, L"http")) + || (0 == ConfigInfo->m_boxInfo.m_pdSrc.compare(0, 5, L"https"))) + { + box.pdSrc = ConfigInfo->m_boxInfo.m_pdSrc; + } else { + box.pdSrc = defaultLocale + ConfigInfo->m_boxInfo.m_pdSrc; + } + box.pdWidth = ConfigInfo->m_boxInfo.m_pdWidth; + box.pdHeight = ConfigInfo->m_boxInfo.m_pdHeight; + } + liveBox.setBox(box); + } + manifest.addLivebox(liveBox); + } +} +#endif + +void TaskManifestFile::setAccount(Manifest& manifest) +{ + WrtDB::ConfigParserData::AccountProvider account = + m_context.widgetConfig.configInfo.accountProvider; + + AccountProviderType provider; + + if (account.m_iconSet.empty()) { + _D("Widget doesn't contain Account"); + return; + } + if (account.m_multiAccountSupport) { + provider.multiAccount = L"true"; + } else { + provider.multiAccount = L"false"; + } + provider.appid = m_context.widgetConfig.tzAppid; + + FOREACH(it, account.m_iconSet) { + std::pair icon; + + if (it->first == ConfigParserData::IconSectionType::DefaultIcon) { + icon.first = L"account"; + } else if (it->first == ConfigParserData::IconSectionType::SmallIcon) { + icon.first = L"account-small"; + } + + // account manifest requires absolute path for icon + // /opt/apps/[package]/shared/res/[icon_path] + icon.second = DPL::FromUTF8String(m_context.locations->getSharedResourceDir()) + + DPL::String(L"/") + + it->second; + provider.icon.push_back(icon); + } + + FOREACH(it, account.m_displayNameSet) { + provider.name.push_back(LabelType(it->second, it->first)); + } + + FOREACH(it, account.m_capabilityList) { + provider.capability.push_back(*it); + } + + Account accountInfo; + accountInfo.addAccountProvider(provider); + manifest.addAccount(accountInfo); +} + +void TaskManifestFile::setPrivilege(Manifest& manifest) +{ + WrtDB::ConfigParserData::PrivilegeList privileges = + m_context.widgetConfig.configInfo.privilegeList; + + PrivilegeType privilege; + + FOREACH(it, privileges) + { + privilege.addPrivilegeName(it->name); + } + + manifest.addPrivileges(privilege); +} + +void TaskManifestFile::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskManifestFile::EndStep() +{ + LOGD("--------- : END ----------"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_manifest_file.h b/src_wearable/jobs/widget_install/task_manifest_file.h new file mode 100755 index 0000000..240cc40 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_manifest_file.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_manifest_file.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include + +#include + +#include +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskManifestFile : + public DPL::TaskDecl +{ + public: + + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ManifestValidationError) + DECLARE_EXCEPTION_TYPE(Base, ManifestParsingError) + + TaskManifestFile(InstallerContext &inCont); + virtual ~TaskManifestFile(); + + private: + //context data + InstallerContext &m_context; + + //TODO stepAbort + //steps + void stepCreateExecFile(); + void stepCopyIconFiles(); + void stepCopyLiveboxFiles(); + void stepCopyAccountIconFiles(); +#ifdef SERVICE_ENABLED + void stepCopyServiceIconFiles(); +#endif + void stepGenerateManifest(); + void stepCreateLinkNPPluginsFile(); + + void stepAbortParseManifest(); + + void StartStep(); + void EndStep(); + + //private data + std::list icon_list; //TODO: this should be registered as + // external files + std::ostringstream backup_dir; + xmlTextWriterPtr writer; + DPL::String manifest_name; + DPL::String manifest_file; + std::string commit_manifest; + + //private methods + + void writeManifest(const DPL::String & path); + void commitManifest(); + + void setWidgetExecPath(UiApplication & uiApp, + const std::string &postfix = std::string()); + void setWidgetName(Manifest & manifest, + UiApplication & uiApp); + void setWidgetIds(Manifest & manifest, + UiApplication & uiApp, + const std::string &postfix = std::string()); + void setWidgetIcons(UiApplication & uiApp); + void setWidgetDescription(Manifest & manifest); + void setWidgetManifest(Manifest & manifest); + void setWidgetOtherInfo(UiApplication & uiApp); + void setAppControlsInfo(UiApplication & uiApp); + void setAppControlInfo(UiApplication & uiApp, + const WrtDB::ConfigParserData::AppControlInfo & service); + void setAppCategory(UiApplication & uiApp); + void setMetadata(UiApplication & uiApp); + void setLiveBoxInfo(Manifest& manifest); + void setAccount(Manifest& uiApp); + void setPrivilege(Manifest& manifest); + + void generateWidgetName(Manifest & manifest, + UiApplication &uiApp, + const DPL::OptionalString& tag, + DPL::OptionalString name, + bool & defaultNameSaved); + void generateWidgetDescription(Manifest & manifest, + const DPL::OptionalString& tag, + DPL::OptionalString description); + void generateWidgetIcon(UiApplication & uiApp, + const DPL::OptionalString& tag, + const DPL::String& language, const std::string &extension, + bool isSmall, bool & defaultIconSaved); + void copyFile(const std::string& sourceFile, + const std::string& targetFile); + bool addBoxUiApplication(Manifest& manifest); + + //for widget update + DPL::String getIconTargetFilename(const DPL::String& languageTag, + const std::string & ext) const; + + static void saveLocalizedKey(std::ofstream &file, + const DPL::String& key, + const DPL::String& languageTag); + + static const char * encoding; + +#ifdef IME_ENABLED + void extractImeInfo(ImeApplication& imeApp); + void setWidgetNameIME(ImeApplication& imeApp); + void setWidgetIdsIME(ImeApplication& imeApp, const std::string& postfix = std::string()); + void generateWidgetNameIME(ImeApplication& imeApp, const DPL::OptionalString& tag, DPL::OptionalString name, bool& defaultNameSaved); + void setWidgetUuidIME(ImeApplication& imeApp); + void setWidgetLanguageIME(ImeApplication& imeApp); + void setWidgetTypeIME(ImeApplication& imeApp); + void setWidgetOptionIME(ImeApplication& imeApp); +#endif + +#ifdef SERVICE_ENABLED + void setServiceInfo(ServiceApplication& serviceApp, WrtDB::ConfigParserData::ServiceAppInfo & service); + void setWidgetExecPathService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service); + void setWidgetIdsService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service); + void setWidgetNameService(ServiceApplication & serviceApp, WrtDB::ConfigParserData::ServiceAppInfo & service); + void setWidgetIconService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service); + void setAppControlsInfoService(ServiceApplication & serviceApp); + void setAppControlInfoService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::AppControlInfo & service); + void setWidgetOtherInfoService(ServiceApplication & serviceApp); + void setWidgetComponentService(ServiceApplication & serviceApp); + void setWidgetAutoRestartService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service); + void setWidgetOnBootService(ServiceApplication & serviceApp, const WrtDB::ConfigParserData::ServiceAppInfo & service); +#endif +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_DESKTOP_FILE_H */ diff --git a/src_wearable/jobs/widget_install/task_pkg_info_update.cpp b/src_wearable/jobs/widget_install/task_pkg_info_update.cpp new file mode 100644 index 0000000..942cca2 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_pkg_info_update.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_pkg_info_update.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task information about package + * update + */ +#include "task_pkg_info_update.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +} + +namespace Jobs { +namespace WidgetInstall { +TaskPkgInfoUpdate::TaskPkgInfoUpdate(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskPkgInfoUpdate::StartStep); + AddStep(&TaskPkgInfoUpdate::StepPkgInfo); + AddStep(&TaskPkgInfoUpdate::StepSetCertiInfo); + AddStep(&TaskPkgInfoUpdate::EndStep); + AddStep(&TaskPkgInfoUpdate::StepSetEndofInstallation); + + AddAbortStep(&TaskPkgInfoUpdate::StepAbortCertiInfo); + AddAbortStep(&TaskPkgInfoUpdate::stepAbortParseManifest); +} + +void TaskPkgInfoUpdate::StepPkgInfo() +{ + int code = 0; + char* updateTags[3] = {NULL, }; + + char preloadTrue[] = "preload=true"; + char removableTrue[] = "removable=true"; + char removableFalse[] = "removable=false"; + + if (InstallMode::InstallTime::CSC == m_context.mode.installTime + || InstallMode::InstallTime::PRELOAD == m_context.mode.installTime) { + updateTags[0] = preloadTrue; + if (m_context.mode.removable) { + updateTags[1] = removableTrue; + } else { + updateTags[1] = removableFalse; + } + updateTags[2] = NULL; + } + + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + m_manifest += "/usr/share/packages/"; + } else { + m_manifest += "/opt/share/packages/"; + } + m_manifest += DPL::ToUTF8String(m_context.widgetConfig.tzPkgid) + ".xml"; + _D("manifest file : %s", m_manifest.c_str()); + + if (m_context.isUpdateMode || ( + m_context.mode.rootPath == InstallMode::RootPath::RO + && (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_context.mode.installTime == InstallMode::InstallTime::FOTA) + && m_context.mode.extension == InstallMode::ExtensionType::DIR)) { + + code = pkgmgr_parser_parse_manifest_for_upgrade( + m_manifest.c_str(), (updateTags[0] == NULL) ? NULL : updateTags); + + if (code != 0) { + _E("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + } else { + code = pkgmgr_parser_parse_manifest_for_installation( + m_manifest.c_str(), (updateTags[0] == NULL) ? NULL : updateTags); + + if (code != 0) { + _E("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_PKGINFO_UPDATE, + "Manifest Update Finished"); + _D("Manifest parsed"); +} + +void TaskPkgInfoUpdate::StepSetCertiInfo() +{ + _D("StepSetCertiInfo"); + + if (pkgmgr_installer_create_certinfo_set_handle(&m_pkgHandle) < 0) { + _E("pkgmgrInstallerCreateCertinfoSetHandle fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to create certificate handle"); + } + + SetCertiInfo(SIGNATURE_AUTHOR); + SetCertiInfo(SIGNATURE_DISTRIBUTOR); + SetCertiInfo(SIGNATURE_DISTRIBUTOR2); + + if ((pkgmgr_installer_save_certinfo( + const_cast(DPL::ToUTF8String( + m_context.widgetConfig.tzPkgid).c_str()), + m_pkgHandle)) < 0) + { + _E("pkgmgrInstallerSaveCertinfo fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Installer Save Certinfo"); + } else { + _D("Succeed to save Certinfo"); + } + + if (pkgmgr_installer_destroy_certinfo_set_handle(m_pkgHandle) < 0) { + _E("pkgmgrInstallerDestroyCertinfoSetHandle fail"); + } +} + +void TaskPkgInfoUpdate::SetCertiInfo(int source) +{ + _D("Set CertiInfo to pkgmgr : %d", source); + CertificateChainList certificateChainList; + m_context.widgetSecurity.getCertificateChainList(certificateChainList, + (CertificateSource)source); + + FOREACH(it, certificateChainList) + { + _D("Insert certinfo to pkgmgr structure"); + + ValidationCore::CertificateCollection chain; + + if (false == chain.load(*it)) { + _E("Chain is broken"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Installer Save Certinfo"); + } + + if (!chain.sort()) { + _E("Chain failed at sorting"); + } + + ValidationCore::CertificateList list = chain.getCertificateList(); + + FOREACH(certIt, list) + { + pkgmgr_instcert_type instCertType = PM_SET_AUTHOR_ROOT_CERT; + if (source == SIGNATURE_AUTHOR) { + _D("set SIGNATURE_AUTHOR"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_AUTHOR_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_AUTHOR_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_AUTHOR_SIGNER_CERT; + } + } + } else if (source == SIGNATURE_DISTRIBUTOR) { + _D("Set SIGNATURE_DISTRIBUTOR"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_DISTRIBUTOR_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_DISTRIBUTOR_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_DISTRIBUTOR_SIGNER_CERT; + } + } + } else if (source == SIGNATURE_DISTRIBUTOR2) { + _D("Set SIGNATURE_DISTRIBUTOR2"); + if ((*certIt)->isRootCert()) { + instCertType = PM_SET_DISTRIBUTOR2_ROOT_CERT; + } else { + if ((*certIt)->isCA()) { + instCertType = PM_SET_DISTRIBUTOR2_INTERMEDIATE_CERT; + } else { + instCertType = PM_SET_DISTRIBUTOR2_SIGNER_CERT; + } + } + } else { + _D("UNKNOWN.."); + } + _D("cert type : %d", instCertType); + if ((pkgmgr_installer_set_cert_value( + m_pkgHandle, + instCertType, + const_cast(((*certIt)->getBase64()).c_str()))) < 0) + { + _E("pkgmgrInstallerSetCertValue fail"); + ThrowMsg(Exceptions::SetCertificateInfoFailed, + "Failed to Set CertValue"); + } + } + } +} + +void TaskPkgInfoUpdate::StepAbortCertiInfo() +{ + if ((pkgmgr_installer_delete_certinfo( + const_cast(DPL::ToUTF8String( + m_context.widgetConfig.tzPkgid).c_str()))) < + 0) + { + _E("pkgmgr_installer_delete_certinfo fail"); + } +} + +void TaskPkgInfoUpdate::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskPkgInfoUpdate::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_SET_CERTINFO, + "Save certinfo to pkgmgr"); + + LOGD("--------- : END ----------"); +} + +void TaskPkgInfoUpdate::stepAbortParseManifest() +{ + _E("[Parse Manifest] Abroting...."); + + int code = pkgmgr_parser_parse_manifest_for_uninstallation( + m_manifest.c_str(), NULL); + + if (0 != code) { + _W("Manifest parser error: %d", code); + ThrowMsg(Exceptions::ManifestInvalid, "Parser returncode: " << code); + } + int ret = unlink(m_manifest.c_str()); + if (0 != ret) { + _W("No manifest file found: %s", m_manifest.c_str()); + } +} + +void TaskPkgInfoUpdate::StepSetEndofInstallation() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_END, + "End installation"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_pkg_info_update.h b/src_wearable/jobs/widget_install/task_pkg_info_update.h new file mode 100644 index 0000000..e1e9235 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_pkg_info_update.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_pkg_info_update.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ + +#include +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPkgInfoUpdate : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void StepPkgInfo(); + void StepSetCertiInfo(); + void SetCertiInfo(int source); + void StepSetEndofInstallation(); + + void stepAbortParseManifest(); + void StepAbortCertiInfo(); + + void StartStep(); + void EndStep(); + + pkgmgr_instcertinfo_h m_pkgHandle; + std::string m_manifest; + + public: + explicit TaskPkgInfoUpdate(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_PKG_INFO_UPDATE_H_ */ diff --git a/src_wearable/jobs/widget_install/task_prepare_files.cpp b/src_wearable/jobs/widget_install/task_prepare_files.cpp new file mode 100644 index 0000000..d8eac2e --- /dev/null +++ b/src_wearable/jobs/widget_install/task_prepare_files.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_generate_config.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "task_prepare_files.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +TaskPrepareFiles::TaskPrepareFiles(InstallerContext &installerContext) : + DPL::TaskDecl(this), + m_installerContext(installerContext) +{ + AddStep(&TaskPrepareFiles::StartStep); + AddStep(&TaskPrepareFiles::StepCopyFiles); + AddStep(&TaskPrepareFiles::EndStep); +} + +void TaskPrepareFiles::CopyFile(const std::string& source) +{ + if (source.empty()) { + _W("No source file specified"); + return; + } + + std::string filename = source; + size_t last = source.find_last_of("\\/"); + if (last != std::string::npos) { + filename = source.substr(last + 1); + } + std::string target = + m_installerContext.locations->getSourceDir() + '/' + + filename; + _D("source %s", source.c_str()); + _D("target %s", target.c_str()); + + Try + { + DPL::FileInput input(source); + DPL::FileOutput output(target); + DPL::Copy(&input, &output); + } + Catch(DPL::FileInput::Exception::Base) + { + _E("File input error"); + // Error while opening or closing source file + ReThrowMsg(Exceptions::CopyIconFailed, source); + } + Catch(DPL::FileOutput::Exception::Base) + { + _E("File output error"); + // Error while opening or closing target file + ReThrowMsg(Exceptions::CopyIconFailed, target); + } + Catch(DPL::CopyFailed) + { + _E("File copy error"); + // Error while copying + ReThrowMsg(Exceptions::CopyIconFailed, target); + } +} + +void TaskPrepareFiles::StepCopyFiles() +{ + CopyFile(m_installerContext.locations->getWidgetSource()); + + size_t last = m_installerContext.locations->getWidgetSource().find_last_of( + "\\/"); + std::string sourceDir = ""; + if (last != std::string::npos) { + sourceDir = m_installerContext.locations->getWidgetSource().substr( + 0, + last + + 1); + } + + _D("Icons copy..."); + FOREACH(it, m_installerContext.widgetConfig.configInfo.iconsList) { + std::ostringstream os; + _D("Coping: %s%ls", sourceDir.c_str(), (it->src).c_str()); + os << sourceDir << DPL::ToUTF8String(it->src); + CopyFile(os.str()); + } +} + +void TaskPrepareFiles::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskPrepareFiles::EndStep() +{ + LOGD("--------- : END ----------"); +} +} // namespace WidgetInstall +} // namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_prepare_files.h b/src_wearable/jobs/widget_install/task_prepare_files.h new file mode 100644 index 0000000..99458e8 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_prepare_files.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_prepare_files.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPrepareFiles : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_installerContext; + + void CopyFile(const std::string& source); + + // Steps + void StepCopyFiles(); + + void StartStep(); + void EndStep(); + + public: + explicit TaskPrepareFiles(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_PREPARE_FILES_H_ */ diff --git a/src_wearable/jobs/widget_install/task_prepare_reinstall.cpp b/src_wearable/jobs/widget_install/task_prepare_reinstall.cpp new file mode 100644 index 0000000..97133bf --- /dev/null +++ b/src_wearable/jobs/widget_install/task_prepare_reinstall.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_prepare_reinstall.cpp + * @author Jihoon Chung(jihoon.chung@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task prepare reinstalling + */ + +#include "task_prepare_reinstall.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const char* const KEY_DELETE = "#delete"; +const char* const KEY_ADD = "#add"; +const char* const KEY_MODIFY = "#modify"; +std::list keyList = {KEY_DELETE, KEY_ADD, KEY_MODIFY}; + +void verifyFile(const std::string &filePath) +{ + if (access(filePath.c_str(), F_OK) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, "File is missed " << filePath); + } +} + +std::string parseSubPath(const std::string& filePath) +{ + std::string subPath(""); + size_t pos = filePath.find_last_of('/') + 1; + + if (pos != std::string::npos) { + subPath = filePath.substr(0, pos); + } + return subPath; +} + +void createDir(const std::string& path) +{ + if (WrtUtilMakeDir(path)) { + _D("Create directory : %s", path.c_str()); + } else { + ThrowMsg(Exceptions::RDSDeltaFailure, "Fail to create dir" << path); + } +} +} // namespace anonymous + +TaskPrepareReinstall::TaskPrepareReinstall(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskPrepareReinstall::StartStep); + AddStep(&TaskPrepareReinstall::StepPrepare); + AddStep(&TaskPrepareReinstall::StepParseRDSDelta); + AddStep(&TaskPrepareReinstall::StepVerifyRDSDelta); + AddStep(&TaskPrepareReinstall::StepAddFile); + AddStep(&TaskPrepareReinstall::StepDeleteFile); + AddStep(&TaskPrepareReinstall::StepModifyFile); + AddStep(&TaskPrepareReinstall::EndStep); +} + +void TaskPrepareReinstall::StepPrepare() +{ + _D("Prepare"); + m_sourcePath = m_context.locations->getTemporaryPackageDir(); + m_sourcePath += "/"; + + m_installedPath = m_context.locations->getPackageInstallationDir(); + m_installedPath += "/"; +} + +void TaskPrepareReinstall::StepParseRDSDelta() +{ + _D("parse RDS delta"); + std::string rdsDeltaPath = m_sourcePath; + rdsDeltaPath += ".rds_delta"; + std::ifstream delta(rdsDeltaPath); + + if (!delta.is_open()) { + ThrowMsg(Exceptions::RDSDeltaFailure, "rds_delta file is missed"); + return; + } + + std::string line; + std::string key; + while (std::getline(delta, line) &&!delta.eof()) { + FOREACH(keyIt, keyList) { + if (line == *keyIt) { + _D("find key = [%s]", line.c_str()); + key = line; + break; + } + } + if (key == line || line.empty() || line == "\n") { + continue; + } + if (key == KEY_DELETE) { + m_deleteFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } else if (key == KEY_ADD) { + m_addFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } else if (key == KEY_MODIFY) { + m_modifyFileList.push_back(line); + _D("line = [%s]", line.c_str()); + } + } +} + +void TaskPrepareReinstall::StepVerifyRDSDelta() +{ + _D("verify RDS delta"); + // Verify ADD file + FOREACH(file, m_addFileList) { + std::string addFilePath = m_sourcePath; + addFilePath += *file; + verifyFile(addFilePath); + } + // Verify DELETE file + FOREACH(file, m_deleteFileList) { + std::string deleteFilePath = m_installedPath; + deleteFilePath += *file; + verifyFile(deleteFilePath); + } + // Verify MODIFY file + FOREACH(file, m_modifyFileList) { + std::string newFilePath = m_sourcePath; + newFilePath += *file; + verifyFile(newFilePath); + + std::string existingFilePath = m_installedPath; + existingFilePath += *file; + verifyFile(existingFilePath); + } + _D("Finished veify RDS Delta"); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_RDS_DELTA_CHECK, + "RDS delta verify finished"); +} + +void TaskPrepareReinstall::StepAddFile() +{ + _D("Add file"); + FOREACH(file, m_addFileList) { + std::string newfile = m_sourcePath; + newfile += *file; + std::string destPath = m_installedPath; + destPath += *file; + + if (WrtUtilDirExists(newfile)) { + // In case of a new directory + createDir(destPath); + } else { + // In case of a new file + + // Parse directory and file separately + std::string subPath = parseSubPath(destPath); + if (subPath.empty()) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Invalid path given" << destPath); + } + + // Create a new directory + createDir(subPath); + + // Add file + if (rename(newfile.c_str(), destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to add file " << newfile); + } + _D("Add %s to %s", newfile.c_str(), destPath.c_str()); + } + } +} + +void TaskPrepareReinstall::StepDeleteFile() +{ + _D("Delete file"); + FOREACH(file, m_deleteFileList) { + std::string deleteFilePath = m_installedPath; + deleteFilePath += *file; + if (remove(deleteFilePath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to DELETE file " << deleteFilePath); + } + _D("Delete %s", deleteFilePath.c_str()); + } +} + +void TaskPrepareReinstall::StepModifyFile() +{ + _D("Modify file"); + FOREACH(file, m_modifyFileList) { + std::string destPath = m_installedPath; + destPath += *file; + if (remove(destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to delete existing file " << destPath); + } + + std::string newfile = m_sourcePath; + newfile += *file; + if (rename(newfile.c_str(), destPath.c_str()) != 0) { + ThrowMsg(Exceptions::RDSDeltaFailure, + "Fail to move new file" << destPath); + } + _D("Replace %s to %s", newfile.c_str(), destPath.c_str()); + } + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_RDS_PREPARE, + "RDS prepare finished"); +} + +void TaskPrepareReinstall::StartStep() +{ + LOGD("---------- : START ----------"); +} + +void TaskPrepareReinstall::EndStep() +{ + LOGD("---------- : END ----------"); + m_context.job->UpdateProgress( + InstallerContext::INSTALL_END, + "End RDS update"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_prepare_reinstall.h b/src_wearable/jobs/widget_install/task_prepare_reinstall.h new file mode 100644 index 0000000..2828f27 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_prepare_reinstall.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_prepare_reinstall.h + * @author Jihoon Chung(jihoon.chung@samsung.com) + * @version 1.0 + * @brief Header file for installer task prepare reinstalling + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H + +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskPrepareReinstall : + public DPL::TaskDecl +{ + public: + TaskPrepareReinstall(InstallerContext& context); + + private: + // install internal location + void StepPrepare(); + void StepParseRDSDelta(); + void StepVerifyRDSDelta(); + void StepAddFile(); + void StepDeleteFile(); + void StepModifyFile(); + + void StepAbortPrepareReinstall(); + + void StartStep(); + void EndStep(); + + InstallerContext& m_context; + // TODO : replace multimap + std::list m_addFileList; + std::list m_deleteFileList; + std::list m_modifyFileList; + std::string m_sourcePath; + std::string m_installedPath; +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PREPARE_REINSTALL_H diff --git a/src_wearable/jobs/widget_install/task_process_config.cpp b/src_wearable/jobs/widget_install/task_process_config.cpp new file mode 100755 index 0000000..4198f4d --- /dev/null +++ b/src_wearable/jobs/widget_install/task_process_config.cpp @@ -0,0 +1,695 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_process_config.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task widget config + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef DBOX_ENABLED +#include +#include +#endif +#include + +#include + +namespace { // anonymous +const DPL::String BR = DPL::FromUTF8String("
"); +const std::string WINDGET_INSTALL_NETWORK_ACCESS = "network access"; +} + +namespace Jobs { +namespace WidgetInstall { + +TaskProcessConfig::TaskProcessConfig(InstallerContext& installContext) : + DPL::TaskDecl(this), + m_installContext(installContext) +{ + AddStep(&TaskProcessConfig::StartStep); + AddStep(&TaskProcessConfig::ReadLocaleFolders); + AddStep(&TaskProcessConfig::StepFillWidgetConfig); + AddStep(&TaskProcessConfig::ProcessLocalizedStartFiles); + AddStep(&TaskProcessConfig::ProcessBackgroundPageFile); + AddStep(&TaskProcessConfig::ProcessLocalizedIcons); + AddStep(&TaskProcessConfig::ProcessWidgetInstalledPath); + AddStep(&TaskProcessConfig::ProcessAppControlInfo); + AddStep(&TaskProcessConfig::ProcessSecurityModel); +#ifdef DBOX_ENABLED + AddStep(&TaskProcessConfig::StepVerifyFeatures); +#endif + AddStep(&TaskProcessConfig::StepVerifyLivebox); + AddStep(&TaskProcessConfig::StepCheckMinVersionInfo); + AddStep(&TaskProcessConfig::EndStep); +} + +void TaskProcessConfig::StepFillWidgetConfig() +{ + if (!fillWidgetConfig(m_installContext.widgetConfig, + m_installContext.widgetConfig.configInfo)) + { + _E("Widget configuration is illformed"); + ThrowMsg(Exception::ConfigParseFailed, "Widget configuration is illformed"); + } +} + +void TaskProcessConfig::ReadLocaleFolders() +{ + _D("Reading locale"); + //Adding default locale + m_localeFolders.insert(L""); + + std::string localePath = + m_installContext.locations->getSourceDir() + "/locales"; + + DIR* localeDir = opendir(localePath.c_str()); + if (!localeDir) { + _D("No /locales directory in the widget package."); + return; + } + + struct stat statStruct; + struct dirent dirent; + struct dirent *result; + int return_code; + errno = 0; + for (return_code = readdir_r(localeDir, &dirent, &result); + result != NULL && return_code == 0; + return_code = readdir_r(localeDir, &dirent, &result)) + { + DPL::String dirName = DPL::FromUTF8String(dirent.d_name); + std::string absoluteDirName = localePath + "/"; + absoluteDirName += dirent.d_name; + + if (stat(absoluteDirName.c_str(), &statStruct) != 0) { + _E("stat() failed with %s", DPL::GetErrnoString().c_str()); + continue; + } + + if (S_ISDIR(statStruct.st_mode)) { + //Yes, we ignore current, parent & hidden directories + if (dirName[0] != L'.') { + _D("Adding locale directory \"%ls\"", dirName.c_str()); + m_localeFolders.insert(dirName); + } + } + } + + if (return_code != 0 || errno != 0) { + _E("readdir_r() failed with %s", DPL::GetErrnoString().c_str()); + } + + if (-1 == closedir(localeDir)) { + _E("Failed to close dir: %s with error: %s", localePath.c_str(), DPL::GetErrnoString().c_str()); + } + + m_installContext.job->UpdateProgress(InstallerContext::INSTALL_WIDGET_CONFIG1, "Read locale folders"); +} + +void TaskProcessConfig::ProcessLocalizedStartFiles() +{ + typedef DPL::String S; + ProcessStartFile( + m_installContext.widgetConfig.configInfo.startFile, + m_installContext.widgetConfig.configInfo. + startFileContentType, + m_installContext.widgetConfig.configInfo.startFileEncoding, + true); + ProcessStartFile(S(L"index.htm"), S(L"text/html")); + ProcessStartFile(S(L"index.html"), S(L"text/html")); + ProcessStartFile(S(L"index.svg"), S(L"image/svg+xml")); + ProcessStartFile(S(L"index.xhtml"), S(L"application/xhtml+xml")); + ProcessStartFile(S(L"index.xht"), S(L"application/xhtml+xml")); + // TODO: we need better check if in current locales widget is valid + FOREACH(it, m_installContext.widgetConfig.localizationData.startFiles) { + if (it->propertiesForLocales.size() > 0) { + return; + } + } + ThrowMsg(Exceptions::InvalidStartFile, + "The Widget has no valid start file"); +} + +void TaskProcessConfig::ProcessStartFile(const DPL::OptionalString& path, + const DPL::OptionalString& type, + const DPL::OptionalString& encoding, + bool typeForcedInConfig) +{ + using namespace WrtDB; + + if (!!path) { + WidgetRegisterInfo::LocalizedStartFile startFileData; + startFileData.path = *path; + + FOREACH(i, m_localeFolders) { + DPL::String pathPrefix = *i; + if (!pathPrefix.empty()) { + pathPrefix = L"locales/" + pathPrefix + L"/"; + } + + DPL::String relativePath = pathPrefix + *path; + DPL::String absolutePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + relativePath; + _D("absolutePath : %ls", absolutePath.c_str()); + + // get property data from packaged app + if (WrtUtilFileExists(DPL::ToUTF8String(absolutePath))) { + WidgetRegisterInfo::StartFileProperties startFileProperties; + if (!!type) { + startFileProperties.type = *type; + } else { + startFileProperties.type = + MimeTypeUtils::identifyFileMimeType(absolutePath); + } + + //proceed only if MIME type is supported + if (MimeTypeUtils::isMimeTypeSupportedForStartFile( + startFileProperties.type)) + { + if (!!encoding) { + startFileProperties.encoding = *encoding; + } else { + MimeTypeUtils::MimeAttributes attributes = + MimeTypeUtils::getMimeAttributes( + startFileProperties.type); + if (attributes.count(L"charset") > 0) { + startFileProperties.encoding = + attributes[L"charset"]; + } else { + startFileProperties.encoding = L"UTF-8"; + } + } + + startFileData.propertiesForLocales[*i] = + startFileProperties; + } else { + //9.1.16.5.content.8 + //(there seems to be no similar requirement in .6, + //so let's throw only when mime type is + // provided explcitly in config.xml) + if (typeForcedInConfig) { + ThrowMsg(Exceptions::WidgetConfigFileInvalid, + "Unsupported MIME type for start file."); + } + } + } else { + // set property data for hosted start url + // Hosted start url only support TIZEN WebApp + if (m_installContext.widgetConfig.webAppType == APP_TYPE_TIZENWEBAPP + ) + { + std::string startPath = DPL::ToUTF8String( + startFileData.path); + + if (strstr(startPath.c_str(), + "http") == startPath.c_str()) + { + WidgetRegisterInfo::StartFileProperties + startFileProperties; + if (!!type) { + startFileProperties.type = *type; + } + if (!!encoding) { + startFileProperties.encoding = *encoding; + } + startFileData.propertiesForLocales[*i] = + startFileProperties; + } + } + } + } + + m_installContext.widgetConfig.localizationData.startFiles.push_back( + startFileData); + } +} + +void TaskProcessConfig::ProcessBackgroundPageFile() +{ + if (!!m_installContext.widgetConfig.configInfo.backgroundPage) { + // check whether file exists + DPL::String backgroundPagePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + *m_installContext.widgetConfig.configInfo.backgroundPage; + //if no then cancel installation + if (!WrtUtilFileExists(DPL::ToUTF8String(backgroundPagePath))) { + ThrowMsg(Exceptions::WidgetConfigFileInvalid, + L"Given background page file not found in archive"); + } + } +} + +void TaskProcessConfig::ProcessLocalizedIcons() +{ + using namespace WrtDB; + FOREACH(i, m_installContext.widgetConfig.configInfo.iconsList) + { + ProcessIcon(*i); + } + ProcessIcon(ConfigParserData::Icon(L"icon.svg")); + ProcessIcon(ConfigParserData::Icon(L"icon.ico")); + ProcessIcon(ConfigParserData::Icon(L"icon.png")); + ProcessIcon(ConfigParserData::Icon(L"icon.gif")); + ProcessIcon(ConfigParserData::Icon(L"icon.jpg")); +} + +void TaskProcessConfig::ProcessIcon(const WrtDB::ConfigParserData::Icon& icon) +{ + _D("enter"); + bool isAnyIconValid = false; + //In case a default filename is passed as custom filename in config.xml, we + //need to keep a set of already processed filenames to avoid icon + // duplication + //in database. + + using namespace WrtDB; + + std::set &checkDuplication = icon.isSmall ? m_processedSmallIconSet : m_processedIconSet; + + if (checkDuplication.count(icon.src) > 0) { + _D("duplication %ls ", icon.src.c_str()); + return; + } + checkDuplication.insert(icon.src); + + LocaleSet localesAvailableForIcon; + + FOREACH(i, m_localeFolders) + { + DPL::String pathPrefix = *i; + if (!pathPrefix.empty()) { + pathPrefix = L"locales/" + pathPrefix + L"/"; + } + + DPL::String relativePath = pathPrefix + icon.src; + DPL::String absolutePath = DPL::FromUTF8String( + m_installContext.locations->getSourceDir()) + L"/" + + relativePath; + + if (WrtUtilFileExists(DPL::ToUTF8String(absolutePath))) { + DPL::String type = MimeTypeUtils::identifyFileMimeType(absolutePath); + + if (MimeTypeUtils::isMimeTypeSupportedForIcon(type)) { + isAnyIconValid = true; + localesAvailableForIcon.insert(*i); + _D("Icon absolutePath: %ls, assigned locale: %ls, type: %ls", + absolutePath.c_str(), (*i).c_str(), type.c_str()); + } + } + } + + if (isAnyIconValid) { + WidgetRegisterInfo::LocalizedIcon localizedIcon(icon, + localesAvailableForIcon); + m_installContext.widgetConfig.localizationData.icons.push_back( + localizedIcon); + } +} + +void TaskProcessConfig::ProcessWidgetInstalledPath() +{ + _D("ProcessWidgetInstalledPath"); + m_installContext.widgetConfig.widgetInstalledPath = + DPL::FromUTF8String( + m_installContext.locations->getPackageInstallationDir()); +} + +void TaskProcessConfig::ProcessAppControlInfo() +{ + _D("ProcessAppControlInfo"); + using namespace WrtDB; + + // In case of dispostion is inline, set the seperate execute + int index = 1; + // 0 index is reserved by default execute + FOREACH(it, m_installContext.widgetConfig.configInfo.appControlList) { + if (it->m_disposition == + ConfigParserData::AppControlInfo::Disposition::INLINE) + { + it->m_index = index++; + } else { + it->m_index = 0; + } + } +} + +void TaskProcessConfig::ProcessSecurityModel() +{ + // 0104. If the "required_version" specified in the Web Application's + // configuration is 2.2 or higher and if the Web Application's + // configuration is "CSP-compatible configuration", then the WRT MUST be + // set to "CSP-based security mode". Otherwise, the WRT MUST be set to + // "WARP-based security mode". + // 0105. A Web Application configuration is "CSP-compatible configuration" + // if the configuration includes one or more of + // / + // / + // elements. + + bool isSecurityModelV1 = false; + bool isSecurityModelV2 = false; + WrtDB::ConfigParserData &data = m_installContext.widgetConfig.configInfo; + + if (!!data.cspPolicy || + !!data.cspPolicyReportOnly || + !data.allowNavigationInfoList.empty()) + { + data.accessInfoSet.clear(); + } + + // WARP is V1 + if (!data.accessInfoSet.empty()) { + isSecurityModelV1 = true; + } + + // CSP & allow-navigation is V2 + if (!!data.cspPolicy || + !!data.cspPolicyReportOnly || + !data.allowNavigationInfoList.empty()) + { + isSecurityModelV2 = true; + } + + if (isSecurityModelV1 && isSecurityModelV2) { + _E("Security model is conflict"); + ThrowMsg(Exceptions::NotAllowed, "Security model is conflict"); + } else if (isSecurityModelV1) { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V1; + } else if (isSecurityModelV2) { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V2; + } else { + data.securityModelVersion = + WrtDB::ConfigParserData::SecurityModelVersion::SECURITY_MODEL_V1; + } + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Finished process security model"); +} + +void TaskProcessConfig::StepCheckMinVersionInfo() +{ + if (!isMinVersionCompatible( + m_installContext.widgetConfig.webAppType.appType, + m_installContext.widgetConfig.minVersion)) + { + _E("Platform version lower than required -> cancelling installation"); + ThrowMsg(Exceptions::NotAllowed, + "Platform version does not meet requirements"); + } + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Check MinVersion Finished"); +} + +void TaskProcessConfig::StepVerifyFeatures() +{ + using namespace WrtDB; + ConfigParserData &data = m_installContext.widgetConfig.configInfo; + ConfigParserData::FeaturesList list = data.featuresList; + ConfigParserData::FeaturesList newList; + + //in case of tests, this variable is unused + std::string featureInfo; + FOREACH(it, list) + { + // check feature vender for permission + // WAC, TIZEN WebApp cannot use other feature + + if (!isFeatureAllowed(m_installContext.widgetConfig.webAppType.appType, + it->name)) + { + _D("This application type not allowed to use this feature"); + ThrowMsg( + Exceptions::WidgetConfigFileInvalid, + "This app type [" << + m_installContext.widgetConfig.webAppType.getApptypeToString() + << + "] cannot be allowed to use [" << + DPL::ToUTF8String(it->name) + "] feature"); + } else { + newList.insert(*it); + featureInfo += DPL::ToUTF8String(it->name); + featureInfo += DPL::ToUTF8String(BR); + } + } + if (!data.accessInfoSet.empty()) { + featureInfo += WINDGET_INSTALL_NETWORK_ACCESS; + featureInfo += DPL::ToUTF8String(BR); + } + data.featuresList = newList; + + m_installContext.job->UpdateProgress( + InstallerContext::INSTALL_WIDGET_CONFIG2, + "Widget Config step2 Finished"); +} + +#ifdef DBOX_ENABLED +void TaskProcessConfig::StepVerifyLivebox() +{ + using namespace WrtDB; + ConfigParserData &data = m_installContext.widgetConfig.configInfo; + ConfigParserData::LiveboxList liveBoxList = data.m_livebox; + + if (liveBoxList.size() <= 0) { + return; + } + + FOREACH (it, liveBoxList) { + std::string boxType; + + size_t found = (**it).m_liveboxId.find_last_of(L"."); + if (found != std::string::npos) { + if (0 != (**it).m_liveboxId.compare(0, found, + m_installContext.widgetConfig.tzAppid)) { + _E("Invalid app-widget id (doesn't begin with application id)"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, + "Invalid app-widget id(doesn't begin with application id)"); + } + } + + if ((**it).m_type.empty()) { + boxType = web_provider_livebox_get_default_type(); + } else { + boxType = DPL::ToUTF8String((**it).m_type); + } + + _D("livebox type: %s", boxType.c_str()); + + ConfigParserData::LiveboxInfo::BoxSizeList boxSizeList = + (**it).m_boxInfo.m_boxSize; + char** boxSize = static_cast( + malloc(sizeof(char*)* boxSizeList.size())); + + int boxSizeCnt = 0; + FOREACH (m, boxSizeList) { + boxSize[boxSizeCnt++] = strdup(DPL::ToUTF8String((*m).m_size).c_str()); + } + + bool chkSize = web_provider_plugin_check_supported_size( + boxType.c_str(), boxSize, boxSizeCnt); + + for(int i = 0; i < boxSizeCnt; i++) { + free(boxSize[i]); + } + free(boxSize); + + if(!chkSize) { + _E("Invalid boxSize"); + ThrowMsg(Exceptions::WidgetConfigFileInvalid, "Invalid boxSize"); + } + } +} +#endif + +bool TaskProcessConfig::isFeatureAllowed(WrtDB::AppType appType, + DPL::String featureName) +{ + using namespace WrtDB; + _D("AppType = [%s]", WidgetType(appType).getApptypeToString().c_str()); + _D("FetureName = [%ls]", featureName.c_str()); + + AppType featureType = APP_TYPE_UNKNOWN; + std::string featureStr = DPL::ToUTF8String(featureName); + const char* feature = featureStr.c_str(); + + // check prefix of feature name + if (strstr(feature, PluginsPrefix::TIZENPluginsPrefix) == feature) { + // Tizen WebApp feature + featureType = APP_TYPE_TIZENWEBAPP; + } else if (strstr(feature, PluginsPrefix::W3CPluginsPrefix) == feature) { + // W3C standard feature + // Both WAC and TIZEN WebApp are possible to use W3C plugins + return true; + } else { + // unknown feature + // unknown feature will be checked next step + return true; + } + + if (appType == featureType) { + return true; + } + return false; +} + +bool TaskProcessConfig::parseVersionString(const std::string &version, + long &majorVersion, + long &minorVersion, + long µVersion) const +{ + std::istringstream inputString(version); + inputString >> majorVersion; + if (inputString.bad() || inputString.fail()) { + _W("Invalid minVersion format."); + return false; + } + inputString.get(); // skip period + inputString >> minorVersion; + if (inputString.bad() || inputString.fail()) { + _W("Invalid minVersion format"); + return false; + } else { + inputString.get(); // skip period + if (inputString.bad() || inputString.fail()) { + inputString >> microVersion; + } + } + return true; +} + +bool TaskProcessConfig::isMinVersionCompatible( + WrtDB::AppType appType, + const DPL::OptionalString & + widgetVersion) const +{ + if (!widgetVersion || (*widgetVersion).empty()) { + if (appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP + ) { + return false; + } else { + _W("minVersion attribute is empty. WRT assumes platform " + "supports this widget."); + return true; + } + } + + //Parse widget version + long majorWidget = 0, minorWidget = 0, microWidget = 0; + if (!parseVersionString(DPL::ToUTF8String(*widgetVersion), majorWidget, + minorWidget, microWidget)) + { + _W("Invalid format of widget version string."); + return false; + } + + //Parse supported version + long majorSupported = 0, minorSupported = 0, microSupported = 0; + std::string version; + if (appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP + ) { + version = WrtDB::GlobalConfig::GetTizenVersion(); + } else { + _W("Invaild AppType"); + return false; + } + + if (!parseVersionString(version, + majorSupported, minorSupported, microSupported)) + { + _W("Invalid format of platform version string."); + return true; + } + + if (majorWidget > majorSupported || + (majorWidget == majorSupported && minorWidget > minorSupported)) + { + _D("Platform doesn't support this widget."); + return false; + } + return true; +} + +bool TaskProcessConfig::isTizenWebApp() const +{ + if (m_installContext.widgetConfig.webAppType.appType == WrtDB::AppType::APP_TYPE_TIZENWEBAPP) + { + return true; + } + return false; +} + +bool TaskProcessConfig::fillWidgetConfig( + WrtDB::WidgetRegisterInfo& pWidgetConfigInfo, + WrtDB::ConfigParserData& configInfo) +{ + pWidgetConfigInfo.guid = configInfo.widget_id; + + if (!!configInfo.version) { + if (!pWidgetConfigInfo.version) { + pWidgetConfigInfo.version = configInfo.version; + } else { + if (pWidgetConfigInfo.version != configInfo.version) { + _E("Invalid archive"); + return false; + } + } + } + if (!!configInfo.minVersionRequired) { + pWidgetConfigInfo.minVersion = configInfo.minVersionRequired; + } else if (!!configInfo.tizenMinVersionRequired) { + pWidgetConfigInfo.minVersion = configInfo.tizenMinVersionRequired; + } + return true; +} + +void TaskProcessConfig::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskProcessConfig::EndStep() +{ + LOGD("--------- : END ----------"); +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_process_config.h b/src_wearable/jobs/widget_install/task_process_config.h new file mode 100755 index 0000000..fa20264 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_process_config.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_process_config.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task widget config + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { + +class TaskProcessConfig : + public DPL::TaskDecl +{ + private: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ConfigParseFailed) + }; + + typedef std::list > StringPairList; + + InstallerContext& m_installContext; + WrtDB::LocaleSet m_localeFolders; + std::set m_processedIconSet; + std::set m_processedSmallIconSet; + + void StepFillWidgetConfig(); + void ReadLocaleFolders(); + void ProcessLocalizedStartFiles(); + void ProcessStartFile( + const DPL::OptionalString& path, + const DPL::OptionalString& type, + const DPL::OptionalString& encoding = + DPL::OptionalString(), + bool typeForcedInConfig = false); + void ProcessBackgroundPageFile(); + void ProcessLocalizedIcons(); + void ProcessIcon(const WrtDB::ConfigParserData::Icon& icon); + void ProcessWidgetInstalledPath(); + void ProcessAppControlInfo(); + void ProcessSecurityModel(); + void StepVerifyFeatures(); + void StepVerifyLivebox(); + void StepCheckMinVersionInfo(); + + template + void StepCancelInstallation(); + + void StartStep(); + void EndStep(); + + DPL::String createAuthorWidgetInfo() const; + bool isFeatureAllowed( + WrtDB::AppType appType, DPL::String featureName); + bool isMinVersionCompatible( + WrtDB::AppType appType, + const DPL::OptionalString &widgetVersion) const; + /** + * @brief Parses version string in format "major.minor.micro anything" + * Returns false if format is invalid + */ + bool isTizenWebApp() const; + bool parseVersionString(const std::string &version, long &majorVersion, + long &minorVersion, long µVersion) const; + + bool fillWidgetConfig(WrtDB::WidgetRegisterInfo& pWidgetConfigInfo, + WrtDB::ConfigParserData& configInfo); + + public: + TaskProcessConfig(InstallerContext& installTaskContext); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_PROCESS_CONFIG_H diff --git a/src_wearable/jobs/widget_install/task_recovery.cpp b/src_wearable/jobs/widget_install/task_recovery.cpp new file mode 100644 index 0000000..fb2b72a --- /dev/null +++ b/src_wearable/jobs/widget_install/task_recovery.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_recovery.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task recovery + */ +#include "task_recovery.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const std::string BACKUP_ID = ".backup"; +} + +namespace Jobs { +namespace WidgetInstall { +TaskRecovery::TaskRecovery(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRecovery::StartStep); + AddStep(&TaskRecovery::StepRecoveryDirectory); + AddStep(&TaskRecovery::StepRecoveryDatabase); + AddStep(&TaskRecovery::EndStep); +} + +void TaskRecovery::StepRecoveryDirectory() +{ + _D("StepRecoveryDirectory ..."); + // check backup folder + DPL::Utils::Path installedPath(WrtDB::GlobalConfig::GetUserInstalledWidgetPath()); + installedPath /= m_context.widgetConfig.tzPkgid; + + DPL::Utils::Path backupPath(WrtDB::GlobalConfig::GetUserInstalledWidgetPath()); + backupPath /= DPL::ToUTF8String(m_context.widgetConfig.tzPkgid) + BACKUP_ID; + + _D("installedPath : %s", installedPath.Fullpath().c_str()); + _D("backupPath : %s", backupPath.Fullpath().c_str()); + + if (backupPath.Exists()) { + DPL::Utils::TryRemove(installedPath); + + DPL::Utils::Rename(backupPath, installedPath); + } else { + ThrowMsg(Exceptions::RecoveryFailed, "backup folder doesn't exist"); + } +} + +void TaskRecovery::StepRecoveryDatabase() +{ + _D("StepRecoveryDatabase ... %s", m_context.widgetConfig.tzPkgid.c_str()); + Try { + std::string backupId, deleteId; + + TizenAppId dbAppId = WidgetDAOReadOnly::getTizenAppId(m_context.widgetConfig.tzPkgid); + _D("Get appid : %ls", dbAppId.c_str()); + std::string appId = DPL::ToUTF8String(dbAppId); + + if (0 == appId.compare(appId.size() - BACKUP_ID.length(), BACKUP_ID.length(), BACKUP_ID)) { + backupId = appId; + deleteId = backupId.substr(0, backupId.length() - + BACKUP_ID.length()); + } else { + backupId = appId + BACKUP_ID; + deleteId = appId; + } + + if (WrtDB::WidgetDAOReadOnly::isWidgetInstalled(DPL::FromUTF8String(backupId))) { + _D("Recovery Database..."); + _D("backupId %s " , backupId.c_str()); + _D("deleteId %s " , deleteId.c_str()); + + // remove ace + ace_unregister_widget(static_cast( + WidgetDAOReadOnly::getHandle(DPL:: + FromUTF8String(deleteId)))); + ace_unregister_widget(static_cast( + WidgetDAOReadOnly::getHandle(DPL:: + FromUTF8String(backupId)))); + + WidgetDAO::unregisterWidget(DPL::FromUTF8String(deleteId)); + WidgetDAO::updateTizenAppId(DPL::FromUTF8String(backupId), + DPL::FromUTF8String(deleteId)); + + if(!AceApi::registerAceWidgetFromDB(WidgetDAOReadOnly::getHandle( + DPL::FromUTF8String(deleteId)))) { + _E("ace database restore failed"); + } + } + + WidgetDAOReadOnly dao(DPL::FromUTF8String(deleteId)); + m_context.requestedPath = + DPL::ToUTF8String(*dao.getWidgetInstalledPath()); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + ThrowMsg(Exceptions::RecoveryFailed, "[WidgetNotExist] Failure in recovery db"); + } + Catch(WidgetDAO::Exception::DatabaseError) + { + ThrowMsg(Exceptions::RecoveryFailed, "[DatabaseError] Failure in recovery db"); + } +} + +void TaskRecovery::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskRecovery::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace RecoveryInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_recovery.h b/src_wearable/jobs/widget_install/task_recovery.h new file mode 100644 index 0000000..92a9d79 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_recovery.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_recovery.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ + +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskRecovery : public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StartStep(); + void EndStep(); + void StepRecoveryDirectory(); + void StepRecoveryDatabase(); + + public: + explicit TaskRecovery(InstallerContext &context); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_RECOVERY_FILES_H_ */ diff --git a/src_wearable/jobs/widget_install/task_remove_backup.cpp b/src_wearable/jobs/widget_install/task_remove_backup.cpp new file mode 100755 index 0000000..d691041 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_remove_backup.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_backup.cpp + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task backup files remove + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetInstall { +TaskRemoveBackupFiles::TaskRemoveBackupFiles(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveBackupFiles::StartStep); + if (m_context.mode.extension != InstallMode::ExtensionType::DIR) + { + AddStep(&TaskRemoveBackupFiles::StepRemoveBackupFiles); + } + AddStep(&TaskRemoveBackupFiles::StepDeleteBackupDB); + AddStep(&TaskRemoveBackupFiles::StepDeleteBackupWidgetInterfaceDB); + AddStep(&TaskRemoveBackupFiles::EndStep); +} + +void TaskRemoveBackupFiles::StepRemoveBackupFiles() +{ + std::ostringstream backupDir; + backupDir << m_context.locations->getBackupDir(); + + if (WrtUtilRemove(backupDir.str())) { + _D("Success to remove backup files : %s", backupDir.str().c_str()); + } else { + _E("Failed to remove backup directory : %s", backupDir.str().c_str()); + ThrowMsg(Exceptions::RemoveBackupFailed, + "Error occurs during removing existing folder"); + } + + std::string tmp = m_context.locations->getTemporaryPackageDir(); + if (WrtUtilRemove(tmp)) { + _D("Success to remove temp directory : %s", tmp.c_str()); + } else { + _E("Failed to remove temp directory : %s", tmp.c_str()); + } +} + +void TaskRemoveBackupFiles::StepDeleteBackupDB() +{ + _D("StepDeleteBackupDB"); + std::list idList = WidgetDAOReadOnly::getTzAppIdList(m_context.widgetConfig.tzPkgid); + FOREACH(it, idList){ + Try + { + DPL::String suffix = L".backup"; + if( it->size() >= suffix.size() && it->compare(it->size() - suffix.size() , suffix.size(), suffix) == 0) + WidgetDAO::unregisterWidget(*it); + } + Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) + { + _E("Fail to delete old version db information"); + } + } +} + +void TaskRemoveBackupFiles::StepDeleteBackupWidgetInterfaceDB() +{ + _D("StepDeleteBackupWidgetInterfaceDB"); + using namespace WidgetInterfaceDB; + using namespace WrtDB; + + DbWidgetHandle handle = + WidgetDAOReadOnly::getHandle(m_context.widgetConfig.tzAppid); + std::string backupDbPath = WidgetInterfaceDAO::databaseFileName(handle); + backupDbPath += GlobalConfig::GetBackupDatabaseSuffix(); + + // remove backup database + if (remove(backupDbPath.c_str()) != 0) { + _W("Fail to remove"); + } +} + +void TaskRemoveBackupFiles::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskRemoveBackupFiles::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_remove_backup.h b/src_wearable/jobs/widget_install/task_remove_backup.h new file mode 100644 index 0000000..1dcdf5b --- /dev/null +++ b/src_wearable/jobs/widget_install/task_remove_backup.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_backup.h + * @author Soyoung kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task backup files remove + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskRemoveBackupFiles : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepRemoveBackupFiles(); + void StepDeleteBackupDB(); + void StepDeleteBackupWidgetInterfaceDB(); + + void StartStep(); + void EndStep(); + + public: + TaskRemoveBackupFiles(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif // INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_REMOVE_BACKUP_FILES_H diff --git a/src_wearable/jobs/widget_install/task_smack.cpp b/src_wearable/jobs/widget_install/task_smack.cpp new file mode 100644 index 0000000..5076d14 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_smack.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.cpp + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task smack + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; +using namespace ValidationCore; + +namespace { +const int MAX_BUF_SIZE = 128; +void freeList(const char** list) { + for (int i = 0; list[i] != NULL; i++) + { + delete(list[i]); + } + delete[] list; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskSmack::TaskSmack(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskSmack::StartStep); + AddStep(&TaskSmack::StepSetInstall); + AddStep(&TaskSmack::StepSmackFolderLabeling); + AddStep(&TaskSmack::StepSmackPrivilege); + AddStep(&TaskSmack::StepAddLabelNPRuntime); + AddStep(&TaskSmack::StepLabelSignatureFiles); + AddStep(&TaskSmack::EndStep); + + AddAbortStep(&TaskSmack::StepAbortSmack); +} + +void TaskSmack::StepSetInstall() +{ + _D("----------------> SMACK: StepStartSetSmack()"); + + m_pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + if (PC_OPERATION_SUCCESS != perm_app_install(m_pkgId.c_str())) { + ThrowMsg(Exceptions::NotAllowed, "Instalation failure. " + "failure in creating smack rules file."); + } +} + +void TaskSmack::StepSmackFolderLabeling() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::SmackFolderLabelingStep()"); + + /* /opt/usr/apps/[pkgid] directory's label is "_" */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + m_context.locations->getPackageInstallationDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getPackageInstallationDir().c_str()); + } + + /* for prelaod */ + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD) { + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + m_context.locations->getUserDataRootDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + } + } + + /* res directory */ + std::string resDir = m_context.locations->getPackageInstallationDir() + + "/res"; + + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), resDir.c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", resDir.c_str()); + } + + /* data directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + m_context.locations->getPrivateStorageDir().c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", m_context.locations->getPrivateStorageDir().c_str()); + } + + /* tmp directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + m_context.locations->getPrivateTempStorageDir().c_str(), + APP_PATH_PRIVATE)) + { + _W("Add label to %s", m_context.locations->getPrivateTempStorageDir().c_str()); + } + + /* bin directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + m_context.locations->getBinaryDir().c_str(), + APP_PATH_PRIVATE)) { + _W("Add label to %s", m_context.locations->getBinaryDir().c_str()); + } + + if(!setLabelForSharedDir(m_pkgId.c_str())) { + _W("Add label to shared directory"); + } + + /* TODO : set label at wrt-client */ +} + +void TaskSmack::StepSmackPrivilege() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::SmackPrivilegeStep()"); + + std::string id = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + char* appId = NULL; + appId = (char*)calloc(1, id.length() + 1); + snprintf(appId, id.length() + 1, "%s", id.c_str()); + + WrtDB::ConfigParserData::PrivilegeList privileges = + m_context.widgetConfig.configInfo.privilegeList; + + char** perm_list = new char*[privileges.size() + 1]; + int index = 0; + FOREACH(it, privileges) { + _D("Permission : %ls", it->name.c_str()); + int length = DPL::ToUTF8String(it->name).length(); + char *priv = new char[length + 1]; + snprintf(priv, length + 1, "%s", + DPL::ToUTF8String(it->name).c_str()); + perm_list[index++] = priv; + } + perm_list[index] = NULL; + + if (PC_OPERATION_SUCCESS != perm_app_enable_permissions(appId, APP_TYPE_WGT, + const_cast(perm_list), true)) { + _W("failure in contructing smack rules based on perm_list"); + } + + free(appId); + index = 0; + while (NULL != perm_list[index]) { + delete [] perm_list[index++]; + } + delete [] perm_list; + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_SMACK_ENABLE, + "Widget SMACK Enabled"); +} + +void TaskSmack::StepAddLabelNPRuntime() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepAddLabelNPRuntime()"); + + if (0 == access(m_context.locations->getNPPluginsDir().c_str(), F_OK)) { + if (PC_OPERATION_SUCCESS != + perm_app_setup_path(DPL::ToUTF8String(m_context.widgetConfig.tzPkgid).c_str(), + m_context.locations->getNPPluginsExecFile().c_str(), + PERM_APP_PATH_NPRUNTIME)) { + _E("failed to set smack execute label to %s", + m_context.locations->getNPPluginsExecFile().c_str()); + } + } +} + + +void TaskSmack::StepLabelSignatureFiles() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepLabelSignatureFiles()"); + + DPL::Utils::Path widgetPath{ + m_context.locations->getPackageInstallationDir()}; + widgetPath /= WrtDB::GlobalConfig::GetWidgetSrcPath(); + + SignatureFileInfoSet signatureFiles; + SignatureFinder signatureFinder(widgetPath.Fullpath()); + if (SignatureFinder::NO_ERROR != signatureFinder.find(signatureFiles)) { + ThrowMsg(Exceptions::SignatureNotFound, + "Error while discovering signature files."); + } + + for (auto it = signatureFiles.cbegin(); it != signatureFiles.cend(); ++it) { + auto sigPath = widgetPath / it->getFileName(); + + _D("Setting label to %s", sigPath.Fullpath().c_str()); + if (PC_OPERATION_SUCCESS != perm_app_setup_path(m_pkgId.c_str(), + sigPath.Fullpath().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Failed to set label to %s", sigPath.Fullpath().c_str()); + } + } +} + +void TaskSmack::StepRevokeForUpdate() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepRevokePrivilegeForUpdate()"); + + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(m_pkgId.c_str())) { + _W("failure in revoking smack permissions"); + } +} + +void TaskSmack::StepAbortSmack() +{ + _D("----------------> SMACK:\ + Jobs::WidgetInstall::TaskSmack::StepAbortSmack()"); + + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(m_pkgId.c_str())) { + _W("failure in revoking smack permissions"); + } + + if (PC_OPERATION_SUCCESS != perm_app_uninstall(m_pkgId.c_str())) { + _W("failure in removing smack rules file"); + } +} + +bool TaskSmack::setLabelForSharedDir(const char* pkgId) +{ + /* /shared directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedRootDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getUserDataRootDir().c_str()); + } + + /* /shared/res directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedResourceDir().c_str(), + APP_PATH_ANY_LABEL, "_")) { + _W("Add label to %s", m_context.locations->getSharedResourceDir().c_str()); + } + + /* /shared/trusted directory */ + CertificatePtr rootCert = m_context.widgetSecurity.getAuthorCertificatePtr(); + if (!!rootCert) { + ValidationCore::Crypto::Hash::SHA1 sha1; + sha1.Append(rootCert->getDER()); + sha1.Finish(); + std::string sha1String = sha1.ToBase64String(); + size_t iPos = sha1String.find("/"); + while(iPos < std::string::npos) { + sha1String.replace(iPos, 1, "#"); + iPos = sha1String.find("/"); + } + + _D("sha1 label string : %s", sha1String.c_str()); + + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedTrustedDir().c_str(), + APP_PATH_GROUP_RW, sha1String.c_str())) { + _W("Add label to %s", m_context.locations->getBinaryDir().c_str()); + } + } + + /* /shared/data directory */ + if (PC_OPERATION_SUCCESS != perm_app_setup_path(pkgId, + m_context.locations->getSharedDataDir().c_str(), + APP_PATH_PUBLIC_RO)) { + _W("Add label to %s", m_context.locations->getSharedDataDir().c_str()); + } + + return true; +} + +void TaskSmack::StartStep() +{ + LOGD("--------- : START ----------"); + if (PC_OPERATION_SUCCESS != perm_begin()) { + LOGE("Failed to smack transaction begin."); + ThrowMsg(Exceptions::SmackTransactionFailed, "Failed to smack transaction begin"); + } +} + +void TaskSmack::EndStep() +{ + LOGD("--------- : END ----------"); + if (PC_OPERATION_SUCCESS != perm_end()) { + LOGE("Failed to smack transaction end."); + ThrowMsg(Exceptions::SmackTransactionFailed, "Failed to smack transaction end"); + } +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_smack.h b/src_wearable/jobs/widget_install/task_smack.h new file mode 100644 index 0000000..2c7b54f --- /dev/null +++ b/src_wearable/jobs/widget_install/task_smack.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.h + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Header file for installer task smack + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskSmack : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + std::string m_pkgId; + + void StepSetInstall(); + void StepSmackFolderLabeling(); + void StepSmackPrivilege(); + void StepAddLabelNPRuntime(); + void StepLabelSignatureFiles(); + void StepRevokeForUpdate(); + void StepAbortSmack(); + + bool setLabelForSharedDir(const char* pkgId); + + void StartStep(); + void EndStep(); + + public: + TaskSmack(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_SMACK_H */ diff --git a/src_wearable/jobs/widget_install/task_status_check.cpp b/src_wearable/jobs/widget_install/task_status_check.cpp new file mode 100644 index 0000000..6a60cff --- /dev/null +++ b/src_wearable/jobs/widget_install/task_status_check.cpp @@ -0,0 +1,159 @@ + /* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_status_check.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task install osp service + */ +#include "task_status_check.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Jobs { +namespace WidgetInstall { +TaskStatusCheck::TaskStatusCheck(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskStatusCheck::StartStep); + AddStep(&TaskStatusCheck::CheckAppRunningStateStep); + AddStep(&TaskStatusCheck::DynamicBoxesRemoveStep); + AddStep(&TaskStatusCheck::EndStep); +} + +void TaskStatusCheck::CheckAppRunningStateStep() +{ + _D("Step: CheckAppRunningStateStep"); + std::string webAppPkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + + int ret = 0; + pkgmgrinfo_pkginfo_h handle; + ret = pkgmgrinfo_pkginfo_get_pkginfo(webAppPkgId.c_str(), &handle); + if (ret != PMINFO_R_OK) { + _E("Can't find [%s] in package manager", webAppPkgId.c_str()); + ThrowMsg(Jobs::WidgetInstall::Exceptions::GetInfoPkgMgrFailed, + "package id can't find from package manager"); + } + ret = pkgmgrinfo_appinfo_get_list(handle, PMINFO_ALL_APP, + terminateRunningApp, NULL); + if (ret != PMINFO_R_OK) { + pkgmgrinfo_pkginfo_destroy_pkginfo(handle); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + pkgmgrinfo_pkginfo_destroy_pkginfo(handle); +} + +int TaskStatusCheck::terminateRunningApp(pkgmgrinfo_appinfo_h handle, + void* /*user_data*/) +{ + char *appId = NULL; + pkgmgrinfo_appinfo_get_appid(handle, &appId); + _D("# Terminating App : [%s]", appId); + + bool isRunning = false; + int ret = app_manager_is_running(appId, &isRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + + if (true == isRunning) { + // get app_context for running application + // app_context must be released with app_context_destroy + app_context_h appCtx = NULL; + ret = + app_manager_get_app_context(appId, &appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get app_context"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + + // terminate app_context_h + ret = app_manager_terminate_app(appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to terminate running application"); + app_context_destroy(appCtx); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } else { + app_context_destroy(appCtx); + // app_manager_terminate_app isn't sync API + // wait until application isn't running (50ms * 100) + bool isStillRunning = true; + int checkingloop = 100; + struct timespec duration = { 0, 50 * 1000 * 1000 }; + while (--checkingloop >= 0) { + nanosleep(&duration, NULL); + int ret = app_manager_is_running(appId, + &isStillRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + if (!isStillRunning) { + break; + } + } + if (isStillRunning) { + _E("Fail to terminate running application"); + ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError, + "widget is running"); + } + _D("terminate application"); + } + } + return 0; +} + +void TaskStatusCheck::DynamicBoxesRemoveStep() +{ + _D("Step: DynamicBoxesRemoveStep"); + + // check if this webapp has dynaimc boxes, and request to remove them + web_provider_service_wait_boxes_removed( + DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str()); + + _D("Finished to handle this web app's dynamic box"); +} + +void TaskStatusCheck::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskStatusCheck::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_status_check.h b/src_wearable/jobs/widget_install/task_status_check.h new file mode 100644 index 0000000..3ae5a95 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_status_check.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_status_check.h + * @author soyoung kim (sy037.kim@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_TASK_STATUS_CHECK_H_ +#define SRC_JOBS_WIDGET_INSTALL_TASK_STATUS_CHECK_H_ + +#include +#include +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskStatusCheck : public DPL::TaskDecl +{ + private: + // Installation context + InstallerContext &m_context; + + void CheckAppRunningStateStep(); + void DynamicBoxesRemoveStep(); + + void StartStep(); + void EndStep(); + + static int terminateRunningApp(pkgmgrinfo_appinfo_h handle, void *user_data); + + public: + explicit TaskStatusCheck(InstallerContext &installerContext); +}; +} // namespace WidgetInstall +} // namespace Jobs +#endif /* SRC_JOBS_WIDGET_INSTALL_TASK_STATUS_CHECK_H_ */ diff --git a/src_wearable/jobs/widget_install/task_update_files.cpp b/src_wearable/jobs/widget_install/task_update_files.cpp new file mode 100644 index 0000000..95d5d96 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_update_files.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_update_files.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task update files + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace WrtDB; + +namespace { +inline const char* GetWidgetBackupDirPath() +{ + return "backup"; +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskUpdateFiles::TaskUpdateFiles(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUpdateFiles::StartStep); + AddStep(&TaskUpdateFiles::StepBackupDirectory); + AddStep(&TaskUpdateFiles::EndStep); + + AddAbortStep(&TaskUpdateFiles::StepAbortBackupDirectory); +} + +void TaskUpdateFiles::StepBackupDirectory() +{ + _D("StepCreateBackupFolder"); + + Try { + std::string pkgid = + DPL::ToUTF8String(m_context.widgetConfig.tzPkgid); + if (APP2EXT_SD_CARD == app2ext_get_app_location(pkgid.c_str())) { + _D("Installed external directory"); + WidgetInstallToExtSingleton::Instance().initialize(pkgid); + WidgetInstallToExtSingleton::Instance().disable(); + } + } Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) { + _E("Failed disable app2sd"); + ReThrowMsg(Exceptions::BackupFailed, "Error occurs during disable app2sd"); + } + + std::string backPath = m_context.locations->getBackupDir(); + _D("Backup resource directory path : %s", backPath.c_str()); + std::string pkgPath = m_context.locations->getPackageInstallationDir(); + + if (0 == access(backPath.c_str(), F_OK)) { + if (!WrtUtilRemove(backPath)) { + ThrowMsg(Exceptions::RemovingFolderFailure, + "Error occurs during removing backup resource directory"); + } + } + _D("copy : %s to %s", pkgPath.c_str(), backPath.c_str()); + if ((rename(pkgPath.c_str(), backPath.c_str())) != 0) { + _E("Failed to rename %s to %s", pkgPath.c_str(), backPath.c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs during rename file"); + } + + WrtUtilMakeDir(pkgPath); +} + +void TaskUpdateFiles::StepAbortBackupDirectory() +{ + _D("StepAbortCopyFiles"); + + std::string backPath = m_context.locations->getBackupDir(); + std::string pkgPath = m_context.locations->getPackageInstallationDir(); + + _D("Backup Folder %s to %s", backPath.c_str(), pkgPath.c_str()); + + if (!WrtUtilRemove(pkgPath)) { + _E("Failed to remove %s", pkgPath.c_str()); + } + + if (rename(backPath.c_str(), pkgPath.c_str()) != 0) { + _E("Failed to rename %s to %s", backPath.c_str(), pkgPath.c_str()); + } +} + +void TaskUpdateFiles::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskUpdateFiles::EndStep() +{ + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_BACKUP_DIR, + "Backup directory created for update"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_update_files.h b/src_wearable/jobs/widget_install/task_update_files.h new file mode 100644 index 0000000..1d02e0e --- /dev/null +++ b/src_wearable/jobs/widget_install/task_update_files.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_update_files.h + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for installer task update files + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H +#define INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H + +#include +#include +#include + +class InstallerContext; + +namespace { +typedef std::set ExistFileList; +} + +namespace Jobs { +namespace WidgetInstall { +class TaskUpdateFiles : + public DPL::TaskDecl +{ + private: + InstallerContext& m_context; + + void StepBackupDirectory(); + + void StepAbortBackupDirectory(); + + void StartStep(); + void EndStep(); + + public: + TaskUpdateFiles(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_INSTALL_TASK_UPDATE_FILES_H */ diff --git a/src_wearable/jobs/widget_install/task_user_data_manipulation.cpp b/src_wearable/jobs/widget_install/task_user_data_manipulation.cpp new file mode 100644 index 0000000..43f29c3 --- /dev/null +++ b/src_wearable/jobs/widget_install/task_user_data_manipulation.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_user_data_manipulation.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task user data folder + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WEBAPP_DEFAULT_UID 5000 +#define WEBAPP_DEFAULT_GID 5000 + +namespace { +const mode_t PRIVATE_STORAGE_MODE = 0700; +const mode_t SHARED_STORAGE_MODE = 0755; +} + +using namespace WrtDB; + +namespace { +void changeOwnerForDirectory(std::string storagePath, mode_t mode) { + if (euidaccess(storagePath.c_str(), F_OK) != 0) { + if (!WrtUtilMakeDir(storagePath, mode)) { + _E("Failed to create directory : %s", storagePath.c_str()); + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Failed to create directory : " << storagePath); + } + // '5000' is default uid, gid for applications. + // So installed applications should be launched as process of uid + // '5000'. + // the process can access private directory 'data' of itself. + if (chown(storagePath.c_str(), + WEBAPP_DEFAULT_UID, + WEBAPP_DEFAULT_GID) != 0) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Chown to invaild user"); + } + } else if (euidaccess(storagePath.c_str(), W_OK | R_OK | X_OK) == 0) { + _D("%s already exists.", storagePath.c_str()); + // Even if private directory already is created, private dircetory + // should change owner (recursively). + if (chown(storagePath.c_str(), + WEBAPP_DEFAULT_UID, + WEBAPP_DEFAULT_GID) != 0) + { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "Chown to invaild user"); + } + if (chmod(storagePath.c_str(), mode) != 0) { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "chmod to 0700"); + } + } else { + ThrowMsg(Jobs::WidgetInstall::Exceptions::FileOperationFailed, + "No access to private storage."); + } +} +} + +namespace Jobs { +namespace WidgetInstall { +TaskUserDataManipulation::TaskUserDataManipulation(InstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUserDataManipulation::StartStep); + AddStep(&TaskUserDataManipulation::StepCreatePrivateStorageDir); + AddStep(&TaskUserDataManipulation::StepCreateSharedFolder); + AddStep(&TaskUserDataManipulation::StepLinkForPreload); + AddStep(&TaskUserDataManipulation::EndStep); +} + +void TaskUserDataManipulation::StepCreatePrivateStorageDir() +{ + + if (m_context.mode.installTime == InstallMode::InstallTime::FOTA + && m_context.isUpdateMode) + { + return; + } + + if (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD + || m_context.mode.installTime == InstallMode::InstallTime::FOTA) { + std::string userWidgetDir = m_context.locations->getUserDataRootDir(); + _D("Create user data directory : %s", userWidgetDir.c_str()); + WrtUtilMakeDir(userWidgetDir); + } + std::string storagePath = m_context.locations->getPrivateStorageDir(); + _D("Create private storage directory : %s", m_context.locations->getPrivateStorageDir().c_str()); + + changeOwnerForDirectory(storagePath, PRIVATE_STORAGE_MODE); + + if (m_context.isUpdateMode) { //update + std::string backData = m_context.locations->getBackupPrivateDir(); + _D("copy private storage %s to %s", backData.c_str(), storagePath.c_str()); + if (!DirectoryApi::DirectoryCopy(backData, storagePath)) { + _E("Failed to rename %s to %s", backData.c_str(), storagePath.c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy private strage files"); + } + } + + std::string tempStoragePath = m_context.locations->getPrivateTempStorageDir(); + _D("Create temp private storage directory : %s", tempStoragePath.c_str()); + changeOwnerForDirectory(tempStoragePath, PRIVATE_STORAGE_MODE); + + m_context.job->UpdateProgress( + InstallerContext::INSTALL_CREATE_PRIVATE_STORAGE, + "Create private storage for user"); +} + +void TaskUserDataManipulation::StepLinkForPreload() +{ + if (m_context.mode.rootPath == InstallMode::RootPath::RO) { + std::string optRes = m_context.locations->getUserDataRootDir() + + WrtDB::GlobalConfig::GetWidgetResPath(); + std::string usrRes = m_context.locations->getPackageInstallationDir() + + WrtDB::GlobalConfig::GetWidgetResPath(); + + std::string oldRes = optRes + ".old"; + + // Rename path if already exist. + if (0 == access(optRes.c_str(), F_OK)) { + if (-1 == rename(optRes.c_str(), oldRes.c_str())) { + _E("Failed To rename %s -> %s", optRes.c_str(), oldRes.c_str()); + } + } + + if (0 != access(optRes.c_str(), F_OK)) { + _D("Make symbolic name for preload app %s to %s", usrRes.c_str(), optRes.c_str()); + + if (symlink(usrRes.c_str(), optRes.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + } + + /* link for data directory */ + std::string storagePath = m_context.locations->getPrivateStorageDir(); + std::string dataDir = m_context.locations->getPackageInstallationDir() + + "/" + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); + if (0 != access(dataDir.c_str(), F_OK)) { + _D("Make symbolic name for preload app %s to %s", storagePath.c_str(), dataDir.c_str()); + + if (symlink(storagePath.c_str(), dataDir.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + changeOwnerForDirectory(dataDir, PRIVATE_STORAGE_MODE); + } + + if (m_context.widgetConfig.packagingType != PKG_TYPE_HYBRID_WEB_APP) { + std::string widgetBinPath = m_context.locations->getBinaryDir(); + std::string userBinPath = m_context.locations->getUserBinaryDir(); + std::string oldBinPath = userBinPath + ".old"; + + // Rename path if already exist. + if (0 == access(userBinPath.c_str(), F_OK)) { + if (-1 == rename(userBinPath.c_str(), oldBinPath.c_str())) { + _E("Failed To rename %s -> %s", userBinPath.c_str(), oldBinPath.c_str()); + } + } + + _D("Make symbolic link for preload app %s to %s", widgetBinPath.c_str(), userBinPath.c_str()); + if (symlink(widgetBinPath.c_str(), userBinPath.c_str()) != 0) + { + int error = errno; + if (error) + _E("Failed to make a symbolic name for a file [%s]", (DPL::GetErrnoString(error)).c_str()); + ThrowMsg(Exceptions::FileOperationFailed, + "Symbolic link creating is not done."); + } + + } + } +} + +void TaskUserDataManipulation::StepCreateSharedFolder() +{ + if (m_context.mode.installTime == InstallMode::InstallTime::FOTA + && m_context.isUpdateMode) + { + return; + } + + _D("StepCreateSharedFolder"); + std::string sharedPath = m_context.locations->getSharedRootDir(); + _D("Create shared directory : %s", m_context.locations->getSharedRootDir().c_str()); + + WrtUtilMakeDir(sharedPath); + WrtUtilMakeDir(m_context.locations->getSharedResourceDir()); + + changeOwnerForDirectory(m_context.locations->getSharedDataDir(), + SHARED_STORAGE_MODE); + changeOwnerForDirectory(m_context.locations->getSharedTrustedDir(), + SHARED_STORAGE_MODE); + + + // additional check for rootPath installation + // If this app is preloaded, "shared" diretory is already on place and do not needs to be moved + // TODO: why "shared" is on RW partion always but "data" and "tmp" are linked + if (m_context.isUpdateMode + && !(m_context.mode.rootPath == InstallMode::RootPath::RO + && m_context.mode.installTime == InstallMode::InstallTime::PRELOAD)) { + + /* Restore /shared/data */ + _D("copy %s to %s", m_context.locations->getBackupSharedDataDir().c_str(), m_context.locations->getSharedDataDir().c_str()); + if (!DirectoryApi::DirectoryCopy( + m_context.locations->getBackupSharedDataDir(), + m_context.locations->getSharedDataDir())) { + _E("Failed to rename %s to %s", m_context.locations->getBackupSharedDataDir().c_str(), m_context.locations->getSharedDataDir().c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy shared strage files"); + } + + /* Restore /shared/trusted */ + _D("copy %s to %s", m_context.locations->getBackupSharedTrustedDir().c_str(), m_context.locations->getSharedTrustedDir().c_str()); + if (!DirectoryApi::DirectoryCopy( + m_context.locations->getBackupSharedTrustedDir(), + m_context.locations->getSharedTrustedDir())) { + _E("Failed to rename %s to %s", m_context.locations->getBackupSharedTrustedDir().c_str(), m_context.locations->getSharedTrustedDir().c_str()); + ThrowMsg(Exceptions::BackupFailed, + "Error occurs copy shared strage files"); + } + } +} + +void TaskUserDataManipulation::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskUserDataManipulation::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/task_user_data_manipulation.h b/src_wearable/jobs/widget_install/task_user_data_manipulation.h new file mode 100644 index 0000000..fbfabcc --- /dev/null +++ b/src_wearable/jobs/widget_install/task_user_data_manipulation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_user_data_manipulation.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task user data folder + */ +#ifndef JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H +#define JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H + +#include + +class InstallerContext; + +namespace Jobs { +namespace WidgetInstall { +class TaskUserDataManipulation : + public DPL::TaskDecl +{ + InstallerContext& m_context; + + void StartStep(); + void EndStep(); + void StepCreatePrivateStorageDir(); + void StepCreateSharedFolder(); + void StepLinkForPreload(); + + public: + TaskUserDataManipulation(InstallerContext& context); +}; +} //namespace WidgetInstall +} //namespace Jobs + +#endif //JOS_WIDGET_INSTALL_TASK_USER_DATA_MANIPULATION_H diff --git a/src_wearable/jobs/widget_install/view_mode.h b/src_wearable/jobs/widget_install/view_mode.h new file mode 100644 index 0000000..a4acc39 --- /dev/null +++ b/src_wearable/jobs/widget_install/view_mode.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file view_mode.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ +#define SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ + +namespace Jobs { +namespace WidgetInstall { +enum ViewMode +{ + WINDOWED = 0, + FLOATING, + FULLSCREEN, + MAXIMIZED, + MINIMIZED +}; +} // WidgetInstall +} // Jobs + +#endif /* SRC_JOBS_WIDGET_INSTALL_VIEW_MODE_H_ */ diff --git a/src_wearable/jobs/widget_install/widget_install_context.h b/src_wearable/jobs/widget_install/widget_install_context.h new file mode 100644 index 0000000..1e7e86d --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_install_context.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_structs.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief Definition file of installer tasks data structures + */ +#ifndef INSTALLER_CONTEXT_H +#define INSTALLER_CONTEXT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class JobWidgetInstall; +} //namespace Jobs +} //namespace WidgetInstall + +class WidgetModel; + +typedef std::map RequestedDevCapsMap; + +struct InstallerContext +{ + typedef enum InstallStepEnum + { + INSTALL_START = 0, + INSTALL_PARSE_CONFIG, + INSTALL_CHECK_FILE, + + INSTALL_RDS_DELTA_CHECK, + INSTALL_RDS_PREPARE, + + INSTALL_CREATE_BACKUP_DIR, /* For Update */ + INSTALL_DIR_CREATE, + INSTALL_UNZIP_WGT, + INSTALL_WIDGET_CONFIG1, + INSTALL_WIDGET_CONFIG2, + INSTALL_DIGSIG_CHECK, + INSTALL_CERT_CHECK, + INSTALL_CERTIFY_LEVEL_CHECK, + INSTALL_CREATE_PRIVATE_STORAGE, + INSTALL_BACKUP_ICONFILE, /* For Update */ + INSTALL_COPY_ICONFILE, + INSTALL_COPY_LIVEBOX_FILES, + INSTALL_CREATE_EXECFILE, + INSTALL_CREATE_MANIFEST, + INSTALL_ECRYPTION_FILES, + INSTALL_INSTALL_OSPSVC, + INSTALL_NEW_DB_INSERT, + INSTALL_ACE_PREPARE, + INSTALL_ACE_CHECK, + INSTALL_SMACK_ENABLE, + INSTALL_PKGINFO_UPDATE, + INSTALL_SET_CERTINFO, + + INSTALL_END + } InstallStep; + + // Installation state variables + WrtDB::WidgetRegisterInfo widgetConfig; ///< WidgetConfigInfo + boost::optional locations; + Jobs::WidgetInstall::WidgetSecurity widgetSecurity; ///< Widget Domain + // information. + InstallStep installStep; ///< current step of installation + Jobs::WidgetInstall::JobWidgetInstall *job; + ///< Whether this is an update or normal installation + Jobs::WidgetInstall::FeatureLogicPtr featureLogic; + /** List of dev-caps that are requested in widget config file. + * Additional flag tells whether dev cap gets "static" permission + * (will always have PERMIT from ACE Policy). They will therefore receive + * static SMACK permission. (They may be forbidden because + * of ACE User Settings, but for now we do not protect this + * case with SMACK). */ + RequestedDevCapsMap staticPermittedDevCaps; + std::string installInfo; /// + InstallLocationType locationType; + bool isUpdateMode; + InstallMode mode; + DPL::String callerPkgId; + + std::string requestedPath; ///input path of widget + bool needEncryption; ///for configuring right task if encryption needed + int certLevel; +}; + +#endif // INSTALLER_CONTEXT_H diff --git a/src_wearable/jobs/widget_install/widget_install_errors.h b/src_wearable/jobs/widget_install/widget_install_errors.h new file mode 100755 index 0000000..59f44d5 --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_install_errors.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file installer_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef INSTALLER_ERRORS_H_ +#define INSTALLER_ERRORS_H_ + +#include +#include +#include + +//TODO SafeException(...) + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetInstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) + +DECLARE_JOB_EXCEPTION(Base, OpenZipFailed, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, ZipEmpty, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, ExtractFileFailed, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, EmptyPluginsDirectory, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, PluginsSubdirectory, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, RDSDeltaFailure, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, MissingConfig, ErrorPackageInvalid) +DECLARE_JOB_EXCEPTION(Base, InvalidStartFile, ErrorPackageInvalid) + +DECLARE_JOB_EXCEPTION(Base, PackageLowerVersion, ErrorPackageLowerVersion) + +DECLARE_JOB_EXCEPTION(Base, ManifestInvalid, ErrorManifestInvalid) + +DECLARE_JOB_EXCEPTION(Base, WidgetConfigFileNotFound, ErrorConfigNotFound) +DECLARE_JOB_EXCEPTION(Base, WidgetConfigFileInvalid, ErrorConfigInvalid) + +DECLARE_JOB_EXCEPTION(Base, SignatureNotFound, ErrorSignatureNotFound) + +DECLARE_JOB_EXCEPTION(Base, SignatureInvalid, ErrorSignatureInvalid) + +DECLARE_JOB_EXCEPTION(Base, SignatureVerificationFailed, ErrorSignatureVerificationFailed) + +DECLARE_JOB_EXCEPTION(Base, RootCertificateNotFound, ErrorRootCertificateNotFound) + +DECLARE_JOB_EXCEPTION(Base, CertificationInvaid, ErrorCertificationInvaid) +DECLARE_JOB_EXCEPTION(Base, NotMatchedCertification, ErrorCertificationInvaid) + +DECLARE_JOB_EXCEPTION(Base, CertificateChainVerificationFailed, ErrorCertificateChainVerificationFailed) + +DECLARE_JOB_EXCEPTION(Base, CertificateExpired, ErrorCertificateExpired) + +DECLARE_JOB_EXCEPTION(Base, NotAllowed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, WidgetRunningError, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, DrmDecryptFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, DatabaseFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemovingFolderFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemovingFileFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, CreateVconfFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, CopyIconFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, FileOperationFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, InstallToExternalFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, BackupFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, InsertNewWidgetFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, RemoveBackupFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, UpdateFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, SetCertificateInfoFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, ErrorExternalInstallingFailure, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, GetInfoPkgMgrFailed, ErrorFatalError) + +DECLARE_JOB_EXCEPTION(Base, PackageAlreadyInstalled, ErrorPackageAlreadyInstalled) +DECLARE_JOB_EXCEPTION(Base, AceCheckFailed, ErrorAceCheckFailed) +DECLARE_JOB_EXCEPTION(Base, EncryptionFailed, ErrorEncryptionFailed) +DECLARE_JOB_EXCEPTION(Base, InstallOspsvcFailed, ErrorInstallOspServcie) +DECLARE_JOB_EXCEPTION(Base, PrivilegeLevelViolation, ErrorPrivilegeLevelViolation) +DECLARE_JOB_EXCEPTION(Base, NotSupportRDSUpdate, ErrorNotSupportRDSUpdate) +DECLARE_JOB_EXCEPTION(Base, SmackTransactionFailed, ErrorFatalError) +DECLARE_JOB_EXCEPTION(Base, OutOfStorageFailed, ErrorOutOfStorage) +DECLARE_JOB_EXCEPTION(Base, RecoveryFailed, ErrorFatalError) +} //namespace +} //namespace +} //namespace + +#endif /* INSTALLER_ERRORS_H_ */ diff --git a/src_wearable/jobs/widget_install/widget_installer_struct.h b/src_wearable/jobs/widget_install/widget_installer_struct.h new file mode 100644 index 0000000..1fd865e --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_installer_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_installer_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include + +//Widget Installer typedefs +typedef void (*InstallerFinishedCallback)( + void *userParam, + std::string tizenId, + Jobs::Exceptions::Type); + +typedef void (*InstallerProgressCallback)(void *userParam, + ProgressPercent percent, + const ProgressDescription &); + +namespace Jobs { +namespace WidgetInstall { +//InstallationStruct +typedef Jobs::JobCallbacksBase +WidgetInstallCallbackBase; + +//Widget Installation Struct +struct WidgetInstallationStruct : public WidgetInstallCallbackBase +{ + InstallMode m_installMode; + std::shared_ptr pkgmgrInterface; + + // It must be empty-constructible as a parameter of generic event + WidgetInstallationStruct() {}; + WidgetInstallationStruct( + InstallerFinishedCallback finished, + InstallerProgressCallback progress, + void *param, + InstallMode mode, + std::shared_ptr + _pkgmgrInterface + ) : + WidgetInstallCallbackBase(finished, progress, param), + m_installMode(mode), + pkgmgrInterface(_pkgmgrInterface) + {} +}; +} // namespace WidgetInstall +} // namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_INSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ diff --git a/src_wearable/jobs/widget_install/widget_security.cpp b/src_wearable/jobs/widget_install/widget_security.cpp new file mode 100644 index 0000000..6a6a10f --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_security.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_security.cpp + * @author Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + * @version 1.0 + * @brief + */ + +#include "widget_security.h" +#include +#include + +namespace Jobs { +namespace WidgetInstall { +void WidgetSecurity::getCertificateChainList( + WrtDB::CertificateChainList& list, + WrtDB::CertificateSource source) const +{ + if (source == WrtDB::CertificateSource::SIGNATURE_AUTHOR) { + FOREACH(certIter, mAuthorsCertificateChainList) + list.push_back(certIter->toBase64String()); + } else if (source == WrtDB::CertificateSource::SIGNATURE_DISTRIBUTOR) { + FOREACH(certIter, mCertificateChainList) + list.push_back(certIter->toBase64String()); + } else if (source == WrtDB::CertificateSource::SIGNATURE_DISTRIBUTOR2) { + FOREACH(certIter, mCertificateChainList2) + list.push_back(certIter->toBase64String()); + } else { + _E("UNKNOWN certificate"); + } +} +} // namespace WidgetInstall +} // namespace Jobs diff --git a/src_wearable/jobs/widget_install/widget_security.h b/src_wearable/jobs/widget_install/widget_security.h new file mode 100644 index 0000000..43673be --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_security.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_security.h + * @author Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WACSECURITY_H_ +#define WACSECURITY_H_ + +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class WidgetSecurity : public WrtDB::IWidgetSecurity +{ + public: + WidgetSecurity() : + mRecognized(false), + mDistributorSigned(false) + {} + + // from IWidgetSecurity + virtual const WrtDB::WidgetCertificateDataList& getCertificateList() const + { + return mCertificateList; + } + + virtual bool isRecognized() const + { + return mRecognized; + } + + virtual bool isDistributorSigned() const + { + return mDistributorSigned; + } + + virtual void getCertificateChainList( + WrtDB::CertificateChainList& list, + WrtDB::CertificateSource source) const; + + void setRecognized(bool recognized) + { + mRecognized = recognized; + } + void setDistributorSigned(bool distributorSigned) + { + mDistributorSigned = distributorSigned; + } + void setAuthorCertificatePtr(ValidationCore::CertificatePtr certPtr) + { + mAuthorCertificate = certPtr; + } + + ValidationCore::CertificatePtr getAuthorCertificatePtr() const + { + return mAuthorCertificate; + } + + ValidationCore::CertificateCollectionList& getCertificateChainListRef() + { + return mCertificateChainList; + } + + ValidationCore::CertificateCollectionList& getCertificateChainList2Ref() + { + return mCertificateChainList2; + } + + ValidationCore::CertificateCollectionList& + getAuthorsCertificateChainListRef() + { + return mAuthorsCertificateChainList; + } + + WrtDB::WidgetCertificateDataList& getCertificateListRef() + { + return mCertificateList; + } + + private: + // This data are used to evaluate policy + WrtDB::WidgetCertificateDataList mCertificateList; + + // author signature verified + bool mRecognized; + // known distribuor + bool mDistributorSigned; + // Author end entity certificate. + // Information from this certificate are shown to user + // during installation process. + ValidationCore::CertificatePtr mAuthorCertificate; + // This certificates are used by OCSP/CRL + ValidationCore::CertificateCollectionList mCertificateChainList; + // This certificates are for distributor2 + ValidationCore::CertificateCollectionList mCertificateChainList2; + // This authors certificates are used by tizen + ValidationCore::CertificateCollectionList mAuthorsCertificateChainList; +}; +} // namespace WidgetInstall +} // namespace Jobs + +#endif /* WACSECURITY_H_ */ diff --git a/src_wearable/jobs/widget_install/widget_unzip.cpp b/src_wearable/jobs/widget_install/widget_unzip.cpp new file mode 100644 index 0000000..70831ae --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_unzip.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_unzip.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer widget unzip + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const char *const DRM_LIB_PATH = "/usr/lib/libdrm-service-core-tizen.so"; +const size_t SPACE_SIZE = 1024 * 1024; +const char *const WEB_APP_CONFIG_XML= "config.xml"; +const char *const HYBRID_CONFIG_XML = "res/wgt/config.xml"; + +struct PathAndFilePair +{ + std::string path; + std::string file; + + PathAndFilePair(const std::string &p, + const std::string &f) : + path(p), + file(f) + {} +}; + +PathAndFilePair SplitFileAndPath(const std::string &filePath) +{ + std::string::size_type position = filePath.rfind('/'); + + // Is this only a file without a path ? + if (position == std::string::npos) { + return PathAndFilePair(std::string(), filePath); + } + + // This is full file-path pair + return PathAndFilePair(filePath.substr(0, + position), + filePath.substr(position + 1)); +} +} + +namespace Jobs { +namespace WidgetInstall { + +WidgetUnzip::WidgetUnzip(const std::string &source) +{ + Try { + m_requestFile = getDecryptedPackage(source); + m_zip.reset(new DPL::ZipInput(m_requestFile)); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, source); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, source); + } +} + +void WidgetUnzip::ExtractFile(DPL::ZipInput::File *input, + const std::string &destFileName) +{ + Try + { + DPL::AbstractWaitableInputAdapter inputAdapter(input); + DPL::FileOutput output(destFileName); + + DPL::Copy(&inputAdapter, &output); + } + Catch(DPL::FileOutput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, destFileName); + } + Catch(DPL::CopyFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, destFileName); + } +} + +void WidgetUnzip::unzipProgress(const std::string &destination) +{ + // Show file info + _D("Unzipping: '%s', Comment: '%s', Compressed size: %lld, Uncompressed size: %lld", + m_zipIterator->name.c_str(), m_zipIterator->comment.c_str(), m_zipIterator->compressedSize, m_zipIterator->uncompressedSize); + + // Normalize file paths + // FIXME: Implement checking for invalid characters + + // Extract file or path + std::string fileName = m_zipIterator->name; + + if (fileName[fileName.size() - 1] == '/') { + // This is path + std::string newPath = destination + "/" + + fileName.substr(0, fileName.size() - 1); + _D("Path to extract: %s", newPath.c_str()); + + // Create path in case of it is empty + createTempPath(newPath); + } else { + // This is regular file + std::string fileExtractPath = destination + "/" + fileName; + + _D("File to extract: %s", fileExtractPath.c_str()); + + // Split into pat & file pair + PathAndFilePair pathAndFile = SplitFileAndPath(fileExtractPath); + + _D("Path and file: %s : %s", pathAndFile.path.c_str(), pathAndFile.file.c_str()); + + // First, ensure that path exists + createTempPath(pathAndFile.path); + + Try + { + // Open file + std::unique_ptr file( + m_zip->OpenFile(fileName)); + + // Extract single file + ExtractFile(file.get(), fileExtractPath); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, fileName); + } + } + + // Check whether there are more files to extract + if (++m_zipIterator == m_zip->end()) { + _D("Unzip progress finished successfuly"); + } else { + unzipProgress(destination); + } +} + +bool WidgetUnzip::isDRMPackage(const std::string &source) +{ + _D("Enter : isDRMPackage()"); + int ret = 0; + void* pHandle = NULL; + char* pErrorMsg = NULL; + int (*drm_oem_sapps_is_drm_file)(const char* pDcfPath, int dcfPathLen); + + //TODO: quickfix for platform issues... + if(!DPL::Utils::Path(DRM_LIB_PATH).Exists()) + { + _E("Cannot open %s!", DRM_LIB_PATH); + return false; + } + + pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL); + if (!pHandle) { + _E("dlopen failed : %s [%s]", source.c_str(), dlerror()); + return false; + } + + // clear existing error + dlerror(); + + drm_oem_sapps_is_drm_file = reinterpret_cast + (dlsym(pHandle, "drm_oem_sapps_is_drm_file")); + + if ((pErrorMsg = dlerror()) != NULL) { + _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg); + dlclose(pHandle); + return false; + } + + if (drm_oem_sapps_is_drm_file == NULL) { + _E("drm_oem_sapps_is_drm_file is NULL : %s", source.c_str()); + dlclose(pHandle); + return false; + } + + ret = drm_oem_sapps_is_drm_file(source.c_str(), source.length()); + dlclose(pHandle); + if (1 == ret) { + _D("%s is DRM file", source.c_str()); + return true; + } + _D("%s isn't DRM file", source.c_str()); + return false; +} + +bool WidgetUnzip::decryptDRMPackage(const std::string &source, const std::string + &decryptedSource) +{ + _D("Enter : decryptDRMPackage()"); + int ret = 0; + void* pHandle = NULL; + char* pErrorMsg = NULL; + int (*drm_oem_sapps_decrypt_package)(const char* pDcfPath, int dcfPathLen, + const char* pDecryptedFile, int decryptedFileLen); + + pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL); + if (!pHandle) { + _E("dlopen failed : %s [%s]", source.c_str(), dlerror()); + return false; + } + + // clear existing error + dlerror(); + + drm_oem_sapps_decrypt_package = reinterpret_cast + (dlsym(pHandle, "drm_oem_sapps_decrypt_package")); + + if ((pErrorMsg = dlerror()) != NULL) { + _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg); + dlclose(pHandle); + return false; + } + + if (drm_oem_sapps_decrypt_package == NULL) { + _E("drm_oem_sapps_decrypt_package is NULL : %s", source.c_str()); + dlclose(pHandle); + return false; + } + + ret = drm_oem_sapps_decrypt_package(source.c_str(), source.length(), + decryptedSource.c_str(), decryptedSource.length()); + dlclose(pHandle); + if (1 == ret) { + _D("%s is decrypted : %s", source.c_str(), decryptedSource.c_str()); + return true; + } + return false; +} + +std::string WidgetUnzip::getDecryptedPackage(const std::string &source) +{ + _D("Check DRM..."); + if (isDRMPackage(source)) { + std::string decryptedFile; + size_t found = source.find_last_of(".wgt"); + if (found == std::string::npos) { + decryptedFile += source + "_tmp.wgt"; + } else { + decryptedFile += source.substr(0, source.find_last_not_of(".wgt") + + 1) + "_tmp.wgt"; + } + + _D("decrypted file name : %s", decryptedFile.c_str()); + if (!decryptDRMPackage(source, decryptedFile)) { + _E("Failed decrypt drm file"); + ThrowMsg(Exceptions::DrmDecryptFailed, source); + } + return decryptedFile; + } + return source; +} + +void WidgetUnzip::unzipWgtFile(const std::string &destination) +{ + _D("Prepare to unzip..."); + Try + { + _D("wgtFile : %s", m_requestFile.c_str()); + _D("Widget package comment: %s", m_zip->GetGlobalComment().c_str()); + + // Widget package must not be empty + if (m_zip->empty()) { + ThrowMsg(Exceptions::ZipEmpty, m_requestFile); + } + + // Set iterator to first file + m_zipIterator = m_zip->begin(); + + unzipProgress(destination); + + // Unzip finished, close internal structures + m_zip.reset(); + + // Done + _D("Unzip finished"); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile); + } + Catch(DPL::ZipInput::Exception::SeekFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } +} + +bool WidgetUnzip::checkAvailableSpace(const std::string &destination) +{ + _D("checkAvailableSpace ... "); + + double unCompressedSize = m_zip->GetTotalUncompressedSize(); + _D("unCompressedSize : %ld", unCompressedSize); + + struct statvfs vfs; + if (-1 == statvfs(destination.c_str(), &vfs)) { + _E("There is no space for installation"); + return false; + } + + double freeSize = (double)vfs.f_bsize * vfs.f_bavail; + _D("Space Size : %ld", freeSize); + + if (unCompressedSize + SPACE_SIZE >= freeSize) { + _E("There is no space for installation"); + return false; + } + return true; +} + +void WidgetUnzip::unzipConfiguration(const std::string &destination, + WrtDB::PackagingType* type) +{ + _D("unzipConfiguration"); + + Try { + _D("wgtFile : %s", m_requestFile.c_str()); + + std::unique_ptr configFile; + + Try { + configFile.reset(m_zip->OpenFile(HYBRID_CONFIG_XML)); + *type = PKG_TYPE_HYBRID_WEB_APP; + } Catch(DPL::ZipInput::Exception::OpenFileFailed) { + configFile.reset(m_zip->OpenFile(WEB_APP_CONFIG_XML)); + *type = PKG_TYPE_NOMAL_WEB_APP; + } + + std::string extractPath = destination + "/" + WEB_APP_CONFIG_XML; + ExtractFile(configFile.get(), extractPath); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + ThrowMsg(Exceptions::ExtractFileFailed, "config.xml"); + } + Catch(Exceptions::DrmDecryptFailed) + { + ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile); + } +} + +} //namespace WidgetInstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_install/widget_unzip.h b/src_wearable/jobs/widget_install/widget_unzip.h new file mode 100644 index 0000000..204cde7 --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_unzip.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_unzip.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task unzip + */ +#ifndef WIDGET_UNZIP_H +#define WIDGET_UNZIP_H + +#include + +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class WidgetUnzip +{ + public: + WidgetUnzip(const std::string &source); + void unzipWgtFile(const std::string &destination); + void unzipConfiguration(const std::string &destination, WrtDB::PackagingType *type); + bool checkAvailableSpace(const std::string &destination); + + private: + // Unzip state + std::unique_ptr m_zip; + DPL::ZipInput::const_iterator m_zipIterator; + std::string m_requestFile; + + void unzipProgress(const std::string &destination); + void ExtractFile(DPL::ZipInput::File *input, const std::string + &destFileName); + bool isDRMPackage(const std::string &source); + bool decryptDRMPackage(const std::string &source, const std::string + &decryptedSource); + std::string getDecryptedPackage(const std::string &source); +}; + +} //namespace WidgetInstall +} //namespace Jobs + +#endif // WIDGET_UNZIP_H diff --git a/src_wearable/jobs/widget_install/widget_update_info.cpp b/src_wearable/jobs/widget_install/widget_update_info.cpp new file mode 100644 index 0000000..7c292c7 --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_update_info.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_update_info.cpp + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief Implementation file for WidgetUpdateInfo + */ + +#include "widget_update_info.h" + +WidgetUpdateInfo::WidgetUpdateInfo( + const WrtDB::TizenAppId & appId, + const OptionalWidgetVersion &existingversion, + const OptionalWidgetVersion &incomingversion, + WrtDB::AppType type) : + tzAppId(appId), + existingVersion(existingversion), + incomingVersion(incomingversion), + appType(type) +{ +} + diff --git a/src_wearable/jobs/widget_install/widget_update_info.h b/src_wearable/jobs/widget_install/widget_update_info.h new file mode 100644 index 0000000..2021b42 --- /dev/null +++ b/src_wearable/jobs/widget_install/widget_update_info.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_update_info.h + * @author Chung Jihoon (jihoon.chung@samsung.com) + * @version 1.0 + * @brief Header file for WidgetUpdateInfo + */ +#ifndef SRC_DOMAIN_WIDGET_UPDATE_INFO_H +#define SRC_DOMAIN_WIDGET_UPDATE_INFO_H + +#include +#include + +/** + * WidgetUpdateInfo + * A structure to hold widget's information needed to be registered. + * @see WidgetConfigurationInfo + */ +struct WidgetUpdateInfo +{ + WrtDB::TizenAppId tzAppId; + // Existing widget + OptionalWidgetVersion existingVersion; + // Incoming widget + OptionalWidgetVersion incomingVersion; + // widget type + WrtDB::AppType appType; + + WidgetUpdateInfo() {}; + WidgetUpdateInfo(const WrtDB::TizenAppId & tzAppid, + const OptionalWidgetVersion &existringversion, + const OptionalWidgetVersion &incomingVersion, + const WrtDB::AppType type); +}; + +#endif // SRC_DOMAIN_WIDGET_UPDATE_INFO_H diff --git a/src_wearable/jobs/widget_uninstall/job_widget_uninstall.cpp b/src_wearable/jobs/widget_uninstall/job_widget_uninstall.cpp new file mode 100755 index 0000000..9bb4897 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/job_widget_uninstall.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { //anonymous +const char* REG_TIZEN_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; +const int PKGID_LENTH = 10; +const DPL::Utils::Path PRELOAD_INSTALLED_PATH("/usr/apps"); + +bool checkDirectoryExist(const std::string& pkgId) +{ + DPL::Utils::Path installPath(GlobalConfig::GetUserInstalledWidgetPath()); + installPath /= pkgId; + return installPath.Exists(); +} +} + +namespace Jobs { +namespace WidgetUninstall { + +class UninstallerTaskFail : + public DPL::TaskDecl +{ + private: + WidgetStatus m_status; + + void StepFail() + { + if (WidgetStatus::NOT_INSTALLED == m_status) { + ThrowMsg(Jobs::WidgetUninstall::Exceptions::WidgetNotExist, + "Widget does not exist"); + } else if (WidgetStatus::PREALOAD == m_status) { + ThrowMsg(Jobs::WidgetUninstall::Exceptions::Unremovable, + "Widget cann't uninstall"); + } else { + Throw(Jobs::WidgetUninstall::Exceptions::Base); + } + } + + public: + UninstallerTaskFail(WidgetStatus status) : + DPL::TaskDecl(this), + m_status(status) + + { + AddStep(&UninstallerTaskFail::StepFail); + } +}; + +JobWidgetUninstall::JobWidgetUninstall( + const std::string & tizenPkgId, + const WidgetUninstallationStruct & + uninstallerStruct) : + Job(Uninstallation), + JobContextBase(uninstallerStruct), + m_id(tizenPkgId), + m_exceptionCaught(Jobs::Exceptions::Success) +{ + using namespace PackageManager; + m_context.removeStarted = false; + m_context.removeFinished = false; + m_context.removeAbnormal = false; + m_context.uninstallStep = UninstallerContext::UNINSTALL_START; + m_context.job = this; + + Try + { + WidgetStatus status = getWidgetStatus(tizenPkgId); + + if (WidgetStatus::Ok == status) { + //TODO: check and save type + + WrtDB::WidgetDAOReadOnly dao(*m_context.tzAppIdList.begin()); + m_context.tzPkgid = DPL::ToUTF8String(dao.getTizenPkgId()); + m_context.locations = WidgetLocation(m_context.tzPkgid); + m_context.locations->registerAppid(DPL::ToUTF8String(*m_context.tzAppIdList.begin())); + m_context.installedPath = + DPL::Utils::Path(*dao.getWidgetInstalledPath()); + m_context.manifestFile = getManifestFile(); + PackagingType packagingType = dao.getPackagingType(); + + _D("Widget model exists. Pkg id : %s", m_context.tzPkgid.c_str()); + + // send start signal of pkgmgr + if (GetInstallerStruct().pkgmgrInterface->setPkgname(m_context.tzPkgid)) + { + GetInstallerStruct().pkgmgrInterface->startJob(InstallationType::Uninstallation); + } + + AddTask(new TaskCheck(m_context)); + if (packagingType == PKG_TYPE_HYBRID_WEB_APP) { + AddTask(new TaskUninstallOspsvc(m_context)); + } + AddTask(new TaskDeletePkgInfo(m_context)); + AddTask(new TaskDbUpdate(m_context)); + AddTask(new TaskSmack(m_context)); + + AddTask(new TaskRemoveCustomHandlers(m_context)); + AddTask(new TaskRemoveFiles(m_context)); + } else if (WidgetStatus::NOT_INSTALLED == status || + WidgetStatus::PREALOAD == status) { + AddTask(new UninstallerTaskFail(status)); + } else if (WidgetStatus::ABNORMAL == status) { + m_context.locations = WidgetLocation(m_context.tzPkgid); + m_context.removeAbnormal = true; + AddTask(new TaskRemoveFiles(m_context)); + } else { + AddTask(new UninstallerTaskFail(WidgetStatus::UNRECOGNIZED)); + } + } Catch(WidgetDAOReadOnly::Exception::Base) { + AddTask(new UninstallerTaskFail(WidgetStatus::UNRECOGNIZED)); + } +} + +WidgetStatus JobWidgetUninstall::getWidgetStatus(const std::string &id) +{ + regex_t regx; + if(regcomp(®x, REG_TIZEN_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED)!=0){ + _D("Regcomp failed"); + } + std::string pkgId = id; + DPL::Utils::Path installPath; + if ((regexec(®x, id.c_str(), + static_cast(0), NULL, 0) != REG_NOERROR)) { + //appid was passed + pkgId = id.substr(0, PKGID_LENTH); + + //Service app cannot uninstall by appid + WrtDB::WidgetDAOReadOnly dao(DPL::FromUTF8String(id)); + if( dao.getWidgetType().appType == APP_TYPE_TIZENWEBSERVICE ){ + _E("Service app cannot uninstall by appid"); + return WidgetStatus::NOT_INSTALLED; + } + } + + m_context.tzAppIdList = WrtDB::WidgetDAOReadOnly::getTzAppIdList(DPL::FromUTF8String(pkgId)); + if( m_context.tzAppIdList.empty() ){ + if(checkDirectoryExist(pkgId)) { + _E("installed widget status is abnormal"); + return WidgetStatus::ABNORMAL; + } + return WidgetStatus::NOT_INSTALLED; + } + return WidgetStatus::Ok; +} + +std::string JobWidgetUninstall::getRemovedTizenId() const +{ + return m_id; +} + +bool JobWidgetUninstall::getRemoveStartedFlag() const +{ + return m_context.removeStarted; +} + +bool JobWidgetUninstall::getRemoveFinishedFlag() const +{ + return m_context.removeFinished; +} + +DPL::Utils::Path JobWidgetUninstall::getManifestFile() const +{ + std::ostringstream manifest_name; + manifest_name << m_context.tzPkgid << ".xml"; + DPL::Utils::Path manifestFile; + + const DPL::Utils::Path PRELOAD_INSTALLED_PATH("/usr/apps/" + m_context.tzPkgid); + const DPL::Utils::Path USR_PACKAGES_PATH("/usr/share/packages"); + const DPL::Utils::Path OPT_PACKAGES_PATH("/opt/share/packages"); + + if (PRELOAD_INSTALLED_PATH == m_context.installedPath) { + _D("This widget is preloaded."); + manifestFile = USR_PACKAGES_PATH; + } else { + manifestFile = OPT_PACKAGES_PATH; + } + + manifestFile /= manifest_name.str(); + _D("Manifest file : %s", manifestFile.Fullpath().c_str()); + + return manifestFile; +} + +void JobWidgetUninstall::SendProgress() +{ + using namespace PackageManager; + if (!getRemoveStartedFlag() || + (getRemoveStartedFlag() && getRemoveFinishedFlag())) + { + if (NULL != GetInstallerStruct().progressCallback) { + // send progress signal of pkgmgr + std::ostringstream percent; + percent << static_cast(GetProgressPercent()); + + _D("Call widget uninstall progressCallback"); + GetInstallerStruct().progressCallback( + GetInstallerStruct().userParam, + GetProgressPercent(), GetProgressDescription()); + } + } +} + +void JobWidgetUninstall::SendFinishedSuccess() +{ + using namespace PackageManager; + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget uninstall success finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + getRemovedTizenId(), + Jobs::Exceptions::Success); +} + +void JobWidgetUninstall::SendFinishedFailure() +{ + using namespace PackageManager; + + LOGE(COLOR_ERROR "Error in uninstallation step: %d" COLOR_END, m_exceptionCaught); + LOGE(COLOR_ERROR "Message: %s" COLOR_END, m_exceptionMessage.c_str()); + fprintf(stderr, "[Err:%d] %s", m_exceptionCaught, m_exceptionMessage.c_str()); + + // send signal of pkgmgr + GetInstallerStruct().pkgmgrInterface->endJob(m_exceptionCaught); + + _D("Call widget uninstall failure finishedCallback"); + GetInstallerStruct().finishedCallback(GetInstallerStruct().userParam, + getRemovedTizenId(), + m_exceptionCaught); + _D("[JobWidgetUninstall] Asynchronous failure callback status sent"); +} + +void JobWidgetUninstall::SaveExceptionData(const Jobs::JobExceptionBase &e) +{ + m_exceptionCaught = static_cast(e.getParam()); + m_exceptionMessage = e.GetMessage(); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/job_widget_uninstall.h b/src_wearable/jobs/widget_uninstall/job_widget_uninstall.h new file mode 100755 index 0000000..d7406ed --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/job_widget_uninstall.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file job_widet_uninstall.h + * @brief Uninstaller header file. + * @author Radoslaw Wicik r.wicik@samsung.com + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ + +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { + +enum class WidgetStatus +{ + Ok, NOT_INSTALLED, PREALOAD, ABNORMAL, UNRECOGNIZED +}; + +typedef JobContextBase WidgetUnistallStructBase; +typedef JobProgressBase UninstallContextBase; + +class JobWidgetUninstall : + public Job, + public UninstallContextBase, + public WidgetUnistallStructBase +{ + private: + UninstallerContext m_context; + + //TODO move it to base class of all jobs + Jobs::Exceptions::Type m_exceptionCaught; + std::string m_exceptionMessage; + // It canbe pkgid or tzAppid + std::string m_id; + + public: + /** + * @brief Uninstaller must to know which widget to uninstall. + * + * @param[in] WrtDB::TizenAppId tzAppId - widget to uninstall + */ + JobWidgetUninstall(const std::string &tizenPkgIdorTizenAppId, + const WidgetUninstallationStruct& uninstallerStruct); + + std::string getRemovedTizenId() const; + bool getRemoveStartedFlag() const; + bool getRemoveFinishedFlag() const; + DPL::Utils::Path getManifestFile() const; + + WidgetStatus getWidgetStatus(const std::string &tizenPkgIdorTizenAppId); + + void SendProgress(); + void SendFinishedSuccess(); + void SendFinishedFailure(); + void SaveExceptionData(const Jobs::JobExceptionBase &e); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_JOB_WIDGET_UNINSTALL_H_ diff --git a/src_wearable/jobs/widget_uninstall/task_check.cpp b/src_wearable/jobs/widget_uninstall/task_check.cpp new file mode 100755 index 0000000..648cdf1 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_check.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_check.cpp + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task check + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskCheck::TaskCheck(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskCheck::StartStep); + AddStep(&TaskCheck::StepUninstallPreCheck); + AddStep(&TaskCheck::StepCheckMDM); + AddStep(&TaskCheck::EndStep); +} + +TaskCheck::~TaskCheck() +{} + +void TaskCheck::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskCheck::EndStep() +{ + m_context.job->UpdateProgress(UninstallerContext::UNINSTALL_PRECHECK, + "Uninstall pre-checking Finished"); + LOGD("--------- : END ----------"); +} + +void TaskCheck::StepUninstallPreCheck() +{ + FOREACH(it , m_context.tzAppIdList){ + bool isRunning = false; + std::string tzAppId = DPL::ToUTF8String(*it); + int ret = app_manager_is_running(tzAppId.c_str(), &isRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Exceptions::PlatformAPIFailure, + "Fail to get widget state"); + } + + if (true == isRunning) { + // get app_context for running application + // app_context must be released with app_context_destroy + app_context_h appCtx = NULL; + ret = app_manager_get_app_context(tzAppId.c_str(), &appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get app_context"); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } + + // terminate app_context_h + ret = app_manager_terminate_app(appCtx); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to terminate running application"); + app_context_destroy(appCtx); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } else { + app_context_destroy(appCtx); + // app_manager_terminate_app isn't sync API + // wait until application isn't running (50ms * 100) + bool isStillRunning = true; + int checkingloop = 100; + struct timespec duration = { 0, 50 * 1000 * 1000 }; + while (--checkingloop >= 0) { + nanosleep(&duration, NULL); + int ret = app_manager_is_running(tzAppId.c_str(), &isStillRunning); + if (APP_MANAGER_ERROR_NONE != ret) { + _E("Fail to get running state"); + ThrowMsg(Exceptions::PlatformAPIFailure, + "Fail to get widget state"); + } + if (!isStillRunning) { + break; + } + } + if (isStillRunning) { + _E("Fail to terminate running application"); + ThrowMsg(Exceptions::AppIsRunning, + "Widget is not stopped. Cannot uninstall!"); + } + _D("terminate application"); + } + } + } + + // check if this webapp has dynamic boxes, and request to remove them + web_provider_service_wait_boxes_removed(m_context.tzAppid.c_str()); + _D("web app(%s) and its dynamic boxes can be terminated.", m_context.tzAppid.c_str()); +} + +void TaskCheck::StepCheckMDM() +{ + _D("StepCheckMDM"); + + if (PMINFO_R_OK != pkgmgr_parser_check_mdm_policy_for_uninstallation( + m_context.manifestFile.Fullpath().c_str())) { + _E("Failed to check mdm policy"); + ThrowMsg(Exceptions::CheckMDMPolicyFailure, "Can't uninstall! Because of MDM policy"); + } +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_check.h b/src_wearable/jobs/widget_uninstall/task_check.h new file mode 100644 index 0000000..29c00db --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_check.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_check.h + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task check + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ + +#include + +struct UninstallerContext; //forward declaration +class WidgetModel; + +namespace Jobs { +namespace WidgetUninstall { +class TaskCheck : + public DPL::TaskDecl +{ + private: + //context + UninstallerContext& m_context; + + //steps + void StepUninstallPreCheck(); + void StepCheckMDM(); + void StartStep(); + void EndStep(); + + public: + TaskCheck(UninstallerContext& context); + virtual ~TaskCheck(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_CHECK_H_ */ diff --git a/src_wearable/jobs/widget_uninstall/task_db_update.cpp b/src_wearable/jobs/widget_uninstall/task_db_update.cpp new file mode 100755 index 0000000..0c391fd --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_db_update.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller task database updating + */ + +#ifdef DBOX_ENABLED +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Jobs { +namespace WidgetUninstall { +TaskDbUpdate::TaskDbUpdate(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskDbUpdate::StartStep); + AddStep(&TaskDbUpdate::StepRemoveExternalLocations); + AddStep(&TaskDbUpdate::StepDbUpdate); +#ifdef DBOX_ENABLED + AddStep(&TaskDbUpdate::StepLiveboxDBDelete); +#endif + AddStep(&TaskDbUpdate::EndStep); +} + +TaskDbUpdate::~TaskDbUpdate() +{} + +void TaskDbUpdate::StepDbUpdate() +{ + Try + { + //TODO: widget handle should not be used any more + FOREACH(it , m_context.tzAppIdList){ + ace_unregister_widget(static_cast(WidgetDAOReadOnly::getHandle(*it))); + WidgetDAO::unregisterWidget(*it); + } + _D("Unregistered widget successfully!"); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + _E("Failed to handle StepDbUpdate!"); + ReThrowMsg(Exceptions::DatabaseFailure, + "Failed to handle StepDbUpdate!"); + } +} + +#ifdef DBOX_ENABLED +void TaskDbUpdate::StepLiveboxDBDelete() +{ + FOREACH(it, m_context.tzAppIdList){ + int ret = + web_provider_livebox_delete_by_app_id(DPL::ToUTF8String(*it).c_str()); + + if (ret < 0) { + _D("failed to delete box info"); + } else { + _D("delete box info: %s", it); + } + } +} +#endif + +void TaskDbUpdate::StepRemoveExternalLocations() +{ + if (!m_context.removeAbnormal) { + FOREACH(it, m_context.tzAppIdList){ + WidgetDAO dao(*it); + _D("Removing external locations:"); + WrtDB::ExternalLocationList externalPaths = dao.getWidgetExternalLocations(); + FOREACH(file, externalPaths) + { + DPL::Utils::Path path(*file); + if(path.Exists()){ + if(path.IsFile()){ + _D(" -> %s", path.Fullpath().c_str()); + Try { + DPL::Utils::Remove(path); + } Catch(DPL::Utils::Path::BaseException){ + _E("Failed to remove the file: %s", path.Fullpath().c_str()); + } + } else if (path.IsDir()){ + _D(" -> %s", path.Fullpath().c_str()); + Try { + DPL::Utils::Remove(path); + } Catch(DPL::Utils::Path::BaseException){ + Throw(Jobs::WidgetUninstall::TaskDbUpdate:: + Exception::RemoveFilesFailed); + } + } + } else { + _W(" -> %s(no such a path)", path.Fullpath().c_str()); + } + } + dao.unregisterAllExternalLocations(); + } + } +} + +void TaskDbUpdate::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskDbUpdate::EndStep() +{ + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_DB_UPDATE, + "Widget DB Update Finished"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_db_update.h b/src_wearable/jobs/widget_uninstall/task_db_update.h new file mode 100644 index 0000000..3b74ad5 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_db_update.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_db_update.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task database updating + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ + +#include +#include +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskDbUpdate : + public DPL::TaskDecl +{ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DbStepFailed) + DECLARE_EXCEPTION_TYPE(Base, RemoveFilesFailed) + }; + + UninstallerContext& m_context; + + private: + void StepDbUpdate(); + void StepLiveboxDBDelete(); + void StepRemoveExternalLocations(); + + void StartStep(); + void EndStep(); + + public: + TaskDbUpdate(UninstallerContext& context); + virtual ~TaskDbUpdate(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DB_UPDATE_H_ diff --git a/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.cpp b/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.cpp new file mode 100644 index 0000000..cf2aa0e --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_delete_pkginfo.cpp + * @author Leerang Song(leerang.song@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller delete package information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskDeletePkgInfo::TaskDeletePkgInfo( + UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskDeletePkgInfo::StartStep); + AddStep(&TaskDeletePkgInfo::StepDeletePkgInfo); + AddStep(&TaskDeletePkgInfo::EndStep); +} + +void TaskDeletePkgInfo::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskDeletePkgInfo::EndStep() +{ + LOGD("--------- : END ----------"); +} + +void TaskDeletePkgInfo::StepDeletePkgInfo() +{ + std::ostringstream manifest_name; + manifest_name << m_context.tzPkgid << ".xml"; + DPL::Utils::Path pre_manifest("/usr/share/packages"); + pre_manifest /= manifest_name.str(); + + if (!(m_context.manifestFile.Exists() == 0 && pre_manifest.Exists())) { + if (0 != pkgmgr_parser_parse_manifest_for_uninstallation( + m_context.manifestFile.Fullpath().c_str(), NULL)) { + _W("Manifest file failed to parse for uninstallation"); + } + } + if (!DPL::Utils::TryRemove(m_context.manifestFile)) { + _W("No manifest file found: %s", m_context.manifestFile.Fullpath().c_str()); + } else { + _D("Manifest file removed: %s", m_context.manifestFile.Fullpath().c_str()); + } +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.h b/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.h new file mode 100644 index 0000000..4624c4a --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_delete_pkginfo.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_delete_pkginfo.h + * @author Leerang Song(leerang.song@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task delete package infomation + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ + +#include +#include + +struct UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskDeletePkgInfo : + public DPL::TaskDecl +{ + UninstallerContext& m_context; + + private: + void StepDeletePkgInfo(); + + void StartStep(); + void EndStep(); + + public: + TaskDeletePkgInfo(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif +// WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_DELETE_PKGINFO_H_ diff --git a/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.cpp b/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.cpp new file mode 100755 index 0000000..4a7ad32 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_custom_handlers.cpp + * @author Przemyslaw Ciezkowski (p.ciezkowski@samsung.com) + * @version 1.0 + * @brief File for uninstaller - remove custom handlers + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskRemoveCustomHandlers::TaskRemoveCustomHandlers(UninstallerContext& context) + : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveCustomHandlers::StartStep); + AddStep(&TaskRemoveCustomHandlers::Step); + AddStep(&TaskRemoveCustomHandlers::EndStep); +} + +void TaskRemoveCustomHandlers::Step() +{ + CustomHandlerDB::Interface::attachDatabaseRW(); + FOREACH(it, m_context.tzAppIdList){ + _D("Removing widget from appsvc"); + int result = appsvc_unset_defapp(DPL::ToUTF8String(*it).c_str()); + _D("Result: %d", result); + + CustomHandlerDB::CustomHandlerDAO handlersDao(*it); + handlersDao.removeWidgetProtocolHandlers(); + handlersDao.removeWidgetContentHandlers(); + } + CustomHandlerDB::Interface::detachDatabase(); +} + +void TaskRemoveCustomHandlers::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskRemoveCustomHandlers::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.h b/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.h new file mode 100644 index 0000000..e6458b9 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_remove_custom_handlers.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_remove_custom_handlers.h + * @author Przemyslaw Ciezkowski (p.ciezkowski@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller - remove custom handlers + */ +#ifndef INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H +#define INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H + +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskRemoveCustomHandlers : + public DPL::TaskDecl +{ + private: + UninstallerContext& m_context; + + void Step(); + + void StartStep(); + void EndStep(); + + public: + TaskRemoveCustomHandlers(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOBS_WIDGET_UNINSTALL_TASK_REMOVE_CUSTOM_HANDLERS_H */ diff --git a/src_wearable/jobs/widget_uninstall/task_remove_files.cpp b/src_wearable/jobs/widget_uninstall/task_remove_files.cpp new file mode 100644 index 0000000..daf4542 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_remove_files.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_files.cpp + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file for uninstaller task for removing widget files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +using namespace WrtDB; + +TaskRemoveFiles::TaskRemoveFiles(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskRemoveFiles::StartStep); + AddStep(&TaskRemoveFiles::StepRemoveInstallationDirectory); + AddStep(&TaskRemoveFiles::StepRemoveFinished); + AddStep(&TaskRemoveFiles::EndStep); +} + +TaskRemoveFiles::~TaskRemoveFiles() +{} + +void TaskRemoveFiles::StepRemoveInstallationDirectory() +{ + _D("StepRemoveInstallationDirectory started"); + Try { + int ret = app2ext_get_app_location(m_context.tzPkgid.c_str()); + + if (APP2EXT_SD_CARD == ret) { + _D("Remove external directory"); + Try { + WidgetInstallToExtSingleton::Instance().initialize(m_context.tzPkgid); + WidgetInstallToExtSingleton::Instance().uninstallation(); + WidgetInstallToExtSingleton::Instance().deinitialize(); + } + Catch(WidgetInstallToExt::Exception::ErrorInstallToExt) + { + // Continue uninstall even fail to remove external directory. + // i.e.) SD card isn't inserted or unmounted. + // This behavior is recommended by platform(app2sd maintainer). + _E("Fail to remove external directory"); + } + } + if (APP2EXT_NOT_INSTALLED != ret) { + _D("Removing directory"); + m_context.removeStarted = true; + DPL::Utils::Path widgetDir= m_context.installedPath; + Try{ + DPL::Utils::Remove(widgetDir); + } Catch(DPL::Utils::Path::BaseException){ + _E("Removing widget installation directory failed : %s", widgetDir.Fullpath().c_str()); + } + DPL::Utils::Path dataDir(m_context.locations->getUserDataRootDir()); + Try{ + DPL::Utils::Remove(dataDir); + } Catch(DPL::Utils::Path::BaseException){ + _W("%s is already removed", dataDir.Fullpath().c_str()); + } + } else { + _E("app is not installed"); + ThrowMsg(Exceptions::WidgetNotExist, "failed to get app location"); + } + } Catch(Exception::RemoveFilesFailed) { + ThrowMsg(Exceptions::RemoveFileFailure, "Cann't remove directory"); + } + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_REMOVE_WIDGETDIR, + "Widget INstallation Directory Removal Finished"); +} + +void TaskRemoveFiles::StepRemoveFinished() +{ + _D("StepRemoveFinished finished"); + + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_REMOVE_FINISHED, + "Widget remove steps Finished"); +} + +void TaskRemoveFiles::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskRemoveFiles::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_remove_files.h b/src_wearable/jobs/widget_uninstall/task_remove_files.h new file mode 100644 index 0000000..276fb24 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_remove_files.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_remove_files.h + * @author Lukasz Wrzosek(l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task remove files + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ + +//forward declaration +struct UninstallerContext; + +#include +#include + +#include + +namespace Jobs { +namespace WidgetUninstall { +class TaskRemoveFiles : + public DPL::TaskDecl +{ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, RemoveFilesFailed) + }; + + UninstallerContext& m_context; + + private: + void StepRemoveInstallationDirectory(); + void StepRemoveFinished(); + + void StartStep(); + void EndStep(); + + public: + explicit TaskRemoveFiles(UninstallerContext& context); + virtual ~TaskRemoveFiles(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif // WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_REMOVE_FILES_H_ diff --git a/src_wearable/jobs/widget_uninstall/task_smack.cpp b/src_wearable/jobs/widget_uninstall/task_smack.cpp new file mode 100644 index 0000000..9599c92 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_smack.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.cpp + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Implementation file for installer task smack + */ + +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +TaskSmack::TaskSmack(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskSmack::StartStep); + AddStep(&TaskSmack::Step); + AddStep(&TaskSmack::EndStep); +} + +void TaskSmack::Step() +{ + _D("------------------------> SMACK: Jobs::WidgetUninstall::TaskSmack::Step()"); + + if (PC_OPERATION_SUCCESS != perm_begin()) { + _E("failure in smack transaction begin"); + return; + } + + const char* pkgId = m_context.tzPkgid.c_str(); + if (PC_OPERATION_SUCCESS != perm_app_revoke_permissions(pkgId)) { + _E("failure in revoking smack permissions"); + } + + if (PC_OPERATION_SUCCESS != perm_app_uninstall(pkgId)) { + _E("failure in removing smack rules file"); + } + + if (PC_OPERATION_SUCCESS != perm_end()) { + _E("failure in smack transaction end"); + return; + } +} + +void TaskSmack::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskSmack::EndStep() +{ + m_context.job->UpdateProgress( + UninstallerContext::UNINSTALL_SMACK_DISABLE, + "Widget SMACK Disabled"); + + LOGD("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_smack.h b/src_wearable/jobs/widget_uninstall/task_smack.h new file mode 100644 index 0000000..c7886b9 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_smack.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_smack.h + * @author Piotr Kozbial (p.kozbial@samsung.com) + * @version 1.0 + * @brief Header file for uninstaller task smack + */ +#ifndef INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H +#define INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H + +#include + +class UninstallerContext; + +namespace Jobs { +namespace WidgetUninstall { +class TaskSmack : + public DPL::TaskDecl +{ + private: + UninstallerContext& m_context; + + void Step(); + + void StartStep(); + void EndStep(); + + public: + TaskSmack(UninstallerContext& context); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* INSTALLER_CORE_JOS_WIDGET_UNINSTALL_TASK_SMACK_H */ diff --git a/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.cpp b/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.cpp new file mode 100644 index 0000000..9d00ea1 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_uninstall_ospsvc.cpp + * @author Soyoung Kim(sy037.kim@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task to uninstall ospsvc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const int MAX_BUF_SIZE = 128; +const char* OSP_INSTALL_STR = "/usr/etc/package-manager/backend/tpk -uv "; +} + +namespace Jobs { +namespace WidgetUninstall { +TaskUninstallOspsvc::TaskUninstallOspsvc(UninstallerContext& context) : + DPL::TaskDecl(this), + m_context(context) +{ + AddStep(&TaskUninstallOspsvc::StartStep); + AddStep(&TaskUninstallOspsvc::StepUninstallOspsvc); + AddStep(&TaskUninstallOspsvc::EndStep); +} + +TaskUninstallOspsvc::~TaskUninstallOspsvc() +{} + +void TaskUninstallOspsvc::StepUninstallOspsvc() +{ + _D("Step : Uninstall Osp service"); + + std::ostringstream commStr; + commStr << OSP_INSTALL_STR << BashUtils::escape_arg(m_context.tzPkgid); + _D("osp uninstall command : %s", commStr.str().c_str()); + + char readBuf[MAX_BUF_SIZE]; + FILE *fd; + fd = popen(commStr.str().c_str(), "r"); + if (NULL == fd) { + _E("Failed to uninstalltion osp service"); + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + uninstall osp service"); + } + + if(fgets(readBuf, MAX_BUF_SIZE, fd) == NULL) + { + _E("Failed to uninstalltion osp service\ + Inability of reading file."); + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + uninstall osp service"); + } + _D("return value : %s", readBuf); + + int result = atoi(readBuf); + if (0 != result) { + ThrowMsg(Exceptions::UninstallOspSvcFailed, + "Error occurs during\ + install osp service"); + } + + pclose(fd); + + _D("Widget Can be uninstalled. Pkgname : %s", m_context.tzPkgid.c_str()); + m_context.job->UpdateProgress(UninstallerContext::UNINSTALL_REMOVE_OSPSVC, + "Uninstall OSP service finished"); +} + +void TaskUninstallOspsvc::StartStep() +{ + LOGD("--------- : START ----------"); +} + +void TaskUninstallOspsvc::EndStep() +{ + LOGD("--------- : END ----------"); +} +} //namespace WidgetUninstall +} //namespace Jobs diff --git a/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.h b/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.h new file mode 100644 index 0000000..50ec347 --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/task_uninstall_ospsvc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file task_uninstall_ospsvc.h + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for widget uninstall task to uninstall ospsvc + */ + +#ifndef WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H +#define WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H + +#include + +struct UninstallerContext; //forward declaration + +namespace Jobs { +namespace WidgetUninstall { +class TaskUninstallOspsvc : + public DPL::TaskDecl +{ + private: + //context + UninstallerContext& m_context; + + //steps + void StepUninstallOspsvc(); + + void StartStep(); + void EndStep(); + + public: + TaskUninstallOspsvc(UninstallerContext& context); + virtual ~TaskUninstallOspsvc(); +}; +} //namespace WidgetUninstall +} //namespace Jobs + +#endif /* WRT_SRC_INSTALLER_CORE_JOB_WIDGET_UNINSTALL_TASK_UNINSTALL_OSPSVC_H */ diff --git a/src_wearable/jobs/widget_uninstall/uninstaller_context.h b/src_wearable/jobs/widget_uninstall/uninstaller_context.h new file mode 100755 index 0000000..ef1963f --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/uninstaller_context.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file uninstaller_context.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version + * @brief Definition file of installer tasks data structures + */ + +#ifndef WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ +#define WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ + +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetUninstall { +class JobWidgetUninstall; +} //namespace WidgetUninstall +} //namespace Jobs + +struct UninstallerContext +{ + enum UninstallStep + { + UNINSTALL_START, + UNINSTALL_PRECHECK, + UNINSTALL_REMOVE_WIDGETDIR, + UNINSTALL_REMOVE_DESKTOP, + UNINSTALL_REMOVE_FINISHED, + UNINSTALL_DB_UPDATE, + UNINSTALL_REMOVE_OSPSVC, + UNINSTALL_SMACK_DISABLE, + UNINSTALL_END + }; + + ///< flag that indicates whether installer starts + //to remove files.rStruct; + bool removeStarted; + ///< flag that indicates whether installer finishes + //to remove files completely. + bool removeFinished; + + boost::optional locations; + + UninstallStep uninstallStep; ///< current step of installation + Jobs::WidgetUninstall::JobWidgetUninstall *job; + std::list tzAppIdList; + std::string tzAppid; + std::string tzPkgid; + std::string tzServiceid; + bool removeAbnormal; + DPL::Utils::Path installedPath; + DPL::Utils::Path manifestFile; + WrtDB::AppType appType; +}; + +#endif // WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_UNINSTALLER_CONTEXT_H_ diff --git a/src_wearable/jobs/widget_uninstall/widget_uninstall_errors.h b/src_wearable/jobs/widget_uninstall/widget_uninstall_errors.h new file mode 100644 index 0000000..3c5970b --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/widget_uninstall_errors.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_uninstall_errors.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief + */ + +#ifndef WIDGET_UNINSTALL_ERRORS_H_ +#define WIDGET_UNINSTALL_ERRORS_H_ + +#include +#include + +using namespace Jobs::Exceptions; + +namespace Jobs { +namespace WidgetUninstall { +namespace Exceptions { + +DECLARE_JOB_EXCEPTION_BASE(JobExceptionBase, Base, ErrorUnknown) + +DECLARE_JOB_EXCEPTION(Base, DatabaseFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, AlreadyUninstalling, + ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, AppIsRunning, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, WidgetNotExist, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, UninstallOspSvcFailed, + ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, PlatformAPIFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, RemoveFileFailure, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, Unremovable, ErrorWidgetUninstallationFailed) +DECLARE_JOB_EXCEPTION(Base, CheckMDMPolicyFailure, ErrorWidgetUninstallationFailed) +} //namespace +} //namespace +} //namespace + +#endif /* WIDGET_UNINSTALL_ERRORS_H_ */ diff --git a/src_wearable/jobs/widget_uninstall/widget_uninstaller_struct.h b/src_wearable/jobs/widget_uninstall/widget_uninstaller_struct.h new file mode 100644 index 0000000..3f33e4b --- /dev/null +++ b/src_wearable/jobs/widget_uninstall/widget_uninstaller_struct.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_uninstaller_struct.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget installer struct + */ +#ifndef WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ +#define WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ + +//SYSTEM INCLUDES +#include + +//WRT INCLUDES +#include +#include +#include +#include +#include + +//Widget Uninstaller typedefs +typedef void (*UninstallerFinishedCallback)( + void *userParam, + std::string tizenId, + Jobs::Exceptions::Type); + +typedef void (*UninstallerProgressCallback)( + void *userParam, + ProgressPercent percent, + const ProgressDescription &description); + +//UninstallationStruct +typedef Jobs::JobCallbacksBase +WidgetUninstallCallbackBase; + +struct WidgetUninstallationStruct : public WidgetUninstallCallbackBase +{ + std::shared_ptr pkgmgrInterface; + + // It must be empty-constructible as a parameter of generic event + WidgetUninstallationStruct() + {} + + WidgetUninstallationStruct( + UninstallerFinishedCallback finished, + UninstallerProgressCallback progress, + void *param, + std::shared_ptr + _pkgmgrInterface + ) : + WidgetUninstallCallbackBase(finished, progress, param), + pkgmgrInterface(_pkgmgrInterface) + {} +}; +#endif // WRT_SRC_INSTALLER_CORE_UNINSTALLER_TASKS_WIDGET_INSTALLER_STRUCT_H_ diff --git a/src_wearable/logic/installer_controller.cpp b/src_wearable/logic/installer_controller.cpp new file mode 100644 index 0000000..d9b41d5 --- /dev/null +++ b/src_wearable/logic/installer_controller.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "installer_controller.h" +#include + +IMPLEMENT_SINGLETON(Logic::InstallerController) + +namespace Logic { +InstallerController::InstallerController() +{} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InstallWidgetEvent &event) +{ + std::string fileName = event.GetArg0(); + std::string pkgId = event.GetArg1(); + Jobs::WidgetInstall::WidgetInstallationStruct installerStruct = event.GetArg2(); + m_installerLogic.InstallWidget(fileName, pkgId, installerStruct); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InstallPluginEvent &event) +{ + std::string dirName = event.GetArg0(); + PluginInstallerStruct installerStruct = event.GetArg1(); + m_installerLogic.InstallPlugin(dirName, installerStruct); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::UninstallWidgetEvent &event) +{ + std::string widgetPkgName = event.GetArg0(); + WidgetUninstallationStruct uninstallerStruct = event.GetArg1(); + m_installerLogic.UninstallWidget(widgetPkgName, uninstallerStruct); +} + +Eina_Bool InstallerController::AddNextStep(void *data) +{ + Jobs::Job* model = static_cast(data); + CONTROLLER_POST_EVENT(InstallerController, + InstallerControllerEvents::NextStepEvent(model)); + + return ECORE_CALLBACK_CANCEL; +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::NextStepEvent &event) +{ + Jobs::Job* model = event.GetArg0(); + Assert(model != NULL); + + if (m_installerLogic.NextStep(model)) { + ecore_idler_add(AddNextStep, model); + } +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::InitializeEvent & /*event*/) +{ + m_installerLogic.Initialize(); +} + +void InstallerController::OnEventReceived( + const InstallerControllerEvents::TerminateEvent & /*event*/) +{ + m_installerLogic.Terminate(); +} +} //Logic + diff --git a/src_wearable/logic/installer_controller.h b/src_wearable/logic/installer_controller.h new file mode 100644 index 0000000..0cde86e --- /dev/null +++ b/src_wearable/logic/installer_controller.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_CONTROLLER_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_CONTROLLER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief holds events send to InstallControler + */ +namespace InstallerControllerEvents { +/** + * @brief Event for inicieting instalation process. + * + * This event holds std::string witch should be path to widget package + */ +DECLARE_GENERIC_EVENT_3(InstallWidgetEvent, + std::string, // zipFileName + std::string, // package id + Jobs::WidgetInstall::WidgetInstallationStruct) // installerStruct + +/** + * @brief Event for iniciating plugin instalation process. + * This event holds std::string witch should be path to plugin directory + * and PluginInstallerStruct which contain + * StatusCallack, progressCallback and private data for callbacks + */ +DECLARE_GENERIC_EVENT_2(InstallPluginEvent, std::string, PluginInstallerStruct) + +/** + * @brief Event for inicietig widget uninstallation. + * + * tizen id is used to point witch widget shuld be uninstalled + */ +DECLARE_GENERIC_EVENT_2(UninstallWidgetEvent, + std::string, + WidgetUninstallationStruct) + +/** + * @brief Event for pushing installation process forward. + */ +DECLARE_GENERIC_EVENT_1(NextStepEvent, Jobs::Job *) + +DECLARE_GENERIC_EVENT_0(InitializeEvent) +DECLARE_GENERIC_EVENT_0(TerminateEvent) +} // namespace InstallerEvents + +namespace Logic { +/** + * @brief Controls Widget installation + * + * Main Controler of wiget installation/uninstallation, this is also used + * for pushing forward each of processes. + * It waits for three events: + *
    + *
  • InstallWidgetEvent
  • + *
  • UninstallWidgetEvent
  • + *
  • NextStepEvent
  • + *
+ */ + +typedef DPL::TypeListDecl< + InstallerControllerEvents::InstallWidgetEvent, + InstallerControllerEvents::InstallPluginEvent, + InstallerControllerEvents::UninstallWidgetEvent, + InstallerControllerEvents::NextStepEvent, + InstallerControllerEvents::InitializeEvent, + InstallerControllerEvents::TerminateEvent>::Type +InstallerControllerEventsSet; + +class InstallerController : public DPL::Event::Controller< + InstallerControllerEventsSet> +{ + protected: + /** + * @brief Executed on InstallWidgetEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::InstallWidgetEvent &event); + + /** + * @brief Executed on InstallPluginEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::InstallPluginEvent &event); + + /** + * @brief Executed on UninstallWidgetEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::UninstallWidgetEvent &event); + /** + * @brief Executed on NextStepEvent received. + */ + virtual void OnEventReceived( + const InstallerControllerEvents::NextStepEvent &event); + + virtual void OnEventReceived( + const InstallerControllerEvents::InitializeEvent &event); + virtual void OnEventReceived( + const InstallerControllerEvents::TerminateEvent &event); + + private: + // Embedded logic + Logic::InstallerLogic m_installerLogic; + + InstallerController(); + + static Eina_Bool AddNextStep(void *data); + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton InstallerControllerSingleton; +} + +#endif // INSTALLER_CONTROLLER_H diff --git a/src_wearable/logic/installer_logic.cpp b/src_wearable/logic/installer_logic.cpp new file mode 100755 index 0000000..b80c98b --- /dev/null +++ b/src_wearable/logic/installer_logic.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace Logic { +InstallerLogic::InstallerLogic() : + m_job(0), + m_NextHandle(0) +{} + +InstallerLogic::~InstallerLogic() +{ + if (m_job) { + delete m_job; + } +} + +void InstallerLogic::Initialize() +{ + _D("Done"); +} + +void InstallerLogic::Terminate() +{ + //TODO how to delete, if it is still running, paused and so on + if(m_job) + m_job->SetPaused(true); + + _D("Done"); +} + +Jobs::JobHandle InstallerLogic::AddAndStartJob() +{ + Jobs::JobHandle handle = GetNewJobHandle(); + m_job->SetJobHandle(handle); + //Start job + CONTROLLER_POST_EVENT(InstallerController, + InstallerControllerEvents::NextStepEvent(m_job)); + + return handle; +} + +//InstallWidget, UninstallWidget InstallPlugin method are almost the same +// But each Job has different constructor, so creating new Job is specific +Jobs::JobHandle InstallerLogic::InstallWidget( + const std::string & widgetPath, + const std::string & pkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Widget Installation:"); + + m_job = new Jobs::WidgetInstall::JobWidgetInstall(widgetPath, pkgId, installerStruct); + + return AddAndStartJob(); +} + +Jobs::JobHandle InstallerLogic::UninstallWidget( + const std::string & widgetPkgName, + const + WidgetUninstallationStruct &uninstallerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Widget Uninstallation"); + + m_job = + new Jobs::WidgetUninstall::JobWidgetUninstall(widgetPkgName, + uninstallerStruct); + + return AddAndStartJob(); +} + +Jobs::JobHandle InstallerLogic::InstallPlugin( + std::string const & pluginPath, // TODO change type to PluginPath + const PluginInstallerStruct & + installerStruct) +{ + if(m_job) + { + _E("Job is in progress. It is impossible to add new job"); + return -1; + } + + _D("New Plugin Installation"); + + // TODO Conversion to PluginPath is temporary + m_job = + new Jobs::PluginInstall::JobPluginInstall(PluginPath(pluginPath), installerStruct); + + // before start install plugin, reset plugin data which is stopped + // during installing. (PluginDAO::INSTALLATION_IN_PROGRESS) + ResetProgressPlugins(); + + return AddAndStartJob(); +} + +#define TRANSLATE_JOB_EXCEPTION() \ + _rethrown_exception.getParam() +#define TRANSLATE_JOB_MESSAGE() \ + _rethrown_exception.GetMessage() + +bool InstallerLogic::NextStep(Jobs::Job *job) +{ + Try { + bool stepSucceded = job->NextStep(); + + job->SendProgress(); + + if (stepSucceded) { + return !job->IsPaused(); + } + + if (!job->GetAbortStarted()) { + //job successfully finished + + //send finished callback + job->SendFinishedSuccess(); + + switch (job->GetInstallationType()) { + case Jobs::PluginInstallation: + InstallWaitingPlugins(); + break; + default: //because of warning + break; + } + } else { + //job abort process completed + job->SendFinishedFailure(); + } + + //clean job + delete job; + m_job=0; + + return false; + } catch (Jobs::JobExceptionBase &exc) { + //start revert job + _D("Exception occured: %d. Reverting job...", exc.getParam()); + bool hasAbortSteps = job->Abort(); + job->SetAbortStarted(true); + job->SaveExceptionData(exc); + + if (!hasAbortSteps) { + //no AbortSteps + job->SendFinishedFailure(); + + //clean job + delete job; + m_job=0; + } + return hasAbortSteps; + } +} + +//TODO implement me +bool InstallerLogic::AbortJob(const Jobs::JobHandle & /*handle*/) +{ + _W("Not implemented"); + return true; +} +void InstallerLogic::InstallWaitingPlugins() +{ + PluginHandleSetPtr waitingPlugins; + + waitingPlugins = + PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_WAITING); + + FOREACH(it, *waitingPlugins) + { + resolvePluginDependencies(*it); + } +} + +void InstallerLogic::ResetProgressPlugins() +{ + PluginHandleSetPtr progressPlugins; + + progressPlugins = + PluginDAO::getPluginHandleByStatus(PluginDAO::INSTALLATION_IN_PROGRESS); + + FOREACH(it, *progressPlugins) { + FeatureHandleListPtr featureListPtr = + FeatureDAOReadOnly::GetFeatureHandleListForPlugin(*it); + FOREACH(ItFeature, *featureListPtr) { + FeatureDAO::UnregisterFeature(*ItFeature); + } + PluginDAO::unregisterPlugin(*it); + } +} + +bool InstallerLogic::resolvePluginDependencies(PluginHandle handle) +{ + PluginHandleSetPtr dependencies(new PluginHandleSet); + + PluginObjects::ObjectsPtr requiredObjects = + PluginDAO::getRequiredObjectsForPluginHandle(handle); + + PluginHandle depHandle = + Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE; + + FOREACH(requiredObject, *requiredObjects) + { + depHandle = + PluginDAO::getPluginHandleForImplementedObject(*requiredObject); + + if (depHandle == + Jobs::PluginInstall::JobPluginInstall::INVALID_HANDLE) + { + _E("Library implementing: %s NOT FOUND", (*requiredObject).c_str()); + + //PluginDAO::SetPluginInstallationStatus(INSTALLATION_WAITING); + return false; + } + dependencies->insert(depHandle); + } + + PluginDAO::registerPluginLibrariesDependencies(handle, dependencies); + PluginDAO::setPluginInstallationStatus(handle, + PluginDAO::INSTALLATION_COMPLETED); + + return true; +} +} + diff --git a/src_wearable/logic/installer_logic.h b/src_wearable/logic/installer_logic.h new file mode 100755 index 0000000..3e87a44 --- /dev/null +++ b/src_wearable/logic/installer_logic.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WRT_SRC_INSTALLER_CORE_INSTALLER_LOGIC_H_ +#define WRT_SRC_INSTALLER_CORE_INSTALLER_LOGIC_H_ + +#include +#include +#include +#include +#include +#include + +namespace Logic { +class InstallerLogic +{ + Jobs::Job* m_job; + + void ResetProgressPlugins(); + void InstallWaitingPlugins(); + bool resolvePluginDependencies(PluginHandle handle); + + Jobs::JobHandle m_NextHandle; + Jobs::JobHandle GetNewJobHandle() + { + return m_NextHandle++; + } + Jobs::JobHandle AddAndStartJob(); + + public: + virtual ~InstallerLogic(); + + void Initialize(); + + void Terminate(); + + Jobs::JobHandle InstallWidget( + const std::string & widgetPath, + const std::string & pkgId, + const Jobs::WidgetInstall::WidgetInstallationStruct & + installerStruct); + + Jobs::JobHandle UninstallWidget( + const std::string & widgetPkgName, + const WidgetUninstallationStruct & + uninstallerStruct); + + Jobs::JobHandle InstallPlugin(std::string const & pluginPath, + const PluginInstallerStruct &installerStruct); + + bool NextStep(Jobs::Job* installModel); + + bool AbortJob(const Jobs::JobHandle &handle); + + private: + InstallerLogic(); + + friend class InstallerController; +}; +} + +#endif // INSTALLER_LOGIC_H diff --git a/src_wearable/misc/feature_logic.cpp b/src_wearable/misc/feature_logic.cpp new file mode 100755 index 0000000..9c1c9ee --- /dev/null +++ b/src_wearable/misc/feature_logic.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "feature_logic.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Jobs { +namespace WidgetInstall { +namespace { +const DPL::String PRIVILEGE_TESTAUTOMATION = + L"http://tizen.org/privilege/testautomation"; +const DPL::String DEVICE_CAPABILITY_TESTAUTOMATION = L"testautomation"; +} +FeatureLogic::FeatureLogic(const WrtDB::TizenAppId & tzAppid) : + m_rejected(false) +{ + WrtDB::WidgetDAOReadOnly widgetDao(tzAppid); + WidgetFeatureSet featureSet = widgetDao.getFeaturesList(); + FOREACH(it, featureSet) { + _D("Feature name : %ls", it->name.c_str()); + WrtDB::DeviceCapabilitySet dcs; + if (!DPL::StringCompare(it->name, PRIVILEGE_TESTAUTOMATION)) { + // special privilege + // This privilege doesn't have plugin in the target + // only use to special behavior + dcs.insert(DEVICE_CAPABILITY_TESTAUTOMATION); + } else { + // normal privilege + dcs = WrtDB::FeatureDAOReadOnly::GetDeviceCapability(it->name); + } + FOREACH(devCap, dcs) { + _D("--- dev cap : %ls", (*devCap).c_str()); + } + Feature feature(*it, dcs); + m_featureList.push_back(feature); + } + m_currentFeature = m_featureList.begin(); + + // ok we must set iterator on the first processable node + if (!isProcessable()) { + next(); + } +} + +bool FeatureLogic::isDone() const +{ + return m_currentFeature == m_featureList.end(); +} + +bool FeatureLogic::next() +{ + while (!isDone()) { + if (m_currentFeature->currentCap != + m_currentFeature->devCapSet.end()) + { + m_currentFeature->currentCap++; + } else { + ++m_currentFeature; + } + // we moved pointer + if (isProcessable()) { + return true; + } + } + return false; +} + +void FeatureLogic::setAceResponse(bool allowed) +{ + AssertMsg(isProcessable(), "Wrong usage"); + if (!allowed) { + m_currentFeature->rejected = true; + m_rejected = true; + } +} + +DPL::String FeatureLogic::getDevice() const +{ + return *(m_currentFeature->currentCap); +} + +bool FeatureLogic::isProcessable() const +{ + if (isDone()) { + return false; + } + + if (m_currentFeature->currentCap == m_currentFeature->devCapSet.end()) { + return false; + } + + return true; +} +} // namespace WidgetInstall +} // namespace Jobs + diff --git a/src_wearable/misc/feature_logic.h b/src_wearable/misc/feature_logic.h new file mode 100644 index 0000000..d407cb0 --- /dev/null +++ b/src_wearable/misc/feature_logic.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_INSTALLER_MISC_FEATURE_LOGIC +#define SRC_INSTALLER_MISC_FEATURE_LOGIC + +#include +#include + +#include +#include +#include + +#include +#include + +namespace Jobs { +namespace WidgetInstall { +class FeatureLogic : DPL::Noncopyable +{ + public: + + FeatureLogic(const WrtDB::TizenAppId & tzAppid); + + bool isDone() const; + + bool next(); + + void setAceResponse(bool allowed); + + DPL::String getDevice() const; + + bool isRejected(void) const + { + return m_rejected; + } + + struct Feature : public WidgetFeature { + WrtDB::DeviceCapabilitySet devCapSet; + WrtDB::DeviceCapabilitySet::const_iterator currentCap; + + Feature(const WidgetFeature &wf, + const WrtDB::DeviceCapabilitySet &set) : + WidgetFeature(wf) + , devCapSet(set) + { + currentCap = devCapSet.begin(); + } + + explicit Feature(const Feature &second) : WidgetFeature(second) + { + devCapSet = second.devCapSet; + currentCap = devCapSet.find(*second.currentCap); + rejected = second.rejected; + } + + private: + void operator=(const Feature &second) + { + name = second.name; + devCapSet = second.devCapSet; + rejected = second.rejected; + pluginId = second.pluginId; + currentCap = devCapSet.find(*second.currentCap); + } + }; + + typedef std::list FeatureList; + typedef FeatureList::const_iterator FeatureIterator; + + FeatureIterator resultBegin() + { + return m_featureList.begin(); + } + FeatureIterator resultEnd() + { + return m_featureList.end(); + } + + private: + bool isProcessable() const; + + FeatureList m_featureList; + FeatureList::iterator m_currentFeature; + bool m_rejected; +}; + +typedef std::shared_ptr FeatureLogicPtr; +} // namespace WidgetInstall +} // namespace Jobs + +#endif // SRC_INSTALLER_MISC_FEATURE_LOGIC diff --git a/src_wearable/misc/libxml_utils.cpp b/src_wearable/misc/libxml_utils.cpp new file mode 100644 index 0000000..114e95b --- /dev/null +++ b/src_wearable/misc/libxml_utils.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libxml_utils.cpp + * @author Tomasz Iwanek (t.iwanek@samsung.com) + */ + +#include "libxml_utils.h" + +#include +#include + +IMPLEMENT_SINGLETON(LibxmlUtils) + +LibxmlUtils::LibxmlUtils() : isInitialized(false) +{} + +LibxmlUtils::~LibxmlUtils() +{ + if (isInitialized) { + _D("Libxml - cleaning"); + // Cleanup function for the XML library. + xmlCleanupParser(); + //this is to debug memory for regression tests + xmlMemoryDump(); + } +} + +void LibxmlUtils::init() +{ + if (!isInitialized) { + LIBXML_TEST_VERSION + isInitialized = true; + _D("Libxml have been initialized"); + } + _D("Libxml already initialized"); +} + diff --git a/src_wearable/misc/libxml_utils.h b/src_wearable/misc/libxml_utils.h new file mode 100644 index 0000000..5354bda --- /dev/null +++ b/src_wearable/misc/libxml_utils.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file libxml_utils.h + * @author Tomasz Iwanek (t.iwanek@samsung.com) + */ + +#ifndef LIBXML_UTILS_H +#define LIBXML_UTILS_H + +#include +#include + +#include +#include +#include +#include + +/** + * @brief The LibxmlUtils class + * + * Singleton for assurence for libxml2 initialization + * + * Use: LibxmlUtils::Instance().init(); to initialize library + * + */ +class LibxmlUtils +{ + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, Libxml2Error) + + LibxmlUtils(); + ~LibxmlUtils(); + + void init(); + + private: + bool isInitialized; + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton LibxmlSingleton; + +#endif // LIBXML_UTILS_H diff --git a/src_wearable/misc/plugin_path.cpp b/src_wearable/misc/plugin_path.cpp new file mode 100644 index 0000000..2b2ebdb --- /dev/null +++ b/src_wearable/misc/plugin_path.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_path_builder.cpp + * @author Kamil Nować (k.nowac@partner.samgsung.com) + * @version + * @brief + */ + +#include +#include +#include + +using namespace DPL::Utils; + +PluginPath::PluginPath(const Path& fullPath) : Path(fullPath.Fullpath()) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(const std::string& fullPath) : Path(fullPath) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(const DPL::String& fullPath) : Path(fullPath) +{ + setLibraryCombinedName( + WrtDB::GlobalConfig::GetPluginPrefix(), + WrtDB::GlobalConfig::GetPluginSuffix()); +}; +PluginPath::PluginPath(){} + +PluginPath PluginPath::getMetaFile() const +{ + PluginPath metaFile = *this; + return metaFile /= WrtDB::GlobalConfig::GetPluginMetafileName(); +} \ No newline at end of file diff --git a/src_wearable/misc/plugin_path.h b/src_wearable/misc/plugin_path.h new file mode 100644 index 0000000..8ada790 --- /dev/null +++ b/src_wearable/misc/plugin_path.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_path_builder.cpp + * @author Kamil Nować (k.nowac@partner.samgsung.com) + * @version + * @brief + */ + +#ifndef PLUGIN_PATH_H +#define PLUGIN_PATH_H + +#include +#include +#include + +class PluginPath: public DPL::Utils::Path{ +private: + std::string m_library; + +public: + PluginPath(const DPL::Utils::Path& fullPath); + PluginPath(const std::string& fullPath); + PluginPath(const DPL::String& fullPath); + PluginPath(); + + //getMetafile() this function adds metafile to current path. + PluginPath getMetaFile() const; + + //setLibraryCombinedName This function creates name for library by adding + //prefix and suffix to PluginPath object filename. + void setLibraryCombinedName(const std::string& prefix, const std::string& sufix) + { + this->m_library = prefix + this->Filename() + sufix; + } + + //getLibraryName returns library name + const std::string& getLibraryName() const + { + return m_library; + } + //getLibraryPath returns full path to the library + const PluginPath getLibraryPath() const + { + return this->operator /(m_library); + } +}; + +#endif // PLUGIN_PATH_H diff --git a/src_wearable/misc/wac_widget_id.cpp b/src_wearable/misc/wac_widget_id.cpp new file mode 100644 index 0000000..dca752b --- /dev/null +++ b/src_wearable/misc/wac_widget_id.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include "wac_widget_id.h" + +#include +#include + +#include + +#include +#include + +namespace { +const char *SCHEME_HTTP = "http"; +const char *SCHEME_HTTPS = "https"; +} + +WacWidgetId::WacWidgetId(const DPL::OptionalString &widgetId) : + m_schemaMatch(false) +{ + if (!!widgetId) { + std::string wid = DPL::ToUTF8String(*widgetId); + parse(wid.c_str()); + } +} + +bool WacWidgetId::matchHost(const DPL::String &second) const +{ + _D("m_schemaMatch is: %d", m_schemaMatch); + if (!m_schemaMatch) { + return false; + } + + _D("Matching DNS identity: %s %ls", m_host.c_str(), second.c_str()); + + return m_host == DPL::ToUTF8String(second); +} + +void WacWidgetId::parse(const char *url) +{ + _D("Widget id to parse: %s", url); + + std::unique_ptr > + iri(iri_parse(url), iri_destroy); + + if (!iri.get()) { + _E("Error in parsing widget id."); + return; // m_schemaMatch == false; + } + + std::string scheme; + + if (iri.get()->scheme) { + scheme = iri.get()->scheme; + } else { + _W("Error. No scheme in widget id."); + return; // m_schemaMatch == false; + } + + // should we support HTTP and HTTPS? wac says nothing + // std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), + // tolower); + + // We only match "http" and "https" schemas + if ((scheme != SCHEME_HTTP) && (scheme != SCHEME_HTTPS)) { + _W("Unknown scheme in widget id. %s", scheme.c_str()); + return; // m_schemaMatch == false; + } else { + m_schemaMatch = true; + } + + if (iri.get()->host) { + m_host = iri.get()->host; + _D("Host has been set to: %s", m_host.c_str()); + } + + // What to do when host is empty? No info in wac documentation. + + // Any post processing algorithm? No info in wac documentation. +} diff --git a/src_wearable/misc/wac_widget_id.h b/src_wearable/misc/wac_widget_id.h new file mode 100644 index 0000000..dba5f36 --- /dev/null +++ b/src_wearable/misc/wac_widget_id.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H +#define WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H + +#include +#include + +class WacWidgetId +{ + public: + explicit WacWidgetId(const DPL::OptionalString &widgetId); + bool matchHost(const DPL::String &second) const; + + private: + void parse(const char *url); + + bool m_schemaMatch; + std::string m_host; +}; + +#endif // WRT_ENGINE_SRC_INSTALLER_CORE_MISC_WAC_WIDGET_ID_H + diff --git a/src_wearable/misc/widget_install_to_external.cpp b/src_wearable/misc/widget_install_to_external.cpp new file mode 100644 index 0000000..b0219dd --- /dev/null +++ b/src_wearable/misc/widget_install_to_external.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_install_to_external.cpp + * @author Soyoung Kim (sy037.kim@smasung.com) + */ +#include "widget_install_to_external.h" + +#include +#include +#include + +IMPLEMENT_SAFE_SINGLETON(WidgetInstallToExt) + +WidgetInstallToExt::WidgetInstallToExt() : + m_handle(NULL), + m_appId("") +{} + +WidgetInstallToExt::~WidgetInstallToExt() +{} + +void WidgetInstallToExt::initialize(std::string appId) +{ + _D("WidgetInstallToExt::initialize()"); + m_appId = appId; + + if (NULL == m_handle) { + m_handle = app2ext_init(APP2EXT_SD_CARD); + if (NULL == m_handle) { + ThrowMsg(Exception::ErrorInstallToExt, "initialize failed"); + } + } +} + +void WidgetInstallToExt::deinitialize() +{ + _D("WidgetInstallToExt::deinitialize()"); + if (NULL != m_handle) { + if (0 < app2ext_deinit(m_handle)) { + ThrowMsg(Exception::ErrorInstallToExt, + "app2ext deinitialize \ + failed"); + } + } +} + +void WidgetInstallToExt::preInstallation(GList *dirList, int dSize) +{ + _D("WidgetInstallToExt::preInstallation()"); + Assert(m_handle); + + int ret = m_handle->interface.pre_install(m_appId.c_str(), dirList, dSize); + + if (APP2EXT_SUCCESS == ret) { + _D("App2Ext pre install success"); + } else { + postInstallation(false); + ThrowMsg(Exception::ErrorInstallToExt, "pre-install failed"); + } +} + +void WidgetInstallToExt::postInstallation(bool status) +{ + _D("WidgetInstallToExt::postInstallation()"); + + if (NULL != m_handle) { + if (status) { + m_handle->interface.post_install(m_appId.c_str(), + APP2EXT_STATUS_SUCCESS); + } else { + m_handle->interface.post_install(m_appId.c_str(), + APP2EXT_STATUS_FAILED); + } + } +} + +void WidgetInstallToExt::preUpgrade(GList *dirList, int dSize) +{ + _D("WidgetInstallToExt::preUpgrade()"); + Assert(m_handle); + + int ret = m_handle->interface.pre_upgrade(m_appId.c_str(), dirList, dSize); + if (APP2EXT_SUCCESS == ret) { + _D("App2Ext pre-upgrade success"); + } else { + postUpgrade(false); + ThrowMsg(Exception::ErrorInstallToExt, "pre-upgrade failed"); + } +} + +void WidgetInstallToExt::postUpgrade(bool status) +{ + _D("WidgetInstallToExt::postUpgrade()"); + if (NULL != m_handle) { + if (status) { + m_handle->interface.post_upgrade(m_appId.c_str(), + APP2EXT_STATUS_SUCCESS); + } else { + m_handle->interface.post_upgrade(m_appId.c_str(), + APP2EXT_STATUS_FAILED); + } + } +} + +void WidgetInstallToExt::uninstallation() +{ + _D("WidgetInstallToExt::uninstallation()"); + + Assert(m_handle); + + int ret = m_handle->interface.pre_uninstall(m_appId.c_str()); + if (APP2EXT_SUCCESS == ret) { + if (APP2EXT_SUCCESS == + m_handle->interface.post_uninstall(m_appId.c_str())) + { + _D("App2Ext pre-uninstall success"); + } else { + ThrowMsg(Exception::ErrorInstallToExt, "post-uninstall failed"); + } + } else { + ThrowMsg(Exception::ErrorInstallToExt, "pre-uninstall failed"); + } +} + +void WidgetInstallToExt::disable() +{ + _D("WidgetInstallToExt::disable()"); + if (NULL != m_handle) { + int ret = m_handle->interface.disable(m_appId.c_str()); + if (APP2EXT_SUCCESS != ret && APP2EXT_ERROR_UNMOUNT != ret) { + ThrowMsg(Exception::ErrorInstallToExt, "disable failed"); + } + } +} diff --git a/src_wearable/misc/widget_install_to_external.h b/src_wearable/misc/widget_install_to_external.h new file mode 100644 index 0000000..cc9c4df --- /dev/null +++ b/src_wearable/misc/widget_install_to_external.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_install_to_external.h + * @author Soyoung Kim (sy037.kim@smasung.com) + */ +#ifndef WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H +#define WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H + +#include +#include +#include +#include + +class WidgetInstallToExt +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ErrorInstallToExt) + }; + + void initialize(std::string appId); + void deinitialize(); + void preInstallation(GList* dirList, int dSize); + void postInstallation(bool status); + void preUpgrade(GList* dirList, int dSize); + void postUpgrade(bool status); + void uninstallation(); + void disable(); + + private: + app2ext_handle *m_handle; + std::string m_appId; + + WidgetInstallToExt(); + ~WidgetInstallToExt(); + + friend class DPL::Singleton; +}; + +typedef DPL::Singleton WidgetInstallToExtSingleton; + +#endif // WRT_INSTALLER_SRC_MISC_WIDGET_INSTALL_TO_EXTERNAL_H diff --git a/src_wearable/misc/widget_location.cpp b/src_wearable/misc/widget_location.cpp new file mode 100755 index 0000000..00f1037 --- /dev/null +++ b/src_wearable/misc/widget_location.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_location.cpp + * @author Iwanek Tomasz (t.iwanek@smasung.com) + */ +#include "widget_location.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +WidgetLocation::DirectoryDeletor::DirectoryDeletor(bool isReadOnly) : + m_dirpath(Jobs::WidgetInstall::createTempPath(isReadOnly)) +{} + +WidgetLocation::DirectoryDeletor::DirectoryDeletor(std::string tempPath) : + m_dirpath(tempPath) +{} + +WidgetLocation::DirectoryDeletor::~DirectoryDeletor() +{ + _D("Removing widget installation temporary directory: %s", m_dirpath.c_str()); + std::string roPath = WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + + if (0 != m_dirpath.compare(0, roPath.length(), roPath)) { + if (!WrtUtilRemove(m_dirpath)) { + _W("Fail at removing directory: %s", m_dirpath.c_str()); + } + } +} + +std::string WidgetLocation::DirectoryDeletor::getTempPath() const +{ + return m_dirpath; +} + +WidgetLocation::WidgetLocation() : + m_temp(new WidgetLocation::DirectoryDeletor(true)) +{} + +WidgetLocation::WidgetLocation(const std::string & widgetname) : + m_pkgid(widgetname), + m_temp(new WidgetLocation::DirectoryDeletor(false)) +{} + +WidgetLocation::~WidgetLocation() +{} + +WidgetLocation::WidgetLocation(const std::string & widgetname, + std::string sourcePath, + WrtDB::PackagingType t, + bool isReadonly, + InstallMode::ExtensionType eType) : + m_pkgid(widgetname), + m_widgetSource(sourcePath), + m_type(t), + m_temp( + new WidgetLocation::DirectoryDeletor(isReadonly)), + m_extensionType(eType) +{ + if (isReadonly) { + m_installedPath += WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + m_installedPath += WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + if (access(m_widgetSource.c_str(), F_OK) != 0) { + m_widgetSource = m_installedPath + "/" + m_pkgid; + } +} + +WidgetLocation::WidgetLocation(const std::string & widgetname, + std::string sourcePath, + std::string dirPath, + WrtDB::PackagingType t, + bool isReadonly, + InstallMode::ExtensionType eType) : + m_pkgid(widgetname), + m_widgetSource(sourcePath), + m_type(t), + m_temp(new WidgetLocation::DirectoryDeletor(dirPath)), + m_extensionType(eType) +{ + if (isReadonly) { + m_installedPath += WrtDB::GlobalConfig::GetUserPreloadedWidgetPath(); + } else { + m_installedPath += WrtDB::GlobalConfig::GetUserInstalledWidgetPath(); + } + if (access(m_widgetSource.c_str(), F_OK) != 0) { + m_widgetSource = m_installedPath + "/" + m_pkgid; + } +} + +// TODO cache all these paths +std::string WidgetLocation::getInstallationDir() const +{ + return m_installedPath; +} + +std::string WidgetLocation::getPackageInstallationDir() const +{ + return m_installedPath + "/" + m_pkgid; +} + +std::string WidgetLocation::getSourceDir() const +{ + return m_installedPath + "/" + + m_pkgid + WrtDB::GlobalConfig::GetWidgetSrcPath(); +} + +std::string WidgetLocation::getBinaryDir() const +{ + return m_installedPath + "/" + + m_pkgid + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getUserBinaryDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getExecFile() const +{ + return getBinaryDir() + "/" + m_appid; +} + +std::string WidgetLocation::getBackupDir() const +{ + return getPackageInstallationDir() + ".backup"; +} + +std::string WidgetLocation::getBackupSourceDir() const +{ + return getBackupDir() + WrtDB::GlobalConfig::GetWidgetSrcPath(); +} + +std::string WidgetLocation::getBackupBinaryDir() const +{ + return getBackupDir() + WrtDB::GlobalConfig::GetUserWidgetExecPath(); +} + +std::string WidgetLocation::getBackupExecFile() const +{ + return getBackupBinaryDir() + "/" + m_appid; +} + +std::string WidgetLocation::getBackupPrivateDir() const +{ + return getBackupDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); +} + +std::string WidgetLocation::getUserDataRootDir() const +{ + return std::string(WrtDB::GlobalConfig::GetWidgetUserDataPath()) + + "/" + m_pkgid; +} + +std::string WidgetLocation::getPrivateStorageDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateStoragePath(); +} + +std::string WidgetLocation::getPrivateTempStorageDir() const +{ + return getUserDataRootDir() + "/" + + WrtDB::GlobalConfig::GetWidgetPrivateTempStoragePath(); +} + + +std::string WidgetLocation::getTemporaryPackageDir() const +{ + return m_temp->getTempPath(); +} + +std::string WidgetLocation::getTemporaryRootDir() const +{ + if (m_extensionType == InstallMode::ExtensionType::DIR) { + return getWidgetSource() + WrtDB::GlobalConfig::GetWidgetSrcPath(); + } + return getSourceDir(); +} + +DPL::String WidgetLocation::getPkgId() const +{ + return DPL::FromUTF8String(m_pkgid); +} + +std::string WidgetLocation::getInstalledIconPath() const +{ + return m_iconPath; +} + +std::string WidgetLocation::getWidgetSource() const +{ + return m_widgetSource; +} + +void WidgetLocation::setIconTargetFilenameForLocale(const std::string & icon) +{ + m_iconPath = icon; +} + +void WidgetLocation::registerExternalLocation(const std::string & file) +{ + m_externals.push_back(file); +} + +WrtDB::ExternalLocationList WidgetLocation::listExternalLocations() const +{ + return m_externals; +} + +void WidgetLocation::registerAppid(const std::string & appid) +{ + m_appid = appid; +} + +#ifdef SERVICE_ENABLED +void WidgetLocation::registerServiceAppid(const std::string & svcAppid) +{ + m_svcAppid = svcAppid; +} +#endif + +std::string WidgetLocation::getSharedRootDir() const +{ + /* TODO : add wrt-commons*/ + return getUserDataRootDir() + "/shared"; +} + +std::string WidgetLocation::getSharedResourceDir() const +{ + return getSharedRootDir() + "/res"; +} + +std::string WidgetLocation::getSharedDataDir() const +{ + return getSharedRootDir() + "/data"; +} + +std::string WidgetLocation::getSharedTrustedDir() const +{ + return getSharedRootDir() + "/trusted"; +} + +std::string WidgetLocation::getBackupSharedDir() const +{ + return getBackupDir() + "/shared"; +} + +std::string WidgetLocation::getBackupSharedDataDir() const +{ + return getBackupSharedDir() + "/data"; +} + +std::string WidgetLocation::getBackupSharedTrustedDir() const +{ + return getBackupSharedDir() + "/trusted"; +} + +std::string WidgetLocation::getNPPluginsExecFile() const +{ + return getBinaryDir() + "/" + m_appid + ".npruntime"; +} + +std::string WidgetLocation::getNPPluginsDir() const +{ + return getSourceDir() + "/plugins"; +} diff --git a/src_wearable/misc/widget_location.h b/src_wearable/misc/widget_location.h new file mode 100755 index 0000000..f7fd546 --- /dev/null +++ b/src_wearable/misc/widget_location.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_location.h + * @author Iwanek Tomasz (t.iwanek@smasung.com) + */ +#ifndef WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H +#define WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H + +#include +#include + +#include +#include +#include +#include + +/** + * @brief The WidgetLocation class + * + * Object that stores locations of several files/directories according + * to package name + * + * Current package layout (of installed package) is following: + * + * /opt/apps/[package_name] + * \_____________ /data + * \_____________ /share + * \_____________ /bin + * \_____________ /bin/[id_of_installed_package] + * \_____________ /res/wgt/ + * \___ config.xml + * \___ [widgets_archive_content] + * + * 1) Normal Widget + * Developer provides content of res/wgt directory (package contains that + * directory as root). + * + * 2) For OSP Service Hybrid App is actually a bit different: + * Root is OSP Service directory, WebApp content is located in [root]/res/wgt + * + * Temporary directory is directory when widget is placed at the begining + * of installation process. After parsing process of config.xml, destination + * directory is created. + */ +class WidgetLocation +{ + class DirectoryDeletor + { + public: + DirectoryDeletor(); + DirectoryDeletor(std::string tempPath); + DirectoryDeletor(bool isPreload); + + ~DirectoryDeletor(); + std::string getTempPath() const; + + private: + std::string m_dirpath; + }; + + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, NoTemporaryPath) + /** + * @brief WidgetLocation + * + * Creates useless object. Needed by optional type + */ + WidgetLocation(); + /** + * @brief WidgetLocation Builds paths for widget location during + * uninstallation + * + * Uninstallation process needs only installed package directory. + * + * @param widgetname name of widget + */ + explicit WidgetLocation(const std::string & widgetname); + /** + * @brief WidgetLocation Builds paths for widget location during + * installation + * + * @param widgetname name of widget + * @param sourcePath given source path + * @param t declaraced type of widget if type is needed + * + * In destruction removes temporary directory + */ + WidgetLocation(const std::string & widgetname, std::string sourcePath, + WrtDB::PackagingType t = WrtDB::PKG_TYPE_NOMAL_WEB_APP, + bool isReadonly = false, + InstallMode::ExtensionType eType = + InstallMode::ExtensionType::WGT); + + WidgetLocation(const std::string & widgetname, std::string sourcePath, + std::string dirPath, + WrtDB::PackagingType t = WrtDB::PKG_TYPE_NOMAL_WEB_APP, + bool isReadonly = false, + InstallMode::ExtensionType eType = + InstallMode::ExtensionType::WGT); + + ~WidgetLocation(); + + // Installed paths + std::string getInstallationDir() const; // /opt/apps or /usr/apps + std::string getPackageInstallationDir() const; // /opt/apps/[package] + std::string getSourceDir() const; // /opt/apps/[package]/res/wgt + std::string getBinaryDir() const; // /opt/apps/[package]/bin or /usr/apps/[package]/bin + std::string getUserBinaryDir() const; // /opt/apps/[package]/bin + std::string getExecFile() const; // /opt/apps/[package]/bin/[package] + std::string getBackupDir() const; // /opt/apps/[package].backup + std::string getBackupSourceDir() const; // /opt/apps/[pkg].backup/res/wgt + std::string getBackupBinaryDir() const; // /opt/apps/[pkg].backup/bin + std::string getBackupExecFile() const; // /opt/apps/[pkg].backup/bin/[pkg] + std::string getBackupPrivateDir() const; // /opt/apps/[pkg].backup/data + std::string getUserDataRootDir() const; // /opt/usr/apps/[package] + std::string getPrivateStorageDir() const; // /opt/usr/apps/[package]/data + std::string getPrivateTempStorageDir() const; // /opt/usr/apps/[package]/tmp + std::string getSharedRootDir() const; // /opt/usr/apps/[package]/shared + std::string getSharedResourceDir() const; // /opt/usr/apps/[package]/shared/res + std::string getSharedDataDir() const; // /opt/usr/apps/[package]/shared/data + std::string getSharedTrustedDir() const; // /opt/usr/apps/[package]/shared/trusted + std::string getBackupSharedDir() const; // /opt/usr/apps/[package].backup/shared + std::string getBackupSharedDataDir() const; // /opt/usr/apps/[package].backup/shared/data + std::string getBackupSharedTrustedDir() const; // /opt/usr/apps/[package].backup/shared/trusted + std::string getNPPluginsDir() const; // /opt/usr/apps/[package]/res/wgt/plugins + std::string getNPPluginsExecFile() const; // /opt/usr/apps/[package]/bin/{execfile} + + // Temporary paths + /** + * @brief getTemporaryRootDir + * @return value of root for developer's provide package (root of unpacked + * .wgt file) + */ + std::string getTemporaryPackageDir() const; + /** + * @brief getTemporaryRootDir + * + * Value of this will differs according to type of installed widget. + * + * @return value of root for content in temporary directory to be copied + * into 'res/wgt' + */ + std::string getTemporaryRootDir() const; + + //icons + /** + * @brief setIconTargetFilenameForLocale set installed ion path according to + * locale + * @param icon path of application icon + */ + void setIconTargetFilenameForLocale(const std::string &icon); + + /** + * @brief getIconTargetFilename gets icon full path + * @param languageTag language tag + * @return value of full path + */ + std::string getInstalledIconPath() const; + + /** + * @brief getWidgetSourcePath return widget's source path given to installer + * @return value of source path + */ + std::string getWidgetSource() const; + /** + * @brief pkgid Returns pkgid + * @return pkgid + */ + DPL::String getPkgId() const; + + //external files + /** + * @brief registerExternalFile Registers file for database registration + * @param file + * + * Registered file will be stored in database and removed automatically a + * + * @return + */ + void registerExternalLocation(const std::string & file); + /** + * @brief listExternalFile list all file to be registered + */ + WrtDB::ExternalLocationList listExternalLocations() const; + + /* + * @brief set appid + */ + void registerAppid(const std::string & appid); +#ifdef SERVICE_ENABLED + void registerServiceAppid(const std::string & svcAppid); +#endif + + private: + std::string m_pkgid; //id of package + std::string m_widgetSource; // Source widget zip + // file/widget url + std::string m_appid; //id of app +#ifdef SERVICE_ENABLED + std::string m_svcAppid; +#endif + std::string m_iconPath; //installed icon path + WrtDB::PackagingType m_type; + std::shared_ptr m_temp; //directory + WrtDB::ExternalLocationList m_externals; + std::string m_installedPath; + InstallMode::ExtensionType m_extensionType; +}; + +#endif // WRT_INSTALLER_SRC_MISC_WIDGET_LOCATION_H diff --git a/src_wearable/pkg-manager/CMakeLists.txt b/src_wearable/pkg-manager/CMakeLists.txt new file mode 100755 index 0000000..d65c064 --- /dev/null +++ b/src_wearable/pkg-manager/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +SET(BACKLIB_SRCS + backendlib.cpp + ${PROJECT_SOURCE_DIR}/src/configuration_parser/widget_parser.cpp + ${PROJECT_SOURCE_DIR}/src/configuration_parser/parser_runner.cpp + ${PROJECT_SOURCE_DIR}/src/configuration_parser/ignoring_parser.cpp + ${PROJECT_SOURCE_DIR}/src/configuration_parser/deny_all_parser.cpp + ${PROJECT_SOURCE_DIR}/src/configuration_parser/libiriwrapper.cpp + ${PROJECT_SOURCE_DIR}/src/wrt-installer/language_subtag_rst_tree.cpp +) + +PKG_CHECK_MODULES(WRT_BACKLIB_PKGS + dpl-efl + dpl-wrt-dao-ro + dpl-wrt-dao-rw + dpl-utils-efl + pkgmgr-installer + pkgmgr-types + pkgmgr + dlog + libpcrecpp + wrt-commons-i18n-dao-ro + REQUIRED) + +INCLUDE_DIRECTORIES( + ${WRT_BACKLIB_PKGS_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/configuration_parser +) + +ADD_LIBRARY(${TARGET_BACKEND_LIB} SHARED + ${BACKLIB_SRCS} +) + +TARGET_LINK_LIBRARIES(${TARGET_BACKEND_LIB} + ${WRT_BACKLIB_PKGS_LIBRARIES} +) + +SET_TARGET_PROPERTIES(${TARGET_BACKEND_LIB} PROPERTIES + LINK_FLAGS "-Wl,--as-needed -Wl,--hash-style=both" +) + +INSTALL(TARGETS ${TARGET_BACKEND_LIB} + DESTINATION etc/package-manager/backendlib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +#SYMLINK +set(SYMLINK_USE OFF) +set(SYMLINK_DEST "${CMAKE_INSTALL_PREFIX}/etc/package-manager") + +IF(SYMLINK_USE) + ADD_CUSTOM_COMMAND(OUTPUT ${SYMLINK_DEST}/backend/wgt + COMMAND mkdir + ARGS -p ${SYMLINK_DEST}/backend + COMMAND ln + ARGS -sf ${CMAKE_INSTALL_PREFIX}/bin/${BACKEND} ${SYMLINK_DEST}/backend/wgt + DEPENDS ${BACKEND} + ) + ADD_CUSTOM_TARGET(test_symlinks ALL + DEPENDS ${SYMLINK_DEST}/backend/wgt + ) +ENDIF(SYMLINK_USE) diff --git a/src_wearable/pkg-manager/DESCRIPTION b/src_wearable/pkg-manager/DESCRIPTION new file mode 100644 index 0000000..a9d3696 --- /dev/null +++ b/src_wearable/pkg-manager/DESCRIPTION @@ -0,0 +1 @@ +Executables for interfacing with the package manager diff --git a/src_wearable/pkg-manager/backendlib.cpp b/src_wearable/pkg-manager/backendlib.cpp new file mode 100644 index 0000000..4260d1e --- /dev/null +++ b/src_wearable/pkg-manager/backendlib.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file backendlib.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @version 0.1 + * @brief This is implementation file for providing widget information + * to package manager + */ +#include "package-manager-plugin.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "root_parser.h" +#include "widget_parser.h" +#include "parser_runner.h" +#include + +using namespace WrtDB; + +#undef TRUE +#undef FALSE +#define TRUE 0 +#define FALSE -1 +#define GET_DIRECTORY_SIZE_KB(x) (x) / 1024 + +#ifdef __cplusplus +extern "C" +{ +#endif + +static void pkg_native_plugin_on_unload(); +static int pkg_plugin_app_is_installed(const char *pkg_name); +static int pkg_plugin_get_installed_apps_list(const char *category, + const char *option, + package_manager_pkg_info_t **list, + int *count); +static int pkg_plugin_get_app_detail_info( + const char *pkg_name, + package_manager_pkg_detail_info_t * + pkg_detail_info); +static int pkg_plugin_get_app_detail_info_from_package( + const char *pkg_path, + package_manager_pkg_detail_info_t + *pkg_detail_info); + +pkgmgr_info *pkgmgr_client_check_pkginfo_from_file(const char *pkg_path); + +static void pkg_native_plugin_on_unload() +{ + _D("pkg_native_plugin_unload() is called"); +} + +static int pkg_plugin_app_is_installed(const char *pkg_name) +{ + const char* REG_PKGID_PATTERN = "^[a-zA-Z0-9]{10}$"; + _D("pkg_plugin_app_is_installed() is called"); + + WrtDB::WrtDatabase::attachToThreadRO(); + + regex_t reg; + if (regcomp(®, REG_PKGID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) { + _D("Regcomp failed"); + } + + WrtDB::TizenAppId appid; + + if (!(regexec(®, pkg_name, + static_cast(0), NULL, 0) == 0)) + { + _E("Invalid argument : %s", pkg_name); + return FALSE; + } + + Try { + WrtDB::TizenPkgId pkgid(DPL::FromUTF8String(pkg_name)); + appid = WidgetDAOReadOnly::getTizenAppId(pkgid); + _D("appid : %ls", appid.c_str()); + } Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + WrtDB::WrtDatabase::detachFromThread(); + return FALSE; + } + WrtDB::WrtDatabase::detachFromThread(); + return TRUE; +} + +static int pkg_plugin_get_installed_apps_list(const char * /*category*/, + const char * /*option*/, + package_manager_pkg_info_t **list, + int *count) +{ + _D("pkg_plugin_get_installed_apps_list() is called"); + + package_manager_pkg_info_t *pkg_list = NULL; + package_manager_pkg_info_t *pkg_last = NULL; + + WrtDB::WrtDatabase::attachToThreadRO(); + TizenAppIdList tizenAppIdList = WidgetDAOReadOnly::getTizenAppIdList(); + *count = 0; + + FOREACH(iterator, tizenAppIdList) { + package_manager_pkg_info_t *pkg_info = + static_cast + (malloc(sizeof(package_manager_pkg_info_t))); + + if (NULL == pkg_list) { + pkg_list = pkg_info; + pkg_last = pkg_info; + } else { + pkg_last->next = pkg_info; + } + + TizenAppId tzAppid = *iterator; + WidgetDAOReadOnly widget(tzAppid); + strncpy(pkg_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + snprintf(pkg_info->pkg_name, PKG_NAME_STRING_LEN_MAX, "%s", + DPL::ToUTF8String(tzAppid).c_str()); + + DPL::OptionalString version = widget.getVersion(); + if (!!version) { + strncpy(pkg_info->version, + DPL::ToUTF8String(*version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + (*count)++; + pkg_last = pkg_info; + } + *list = pkg_list; + WrtDB::WrtDatabase::detachFromThread(); + + return TRUE; +} + +static int pkg_plugin_get_app_detail_info( + const char *pkg_name, + package_manager_pkg_detail_info_t * + pkg_detail_info) +{ + _D("pkg_plugin_get_app_detail_info() is called"); + + WrtDB::WrtDatabase::attachToThreadRO(); + + WrtDB::TizenAppId appid; + Try { + WrtDB::TizenPkgId pkgid(DPL::FromUTF8String(pkg_name)); + appid = WidgetDAOReadOnly::getTizenAppId(pkgid); + _D("appid : %ls", appid.c_str()); + } Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) { + WrtDB::WrtDatabase::detachFromThread(); + return FALSE; + } + + WidgetDAOReadOnly widget(appid); + + DPL::OptionalString version = widget.getVersion(); + DPL::OptionalString id = widget.getGUID(); + DPL::OptionalString locale = widget.getDefaultlocale(); + + if (!!version) { + strncpy(pkg_detail_info->version, + DPL::ToUTF8String(*version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + snprintf(pkg_detail_info->pkgid, PKG_NAME_STRING_LEN_MAX, "%s", + pkg_name); + snprintf(pkg_detail_info->optional_id, PKG_NAME_STRING_LEN_MAX, "%s", + DPL::ToUTF8String(appid).c_str()); + WidgetLocalizedInfo localizedInfo; + + if (!locale) { + _D("locale is NULL"); + DPL::String languageTag(L""); + localizedInfo = widget.getLocalizedInfo(languageTag); + } else { + localizedInfo = widget.getLocalizedInfo(*locale); + } + DPL::OptionalString desc(localizedInfo.description); + + if (!!desc) { + strncpy(pkg_detail_info->pkg_description, + DPL::ToUTF8String(*desc).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + strncpy(pkg_detail_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + strncpy(pkg_detail_info->pkg_name, pkg_name, PKG_NAME_STRING_LEN_MAX - 1); + + std::string min_version = DPL::ToUTF8String((*widget.getMinimumWacVersion())); + + strncpy(pkg_detail_info->min_platform_version, min_version.c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + + /* set installed time */ + pkg_detail_info->installed_time = widget.getInstallTime(); + + /* set Widget size */ + DPL::String pkgName = DPL::FromUTF8String(pkg_name); + std::string installPath = WidgetConfig::GetWidgetBasePath(pkgName); + std::string persistentPath = + WidgetConfig::GetWidgetPersistentStoragePath(pkgName); + std::string tempPath = + WidgetConfig::GetWidgetTemporaryStoragePath(pkgName); + installPath += "/"; + tempPath += "/"; + persistentPath += "/"; + + size_t installedSize = Utils::getFolderSize(installPath); + size_t persistentSize = Utils::getFolderSize(persistentPath); + size_t appSize = installedSize - persistentSize; + size_t dataSize = persistentSize + Utils::getFolderSize(tempPath); + + pkg_detail_info->installed_size = GET_DIRECTORY_SIZE_KB(installedSize); + pkg_detail_info->app_size = GET_DIRECTORY_SIZE_KB(appSize); + pkg_detail_info->data_size = GET_DIRECTORY_SIZE_KB(dataSize); + + WrtDB::WrtDatabase::detachFromThread(); + return TRUE; +} + +int getConfigParserData(const std::string &widgetPath, ConfigParserData& configInfo) +{ + const char* CONFIG_XML = "config.xml"; + const char* WITH_OSP_XML = "res/wgt/config.xml"; + + Try { + ParserRunner parser; + + std::unique_ptr zipFile( + new DPL::ZipInput(widgetPath)); + + std::unique_ptr configFile; + + // Open config.xml file + Try { + configFile.reset(zipFile->OpenFile(CONFIG_XML)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + configFile.reset(zipFile->OpenFile(WITH_OSP_XML)); + } + + // Extract config + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(configFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + parser.Parse(&buffer, + ElementParserPtr( + new RootParser(configInfo, + DPL:: + FromUTF32String( + L"widget")))); + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _E("Failed to open widget package"); + return FALSE; + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _E("Failed to open config.xml file"); + return FALSE; + } + Catch(DPL::CopyFailed) + { + _E("Failed to extract config.xml file"); + return FALSE; + } + Catch(DPL::FileInput::Exception::OpenFailed) + { + _E("Failed to open config.xml file"); + return FALSE; + } + Catch(ElementParser::Exception::ParseError) + { + _E("Failed to parse config.xml file"); + return FALSE; + } + Catch(DPL::ZipInput::Exception::SeekFileFailed) + { + _E("Failed to seek widget archive - corrupted package?"); + return FALSE; + } + + return TRUE; +} + +char* getIconInfo(const std::string &widgetPath, + const std::string &icon_name, int &icon_size) +{ + Try { + std::unique_ptr zipFile( + new DPL::ZipInput(widgetPath)); + + std::unique_ptr iconFile; + + Try { + iconFile.reset(zipFile->OpenFile(icon_name)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _D("This web app is hybrid web app"); + std::string hybrid_icon = "res/wgt/" + icon_name; + iconFile.reset(zipFile->OpenFile(hybrid_icon)); + } + + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(iconFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + icon_size = buffer.Size(); + char *getBuffer = (char*) calloc(1, (sizeof(char) * icon_size) + 1); + buffer.Flatten(getBuffer, buffer.Size()); + return getBuffer; + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _D("Failed to open widget package"); + return NULL; + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + _D("Not found icon file %s", icon_name.c_str()); + return NULL; + } +} + +char* getIconForLocale(const std::string& bp, const std::string& tag, + const std::string& icon, int & size) +{ + std::string iconPath; + if (!tag.empty()) { + iconPath += std::string("locales/") + tag; + } + if (!iconPath.empty()) { + iconPath += "/"; + } + + iconPath += icon; + return getIconInfo(bp, iconPath, size); +} + +char* getIcon(const std::string & basepath, const WrtDB::ConfigParserData & config, int & size) +{ + const std::list defaultIcons{ "icon.svg", "icon.ico", "icon.png", "icon.gif", "icon.jpg" }; + LanguageTags tagsList = + LanguageTagsProviderSingleton::Instance().getLanguageTags(); + + char * result = NULL; + + //for each locale tag - searching for icon presence and returning raw data + //first found is best as locale tags are ordered + FOREACH(tag, tagsList) + { + FOREACH(icon, config.iconsList) + { + std::string src = DPL::ToUTF8String(icon->src); + result = getIconForLocale(basepath, DPL::ToUTF8String(*tag), src, size); + if(result) { + return result; + } + } + FOREACH(i, defaultIcons) + { + result = getIconForLocale(basepath, DPL::ToUTF8String(*tag), *i, size); + if(result) { + return result; + } + } + } + return NULL; +} + +int getWidgetDetailInfoFromPackage(const char* pkgPath, + package_manager_pkg_detail_info_t* pkg_detail_info) +{ + const std::string widget_path(pkgPath); + ConfigParserData configInfo; + + if (FALSE == getConfigParserData(widget_path, configInfo)) { + return FALSE; + } + + strncpy(pkg_detail_info->pkg_type, "wgt", PKG_TYPE_STRING_LEN_MAX); + if (!!configInfo.tizenPkgId) { + strncpy(pkg_detail_info->pkgid, + DPL::ToUTF8String(*configInfo.tizenPkgId).c_str(), PKG_TYPE_STRING_LEN_MAX - 1); + } + if (!!configInfo.tizenAppId) { + strncpy(pkg_detail_info->pkg_name, + DPL::ToUTF8String(*configInfo.tizenAppId).c_str(), + PKG_NAME_STRING_LEN_MAX - 1); + } + if (!!configInfo.version) { + strncpy(pkg_detail_info->version, + DPL::ToUTF8String(*configInfo.version).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + DPL::OptionalString name; + DPL::OptionalString desc; + + LanguageTags tags = LanguageTagsProviderSingleton::Instance().getLanguageTags(); + + auto toLowerCase = [](const DPL::String & r) + { + DPL::String result; + std::transform(r.begin(), r.end(), std::inserter(result, result.begin()), ::tolower); + return result; + }; + + if (!!configInfo.defaultlocale) + { + Locale & dl = *configInfo.defaultlocale; + configInfo.defaultlocale = toLowerCase(dl); + } + + bool found = false; + FOREACH(tag, tags) + { + *tag = toLowerCase(*tag); + FOREACH(localizedData, configInfo.localizedDataSet) + { + Locale i = localizedData->first; + i = toLowerCase(i); + + if (!!configInfo.defaultlocale && *configInfo.defaultlocale == i) + { + name = localizedData->second.name; + desc = localizedData->second.description; + } + if (*tag == i) + { + name = localizedData->second.name; + desc = localizedData->second.description; + found = true; + break; + } + } + if(found) break; + } + + if (!!name) { + strncpy(pkg_detail_info->label, DPL::ToUTF8String(*name).c_str(), + PKG_LABEL_STRING_LEN_MAX - 1); + } + + if (!!desc) { + strncpy(pkg_detail_info->pkg_description, + DPL::ToUTF8String(*desc).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + + if (!!configInfo.tizenMinVersionRequired) { + strncpy(pkg_detail_info->min_platform_version, + DPL::ToUTF8String(*configInfo.tizenMinVersionRequired).c_str(), + PKG_VERSION_STRING_LEN_MAX - 1); + } + + if (!!configInfo.authorName) { + strncpy(pkg_detail_info->author, + DPL::ToUTF8String(*configInfo.authorName).c_str(), + PKG_VALUE_STRING_LEN_MAX - 1); + } + + + pkg_detail_info->privilege_list = NULL; + FOREACH(it, configInfo.featuresList) { + std::string featureInfo = DPL::ToUTF8String(it->name); + _D("privilege : %s", featureInfo.c_str()); + int length = featureInfo.size(); + char *privilege = (char*) calloc(1, (sizeof(char) * (length + 1))); + snprintf(privilege, length + 1, "%s", featureInfo.c_str()); + pkg_detail_info->privilege_list = + g_list_append(pkg_detail_info->privilege_list, privilege); + } + + char* icon_buf = getIcon(widget_path, configInfo, pkg_detail_info->icon_size); + + if (icon_buf) { + _D("icon size : %d", pkg_detail_info->icon_size); + pkg_detail_info->icon_buf = icon_buf; + } else { + _D("No icon"); + pkg_detail_info->icon_size = 0; + pkg_detail_info->icon_buf = NULL; + } + + return TRUE; +} + +static int pkg_plugin_get_app_detail_info_from_package( + const char * pkg_path, + package_manager_pkg_detail_info_t * pkg_detail_info) +{ + _D("pkg_plugin_get_app_detail_info_from_package() is called"); + return getWidgetDetailInfoFromPackage(pkg_path, pkg_detail_info); +} + +pkgmgr_info *pkgmgr_client_check_pkginfo_from_file(const char *pkg_path) +{ + _D("pkgmgr_client_check_pkginfo_from_file() is called"); + package_manager_pkg_detail_info_t *pkg_detail_info; + pkg_detail_info = (package_manager_pkg_detail_info_t*)malloc( + sizeof(package_manager_pkg_detail_info_t)); + int ret = getWidgetDetailInfoFromPackage(pkg_path, pkg_detail_info); + if (FALSE == ret) { + _E("Failed to get package detail info "); + free(pkg_detail_info); + return NULL; + } + return reinterpret_cast(pkg_detail_info); +} + +__attribute__ ((visibility("default"))) +int pkg_plugin_on_load(pkg_plugin_set *set) +{ + DPL::Log::LogSystemSingleton::Instance().SetTag("WGT-BACKLIB"); + if (NULL == set) { + return FALSE; + } + memset(set, 0x00, sizeof(pkg_plugin_set)); + + set->plugin_on_unload = pkg_native_plugin_on_unload; + set->pkg_is_installed = pkg_plugin_app_is_installed; + set->get_installed_pkg_list = pkg_plugin_get_installed_apps_list; + set->get_pkg_detail_info = pkg_plugin_get_app_detail_info; + set->get_pkg_detail_info_from_package = + pkg_plugin_get_app_detail_info_from_package; + + return TRUE; +} + +#ifdef __cplusplus +} +#endif diff --git a/src_wearable/pkg-manager/pkgmgr_signal.cpp b/src_wearable/pkg-manager/pkgmgr_signal.cpp new file mode 100644 index 0000000..a5f2383 --- /dev/null +++ b/src_wearable/pkg-manager/pkgmgr_signal.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Yunchan Cho (yunchan.cho@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include + +#include +#include +#include + +namespace { +// package type sent in every signal +const char PKGMGR_WEBAPP_TYPE[] = "wgt"; + +// notification about opoeration start +const char PKGMGR_START_KEY[] = "start"; + +// value for new installation +const char PKGMGR_START_INSTALL[] = "install"; + +// value for update installation +const char PKGMGR_START_UPDATE[] = "update"; + +// value for uninstallation +const char PKGMGR_START_UNINSTALL[] = "uninstall"; + +// notification about progress of installation with percentage number +const char PKGMGR_PROGRESS_KEY[] = "install_percent"; + +// notification about icon path for installation frontend +const char PKGMGR_ICON_PATH[] = "icon_path"; + +// notification about error before end with given error code +// (currently, same as backend exit status) +const char PKGMGR_ERROR[] = "error"; + +// notification about end of installation with status +const char PKGMGR_END_KEY[] = "end"; + +// success value of end of installation +const char PKGMGR_END_SUCCESS[] = "ok"; + +// failure value of end of installation +const char PKGMGR_END_FAILURE[] = "fail"; +} + +namespace PackageManager { +PkgmgrSignal::PkgmgrSignal() : + m_initialized(false), + m_handle(NULL), + m_reqType(RequestType::UNSUPPORTED), + m_percent(0) +{} + +PkgmgrSignal::~PkgmgrSignal() +{ + deinitialize(); +} + +bool PkgmgrSignal::initialize(int argc, char* argv[]) +{ + if (m_handle) { + _D("Release already allocated pkgmgr handle"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + } + + m_handle = pkgmgr_installer_new(); + if (!m_handle) { + _E("Fail to get pkgmgr installer handle"); + return false; + } + + // set information from pkgmgr + if (!pkgmgr_installer_receive_request( + m_handle, argc, argv)) + { + auto pkgmgrtype = pkgmgr_installer_get_request_type(m_handle); + switch(pkgmgrtype) + { + case PKGMGR_REQ_INSTALL: + m_reqType = RequestType::INSTALL; + break; + case PKGMGR_REQ_UNINSTALL: + m_reqType = RequestType::UNINSTALL; + break; + case PKGMGR_REQ_REINSTALL: + m_reqType = RequestType::REINSTALL; + break; + default: + m_reqType = RequestType::UNSUPPORTED; + break; + } + + if (m_reqType == RequestType::UNSUPPORTED) + { + _E("Fail to get request type of pkgmgr"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + return false; + } + const char *callerId = pkgmgr_installer_get_caller_pkgid(m_handle); + if(callerId) + m_callerId = callerId; + + } else { + _E("Fail to get information of pkgmgr's request"); + pkgmgr_installer_free(m_handle); + m_handle = NULL; + return false; + } + + m_type = PKGMGR_WEBAPP_TYPE; + m_initialized = true; + return true; +} + +bool PkgmgrSignal::deinitialize() +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + if (!m_recoveryFile.empty() && (0 != unlink(m_recoveryFile.c_str()))) { + _E("Failed to remove %s", m_recoveryFile.c_str()); + } + + if (m_handle) { + pkgmgr_installer_free(m_handle); + } + + m_handle = NULL; + m_initialized = false; + return true; +} + +bool PkgmgrSignal::setPkgname(const std::string& name) +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + if (name.empty()) { + _E("name is empty"); + return false; + } + + m_pkgname = name; + _D("Success to set tizen package name: %s", m_pkgname.c_str()); + setRecoveryFile(); + + return true; +} + +void PkgmgrSignal::setRecoveryFile() +{ + std::string filePath = WrtDB::GlobalConfig::GetTempInstallInfoPath(); + filePath += "/" + m_pkgname; + + m_recoveryFile = filePath; + _D("SetRecoveryFile... %s", filePath.c_str()); + if (access(filePath.c_str(), F_OK) != 0) { + FILE *file = fopen(filePath.c_str(), "w"); + if (file != NULL) { + fclose(file); + } + } else { + _D("Recovery File : %s is already exist", filePath.c_str()); + } +} + +bool PkgmgrSignal::startJob(Jobs::InstallationType type) +{ + switch(type) + { + case Jobs::InstallationType::NewInstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_INSTALL); + break; + case Jobs::InstallationType::UpdateInstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_UPDATE); + break; + case Jobs::InstallationType::Uninstallation: + sendSignal(PKGMGR_START_KEY, PKGMGR_START_UNINSTALL); + break; + default: + _E("Trying to send unknown installation type to pkgmgr"); + return false; + } + return true; +} + +bool PkgmgrSignal::endJob(Jobs::Exceptions::Type ecode) +{ + if(ecode == Jobs::Exceptions::Type::Success) + { + return sendSignal(PKGMGR_END_KEY, PKGMGR_END_SUCCESS); + } + else + { + sendSignal(PKGMGR_ERROR, DPL::lexical_cast(ecode)); + return sendSignal(PKGMGR_END_KEY, PKGMGR_END_FAILURE); + } +} + +bool PkgmgrSignal::sendProgress(int percent) +{ + if (m_percent == percent) { + return true; + } + + m_percent = percent; + return sendSignal(PKGMGR_PROGRESS_KEY, DPL::lexical_cast(percent)); +} + +bool PkgmgrSignal::sendIconPath(const std::string & iconpath) +{ + return sendSignal(PKGMGR_ICON_PATH, iconpath); +} + +bool PkgmgrSignal::sendSignal(const std::string& key, + const std::string& value) const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + return false; + } + + if (key.empty() || value.empty()) { + _D("key or value is empty"); + return false; + } + + if (m_handle == NULL || m_type.empty()) { + _E("Some data of PkgmgrSignal is empty"); + return false; + } + + // send pkgmgr signal + if (pkgmgr_installer_send_signal( + m_handle, m_type.c_str(), m_pkgname.c_str(), + key.c_str(), value.c_str())) + { + _E("Fail to send pkgmgr signal"); + return false; + } + + _D("Success to send pkgmgr signal: %s - %s", key.c_str(), value.c_str()); + return true; +} + +std::string PkgmgrSignal::getPkgname() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_pkgname; +} + +PkgmgrSignal::RequestType PkgmgrSignal::getRequestedType() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_reqType; +} + +std::string PkgmgrSignal::getCallerId() const +{ + if (!m_initialized) { + _E("PkgmgrSingal not yet intialized"); + } + + return m_callerId; +} +} // PackageManager diff --git a/src_wearable/pkg-manager/pkgmgr_signal.h b/src_wearable/pkg-manager/pkgmgr_signal.h new file mode 100644 index 0000000..26ce559 --- /dev/null +++ b/src_wearable/pkg-manager/pkgmgr_signal.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Yunchan Cho (yunchan.cho@samsung.com) + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.2 + * @brief + */ + +#ifndef WRT_PKGMGR_SIGNAL_H_ +#define WRT_PKGMGR_SIGNAL_H_ + +#include + +struct pkgmgr_installer; + +namespace PackageManager { + +class PkgmgrSignal : public IPkgmgrSignal +{ +public: + enum class RequestType + { + UNSUPPORTED, + INSTALL, + UNINSTALL, + REINSTALL + }; + + bool initialize(int argc, char* argv[]); + bool deinitialize(); + bool setPkgname(const std::string& name); + std::string getPkgname() const; + RequestType getRequestedType() const; + std::string getCallerId() const; + + bool startJob(Jobs::InstallationType type); + bool endJob(Jobs::Exceptions::Type ecode); + bool sendProgress(int percent); + bool sendIconPath(const std::string & iconpath); + void setRecoveryFile(); + + PkgmgrSignal(); + virtual ~PkgmgrSignal(); + +protected: + bool sendSignal(const std::string& key, const std::string& value) const; + +private: + bool m_initialized; + pkgmgr_installer* m_handle; + std::string m_type; + std::string m_pkgname; + RequestType m_reqType; + std::string m_callerId; + int m_percent; + std::string m_recoveryFile; +}; +} // PackageManager + +#endif // WRT_PKGMGR_SIGNAL_H_ + diff --git a/src_wearable/pkg-manager/pkgmgr_signal_dummy.h b/src_wearable/pkg-manager/pkgmgr_signal_dummy.h new file mode 100644 index 0000000..42b0aa4 --- /dev/null +++ b/src_wearable/pkg-manager/pkgmgr_signal_dummy.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.1 + * @brief Dummy version of PkgmgrSignal. + */ + +#ifndef WRT_PKGMGR_SIGNAL_DUMMY_H_ +#define WRT_PKGMGR_SIGNAL_DUMMY_H_ + +#include + +#include + +namespace PackageManager { +class PkgmgrSignalDummy : public IPkgmgrSignal +{ + public: + PkgmgrSignalDummy() + {} + + virtual ~PkgmgrSignalDummy() + {} + + bool setPkgname(const std::string& /*name*/) + { + return false; + } + + std::string getPkgname() const + { + return ""; + } + + std::string getCallerId() const + { + return ""; + } + + bool startJob(Jobs::InstallationType type DPL_UNUSED) + { + return false; + } + + bool endJob(Jobs::Exceptions::Type ecode DPL_UNUSED) + { + return false; + } + + bool sendProgress(int percent DPL_UNUSED) + { + return false; + } + + bool sendIconPath(const std::string & iconpath DPL_UNUSED) + { + return false; + } +}; +} // PkgmgrSignalDummy + +#endif // WRT_PKGMGR_SIGNAL_DUMMY_H_ diff --git a/src_wearable/pkg-manager/pkgmgr_signal_interface.h b/src_wearable/pkg-manager/pkgmgr_signal_interface.h new file mode 100644 index 0000000..1e38a17 --- /dev/null +++ b/src_wearable/pkg-manager/pkgmgr_signal_interface.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Jan Olszak (j.olszak@samsung.com) + * @version 0.1 + * @brief Interface for PkgmgrSignal. + */ + +#ifndef WRT_PKGMGR_SIGNAL_INTERFACE_H_ +#define WRT_PKGMGR_SIGNAL_INTERFACE_H_ + +#include + +#include +#include + +namespace PackageManager { +class IPkgmgrSignal +{ + public: + virtual bool setPkgname(const std::string& name) = 0; + virtual std::string getPkgname() const = 0; + virtual std::string getCallerId() const = 0; + + virtual bool startJob(Jobs::InstallationType type) = 0; + virtual bool endJob(Jobs::Exceptions::Type ecode) = 0; + virtual bool sendProgress(int percent) = 0; + virtual bool sendIconPath(const std::string & iconpath) = 0; + virtual ~IPkgmgrSignal(){} +}; +} // IPkgmgrSignal + +#endif // WRT_PKGMGR_SIGNAL_INTERFACE_H_ diff --git a/src_wearable/wrt-installer/CMakeLists.txt b/src_wearable/wrt-installer/CMakeLists.txt new file mode 100644 index 0000000..533bcfa --- /dev/null +++ b/src_wearable/wrt-installer/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Lukasz Wrzosek (l.wrzosek@samsung.com) +# @version 1.0 +# + +SET(WRT_INSTALLER_DIR + ${INSTALLER_SRC_DIR}/wrt-installer + ) + +SET(PKG_MANAGER_DIR + ${INSTALLER_SRC_DIR}/pkg-manager + ) + +SET(WRT_INSTALLER_SOURCES + ${WRT_INSTALLER_DIR}/wrt-installer.cpp + ${WRT_INSTALLER_DIR}/installer_callbacks_translate.cpp + ${WRT_INSTALLER_DIR}/plugin_utils.cpp + ${WRT_INSTALLER_DIR}/language_subtag_rst_tree.cpp + ${WRT_INSTALLER_DIR}/installer_main_thread.cpp + ${WRT_INSTALLER_DIR}/command_parser.cpp + ${PKG_MANAGER_DIR}/pkgmgr_signal.cpp +) + +PKG_CHECK_MODULES(WRT_INSTALLER_DEPS + pkgmgr-installer + libpcrecpp + pkgmgr-info + pkgmgr + security-install + wrt-commons-i18n-dao-ro + capi-system-power + REQUIRED) + +INCLUDE_DIRECTORIES( + ${PKG_MANAGER_DIR} + ${WRT_INSTALLER_DEP_INCLUDES} + ${WRT_INSTALLER_INCLUDES} + ${WRT_INSTALLER_DEPS_INCLUDE_DIRS} +) + +ADD_EXECUTABLE(${TARGET_INSTALLER} + ${TARGET_INSTALLER_STATIC_SRC} + ${WRT_INSTALLER_SOURCES} +) + +ADD_DEFINITIONS(${WRT_INSTALLER_DEPS_CFLAGS}) + +TARGET_LINK_LIBRARIES(${TARGET_INSTALLER} + ${TARGET_INSTALLER_STATIC} + ${WRT_INSTALLER_DEPS_LIBRARIES} +) + + +SET_TARGET_PROPERTIES(${TARGET_INSTALLER} PROPERTIES + LINK_FLAGS "-Wl,--as-needed -Wl,--hash-style=both" + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_INSTALLER} DESTINATION bin) diff --git a/src_wearable/wrt-installer/command_parser.cpp b/src_wearable/wrt-installer/command_parser.cpp new file mode 100644 index 0000000..2459596 --- /dev/null +++ b/src_wearable/wrt-installer/command_parser.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file command_parser.cpp + * @author Soyoung Kim (sy037.kim@samsung.com) + * @brief Implementation file for OptionParser. + */ + +#include +#include +#include +#include "command_parser.h" + +namespace { +typedef std::pair DataPair; + +const char* const KEY_OP = "op"; +const char* const KEY_PATH = "path"; +const char* const KEY_REMOVABLE = "removable"; +} + +bool CommandParser::CscCommandParser(const std::string& arg, CscOption &option) +{ + using namespace Command; + // path=/opt/system/csc/Ozq2iEG15R-2.0.0-arm.wgt:op=install:removable=true + // parsing CSC configuration string + _D("CscCommandParser"); + if (arg.empty()) { + return false; + } + DataMap cmdMap = ArgumentsParser(arg); + + DataMap::iterator it; + it = cmdMap.find(KEY_OP); + if (it == cmdMap.end()) { + return false; + } + + if (it->second == VALUE_INSTALL) { + _D("operation = %s", it->second.c_str()); + option.operation = VALUE_INSTALL; + it = cmdMap.find(KEY_PATH); + if (it == cmdMap.end()) { + return false; + } + option.path = it->second; + _D("path = %s", option.path.c_str()); + + it = cmdMap.find(KEY_REMOVABLE); + if (it == cmdMap.end()) { + return false; + } + + option.removable = true; + if (0 == it->second.compare(VALUE_FALSE)) { + option.removable = false; + } + } else if (it->second == VALUE_UNINSTALL) { + _D("operation = %s", it->second.c_str()); + // uninstall command isn't confirmed yet + it = cmdMap.find(KEY_PATH); + if (it == cmdMap.end()) { + return false; + } + option.path = it->second; + _D("operation = uninstall"); + _D("path = %s", option.path.c_str()); + } else { + _E("Unknown operation : %s", it->second.c_str()); + _D("operation = %s", it->second.c_str()); + return false; + } + + return true; +} + +bool CommandParser::FotaCommandParser(const std::string& arg, FotaOption + &option) +{ + using namespace Command; + // path=pkgid:op=install + _D("FotaCommandParser"); + DataMap cmdMap = ArgumentsParser(arg); + + DataMap::iterator it; + it = cmdMap.find(KEY_OP); + if (it == cmdMap.end()) { + return false; + } + option.operation = it->second; + + it = cmdMap.find(KEY_PATH); + if (it == cmdMap.end()) { + return false; + } + + option.pkgId = it->second; + _D("Fota : package_id [%s], operaion [%s]", option.pkgId.c_str(), + option.operation.c_str()); + + return true; +} + +CommandParser::DataMap CommandParser::ArgumentsParser(const std::string& arg) +{ + DataMap result; + + if (arg.empty()) { + _D("Input argument is empty"); + return result; + } + + const char* ptr = strtok(const_cast(arg.c_str()),":"); + while (ptr != NULL) { + std::string string = ptr; + ptr = strtok (NULL, ":"); + size_t pos = string.find('='); + if (pos == std::string::npos) { + continue; + } + result.insert( + DataPair(string.substr(0, pos), + string.substr(pos+1))); + } + + return result; +} diff --git a/src_wearable/wrt-installer/command_parser.h b/src_wearable/wrt-installer/command_parser.h new file mode 100755 index 0000000..49527f9 --- /dev/null +++ b/src_wearable/wrt-installer/command_parser.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file command_parser.h + * @author Soyoung Kim (sy037.kim@samsung.com) + * @brief Header file for Command parser + */ + +#ifndef WRT_INSTALLER_SRC_WRT_INSTALLER_COMMAND_PARSER_H_ +#define WRT_INSTALLER_SRC_WRT_INSTALLER_COMMAND_PARSER_H_ + +#include +#include +#include + +namespace Command { +const char* const VALUE_INSTALL = "install"; +const char* const VALUE_UNINSTALL = "uninstall"; +const char* const VALUE_UPGRADE = "upgrade"; +const char* const VALUE_UPDATE = "update"; +const char* const VALUE_TRUE = "true"; +const char* const VALUE_FALSE = "false"; +} + +class CommandParser +{ + typedef std::map DataMap; + + public: + struct CscOption { + std::string path; + std::string operation; + bool removable; + }; + + struct FotaOption { + std::string pkgId; + std::string operation; + }; + + static bool CscCommandParser(const std::string& arg, CscOption &option); + static bool FotaCommandParser(const std::string& arg, FotaOption &option); + + private: + static DataMap ArgumentsParser(const std::string& arg); +}; + +#endif diff --git a/src_wearable/wrt-installer/installer_callbacks_translate.cpp b/src_wearable/wrt-installer/installer_callbacks_translate.cpp new file mode 100644 index 0000000..ca52dd6 --- /dev/null +++ b/src_wearable/wrt-installer/installer_callbacks_translate.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file api_callbacks_translate.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Source file for api callbacks translate functions + */ + +#include +#include +#include + +namespace InstallerCallbacksTranslate { + +// callback for finished install +void installFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->status_callback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + + case Jobs::Exceptions::ErrorPackageNotFound: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorPackageInvalid: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_INVALID; + break; + + case Jobs::Exceptions::ErrorPackageLowerVersion: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION; + break; + + case Jobs::Exceptions::ErrorPackageExecutableNotFound: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_EXCUTABLE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorManifestNotFound: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorManifestInvalid: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_INVALID; + break; + + case Jobs::Exceptions::ErrorConfigNotFound: + errorStatus = WRT_INSTALLER_CONFIG_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorConfigInvalid: + errorStatus = WRT_INSTALLER_ERROR_CONFIG_INVALID; + break; + + case Jobs::Exceptions::ErrorSignatureNotFound: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorSignatureInvalid: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_INVALID; + break; + + case Jobs::Exceptions::ErrorSignatureVerificationFailed: + errorStatus = WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED; + break; + + case Jobs::Exceptions::ErrorRootCertificateNotFound: + errorStatus = WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorCertificationInvaid: + errorStatus = WRT_INSTALLER_ERROR_CERTIFICATION_INVAID; + break; + + case + Jobs::Exceptions::ErrorCertificateChainVerificationFailed: + errorStatus = + WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED; + break; + + case Jobs::Exceptions::ErrorCertificateExpired: + errorStatus = WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED; + break; + + case Jobs::Exceptions::ErrorInvalidPrivilege: + errorStatus = WRT_INSTALLER_ERROR_INVALID_PRIVILEGE; + break; + + case Jobs::Exceptions::ErrorPrivilegeLevelViolation: + errorStatus = WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION; + break; + + case Jobs::Exceptions::ErrorMenuIconNotFound: + errorStatus = WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND; + break; + + case Jobs::Exceptions::ErrorFatalError: + errorStatus = WRT_INSTALLER_ERROR_FATAL_ERROR; + break; + + case Jobs::Exceptions::ErrorOutOfStorage: + errorStatus = WRT_INSTALLER_ERROR_OUT_OF_STORAGE; + break; + + case Jobs::Exceptions::ErrorOutOfMemory: + errorStatus = WRT_INSTALLER_ERROR_OUT_OF_MEMORY; + break; + + case Jobs::Exceptions::ErrorArgumentInvalid: + errorStatus = WRT_INSTALLER_ERROR_ARGUMENT_INVALID; + break; + + case Jobs::Exceptions::ErrorPackageAlreadyInstalled: + errorStatus = WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED; + break; + + case Jobs::Exceptions::ErrorAceCheckFailed: + errorStatus = WRT_INSTALLER_ERROR_ACE_CHECK_FAILED; + break; + + case Jobs::Exceptions::ErrorManifestCreateFailed: + errorStatus = WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED; + break; + + case Jobs::Exceptions::ErrorEncryptionFailed: + errorStatus = WRT_INSTALLER_ERROR_ENCRYPTION_FAILED; + break; + + case Jobs::Exceptions::ErrorInstallOspServcie: + errorStatus = WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE; + break; + + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + // Callback + apiStr->status_callback(tizenId, errorStatus, apiStr->userdata); + } else { + _D("installFinishedCallback: No callback"); + } +} + +// callback for finished install +void uninstallFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->status_callback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + + case Jobs::Exceptions::ErrorWidgetUninstallationFailed: + errorStatus = WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED; + break; + + case Jobs::Exceptions::ErrorUnknown: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + // Callback + apiStr->status_callback(tizenId, errorStatus, apiStr->userdata); + } else { + _D("uninstallFinishedCallback: No callback"); + } +} + +void pluginInstallFinishedCallback(void *userParam, + Jobs::Exceptions::Type status) +{ + Assert(userParam); + + PluginStatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->statusCallback) { + // Translate error + WrtErrStatus errorStatus; + + switch (status) { + case Jobs::Exceptions::Success: + errorStatus = WRT_SUCCESS; + break; + case Jobs::Exceptions::ErrorPluginInstallationFailed: + errorStatus = WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED; + break; + default: + errorStatus = WRT_INSTALLER_ERROR_UNKNOWN; + break; + } + + apiStr->statusCallback(errorStatus, apiStr->userdata); + } else { + _D("PluginInstallFinishedCallback: No callback"); + } + + delete apiStr; +} + +// callback for progress of install OR uninstall +void installProgressCallback(void *userParam, + ProgressPercent percent, + const ProgressDescription &description) +{ + Assert(userParam != NULL); + + StatusCallbackStruct *apiStr = + static_cast(userParam); + + if (apiStr->progress_callback) { + //CALLBACK EXEC + _D("Entered %2.0f%% %s", percent, description.c_str()); + apiStr->progress_callback(static_cast(percent), + description.c_str(), + apiStr->userdata); + } else { + _D("installProgressCallback: ignoring NULL callback pointer"); + } +} +} //namespace + diff --git a/src_wearable/wrt-installer/installer_callbacks_translate.h b/src_wearable/wrt-installer/installer_callbacks_translate.h new file mode 100644 index 0000000..f20ecc2 --- /dev/null +++ b/src_wearable/wrt-installer/installer_callbacks_translate.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file api_callbacks_translate.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief Header file for api callbacks translate functions + */ +#ifndef WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ +#define WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*WrtInstallerInitCallback)(WrtErrStatus status, + void *data); +typedef void (*WrtPluginInstallerStatusCallback)(WrtErrStatus status, + void *data); +typedef void (*WrtInstallerStatusCallback)(std::string tizenId, + WrtErrStatus status, + void *data); +typedef void (*WrtProgressCallback)(float percent, + const char *description, + void *data); + + +namespace InstallerCallbacksTranslate { +struct StatusCallbackStruct +{ + void* userdata; + WrtInstallerStatusCallback status_callback; + WrtProgressCallback progress_callback; + + StatusCallbackStruct(void* u, + WrtInstallerStatusCallback s, + WrtProgressCallback p) : + userdata(u), + status_callback(s), + progress_callback(p) + {} +}; + +struct PluginStatusCallbackStruct +{ + void* userdata; + WrtPluginInstallerStatusCallback statusCallback; + WrtProgressCallback progressCallback; + + PluginStatusCallbackStruct(void* u, + WrtPluginInstallerStatusCallback s, + WrtProgressCallback p) : + userdata(u), + statusCallback(s), + progressCallback(p) + {} +}; + +void installFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status); + +void uninstallFinishedCallback(void *userParam, + std::string tizenId, + Jobs::Exceptions::Type status); + +void pluginInstallFinishedCallback(void *userParam, + Jobs::Exceptions::Type status); + +// callback for progress of install OR uninstall +void installProgressCallback(void *userParam, + ProgressPercent percent, + const ProgressDescription &description); +} //namespace + +#endif /* WRT_SRC_API_API_CALLBACKS_TRANSLATE_H_ */ diff --git a/src_wearable/wrt-installer/installer_main_thread.cpp b/src_wearable/wrt-installer/installer_main_thread.cpp new file mode 100644 index 0000000..b398d3c --- /dev/null +++ b/src_wearable/wrt-installer/installer_main_thread.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file installer_main_thread.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#include "installer_main_thread.h" +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(InstallerMainThread) + +using namespace WrtDB; + +InstallerMainThread::InstallerMainThread() : m_attached(false) {} + +InstallerMainThread::~InstallerMainThread() +{ + Assert(!m_attached); +} + +void InstallerMainThread::AttachDatabases() +{ + Assert(!m_attached); + // Attach databases + ValidationCore::AttachToThreadRW(); + ace_return_t ret = ace_install_initialize(); + Assert(ACE_OK == ret); // to be changed to exception in the future + WrtDB::WrtDatabase::attachToThreadRW(); + m_attached = true; +} + +void InstallerMainThread::DetachDatabases() +{ + Assert(m_attached); + m_attached = false; + // Detach databases + ValidationCore::DetachFromThread(); + ace_return_t ret = ace_install_shutdown(); + Assert(ACE_OK == ret); // to be changed to exception in the future + WrtDB::WrtDatabase::detachFromThread(); +} + +void InstallerMainThread::TouchArchitecture() +{ + // Touch controller + Logic::InstallerControllerSingleton::Instance().Touch(); +} + +void InstallerMainThread::TouchArchitectureOnlyInstaller() +{ + // Touch controller + Logic::InstallerControllerSingleton::Instance().Touch(); +} diff --git a/src_wearable/wrt-installer/installer_main_thread.h b/src_wearable/wrt-installer/installer_main_thread.h new file mode 100644 index 0000000..bd70b16 --- /dev/null +++ b/src_wearable/wrt-installer/installer_main_thread.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file installer_main_thread.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + */ + +#ifndef INSTALLER_MAINTHREAD_H_ +#define INSTALLER_MAINTHREAD_H_ + +#include + +class InstallerMainThread +{ + public: + void AttachDatabases(); + void DetachDatabases(); + void TouchArchitecture(); + void TouchArchitectureOnlyInstaller(); + + private: + friend class DPL::Singleton; + + InstallerMainThread(); + virtual ~InstallerMainThread(); + + bool m_attached; +}; + +typedef DPL::Singleton InstallerMainThreadSingleton; + +#endif /* INSTALLER_MAINTHREAD_H_ */ diff --git a/src_wearable/wrt-installer/language_subtag_rst_tree.cpp b/src_wearable/wrt-installer/language_subtag_rst_tree.cpp new file mode 100644 index 0000000..a2bfaf5 --- /dev/null +++ b/src_wearable/wrt-installer/language_subtag_rst_tree.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file language_subtag_rst_tree.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(LanguageSubtagRstTree) + +namespace I18nDAOReadOnly = I18n::DB::I18nDAOReadOnly; + +bool LanguageSubtagRstTree::ValidateLanguageTag(const std::string &tag_input) +{ + std::string tag = tag_input; + std::transform(tag.begin(), tag.end(), tag.begin(), &tolower); + + std::vector parts; + DPL::Tokenize(DPL::FromUTF8String(tag), + '-', + std::back_inserter(parts), + false); + std::vector::iterator token = parts.begin(); + if (token == parts.end()) + { + return false; + } + + I18n::DB::Interface::attachDatabaseRO(); + DPL_SCOPE_EXIT() + { + I18n::DB::Interface::detachDatabase(); + }; + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_LANGUAGE)) + { + ++token; + } + else + { + return false; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_EXTLANG)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_SCRIPT)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_REGION)) + { + ++token; + } + + if (token == parts.end()) + { + return true; + } + + while (token != parts.end()) + { + if (I18nDAOReadOnly::IsValidSubTag(*token, RECORD_TYPE_VARIANT)) + { + ++token; + } + else + { + break; + } + } + + //'u' - unicode extension - only one BCP47 extension is registered. + //TODO: unicode extension should be also validated (l.wrzosek) + if (token == parts.end()) + { + return true; + } + + if (*token == L"u") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + token->size() > 1 && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + if (!one_or_more) + { + return false; + } + } + + //'x' - privateuse + if (token == parts.end()) + { + return true; + } + + if (*token == L"x") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + !token->empty() && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + if (!one_or_more) + { + return false; + } + } + + if (token == parts.end()) + { + return true; + } + + //Try private use now: + token = parts.begin(); + if (*token == L"x") + { + ++token; + bool one_or_more = false; + while (token != parts.end() && + !token->empty() && + token->size() <= 8) + { + one_or_more = true; + ++token; + } + return one_or_more; + } + + //grandfathered is always rejected + return false; +} + +#define TEST_LANG(str, cond) \ + if (LanguageSubtagRstTreeSingleton::Instance(). \ + ValidateLanguageTag(str) == cond) { \ + _D("Good validate status for lang: %s", str); \ + } else { \ + _E("Wrong validate status for lang: %s, should be %d", str, cond); \ + } + +void LanguageSubtagRstTree::Initialize() +{ + /* Temporarily added unit test. Commented out due to performance drop. + * TEST_LANG("zh", true); + * TEST_LANG("esx-al", true); + * TEST_LANG("zh-Hant", true); + * TEST_LANG("zh-Hant-CN", true); + * TEST_LANG("zh-Hant-CN-x-private1-private2", true); + * TEST_LANG("plxxx", false); + * TEST_LANG("pl-x-private111", false); + * TEST_LANG("x-private1", false); //do not support pure private ones + * TEST_LANG("x-private22", false); + * TEST_LANG("i-private22", false); //do not support i-* + */ +} + +#undef TEST_LANG diff --git a/src_wearable/wrt-installer/language_subtag_rst_tree.h b/src_wearable/wrt-installer/language_subtag_rst_tree.h new file mode 100644 index 0000000..b057059 --- /dev/null +++ b/src_wearable/wrt-installer/language_subtag_rst_tree.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file language_subtag_rst_tree.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ +#ifndef LANGUAGE_SUBTAG_RST_TREE_H +#define LANGUAGE_SUBTAG_RST_TREE_H + +#include +#include +#include +class LanguageSubtagRstTree : DPL::Noncopyable +{ + public: + void Initialize(); + bool ValidateLanguageTag(const std::string &tag); +}; + +typedef DPL::Singleton LanguageSubtagRstTreeSingleton; + +enum iana_record_types_e +{ + RECORD_TYPE_LANGUAGE, + RECORD_TYPE_SCRIPT, + RECORD_TYPE_REGION, + RECORD_TYPE_VARIANT, + RECORD_TYPE_GRANDFATHERED, + RECORD_TYPE_REDUNDANT, + RECORD_TYPE_EXTLANG +}; + +#endif //LANGUAGE_SUBTAG_RST_TREE_H diff --git a/src_wearable/wrt-installer/plugin_utils.cpp b/src_wearable/wrt-installer/plugin_utils.cpp new file mode 100644 index 0000000..cabda19 --- /dev/null +++ b/src_wearable/wrt-installer/plugin_utils.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin-utils.cpp + * @author + * @version 1.0 + * @brief Header file for plugin util + */ + +#include +#include "plugin_utils.h" +#include +#include +#include +#include + +using namespace WrtDB; + +namespace PluginUtils { +const char* PLUGIN_INSTALL_LOCK_FILE = "/tmp/.wrt_plugin_install_lock"; + +static int s_plugin_install_lock_fd = -1; + +bool lockPluginInstallation(bool isPreload) +{ + if (isPreload) { + fprintf(stderr, "Skip create lock file.. \n"); + return true; + } + + int ret = 0; + + _D("Try to lock for plugins installation."); + + s_plugin_install_lock_fd = + open(PLUGIN_INSTALL_LOCK_FILE, O_RDONLY | O_CREAT, 0666); + + if (s_plugin_install_lock_fd == -1) { + _E("Lock file open failed!"); + + return false; + } + + ret = flock(s_plugin_install_lock_fd, LOCK_EX); //lock with waiting + + if (ret == -1) { + _E("Lock failed!"); + + close(s_plugin_install_lock_fd); + s_plugin_install_lock_fd = -1; + + return false; + } + + return true; +} + +bool unlockPluginInstallation(bool isPreload) +{ + _D("Unlock for plugins installation."); + if (isPreload) { + fprintf(stderr, "Skip plugin unlock.. \n"); + return true; + } + + if (s_plugin_install_lock_fd != -1) { + int ret = 0; + + ret = flock(s_plugin_install_lock_fd, LOCK_UN); //unlock + + if (ret == -1) { + _E("Unlock failed!"); + } + + close(s_plugin_install_lock_fd); + s_plugin_install_lock_fd = -1; + + return true; + } else { + _E("Lock file was not created!"); + } + + return false; +} + +bool checkPluginInstallationRequired() +{ + std::string installRequest = + std::string(GlobalConfig::GetPluginInstallInitializerName()); + + FileState::Type installationRequest = + checkFile(installRequest); + + switch (installationRequest) { + case FileState::FILE_EXISTS: + return true; + case FileState::FILE_NOT_EXISTS: + return false; + default: + _W("Opening installation request file failed"); + return false; + } +} + +bool removeInstallationRequiredFlag() +{ + std::string installRequest = + std::string(GlobalConfig::GetPluginInstallInitializerName()); + + return removeFile(installRequest); +} + +//checks if file exists and is regular file +FileState::Type checkFile(const std::string& filename) +{ + struct stat tmp; + + if (-1 == stat(filename.c_str(), &tmp)) { + if (ENOENT == errno) { + return FileState::FILE_NOT_EXISTS; + } + return FileState::FILE_READ_DATA_ERROR; + } else if (!S_ISREG(tmp.st_mode)) { + return FileState::FILE_EXISTS_NOT_REGULAR; + } + return FileState::FILE_EXISTS; +} + +bool removeFile(const std::string& filename) +{ + if (0 != unlink(filename.c_str())) { + return false; + } + + return true; +} +} //namespace PluginUtils diff --git a/src_wearable/wrt-installer/plugin_utils.h b/src_wearable/wrt-installer/plugin_utils.h new file mode 100644 index 0000000..8659f20 --- /dev/null +++ b/src_wearable/wrt-installer/plugin_utils.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin-utils.h + * @author + * @version 1.0 + * @brief Header file for plugin util + */ +#ifndef PLUGIN_UTILS_H +#define PLUGIN_UTILS_H + +#include +#include + +namespace PluginUtils { +struct FileState +{ + enum Type + { + FILE_EXISTS, + FILE_EXISTS_NOT_REGULAR, + FILE_NOT_EXISTS, + FILE_READ_DATA_ERROR + }; +}; + +bool lockPluginInstallation(bool isPreload); +bool unlockPluginInstallation(bool isPreload); +bool checkPluginInstallationRequired(); +bool removeInstallationRequiredFlag(); +FileState::Type checkFile(const std::string& filename); +bool removeFile(const std::string& filename); +} +#endif // PLUGIN_UTILS_H diff --git a/src_wearable/wrt-installer/wrt-installer.cpp b/src_wearable/wrt-installer/wrt-installer.cpp new file mode 100755 index 0000000..9fc4df6 --- /dev/null +++ b/src_wearable/wrt-installer/wrt-installer.cpp @@ -0,0 +1,1497 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* @file wrt-installer.cpp + * @version 1.0 + * @brief Implementation file for installer + */ + +#include "wrt-installer.h" +#include "plugin_utils.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { // anonymous +const char * const PKGMGR_INSTALL_MSG = "Install widget"; +const char * const PKGMGR_UNINSTALL_MSG = "Uninstall widget"; + +const char * const CONFIG_XML = "config.xml"; +const char * const HYBRID_CONFIG_XML = "res/wgt/config.xml"; + +const unsigned int NOFILE_CNT_FOR_INSTALLER = 9999; + +struct free_deleter +{ + void operator()(void* x) + { + free(x); + } +}; + +struct PluginInstallerData +{ + void* wrtInstaller; + std::string pluginPath; +}; + +std::string cutOffFileName(const std::string& path) +{ + size_t found = path.find_last_of("/"); + if (found == std::string::npos) { + return path; + } else { + return path.substr(0, found); + } +} + +bool checkPath(const std::string& path) +{ + struct stat st; + if (0 == stat(path.c_str(), &st) && S_ISDIR(st.st_mode)) { + return true; + } + _E("Cannot access directory [ %s ]", path.c_str()); + return false; +} + +bool checkPaths() +{ + bool if_ok = true; + + if_ok &= (checkPath(cutOffFileName(GlobalConfig::GetWrtDatabaseFilePath()))); + if_ok &= (checkPath(GlobalConfig::GetDevicePluginPath())); + if_ok &= (checkPath(GlobalConfig::GetUserInstalledWidgetPath())); + if_ok &= (checkPath(GlobalConfig::GetUserPreloadedWidgetPath())); + + return if_ok; +} + +} // namespace anonymous + +WrtInstaller::WrtInstaller(int argc, char **argv) : + Application(argc, argv, "backend", false), + DPL::TaskDecl(this), + m_packagePath(), + m_initialized(false), + m_numPluginsToInstall(0), + m_totalPlugins(0), + m_returnStatus(-1), + m_sendPkgSig(false), + m_startupPluginInstallation(false) +{ + Touch(); + _D("App Created"); +} + +WrtInstaller::~WrtInstaller() +{ + _D("App Finished"); +} + +int WrtInstaller::getReturnStatus() const +{ + return m_returnStatus; +} + +void WrtInstaller::OnStop() +{ + _D("Stopping Dummy Client"); +} + +void WrtInstaller::OnCreate() +{ + _D("Creating DummyClient"); + + //pm lock + power_lock_state(POWER_STATE_SCREEN_OFF, 60*1000); + + showArguments(); + + AddStep(&WrtInstaller::initStep); + + std::string arg = m_argv[0]; + + using namespace PackageManager; + + auto pkgmgrSignal = std::shared_ptr(new PackageManager::PkgmgrSignal()); + + pkgmgrSignalInterface = + std::static_pointer_cast( + std::shared_ptr( + new PackageManager::PkgmgrSignalDummy())); + + if (arg.empty()) { + return showHelpAndQuit(); + } + + installNewPlugins(); + + if (arg.find("wrt-installer") != std::string::npos) { + if (m_argc <= 1) { + return showHelpAndQuit(); + } + + arg = m_argv[1]; + if (arg == "-h" || arg == "--help") { + if (m_argc != 2) { + return showHelpAndQuit(); + } + + // Just show help + return showHelpAndQuit(); + } else if (arg == "-p" || arg == "--install-plugins") { + if (m_argc != 2) { + return showHelpAndQuit(); + } + + if (!m_startupPluginInstallation) { + AddStep(&WrtInstaller::installPluginsStep); + } else { + _D("Plugin installation alredy started"); + } + } else if (arg == "-i" || arg == "--install") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + + struct stat info; + if (-1 != stat(m_argv[2], &info) && S_ISDIR(info.st_mode)) { + _D("Installing package directly from directory"); + m_installMode.extension = InstallMode::ExtensionType::DIR; + } else { + _D("Installing from regular location"); + m_installMode.extension = InstallMode::ExtensionType::WGT; + } + m_packagePath = m_argv[2]; + m_sendPkgSig = true; + pkgmgrSignal->initialize(m_argc, m_argv); + pkgmgrSignalInterface = std::static_pointer_cast(pkgmgrSignal); + AddStep(&WrtInstaller::installStep); + } else if (arg == "-ip" || arg == "--install-preload") { + _D("Install preload web app"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + m_installMode.installTime = InstallMode::InstallTime::PRELOAD; + m_installMode.rootPath = InstallMode::RootPath::RO; + m_installMode.removable = false; + AddStep(&WrtInstaller::installStep); + } else if (arg == "-ipw" || arg == "--install-preload-writable") { + _D("Install preload web application to writable storage"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + m_installMode.installTime = InstallMode::InstallTime::PRELOAD; + m_installMode.rootPath = InstallMode::RootPath::RW; + m_installMode.removable = true; + AddStep(&WrtInstaller::installStep); + } else if (arg == "-c" || arg == "--csc-update") { + // "path=/opt/system/csc/Ozq2iEG15R-2.0.0-arm.wgt:op=install:removable=true" + _D("Install & uninstall by csc configuration"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_installMode.installTime = InstallMode::InstallTime::CSC; + std::string configuration = m_argv[2]; + + CommandParser::CscOption option; + if (!CommandParser::CscCommandParser(configuration, option)) { + return showHelpAndQuit(); + } + + if (0 == option.operation.compare(Command::VALUE_INSTALL)) { + m_installMode.extension = InstallMode::ExtensionType::WGT; + m_packagePath = option.path; + m_installMode.removable = option.removable; + m_installMode.cscPath = m_argv[2]; + _D("operation = %s", option.operation.c_str()); + _D("path = %s", m_packagePath.c_str()); + _D("removable = %d", m_installMode.removable); + _D("csc Path = %s", m_installMode.cscPath.c_str()); + AddStep(&WrtInstaller::installStep); + } else if (0 == option.operation.compare(Command::VALUE_UNINSTALL)){ + m_packagePath = option.path; + _D("operation = %s", option.operation.c_str()); + _D("path = %s", m_packagePath.c_str()); + AddStep(&WrtInstaller::unistallWgtFileStep); + } else { + _E("Unknown operation : %s", option.operation.c_str()); + return showHelpAndQuit(); + } + } else if (arg == "-un" || arg == "--uninstall-name") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_name = m_argv[2]; + m_sendPkgSig = true; + m_argv[1] = (char*)"-d"; + pkgmgrSignal->initialize(m_argc, m_argv); + pkgmgrSignalInterface = std::static_pointer_cast(pkgmgrSignal); + AddStep(&WrtInstaller::uninstallPkgNameStep); + } else if (arg == "-up" || arg == "--uninstall-packagepath") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + m_packagePath = m_argv[2]; + AddStep(&WrtInstaller::unistallWgtFileStep); + } else if (arg == "-r" || arg == "--reinstall") { + if (m_argc != 3) { + return showHelpAndQuit(); + } + _D("Installing package directly from directory"); + m_installMode.command = InstallMode::Command::REINSTALL; + m_installMode.extension = InstallMode::ExtensionType::DIR; + m_packagePath = m_argv[2]; + m_sendPkgSig = true; + pkgmgrSignal->initialize(m_argc, m_argv); + pkgmgrSignalInterface = std::static_pointer_cast(pkgmgrSignal); + AddStep(&WrtInstaller::installStep); + } else if (arg == "-f" || arg == "--fota") { + // "path=8HPzsUYyNZ:op=install" + _D("Install & uninstall by fota"); + if (m_argc != 3) { + return showHelpAndQuit(); + } + std::string configuration = m_argv[2]; + CommandParser::FotaOption option; + if (!CommandParser::FotaCommandParser(configuration, option)) { + return showHelpAndQuit(); + } + + if ((0 == option.operation.compare(Command::VALUE_INSTALL)) || + (0 == option.operation.compare(Command::VALUE_UPGRADE)) || + (0 == option.operation.compare(Command::VALUE_UPDATE))) { + _D("FOTA ... Installation"); + m_name = option.pkgId; + m_packagePath += + std::string(WrtDB::GlobalConfig::GetUserPreloadedWidgetPath()) + + "/" + option.pkgId; + _D("package id = %s", m_name.c_str()); + _D("operation = %s", option.operation.c_str()); + _D("package path = %s", m_packagePath.c_str()); + + m_installMode.installTime = InstallMode::InstallTime::FOTA; + m_installMode.rootPath = InstallMode::RootPath::RO; + m_installMode.extension = InstallMode::ExtensionType::DIR; + AddStep(&WrtInstaller::installStep); + } else if (0 == option.operation.compare(Command::VALUE_UNINSTALL)){ + _D("FOTA ... Uninstallation"); + m_name = option.pkgId; + _D("package id = %s", m_name.c_str()); + AddStep(&WrtInstaller::uninstallPkgNameStep); + } else { + _E("Unknown operation : %s", option.operation.c_str()); + return showHelpAndQuit(); + } + } else if (arg == "-b" || arg == "--recovery") { + getRecoveryPackageId(m_name); + _D("m_name : %s", m_name.c_str()); + + if (!m_name.empty()) { + pkgmgrinfo_pkginfo_h handle = NULL; + if (0 == pkgmgrinfo_pkginfo_get_pkginfo(m_name.c_str(), &handle)) { + m_installMode.command = InstallMode::Command::RECOVERY; + m_installMode.extension = InstallMode::ExtensionType::DIR; + AddStep(&WrtInstaller::installStep); + } else { + _D("package id = %s", m_name.c_str()); + AddStep(&WrtInstaller::uninstallPkgNameStep); + } + } + } else { + return showHelpAndQuit(); + } + } else if (arg.find("backend") != std::string::npos) { + m_sendPkgSig = true; + pkgmgrSignal->initialize(m_argc, m_argv); + PkgmgrSignal::RequestType reqType = pkgmgrSignal->getRequestedType(); + pkgmgrSignalInterface = std::static_pointer_cast(pkgmgrSignal); + + switch (reqType) { + case PackageManager::PkgmgrSignal::RequestType::INSTALL: + m_packagePath = m_argv[4]; + if (6 < m_argc) { + m_name = std::string(m_argv[6]); + } + + struct stat info; + if (-1 != stat(m_argv[4], &info) && S_ISDIR(info.st_mode)) { + _D("Installing package directly from directory"); + m_installMode.extension = InstallMode::ExtensionType::DIR; + } else { + _D("Installing from regular location"); + m_installMode.extension = InstallMode::ExtensionType::WGT; + } + AddStep(&WrtInstaller::installStep); + break; + case PackageManager::PkgmgrSignal::RequestType::UNINSTALL: + { + m_name = m_argv[4]; + pkgmgrinfo_pkginfo_h handle = NULL; + bool preload = false; + bool system = false; + bool removable = true; + bool update = false; + char *cscPath = NULL; + + if (0 == pkgmgrinfo_pkginfo_get_pkginfo(m_name.c_str(), &handle)) { + if (0 > pkgmgrinfo_pkginfo_is_preload(handle, &preload)) { + _E("Can't get package information : %s", m_name.c_str()); + } + if (0 > pkgmgrinfo_pkginfo_is_system(handle, &system)) { + _E("Can't get package information : %s", m_name.c_str()); + } + if (0 > pkgmgrinfo_pkginfo_is_removable(handle, &removable)) { + _E("Can't get package information : %s", m_name.c_str()); + } + if (0 > pkgmgrinfo_pkginfo_is_update(handle, &update)) { + _E("Can't get package information about update : %s", m_name.c_str()); + } + if (0 > pkgmgrinfo_pkginfo_get_csc_path(handle, &cscPath)) { + _E("Can't get package information about update : %s", m_name.c_str()); + } + } + + _D("preload app : %d", preload); + _D("system app : %d", system); + _D("removable app : %d", removable); + _D("update : %d", update); + _D("csc path : %s", cscPath); + + if (preload && update) { + if (system) { + AddStep(&WrtInstaller::removeUpdateStep); + } else if (setInitialCSC(cscPath)) { + AddStep(&WrtInstaller::uninstallPkgNameStep); + AddStep(&WrtInstaller::installStep); + } else if (removable) { + AddStep(&WrtInstaller::uninstallPkgNameStep); + } + } else { + AddStep(&WrtInstaller::uninstallPkgNameStep); + } + pkgmgrinfo_pkginfo_destroy_pkginfo(handle); + break; + } + case PackageManager::PkgmgrSignal::RequestType::REINSTALL: + m_packagePath = m_argv[4]; + m_installMode.command = InstallMode::Command::REINSTALL; + m_installMode.extension = InstallMode::ExtensionType::DIR; + AddStep(&WrtInstaller::installStep); + break; + default: + _D("Not available type"); + break; + } + } + + AddStep(&WrtInstaller::shutdownStep); + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::NextStepEvent()); +} + +void WrtInstaller::OnReset(bundle* /*b*/) +{ + _D("OnReset"); +} + +void WrtInstaller::OnTerminate() +{ + _D("Wrt Shutdown now"); + + //pm unlock + power_unlock_state(POWER_STATE_SCREEN_OFF); + + PluginUtils::unlockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD); + if (m_initialized) { + try { + _D("DEINITIALIZING WRT INSTALLER..."); + // Installer termination + CONTROLLER_POST_SYNC_EVENT( + Logic::InstallerController, + InstallerControllerEvents:: + TerminateEvent()); + InstallerMainThreadSingleton::Instance().DetachDatabases(); + I18n::DB::Interface::detachDatabase(); + + // This must be done after DetachDatabase + ValidationCore::VCoreDeinit(); + + // Global deinit check + _D("Cleanup libxml2 global values."); + xmlCleanupParser(); + } catch (const DPL::Exception& ex) { + _E("Internal Error during Shutdown:"); + DPL::Exception::DisplayKnownException(ex); + } + } +} + +void WrtInstaller::showHelpAndQuit() +{ + printf("Usage: wrt-installer [OPTION]... [WIDGET: ID/NAME/PATH]...\n" + "Operate with WebRuntime daemon: install, uninstall" + " and launch widgets.\n" + "Query list of installed widgets and setup up debugging support.\n" + "\n" + "Exactly one option must be selected.\n" + "Mandatory arguments to long options are mandatory for short " + "options too.\n" + " -h, --help show this help\n" + " -p, --install-plugins install plugins\n" + " -i, --install " + "install or update widget package for given path\n" + " -c, --csc-update " + "install or uninstall by CSC configuration \n" + " -un, --uninstall-name " + "uninstall widget for given package name\n" + " -up, --uninstall-packagepath " + "uninstall widget for given package file path\n" + " -r, --reinstall " + "reinstall mode for sdk (this is NOT normal reinstallation/update)\n" + "\n"); + + Quit(); +} + +void WrtInstaller::showArguments() +{ + fprintf(stderr, + "===========================================================\n"); + fprintf(stderr, "# wrt-installer #\n"); + fprintf(stderr, "# argc [%d]\n", m_argc); + for (int i = 0; i < m_argc; i++) { + fprintf(stderr, "# argv[%d] = [%s]\n", i, m_argv[i]); + } + fprintf(stderr, + "===========================================================\n"); + // for dlog + _D("==========================================================="); + _D("# wrt-installer #"); + _D("# argc %d", m_argc); + for (int i = 0; i < m_argc; i++) { + _D("# argv[%d] = %s", i, m_argv[i]); + } + _D("==========================================================="); + +} + +void WrtInstaller::OnEventReceived(const WRTInstallerNS::QuitEvent& /*event*/) +{ + _D("Quiting"); + + if (m_initialized) { + _D("Wrt Shutdown now"); + SwitchToStep(&WrtInstaller::shutdownStep); + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::NextStepEvent()); + } else { + _D("Quiting application"); + return Quit(); + } +} + +void WrtInstaller::OnEventReceived( + const WRTInstallerNS::NextStepEvent& /*event*/) +{ + _D("Executing next step"); + NextStep(); +} + +void WrtInstaller::OnEventReceived( + const WRTInstallerNS::InstallPluginEvent& /*event*/) +{ + PluginInstallerData* privateData = new PluginInstallerData; + privateData->wrtInstaller = this; + + if (!(*m_pluginsPaths).empty()) { + privateData->pluginPath = (*m_pluginsPaths).front(); + (*m_pluginsPaths).pop_front(); + + _D("INSTALL PLUGIN: %s", privateData->pluginPath.c_str()); + //Private data for status callback + //Resource is free in pluginInstallFinishedCallback + InstallerCallbacksTranslate::PluginStatusCallbackStruct* + callbackStruct = + new InstallerCallbacksTranslate::PluginStatusCallbackStruct( + privateData, &staticWrtPluginInstallationCallback, &staticWrtPluginInstallProgressCb); + + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::InstallPluginEvent( + privateData->pluginPath, + PluginInstallerStruct( + InstallerCallbacksTranslate:: + pluginInstallFinishedCallback, + InstallerCallbacksTranslate:: + installProgressCallback, callbackStruct))); + } else { + delete privateData; + } +} + +void WrtInstaller::initStep() +{ + try { + _D("INITIALIZING WRT INSTALLER..."); + + // Touch InstallerController Singleton + InstallerMainThreadSingleton::Instance().TouchArchitecture(); + + // Check paths + if (!checkPaths()) { + makeStatusOfWrtInit(WRT_INSTALLER_ERROR_FATAL_ERROR); + return; + } + + // Initialize ValidationCore - this must be done before AttachDatabases + ValidationCore::VCoreInit( + std::string(GlobalConfig::GetFingerprintListFile()), + std::string(GlobalConfig::GetFingerprintListSchema()), + std::string(GlobalConfig::GetVCoreDatabaseFilePath())); + + InstallerMainThreadSingleton::Instance().AttachDatabases(); + + _D("Prepare libxml2 to work in multithreaded program."); + xmlInitParser(); + + // Initialize Language Subtag registry + LanguageSubtagRstTreeSingleton::Instance().Initialize(); + + // Installer init + CONTROLLER_POST_SYNC_EVENT( + Logic::InstallerController, + InstallerControllerEvents:: + InitializeEvent()); + + makeStatusOfWrtInit(WRT_SUCCESS); + } catch (const DPL::Exception& ex) { + _E("Internal Error during Init:"); + DPL::Exception::DisplayKnownException(ex); + makeStatusOfWrtInit(WRT_INSTALLER_ERROR_FATAL_ERROR); + } +} + +void WrtInstaller::installStep() +{ + std::unique_ptr packagePath(canonicalize_file_name( + m_packagePath.c_str())); + + if (InstallMode::InstallTime::PRELOAD == m_installMode.installTime) { + DPL::Log::OldStyleLogProvider *oldStyleProvider = + new DPL::Log::OldStyleLogProvider(false, false, false, true, + false, true); + DPL::Log::LogSystemSingleton::Instance().AddProvider(oldStyleProvider); + } + + std::string path = packagePath ? packagePath.get() : m_packagePath.c_str(); + _D("INSTALL WIDGET: %s", path.c_str()); + // Post installation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::InstallWidgetEvent( + path, m_name.c_str(), + Jobs::WidgetInstall::WidgetInstallationStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + this, &staticWrtStatusCallback, (m_sendPkgSig) + ? &staticWrtInstallProgressCallback : NULL), + m_installMode, pkgmgrSignalInterface))); +} + +void WrtInstaller::installPluginsStep() +{ + _D("Installing plugins ..."); + fprintf(stderr, "Installing plugins ...\n"); + + if (m_startupPluginInstallation) { + _D("Plugin installation started because new plugin package found"); + } else if (!PluginUtils::lockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _E("Failed to open plugin installation lock file" + " Plugins are currently installed by other process"); + staticWrtPluginInstallationCallback(WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED, + this); + return; + } + + std::string PLUGIN_PATH = std::string(GlobalConfig::GetDevicePluginPath()); + + DIR *dir; + dir = opendir(PLUGIN_PATH.c_str()); + + if (!dir) { + return; + } + + _D("Plugin DIRECTORY IS %s", PLUGIN_PATH.c_str()); + + std::list pluginsPaths; + struct dirent libdir; + struct dirent *result; + int return_code; + errno = 0; + for (return_code = readdir_r(dir, &libdir, &result); + result != NULL && return_code == 0; + return_code = readdir_r(dir, &libdir, &result)) + { + if (strcmp(libdir.d_name, ".") == 0 || + strcmp(libdir.d_name, "..") == 0) + { + continue; + } + + std::string path = PLUGIN_PATH; + path += "/"; + path += libdir.d_name; + + struct stat tmp; + + if (stat(path.c_str(), &tmp) == -1) { + _E("Failed to open file %s", path.c_str()); + continue; + } + + if (!S_ISDIR(tmp.st_mode)) { + _E("Not a directory %s", path.c_str()); + continue; + } + + pluginsPaths.push_back(path); + } + + if (return_code != 0 || errno != 0) { + _E("readdir_r() failed with %s", DPL::GetErrnoString().c_str()); + } + + //set nb of plugins to install + //this value indicate how many callbacks are expected + m_numPluginsToInstall = pluginsPaths.size(); + _D("Plugins to install: %d", m_numPluginsToInstall); + m_pluginsPaths = pluginsPaths; + + m_totalPlugins = m_numPluginsToInstall; + DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::InstallPluginEvent()); + + if (-1 == closedir(dir)) { + _E("Failed to close dir: %s with error: %s", PLUGIN_PATH.c_str(), DPL::GetErrnoString().c_str()); + } +} + +void WrtInstaller::uninstallPkgNameStep() +{ + _D("Package name : %s", m_name.c_str()); + + _D("UNINSTALL WIDGET: %s", m_name.c_str()); + // Post uninstallation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::UninstallWidgetEvent( + m_name, + WidgetUninstallationStruct( + InstallerCallbacksTranslate::uninstallFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + this, &staticWrtStatusCallback, + (m_sendPkgSig) ? &staticWrtUninstallProgressCallback : NULL), + pkgmgrSignalInterface) + ) + ); +} + +void WrtInstaller::removeUpdateStep() +{ + _D("This web app need to initialize preload app"); + _D("Package name : %s", m_name.c_str()); + + _D("UNINSTALL WIDGET: %s", m_name.c_str()); + // Post uninstallation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::UninstallWidgetEvent( + m_name, + WidgetUninstallationStruct( + InstallerCallbacksTranslate::uninstallFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + this, &staticWrtInitializeToPreloadCallback, (m_sendPkgSig) + ? &staticWrtUninstallProgressCallback : NULL), + pkgmgrSignalInterface + ) + ) + ); +} + +bool WrtInstaller::setInitialCSC(std::string cscPath) +{ + _D("This web app need to initialize initial csc app"); + _D("UNINSTALL WIDGET: %s", m_name.c_str()); + _D("csc path: %s", cscPath.c_str()); + + m_installMode.installTime = InstallMode::InstallTime::CSC; + std::string configuration = cscPath; + + CommandParser::CscOption option; + if (!CommandParser::CscCommandParser(configuration, option)) { + _E("Failure command parser"); + return false; + } + + if (0 == option.operation.compare(Command::VALUE_INSTALL)) { + m_installMode.extension = InstallMode::ExtensionType::WGT; + m_packagePath = option.path; + m_installMode.removable = option.removable; + m_installMode.cscPath = cscPath; + _D("operation = %s", option.operation.c_str()); + _D("path = %s", m_packagePath.c_str()); + _D("removable = %d", m_installMode.removable); + _D("csc Path = %s", m_installMode.cscPath.c_str()); + } else { + _E("Unknown operation : %s", option.operation.c_str()); + return false; + } + return true; +} + +void WrtInstaller::unistallWgtFileStep() +{ + _D("Uninstalling widget ..."); + + Try { + // Parse config + ParserRunner parser; + ConfigParserData configInfo; + + // Open zip file + std::unique_ptr zipFile( + new DPL::ZipInput(m_packagePath)); + std::unique_ptr configFile; + + Try { + // Open config.xml file + configFile.reset(zipFile->OpenFile(CONFIG_XML)); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + // Open config.xml file for hybrid + configFile.reset(zipFile->OpenFile(HYBRID_CONFIG_XML)); + } + + // Extract config + DPL::BinaryQueue buffer; + DPL::AbstractWaitableInputAdapter inputAdapter(configFile.get()); + DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer); + DPL::Copy(&inputAdapter, &outputAdapter); + parser.Parse(&buffer, + ElementParserPtr( + new RootParser(configInfo, + DPL::FromUTF32String( + L"widget")))); + + DPL::OptionalString pkgId = configInfo.tizenPkgId; + if (!!pkgId) { + _D("Pkgid from packagePath : %ls", (*pkgId).c_str()); + _D("UNINSTALL WIDGET: %s", DPL::ToUTF8String(*pkgId).c_str()); + // Post uninstallation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::UninstallWidgetEvent( + DPL::ToUTF8String(*pkgId), + WidgetUninstallationStruct( + InstallerCallbacksTranslate::uninstallFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + this, &staticWrtStatusCallback, !m_sendPkgSig + ? &staticWrtUninstallProgressCallback : NULL), + pkgmgrSignalInterface) + ) + ); + + } else { + _E("Fail to uninstalling widget... "); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + _E("Failed to open widget package"); + printf("failed: widget package does not exist\n"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + Catch(DPL::ZipInput::Exception::OpenFileFailed) + { + printf("failed: widget config file does not exist\n"); + _E("Failed to open config.xml file"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + Catch(ElementParser::Exception::ParseError) + { + printf("failed: can not parse config file\n"); + _E("Failed to parse config.xml file"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } + Catch(DPL::Exception) + { + printf("Unknown DPL exception\n"); + _E("Unknown DPL exception"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::shutdownStep() +{ + _D("Closing Wrt connection ..."); + if (m_initialized) { + try { + _D("DEINITIALIZING WRT INSTALLER..."); + // Installer termination + CONTROLLER_POST_SYNC_EVENT( + Logic::InstallerController, + InstallerControllerEvents:: + TerminateEvent()); + + InstallerMainThreadSingleton::Instance().DetachDatabases(); + + // This must be done after DetachDatabase + ValidationCore::VCoreDeinit(); + + // Global deinit check + _D("Cleanup libxml2 global values."); + xmlCleanupParser(); + } catch (const DPL::Exception& ex) { + _E("Internal Error during Shutdown:"); + DPL::Exception::DisplayKnownException(ex); + } + m_initialized = false; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::makeStatusOfWrtInit(WrtErrStatus status) +{ + if (status == WRT_SUCCESS) { + _D("Init succesfull"); + m_initialized = true; + m_returnStatus = 0; + + DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } else { + _E("Init unsuccesfull"); + m_returnStatus = -1; + DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::QuitEvent()); + } +} + +void WrtInstaller::staticWrtInitializeToPreloadCallback(std::string tizenId, WrtErrStatus + status, void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + std::string printMsg = "uninstallation"; + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = 1; + + This->showErrorMsg(status, tizenId, printMsg); + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + } else { + InstallMode mode; + mode.extension = InstallMode::ExtensionType::DIR; + mode.installTime = InstallMode::InstallTime::PRELOAD; + mode.rootPath = InstallMode::RootPath::RO; + std::string packagePath = + std::string(WrtDB::GlobalConfig::GetUserPreloadedWidgetPath()) + + "/" + This->m_name; + + if (InstallMode::InstallTime::PRELOAD == This->m_installMode.installTime) { + DPL::Log::OldStyleLogProvider *oldStyleProvider = + new DPL::Log::OldStyleLogProvider(false, false, false, true, + false, true); + DPL::Log::LogSystemSingleton::Instance().AddProvider(oldStyleProvider); + } + + _D("INSTALL WIDGET: %s", packagePath.c_str()); + // Post installation event + CONTROLLER_POST_EVENT( + Logic::InstallerController, + InstallerControllerEvents::InstallWidgetEvent( + packagePath, tizenId.c_str(), Jobs::WidgetInstall::WidgetInstallationStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + This, &staticWrtInitPreloadStatusCallback, NULL), + mode, + This->pkgmgrSignalInterface))); + } +} + +void WrtInstaller::staticWrtInitPreloadStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + std::string printMsg = "initialization"; + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = status; + + This->showErrorMsg(status, tizenId, printMsg); + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + } else { + fprintf(stderr, + "## wrt-installer : %s %s was successful.\n", + tizenId.c_str(), + printMsg.c_str()); + _D("Status succesfull"); + This->m_returnStatus = 0; + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } +} + +void WrtInstaller::staticWrtStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata) +{ + WrtInstaller *This = static_cast(userdata); + Assert(This); + + Step current = This->GetCurrentStep(); + std::string printMsg; + + if (current == &WrtInstaller::installStep) { + printMsg = "installation"; + } else if (current == &WrtInstaller::uninstallPkgNameStep || + current == &WrtInstaller::unistallWgtFileStep) + { + printMsg = "uninstallation"; + } + + if (WRT_SUCCESS != status) { + // Failure + _E("Step failed"); + This->m_returnStatus = status; + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::QuitEvent()); + + This->showErrorMsg(status, tizenId, printMsg); + } else { + fprintf(stderr, + "## wrt-installer : %s %s was successful.\n", + tizenId.c_str(), + printMsg.c_str()); + _D("Status succesfull"); + This->m_returnStatus = 0; + + if (This->m_installMode.installTime == InstallMode::InstallTime::PRELOAD && + !This->m_packagePath.empty()) + { + _D("This widget is preloaded so it will be removed : %s", This->m_packagePath.c_str()); + if (!WrtUtilRemove(This->m_packagePath)) { + _E("Failed to remove %s", This->m_packagePath.c_str()); + } + } + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } +} + +void WrtInstaller::showErrorMsg(WrtErrStatus status, std::string tizenId, + std::string printMsg) +{ + switch (status) { + case WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - widget package does not exist\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - invalid widget package\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION: + fprintf(stderr, "## wrt-installer : %s %s has failed - given" + " version is lower than existing version\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - manifest" + " file doesn't find in package.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid manifestx.xml\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_CONFIG_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "config.xml does not exist\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CONFIG_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid config.xml\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "signature doesn't exist in package.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid signature.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "signature verification failed.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "root certificate could not find.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATION_INVAID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid certification.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "certificate chain verification failed.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "certificate expired.\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_INVALID_PRIVILEGE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid privilege\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "privilege level violation\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "menu icon could not find\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_FATAL_ERROR: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "fatal error\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_OUT_OF_STORAGE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "out of storage\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_OUT_OF_MEMORY: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "out of memory\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ARGUMENT_INVALID: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "invalid argument\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "package already installed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ACE_CHECK_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "ace check failure\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "to create manifest failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_ENCRYPTION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "encryption of resource failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "installation of osp service failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + case WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED: + fprintf(stderr, "## wrt-installer : %s %s has failed - " + "widget uninstallation failed\n", + tizenId.c_str(), printMsg.c_str()); + break; + + + case WRT_INSTALLER_ERROR_UNKNOWN: + fprintf(stderr,"## wrt-installer : %s %s has failed - unknown error\n", + tizenId.c_str(), printMsg.c_str()); + break; + + default: + break; + } + +} + +void WrtInstaller::staticWrtPluginInstallationCallback(WrtErrStatus status, + void* userdata) +{ + Assert(userdata); + + PluginInstallerData* data = static_cast(userdata); + + WrtInstaller *This = static_cast(data->wrtInstaller); + + std::string path = std::string(data->pluginPath); + delete data; + + This->m_numPluginsToInstall--; + _D("Plugins to install: %d", This->m_numPluginsToInstall); + + if (This->m_numPluginsToInstall < 1) { + _D("All plugins installation completed"); + fprintf(stderr, "All plugins installation completed.\n"); + + //remove installation request + if (!PluginUtils::removeInstallationRequiredFlag()) { + _D("Failed to remove file initializing plugin installation"); + } + + //remove lock file + if (!PluginUtils::unlockPluginInstallation( + This->m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _D("Failed to remove installation lock"); + } + + This->DPL::Event::ControllerEventHandler + ::PostEvent(WRTInstallerNS::NextStepEvent()); + } else { + This->DPL::Event::ControllerEventHandler:: + PostEvent( + WRTInstallerNS::InstallPluginEvent()); + } + + if (WRT_SUCCESS == status) { + This->m_returnStatus = 0; + fprintf(stderr, + "## wrt-installer : plugin installation successfull [%s]\n", + path.c_str()); + _D("One plugin Installation succesfull: %s", path.c_str()); + return; + } + + // Failure + _W("One of the plugins installation failed!: %s", path.c_str()); + + switch (status) { + case WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED: + _E("failed: plugin installation failed\n"); + break; + + case WRT_INSTALLER_ERROR_UNKNOWN: + _E("failed: unknown error\n"); + break; + + default: + break; + } +} + +void WrtInstaller::staticWrtPluginInstallProgressCb(float percent, + const char* description, + void* userdata) +{ + PluginInstallerData* data = static_cast(userdata); + + std::string path = std::string(data->pluginPath); + + _D("Plugin Installation: %s progress: %2.0f description: %s", path.c_str(), percent, description); +} + +void WrtInstaller::staticWrtInstallProgressCallback(float percent, + const char* description, + void* /*userdata*/) +{ + //WrtInstaller *This = static_cast(userdata); + _D(" progress: %2.0f description: %s", percent, description); +} +void WrtInstaller::staticWrtUninstallProgressCallback(float percent, + const char* description, + void* /*userdata*/) +{ + //WrtInstaller *This = static_cast(userdata); + _D(" progress: %2.0f description: %s", percent, description); +} + +void WrtInstaller::installNewPlugins() +{ + _D("Install new plugins"); + + if (!PluginUtils::lockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD)) + { + _D("Lock NOT created"); + return; + } + + if (!PluginUtils::checkPluginInstallationRequired()) { + _D("Plugin installation not required"); + PluginUtils::unlockPluginInstallation( + m_installMode.installTime == InstallMode::InstallTime::PRELOAD); + return; + } + + m_startupPluginInstallation = true; + AddStep(&WrtInstaller::installPluginsStep); +} + +void WrtInstaller::getRecoveryPackageId(std::string &pkgId) +{ + _D("getRecoveryPackageId"); + std::string folderPath = + std::string(WrtDB::GlobalConfig::GetTempInstallInfoPath()) + "/"; + + DIR* dir = opendir(folderPath.c_str()); + if (NULL == dir) { + return; + } + + struct dirent dEntry; + struct dirent *dEntryResult; + int return_code; + + do { + struct stat statInfo; + return_code = readdir_r(dir, &dEntry, &dEntryResult); + if (dEntryResult != NULL && return_code == 0) { + std::string fileName = dEntry.d_name; + std::string fullName = folderPath + "/" + fileName; + + if (stat(fullName.c_str(), &statInfo) != 0) { + closedir(dir); + return; + } + + if (S_ISDIR(statInfo.st_mode)) { + if (("." == fileName) || (".." == fileName)) { + continue; + } + } else { + pkgId = fileName; + if (0 != unlink(fullName.c_str())) { + _E("Fail to delete : %s", fullName.c_str()); + } + } + } + } while (dEntryResult != NULL && return_code == 0); + closedir(dir); +} + +#if 0 +void shell(const char* cmd) { + char buf[256]; + FILE *fp; + + _E("### %s ###", cmd); + fp = popen(cmd, "r"); + if (fp == NULL) { + _E("error: fp is NULL"); + } else { + while(fgets(buf, 256, fp) != NULL) { + _E("%s", buf); + } + pclose(fp); + } +} +#endif + +int main(int argc, char *argv[]) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + DPL::Log::LogSystemSingleton::Instance().SetTag("WRT_INSTALLER"); + + // Output on stdout will be flushed after every newline character, + // even if it is redirected to a pipe. This is useful for running + // from a script and parsing output. + // (Standard behavior of stdlib is to use full buffering when + // redirected to a pipe, which means even after an end of line + // the output may not be flushed). + setlinebuf(stdout); + + // Check and re-set the file open limitation + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim) != -1) { + _D("RLIMIT_NOFILE sft(%d)", rlim.rlim_cur); + _D("RLIMIT_NOFILE hrd(%d)", rlim.rlim_max); + + if (rlim.rlim_cur < NOFILE_CNT_FOR_INSTALLER) { + rlim.rlim_cur = NOFILE_CNT_FOR_INSTALLER; + rlim.rlim_max = NOFILE_CNT_FOR_INSTALLER; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + _E("setrlimit is fail!!"); + } + } + } else { + _E("getrlimit is fail!!"); + } + + WrtInstaller app(argc, argv); + int ret = app.Exec(); + // In FOTA environment, appcore will return -1 due to /tmp is read-only. + if (ret != 0) { + app.OnCreate(); + elm_run(); + app.OnTerminate(); + } + _D("App returned: %d", ret); + ret = app.getReturnStatus(); + _D("WrtInstaller returned: %d", ret); + return ret; + } + UNHANDLED_EXCEPTION_HANDLER_END +} diff --git a/src_wearable/wrt-installer/wrt-installer.h b/src_wearable/wrt-installer/wrt-installer.h new file mode 100755 index 0000000..a60d36c --- /dev/null +++ b/src_wearable/wrt-installer/wrt-installer.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt-installer.h + * @version 1.0 + * @brief Implementation file for installer + */ +#ifndef WRT_INSTALLER_H +#define WRT_INSTALLER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WRTInstallerNS { //anonymous +DECLARE_GENERIC_EVENT_0(QuitEvent) +DECLARE_GENERIC_EVENT_0(NextStepEvent) +DECLARE_GENERIC_EVENT_0(InstallPluginEvent) +} + +typedef void (*ShowResultCallback)(void *data, Evas_Object *obj, + void *event_info); +class WrtInstaller : + public DPL::Application, + private DPL::Event::Controller:: + Type>, + public DPL::TaskDecl +{ + public: + WrtInstaller(int argc, + char **argv); + virtual ~WrtInstaller(); + + int getReturnStatus() const; + + virtual void OnStop(); + virtual void OnCreate(); + virtual void OnReset(bundle *b); + virtual void OnTerminate(); + + private: + void showHelpAndQuit(); + void showArguments(); + + // Events + virtual void OnEventReceived(const WRTInstallerNS::QuitEvent &event); + virtual void OnEventReceived(const WRTInstallerNS::NextStepEvent& event); + virtual void OnEventReceived( + const WRTInstallerNS::InstallPluginEvent& event); + + // Installation steps + void initStep(); + void installStep(); + void installPluginsStep(); + void uninstallPkgNameStep(); + void unistallWgtFileStep(); + void removeUpdateStep(); + void shutdownStep(); + + // Static callbacks + static void staticWrtStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + static void staticWrtPluginInstallationCallback(WrtErrStatus status, + void* userdata); + static void staticWrtPluginInstallProgressCb(float percent, + const char* description, + void* userdata); + static void staticWrtInstallProgressCallback(float percent, + const char* description, + void* userdata); + + static void staticWrtUninstallProgressCallback(float percent, + const char* description, + void* userdata); + + static void staticWrtInitializeToPreloadCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + + static void staticWrtInitPreloadStatusCallback(std::string tizenId, + WrtErrStatus status, + void* userdata); + + void installNewPlugins(); + void showErrorMsg(WrtErrStatus status, std::string tizenId, std::string + printMsg); + + void makeStatusOfWrtInit(WrtErrStatus status); + void getRecoveryPackageId(std::string &pkgId); + bool setInitialCSC(std::string cscPath); + + // Private data + std::shared_ptr pkgmgrSignalInterface; + InstallMode m_installMode; + std::string m_packagePath; + std::string m_name; + bool m_initialized; + size_t m_numPluginsToInstall; + size_t m_totalPlugins; + int m_returnStatus; + bool m_sendPkgSig; + bool m_startupPluginInstallation; + + typedef std::list PluginPathList; + boost::optional m_pluginsPaths; +}; +#endif // WRT_INSTALLER_H diff --git a/src_wearable/wrt-installer/wrt_type.h b/src_wearable/wrt-installer/wrt_type.h new file mode 100644 index 0000000..3a5ed67 --- /dev/null +++ b/src_wearable/wrt-installer/wrt_type.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_type.h + * @author jihoon Chung (jihoon.Chung@samsung.com) + * @version 1.0 + * @brief This file contains declarations of wrt api + */ + +/* + * @defgroup wrt_engine_group WebRunTime engine Library + * @ingroup internet_FW + * Functions to APIs to access wrt-engine + */ + +#ifndef WRT_TYPE_H_ +#define WRT_TYPE_H_ + +#include +#include + +#define WRT_DEPRECATED __attribute__((deprecated)) + +typedef enum +{ + /* Generic success */ + WRT_SUCCESS = 0, /*< Success*/ + + /* pkgmgr error */ + WRT_INSTALLER_ERROR_PACKAGE_NOT_FOUND, ///< + WRT_INSTALLER_ERROR_PACKAGE_INVALID, ///< invalid widget package + WRT_INSTALLER_ERROR_PACKAGE_LOWER_VERSION, ///< given version is lower than existing version + WRT_INSTALLER_ERROR_PACKAGE_EXCUTABLE_NOT_FOUND, + + WRT_INSTALLER_ERROR_MANIFEST_NOT_FOUND = 11,///< + WRT_INSTALLER_ERROR_MANIFEST_INVALID, ///< + WRT_INSTALLER_CONFIG_NOT_FOUND, ///< couldn't find config.xml + ///< in package. + WRT_INSTALLER_ERROR_CONFIG_INVALID, ///< invalid config.xml + + WRT_INSTALLER_ERROR_SIGNATURE_NOT_FOUND = 21, ///< signature file not exist. + WRT_INSTALLER_ERROR_SIGNATURE_INVALID, ///< invalid signature file + WRT_INSTALLER_ERROR_SIGNATURE_VERIFICATION_FAILED, ///< failure in verificate signature + WRT_INSTALLER_ERROR_ROOT_CERTIFICATE_NOT_FOUND = 31, ///< couldn't find root certificate. + WRT_INSTALLER_ERROR_CERTIFICATION_INVAID, ///< invalid certification + WRT_INSTALLER_ERROR_CERTIFICATE_CHAIN_VERIFICATION_FAILED, ///< failure in verificate certification chain. + WRT_INSTALLER_ERROR_CERTIFICATE_EXPIRED, ///< expire cerification. + + WRT_INSTALLER_ERROR_INVALID_PRIVILEGE = 41, ///< invalid privilege. + WRT_INSTALLER_ERROR_PRIVILEGE_LEVEL_VIOLATION, + + WRT_INSTALLER_ERROR_MENU_ICON_NOT_FOUND = 51, ///< + + WRT_INSTALLER_ERROR_FATAL_ERROR = 61, ///< failure in db operation or file opertion.. + WRT_INSTALLER_ERROR_OUT_OF_STORAGE, ///< failure in shortage of memory + WRT_INSTALLER_ERROR_OUT_OF_MEMORY, ///< failure in shortage of RAM + WRT_INSTALLER_ERROR_ARGUMENT_INVALID, + + /* wrt-installer error */ + /* 121-140 : reserved for Web installer */ + + /* installation */ + WRT_INSTALLER_ERROR_PACKAGE_ALREADY_INSTALLED = 121, + WRT_INSTALLER_ERROR_ACE_CHECK_FAILED, + WRT_INSTALLER_ERROR_MANIFEST_CREATE_FAILED, ///< + WRT_INSTALLER_ERROR_ENCRYPTION_FAILED, ///< Failure in reousrce encrypttion + WRT_INSTALLER_ERROR_INSTALL_OSP_SERVCIE, ///< Failure in installing osp service + WRT_INSTALLER_ERROR_PLUGIN_INSTALLATION_FAILED, + WRT_INSTALLER_ERROR_UNINSTALLATION_FAILED, + + WRT_INSTALLER_ERROR_UNKNOWN = 140, ///< do not use this error code. + +} WrtErrStatus; + +#endif /* WRT_TYPE_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..5951524 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Karol Pawlowski (k.pawlowski@samsung.com) +# + + +ADD_SUBDIRECTORY(general) diff --git a/tests/general/AceRegistrationTests.cpp b/tests/general/AceRegistrationTests.cpp new file mode 100644 index 0000000..969c194 --- /dev/null +++ b/tests/general/AceRegistrationTests.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file AceRegistrationTests.cpp + * @author Dominik Duda (d.duda@samsung.com) + * @version 1.0 + * @brief Tests functions from wrt-installer/src/jobs/widget_install/ace_registration.cpp + */ + +#include +#include +#include +#include +#include +#include +#include + + +using namespace AceApi; +using namespace WrtDB; +using namespace InstallerWrapper; + +RUNNER_TEST_GROUP_INIT(AceRegistration) + +namespace{ + const std::string wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; + + std::string tizenId; + DbWidgetHandle wgtHandle; +} + +/* +Name: ace_registration_tests_00 +Description: Install a widget which is needed for further tests. +Expected: The widget should be successfully installed. +*/ +RUNNER_TEST(ace_registration_tests_00) +{ + RUNNER_ASSERT_MSG(install(wgtPath, tizenId) == InstallerWrapper::Success, + "Failed to install widget"); + + wgtHandle = WidgetDAOReadOnly::getHandle(DPL::FromUTF8String(tizenId)); +} + +/* +Name: ace_registration_tests_01 +Description: Tests registration of the widget in ACE database. +Expected: The widget should be successfully registered. +*/ +RUNNER_TEST(ace_registration_tests_01) +{ + bool test1; + ace_return_t test2, test3, test4; + WidgetRegisterInfo regInfo; + WidgetCertificateDataList certificates; + WidgetCertificateData cert1, cert2, cert3; + + regInfo.webAppType.appType = WrtDB::APP_TYPE_TIZENWEBAPP; + regInfo.configInfo.widget_id = DPL::FromUTF8String(tizenId); + regInfo.configInfo.version = DPL::FromUTF8String("1.0"); + regInfo.configInfo.authorName = DPL::FromUTF8String("Author"); + + cert1.owner = WidgetCertificateData::Owner::AUTHOR; + cert1.type = WidgetCertificateData::Type::ROOT; + cert1.chainId = wgtHandle; + cert1.strMD5Fingerprint = ""; + cert1.strSHA1Fingerprint = ""; + cert1.strCommonName = DPL::FromUTF8String(""); + + cert2.owner = WidgetCertificateData::Owner::DISTRIBUTOR; + cert2.type = WidgetCertificateData::Type::ENDENTITY; + cert2.chainId = wgtHandle; + cert2.strMD5Fingerprint = ""; + cert2.strSHA1Fingerprint = ""; + cert2.strCommonName = DPL::FromUTF8String(""); + + cert3.owner = WidgetCertificateData::Owner::UNKNOWN; + cert3.type = static_cast(2); + cert3.chainId = wgtHandle; + cert3.strMD5Fingerprint = ""; + cert3.strSHA1Fingerprint = ""; + cert3.strCommonName = DPL::FromUTF8String(""); + + certificates.push_back(cert1); + certificates.push_back(cert2); + certificates.push_back(cert3); + + test2 = ace_install_initialize(); + wgtHandle = WidgetDAOReadOnly::getHandle(DPL::FromUTF8String(tizenId)); + + //Unregister widget in ACE db in order to test registerAceWidget function + test3 = ace_unregister_widget( + static_cast(wgtHandle)); + test1 = registerAceWidget(wgtHandle, regInfo, certificates); + test4 = ace_install_shutdown(); + + RUNNER_ASSERT_MSG(test1, "Registering widget from the DB failed!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Cannot initialize ACE database!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Cannot unregister widget in ACE database!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Shutting down ACE database failed!"); +} + +/* +Name: ace_registration_tests_02 +Description: Tests registration of the widget which data already are in +the database. +Expected: The widget should be successfully registered. +*/ +RUNNER_TEST(ace_registration_tests_02) +{ + bool test1; + ace_return_t test2, test3, test4; + + test2 = ace_install_initialize(); + + //Unregister widget in ACE db in order to test registerAceWidgetFromDB + //function + test3 = ace_unregister_widget( + static_cast(wgtHandle)); + test1 = registerAceWidgetFromDB(wgtHandle); + test4 = ace_install_shutdown(); + + RUNNER_ASSERT_MSG(test1, "Registering widget from the DB failed!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Cannot initialize ACE database!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Cannot unregister widget in ACE database!"); + RUNNER_ASSERT_MSG(test2 == ACE_OK, "Shutting down ACE database failed!"); +} + +/* +Name: ace_registration_tests_03 +Description: Uninstalls the widget previously installed for tests. +Expected: The widget should be successfully uninstalled. +*/ +RUNNER_TEST(ace_registration_tests_03) +{ + RUNNER_ASSERT_MSG(uninstall(tizenId), "Failed to uninstall widget!"); +} diff --git a/tests/general/BackgroundPageTests.cpp b/tests/general/BackgroundPageTests.cpp new file mode 100644 index 0000000..b996cbe --- /dev/null +++ b/tests/general/BackgroundPageTests.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestCases.cpp + * @author Karol Pawlowski (k.pawlowski@samsung.com) + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @version 1.0 + * @brief Background page installation test's bodies + */ + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(BackgroundPage) + +/* +Name: widgetWithBackgroundPage +Description: Tests if widget with background page is installed correctly +Expected: widget should be installed correctly +*/ +RUNNER_TEST(widgetWithBackgroundPage) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/bg-00-with_bg.wgt", + tizenId) == InstallerWrapper::Success); + uninstall(tizenId); +} + +/* +Name: missingBackgroundFile +Description: Tests if widget with declared in conifg background page + but missing background file will be installed correctly. +Expected: widget should NOT be installed +*/ +RUNNER_TEST(missingBackgroundFile) +{ + std::string tizenId; + if(install(miscWidgetsStuff + "widgets/bg-01-missing_file.wgt", + tizenId) == InstallerWrapper::Success) { + uninstall(tizenId); + RUNNER_ASSERT_MSG(false, "Invalid widget package installed"); + } +} + +/* +Name: widgetWithoutBackgroundPage +Description: Complementary test to check if normal widget\ + without background page is successfully installed +Expected: widget should be installed +*/ +RUNNER_TEST(widgetWithoutBackgroundPage) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/bg-02-without_bg.wgt", + tizenId) == InstallerWrapper::Success); + uninstall(tizenId); +} diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt new file mode 100644 index 0000000..05a4161 --- /dev/null +++ b/tests/general/CMakeLists.txt @@ -0,0 +1,181 @@ +# Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Karol Pawlowski (k.pawlowski@samsung.com) +# + +PKG_CHECK_MODULES(COMMON_LIB_PKGS + dbus-1 + libpcrecpp + dpl-efl + dpl-test-efl + dpl-utils-efl + dpl-wrt-dao-ro + dpl-event-efl + cert-svc-vcore + xmlsec1 + libiri + REQUIRED + ) + +pkg_search_module(dpl REQUIRED dpl-efl) +pkg_search_module(dpl-test REQUIRED dpl-test-efl) + +SET(WRT_TEST_LIBRARY "wrt-tests-libs") + +include_directories( + ${dpl_INCLUDE_DIRS} + ${dpl-test_INCLUDE_DIRS} + ${ace-client_INCLUDE_DIRS} +) + +SET(COMMON_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") + +SET_PROPERTY(GLOBAL APPEND PROPERTY COMMON_TESTS_LIBRARY ${WRT_TEST_LIBRARY}) + +SET_PROPERTY(GLOBAL APPEND PROPERTY TESTS_INCLUDE_DIRS ${COMMON_LIB_PKGS_INCLUDE_DIRS}) + +SET_PROPERTY(GLOBAL APPEND PROPERTY TESTS_LIBRARY_DIRS ${COMMON_LIB_PKGS_LIBRARY_DIRS}) + +SET_PROPERTY(GLOBAL APPEND PROPERTY TESTS_LIBRARIES ${COMMON_LIB_PKGS_LIBRARIES}) + +SET(WRT_DETAIL_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/InstallerWrapper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ManifestFile.cpp +) + +INCLUDE_DIRECTORIES(${COMMON_INCLUDES}) +INCLUDE_DIRECTORIES(${COMMON_LIB_PKGS_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(SYSTEM ${SYS_INSTALLER_STATIC_DEP_INCLUDE_DIRS}) + +ADD_LIBRARY(${WRT_TEST_LIBRARY} STATIC ${WRT_DETAIL_SOURCES}) + + +SET(INSTALLER_TESTS_TARGET "wrt-installer-tests-general") + +PKG_CHECK_MODULES(INSTALLER_TESTS_DEPS + wrt-commons-i18n-dao-ro + REQUIRED) + +SET(INSTALLER_TESTS_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/TestInit.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ManifestTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BackgroundPageTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/NPluginsInstallTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingAccountTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingAllowNavigationTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingAppWidgetTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingCategoryTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingContentTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingCspTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingMetadataTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingTizenAppcontrolTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingTizenPrivilegeTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParsingSplashImgTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WidgetUpdateTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/PluginsInstallation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/LanguageSubtagRstTreeTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WidgetInstallManifestTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WidgetLocationTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/AceRegistrationTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParserRunnerTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TaskConfigurationTests.cpp + ${CMAKE_SOURCE_DIR}/src/wrt-installer/language_subtag_rst_tree.cpp + ${CMAKE_SOURCE_DIR}/src/wrt-installer/installer_callbacks_translate.cpp +) + +SET(INSTALLER_TESTS_INCLUDE_DIRS + ${INSTALLER_TESTS_DEPS_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/src/wrt-installer + ${CMAKE_SOURCE_DIR}/src/configuration_parser + ${CMAKE_SOURCE_DIR}/src/commons + ${CMAKE_SOURCE_DIR}/src/jobs + ${CMAKE_SOURCE_DIR}/src/jobs/widget_install + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/misc + ${CMAKE_SOURCE_DIR}/src/pkg-manager +) + +# Functions used to build test targets (proper sources, includes, libs are +# added automatically) +FUNCTION(WRT_TEST_BUILD TARGET_NAME) + # get include dirs global property + GET_PROPERTY(INCLUDE_DIRS GLOBAL PROPERTY TESTS_INCLUDE_DIRS) + GET_PROPERTY(TEST_INCLUDE_DIRS GLOBAL PROPERTY ${TARGET_NAME}_INCLUDE_DIRS) + INCLUDE_DIRECTORIES( + ${INCLUDE_DIRS} + ${TEST_INCLUDE_DIRS} + ) + + # get library dirs global property + GET_PROPERTY(LIBRARY_DIRS GLOBAL PROPERTY TESTS_LIBRARY_DIRS) + GET_PROPERTY(TEST_LIBRARY_DIRS GLOBAL PROPERTY ${TARGET_NAME}_LIBRARY_DIRS) + LINK_DIRECTORIES( + ${LIBRARY_DIRS} + ${TEST_LIBRARY_DIRS} + ) + + SET(SOURCES "${ARGN}") + ADD_EXECUTABLE("${TARGET_NAME}" ${SOURCES}) + + # get link libraries global property + GET_PROPERTY(LINK_LIBRARIES GLOBAL PROPERTY TESTS_LIBRARIES) + GET_PROPERTY(TEST_LIBRARIES GLOBAL PROPERTY ${TARGET_NAME}_LIBRARIES) + TARGET_LINK_LIBRARIES("${TARGET_NAME}" + ${LINK_LIBRARIES} + ${TEST_LIBRARIES} + ) +ENDFUNCTION(WRT_TEST_BUILD) + +FUNCTION(WRT_TEST_INSTALL) + SET_TARGET_PROPERTIES(${ARGV} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON + ) + INSTALL(TARGETS ${ARGV} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) +ENDFUNCTION(WRT_TEST_INSTALL) + +FUNCTION(WRT_TEST_INCLUDE_DIRS TARGET_NAME) + SET_PROPERTY(GLOBAL APPEND PROPERTY ${TARGET_NAME}_INCLUDE_DIRS ${ARGN}) +ENDFUNCTION(WRT_TEST_INCLUDE_DIRS) + +WRT_TEST_INCLUDE_DIRS(${INSTALLER_TESTS_TARGET} ${INSTALLER_TESTS_INCLUDE_DIRS}) +WRT_TEST_BUILD(${INSTALLER_TESTS_TARGET} ${INSTALLER_TESTS_SOURCES}) +WRT_TEST_INSTALL(${INSTALLER_TESTS_TARGET}) +target_link_libraries(${INSTALLER_TESTS_TARGET} + ${dpl_LIBRARIES} + ${dpl-test_LIBRARIES} + ${WRT_TEST_LIBRARY} + ${TARGET_CORE_MODULE_LIB} + ${COMMON_LIB_PKGS_LIBRARIES} + ${INSTALLER_TESTS_DEPS_LIBRARIES} + ${TARGET_INSTALLER_STATIC} +) + +#widgets +FILE(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/widgets/*.wgt") +INSTALL(FILES ${files} "${CMAKE_CURRENT_SOURCE_DIR}/widgets/widgetInDir.tar" DESTINATION /opt/share/widget/tests/installer/widgets/) + +FILE(GLOB files_conf "${CMAKE_CURRENT_SOURCE_DIR}/configs/*.xml") +INSTALL(FILES ${files_conf} DESTINATION /opt/share/widget/tests/installer/configs/) diff --git a/tests/general/InstallerWrapper.cpp b/tests/general/InstallerWrapper.cpp new file mode 100644 index 0000000..de98a3f --- /dev/null +++ b/tests/general/InstallerWrapper.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InstallerWrapper.h" + +#include + +#include + +namespace +{ + +const std::string params = "DPL_USE_OLD_STYLE_LOGS=0 " + "DPL_USE_OLD_STYLE_PEDANTIC_LOGS=0 WRT_TEST_MODE=1 "; +const std::string installCmd = params + "wrt-installer -i "; +const std::string uninstallCmd = params + "wrt-installer -un "; +const std::string redirection = " 2>&1"; +const std::string INSTALLER_MESSAGE_ID_LINE = + "## wrt-installer : %s installation was successful.\n"; +const std::string INSTALLER_MESSAGE_ID_LINE_FAIL = + "## wrt-installer : %s installation has failed - package already installed"; +const std::string INSTALLER_MESSSGE_START = "## wrt-installer : "; + +std::string getAndCutInstallerLogLine(std::string &src) +{ + size_t startIndex = src.find(INSTALLER_MESSSGE_START); + if (startIndex == std::string::npos) + { + _W("Installer message can not be found"); + return std::string(); + } + size_t newLineIndex = src.find("\n", startIndex); + std::string line = src.substr(startIndex, newLineIndex - startIndex + 1); + src.erase(0, newLineIndex + 1); + return line; +} + +} + +namespace InstallerWrapper +{ + +InstallResult install( + const std::string& path, + std::string& tizenId, + const std::string& user) +{ + std::string msg; + + auto cmd = installCmd + path + redirection; + if(user.length()) //if other user should be used + { + cmd = "su " + user + " -c '" + cmd + "'"; + } + auto filehandle = popen(cmd.c_str(), "r"); + if (!filehandle) {; + return OtherError; + } + + char buffer[1024] = ""; + while ( fread_unlocked(buffer, sizeof(char), + sizeof(buffer)/sizeof(char), filehandle) > 0 ) + { + msg += buffer; + } + _D("%s", msg.c_str()); + auto err = pclose(filehandle); + if (!WIFEXITED(err)) { + return OtherError; + } + + char* id = NULL; + std::string line; + + if (0 != WEXITSTATUS(err)) { + while ((line = getAndCutInstallerLogLine(msg)) != "") + { + if(line.find("failed") != std::string::npos) + { + id = new char[line.length()]; + int nr = sscanf(line.c_str(), INSTALLER_MESSAGE_ID_LINE_FAIL.c_str(), id); + if (1 != nr) + { + _W("Can not read widget ID from message: %s", line.c_str()); + delete[] id; + return OtherError; + } + tizenId = id; + delete[] id; + } + } + + if (1 == WEXITSTATUS(err)) { + return WrongWidgetPackage; + } + return OtherError; + } + + while ((line = getAndCutInstallerLogLine(msg)) != "") + { + if (line.find("successful") != std::string::npos) + { + id = new char[line.length()]; + int nr = sscanf(line.c_str(), INSTALLER_MESSAGE_ID_LINE.c_str(), id); + + if (1 != nr) + { + _W("Can not read widget ID from message: %s", line.c_str()); + delete[] id; + return OtherError; + } + tizenId = id; + delete[] id; + if (tizenId != "plugin") + { + return Success; + } + } + } + + return OtherError; +} + +bool uninstall(const std::string& tizenId) +{ + std::string cmd = uninstallCmd + tizenId + " > /dev/null 2>/dev/null"; + _D("executing: %s", cmd.c_str()); + return (system(cmd.c_str()) == EXIT_SUCCESS); +} + +bool sigintWrtClients() +{ + return (system("pkill -2 wrt-client") == 0); +} + +} + diff --git a/tests/general/InstallerWrapper.h b/tests/general/InstallerWrapper.h new file mode 100644 index 0000000..404a3e8 --- /dev/null +++ b/tests/general/InstallerWrapper.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WRT_INSTALLER_TESTS_GENERAL_INSTALLER_WRAPPER_H +#define WRT_INSTALLER_TESTS_GENERAL_INSTALLER_WRAPPER_H + +#include + +namespace InstallerWrapper +{ + +typedef int InstallResult; +const InstallResult WrongWidgetPackage = -2; +const InstallResult OtherError = -1; +const InstallResult Success = 0; + +const std::string miscWidgetsStuff = "/opt/share/widget/tests/installer/"; + +struct Result { + bool m_exc; + bool m_exd; + bool m_exs; + std::string message; + Result(bool exc = false, bool exd = false, bool exs = false) + : m_exc(exc), m_exd(exd), m_exs(exs) {} +}; + +InstallResult install( + const std::string& path, + std::string& tizenId, + const std::string& user = ""); +bool uninstall(const std::string& tizenId); +/** + * @brief killWrtClients kills processes that matches 'wrt-client' + * @return True if any client was killed + */ +bool sigintWrtClients(); + +} + +#endif//WRT_INSTALLER_TESTS_GENERAL_INSTALLER_WRAPPER_H diff --git a/tests/general/LanguageSubtagRstTreeTests.cpp b/tests/general/LanguageSubtagRstTreeTests.cpp new file mode 100644 index 0000000..bd05f87 --- /dev/null +++ b/tests/general/LanguageSubtagRstTreeTests.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file LanguageSubtagRstTreeTests.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Language tags tests + */ + +#include +#include + +namespace { +const char LANGUAGE_TAG_VALID[] = "en-us"; +const char LANGUAGE_TAG_INVALID[] = "invalid0"; +} + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(LanguageSubtagRstTree) + +/* +Name: ValidateLanguageTag_Valid +Description: tests result returned for valid language tag +Expected: value true should be returned +*/ +RUNNER_TEST(ValidateLanguageTag_Valid) +{ + RUNNER_ASSERT(LanguageSubtagRstTreeSingleton::Instance(). + ValidateLanguageTag(LANGUAGE_TAG_VALID)); +} + +/* +Name: ValidateLanguageTag_Invalid +Description: tests result returned for invalid language tag +Expected: value false should be returned +*/ +RUNNER_TEST(ValidateLanguageTag_Invalid) +{ + RUNNER_ASSERT(!LanguageSubtagRstTreeSingleton::Instance(). + ValidateLanguageTag(LANGUAGE_TAG_INVALID)); +} diff --git a/tests/general/ManifestFile.cpp b/tests/general/ManifestFile.cpp new file mode 100644 index 0000000..2206c2a --- /dev/null +++ b/tests/general/ManifestFile.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ManifestFile.cpp + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @brief Manifest file reading + */ + +#include + +#include +#include + +#include + +//TODO: This file reads manifest file. This functionality is familiar with writing +// in wrt-installer but reading ws not necessary there. +// Maybe it should be changed in some way. + +ManifestFile::ManifestFile(const std::string & file) : filename(file) +{ + xmlXPathInit(); + parse(); +} + +ManifestFile::~ManifestFile() +{ + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); +} + +void ManifestFile::parse() +{ + doc = xmlReadFile(filename.c_str(), NULL, 0); + if (doc == NULL) + { + ThrowMsg(ManifestParseError,"File Problem"); + } + else + { + //context + xpathCtx = xmlXPathNewContext(doc); + if(xpathCtx == NULL) + { + ThrowMsg(ManifestParseError,"Error: unable to create new XPath context\n"); + } + xpathCtx->node = xmlDocGetRootElement(doc); + + if(xmlXPathRegisterNs(xpathCtx, BAD_CAST "p", BAD_CAST "http://tizen.org/ns/packages") != 0) + { + ThrowMsg(ManifestParseError,"Error: unable to register namespace\n"); + } + } +} + +std::string ManifestFile::getValueByXpath(const std::string & path) const +{ + std::string result; + xmlXPathObjectPtr xpathObject; + //get requested node's values + xpathObject = xmlXPathEvalExpression(BAD_CAST path.c_str(), xpathCtx); + if(xpathObject == NULL) + { + ThrowMsg(ManifestParseError,"XPath evaluation failure: " << path); + } + xmlNodeSetPtr nodes = xpathObject->nodesetval; + int size = (nodes) ? nodes->nodeNr : 0; + if(size != 1) + { + ThrowMsg(ManifestParseError,"Xpath does not point 1 element but " << size + << " for xpath: " << path); + } + else + { + if(nodes->nodeTab[0]->type == XML_ELEMENT_NODE) + { + xmlNodePtr cur = nodes->nodeTab[0]; + xmlChar * value = xmlNodeGetContent(cur); + result = std::string(reinterpret_cast(value)); //this cast should be safe... + xmlFree(value); + } + else if(nodes->nodeTab[0]->type == XML_ATTRIBUTE_NODE) + { + xmlNodePtr cur = nodes->nodeTab[0]; + xmlChar * value = xmlNodeGetContent(cur); + result = std::string(reinterpret_cast(value)); + xmlFree(value); + } + } + //Cleanup of XPath data + xmlXPathFreeObject(xpathObject); + return result; +} diff --git a/tests/general/ManifestFile.h b/tests/general/ManifestFile.h new file mode 100644 index 0000000..ccb6a7b --- /dev/null +++ b/tests/general/ManifestFile.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ManifestFile.h + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @brief Manifest file reading + */ + +#ifndef WRT_INSTALLER_TESTS_GENERAL_MANIFESTFILE_H +#define WRT_INSTALLER_TESTS_GENERAL_MANIFESTFILE_H + +#include + +#include +#include + +#include + +/** + * @brief The ManifestFile class which serialize xml file to tree + */ +class ManifestFile +{ +public: + DECLARE_EXCEPTION_TYPE(DPL::Exception,Base) + DECLARE_EXCEPTION_TYPE(Base,ManifestParseError) + + ManifestFile(const std::string & file); + ~ManifestFile(); + + std::string getValueByXpath(const std::string & path) const; +private: + void parse(); + + std::string filename; + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; +}; + + +#endif //WRT_INSTALLER_TESTS_GENERAL_MANIFESTFILE_H diff --git a/tests/general/ManifestTests.cpp b/tests/general/ManifestTests.cpp new file mode 100644 index 0000000..4e316da --- /dev/null +++ b/tests/general/ManifestTests.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestCases.cpp + * @author Karol Pawlowski (k.pawlowski@samsung.com) + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @version 1.0 + * @brief Manifest installation test's bodies + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(Manifest) + +/* +Name: creatingManifestFile +Description: Creation of manifest file by wrt-installer test +Expected: file should be created and installed by wrt-installer. Content should + match expected values +*/ +RUNNER_TEST(creatingManifestFile) +{ + std::string manifestPath = "/opt/share/packages/"; + /* This widget removal should stay here in case previous test run failed + * (so widget has not been uninstalled) */ + std::string tizenId; + + if(install(miscWidgetsStuff + "widgets/manifest.wgt", tizenId) + != InstallerWrapper::Success) + { + uninstall(tizenId); + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/manifest.wgt", tizenId) + == InstallerWrapper::Success); + } + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0,10)).append(".xml"))); + ManifestFile mf(manifestPath); + + Try + { + _D("Package %s", mf.getValueByXpath("/p:manifest/@package").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/@package") + == tizenId.substr(0,10)); + _D("type %s", mf.getValueByXpath("/p:manifest/@type").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/@type") + == "wgt"); + _D("version %s", mf.getValueByXpath("/p:manifest/@version").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/@version") + == "1.0"); + _D("label %s", mf.getValueByXpath("/p:manifest/p:label").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:label") + == "Manifest Example"); + + _D("email %s", mf.getValueByXpath("/p:manifest/p:author/@email").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:author/@email") + == "manifest@misc.test.create.desktop.com"); + _D("href %s", mf.getValueByXpath("/p:manifest/p:author/@href").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:author/@href") + == "http://misc.test.create.desktop.com"); + _D("author %s", mf.getValueByXpath("/p:manifest/p:author").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:author") + == "Manifest"); + + _D("appid %s", mf.getValueByXpath("/p:manifest/p:ui-application/@appid").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/@appid") + == tizenId); + _D("type %s", mf.getValueByXpath("/p:manifest/p:ui-application/@type").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/@type") + == "webapp"); + _D("extraid %s", mf.getValueByXpath("/p:manifest/p:ui-application/@extraid").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/@extraid") + == "http://test.samsung.com/widget/manifestTest"); + _D("icon %s", mf.getValueByXpath("/p:manifest/p:ui-application/p:icon").c_str()); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:icon") + == (std::string(WrtDB::GlobalConfig::GetUserInstalledWidgetPath()) + "/" + tizenId.substr(0,10) + WrtDB::GlobalConfig::GetWidgetSharedPath() + WrtDB::GlobalConfig::GetWidgetResPath() + "/" + tizenId + ".png")); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:label[not(@xml:lang)]") + == "Manifest Example"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:label[@xml:lang='de-de']") + == "Manifest Beispiel"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:label[@xml:lang='en-us']") + == "Manifest Example"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:label[@xml:lang='pl']") + == "Przykład Manifest"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:label[@xml:lang='pt-pt']") + == "Exemplo manifesto"); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } + /* If test finished sucessfully than uninstall test widget */ + uninstall(tizenId); +} diff --git a/tests/general/NPluginsInstallTests.cpp b/tests/general/NPluginsInstallTests.cpp new file mode 100644 index 0000000..ac141d8 --- /dev/null +++ b/tests/general/NPluginsInstallTests.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestCases.cpp + * @author Karol Pawlowski (k.pawlowski@samsung.com) + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @version 1.0 + * @brief NPlugins installation test's bodies + */ + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(NPluginsInstall) + +/* +Name: pluginFilesAdded +Description: Tests installation of plugins attached to widget +Expected: widget should be succesfully installed +*/ +RUNNER_TEST(pluginFilesAdded) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + + "widgets/inst_nplug_1.wgt", tizenId) == InstallerWrapper::Success); + uninstall(tizenId); +} + +/* +Name: pluginFileAndOtherFile +Description: Tests installation with plugins directory and data files +Expected: widget should be installed +*/ +RUNNER_TEST(pluginFileAndOtherFile) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + + "widgets/inst_nplug_3.wgt", tizenId) == InstallerWrapper::Success); + uninstall(tizenId); +} diff --git a/tests/general/ParserRunnerTests.cpp b/tests/general/ParserRunnerTests.cpp new file mode 100644 index 0000000..e87d90f --- /dev/null +++ b/tests/general/ParserRunnerTests.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParserRunnerTests.cpp + * @author Adrian Szafranek (a.szafranek@samsung.com) + * @version 1.0 + * @brief Perform ParserRunner test's. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(ParserRunner) + +using namespace DPL::Utils; + +namespace { +const std::string rootTest = "/tmp/"; +const std::string schemaFile = "/usr/etc/wrt-installer/widgets.xsd"; +const std::string configFile = "/usr/lib/wrt-plugins/tizen-content/config.xml"; +const std::string installConfigFile = "/opt/share/widget/tests/installer/configs/InstallConfig.xml"; +} + +class StreamRedirector +{ + public: + StreamRedirector(FILE* src, std::string dest) + : source(src), file_dest(dest) + { + fd = dup(fileno(source)); + if ((fd < 0) || (freopen(file_dest.c_str(), "w", source) == NULL)) + { + _D("Error catching stream."); + } + } + ~StreamRedirector() + { + if (TEMP_FAILURE_RETRY(fflush(source)) == 0) + { + if (dup2(fd, fileno(source)) >= 0) + { + close(fd); + clearerr(source); + } + else + { + _D("Error returning stream."); + } + } + else + { + _D("Error returning stream."); + } + } + + private: + FILE* source; + std::string file_dest; + int fd; +}; + + +/* +Name: parserRunner01 +Description: Tests validation and parsing functionality. +Test performed for proper files existing in system, wrong files or empty parameters. +*/ +RUNNER_TEST(parserRunner01) +{ + ParserRunner parser; + + std::unique_ptr sd(new StreamRedirector(stderr, "/dev/null")); + + + if (Path(installConfigFile).Exists() && Path(schemaFile).Exists()) { + RUNNER_ASSERT(parser.Validate(installConfigFile, schemaFile) == true); + } + + if (Path(configFile).Exists() && Path(schemaFile).Exists()) { + RUNNER_ASSERT(parser.Validate(configFile, schemaFile) == false); + } + + RUNNER_ASSERT(parser.Validate("", "") == false); +} + +/* +Name: parserRunner02 +Description: Tests validation and parsing functionality. +Test performed for wrong 'xml', empty 'xsd' file and wrong 'xml', non empty 'xsd' file. +*/ +RUNNER_TEST(parserRunner02) +{ + ParserRunner parser; + + std::unique_ptr sd(new StreamRedirector(stderr, "/dev/null")); + + + Path pathF1 = Path(rootTest + "testFile1.xml"); + if (!pathF1.Exists()) { + MakeEmptyFile(pathF1); + + DPL_SCOPE_EXIT(&pathF1) { TryRemove(pathF1); }; + + std::ofstream ofs1; + ofs1.open(pathF1.Fullpath(), std::ofstream::out); + if (!ofs1) { + RUNNER_ASSERT_MSG(false, "Error creating file"); + } + ofs1 << "wrongContent"; + ofs1.close(); + + RUNNER_ASSERT(parser.Validate(pathF1.Fullpath(), "") == false); + if (Path(schemaFile).Exists()) { + RUNNER_ASSERT(parser.Validate(pathF1.Fullpath(), schemaFile) == false); + } + } +} + +/* +Name: parserRunner03 +Description: Tests validation and parsing functionality. +Test performed for empty 'xml', wrong 'xsd' file and non empty 'xml', wrong 'xsd' file. +*/ +RUNNER_TEST(parserRunner03) +{ + ParserRunner parser; + + std::unique_ptr sd(new StreamRedirector(stderr, "/dev/null")); + + + Path pathF2 = Path(rootTest + "testFile2.xsd"); + if (!pathF2.Exists()) { + MakeEmptyFile(pathF2); + + DPL_SCOPE_EXIT(&pathF2) { TryRemove(pathF2); }; + + std::ofstream ofs2; + ofs2.open(pathF2.Fullpath(), std::ofstream::out); + if (!ofs2) { + RUNNER_ASSERT_MSG(false, "Error creating file"); + } + ofs2 << ""; + ofs2.close(); + + RUNNER_ASSERT(parser.Validate("", pathF2.Fullpath()) == false); + if (Path(configFile).Exists()) { + RUNNER_ASSERT(parser.Validate(configFile, pathF2.Fullpath()) == false); + } + } +} diff --git a/tests/general/ParsingAccountTests.cpp b/tests/general/ParsingAccountTests.cpp new file mode 100644 index 0000000..f19e52b --- /dev/null +++ b/tests/general/ParsingAccountTests.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingAccountTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief Account element installation tests + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingAccount) + +/* +Name: InstallWidgetWithAccount +Description: Tests if widget with account is installed correctly +Expected: widget should be installed correctly and account information should be present in manifest file +*/ +RUNNER_TEST(InstallWidgetWithAccount) +{ + std::string tizenId; + std::string manifestPath = "/opt/share/packages/"; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/account.wgt", tizenId) == InstallerWrapper::Success); + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0, 10)).append(".xml"))); + ManifestFile mf(manifestPath); + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/@multiple-accounts-support") == "true"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:icon[1]/@section") == "account"); + std::string iconPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSharedPath() + + WrtDB::GlobalConfig::GetWidgetResPath() + "/"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:icon[1]") == iconPath + "icon1.png"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:icon[2]/@section") == "account-small"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:icon[2]") == iconPath + "icon2.png"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:label[1]") == "Name1"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:label[2]/@xml:lang") == "en-US"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:label[2]") == "Name2"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:capability[1]") == "http://tizen.org/account/capability/contact"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:account/p:account-provider/p:capability[2]") == "http://tizen.org/account/capability/calendar"); + uninstall(tizenId); +} diff --git a/tests/general/ParsingAllowNavigationTests.cpp b/tests/general/ParsingAllowNavigationTests.cpp new file mode 100644 index 0000000..f5e219c --- /dev/null +++ b/tests/general/ParsingAllowNavigationTests.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingAllowNavigationTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief allow-navigation element installation tests + */ + +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +namespace{ + +struct AllowNavigationComparator { + AllowNavigationComparator(const DPL::String & scheme, const DPL::String& host) : + m_scheme(scheme), m_host(host){} + const DPL::String m_scheme; + const DPL::String m_host; + bool operator()(const WrtDB::ConfigParserData::AllowNavigationInfo& navs) const + { + return navs.m_host == m_host && navs.m_scheme == m_scheme; + } + bool operator()(const WrtDB::WidgetAllowNavigationInfo& navs) const + { + return navs.host == m_host && navs.scheme == m_scheme; + } +}; + +} + + + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingAllowNavigation) + +/* +Name: InstallWidgetWithAllowNavigation +Description: Tests if widget with allow-navigation is installed correctly +Expected: widget should be installed correctly and allowNavigation info should be stored in database +*/ +RUNNER_TEST(InstallWidgetWithAllowNavigation) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/allowNavigation.wgt", tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + WrtDB::WidgetAllowNavigationInfoList allowNavigationList; + dao.getWidgetAllowNavigationInfo(allowNavigationList); + + uninstall(tizenId); + + RUNNER_ASSERT(allowNavigationList.size() == 4); + RUNNER_ASSERT(1 == std::count_if(allowNavigationList.begin(), allowNavigationList.end(), + AllowNavigationComparator(L"http",L"test2.org"))); + RUNNER_ASSERT(1 == std::count_if(allowNavigationList.begin(), allowNavigationList.end(), + AllowNavigationComparator(L"*",L"test3.org"))); + RUNNER_ASSERT(1 == std::count_if(allowNavigationList.begin(), allowNavigationList.end(), + AllowNavigationComparator(L"*",L"*.test4.org"))); + RUNNER_ASSERT(1 == std::count_if(allowNavigationList.begin(), allowNavigationList.end(), + AllowNavigationComparator(L"*",L"*"))); +} + + +/* +Name: NoAllowNavigation +Description: Tests parsing configuration file without allow-navigation element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(NoAllowNavigation) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/NoAllowNavigation.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(0 == widgetConfig.allowNavigationInfoList.size()); +} + +/* +Name: AllowNavigationEmpty +Description: Tests parsing configuration file with empty allow-navigation element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(AllowNavigationEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AllowNavigationEmpty.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(0 == widgetConfig.allowNavigationInfoList.size()); +} + +/* +Name: MultipleAllowNavigation +Description: Tests parsing configuration file with multiple allow-navigation element +Expected: Element should be parsed correctly. Only values from first element should be stored +*/ +RUNNER_TEST(MultipleAllowNavigation) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/MultipleAllowNavigation.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.allowNavigationInfoList.size()); + RUNNER_ASSERT(L"*" == widgetConfig.allowNavigationInfoList.begin()->m_scheme); + RUNNER_ASSERT(L"test1.org" == widgetConfig.allowNavigationInfoList.begin()->m_host); +} + +/* +Name: AllowNavigationMultipleHosts +Description: Tests parsing configuration file with multiple values in allow-navigation element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(AllowNavigationMultipleHosts) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AllowNavigationMultipleHosts.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(4 == widgetConfig.allowNavigationInfoList.size()); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"http",L"test2.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"test3.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"*.test4.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"*"))); +} + +/* +Name: AllowNavigationMultipleHosts +Description: Tests parsing configuration file with multiple values in allow-navigation element + with special characters like \n and \t +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(AllowNavigationMultipleHostsMultiline) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AllowNavigationMultipleHostsMultiline.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(4 == widgetConfig.allowNavigationInfoList.size()); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"http",L"test2.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"test3.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"*.test4.org"))); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.allowNavigationInfoList.begin(), widgetConfig.allowNavigationInfoList.end(), + AllowNavigationComparator(L"*",L"*"))); +} diff --git a/tests/general/ParsingAppWidgetTests.cpp b/tests/general/ParsingAppWidgetTests.cpp new file mode 100644 index 0000000..6fbc2df --- /dev/null +++ b/tests/general/ParsingAppWidgetTests.cpp @@ -0,0 +1,1043 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingAppWidgetTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief Parsing Tizen app-widget bodies + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace{ + +template +bool checkException(Function fun) +{ + Try + { + fun(); + } + Catch(Exception){ + return true; + } + return false; +} + +} // namespace + +#define RUNNER_ASSERT_EXCEPTION(exceptionType, function) \ + { \ + RUNNER_ASSERT(checkException([&](){function})); \ + } + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingAppWidget) + +/* +Name: InstallWidgetWithAppWidgetFull +Description: Tests if app-widget tag is correctly parsed when all children are included +Expected: widget should be installed. All information should be stored in manifest file +*/ +RUNNER_TEST(InstallWidgetWithAppWidgetFull) +{ + std::string manifestPath = "/opt/share/packages/"; + + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/appWidgetFull.wgt", tizenId) == InstallerWrapper::Success); + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0, 10)).append(".xml"))); + ManifestFile mf(manifestPath); + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/@appid") == "jeyk39ehc8.appwidget.default"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/@primary") == "true"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/@period") == "1800.0"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:launch") == "true"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:label") == "My DynamicBox"); + std::string iconPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSharedPath() + + WrtDB::GlobalConfig::GetWidgetDataPath() + "/" + tizenId + ".default.icon.png"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:icon") == iconPath); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/@mouse_event") == "false"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/@touch_effect") == "false"); + std::string boxSrcPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSrcPath() + + "/app-widget/index.html"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:script/@src") == boxSrcPath); + std::string boxPreviewPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSharedPath() + + WrtDB::GlobalConfig::GetWidgetDataPath() + "/" + tizenId + ".default.1x1.preview.png"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:size/@preview") == boxPreviewPath); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:size/@need_frame") == "false"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:size") == "1x1"); + std::string pdSrcPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSrcPath() + + "/pd/index.html"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:pd/p:script/@src") == pdSrcPath); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:pd/p:size") == "720x150"); + + RUNNER_ASSERT(uninstall(tizenId)); +} + +/* +Name: InstallWidgetWithAppWidgetMinimal +Description: Tests if app-widget tag is correctly parsed when only mandatory children are included +Expected: widget should be installed. All information should be stored in manifest file +*/ +RUNNER_TEST(InstallWidgetWithAppWidgetMinimal) +{ + std::string manifestPath = "/opt/share/packages/"; + + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/appWidgetMinimal.wgt", tizenId) == InstallerWrapper::Success); + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0, 10)).append(".xml"))); + ManifestFile mf(manifestPath); + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/@appid") == "djel94jdl9.appwidget.default"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/@primary") == "true"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:label") == "My DynamicBox"); + std::string boxSrcPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath()) + WrtDB::GlobalConfig::GetWidgetSrcPath() + + "/app-widget/index.html"; + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:script/@src") == boxSrcPath); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:livebox/p:box/p:size") == "1x1"); + + RUNNER_ASSERT(uninstall(tizenId)); +} + +/* +Name: InstallWidgetWithAppWidgetIncorrect +Description: Tests widget installation when app-widget Id and application Id are different +Expected: widget should not be installed. +*/ +RUNNER_TEST(InstallWidgetWithAppWidgetIncorrect) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/appWidgetIncorrect.wgt", tizenId) != InstallerWrapper::Success); +} + + +/* +Name: AppWidgetFull +Description: Tests parsing app-widget element with all children +Expected: Elements should be parsed correctly. +*/ +RUNNER_TEST(AppWidgetFull) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetFull.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"tizenScmgz.Sample.default" == (**widgetConfig.m_livebox.begin()).m_liveboxId); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_primary); + RUNNER_ASSERT(L"1800.0" == (**widgetConfig.m_livebox.begin()).m_updatePeriod); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_autoLaunch); + RUNNER_ASSERT(L"My DynamicBox" == (**widgetConfig.m_livebox.begin()).m_label.begin()->second); + RUNNER_ASSERT(L"box-icon.png" == (**widgetConfig.m_livebox.begin()).m_icon); + RUNNER_ASSERT(L"app-widget/index.html" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSrc); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxMouseEvent); + RUNNER_ASSERT(L"false" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxTouchEffect); + RUNNER_ASSERT(1 == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.size()); + RUNNER_ASSERT(L"app-widget/preview-lb1-11.png" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_preview); + RUNNER_ASSERT(L"false" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_useDecoration); + RUNNER_ASSERT(L"1x1" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_size); + RUNNER_ASSERT(L"pd/index.html" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdSrc); + RUNNER_ASSERT(L"720" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdWidth); + RUNNER_ASSERT(L"150" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdHeight); +} + + +/* +Name: AppWidgetMinimal +Description: Tests parsing app-widget element with mandatory children +Expected: Elements should be parsed correctly. +*/ +RUNNER_TEST(AppWidgetMinimal) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetMinimal.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"tizenScmgz.Sample.default" == (**widgetConfig.m_livebox.begin()).m_liveboxId); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_primary); + RUNNER_ASSERT(L"My DynamicBox" == (**widgetConfig.m_livebox.begin()).m_label.begin()->second); + RUNNER_ASSERT(L"app-widget/index.html" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSrc); + RUNNER_ASSERT(L"1x1" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_size); +} + +/* +Name: AppWidgetIdTooShort +Description: Tests parsing app-widget element with too short id +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetIdTooShort) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetIdTooShort.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetIdTooLong +Description: Tests parsing app-widget element with too long id +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetIdTooLong) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetIdTooLong.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetIdWrongChar +Description: Tests parsing app-widget element with ill-formed id +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetIdWrongChar) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetIdWrongChar.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetIdEmpty +Description: Tests parsing app-widget element with empty id +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetIdEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetIdEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetNoId +Description: Tests parsing app-widget element without id +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetNoId) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetNoId.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetPrimaryWrongValue +Description: Tests parsing app-widget element with wrong primary argument +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetPrimaryWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetPrimaryWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetPrimaryEmpty +Description: Tests parsing app-widget element with empty primary argument +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetPrimaryEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetPrimaryEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetNoPrimary +Description: Tests parsing app-widget element without primary argument +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetNoPrimary) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetNoPrimary.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetMultiplePrimary +Description: Tests parsing configuration with multiple app-widget element. +Expected: Parsing should be successful. +*/ +RUNNER_TEST(AppWidgetMultiplePrimary) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetMultiplePrimary.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(3 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(2 == std::count_if(widgetConfig.m_livebox.begin(), widgetConfig.m_livebox.end(), + [](const WrtDB::ConfigParserData::OptionalLiveboxInfo& liveBox){ + return liveBox->m_primary == L"true"; + }) + ); + RUNNER_ASSERT(1 == std::count_if(widgetConfig.m_livebox.begin(), widgetConfig.m_livebox.end(), + [](const WrtDB::ConfigParserData::OptionalLiveboxInfo& liveBox){ + return liveBox->m_primary == L"false"; + }) + ); +} + +/* +Name: AppWidgetUpdatePeriodLow +Description: Tests parsing app-widget element with update-period argument too low +Expected: Parsing should be successful. updatePeriod should have low boundary value. +*/ +RUNNER_TEST(AppWidgetUpdatePeriodLow) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetUpdatePeriodLow.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"1800.0" == (**widgetConfig.m_livebox.begin()).m_updatePeriod); +} + +/* +Name: AppWidgetUpdatePeriodWrongFormat +Description: Tests parsing app-widget element with wrong update-period argument. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetUpdatePeriodWrongFormat) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetUpdatePeriodWrongFormat.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetUpdatePeriodEmpty +Description: Tests parsing app-widget element with empty update-period argument. +Expected: Parsing should be successful. updatePeriod should have empty value. +*/ +RUNNER_TEST(AppWidgetUpdatePeriodEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetUpdatePeriodEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT((**widgetConfig.m_livebox.begin()).m_updatePeriod.empty()); +} + +/* +Name: RUNNER_TEST(AppWidgetAutoLaunchWrongValue) +Description: Tests parsing app-widget element with wrong auto-launch argument. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetAutoLaunchWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetAutoLaunchWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetAutoLaunchEmpty +Description: Tests parsing app-widget element with empty auto-launch argument. +Expected: Parsing should be successful. auto-launch should have empty value. +*/ +RUNNER_TEST(AppWidgetAutoLaunchEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetAutoLaunchEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"false" == (**widgetConfig.m_livebox.begin()).m_autoLaunch); +} + +/* +Name: BoxLabelEmpty +Description: Tests parsing empty box-label element. +Expected: Parsing should be successful. label should have empty value. +*/ +RUNNER_TEST(BoxLabelEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxLabelEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(1 == (**widgetConfig.m_livebox.begin()).m_label.size()); + RUNNER_ASSERT((**widgetConfig.m_livebox.begin()).m_label.begin()->first.empty()); + RUNNER_ASSERT((**widgetConfig.m_livebox.begin()).m_label.begin()->second.empty()); +} + +/* +Name: AppWidgetNoBoxLabel +Description: Tests parsing app-widget element without box-label element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetNoBoxLabel) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetNoBoxLabel.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetMultipleBoxLabel +Description: Tests parsing app-widget element with multiple box-label element. +Expected: Parsing should be successful and elements stored correctly. +*/ +RUNNER_TEST(AppWidgetMultipleBoxLabel) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/AppWidgetMultipleBoxLabel.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(3 == (**widgetConfig.m_livebox.begin()).m_label.size()); + + RUNNER_ASSERT( + 1 == std::count((**widgetConfig.m_livebox.begin()).m_label.begin(), (**widgetConfig.m_livebox.begin()).m_label.end(), + std::pair(L"en",L"test_en") )); + RUNNER_ASSERT( + 1 == std::count((**widgetConfig.m_livebox.begin()).m_label.begin(), (**widgetConfig.m_livebox.begin()).m_label.end(), + std::pair(L"pl",L"test_pl") )); + RUNNER_ASSERT( + 1 == std::count((**widgetConfig.m_livebox.begin()).m_label.begin(), (**widgetConfig.m_livebox.begin()).m_label.end(), + std::pair(L"",L"test") )); + +} + +/* +Name: BoxIconEmpty +Description: Tests parsing empty box-icon element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxIconEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxIconEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxIconSrcEmpty +Description: Tests parsing box-icon element with empty src attribute. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxIconSrcEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxIconSrcEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetMultipleBoxIcon +Description: Tests parsing app-widget with multiple box-icon elements. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetMultipleBoxIcon) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetMultipleBoxIcon.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: AppWidgetNoBoxContent +Description: Tests parsing app-widget without box-content element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetNoBoxContent) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetNoBoxContent.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + + +/* +Name: AppWidgetMultipleBoxContent +Description: Tests parsing app-widget with multiple box-content element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(AppWidgetMultipleBoxContent) +{ + + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/AppWidgetMultipleBoxContent.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + + +/* +Name: BoxContentEmpty +Description: Tests parsing empty box-content element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxContentEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxContentNoSrc +Description: Tests parsing box-content element without src attribute. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxContentNoSrc) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentNoSrc.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxContentSrcEmpty +Description: Tests parsing box-content element with empty src attribute. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxContentSrcEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentSrcEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxContentNoMouseEvent +Description: Tests parsing box-content element without mouse-event attribute. +Expected: Parsing should be successful. boxMouseEvent should have default value. +*/ +RUNNER_TEST(BoxContentNoMouseEvent) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxContentNoMouseEvent.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"false" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxMouseEvent); +} + +/* +Name: BoxContentMouseEventEmpty +Description: Tests parsing box-content element with empty mouse-event attribute. +Expected: Parsing should be successful. boxMouseEvent should have default value. +*/ +RUNNER_TEST(BoxContentMouseEventEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxContentMouseEventEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"false" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxMouseEvent); +} + +/* +Name: BoxContentMouseEventWrongValue +Description: Tests parsing box-content element with wrong mouse-event attribute. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxContentMouseEventWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentMouseEventWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxContentNoTouchEfect +Description: Tests parsing box-content element without touch-effect attribute. +Expected: Parsing should be successful. boxTouchEffect should have default value. +*/ +RUNNER_TEST(BoxContentNoTouchEfect) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxContentNoTouchEfect.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxTouchEffect); +} + +/* +Name: BoxContentTouchEfectEmpty +Description: Tests parsing box-content element with empty touch-effect attribute. +Expected: Parsing should be successful. boxTouchEffect should have default value. +*/ +RUNNER_TEST(BoxContentTouchEfectEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxContentTouchEfectEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxTouchEffect); +} + +/* +Name: BoxContentTouchEfectWrongValue +Description: Tests parsing box-content element with wrong touch-effect attribute. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxContentTouchEfectWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentTouchEfectWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxContentMultipleBoxSize +Description: Tests parsing box-content element with multiple box-size elements. +Expected: Parsing should be successful. +*/ +RUNNER_TEST(BoxContentMultipleBoxSize) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxContentMultipleBoxSize.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(3 == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.size()); + + RUNNER_ASSERT(1 == std::count_if((**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin(), + (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.end(), + [](const WrtDB::ConfigParserData::LiveboxInfo::BoxSizeInfo& boxSize){ + return boxSize.m_size == L"1x1"; + }) + ); + RUNNER_ASSERT(1 == std::count_if((**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin(), + (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.end(), + [](const WrtDB::ConfigParserData::LiveboxInfo::BoxSizeInfo& boxSize){ + return boxSize.m_size == L"2x1"; + }) + ); + RUNNER_ASSERT(1 == std::count_if((**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin(), + (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.end(), + [](const WrtDB::ConfigParserData::LiveboxInfo::BoxSizeInfo& boxSize){ + return boxSize.m_size == L"2x2"; + }) + ); +} + +/* +Name: BoxSizeEmpty +Description: Tests parsing empty box-size element. +Expected: Exception should be thrown. +*/ +RUNNER_TEST(BoxSizeEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxSizeEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: BoxSizePreviewEmpty +Description: Tests parsing box-size element with empty preview attribute. +Expected: Parsing should be successful. Preview value should be empty +*/ +RUNNER_TEST(BoxSizePreviewEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxSizePreviewEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(1 == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.size()); + RUNNER_ASSERT((**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_preview.empty()); +} + +/* +Name: BoxSizeNoUserDecoration +Description: Tests parsing box-size element without use-decoration attribute. +Expected: Parsing should be successful. useDecoration should be set to default value +*/ +RUNNER_TEST(BoxSizeNoUserDecoration) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxSizeNoUserDecoration.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(1 == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.size()); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_useDecoration); +} + +/* +Name: BoxSizeUserDecorationEmpty +Description: Tests parsing box-size element with empty use-decoration attribute. +Expected: Parsing should be successful. useDecoration should be set to default value +*/ +RUNNER_TEST(BoxSizeUserDecorationEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/BoxSizeUserDecorationEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(1 == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.size()); + RUNNER_ASSERT(L"true" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_boxSize.begin()->m_useDecoration); +} + +/* +Name: BoxContentMultiplePd +Description: Tests parsing box-content element with multiple pd element. +Expected: Exception should be thrown +*/ +RUNNER_TEST(BoxContentMultiplePd) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/BoxContentMultiplePd.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdNoSrc +Description: Tests parsing pd element without src attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdNoSrc) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdNoSrc.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdSrcEmpty +Description: Tests parsing pd element with empty src attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdSrcEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdSrcEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdNoWidth +Description: Tests parsing pd element without width attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdNoWidth) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdNoWidth.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdWidthEmpty +Description: Tests parsing pd element with empty width attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdWidthEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdWidthEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdWidthZero +Description: Tests parsing pd element with width zero value. +Expected: Parsing should be successful. +*/ +RUNNER_TEST(PdWidthZero) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/PdWidthZero.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"0" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdWidth); +} + +/* +Name: PdWidthNegative +Description: Tests parsing pd element with width negative value. +Expected: Parsing should be successful. +*/ +RUNNER_TEST(PdWidthNegative) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/PdWidthNegative.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"-1" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdWidth); +} + +/* +Name: PdWidthWrongValue +Description: Tests parsing pd element with width wrong value. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdWidthWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdWidthWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdNoHeight +Description: Tests parsing pd element without height attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdNoHeight) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdNoHeight.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdHeightEmpty +Description: Tests parsing pd element with empty height attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdHeightEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdHeightEmpty.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: PdHeightTooLow +Description: Tests parsing pd element with height attribute below range. +Expected: Parsing should be successful. Height should have low boundary value. +*/ +RUNNER_TEST(PdHeightTooLow) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/PdHeightTooLow.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"1" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdHeight); +} + +/* +Name: PdHeightExcessive +Description: Tests parsing pd element with height attribute with value above range. +Expected: Parsing should be successful. Height should have high boundary value. +*/ +RUNNER_TEST(PdHeightExcessive) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/PdHeightExcessive.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(1 == widgetConfig.m_livebox.size()); + RUNNER_ASSERT(L"380" == (**widgetConfig.m_livebox.begin()).m_boxInfo.m_pdHeight); +} + +/* +Name: PdHeightWrongValue +Description: Tests parsing pd element with wrong height attribute. +Expected: Exception should be thrown +*/ +RUNNER_TEST(PdHeightWrongValue) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/PdHeightWrongValue.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + ); +} diff --git a/tests/general/ParsingCategoryTests.cpp b/tests/general/ParsingCategoryTests.cpp new file mode 100644 index 0000000..a376807 --- /dev/null +++ b/tests/general/ParsingCategoryTests.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingCategoryTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief Category element installation tests + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingCategory) + +/* +Name: InstallWidgetWithCategory +Description: Tests if widget with category is installed correctly +Expected: widget should be installed correctly and category should be present in manifest file +*/ +RUNNER_TEST(InstallWidgetWithCategory) +{ + std::string tizenId; + std::string manifestPath = "/opt/share/packages/"; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/category.wgt", tizenId) == InstallerWrapper::Success); + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0, 10)).append(".xml"))); + ManifestFile mf(manifestPath); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:category[1]/@name") == "testCategory"); + uninstall(tizenId); +} + + +/* +Name: CategoryElementOk +Description: Tests parsing correct category element +Expected: Element should be parsed correcty. +*/ +RUNNER_TEST(CategoryElementOk) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_category1.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(2 == widgetConfig.categoryList.size()); + RUNNER_ASSERT(std::count(widgetConfig.categoryList.begin(), widgetConfig.categoryList.end(), L"testCategory1") == 1); + RUNNER_ASSERT(std::count(widgetConfig.categoryList.begin(), widgetConfig.categoryList.end(), L"testCategory2") == 1); +} + +/* +Name: CategoryElementEmptyName +Description: Tests parsing splash element with empty name attribute +Expected: No exception and categoryList should be empty +*/ +RUNNER_TEST(CategoryElementEmptyName) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_category2.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.categoryList.empty()); +} + +/* +Name: CategoryElementNoName +Description: Tests parsing category element with no name attribute +Expected: No exception and categoryList should be empty +*/ +RUNNER_TEST(CategoryElementNoName) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_category3.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.categoryList.empty()); +} + +/* +Name: CategoryElementDuplicated +Description: Tests parsing three category elements (two are identical) +Expected: No exception and categoryList should have two distinct elements +*/ +RUNNER_TEST(CategoryElementDuplicated) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_category4.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(2 == widgetConfig.categoryList.size()); + RUNNER_ASSERT(std::count(widgetConfig.categoryList.begin(), widgetConfig.categoryList.end(), L"testCategory1") == 1); + RUNNER_ASSERT(std::count(widgetConfig.categoryList.begin(), widgetConfig.categoryList.end(), L"testCategory2") == 1); + +} diff --git a/tests/general/ParsingContentTests.cpp b/tests/general/ParsingContentTests.cpp new file mode 100644 index 0000000..56b114e --- /dev/null +++ b/tests/general/ParsingContentTests.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingContentTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief content element installation tests + */ + +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +namespace{ + +template +bool checkException(Function fun) +{ + Try + { + fun(); + } + Catch(Exception){ + return true; + } + return false; +} + +} // namespace + +#define RUNNER_ASSERT_EXCEPTION(exceptionType, function) \ + { \ + RUNNER_ASSERT(checkException([&](){function})); \ + } + + + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingContent) + +/* +Name: InstallWidgetWithContentCorrect +Description: Tests if widget with correct content element is installed correctly +Expected: widget should be installed correctly and startFile info should be stored in database +*/ +RUNNER_TEST(InstallWidgetWithContentCorrect) +{ + std::string tizenId; + std::string manifestPath = "/opt/share/packages/"; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/contentCorrect.wgt", tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + WrtDB::WidgetDAOReadOnly::WidgetStartFileList startFileList = dao.getStartFileList(); + + RUNNER_ASSERT(6 == startFileList.size()); + RUNNER_ASSERT( + 1 == std::count_if(startFileList.begin(), startFileList.end(), + [](const WrtDB::WidgetDAOReadOnly::WidgetStartFileRow& startFileRow){ + return L"http://test.org" == startFileRow.src; + }) + ); + + + uninstall(tizenId); +} + +/* +Name: InstallWidgetWithContentIncorrect +Description: Tests if widget with incorrect content element is not installed correctly +Expected: widget should not be installed +*/ +RUNNER_TEST(InstallWidgetWithContentIncorrect) +{ + std::string tizenId; + std::string manifestPath = "/opt/share/packages/"; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/contentIncorrect.wgt", tizenId) == InstallerWrapper::OtherError); +} + +/* +Name: NoContent +Description: Tests parsing configuration file without content element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(NoContent) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/NoContent.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(widgetConfig.metadataList.empty()); +} + +/* +Name: ContentEmpty +Description: Tests parsing configuration file with empty content element +Expected: Exception should be thrown +*/ +RUNNER_TEST(ContentEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/ContentEmpty.xml", + ElementParserPtr( new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: ContentSrcEmpty +Description: Tests parsing configuration file with empty src attribute in content element +Expected: Exception should be thrown +*/ +RUNNER_TEST(ContentSrcEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/ContentSrcEmpty.xml", + ElementParserPtr( new RootParser(widgetConfig, L"widget"))); + ); +} + +/* +Name: ContentSrcCorrect +Description: Tests parsing configuration file with correct content element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(ContentSrcCorrect) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/ContentSrcCorrect.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(DPL::OptionalString(L"http://test.org") == widgetConfig.startFile); +} + +/* +Name: MultipleContentCorrect +Description: Tests parsing configuration file with multiple content element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(MultipleContentCorrect) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/MultipleContentCorrect.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(DPL::OptionalString(L"http://test.org") == widgetConfig.startFile); +} + +/* +Name: MultipleContentCorrect +Description: Tests parsing configuration file with multiple content element. + First occurrence is incorrect +Expected: Exception should be thrown +*/ +RUNNER_TEST(MultipleContentIncorrect) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, + parser.Parse(miscWidgetsStuff + "configs/MultipleContentIncorrect.xml", + ElementParserPtr( new RootParser(widgetConfig, L"widget"))); + ); +} diff --git a/tests/general/ParsingCspTests.cpp b/tests/general/ParsingCspTests.cpp new file mode 100644 index 0000000..0dcbf3e --- /dev/null +++ b/tests/general/ParsingCspTests.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingCspTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief content-security-policy and content-security-policy-report-only element installation + * tests + */ + +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingCsp) + +/* +Name: InstallWidgetWithCsp +Description: Tests if widget with content-security-policy and content-security-policy-report-only is installed correctly +Expected: widget should be installed correctly and CSP values should be correctly stored in database +*/ +RUNNER_TEST(InstallWidgetWithCsp) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/csp.wgt", tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + + RUNNER_ASSERT(L"testCSP" == *dao.getCspPolicy()); + RUNNER_ASSERT(L"testCSPro" == *dao.getCspPolicyReportOnly()); + + uninstall(tizenId); +} + + +/* +Name: NoCsp +Description: Tests parsing configuration file without content-security-policy and content-security-policy-report-only element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(NoCsp) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/NoCsp.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(!widgetConfig.cspPolicy); + RUNNER_ASSERT(!widgetConfig.cspPolicyReportOnly); +} + +/* +Name: CspEmpty +Description: Tests parsing configuration file with empty content-security-policy element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(CspEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/CspEmpty.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(!widgetConfig.cspPolicy); + RUNNER_ASSERT(!widgetConfig.cspPolicyReportOnly); +} + +/* +Name: CspReportOnlyEmpty +Description: Tests parsing configuration file with empty content-security-policy-report-only element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(CspReportOnlyEmpty) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/CspReportOnlyEmpty.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(!widgetConfig.cspPolicy); + RUNNER_ASSERT(!widgetConfig.cspPolicyReportOnly); +} + +/* +Name: MultipleCsp +Description: Tests parsing configuration file with multiple content-security-policy elements +Expected: Element should be parsed correctly. Only values from first element should be stored +*/ +RUNNER_TEST(MultipleCsp) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/MultipleCsp.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(L"testCSP" == *widgetConfig.cspPolicy); +} + +/* +Name: MultipleCsp +Description: Tests parsing configuration file with multiple content-security-policy-report-only elements +Expected: Element should be parsed correctly. Only values from first element should be stored +*/ +RUNNER_TEST(MultipleCspReportOnly) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/MultipleCspReportOnly.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(L"testCSP" == *widgetConfig.cspPolicyReportOnly); +} diff --git a/tests/general/ParsingMetadataTests.cpp b/tests/general/ParsingMetadataTests.cpp new file mode 100644 index 0000000..a3a0818 --- /dev/null +++ b/tests/general/ParsingMetadataTests.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingMetadataTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief metadata element installation tests + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +namespace{ + +template +bool checkException(Function fun) +{ + Try + { + fun(); + } + Catch(Exception){ + return true; + } + return false; +} + +} // namespace + +#define RUNNER_ASSERT_EXCEPTION(exceptionType, function) \ + { \ + RUNNER_ASSERT(checkException([&](){function})); \ + } + + + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingMetadata) + +/* +Name: InstallWidgetWithMetadata +Description: Tests if widget with metadata element is installed correctly +Expected: widget should be installed correctly and metadata info should be stored in manifest file +*/ +RUNNER_TEST(InstallWidgetWithMetadata) +{ + std::string tizenId; + std::string manifestPath = "/opt/share/packages/"; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/metadata.wgt", tizenId) == InstallerWrapper::Success); + + RUNNER_ASSERT(WrtUtilFileExists(manifestPath.append(tizenId.substr(0, 10)).append(".xml"))); + ManifestFile mf(manifestPath); + + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:metadata[1]/@key") == "key1"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:metadata[2]/@key") == "key2"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:metadata[2]/@value") == "value2"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:metadata[3]/@key") == "key3"); + RUNNER_ASSERT(mf.getValueByXpath("/p:manifest/p:ui-application/p:metadata[3]/@value") == "value3"); + uninstall(tizenId); +} + + +/* +Name: NoMetadata +Description: Tests parsing configuration file without metadata element +Expected: Element should be parsed correctly. +*/ +RUNNER_TEST(NoMetadata) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/NoMetadata.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(widgetConfig.metadataList.empty()); +} + +/* +Name: MetadataEmpty +Description: Tests parsing configuration file with empty metadata element +Expected: Exception should be thrown +*/ +//TODO: Fix in parser needed +//RUNNER_TEST(MetadataEmpty) +//{ +// ParserRunner parser; +// WrtDB::ConfigParserData widgetConfig; +// +// RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, +// parser.Parse(miscWidgetsStuff + "configs/MetadataEmpty.xml", +// ElementParserPtr( new RootParser(widgetConfig, L"widget"))); +// ); +//} + +/* +Name: MultipleMetadata +Description: Tests parsing configuration file with multiple metadata element +Expected: Element should be parsed correctly. All values should be stored +*/ +RUNNER_TEST(MultipleMetadata) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/MultipleMetadata.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(3 == widgetConfig.metadataList.size()); + + RUNNER_ASSERT(1 == std::count(widgetConfig.metadataList.begin(), widgetConfig.metadataList.end(), + WrtDB::ConfigParserData::Metadata(DPL::OptionalString(L"key1"), DPL::OptionalString()))); + RUNNER_ASSERT(1 == std::count(widgetConfig.metadataList.begin(), widgetConfig.metadataList.end(), + WrtDB::ConfigParserData::Metadata(DPL::OptionalString(L"key2"), DPL::OptionalString(L"value2")))); + RUNNER_ASSERT(1 == std::count(widgetConfig.metadataList.begin(), widgetConfig.metadataList.end(), + WrtDB::ConfigParserData::Metadata(DPL::OptionalString(L"key3"), DPL::OptionalString(L"value3")))); + +} + +/* +Name: MetadataDuplicatedKey +Description: Tests parsing configuration file with duplicated key attribute value in metadata element +Expected: Exception should be thrown +*/ +//TODO: Fix in parser needed +//RUNNER_TEST(MetadataDuplicatedKey) +//{ +// ParserRunner parser; +// WrtDB::ConfigParserData widgetConfig; +// +// RUNNER_ASSERT_EXCEPTION(ElementParser::Exception::ParseError, +// parser.Parse(miscWidgetsStuff + "configs/MetadataDuplicatedKey.xml", +// ElementParserPtr( new RootParser(widgetConfig, L"widget"))); +// ); +//} diff --git a/tests/general/ParsingSplashImgTests.cpp b/tests/general/ParsingSplashImgTests.cpp new file mode 100644 index 0000000..0e550f7 --- /dev/null +++ b/tests/general/ParsingSplashImgTests.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingSplashImgTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief Splash element installation tests + */ + +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingSplashImg) + +/* +Name: InstallWidgetWithSplash +Description: Tests if widget with splash is installed correctly +Expected: widget should be installed correctly and splashImg registered in database +*/ +RUNNER_TEST(InstallWidgetWithSplash) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/splash.wgt", tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + + RUNNER_ASSERT(*dao.getWidgetInstalledPath() + L"/res/wgt/splash.html" == *dao.getSplashImgSrc()); + + uninstall(tizenId); +} + + +/* +Name: SplashElementOk +Description: Tests parsing correct splash element +Expected: Element should be parsed correcty. +*/ +RUNNER_TEST(SplashElementOk) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_splash1.xml", + ElementParserPtr( + new RootParser(widgetConfig, + L"widget"))); + + RUNNER_ASSERT(DPL::OptionalString(L"splash.html") == widgetConfig.splashImgSrc); +} + +/* +Name: SplashElementEmptySrc +Description: Tests parsing splash element with empty src attribute +Expected: No exception and splash should be null +*/ +RUNNER_TEST(SplashElementEmptySrc) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_splash2.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(!widgetConfig.splashImgSrc); +} + +/* +Name: SplashElementNoSrc +Description: Tests parsing splash element with no src attribute +Expected: No exception and splash should be null +*/ +RUNNER_TEST(SplashElementNoSrc) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_splash3.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(!widgetConfig.splashImgSrc); + +} + +/* +Name: SplashElementNoNamespace +Description: Tests parsing splash element without tizen namespace +Expected: No exception and splash should be null +*/ +RUNNER_TEST(SplashElementNoNamespace) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_splash4.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(!widgetConfig.splashImgSrc); + +} diff --git a/tests/general/ParsingTizenAppcontrolTests.cpp b/tests/general/ParsingTizenAppcontrolTests.cpp new file mode 100644 index 0000000..299354f --- /dev/null +++ b/tests/general/ParsingTizenAppcontrolTests.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestCases.cpp + * @author Karol Pawlowski (k.pawlowski@samsung.com) + * @author Andrzej Surdej (a.surdej@samsung.com) + * @version 1.0 + * @brief Parsing Tizen app-control test's bodies + */ + +#include +#include +#include +#include +#include +#include + +using namespace InstallerWrapper; + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingTizenAppcontrol) + +/* +Name: tizen_app-contro +Description: Tests if widget app-control tag is correctly parsed +Expected: widget should be installed +*/ +RUNNER_TEST(tizen_app_control) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/app-control.wgt", + tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + WrtDB::WidgetAppControlList appcontrolList; + dao.getAppControlList(appcontrolList); + uninstall(tizenId); + + _D("Actual size %d", appcontrolList.size()); + RUNNER_ASSERT_MSG(appcontrolList.size() == 4, "Incorrect list size"); + WrtDB::WidgetAppControl s; + s.src = DPL::FromUTF8String("edit1.html"); + s.operation = DPL::FromUTF8String("http://tizen.org/appcontrol/operation/edit"); + s.mime = DPL::FromUTF8String("image/jpg"); /* mime type */ + s.disposition = WrtDB::WidgetAppControl::Disposition::WINDOW; + + RUNNER_ASSERT_MSG( + std::find(appcontrolList.begin(), appcontrolList.end(), s) != appcontrolList.end(), + "Unable to find service #"); + + s.src = DPL::FromUTF8String("edit2.html"); + s.operation = DPL::FromUTF8String("http://tizen.org/appcontrol/operation/view"); + s.mime = DPL::FromUTF8String("audio/ogg"); /* mime type */ + s.disposition = WrtDB::WidgetAppControl::Disposition::WINDOW; + + RUNNER_ASSERT_MSG( + std::find(appcontrolList.begin(), appcontrolList.end(), s) != appcontrolList.end(), + "Unable to find service ##"); + + s.src = DPL::FromUTF8String("edit3.html"); + s.operation = DPL::FromUTF8String("http://tizen.org/appcontrol/operation/call"); + s.mime = DPL::FromUTF8String("image/png"); /* mime type */ + s.disposition = WrtDB::WidgetAppControl::Disposition::WINDOW; + + RUNNER_ASSERT_MSG( + std::find(appcontrolList.begin(), appcontrolList.end(), s) != appcontrolList.end(), + "Unable to find service ###"); + + s.src = DPL::FromUTF8String("edit4.html"); + s.operation = DPL::FromUTF8String("http://tizen.org/appcontrol/operation/send"); + s.mime = DPL::FromUTF8String("text/css"); /* mime type */ + s.disposition = WrtDB::WidgetAppControl::Disposition::WINDOW; + + RUNNER_ASSERT_MSG( + std::find(appcontrolList.begin(), appcontrolList.end(), s) != appcontrolList.end(), + "Unable to find service ####"); +} diff --git a/tests/general/ParsingTizenPrivilegeTests.cpp b/tests/general/ParsingTizenPrivilegeTests.cpp new file mode 100644 index 0000000..1cf2032 --- /dev/null +++ b/tests/general/ParsingTizenPrivilegeTests.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file ParsingTizenPrivilegeTests.cpp + * @author Slawomir Pajak (s.pajak@partner.samsung.com) + * @version 1.0 + * @brief Parsing Tizen privilege bodies + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace InstallerWrapper; + +namespace { + +class CompareFeatureByName { +public: + CompareFeatureByName(DPL::String name) : + m_name(name) + { + + } + bool operator()(const WrtDB::DbWidgetFeature& feature) + { + return feature.name == m_name; + } +private: + DPL::String m_name; +}; + +} +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(ParsingTizenPrivilege) + +/* +Name: tizen_privilege +Description: Tests if widget privilege tag is correctly parsed +Expected: widget should be installed. Privileges and features registered in database +*/ +RUNNER_TEST(InstallWidgetWithPrivilege) +{ + std::string tizenId; + RUNNER_ASSERT(install(miscWidgetsStuff + "widgets/privilege.wgt", tizenId) == InstallerWrapper::Success); + + WrtDB::WidgetDAOReadOnly dao(DPL::FromASCIIString(tizenId)); + WrtDB::PrivilegeList privilegeList = dao.getWidgetPrivilege(); + WrtDB::DbWidgetFeatureSet featureList = dao.getFeaturesList(); + uninstall(tizenId); + + RUNNER_ASSERT(privilegeList.size() == 5); + RUNNER_ASSERT(std::count(privilegeList.begin(), privilegeList.end(), L"http://tizen.org/privilege/location") == 1); + RUNNER_ASSERT( + std::count(privilegeList.begin(), privilegeList.end(), L"http://tizen.org/privilege/notification") == 1); + RUNNER_ASSERT( + std::count(privilegeList.begin(), privilegeList.end(), L"http://tizen.org/privilege/mediacapture") == 1); + RUNNER_ASSERT( + std::count(privilegeList.begin(), privilegeList.end(), L"http://tizen.org/privilege/fullscreen") == 1); + RUNNER_ASSERT( + std::count(privilegeList.begin(), privilegeList.end(), L"http://tizen.org/privilege/unlimitedstorage") == 1); + + RUNNER_ASSERT(featureList.size() == 5); + RUNNER_ASSERT( + std::count_if(featureList.begin(), featureList.end(), CompareFeatureByName(L"http://tizen.org/privilege/location")) == 1); + RUNNER_ASSERT( + std::count_if(featureList.begin(), featureList.end(), CompareFeatureByName(L"http://tizen.org/privilege/notification")) == 1); + RUNNER_ASSERT( + std::count_if(featureList.begin(), featureList.end(), CompareFeatureByName(L"http://tizen.org/privilege/mediacapture")) == 1); + RUNNER_ASSERT( + std::count_if(featureList.begin(), featureList.end(), CompareFeatureByName(L"http://tizen.org/privilege/fullscreen")) == 1); + RUNNER_ASSERT( + std::count_if(featureList.begin(), featureList.end(), CompareFeatureByName(L"http://tizen.org/privilege/unlimitedstorage")) == 1); +} + +/* +Name: PrivilegeElementOk +Description: Tests parsing correct privilege element +Expected: Element should be parsed correcty. +*/ +RUNNER_TEST(PrivilegeElementOk) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege1.xml", + ElementParserPtr(new RootParser(widgetConfig, L"widget"))); + + RUNNER_ASSERT(5 == widgetConfig.privilegeList.size()); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/location")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/notification")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/mediacapture")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/fullscreen")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/unlimitedstorage")) == 1); + + RUNNER_ASSERT(5 == widgetConfig.featuresList.size()); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/location")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/notification")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/mediacapture")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/fullscreen")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/unlimitedstorage")) == 1); +} + +/* +Name: CategoryElementEmptyName +Description: Tests parsing privilege element with empty name attribute +Expected: No exception. PrivilegeList and featuresList should be empty +*/ +RUNNER_TEST(PrivilegeElementEmptyName) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege2.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.privilegeList.empty()); + RUNNER_ASSERT(widgetConfig.featuresList.empty()); +} + +/* +Name: PrivilegeElementNoName +Description: Tests parsing privilege element with no name attribute +Expected: No exception. PrivilegeList and featuresList should be empty +*/ +RUNNER_TEST(PrivilegeElementNoName) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege3.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.categoryList.empty()); + RUNNER_ASSERT(widgetConfig.featuresList.empty()); +} + +/* +Name: PrivilegeElementNoNameSpace +Description: Tests parsing privilege element without proper namespace +Expected: No exception. PrivilegeList and featuresList should be empty +*/ +RUNNER_TEST(PrivilegeElementNoNameSpace) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege4.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.categoryList.empty()); + RUNNER_ASSERT(widgetConfig.featuresList.empty()); +} + +/* +Name: PrivilegeElementDuplicated +Description: Tests parsing three privilege elements (two are identical) +Expected: No exception. PrivilegeList and featuresList should have two distinct elements +*/ +RUNNER_TEST(PrivilegeElementDuplicated) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege5.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(2 == widgetConfig.privilegeList.size()); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/location")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.privilegeList.begin(), widgetConfig.privilegeList.end(), + WrtDB::ConfigParserData::Privilege(L"http://tizen.org/privilege/notification")) == 1); + + RUNNER_ASSERT(2 == widgetConfig.featuresList.size()); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/location")) == 1); + RUNNER_ASSERT( + std::count(widgetConfig.featuresList.begin(), widgetConfig.featuresList.end(), + WrtDB::ConfigParserData::Feature(L"http://tizen.org/privilege/notification")) == 1); + +} + +/* +Name: PrivilegeElementWrongFormat +Description: Tests parsing privilege elements with wrong format +Expected: No exception. PrivilegeList and featuresList should be empty +*/ +RUNNER_TEST(PrivilegeElementWrongFormat) +{ + ParserRunner parser; + WrtDB::ConfigParserData widgetConfig; + + parser.Parse(miscWidgetsStuff + "configs/config_privilege6.xml", + ElementParserPtr( + new RootParser(widgetConfig, + DPL:: + FromUTF32String( + L"widget")))); + + RUNNER_ASSERT(widgetConfig.privilegeList.empty()); + RUNNER_ASSERT(widgetConfig.featuresList.empty()); +} diff --git a/tests/general/PluginsInstallation.cpp b/tests/general/PluginsInstallation.cpp new file mode 100644 index 0000000..8a779ea --- /dev/null +++ b/tests/general/PluginsInstallation.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file PluginsInstallation.cpp + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @version 1.0 + * @brief PluginsInstallation tests implementation + */ + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +RUNNER_TEST_GROUP_INIT(PluginsInstallation) + +STATIC_BLOCK +{ + (void)system("wrt_reset_all.sh"); + (void)system("wrt-installer -p"); +} + +#define MAKE_PLUGIN_CHECK_TESTCASE(TESTCASE, LIBNAME) \ + RUNNER_TEST(PluginsInstallation_##TESTCASE) \ + { \ + Try { \ + WrtDB::PluginDAOReadOnly pdao(#LIBNAME); \ + RUNNER_ASSERT_MSG(pdao.getInstallationStatus() == WrtDB::PluginDAOReadOnly::INSTALLATION_COMPLETED, "Plugin is not installed correctly"); \ + } Catch(DPL::Exception) { \ + _E("%s", _rethrown_exception.DumpToString().c_str()); \ + RUNNER_ASSERT_MSG(false, "DPL::Exception"); \ + } \ + } \ + +MAKE_PLUGIN_CHECK_TESTCASE(contact, libwrt-plugins-tizen-contact.so) +MAKE_PLUGIN_CHECK_TESTCASE(systemsetting, libwrt-plugins-tizen-systemsetting.so) +MAKE_PLUGIN_CHECK_TESTCASE(systeminfo, libwrt-plugins-tizen-systeminfo.so) +MAKE_PLUGIN_CHECK_TESTCASE(nfc, libwrt-plugins-tizen-nfc.so) +MAKE_PLUGIN_CHECK_TESTCASE(content, libwrt-plugins-tizen-content.so) +MAKE_PLUGIN_CHECK_TESTCASE(alarm, libwrt-plugins-tizen-alarm.so) +MAKE_PLUGIN_CHECK_TESTCASE(power, libwrt-plugins-tizen-power.so) +MAKE_PLUGIN_CHECK_TESTCASE(secureelement, libwrt-plugins-tizen-secureelement.so) +MAKE_PLUGIN_CHECK_TESTCASE(timeutil, libwrt-plugins-tizen-timeutil.so) +MAKE_PLUGIN_CHECK_TESTCASE(calendar, libwrt-plugins-tizen-calendar.so) +MAKE_PLUGIN_CHECK_TESTCASE(datacontrol, libwrt-plugins-tizen-datacontrol.so) +MAKE_PLUGIN_CHECK_TESTCASE(bookmark, libwrt-plugins-tizen-bookmark.so) +MAKE_PLUGIN_CHECK_TESTCASE(messaging, libwrt-plugins-tizen-messaging.so) +MAKE_PLUGIN_CHECK_TESTCASE(messageport, libwrt-plugins-tizen-messageport.so) +MAKE_PLUGIN_CHECK_TESTCASE(datasync, libwrt-plugins-tizen-datasync.so) +MAKE_PLUGIN_CHECK_TESTCASE(networkbearerselection, libwrt-plugins-tizen-networkbearerselection.so) +MAKE_PLUGIN_CHECK_TESTCASE(package, libwrt-plugins-tizen-package.so) +MAKE_PLUGIN_CHECK_TESTCASE(filesystem, libwrt-plugins-tizen-filesystem.so) +MAKE_PLUGIN_CHECK_TESTCASE(download, libwrt-plugins-tizen-download.so) +MAKE_PLUGIN_CHECK_TESTCASE(application, libwrt-plugins-tizen-application.so) +MAKE_PLUGIN_CHECK_TESTCASE(notification, libwrt-plugins-tizen-notification.so) +MAKE_PLUGIN_CHECK_TESTCASE(push, libwrt-plugins-tizen-push.so) +MAKE_PLUGIN_CHECK_TESTCASE(tizen, libwrt-plugins-tizen-tizen.so) +MAKE_PLUGIN_CHECK_TESTCASE(callhistory, libwrt-plugins-tizen-callhistory.so) +MAKE_PLUGIN_CHECK_TESTCASE(bluetooth, libwrt-plugins-tizen-bluetooth.so) diff --git a/tests/general/TaskConfigurationTests.cpp b/tests/general/TaskConfigurationTests.cpp new file mode 100644 index 0000000..b129e10 --- /dev/null +++ b/tests/general/TaskConfigurationTests.cpp @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TaskConfigurationTests.cpp + * @author Dominik Duda (d.duda@samsung.com) + * @version 1.0 + * @brief Tests functions from + * wrt-installer/src/jobs/widget_install/task_configuration.cpp + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Jobs; +using namespace WidgetInstall; + +RUNNER_TEST_GROUP_INIT(TaskConfiguration) + +namespace{ +const std::string wgtTmpPath = "/tmp/J7ZwBudste"; +const std::string wgtPath = InstallerWrapper::miscWidgetsStuff + + "widgets/widgetUpdateVer100Signed.wgt"; +const std::string tarCMD = "tar -xf " + InstallerWrapper::miscWidgetsStuff + + "widgets/widgetInDir.tar -C /tmp"; + +std::string tizenId; + +void staticWrtInitPreloadStatusCallback(std::string tizenId, WrtErrStatus status, + void* userdata) +{ + return; +} +} + +/* +Name: task_configuration_test_01 +Description: Installs widget which is needed for further tests. +This installation will test standard use of the TaskConfiguration class. +Expected: The widget should be successfully installed. +*/ +RUNNER_TEST(task_configuration_test_01) +{ + //Install the widget to get a tizenID + InstallerWrapper::install(wgtPath, tizenId); + + //Uninstall the widget in order to be sure that a next installation + //will be first. + RUNNER_ASSERT_MSG(InstallerWrapper::uninstall(tizenId), + "Failed to uninstall a widget"); + + //That will be the first installation of the widget. + RUNNER_ASSERT_MSG( + InstallerWrapper::install(wgtPath, tizenId) == InstallerWrapper::Success, + "Failed to install a widget"); +} + +/* +Name: task_configuration_test_02 +Description: Tests recognizing an update installation. +Expected: All task configration steps should be completed without errors. +*/ +RUNNER_TEST(task_configuration_test_02) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = wgtPath; + i_context.job = new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt); +} + +/* +Name: task_configuration_test_03 +Description: Tests widget installation with incorrect config.xml file. +Expected: Task configuration process should throw an exception when parsing +configuration file. +*/ +RUNNER_TEST(task_configuration_test_03) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = InstallerWrapper::miscWidgetsStuff + "widgets/widgetFakeConfig.wgt"; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + Try{ + while(i < stepsCnt && result) + { + i++; + result = taskConf.NextStep(); + } + } + Catch(WidgetInstall::Exceptions::WidgetConfigFileInvalid){ + RUNNER_ASSERT(i == 4); + return; + } + + RUNNER_ASSERT_MSG(false, + "An Exception should be thrown if config.xml file is incorrect."); +} + +/* +Name: task_configuration_test_04 +Description: Tests task configuration for widget installation directly from +a directory. +Expected: Widget should be successfully installed. +*/ +RUNNER_TEST(task_configuration_test_04) +{ + int ret = system(tarCMD.c_str()); + + RUNNER_ASSERT_MSG(!WIFEXITED(ret) || !WIFSIGNALED(ret), + "Cannot untar a widget to check direct installation from the directory!"); + + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.mode.extension = InstallMode::ExtensionType::DIR; + i_context.requestedPath = wgtTmpPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt); +} + +/* +Name: task_configuration_test_05 +Description: Tests if an exception will ocure when there is no config.xml file +in the widget directory. +Expected: An exception should be thrown. +*/ +RUNNER_TEST(task_configuration_test_05) +{ + int ret = system(tarCMD.c_str()); + + RUNNER_ASSERT_MSG(!WIFEXITED(ret) || !WIFSIGNALED(ret), + "Cannot untar widget to check direct installation from directory!"); + + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.mode.extension = InstallMode::ExtensionType::DIR; + i_context.requestedPath = wgtTmpPath + "/TestWgt.wgt"; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + Try{ + while(i < stepsCnt && result) + { + i++; + + if (i == 3) + { + //Remove config file + RUNNER_ASSERT(WrtUtilRemove(wgtTmpPath + "/config.xml")); + } + + result = taskConf.NextStep(); + } + } + Catch(WrtDB::WidgetDAOReadOnly::Exception::WidgetNotExist){ + RUNNER_ASSERT(i == 3); + return; + } + + RUNNER_ASSERT_MSG(false, + "An Exception should be thrown after deletion of config.xml file."); +} + +/* +Name: task_configuration_test_06 +Description: Tests if missing config file will be detected during parsing step. +Expected: An exception should be thrown if there is no config file. +*/ +RUNNER_TEST(task_configuration_test_06) +{ + int ret = system(tarCMD.c_str()); + + RUNNER_ASSERT_MSG(!WIFEXITED(ret) || !WIFSIGNALED(ret), + "Cannot untar widget to check direct installation from directory!"); + + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.mode.extension = InstallMode::ExtensionType::DIR; + i_context.requestedPath = wgtTmpPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + Try{ + while(i < stepsCnt && result) + { + i++; + + if (i == 4) + { + //Remove config file + RUNNER_ASSERT(WrtUtilRemove(wgtTmpPath + "/config.xml")); + } + + result = taskConf.NextStep(); + } + } + Catch(WidgetInstall::Exceptions::MissingConfig){ + RUNNER_ASSERT(i == 4); + return; + } + + RUNNER_ASSERT_MSG(false, + "An Exception should be thrown in parsing step if there is no " + "config.xml file in the directory."); +} + +/* +Name: task_configuration_test_07 +Description: Tests reinstallation of a widget from the directory. +Expected: A widget should be successfully installed. +*/ +RUNNER_TEST(task_configuration_test_07) +{ + int ret = system(tarCMD.c_str()); + + RUNNER_ASSERT_MSG(!WIFEXITED(ret) || !WIFSIGNALED(ret), + "Cannot untar widget to check direct installation from directory!"); + + //This widget is needed to be installed to find the tizen PkgId in the database + //during reinstallation step. + RUNNER_ASSERT_MSG( + InstallerWrapper::install(wgtTmpPath + "/TestWgt.wgt", tizenId) == InstallerWrapper::Success, + "Failed to install a widget"); + + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.mode.command = InstallMode::Command::REINSTALL; + i_context.mode.extension = InstallMode::ExtensionType::DIR; + i_context.requestedPath = wgtTmpPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt); +} + +/* +Name: task_configuration_test_08 +Description: Tests recovery installation of the widget from the directory. +Expected: A widget should be successfully installed. +*/ +RUNNER_TEST(task_configuration_test_08) +{ + int ret = system(tarCMD.c_str()); + + RUNNER_ASSERT_MSG(!WIFEXITED(ret) || !WIFSIGNALED(ret), + "Cannot untar widget to check direct installation from directory!"); + + //This widget is needed to be installed to find the tizen PkgId in the database + //during reinstallation step. + RUNNER_ASSERT_MSG( + InstallerWrapper::install(wgtTmpPath + "/TestWgt.wgt", tizenId) == InstallerWrapper::Success, + "Failed to install a widget"); + + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.mode.command = InstallMode::Command::RECOVERY; + i_context.mode.extension = InstallMode::ExtensionType::DIR; + i_context.requestedPath = wgtTmpPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt); +} + +/* +Name: task_configuration_test_09 +Description: Tests if a tizenAppID and tizenPkgID will be generated if were not +set earlier. +Expected: IDs should be properly generated. +*/ +RUNNER_TEST(task_configuration_test_09) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = wgtPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + + if (i == 5){ + i_context.widgetConfig.tzAppid = L""; + i_context.widgetConfig.tzPkgid = L""; + i_context.widgetConfig.configInfo.tizenAppId = boost::none; + i_context.widgetConfig.configInfo.tizenPkgId = boost::none; + } + + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt && + i_context.widgetConfig.tzAppid != L"" && + i_context.widgetConfig.tzPkgid != L""); +} + +/* +Name: task_configuration_test_10 +Description: Tests if a tizenPkgID will be generated if was not set earlier. +Expected: ID should be properly generated. +*/ +RUNNER_TEST(task_configuration_test_10) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = wgtPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + while(i < stepsCnt && result) + { + i++; + + if (i == 5){ + i_context.widgetConfig.tzPkgid = L""; + i_context.widgetConfig.configInfo.tizenPkgId = boost::none; + } + + result = taskConf.NextStep(); + } + + RUNNER_ASSERT(i == stepsCnt && + i_context.widgetConfig.tzPkgid != L""); +} + +/* +Name: task_configuration_test_11 +Description: Tests if a tizenAppId and tizenPkgID will be generated if +tizenApp ID is too short and tizenPkgID is not set. +Expected: IDs should be properly generated. An exception should be thrown +in step 9 beacuse this widget is already installed. +*/ +RUNNER_TEST(task_configuration_test_11) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = wgtPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + Try{ + while(i < stepsCnt && result) + { + i++; + + if (i == 5){ + i_context.widgetConfig.tzPkgid = L""; + i_context.widgetConfig.configInfo.tizenAppId = L"abcd"; + i_context.widgetConfig.configInfo.tizenPkgId = boost::none; + } + + result = taskConf.NextStep(); + } + } + Catch(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid){ + RUNNER_ASSERT(i == 9 && + i_context.widgetConfig.tzPkgid != L""); + return; + } + + RUNNER_ASSERT_MSG(false, "An exception should be thrown because this widget" + " is already installed!"); +} + +/* +Name: task_configuration_test_12 +Description: Tests if a tizenAppId and tizenPkgID will be generated if +tizenApp ID has incorrect characters and tizenPkgID is not set. +Expected: IDs should be properly generated. An exception should be thrown +in step 9 beacuse this widget is already installed. +*/ +RUNNER_TEST(task_configuration_test_12) +{ + const WidgetInstallationStruct installerStruct( + InstallerCallbacksTranslate::installFinishedCallback, + InstallerCallbacksTranslate::installProgressCallback, + new InstallerCallbacksTranslate::StatusCallbackStruct( + NULL, &staticWrtInitPreloadStatusCallback, NULL), + InstallMode(), + std::make_shared()); + + InstallerContext i_context; + + i_context.mode = InstallMode(); + i_context.requestedPath = wgtPath; + + i_context.job = + new Jobs::WidgetInstall::JobWidgetInstall(wgtPath, "", + installerStruct); + + TaskConfiguration taskConf(i_context); + size_t stepsCnt = taskConf.GetStepCount(); + + unsigned int i = 0; + bool result = true; + + Try{ + while(i < stepsCnt && result) + { + i++; + + if (i == 5){ + i_context.widgetConfig.tzPkgid = L""; + i_context.widgetConfig.configInfo.tizenAppId = L"1234!@#$qw"; + i_context.widgetConfig.configInfo.tizenPkgId = boost::none; + + FOREACH(localizedData, i_context.widgetConfig.configInfo.localizedDataSet) + { + localizedData->second.name = L"1234!@#$qw"; + } + } + + result = taskConf.NextStep(); + } + } + Catch(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid){ + RUNNER_ASSERT(i == 9 && + i_context.widgetConfig.tzPkgid != L""); + return; + } + + RUNNER_ASSERT_MSG(false, "An exception should be thrown because this widget" + " is already installed!"); +} diff --git a/tests/general/TestInit.cpp b/tests/general/TestInit.cpp new file mode 100644 index 0000000..5c433ba --- /dev/null +++ b/tests/general/TestInit.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestInit.cpp + * @author Tomasz Iwanek (t.iwanek@samsung.com) + * @version 1.0 + * @brief Main for wrt-installer general tests + */ + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ + _D("Starting tests"); + + WrtDB::WrtDatabase::attachToThreadRW(); + //libxml2 initialization + xmlInitParser(); + LIBXML_TEST_VERSION + + int status = + DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); + xmlCleanupParser(); + WrtDB::WrtDatabase::detachFromThread(); + + return status; +} diff --git a/tests/general/WidgetInstallManifestTests.cpp b/tests/general/WidgetInstallManifestTests.cpp new file mode 100644 index 0000000..2a73cdd --- /dev/null +++ b/tests/general/WidgetInstallManifestTests.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file WidgetInstallManifestTests.cpp + * @author Dominik Duda (d.duda@samsung.com) + * @version 1.0 + * @brief Tests functions from wrt-installer/src/jobs/widget_install/manifest.cpp + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Jobs; +using namespace WidgetInstall; + +RUNNER_TEST_GROUP_INIT(WidgetInstallManifest) + +namespace{ + const std::string manifestFilePath("/tmp/manifest.xml"); + Manifest manifest; +} + +/* +Name: wgt_install_manifest_test_01 +Description: Tests creation of an empty manifest file. +Expected: The file should be created. +*/ +RUNNER_TEST(wgt_install_manifest_test_01) +{ + Manifest manifest; + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + RUNNER_ASSERT(WrtUtilFileExists(manifestFilePath)); +} + +/* +Name: wgt_install_manifest_test_02 +Description: Tests creation of a manifest file with empty icon, label, author +and description nodes. +Expected: All nodes should be successfully created. +*/ +RUNNER_TEST(wgt_install_manifest_test_02) +{ + Manifest manifest; + + manifest.addIcon(IconType()); + manifest.addLabel(LabelType()); + manifest.addAuthor(AuthorType()); + manifest.addDescription(DescriptionType()); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile mFile(manifestFilePath); + + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:icon") == ""); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:label") == ""); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:author") == ""); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:description") == ""); +} + +/* +Name: wgt_install_manifest_test_03 +Description: Tests creation of a manifest file with icon, label, author and +description nodes. +Expected: All nodes should be successfully created. +*/ +RUNNER_TEST(wgt_install_manifest_test_03) +{ + Manifest manifest; + + manifest.addIcon(IconType(L"manifestIcon.png")); + manifest.addLabel(LabelType(L"manifest label")); + manifest.addDescription(DescriptionType(L"manifest description")); + manifest.addDescription(DescriptionType(L"opis manifestu", L"pl-pl")); + manifest.addAuthor(AuthorType( + DPL::String(L"some@email.com"), + DPL::String(L"emailto:some@email.com"), + DPL::String(L"en"), + DPL::String(L"Manifest email"))); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile mFile(manifestFilePath); + + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:icon") + == "manifestIcon.png"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:label") + == "manifest label"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:author/@email") + == "some@email.com"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:author/@href") + == "emailto:some@email.com"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:author/@xml:lang") + == "en"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:author") + == "Manifest email"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:description[1]") + == "manifest description"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:description[2]") + == "opis manifestu"); + RUNNER_ASSERT(mFile.getValueByXpath("/p:manifest/p:description[2]/@xml:lang") + == "pl-pl"); +} + +/* +Name: wgt_install_manifest_test_04 +Description: Tests creation of an account node with a capability node as a child +node of the account-provider node. +Expected: The node should be successfully created with all attributes and child +nodes. +*/ +RUNNER_TEST(wgt_install_manifest_test_04) +{ + Manifest manifest; + Account acc; + AccountProviderType accProv; + std::pair icon; + + accProv.appid = L"id.manifest.xml"; + accProv.multiAccount = L"true"; + + accProv.name.push_back(LabelType()); + accProv.name.push_back(LabelType(L"only name")); + accProv.name.push_back(LabelType(L"name with lang", L"en-gb")); + accProv.name.push_back(LabelType(L"nazwa z lang", L"pl-pl")); + + accProv.capability.push_back(L"Capability_01"); + accProv.capability.push_back(L""); + + icon.first = L"account"; + icon.second = L"/tmp/icon.png"; + accProv.icon.push_back(icon); + + acc.addAccountProvider(accProv); + + manifest.addAccount(acc); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/@appid") == "id.manifest.xml"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/@multiple-accounts-support") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:icon/@section") == "account"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:icon") == "/tmp/icon.png"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[1]") == ""); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[2]") == "only name"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[3]") == "name with lang"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[4]") == "name with lang"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[4]/@xml:lang") == "en-gb"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[5]") == "nazwa z lang"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:label[5]/@xml:lang") == "pl-pl"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:capability[1]") == "Capability_01"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:account/" + "p:account-provider/p:capability[2]") == ""); +} + +/* +Name: wgt_install_manifest_test_05 +Description: Tests creation of a service-application node. +Expected: The node should be successfully created with all attributes and child +nodes. +*/ +RUNNER_TEST(wgt_install_manifest_test_05) +{ + Manifest manifest; + AppControlType appType; + ServiceApplicationType servApp; + + appType.addMime(L"text/plain"); + appType.addUri(L"http://someurl.org"); + appType.addOperation(L"simple operation"); + + servApp.setAppid(L"manifest.id"); + servApp.setAutoRestart(false); + servApp.setExec(L"exec"); + servApp.setOnBoot(false); + servApp.setType(L"someType"); + servApp.addLabel(LabelType(L"simpleLabel")); + servApp.addIcon(IconType(L"simpleIcon.png")); + servApp.addAppControl(appType); + + manifest.addServiceApplication(servApp); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/@appid") == "manifest.id"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/@auto-restart") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/@exec") == "exec"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/@on-boot") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/@type") == "someType"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/p:label") == "simpleLabel"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/p:icon") == "simpleIcon.png"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/p:app-control/p:operation/@name") == "simple operation"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/p:app-control/p:uri/@name") == "http://someurl.org"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:service-application/p:app-control/p:mime/@name") == "text/plain"); +} + +/* +Name: wgt_install_manifest_test_06 +Description: Creats manifest file with an empty ui-application node. +Expected: The empty ui-application node should be created. All node's parameters +should be empty too. +*/ +RUNNER_TEST(wgt_install_manifest_test_06) +{ + Manifest manifest; + UiApplicationType uiApp; + + manifest.addUiApplication(uiApp); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + Try + { + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application") == ""); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@appid") == ""); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@exec") == ""); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@type") == ""); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@extraid") == ""); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } +} + +/* +Name: wgt_install_manifest_test_07 +Description: Tests creation of an ui-application node. +Expected: The node should be successfully created with all attributes and child +nodes. +*/ +RUNNER_TEST(wgt_install_manifest_test_07) +{ + Manifest manifest; + UiApplicationType uiApp; + + uiApp.setAppid(L"manifest.AppID"); + uiApp.setCategories(L"categories"); + uiApp.setExtraid(L"extraID"); + uiApp.setExec(L"exec"); + uiApp.setMultiple(false); + uiApp.setNodisplay(false); + uiApp.setTaskmanage(true); + uiApp.setType(L"uiType"); + + uiApp.addLabel(LabelType(L"uiLabel")); + uiApp.addIcon(IconType(L"icon.png")); + uiApp.addAppCategory(L"appCategory"); + uiApp.addMetadata(MetadataType(DPL::OptionalString(L"key"), DPL::OptionalString(L"value"))); + + AppControlType appCtrl; + appCtrl.addMime(L"text/plain"); + appCtrl.addOperation(L"appOperation"); + appCtrl.addUri(L"some.uri.com"); + + uiApp.addAppControl(appCtrl); + + manifest.addUiApplication(uiApp); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + Try + { + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@appid") == "manifest.AppID"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@exec") == "exec"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@type") == "uiType"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@extraid") == "extraID"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@multiple") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@nodisplay") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@taskmanage") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/@categories") == "categories"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:label") == "uiLabel"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:icon") == "icon.png"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:category/@name") == "appCategory"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:metadata/@key") == "key"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:metadata/@value") == "value"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:app-control/p:operation/@name") == "appOperation"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:app-control/p:uri/@name") == "some.uri.com"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ui-application/p:app-control/p:mime/@name") == "text/plain"); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } +} + +/* +Name: wgt_install_manifest_test_08 +Description: Tests creation of an ime-application node. +Expected: The node should be successfully created with all attributes and child +nodes. +*/ +RUNNER_TEST(wgt_install_manifest_test_08) +{ + Manifest manifest; + ImeApplicationType ime; + + ime.setAppid(L"appID"); + ime.setExec(L"exec"); + ime.setMultiple(true); + ime.setNodisplay(true); + ime.setType(L"type"); + ime.addIcon(IconType(L"imeicon.png")); + ime.addLabel(LabelType(L"imeLabel")); + + manifest.addImeApplication(ime); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + Try + { + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/@appid") == "appID"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/@exec") == "exec"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/@multiple") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/@nodisplay") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/@type") == "type"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/p:label") == "imeLabel"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:ime-application/p:icon") == "imeicon.png"); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } +} + +/* +Name: wgt_install_manifest_test_09 +Description: Tests creation of a livebox node. +Expected: The node should be successfully created with all child nodes. +*/ +RUNNER_TEST(wgt_install_manifest_test_09) +{ + Manifest manifest; + LiveBoxInfo lbox; + BoxInfoType binfo; + BoxSizeType bst; + BoxLabelType blt; + WrtDB::ConfigParserData::LiveboxInfo::BoxSizeInfo bsize; + + lbox.setLiveboxId(L"lboxID"); + lbox.setIcon(L"lboxicon.png"); + lbox.setPrimary(L"lboxprim"); + + + blt.push_back(std::pair(L"pl-pl", L"lbl")); + lbox.setLabel(blt); + + bsize.m_preview = L"true"; + bsize.m_size = L"20;20"; + bsize.m_useDecoration = L"false"; + bst.push_back(bsize); + + binfo.boxMouseEvent = L"onclick"; + binfo.boxSize = bst; + binfo.boxSrc = L"boxSrc"; + binfo.boxTouchEffect = L"false"; + binfo.pdHeight = L"100"; + binfo.pdSrc = L"pdSrc"; + binfo.pdWidth = L"100"; + lbox.setBox(binfo); + + manifest.addLivebox(lbox); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + Try + { + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/@appid") == "lboxID"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/@primary") == "lboxprim"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/@abi") == "html"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/@network") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/@nodisplay") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:label[1]") == "lbl"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:label[1]/@xml:lang") == "pl-pl"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:label[2]") == "NO NAME"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:icon") == "lboxicon.png"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/@type") == "buffer"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/@mouse_event") == "onclick"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/@touch_effect") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/p:size") == "20;20"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/p:size/@preview") == "true"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/p:size/@need_frame") == "false"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:box/p:script/@src") == "boxSrc"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:pd/@type") == "buffer"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:pd/p:size") == "100x100"); + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:livebox/p:pd/p:script/@src") == "pdSrc"); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } +} + +/* +Name: wgt_install_manifest_test_10 +Description: Tests creation of a privilege node. +Expected: The node should be successfully created. +*/ +RUNNER_TEST(wgt_install_manifest_test_10) +{ + PrivilegeType prv1; + + prv1.addPrivilegeName(L"name_1"); + + manifest.addPrivileges(prv1); + manifest.generate(DPL::FromASCIIString(manifestFilePath)); + + ManifestFile manReader(manifestFilePath); + + Try + { + RUNNER_ASSERT(manReader.getValueByXpath("/p:manifest/p:privileges/p:privilege") == "name_1"); + } + Catch(ManifestFile::ManifestParseError) + { + RUNNER_ASSERT_MSG(false,DPL::Exception::KnownExceptionToString(_rethrown_exception)); + } +} + +/* +Name: wgt_install_manifest_test_11 +Description: Deletes the XML file used for tests. +Expected: The file should be successfully deleted. +*/ +RUNNER_TEST(wgt_install_manifest_test_11) +{ + RUNNER_ASSERT(WrtUtilRemove(manifestFilePath)); +} diff --git a/tests/general/WidgetLocationTests.cpp b/tests/general/WidgetLocationTests.cpp new file mode 100644 index 0000000..f7075de --- /dev/null +++ b/tests/general/WidgetLocationTests.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /** + * @file WidgetLocationTests.cpp + * @author Maciej Piotrowski (m.piotrowski@samsung.com) + * @version 1.0 + * @brief WidgetLocation tests + */ + +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(WidgetLocation) + + +/* +Name: WidgetLocationCleanInit +Description: Tests WidgetLocation creation for WidgetLocation::WidgetLocation +*/ +RUNNER_TEST(WidgetLocationCleanInit) +{ + WidgetLocation wl; + + RUNNER_ASSERT(wl.getInstallationDir() == ""); + RUNNER_ASSERT(wl.getPackageInstallationDir() == "/"); + RUNNER_ASSERT(wl.getSourceDir() == "//res/wgt"); + RUNNER_ASSERT(wl.getBinaryDir() == "//bin"); + RUNNER_ASSERT(wl.getUserBinaryDir() == "/opt/usr/apps///bin"); + RUNNER_ASSERT(wl.getExecFile() == "//bin/"); + RUNNER_ASSERT(wl.getBackupDir() == "/.backup"); + RUNNER_ASSERT(wl.getBackupSourceDir() == "/.backup/res/wgt"); + RUNNER_ASSERT(wl.getBackupBinaryDir() == "/.backup/bin"); + RUNNER_ASSERT(wl.getBackupExecFile() == "/.backup/bin/"); + RUNNER_ASSERT(wl.getBackupPrivateDir() == "/.backup/data"); + RUNNER_ASSERT(wl.getUserDataRootDir() == "/opt/usr/apps/"); + RUNNER_ASSERT(wl.getPrivateStorageDir() == "/opt/usr/apps//data"); + RUNNER_ASSERT(wl.getPrivateTempStorageDir() == "/opt/usr/apps//tmp"); + RUNNER_ASSERT(wl.getSharedRootDir() == "/opt/usr/apps//shared"); + RUNNER_ASSERT(wl.getSharedResourceDir() == "/opt/usr/apps//shared/res"); + RUNNER_ASSERT(wl.getSharedDataDir() == "/opt/usr/apps//shared/data"); + RUNNER_ASSERT(wl.getSharedTrustedDir() == "/opt/usr/apps//shared/trusted"); + RUNNER_ASSERT(wl.getBackupSharedDir() == "/.backup/shared"); + RUNNER_ASSERT(wl.getBackupSharedDataDir() == "/.backup/shared/data"); + RUNNER_ASSERT(wl.getBackupSharedTrustedDir() == "/.backup/shared/trusted"); + RUNNER_ASSERT(wl.getNPPluginsDir() == "//res/wgt/plugins"); + RUNNER_ASSERT(wl.getNPPluginsExecFile() == "//bin/.npruntime"); + RUNNER_ASSERT(WrtUtilDirExists(wl.getTemporaryPackageDir())); + RUNNER_ASSERT(wl.getTemporaryRootDir() == "//res/wgt"); + RUNNER_ASSERT(wl.getInstalledIconPath() == ""); + RUNNER_ASSERT(wl.getWidgetSource() == ""); + RUNNER_ASSERT(wl.getPkgId() == DPL::String(L"")); +} + +/* +Name: WidgetLocationPkgIdInit +Description: Tests WidgetLocation creation for WidgetLocation::WidgetLocation wirh pkgid +*/ +RUNNER_TEST(WidgetLocationPkgIdInit) +{ + WidgetLocation wl("1234567890"); + + RUNNER_ASSERT(wl.getInstallationDir() == ""); + RUNNER_ASSERT(wl.getPackageInstallationDir() == "/1234567890"); + RUNNER_ASSERT(wl.getSourceDir() == "/1234567890/res/wgt"); + RUNNER_ASSERT(wl.getBinaryDir() == "/1234567890/bin"); + RUNNER_ASSERT(wl.getUserBinaryDir() == "/opt/usr/apps/1234567890//bin"); + RUNNER_ASSERT(wl.getExecFile() == "/1234567890/bin/"); + RUNNER_ASSERT(wl.getBackupDir() == "/1234567890.backup"); + RUNNER_ASSERT(wl.getBackupSourceDir() == "/1234567890.backup/res/wgt"); + RUNNER_ASSERT(wl.getBackupBinaryDir() == "/1234567890.backup/bin"); + RUNNER_ASSERT(wl.getBackupExecFile() == "/1234567890.backup/bin/"); + RUNNER_ASSERT(wl.getBackupPrivateDir() == "/1234567890.backup/data"); + RUNNER_ASSERT(wl.getUserDataRootDir() == "/opt/usr/apps/1234567890"); + RUNNER_ASSERT(wl.getPrivateStorageDir() == "/opt/usr/apps/1234567890/data"); + RUNNER_ASSERT(wl.getPrivateTempStorageDir() == "/opt/usr/apps/1234567890/tmp"); + RUNNER_ASSERT(wl.getSharedRootDir() == "/opt/usr/apps/1234567890/shared"); + RUNNER_ASSERT(wl.getSharedResourceDir() == "/opt/usr/apps/1234567890/shared/res"); + RUNNER_ASSERT(wl.getSharedDataDir() == "/opt/usr/apps/1234567890/shared/data"); + RUNNER_ASSERT(wl.getSharedTrustedDir() == "/opt/usr/apps/1234567890/shared/trusted"); + RUNNER_ASSERT(wl.getBackupSharedDir() == "/1234567890.backup/shared"); + RUNNER_ASSERT(wl.getBackupSharedDataDir() == "/1234567890.backup/shared/data"); + RUNNER_ASSERT(wl.getBackupSharedTrustedDir() == "/1234567890.backup/shared/trusted"); + RUNNER_ASSERT(wl.getNPPluginsDir() == "/1234567890/res/wgt/plugins"); + RUNNER_ASSERT(wl.getNPPluginsExecFile() == "/1234567890/bin/.npruntime"); + RUNNER_ASSERT(WrtUtilDirExists(wl.getTemporaryPackageDir())); + RUNNER_ASSERT(wl.getTemporaryRootDir() == "/1234567890/res/wgt"); + RUNNER_ASSERT(wl.getInstalledIconPath() == ""); + RUNNER_ASSERT(wl.getWidgetSource() == ""); + RUNNER_ASSERT(wl.getPkgId() == DPL::String(L"1234567890")); +} + +/* +Name: WidgetLocationPkgIdInitAppId +Description: Tests WidgetLocation creation for WidgetLocation::WidgetLocation + with pkgid and appid +*/ +RUNNER_TEST(WidgetLocationPkgIdInitAppId) +{ + WidgetLocation wl("1234567890"); + wl.registerAppid("id123456"); + + RUNNER_ASSERT(wl.getInstallationDir() == ""); + RUNNER_ASSERT(wl.getPackageInstallationDir() == "/1234567890"); + RUNNER_ASSERT(wl.getSourceDir() == "/1234567890/res/wgt"); + RUNNER_ASSERT(wl.getBinaryDir() == "/1234567890/bin"); + RUNNER_ASSERT(wl.getUserBinaryDir() == "/opt/usr/apps/1234567890//bin"); + RUNNER_ASSERT(wl.getExecFile() == "/1234567890/bin/id123456"); + RUNNER_ASSERT(wl.getBackupDir() == "/1234567890.backup"); + RUNNER_ASSERT(wl.getBackupSourceDir() == "/1234567890.backup/res/wgt"); + RUNNER_ASSERT(wl.getBackupBinaryDir() == "/1234567890.backup/bin"); + RUNNER_ASSERT(wl.getBackupExecFile() == "/1234567890.backup/bin/id123456"); + RUNNER_ASSERT(wl.getBackupPrivateDir() == "/1234567890.backup/data"); + RUNNER_ASSERT(wl.getUserDataRootDir() == "/opt/usr/apps/1234567890"); + RUNNER_ASSERT(wl.getPrivateStorageDir() == "/opt/usr/apps/1234567890/data"); + RUNNER_ASSERT(wl.getPrivateTempStorageDir() == "/opt/usr/apps/1234567890/tmp"); + RUNNER_ASSERT(wl.getSharedRootDir() == "/opt/usr/apps/1234567890/shared"); + RUNNER_ASSERT(wl.getSharedResourceDir() == "/opt/usr/apps/1234567890/shared/res"); + RUNNER_ASSERT(wl.getSharedDataDir() == "/opt/usr/apps/1234567890/shared/data"); + RUNNER_ASSERT(wl.getSharedTrustedDir() == "/opt/usr/apps/1234567890/shared/trusted"); + RUNNER_ASSERT(wl.getBackupSharedDir() == "/1234567890.backup/shared"); + RUNNER_ASSERT(wl.getBackupSharedDataDir() == "/1234567890.backup/shared/data"); + RUNNER_ASSERT(wl.getBackupSharedTrustedDir() == "/1234567890.backup/shared/trusted"); + RUNNER_ASSERT(wl.getNPPluginsDir() == "/1234567890/res/wgt/plugins"); + RUNNER_ASSERT(wl.getNPPluginsExecFile() == "/1234567890/bin/id123456.npruntime"); + RUNNER_ASSERT(WrtUtilDirExists(wl.getTemporaryPackageDir())); + RUNNER_ASSERT(wl.getTemporaryRootDir() == "/1234567890/res/wgt"); + RUNNER_ASSERT(wl.getWidgetSource() == ""); + RUNNER_ASSERT(wl.getPkgId() == DPL::String(L"1234567890")); +} + +/* +Name: WidgetLocationExternalLocations +Description: Tests WidgetLocation::listExternalLocations() and WidgetLocation::registerExternalLocation() +*/ +RUNNER_TEST(WidgetLocationExternalLocations) +{ + WidgetLocation wl; + RUNNER_ASSERT(wl.listExternalLocations().size() == 0); + wl.registerExternalLocation("filepath1"); + wl.registerExternalLocation("filepath2"); + wl.registerExternalLocation("filepath3"); + RUNNER_ASSERT(wl.listExternalLocations().size() == 3); + wl.registerExternalLocation("filepath1"); + RUNNER_ASSERT(wl.listExternalLocations().size() == 4); +} + +/* +Name: WidgetLocationAdvancedInit1 +Description: Tests WidgetLocation::WidgetLocation() +*/ +RUNNER_TEST(WidgetLocationAdvancedInit1) +{ + WidgetLocation wl("9876543210", "/opt/usr/apps/9876543210", WrtDB::PKG_TYPE_NOMAL_WEB_APP, false, InstallMode::ExtensionType::DIR); + RUNNER_ASSERT(WrtUtilDirExists(wl.getTemporaryPackageDir())); + RUNNER_ASSERT(wl.getTemporaryRootDir() == "/opt/usr/apps/9876543210/res/wgt"); +} + + +/* +Name: WidgetLocationAdvancedInit2 +Description: Tests WidgetLocation::WidgetLocation() +*/ +RUNNER_TEST(WidgetLocationAdvancedInit2) +{ + WidgetLocation wl("1234567890", "/opt/usr/apps/1234567890/", "/tmp/tempdir/", WrtDB::PKG_TYPE_NOMAL_WEB_APP, false, InstallMode::ExtensionType::WGT); + RUNNER_ASSERT(wl.getTemporaryPackageDir() == "/tmp/tempdir/"); + RUNNER_ASSERT(wl.getTemporaryRootDir() == "/opt/usr/apps/1234567890/res/wgt"); + //fails because there is no use of Jobs::WidgetInstall::createTempPath like it is in constructor + //from WidgetLocationAdvancedInit1 case + //RUNNER_ASSERT(WrtUtilDirExists(wl.getTemporaryPackageDir())); +} diff --git a/tests/general/WidgetUpdateTests.cpp b/tests/general/WidgetUpdateTests.cpp new file mode 100644 index 0000000..064e545 --- /dev/null +++ b/tests/general/WidgetUpdateTests.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file WidgetUpdateTests.cpp + * @author Grzegorz Rynkowski (g.rynkowski@samsung.com) + * @version 1.0 + * @brief Test a process of updating web applications. + */ + +#include +#include +#include +#include + +using namespace InstallerWrapper; + +#define RUNNER_ASSERT_MSG_SAFE(test, message) \ + { \ + DPL::Test::TestRunnerSingleton::Instance().MarkAssertion(); \ + \ + if (!(test)) \ + { \ + uninstall(tizenId); \ + std::ostringstream assertMsg; \ + assertMsg << message; \ + throw DPL::Test::TestRunner::TestFailed(#test, \ + __FILE__, \ + __LINE__, \ + assertMsg.str()); \ + } \ + } + +RUNNER_TEST_GROUP_INIT(WidgetUpdate) + +/* +Name: validUpdateOfSigned +Description: Tests update the web app where origin and a new one are signed. +Expected: Widget should be successfully installed. +*/ +RUNNER_TEST(validUpdateOfSigned) +{ + std::string tizenId; + std::string wgtPath; + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; + RUNNER_ASSERT_MSG(install(wgtPath, tizenId) == InstallerWrapper::Success, + "Failed to install widget"); + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Signed.wgt"; + RUNNER_ASSERT_MSG_SAFE(install(wgtPath, tizenId) == InstallerWrapper::Success, + "Failed update in case both programs are signed"); + uninstall(tizenId); +} + +/* +Name: validUpdateOfUnsigned +Description: Tests update the web app where origin and a new one are unsigned. +Expected: Widget should be successfully updated. +*/ +RUNNER_TEST(validUpdateOfUnsigned) +{ + std::string tizenId; + std::string wgtPath; + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Unsigned.wgt"; + RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed to install widget"); + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Unsigned.wgt"; + RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed update in case both programs are signed"); + uninstall(tizenId); +} + +/* + * Information: + * These tests are incompatible to the specification 2.1 + * (but compatible to the specification 3.0). + */ +///* +//Name: unupdateOfSigned +//Description: Tests update that signed web app could be downgraded. +//Expected: Widget should not be updated. +//*/ +//RUNNER_TEST(unupdateOfSigned) +//{ +// std::string tizenId; +// std::string wgtPath; +// +// wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Signed.wgt"; +// RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), +// "Failed to install widget"); +// +// wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; +// RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success != install(wgtPath, tizenId), +// "Unupdate should be prohibited."); +// uninstall(tizenId); +//} +// +///* +//Name: unupdateOfSigned +//Description: Tests update that unsigned web app could be downgraded. +//Expected: Widget should not be updated. +//*/ +//RUNNER_TEST(unupdateOfUnsigned) +//{ +// std::string tizenId; +// std::string wgtPath; +// +// wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Unsigned.wgt"; +// RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), +// "Failed to install widget"); +// +// wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Unsigned.wgt"; +// RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success != install(wgtPath, tizenId), +// "Unupdate should be prohibited."); +// uninstall(tizenId); +//} + +/* +Name: validUpdateOfCrossSigned +Description: Tests update the web app where one of widgets are signed and second not. +Expected: Widget should not be updated. +*/ +RUNNER_TEST(updateOfCrossSignedWidgets) +{ + std::string tizenId; + std::string wgtPath; + + { + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Unsigned.wgt"; + RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed to install widget"); + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Signed.wgt"; + RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success != install(wgtPath, tizenId), + "The update unsigned app by the signed app should not be possible"); + uninstall(tizenId); + } + { + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; + RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed to install widget"); + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Unsigned.wgt"; + RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success != install(wgtPath, tizenId), + "The update signed app by the unsigned app should not be possible"); + uninstall(tizenId); + } +} + + +/* +Name: updateAnotherAuthor +Description: Tests update the web app by the widget signed by another author. +Expected: Widget should not be updated. +*/ +RUNNER_TEST(updateAnotherAuthor) +{ + std::string tizenId; + std::string wgtPath; + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; + RUNNER_ASSERT_MSG(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed to install widget"); + + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220SignedAnotherAuthor.wgt"; + RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success != install(wgtPath, tizenId), + "The update by another author should not be possible"); + uninstall(tizenId); +} + + +/* +Name: updateWidgetDataRemember +Description: Tests of keeping app data during widget updating. +Expected: App data should be kept. +*/ +RUNNER_TEST(updateWidgetDataRemember) +{ + std::string tizenId; + std::string wgtPath; + + // Installation of the widget + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer100Signed.wgt"; + RUNNER_ASSERT_MSG(install(wgtPath, tizenId) == InstallerWrapper::Success, + "Failed to install widget"); + + // Creating a file + std::string filePath = "/opt/usr/apps/HAdisUJ4Kn/data/test"; + std::string text = "slonce swieci dzisiaj wyjatkowo wysoko"; + std::string command = "echo " + text + " > " + filePath; + system(command.c_str()); + + // Second installation of the widget + wgtPath = miscWidgetsStuff + "widgets/widgetUpdateVer220Signed.wgt"; + RUNNER_ASSERT_MSG_SAFE(InstallerWrapper::Success == install(wgtPath, tizenId), + "Failed update in case both programs are signed"); + + + // Checking of the file created before + std::stringstream ss; + std::ifstream file(filePath); + RUNNER_ASSERT_MSG_SAFE(file.good(), "File is gone"); + + for( std::string line; getline( file, line ); ss << line); + file.close(); + RUNNER_ASSERT_MSG_SAFE(text == ss.str(), "Content of file is not the same"); + uninstall(tizenId); +} diff --git a/tests/general/configs/AllowNavigationEmpty.xml b/tests/general/configs/AllowNavigationEmpty.xml new file mode 100644 index 0000000..720795c --- /dev/null +++ b/tests/general/configs/AllowNavigationEmpty.xml @@ -0,0 +1,6 @@ + + + + test2.org test3.org + + diff --git a/tests/general/configs/AllowNavigationMultipleHosts.xml b/tests/general/configs/AllowNavigationMultipleHosts.xml new file mode 100644 index 0000000..4663bb1 --- /dev/null +++ b/tests/general/configs/AllowNavigationMultipleHosts.xml @@ -0,0 +1,5 @@ + + + http://test2.org test3.org *.test4.org * + + diff --git a/tests/general/configs/AllowNavigationMultipleHostsMultiline.xml b/tests/general/configs/AllowNavigationMultipleHostsMultiline.xml new file mode 100644 index 0000000..d3128c4 --- /dev/null +++ b/tests/general/configs/AllowNavigationMultipleHostsMultiline.xml @@ -0,0 +1,10 @@ + + + + http://test2.org + *.test4.org * + + test3.org + + + diff --git a/tests/general/configs/AppWidgetAutoLaunchEmpty.xml b/tests/general/configs/AppWidgetAutoLaunchEmpty.xml new file mode 100644 index 0000000..5830a27 --- /dev/null +++ b/tests/general/configs/AppWidgetAutoLaunchEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetAutoLaunchWrongValue.xml b/tests/general/configs/AppWidgetAutoLaunchWrongValue.xml new file mode 100644 index 0000000..b510ebb --- /dev/null +++ b/tests/general/configs/AppWidgetAutoLaunchWrongValue.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetFull.xml b/tests/general/configs/AppWidgetFull.xml new file mode 100644 index 0000000..eaa2a0a --- /dev/null +++ b/tests/general/configs/AppWidgetFull.xml @@ -0,0 +1,12 @@ + + + + My DynamicBox + + + 1x1 + + + + + \ No newline at end of file diff --git a/tests/general/configs/AppWidgetIdEmpty.xml b/tests/general/configs/AppWidgetIdEmpty.xml new file mode 100644 index 0000000..ae70c36 --- /dev/null +++ b/tests/general/configs/AppWidgetIdEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetIdTooLong.xml b/tests/general/configs/AppWidgetIdTooLong.xml new file mode 100644 index 0000000..eb0066e --- /dev/null +++ b/tests/general/configs/AppWidgetIdTooLong.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetIdTooShort.xml b/tests/general/configs/AppWidgetIdTooShort.xml new file mode 100644 index 0000000..80a5ed2 --- /dev/null +++ b/tests/general/configs/AppWidgetIdTooShort.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetIdWrongChar.xml b/tests/general/configs/AppWidgetIdWrongChar.xml new file mode 100644 index 0000000..f132c0f --- /dev/null +++ b/tests/general/configs/AppWidgetIdWrongChar.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetMinimal.xml b/tests/general/configs/AppWidgetMinimal.xml new file mode 100644 index 0000000..aa7bb26 --- /dev/null +++ b/tests/general/configs/AppWidgetMinimal.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetMultipleBoxContent.xml b/tests/general/configs/AppWidgetMultipleBoxContent.xml new file mode 100644 index 0000000..619c225 --- /dev/null +++ b/tests/general/configs/AppWidgetMultipleBoxContent.xml @@ -0,0 +1,13 @@ + + + + My DynamicBox + + 1x1 + + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetMultipleBoxIcon.xml b/tests/general/configs/AppWidgetMultipleBoxIcon.xml new file mode 100644 index 0000000..9b905bf --- /dev/null +++ b/tests/general/configs/AppWidgetMultipleBoxIcon.xml @@ -0,0 +1,12 @@ + + + + My DynamicBox + + + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetMultipleBoxLabel.xml b/tests/general/configs/AppWidgetMultipleBoxLabel.xml new file mode 100644 index 0000000..c046194 --- /dev/null +++ b/tests/general/configs/AppWidgetMultipleBoxLabel.xml @@ -0,0 +1,12 @@ + + + + test + test_en + test_pl + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetMultiplePrimary.xml b/tests/general/configs/AppWidgetMultiplePrimary.xml new file mode 100644 index 0000000..365fc19 --- /dev/null +++ b/tests/general/configs/AppWidgetMultiplePrimary.xml @@ -0,0 +1,22 @@ + + + + My DynamicBox + + 1x1 + + + + My DynamicBox + + 1x1 + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetNoBoxContent.xml b/tests/general/configs/AppWidgetNoBoxContent.xml new file mode 100644 index 0000000..8d882f5 --- /dev/null +++ b/tests/general/configs/AppWidgetNoBoxContent.xml @@ -0,0 +1,7 @@ + + + + My DynamicBox + + + diff --git a/tests/general/configs/AppWidgetNoBoxLabel.xml b/tests/general/configs/AppWidgetNoBoxLabel.xml new file mode 100644 index 0000000..8289b97 --- /dev/null +++ b/tests/general/configs/AppWidgetNoBoxLabel.xml @@ -0,0 +1,9 @@ + + + + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetNoId.xml b/tests/general/configs/AppWidgetNoId.xml new file mode 100644 index 0000000..39b0a32 --- /dev/null +++ b/tests/general/configs/AppWidgetNoId.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetNoPrimary.xml b/tests/general/configs/AppWidgetNoPrimary.xml new file mode 100644 index 0000000..f501865 --- /dev/null +++ b/tests/general/configs/AppWidgetNoPrimary.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetPrimaryEmpty.xml b/tests/general/configs/AppWidgetPrimaryEmpty.xml new file mode 100644 index 0000000..acb58ff --- /dev/null +++ b/tests/general/configs/AppWidgetPrimaryEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetPrimaryWrongValue.xml b/tests/general/configs/AppWidgetPrimaryWrongValue.xml new file mode 100644 index 0000000..bcb4cdf --- /dev/null +++ b/tests/general/configs/AppWidgetPrimaryWrongValue.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetUpdatePeriodEmpty.xml b/tests/general/configs/AppWidgetUpdatePeriodEmpty.xml new file mode 100644 index 0000000..2f41ddb --- /dev/null +++ b/tests/general/configs/AppWidgetUpdatePeriodEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetUpdatePeriodLow.xml b/tests/general/configs/AppWidgetUpdatePeriodLow.xml new file mode 100644 index 0000000..4c54ab8 --- /dev/null +++ b/tests/general/configs/AppWidgetUpdatePeriodLow.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/AppWidgetUpdatePeriodWrongFormat.xml b/tests/general/configs/AppWidgetUpdatePeriodWrongFormat.xml new file mode 100644 index 0000000..6cb62f0 --- /dev/null +++ b/tests/general/configs/AppWidgetUpdatePeriodWrongFormat.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentEmpty.xml b/tests/general/configs/BoxContentEmpty.xml new file mode 100644 index 0000000..183ac21 --- /dev/null +++ b/tests/general/configs/BoxContentEmpty.xml @@ -0,0 +1,8 @@ + + + + My DynamicBox + + + + diff --git a/tests/general/configs/BoxContentMouseEventEmpty.xml b/tests/general/configs/BoxContentMouseEventEmpty.xml new file mode 100644 index 0000000..c22f057 --- /dev/null +++ b/tests/general/configs/BoxContentMouseEventEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentMouseEventWrongValue.xml b/tests/general/configs/BoxContentMouseEventWrongValue.xml new file mode 100644 index 0000000..732ae52 --- /dev/null +++ b/tests/general/configs/BoxContentMouseEventWrongValue.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentMultipleBoxSize.xml b/tests/general/configs/BoxContentMultipleBoxSize.xml new file mode 100644 index 0000000..d5412db --- /dev/null +++ b/tests/general/configs/BoxContentMultipleBoxSize.xml @@ -0,0 +1,12 @@ + + + + My DynamicBox + + 1x1 + 2x1 + 2x2 + + + + diff --git a/tests/general/configs/BoxContentMultiplePd.xml b/tests/general/configs/BoxContentMultiplePd.xml new file mode 100644 index 0000000..66e05e1 --- /dev/null +++ b/tests/general/configs/BoxContentMultiplePd.xml @@ -0,0 +1,12 @@ + + + + My DynamicBox + + 1x1 + + + + + + diff --git a/tests/general/configs/BoxContentNoMouseEvent.xml b/tests/general/configs/BoxContentNoMouseEvent.xml new file mode 100644 index 0000000..aa7bb26 --- /dev/null +++ b/tests/general/configs/BoxContentNoMouseEvent.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentNoSrc.xml b/tests/general/configs/BoxContentNoSrc.xml new file mode 100644 index 0000000..e41081a --- /dev/null +++ b/tests/general/configs/BoxContentNoSrc.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentNoTouchEfect.xml b/tests/general/configs/BoxContentNoTouchEfect.xml new file mode 100644 index 0000000..aa7bb26 --- /dev/null +++ b/tests/general/configs/BoxContentNoTouchEfect.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentSrcEmpty.xml b/tests/general/configs/BoxContentSrcEmpty.xml new file mode 100644 index 0000000..d14c7c7 --- /dev/null +++ b/tests/general/configs/BoxContentSrcEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentTouchEfectEmpty.xml b/tests/general/configs/BoxContentTouchEfectEmpty.xml new file mode 100644 index 0000000..d62bb8d --- /dev/null +++ b/tests/general/configs/BoxContentTouchEfectEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxContentTouchEfectWrongValue.xml b/tests/general/configs/BoxContentTouchEfectWrongValue.xml new file mode 100644 index 0000000..09fed1f --- /dev/null +++ b/tests/general/configs/BoxContentTouchEfectWrongValue.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxIconEmpty.xml b/tests/general/configs/BoxIconEmpty.xml new file mode 100644 index 0000000..42f3902 --- /dev/null +++ b/tests/general/configs/BoxIconEmpty.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + + 1x1 + + + + diff --git a/tests/general/configs/BoxIconSrcEmpty.xml b/tests/general/configs/BoxIconSrcEmpty.xml new file mode 100644 index 0000000..530f2bf --- /dev/null +++ b/tests/general/configs/BoxIconSrcEmpty.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + + 1x1 + + + + diff --git a/tests/general/configs/BoxLabelEmpty.xml b/tests/general/configs/BoxLabelEmpty.xml new file mode 100644 index 0000000..72e1c3b --- /dev/null +++ b/tests/general/configs/BoxLabelEmpty.xml @@ -0,0 +1,10 @@ + + + + + + 1x1 + + + + diff --git a/tests/general/configs/BoxSizeEmpty.xml b/tests/general/configs/BoxSizeEmpty.xml new file mode 100644 index 0000000..a8ac2b1 --- /dev/null +++ b/tests/general/configs/BoxSizeEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + + + + + diff --git a/tests/general/configs/BoxSizeNoUserDecoration.xml b/tests/general/configs/BoxSizeNoUserDecoration.xml new file mode 100644 index 0000000..aa7bb26 --- /dev/null +++ b/tests/general/configs/BoxSizeNoUserDecoration.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxSizePreviewEmpty.xml b/tests/general/configs/BoxSizePreviewEmpty.xml new file mode 100644 index 0000000..c428737 --- /dev/null +++ b/tests/general/configs/BoxSizePreviewEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/BoxSizeUserDecorationEmpty.xml b/tests/general/configs/BoxSizeUserDecorationEmpty.xml new file mode 100644 index 0000000..5faf818 --- /dev/null +++ b/tests/general/configs/BoxSizeUserDecorationEmpty.xml @@ -0,0 +1,10 @@ + + + + My DynamicBox + + 1x1 + + + + diff --git a/tests/general/configs/ContentEmpty.xml b/tests/general/configs/ContentEmpty.xml new file mode 100644 index 0000000..df27d2d --- /dev/null +++ b/tests/general/configs/ContentEmpty.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/ContentSrcCorrect.xml b/tests/general/configs/ContentSrcCorrect.xml new file mode 100644 index 0000000..78155b0 --- /dev/null +++ b/tests/general/configs/ContentSrcCorrect.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/ContentSrcEmpty.xml b/tests/general/configs/ContentSrcEmpty.xml new file mode 100644 index 0000000..3ac71ef --- /dev/null +++ b/tests/general/configs/ContentSrcEmpty.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/CspEmpty.xml b/tests/general/configs/CspEmpty.xml new file mode 100644 index 0000000..a44c568 --- /dev/null +++ b/tests/general/configs/CspEmpty.xml @@ -0,0 +1,6 @@ + + + + anyCSP + + diff --git a/tests/general/configs/CspReportOnlyEmpty.xml b/tests/general/configs/CspReportOnlyEmpty.xml new file mode 100644 index 0000000..a9a50d6 --- /dev/null +++ b/tests/general/configs/CspReportOnlyEmpty.xml @@ -0,0 +1,6 @@ + + + + anyCSP + + diff --git a/tests/general/configs/InstallConfig.xml b/tests/general/configs/InstallConfig.xml new file mode 100644 index 0000000..7ee37d7 --- /dev/null +++ b/tests/general/configs/InstallConfig.xml @@ -0,0 +1,25 @@ + + app-control + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/general/configs/MetadataDuplicatedKey.xml b/tests/general/configs/MetadataDuplicatedKey.xml new file mode 100644 index 0000000..d48e5c2 --- /dev/null +++ b/tests/general/configs/MetadataDuplicatedKey.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/general/configs/MetadataEmpty.xml b/tests/general/configs/MetadataEmpty.xml new file mode 100644 index 0000000..71ba7c3 --- /dev/null +++ b/tests/general/configs/MetadataEmpty.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/MultipleAllowNavigation.xml b/tests/general/configs/MultipleAllowNavigation.xml new file mode 100644 index 0000000..97d63a6 --- /dev/null +++ b/tests/general/configs/MultipleAllowNavigation.xml @@ -0,0 +1,6 @@ + + + test1.org + test2.org test3.org + + diff --git a/tests/general/configs/MultipleContentCorrect.xml b/tests/general/configs/MultipleContentCorrect.xml new file mode 100644 index 0000000..f9d5512 --- /dev/null +++ b/tests/general/configs/MultipleContentCorrect.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/general/configs/MultipleContentIncorrect.xml b/tests/general/configs/MultipleContentIncorrect.xml new file mode 100644 index 0000000..a9d45d4 --- /dev/null +++ b/tests/general/configs/MultipleContentIncorrect.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/general/configs/MultipleCsp.xml b/tests/general/configs/MultipleCsp.xml new file mode 100644 index 0000000..6a5a362 --- /dev/null +++ b/tests/general/configs/MultipleCsp.xml @@ -0,0 +1,6 @@ + + + testCSP + anyCSP + + diff --git a/tests/general/configs/MultipleCspReportOnly.xml b/tests/general/configs/MultipleCspReportOnly.xml new file mode 100644 index 0000000..48e5e8f --- /dev/null +++ b/tests/general/configs/MultipleCspReportOnly.xml @@ -0,0 +1,6 @@ + + + testCSP + anyCSP + + diff --git a/tests/general/configs/MultipleMetadata.xml b/tests/general/configs/MultipleMetadata.xml new file mode 100644 index 0000000..25fa227 --- /dev/null +++ b/tests/general/configs/MultipleMetadata.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/general/configs/NoAllowNavigation.xml b/tests/general/configs/NoAllowNavigation.xml new file mode 100644 index 0000000..a6b2b12 --- /dev/null +++ b/tests/general/configs/NoAllowNavigation.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/general/configs/NoContent.xml b/tests/general/configs/NoContent.xml new file mode 100644 index 0000000..4bb8e41 --- /dev/null +++ b/tests/general/configs/NoContent.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/general/configs/NoCsp.xml b/tests/general/configs/NoCsp.xml new file mode 100644 index 0000000..a6b2b12 --- /dev/null +++ b/tests/general/configs/NoCsp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/general/configs/NoMetadata.xml b/tests/general/configs/NoMetadata.xml new file mode 100644 index 0000000..afab039 --- /dev/null +++ b/tests/general/configs/NoMetadata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/general/configs/PdHeightEmpty.xml b/tests/general/configs/PdHeightEmpty.xml new file mode 100644 index 0000000..f92cfb9 --- /dev/null +++ b/tests/general/configs/PdHeightEmpty.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdHeightExcessive.xml b/tests/general/configs/PdHeightExcessive.xml new file mode 100644 index 0000000..825a64d --- /dev/null +++ b/tests/general/configs/PdHeightExcessive.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdHeightTooLow.xml b/tests/general/configs/PdHeightTooLow.xml new file mode 100644 index 0000000..aec51bc --- /dev/null +++ b/tests/general/configs/PdHeightTooLow.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdHeightWrongValue.xml b/tests/general/configs/PdHeightWrongValue.xml new file mode 100644 index 0000000..8ed5704 --- /dev/null +++ b/tests/general/configs/PdHeightWrongValue.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdNoHeight.xml b/tests/general/configs/PdNoHeight.xml new file mode 100644 index 0000000..d90afdf --- /dev/null +++ b/tests/general/configs/PdNoHeight.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdNoSrc.xml b/tests/general/configs/PdNoSrc.xml new file mode 100644 index 0000000..f1e6490 --- /dev/null +++ b/tests/general/configs/PdNoSrc.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdNoWidth.xml b/tests/general/configs/PdNoWidth.xml new file mode 100644 index 0000000..f6112a7 --- /dev/null +++ b/tests/general/configs/PdNoWidth.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdSrcEmpty.xml b/tests/general/configs/PdSrcEmpty.xml new file mode 100644 index 0000000..af95f32 --- /dev/null +++ b/tests/general/configs/PdSrcEmpty.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdWidthEmpty.xml b/tests/general/configs/PdWidthEmpty.xml new file mode 100644 index 0000000..284daed --- /dev/null +++ b/tests/general/configs/PdWidthEmpty.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdWidthNegative.xml b/tests/general/configs/PdWidthNegative.xml new file mode 100644 index 0000000..9699465 --- /dev/null +++ b/tests/general/configs/PdWidthNegative.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdWidthWrongValue.xml b/tests/general/configs/PdWidthWrongValue.xml new file mode 100644 index 0000000..7b31656 --- /dev/null +++ b/tests/general/configs/PdWidthWrongValue.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/PdWidthZero.xml b/tests/general/configs/PdWidthZero.xml new file mode 100644 index 0000000..90e3711 --- /dev/null +++ b/tests/general/configs/PdWidthZero.xml @@ -0,0 +1,11 @@ + + + + My DynamicBox + + 1x1 + + + + + diff --git a/tests/general/configs/config_category1.xml b/tests/general/configs/config_category1.xml new file mode 100644 index 0000000..5f9d631 --- /dev/null +++ b/tests/general/configs/config_category1.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/general/configs/config_category2.xml b/tests/general/configs/config_category2.xml new file mode 100644 index 0000000..45afbd7 --- /dev/null +++ b/tests/general/configs/config_category2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/config_category3.xml b/tests/general/configs/config_category3.xml new file mode 100644 index 0000000..4f33bb9 --- /dev/null +++ b/tests/general/configs/config_category3.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/config_category4.xml b/tests/general/configs/config_category4.xml new file mode 100644 index 0000000..46c1c0c --- /dev/null +++ b/tests/general/configs/config_category4.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/general/configs/config_privilege1.xml b/tests/general/configs/config_privilege1.xml new file mode 100644 index 0000000..b97e685 --- /dev/null +++ b/tests/general/configs/config_privilege1.xml @@ -0,0 +1,12 @@ + + + privilege + + + + + + + + + diff --git a/tests/general/configs/config_privilege2.xml b/tests/general/configs/config_privilege2.xml new file mode 100644 index 0000000..64491af --- /dev/null +++ b/tests/general/configs/config_privilege2.xml @@ -0,0 +1,8 @@ + + + privilege + + + + + diff --git a/tests/general/configs/config_privilege3.xml b/tests/general/configs/config_privilege3.xml new file mode 100644 index 0000000..48dcf6f --- /dev/null +++ b/tests/general/configs/config_privilege3.xml @@ -0,0 +1,8 @@ + + + privilege + + + + + diff --git a/tests/general/configs/config_privilege4.xml b/tests/general/configs/config_privilege4.xml new file mode 100644 index 0000000..0578160 --- /dev/null +++ b/tests/general/configs/config_privilege4.xml @@ -0,0 +1,8 @@ + + + privilege + + + + + diff --git a/tests/general/configs/config_privilege5.xml b/tests/general/configs/config_privilege5.xml new file mode 100644 index 0000000..4869c0b --- /dev/null +++ b/tests/general/configs/config_privilege5.xml @@ -0,0 +1,10 @@ + + + privilege + + + + + + + diff --git a/tests/general/configs/config_privilege6.xml b/tests/general/configs/config_privilege6.xml new file mode 100644 index 0000000..791e674 --- /dev/null +++ b/tests/general/configs/config_privilege6.xml @@ -0,0 +1,10 @@ + + + privilege + + + + + + + diff --git a/tests/general/configs/config_splash1.xml b/tests/general/configs/config_splash1.xml new file mode 100644 index 0000000..8604428 --- /dev/null +++ b/tests/general/configs/config_splash1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/config_splash2.xml b/tests/general/configs/config_splash2.xml new file mode 100644 index 0000000..2a5653a --- /dev/null +++ b/tests/general/configs/config_splash2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/config_splash3.xml b/tests/general/configs/config_splash3.xml new file mode 100644 index 0000000..7bc68cb --- /dev/null +++ b/tests/general/configs/config_splash3.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/configs/config_splash4.xml b/tests/general/configs/config_splash4.xml new file mode 100644 index 0000000..c7682dc --- /dev/null +++ b/tests/general/configs/config_splash4.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/general/widgets/account.wgt b/tests/general/widgets/account.wgt new file mode 100644 index 0000000000000000000000000000000000000000..9d3592a77a3845ed18ede204656b7011577dd925 GIT binary patch literal 14064 zcmeI3_g56n(tu}|C_zy{5Rf1%IR_CWOODIR5=3$m0RcfIvt&Ue2SJb^AVJcCNE8r} zmmHRifCO1G3M@I?;eO|SukR1{FSuKCdS_-&_jFHnRZpMiRKYa}2&n;5@QBh*RRI2w z3*fc=6Kfk!{zvXkV$l^BTUjU+_k2T>q+g-+6Z1|k6X=&K%#)=DHVKIKc)Z8>NM6Nd ztO4dXEtayCY_zP+9?BpLjXdOryEov%+Y%m@pQ+|ydYligN-Kx^8&@*=yl#MX?1@?3 zI9se4vx7Cs%PED2KIuWHSMyigTWwM?gKLnG77E3TxPt>p1q=MY4wdwLs8-HT_+6gZ zL5`^iHY4ELDwJgOWZ?glYO0C|@T_rONQuGUJKEkW;0fY}P*DI%dl{F(n+q=lK`HBk$dFob=kZ8oMY1yhs^2;m9N>fThVnacurcj9H z7amPp?|i#MuiY4lDR)(2kq||ewZfIo!X5X_4V|-TtUJNkOqlcho#=-J6#{=XCnA`d zUmR^VHK~u{WY;E}&i;yP4WF#9t6PM;fR33xcyKuNTF9!iJ%(Afy0~~%cC^f_Oyy=A z9jhWqGeP+U)~rMX;6A3>y7jXw7*J2n}IvC5NkR zDkvzZ&&iqV8XO!9aC-9OEvyU8jG|?%g|RfAj2PeN(9_u$kpFSlPbO0gkRu9a3t5ut z%Pc49=+zc7aftR7hPpYQO7yts54gQzGReW-+1SGOn;B3fOGiJFR3PouXBZ zC!4S~8nNq5_iADxZGymwvtFoOKN!wJ8W^0ZQH6X}qUiJ}eM$AS0gc|ufEO%12e>b= z>MjCd`h4Oas}hGtMovk96U*cR89hw8sQut-0*B5MsNglZj#%hqxkYQic%|Lh^3CuB zOfp#av%ZPRi4IT-aUrU-`flzc<+ZiY;cP1BEdJ<<2X5G)3A=ZM1@#r!v=ny``@~l*>mv;nog?3`0BVmT%R}8=orE{IW?KqI5 zB%-A2*!1;Qk&dBM->0OM6xmi3QsmtxGgbL;v4Poj3gQ4p+bnP3rQ*%IHb(w*as=D) z7Rgb2kuCeDayKO1U(_$>zgc{bD7>(?kfMAoAo^p2r<6?fhYufaXJ~T(k9c9iMl)MX zKUOz23v(qG!KVZ(2G6%=|FEJAn69Y5t ztn{O9Yhih{cy6QE2Vb~LwL^2>(|^;>TztN9JfoCs6Dq(4e}Z{-(rGSe*z@#T9B-6M z7e?CLM10+Q)}7a5>Xx*|Q~$Sd*22qz?XJ4Syq#OhpHr;GdhHtu?~X{{NU^hWHJF=- zjEof0q|A`~44_smemq|r6MChE+I!``|+QGb{#~GZ5 zI*-##yVsHlS$4tyZ|MFLy}LQFay)VRzxz0U90yT9*t z+2xMJruDU9wGW5v)r%EiLV-WQ5YoYqclhGrl4z zm;tBBMZ=%+gw1!S%R&&6_T>rKkrDk|1Q%hje0Q2y@yyJ@i9fFq8dj!vhf2Tn;rRTS zakZnofdMTy56{5RP{XC53$6QOt$o6o78AEkwpj?M}@SGPD4Gj%| z0$q8bN(mIK^fXUd!`$whH*b8_93DSbxWh=CN6jGqr2CC0d5{ZHJC0Md2FKe3QyXXr z6SE(Lg|u1#&G}ky!WzDzUFVhGd!y<%KTkd*&Ed4{O_%uL#T23Vg;H#*z(R=UuvUT% zG!s%*MGf|ZO>gdt!GroA3}<&`4;B(w!=J4TaXC&mj3dGQM%{;E0j`#{@k$6la*2sI zmpkV@X4x;<#^|r!8xH`-UNkQmfWzS)vMy_9JAsz;S~fri$jZwja&mIIcToc)>I;6* z1huY$%iF70uS&_16Rk_S&)sTIim}}l&D+H*Sw5BHllp`4 zr;BLbdl=D%!aT79qn|M|vnJ z6{!fNGr4VVReoZydDRP?0?S=>J{5xKSX^ix!PpE6vXoF7{^gYA~BPzA`-OcmhXUg_;*34$R-`-UCGUh#26PfNO(C|y4ce`&hTMVPbQ;a!r zA*~Nv$e4dqlh!DXFnH*~4^otwA(Gu~H;tG9Ff%o+$gir}8M1+ygkZgftJmqsFH;k9 zemkTGUakh0;|{Q^Nx$dYWt+DfWV&y?^rkpU>4xOe<=&?-j8ytnB`e##oK|{xa>|2Q zcJC3?SAnuzjZpOXYFWXVki!qCKp1V6WqN}U^<{ZVw}I{bi1`Z8Qa;LL3P_~qX~gH| z78P9+nY;KQK@-hLQ{|?14T3WBKggS(a44gRxpCLyUAsRegG)$k)y(EhvCX@6YV~7+MlCwW>$LsY7LT zD@7(nMcTKuD!<%ay0vtm%0jg-lcdg7?evCsNveh7koB6uvkKpG6~8`%W+3qvM*rTu z))qtF+uR8qQJ+KPPhQQ}6GO3zd6hmB>3R;0^p` zsYOMmZgF8W)Z=;gJivE(<20!^ii;u{5&$l~B`ONvW1uj+4H)YtQlp{=-l;IJZ)}|2 z=;ckX>F-bf9l=5x4({;$wb>J@?bEK?cHe-9zs5S)*=q^Y7Py7}B(jAHQnvP6Rv0=? zRN2pf^M4`Cg4#sx5kE}f1(zD)4NNT{rhOh-DL>Wi$&4#7y}RwJeVXDn@>RU~C3?I= z3sCm)siz?e_2@#U?;iN-wh9LAbiD=~sY*0sAwarP$HZjG<<@7cr#Ez&l<=apa4NqR zhPn%_86)~pB+0G8lw%UBPfhl$5IY~8k>o0GJ)>L$9pA%o!_?^SBM^v0P4<8?PIVeU zfs5wpl#2|DT}nLgIv(~hBFFQgE_F0y&`>VboZBP4W!e0MZZgGUHqeh>+;{pnIl6cr zW<453%XUQefD0w8psFTH-^iV$9-RpD`e2`?wCr9Ng7EI`N&t;flZx=|e9i62Y6*F& zXs*aFB4UH7xiD#5!Bx$ZbZ&+8z@h7$-lgJq>0ViJud8_2lo2?=CQAYMO;Uv+^oJLm zk!(2woVwIX7m0&M!7XvV{X}0;(J9mTrpbDWjn{jan!$nZXV(bDxq3J!wLZ0yD8)UJ zEVK~=fgQU-Mp`Yw3U#o1+KFt3zr!w#%V}D9!|hlVkCvj4H6MY?`8jNKa@e2QR0KRM zN2%=B?er%O#(zhDbuks_KE=wZ3$ZcTc@(^atlF+5TXNe`D5iy7NoKQI;C%!kZV$xn zWDp%}Y}xM|3+G;4lkD=RAm(lz>d>tZA$bk$`&vLU=p~R(S9IrvCnM^To&Is8W)SqP z)^78a>%5s9JHNhp5cm43v-I2^uJn=>G53bq!`V$vFU{l}yN(#Sy#NUYpr)j)SgK$i zbdF8z^;@1pE(5?Z)t}g8;=xVD^P2yWb36i46I**beorUIjCd`bRzZre%_AD!4X3eh z5Tp|aKVxlFMx0uSIiDG9xpN;^Wny9WqLp9wc=0#Sq-*V=Xxh4<;?wH6?c->XszEVp zWSh8v&NzYDa3%VkvW4Z4-m&y8GtxN0e5s&_5@fBrG9%SMAhk??v?%vNcWh7Zy3RY+ z4f68eqt&Z*4aST7oEGy7*EtGQcS3g!Tc}LpaZwdla@|X`x~nAPMqg_wTr=J@QKiwz zZi`#DkMBjl5PpS}W`dhu+LGGSPqM5zB`KqP z(Yg|r`iiH@Nwn^$M6}B{NMf7iQ`WDYxJnc=ACaSJpT6o2xrCpm^bzs7(zArZX{8|m z@CEv((jS}tQ|bT4YM|DI@URLGtMIT253BI73JNdJ!++r>!gy|i=O%b=g6Af9Zi43~cy5B{CU|aw=O%b=g6Af9Zi43~cy5B{ zCjX<{1OlZ7{!ZljGjHlV;^d$5|7l%tjej=#Gj8ZSsp@Yj1~31UV)bXM^O&MP17FT# zivE^4Fb3)G2%|sSorlHz+1oz{X#nP7o%iGbU@YZwVoiu?_~2kTv@fVq_bV>|6QTm$3_3 zGS(EvzWg44{N8WxdtL8+uJfGpJl8qrxzByypL6c(gU}=)WdJC_5~Y)(2>c~fVBhhX zt(~{f6Hgb3=!%PNoHWXNexZpnZ*YbQ`6ri249gW4sMCX*gvEQkKHz<&>4_QZfaMLV zWyOMQ(R{7$Q`ri=tz&GSl04nC!Hd{)5*;(=6E1WNnZR=^i3H#HLv0JuPZULing zIx_&!<)|ww=zC9Yr2D!tjAV8+efZ#8>&vsP)_h-0{&MgfuBH2CqylZs%|WM)Pt?YG zemU@j+CLDD#21=f228h#3x@=2*NzNu$eS~ARgo7iuO_KXs|-sF2b-HiAzH11TJ}B# z4u`1Sn7h-SYNBEoWzO}Y)vlr)&#Yg%XEOv(lC#+`*9EEQ$AuNb|1>8cIhtP`Z8kM& zj1lG5r<%_GdD|8~RaaZP1bGD=w|MmEaQfYCo34(S>vGk_#cOh7Wfo{%wP8+RSe#P$Wo*>Eyy`jA&&cVAfH$31`9ED1oKEQA}ge7`@du%yK7 zY%#vu6`&)f1300-%@PLq@)sj`GYgB#%Fg8JFvEaVI+J`RL3eCqq+x1cz|F>8-JObr z&{UYMr$HhiArVMJ3YEJQypt|jP08Xrv3T5^2>EezHk4JyXQzooL`VjLWlA2q>Qj=ql6XVrTp;$?q6|2g>X|bH{lwS4B)k&$+4n zCRW$@e*fph1pzeJyN` z7o*>atGB>c(V(kTTOV8cU&D1=4?Ny&INI&4(ds%R1q=*`*D?u#7qZh)-+oMd%iQGN zXXxtcI_>2ySbWZDKUF;|621|bh6WrdTd1% z)Z+w9Vjr~%lyZp1VEi<2%(5#!$H-`>Kb+`92HmS&P0 zK69RMuj!jIn$H6=-`a|A^4%V`{$9*qrqFba@Tzk28hH&~=n?dDOuD4FdWyM%Em^`I| z2s3Hg6BW^v`wr4M2uh4?iS~`RZh3;SowmwLm)`@5KrO`9JBDc|F(KjmSTd8}RwwW6 zz*9ePF&mL?95{^b_1A&H!5|smUCX20jqwT_RRyM_W|B*mf9AiIyVO}C0Jk<;$!5a# z-1`_=MFRuIp%w98_1ik>Qtr8y2+%|0^(Zmtsc$VI6z*eYFTV`sW1nX59qK+!HSbwZ zBIW!^%i^6$c++Ip-5tIIwE*H}_CkcEc{Y?ST0~oW;M>TG9P45ai_a5$h9661ZhT55M zV$cK>DHIZQO@i9yTa*6#P3kQF-HH0Rd_L+7S^q;cmIB)!C+?ULJqEc?YAKDT?yE3L(X!7O6I>MS^8e(0KUzEO&RkA7 zM{5*|*yE6+9lY+s?v!i4(`#r@Gy3uPcE8-_?se4S+u-OgKa)%m`ue1J@xh^?U@acJ z;OjZ5XZ8Jr3-dkCfuLrZM4ob_#VAp(!DKwtAcN4Q^Qa? zgJXvk1)6mFRA%oxK5$u!pQep@fH}$T3l`QbnwpyWTslJK_0=yYlCyqd$@oE+sU_o| z3Z%{VX38)~X~*(-!sw`B9+IClM4=~DqIh=p;3NQUf`gUmOVJsYKAu=OGp%-3Ffw8i z5EL979>zQok2Hpl67D9!O`rgRfeiGKNohFZg1nXaz-Gxn6yTQIBwaVg8VRCl=ILys ze*#j&gz-6I0lvB0LUFz3$hGlcNjA`tm)To@-5R*f-87$uI|pF2pN7zKeT}+Rd>I2QAC8mQD6Diw%!JLI*RtSxvI>W?nSz`@okbI|Q$UKUJk&zKVgHT?qQUQe^ zGb315Kfjxnmgc+e^z^Bs6dQRy1FPh-o;2?Z!ER(7L_YBvBD@KvKG+f_;Wz}tv{?bo z1=?w0_1|#r3lBc{pzAgpr(RIz@>%z#-~EB&h)`~&mDnn@x-EG4^wYU?jbh3zP-2^Yh7ETwIhcYU0KHzza=8n;tyg-n@BJ zN}ZHoThen5tG)Yd9qu@IH(a`WJTG*GJU`}+WK)}a`dwxw8M`iqkEWk5;^0bn@%qEn z;)boVoTsdJoaOIxP;j+}5_`n@Yjo_VYhR$cyAzOakdsT05&F?yT3Tf~Qkg6P`>Sk;`$#TG%g4MOHLab8~Y$GAppKWyA8|?ruCh`)k19r zFbh(~Nd}Q(`2A~W=Mw=2>bLXr^AG8Hb&hTKeobHciTs)s-J&xA zM;pO5g1jMAp^7Eod3rHV9QW|B_Or3>Rk*QRU@7HH^+xcGbr3R-jEp1~6Nes81(8tr zux+tbO!!?9Rs_>y#SO3qSKTB_||N13Vh`SP@+!)cZz=K`@0?5nhn`a?x#MT>e zu!G{_V%L+E4_Tv=)iUYH*N7A~N(^7yu-F2?6BsA4WI{Ip(I{RMN1phKYpkpp_AZYnt|U}0`vQBYO2Gi(Pl!w^s-)f>zgE;EqxeLK7cyj}|_Cms;i z68|i8$Te@*%l6!S?L%{v+ylvD&U;8>9I5iVN=~k4CAIYM09ON%dI+ZcTu;24~-w{B|>V}D}n%$f&wtK(9fZL{1jlEUz5?zvMRURwM zc9o`W+`z=dB;asoWzvc?63Y2j&DKFWqYt6+f=6$IW>}A0C{!%#;XfB%7vLSU_#!F% zWi8$mIKB@L^1PcJpplQn+L=hlXX^ki&K}MNF-#QH>QzrfQ-;fGSFvVTtj-?Xo5T{i^Jv*@){S%C~4BXh+e72}N6 zG7Oz;iq;m*F#n~(HX!-Y%ucaOD`{;_g@Dak{nM)#e4b2JttMDd%p-N z#pNl5Sv^V7`eq&H(PcBhzR6@WQ`Wy#yU9jT4KY@~S(KcV!~r4|i#7Ln8^*{mk+0+h z?j_A-N`H(PMKLT4T+AfHhVQY`7~cU*^%5A+(Sz?*uW$VNb;{lcPp=smNdFVTNf{1i zcmX;*@zsv0EcQL@i12p=CkIDuQKmwV(4S=XP!Za;0qY85=gBI^S zf*}L-iz328bVj1Pg6-^s8tB9xQ2?fX?I9A0Owi&9EaTH)1QhuhpHI8Vaylf(0q^2q zpCWR-AL}thLxzmyQ!E9%(py$6Pnf5Yt>%LKg(UrE{#?KnFTiZaqL{dks2}m8MHSW5 z#jiC8Bx*z_z)&9@Q&m9lfeZ$quA-w{s%RN}PMbIyw!DN~27qI_ziAVR z$MH(%BmXPs^a#kB*gHB1dAm5j(QDCZ=VYkg6Qop)53Y5})t+iV?c_NqEz{3xK!R?4 zEBjpDJ1H^foi^9o<*7(@(sB}aKRO5j-5p|quCwtwpmH$qD`%sukSq+t@y|J=( zyROJAlkM)q2_sCE-4JDo3---BM1+*2%)!}ZR>^&3p^g^kOfkCV}IxXKfrsWTfd zo%isv{J~_I<+|PgCq}{2@c4);yo+gw@~e9Db0=tyFhZsM!LhF9C4VB>xqV2?=742z z_Y;tF`#_)!z`q>W-;~oi2ll_^e;Bd&Iu$p>2~NK^s9e*j3@S~UOw literal 0 HcmV?d00001 diff --git a/tests/general/widgets/app-control.wgt b/tests/general/widgets/app-control.wgt new file mode 100644 index 0000000000000000000000000000000000000000..cc98093264d6a50e27e4240e4d58cf794d183976 GIT binary patch literal 5290 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/appWidgetFull.wgt b/tests/general/widgets/appWidgetFull.wgt new file mode 100644 index 0000000000000000000000000000000000000000..b228a23c226f1c80199541499096bcef716420c5 GIT binary patch literal 14164 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/appWidgetIncorrect.wgt b/tests/general/widgets/appWidgetIncorrect.wgt new file mode 100644 index 0000000000000000000000000000000000000000..53964116c1dbeb8f445f06cd87464fda882dfa14 GIT binary patch literal 5100 zcmZ`-XH-*7w>}A>ND~zV1Q8_C1*Aw(das5^73s|g2uP7mfFM$&7wJVn2ucZvNJmg! zdI`Ns4ONuh`3^t6`+DzMch8zLbIzKXvuE%9%Bld02x>!wNvDQzvL3wN4>DN z@#KHz<|rCfPTj&vskrYOk|^~Sr=O62dWA^8On#m`J+M(gq}$^I-beD zN6ALZ+U%(`($L65cBtzYLTFpU!?J;99BJn)y?yz>QOs* zqnxZ#Xvm9hTzVCMxx!kbiWx$Kl&t7p^spPKND5dH|F5cK7phu0z2J9#VFx*(A=(T> zXsb|>-yjFSQ>m#cBEeSUqLPt-PjPMURj`4$B30ypk{;$2@a2-Ts*x)IP+YsH5Fj<3 z0RU)n)D-1(J;yiFeVl2BGTRzIeDJC9;n-GfdZ;RUC1@7c+<7Nljw8 z9KM8_KM?gqca07``n$!21H3hB$9g#AooSiMaEdFdNlH^ngQ9~$rlwGc<~LqVTkit9 zBd@(^i77W#Az_Rn>w3{@N70U3)~`qB(*!r7^O@I9^WsrY3(E!mX-Yt{G`%_AY;05? z*_BzJXgvQXwk33;wx(tY@&-C;_T+zQz^^)EF+E`dvSeu(W z*EujS5a9UYMJBuxcMDC={1whxe>!aZm{a%Bfq>l42Y%97qJS)MFgs@XVQ*I9r%xv5 z3-O&!01b==V1@oRN$BIsUkKyOEG#N5J(s1y3<4JE^smJE|-W1 zjfL4d>O`WVq5+gJsLbV{opiBEXYomi7n9X8J!xW2d}vE~)tI8?kk6Hkrj(ptOXxOeN z-K&w6tPz4B$$q28csP`;XJBxyMuTZpqU`V}iJ*B|hr?}UAPSdX0X&!3be4eE`gg@X zS0)S%51)|&r}aGK-eNu?oBMmD{25_#|*_gT9H$ z=_8;7;!Ip&HEHhi&}(a<-N{teN$lBGkKG}IM#BCv0XU#SBY~}KD3g@;P9u>ZzYe0F z#aT~p$QZDNylX{MMPcq5mSv|9yu8DptMuaw?eQ~2zoP*TD1*!NeTV*BC1E8UhsM@B z*hhvBd%q+mC(E>;^@QJV-l8cRDmE}NRZ2|HoVLmM1H^qxb}H(t+j zVszVab!PZVN_3^lx2NWQH*jsY0#5eokN3K(H9L-AfS%s&jZ8uSMRvN^kDp^dGB?=| zXgfMO&brwP7GAR2PE^eZMlyM3o{x!ii=$!jf}B-(^>F*?IxkTZ6B7e7?df^9B3BzzrP$_>fZqCKd(q6foLHih>lFMZqP1KzO*nh1MXU5C*HgRgihPVJT$W5u zkO7wVOi3u^p`Bz7f(&E*N$Ym3a~^L@yS3udmG^)=Py?~{jHcg7Oh}jeDCY;50vuRGe6$j7%jI_k)uCuBD!q;XYPBMW34#?aBiUzYa(pV zzK@oc*VCgLSP}VEx2>Hn?viVc06jEX=Oyeg@uNAI)Mdov)z^W1?DGt+qestEO}o~U zV62N&jGmc1IJmmHMyJQp~0Vh?Em&R)$}d^YfE>Vr*RQ`Nz`tU^dh%3unP_aI{w1-`_v0{wScE z@F3o;Zg1S)bG^I`0HURjxTMbx=7a96&4%70iNEdp=R3XKCBZ+B)k{fYte-DqXnw(3 zx6ZH>Am~j?KX~t54^v$J^ksTngFMS`Z>%mZpNl+0+V2RBB?UL<+J-GE;$D8o>=|nzlJ3ybD4dexE%bMrozppUnwn z`4-72{51G@2mfe(Z^Eh9J^~upgnl}@-7B-Xcgt%bHYn=rVv;dJR~LpC=^q#f(&WJN zM$C%8sOu$A%yqp40=Hq-bLIzTc#(4VHT{PNKZE;3r~+ky&ZN5vf(*=81I|zPXXcmt z-)rynZ?^1sHx(3c>_eQ!aQVFi!uBDb{F|)6ILLv-p7mWNof?vD4(am~*L`{0?$nUg z)m64Yy686Ww@`rLo+WIlDNO@dnF1F0JR<)-=^z*jw6;H-4nQ5&NaQMt}mIv zvcu#Bnsn++YJUS}TB8krk3Lt2SK_3~Fgd!+pElm41KlMcd&bf_}wIfX7AeyG1&ouZY zAk~fNULxlCHusuIZ&e>VH5@KU2e`J~d{y0ORPXKHV82Q7n||p^GPJr!rv(pCQz_-k z>FELgtZZ=QBlPIP6mJs>xYQd_-D{uFr-%<=2t zBcW5eSArVPp17+P=PkQdi^-6;vSP1tnvwqL;;}v&I92<~YOXcxotP6#P%|sCNcVD! zwELenkPk&GjZE4mq_(4Un5+tzxlN&Y5hJ1 zw9;PJ{lK}*D}V4t*KRgUxRd2_S@xt${Pbc8Q~X9Hx>aa#kN4=S1UnceWNeBW9Pyjp zJk-Gh`X3Ev_hb$i;@LvoR|mNrrt8M^z~ET$tPJhnisM{k{^9vtg*!J+2!JopY5_BYDP4PTwGe9_ z%)Gc^l3utl{@@1M;gpA#Ja%qw?(sEF?Gx+$UsIPCk>3+}4QiUm*kWuL^Yil=#9lCi zc$&l*;YA+qPr7;@E+%hJXU}Z5`Rz}IuHZiqH1*Q`1nPdv_iWei3}{quR86pU3QhYT zZvwgNuVtuRZu=F)W*cP+5F|n_#Vn?W-u26ihW1XR}nR2s5 zFdMsyru5{Zy@3!+6*%(f=v`b~EV;Y>>lc3^?rNA~Ls;892YMdxCn>wuGy`GY{dPMJ zepp;w>~y;FA!~TNN-911#x7~K0`2z}EVck}14fC=>CyE-lozKFiX*=K1{2e(@?;3v zYrIDEA=QHuf0?XyYX{rWc%Y6#v8#d#I9y6|ngfH8J(^kKCElE*h~9@CGwR>is5P<+ z3m&}mlMHQUsK?>DmrB9}n3ExNRDhn7ie#(4R6b%3=hV zuT!>+>KPds`5*19j9b9Mp{%j0)^?H^JqUGo4xJ6kK^+qQ5aF!H|4>8};89sz;iP`j zW^W5n??VIKB(nY0^O0B^Bgyz|ZNSmN)j=4WE4)LN=EPlmREsr-PFVa94H zhDJI?bBl72`*L9m5OptceRoJY#hX=>|@BQ^jsMBGQZmv+PhV=$xK=WF;u%#l$?~r0wNWQHFb@BO-DPHuiydh zCCza{cVst`bWi}G&LqZ$?lVyu-Up0z5@^v;{qI$7ZT$LmcC&{sy}GY2{ZAMxStyv{ z`D=5;SD{iFZM$wFLf;YW?NC}m^o6b=i^R52L8_KM%W^}9@k-PT=>JQvEoe<-pYg-x z-*BrT)8J|W(QWh43c0B+&s)2NrVqA#wa=1Whg-#(B5-5vT7a^TPc0pJh({+beecj$ zr$sR6V0U`JnGMosZ6v+8e;hqt<)^4Ol>H&dIg;^D@Ve83$5JY}TiODLJ@ZiRo~|O) zirbSBk&S9lQBaz4iO^$$4E_FIUo}2FFl(RIeU2+`oE)M=V zEZ6g?4s8@%y;?^1+I7=Zaor7&wfn)gc~g+uc{_; zqn;;GJt_h2^%0e-wBlBSL3;Oe#)HAANdwEAuf9J~B_T%>#U1`lSacvI4=%M^cun&( zoku=BaFCJ9yF}~(!&@t!4HXZYQX)tALyP9OlV@Ii&`ub`TyUhaMGYCmrAYmti`0&>jYUe~K@7lU#r#~eL zPs?DtPA!J?9kjQ#kaWOHpnw4@{>GCTec4X`BwRBHnyIzdbd`}Wi*x7q4-b+aUv<{* z`$H98GQ#HGa1?^WmwXx^P=>WZ{3- zY~mRz`kA`B-1bEhj!k)YF7;^G^Q6b~Mm9O~=An2_wocwn&H2^yzNe*v!IeUy*(!GoGe)v4_4x*UhW>yC~#e>5I4OdwzYq>(C9mEp5gd`6?bbeJ{wb^~Kms zIR|dXwkU1ua>N;t0GZ$X80gN(PI@cDjn!1ZG`2uSF2K40a$(M+ZPNvM2mmNSFU$#n z(gOcdW`8qO7nIrmmj5Bp{_nKEJ#;}|{VT;__rGM;-?J{f_qQS!-uqYPKyUx+ON0jT S1=9r(fh8Yo?}5A)0Q?87(r*p` literal 0 HcmV?d00001 diff --git a/tests/general/widgets/appWidgetMinimal.wgt b/tests/general/widgets/appWidgetMinimal.wgt new file mode 100644 index 0000000000000000000000000000000000000000..2a7aa5d7ba2db0e32fce11e847ffd90e8a3516c0 GIT binary patch literal 5098 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/bg-01-missing_file.wgt b/tests/general/widgets/bg-01-missing_file.wgt new file mode 100644 index 0000000000000000000000000000000000000000..b14d665f89aa33070d08f331393188a5b6dfbef7 GIT binary patch literal 628 zcmWIWW@Zs#U|`^2kO*Dj;>}Ype6n(V z=W<)kw+%>I-E__Jxa#vr=QIV`E%yvWW$ZuIq?_HS+{U1>eOvFc@YNGempZ0%?^!I! zUq31K`o?$jT2pqkPnMgrAa$Glt@+!(Up0;F%WIkLH@!coc9uv~{9doc%bWPu-&kw1 zA|=_)TwI{jLog$mM|1MOWQ(HCM^E1QI0v!XUSD%=-NHRGf;LWCOBp>pu0?FAzHNTw zL78T9&YDSk9|=79^YX){{~Pl2PL-wE6dphE%{fKz>BTFmE}oa-*ZS_d^^beX^hZ-1 zEPt^EK!ccpp~r8g)4X|mQoMly4#HeOoSB!BTA`OwlAB`#BJ8*n6l{|6Qz}6WATz_* z&M7fDJH05sG%rOVATd2vp(M4qM4>!0B|WvoMjt2-(xDI00_20u2=HcPl4HgduM$9O z85n?3%&?^q#6pf`R!A(P#W2V`5wLmKJpnQW1hzCL1C2#^DZrbR4J6J4gdITo8pu)x E0K}u(N&o-= literal 0 HcmV?d00001 diff --git a/tests/general/widgets/bg-02-without_bg.wgt b/tests/general/widgets/bg-02-without_bg.wgt new file mode 100644 index 0000000000000000000000000000000000000000..ef45c950c932afd9cb3070776184561887e306cb GIT binary patch literal 611 zcmWIWW@Zs#U|`^2Pzzn*l&bKQm5-5uVG|H@F~~3^=jWwmrt4MY=7ffDGBB^;?+OIr z(h6<{MwS=M3=Ci*c7mb*VFQ8I^iSG4{T@eJ#R5b9v_hLY8y9kzJ-MO&+$CB1cBa7o zdEU~F(f;4=KCga1FQ&fSdRzO93%k4SPcuGuAT$5M&941<^YfAuIiE@mihVU#ckBBj6UBS)20iC3e*JsT#y?ISEAE8VY+Pb}v*2a^ z0|TC?2Ku#I4lT`$nzbPGn0&$iTaRB)TGOrlGKBrB+e?4x+V^L=HyKQS6tHw%;}YdI zpFBSWv5usb{0EPpIkxr1Y0W4DtDsC?O+)#MN|rmHEz)~(@Pt?E2IsYIKPCv|d`;eO z{{DP~Sy`F6%;pS@ZiiJ%%DM`~94273?MVZ*v=_2IXk^5zcepJAs{h5RiPxc zxJ02mGbKH>#6}+|57MCz(E{Xy%?R*jWRhdX6^#-=YZ(}TG0L!|5yV1{R8~l&qD3gk lJQ1*Y*gXL<1q8M<1_6ylcqzb}l?^1$1cV(x`Y6a!1^}-c)YAX} literal 0 HcmV?d00001 diff --git a/tests/general/widgets/category.wgt b/tests/general/widgets/category.wgt new file mode 100644 index 0000000000000000000000000000000000000000..abb0387544163bdaa888186776e035c308e45f78 GIT binary patch literal 5031 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 A`v3p{ literal 0 HcmV?d00001 diff --git a/tests/general/widgets/contentCorrect.wgt b/tests/general/widgets/contentCorrect.wgt new file mode 100644 index 0000000000000000000000000000000000000000..dd68ae2d06e7f52682a13deea78c0fe450bce616 GIT binary patch literal 5030 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 DUFTCq literal 0 HcmV?d00001 diff --git a/tests/general/widgets/contentIncorrect.wgt b/tests/general/widgets/contentIncorrect.wgt new file mode 100644 index 0000000000000000000000000000000000000000..a0a7e07549ce95f5070e5e5475d46f1506fe4b0f GIT binary patch literal 5030 zcmZ`-cQ{+i*gMza7ZuF=ds7MM}5&y5Ml;^5iI5`S9JK8}0z(}^j z5ZWp4K79&>Xnwt|Y3)^D zbL6=fEj8_?DlCFgV%sQQ>nh%L%lfHvHbZbDIhze}T9Ax-QdBAUPfG%lwdKw6R&%rZ z7*TFxs`>1n*w)ag`ntMh$Q$VRv&WB*rr+JQ=<0~RDOXccvMx7P{;XW(RxAU%5=9G1 z#Rc~41SH@#uG+f2)ExwBy{Te3IXUcn;khk8Tz|_H^7{Pb$35kVpPP${it2N7 z=evi7hWs5I9W&wGxSMEtmd|ju#*jy?iD?XKZAoacW?|*}_H5H(jFIS@OG-c#SK~$^5q(_fMn;N!`gbHg zRws;%j-FBgC+0~-vU>P*QQM*Qcy1j>sL*x!&KT%ag;{ISM3v3i>aEatd=faeN#Dfe zLGV|IN#glccjAaaGhzRj036Vuk-&B~lv&1Wx0yspKo`-- z>Wo5-7z5UjcWr2zD9jzhitH4Er&kzsjec^mGk%ujS2Vx{WpJ4hw;#+^7E#uKNYd|CE@VEZ2%giM-#s39A?>F?hyEP43TPo$UogDBZeeW#q>oPqGtdmK3=k{^j6Q z{)V*MoBGv)wBbSH;j5|9)hb_~nEEo|I&S*^*=s!B>#f!7IwA#7C?ZoP!T(}*y63ka6W=noI1XsL zy1Gt#ISLkEvRO~n%nC&^dt{zXi1tdNN#ljMYw{Z5b~6p0VkRag2G6v!(~o;Bg%#A| z_>5v6f8{IF4$duP`lg+={QBpgS>+_FU_lOqBmUJ%m#L6p@5^to{E^Pxco|a@iA}FL zH-7hNP8p4tewneB!mC0ZF1qCWUE2?vk}bvhY#WO2jmq3ewy|(An4b&}55KEPlOf#% zpx4cQyxthU`&J9R_dE{%Wow_~Ue-jn&{t8dmAVK-q8nNj{n@aioi6E;Yl;9pG~VDTVn6lmOAv+2n91wUL;2Vj89YZiFH)cOY$TDg zEzvM~WD?#sZ5Qp&`e$7q(!}RYPfxo|)i}6yCGdLgRG~`r2LnV22V!=kwP(iF&j+QJ zLUd$^3EuXuuCCGPv9MfB>zoAef%F#za1FqibUvY0TzV>mckFCQEnoKx5F?;Qmh*&`m=(}RUT&h@#_o8JjU|H3I_I*v~Bwo zdl3Zv)AB;Ez3X90E2W=iCN-$CeD@|A;_`W@GGu*^&{zs^bFLlO;$q&Fcg#)=v3%i* zy!pG`g3Wnx@C=#m(oWnTI`kOiKB=iRnyRnTAVt$UXYBT(aF_2#kNZ(tY2tG^p{!pc z8AYB19q;0G7WSr``t2g10WIh!<2(IwTYEP>7h?mXJ})I1BlPr0@uGu6LxGxH_}dY4 zl8z1igp2b%FM)s^Qp|;GIgInJsmF=?vCg zW>3(h)2A}~-|_w{n!MDl3$`RK;pGeI5h$Q0&9j2BImlQ}_ z?#+~AkkYml@r2P){X8TuX^=uss#wYF?BR(YzYz{zt|tl8FMBevaAsU%uV7$6&v*Ow z;P5c!xoEf{|0qE!iQfncAZW=z9~qT}A}-3CJssF89f$;+bDO2>$C$%GG|jx2ZSqY( zsvFV0L@e-c?R}xRS$pi%bhs?*@7i(Wb#1dzqt~k@yDjox^vhS0p|yRwt$5%PjdH#M z3I+VLy2+D|K+%OM-69n5s5jg8u9r9e+)KV=JW6l^@6ENnSx-WS57GyX(zwu_W*5Rm;8D?CPUG}f}_T1R`!RB`^I>{bp30K`L?ik5>BjvU)YevdY4;e zU#;l@Eu*EzgA)@eJ3fJIMImMg_2PQ{#BNzbb!dQmr)ki{k=_`%cc9GfL+ee?JIKVKWWLMJQir@0$ zyA(8}|G{u>PwsFro;~!{+Ay#EOv3~U%s1*jlnC;E*_fz;02G&5`SbX43-PPINmfSx z^xb$2*!SW1sQ@C8_=J5`JI4Wxq<3=uvOsozKAD4q!-Gp2cu^nlLKD)u1|Dy3-@YxQ zN=mRS?Ky|l{(Y7X7aV^#zjVcTUhpb;ezdqmbE`|b6a&4CRTu3C<4>1x{15P=4M%Gw zjoalpFPO#cIL zFOvrqVXII@6v z`i?Qei{9V=?&@*4l)N*OJ-gN6yFVSeiZ3K+qSAc@8-6MF?KJKVY1D4kOtEzf&-fj0 z23p+a4!RJmSPGt}m-0k$kB;g-8R}f;H+1$dqnxSP47{}gLgul7fy83s(37bE5(+Pt zoz}~81FCT3K&6$H)ftWHHS}u;KZOu;3j=plb|Bj?FgU)`UJCWsgkL@T(O$|!HUC@k zi0bj3Zbjjgw$6C&LC_K(?Z)a}#FXbFdEH*UA{I}cs3mT^a9{VUU98u7Q#aOdMgfo zSW;5rbh28QH9A=%lb*~(q^NyB+un-B765L*IEg7ex)F%-TwW|4Y{HzhBo_vXNi`!zRfjS)$M zhA;e}L_ae`ak=iLk~0I(o<6NCsIJ}}wt|~r2%aM~n+zAP(310fJ7NMN)`KdDhlKUS z-wPdbEjtaeJ)99<)W^v^kUWOGN7RPl%D<}R(ocPd|_OEb+XVgy;P zQ@4(zjEs!@j&@fk%}B$cY_Y1AHqse=2=!N7x|`I)y5s`EB3Y0AxfoG^x6R@Sr|^}1 z_O`(GeQ1E2RJNabJ`!tXBpsiv4LI1l+M}ZBDX7${p9`l9m)EUfO|V#PajmMa_f|Mp z4prG;2eOIkyfqGK{44jrP#>{hH+WU)Q=#J9Z_olHaN_kJJZSx5$S=+p-x=A2Q8;syL)LKdon88%QNmasZ7=TS>Qoj z-h&Xc=P8=s%;Q|UECz0D(Ob-v_ixv2u~5`N4AnS`larEIL8M}_PhDd}=x8VMAGm{i zNpqOe8zV+i3=0C6GRd%^`^?ma;()Pk0xddf@V&~-&7VI{Z}jn}*A5J%{|;lL3z*5k(02qo8(S@5`XblhB{FNM5KZfVd8MKKWVP)q=>H2LX0#^q&jsL$ zZ+O*^X>c|F=#B+wmBMt7$4z3<(|bEU+Na5`qiqr`5x9v?E#RTIcRd|duzNQyeecjm zw^b75n+Hkt}NJb@hTU+q3Zvo2N*Hdh} z>h^d{bh8%JR3(a~7@%CMV`Vkxb?rCS(;L1*NqWgrI7L7UPuq>tj28VUlIYs-lzWPx zPfPWxn6MC)k?5jeIs32{ImauNslR2hJ{MX5N9`|yS*oFjLTN0(Ol5_!-Vm=YJ* zPWEH5r>qlOCY#Ato`rBVgF~MtmoTOIdIS%xKCQAS^#h7*oDnmL4VPj@>SvNQ+90>o z6S+=5yFEJR)6|Lv@fa2NFIdcmx8Rk6Tn+|#!cq>5$N0vJ{xMK)R3!599E0`&mrU;0fgNQvcsR-w!43X^R8`3cl%M3 z^R*6l>egc@-a-4@iYSIW1q&Fkl5ad%(3frW|AcD>LNm4YTCOtkXL0ZT`sPmF=cCTn zD?U=?DJNp;1-C_TnVepp&HdvtYUKI`WH10VWo@N0Mbp4@+Qe4>%WKFL0Qdv@n>L}> zQBpb|`CmDwM?luZ+SW$E!@>S7Lc=jg2sZ6X)qOmk=XQmitv)l`#2|@LO29*NL(PP^ z8=Sxe<3u)kd(FChJbNi~jDeMQq&g}0F=p?TDnHu!G?v^OwUzcky+%yFr$$T;Mw6dF z8mpuykU2oPneiv_pS0RRlk#@z~57 zD8+R$l=Nv9hva;EJZR{ic>*zsecj)*(rz`A?N&(5uhs`;(GizFZekLkAFRr1&$c^# zXXG=CKPV{OZ#(;a=2>z$#MSKw@hD?ScK=(EbG!d5^Pr#p^#VeJ?3|+lNWhX0w(D2V-~IzI C_)v2I literal 0 HcmV?d00001 diff --git a/tests/general/widgets/csp.wgt b/tests/general/widgets/csp.wgt new file mode 100644 index 0000000000000000000000000000000000000000..10a7d2edcdb6834cca163a8e97ae39a1c8e5354d GIT binary patch literal 5062 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/inst_nplug_1.wgt b/tests/general/widgets/inst_nplug_1.wgt new file mode 100644 index 0000000000000000000000000000000000000000..8447f91b1fa39d7acce586efed8bb5cd475e9dd8 GIT binary patch literal 1293 zcmbVMZ%7nD6d&(WItvQA4;hui8stUZng*5biQZ`xT3&S`hGFa7c)Pf_yLUU=osl2R zAkq>)^cTXSpoED2DM1t^fk_|=l1iEcMff2CQ8M~N&Fp%mqJHQtGxHw2_j_-CZ{B)~ zt;_5HfaQQKYj@xMP>|YBto#H3lEHf5SL7No=!}J=GM^1tpR8&+K$jj{0TPytTLCaO z)LL5How>5R@5`Z6qenKJ>=CBhral&U`wqVCe>{w|-qERnk?5Lcom-2aRSv$i+k>5HDbe-| z-wJ-7lLx9E-no9hX5>)x+NagKJmLdKg(IAwzGC~~P@#V7-3#~D-xhb<)vlhl!SCty zZvDoNzQGoMlRVTs_Wnn2&X3$7uJX-hc4Kq6G4t?rwe{C%TFF$-t1~|*@BOxif9am` zdW(0469CTczv9knNjlp}_@FD9a3jhA6mtqVByoFu`%3bXArWp*Q9+@p8fFM#9ts$% z87Qoa$6Sm@kuj2ShObUV3?#f#%f(<6!z{(hg?@pLXb8K?e7iGtGPAI-h$WQgmGqz} zYmh9=OA?PoMTQt@7^ZJ?aIEQM*20>_n1;KwumWnhPC_oGn)lZRBZ@8uGW?39L~?== zgk+M-X5)nHymB!RL>NXzEI@T8of=V86p`xUc~O94i7Smz>AtrH)+svF5Q10`HK_7I zsv()c7-%Z5X($j6H_IlVexzwmioy~YN*NVSh_JIPQC4*fF=339u4Amo3{+%~B>HPz zOqBFNi8}p~qM>w0E(4G8l1?0pVj_mTsxmW397`Ff4=u()(IOjwqtZ*Km096jgaILL2h;Wn6bE{;P9jY~%ZzbKAD SZ(4fsuiMi$R7VIX^EgGhMGDHzzcNlY#jbe^(#~ zmsW5yFtWU0W?%plsT2JD4;%0tJ^v-xW#8ltg^r=O?oKFk<~Szk+IiL0`>xK%6Avy= z-pcXu_$2;m^;Pe0Ru#)NR~(-5*F|>C6J8D#YfFD;_VfPVua|FlS^6>0L2T;MI|t`k zZu6QNl(KVbu#`yZRs~716{-Ilzx1BF9KR{wMb|g&$Unt7Z2pl~(uA8HO1T@PiDj=% zTeS01nUdS1dPY~jZOs0B=ZnrKGn6h&KY#6r#GcqMdtN7;?ti~}PxZ~-Pmf$TO09Qv zN#;*GFuz)WvtNDwg}wiN`HNnUH~(^0JpdXg3=BPfGo233;Z!;V3=k0J0tQZIUP@|( zUPei7j*Ul%ua6y<0ub1ExH`H(m|$*hYDuC(MoCG5Zfap^W|^&$bADb)YF>$MNM%8) zk^(SbK|EWflGKV4eUMq!3dtFXMa8KlwxJWJpG4F-%QyMt*5d zib7s~i9%8;LWe?Pu|jHYK}n@T0o2VYnMJ9|CHX}_k)r&v%#_p=plX;Y1wcn7rl;zG ztk4JA0SZ45u*oRWN8)AX6_l1JlmLSd=#SEplKea+h5S6{oXq5GTcvVfq~w?DCFkT9 zr)p?gD=CyE=9B^zx>jVCC?pmXC_zoK(FX;G9hZ$hBYe*DP6u!DzuJ%wcOtHF7nrq|^vIU~E;%n1<E%W>u9Z#o#7tNfpfld<~#RXXSpdN z9sm#pDr*J`T@U8r3*zO60T2z6fuP9sqQ@HWNi{ArFwJf}b$}j!AO?g*Oql>!Q0RPB zc|_K_(dW;uSZ=-Bsb9EcYpafp<1B&Bv6J%k&a1w0>+#mxHfG*D89V#=PWRM@#)ub#3^`+ox%54u`KaxIQWSC_L8x z^+$Ja&dajSp2pVpwc7^T$ECX6?39`&`wCVtwG(%<%Lu5ZWSUHRfjm2;63 z0M6|1DU15?-liXD|w2mSW{X!OLqp!uA^1-pp(!1PhB;LU~Tf?-6Akl7o3k z;<2d65F;H!i!Fm=jUb~J)@a6X3ag3_K^?b9$j;R9LW4(B{IWY!P$Wgm@n{Iiq?Xyt zd2M+IMYjiG*eqf%R2R#snxZ02YFVm_+#E|nX@*MmzIxcA_@Ryv#9mQ{D(|5dk_pTL zn#$`saxbMDZ4*!d>AIDoumpxuMuqcgY=|eysvkp47$dFwF;-*-Dsqt|3JrFqnan|H zwhEG>BTGgu1DklsPXZkQ5kp>8nMEXyr3~anbF49~<-;ZO{3Dx*wfNgCulZPA@u7NA zLRK8Wt7j#y$g1ZXkbz>=6=!)^_*TrFMa{<-*=Ou zP#Frw6WG5ub!DCrWoV-d!yXFGg#V9(<<&z`C=E+86ig>iLnxIiNQqoSLnFwyiL`l` H1OxCJ?we8$ literal 0 HcmV?d00001 diff --git a/tests/general/widgets/inst_nplug_4.wgt b/tests/general/widgets/inst_nplug_4.wgt new file mode 100644 index 0000000000000000000000000000000000000000..a5ddecba7be0732a3eced6d3041c8ace07b230a3 GIT binary patch literal 1045 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/splash.wgt b/tests/general/widgets/splash.wgt new file mode 100644 index 0000000000000000000000000000000000000000..79dcd496036c3d49c5f18877a2ad2ed2ee1f08e2 GIT binary patch literal 5027 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetFakeConfig.wgt b/tests/general/widgets/widgetFakeConfig.wgt new file mode 100644 index 0000000000000000000000000000000000000000..6d7af05ff441a8750f57e50865576b2bef210eab GIT binary patch literal 38773 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%0DI*B70V&f<<-o$boaN*JG8ogze<{3&4#NZh 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 QaEP(R1>4F_!sV}j0i1a6`v3p{ literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetInDir.tar b/tests/general/widgets/widgetInDir.tar new file mode 100644 index 0000000000000000000000000000000000000000..bf0830a15836068cf7a91e2ef0f603970687c8c2 GIT binary patch literal 266240 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 u3vCH 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer100Signed.wgt b/tests/general/widgets/widgetUpdateVer100Signed.wgt new file mode 100644 index 0000000000000000000000000000000000000000..b3ff8bf9baee12ab28a032b75e7bb4b0e53fb78d GIT binary patch literal 23379 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 b%vAF~C5whKGRoig$S=?97s*3v_;>VQtZye? literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer100Unsigned.wgt b/tests/general/widgets/widgetUpdateVer100Unsigned.wgt new file mode 100644 index 0000000000000000000000000000000000000000..751aed0c21f8ef60c876f2637f71ee57e97db9b7 GIT binary patch literal 19629 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 E7Y=I_tN;K2 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer220Signed.wgt b/tests/general/widgets/widgetUpdateVer220Signed.wgt new file mode 100644 index 0000000000000000000000000000000000000000..5ef42af1fc177f87afd2b366d3a1cea279a29220 GIT binary patch literal 23398 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer220SignedAnotherAuthor.wgt b/tests/general/widgets/widgetUpdateVer220SignedAnotherAuthor.wgt new file mode 100644 index 0000000000000000000000000000000000000000..c1aeaa343ac08f3ec082b5199a19deaf13588bfd GIT binary patch literal 23430 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

toxC1Pz9xbjIB| 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 literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer220Unsigned.wgt b/tests/general/widgets/widgetUpdateVer220Unsigned.wgt new file mode 100644 index 0000000000000000000000000000000000000000..181447de51b15364ce913f507e836de4dd037ff2 GIT binary patch literal 19647 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 Z-Ld)eaM%OcIgWN@Jshu&<|{|5{{nI5AcX(` literal 0 HcmV?d00001 diff --git a/tests/general/widgets/widgetUpdateVer220UnsignedDifferentId.wgt b/tests/general/widgets/widgetUpdateVer220UnsignedDifferentId.wgt new file mode 100644 index 0000000000000000000000000000000000000000..8fb6c19a598415bddac21c1209651cfed23803b5 GIT binary patch literal 19649 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 literal 0 HcmV?d00001 diff --git a/uncrustify.cfg b/uncrustify.cfg new file mode 100644 index 0000000..2bf1d96 --- /dev/null +++ b/uncrustify.cfg @@ -0,0 +1,170 @@ +indent_align_string=true +indent_braces=false +indent_braces_no_func=false +indent_brace_parent=false +indent_namespace=false +indent_extern=false +indent_class=true +indent_class_colon=false +indent_else_if=false +indent_func_call_param=false +indent_func_def_param=false +indent_func_proto_param=false +indent_func_class_param=false +indent_func_ctor_var_param=false +indent_template_param=false +indent_func_param_double=false +indent_relative_single_line_comments=false +indent_col1_comment=true +indent_access_spec_body=false +indent_paren_nl=false +indent_comma_paren=false +indent_bool_paren=false +indent_square_nl=false +indent_preserve_sql=false +indent_align_assign=false +sp_balance_nested_parens=false +align_keep_tabs=false +align_with_tabs=false +align_on_tabstop=false +align_number_left=false +align_func_params=false +align_same_func_call_params=false +align_var_def_colon=false +align_var_def_attribute=false +align_var_def_inline=false +align_right_cmt_mix=false +align_on_operator=false +align_mix_var_proto=false +align_single_line_func=false +align_single_line_brace=false +align_nl_cont=false +align_left_shift=true +nl_collapse_empty_body=true +nl_assign_leave_one_liners=false +nl_class_leave_one_liners=false +nl_enum_leave_one_liners=false +nl_getset_leave_one_liners=false +nl_func_leave_one_liners=false +nl_if_leave_one_liners=false +nl_multi_line_cond=true +nl_multi_line_define=false +nl_before_case=false +nl_after_case=false +nl_after_return=false +nl_after_semicolon=true +nl_after_brace_open=false +nl_after_brace_open_cmt=false +nl_after_vbrace_open=false +nl_after_brace_close=false +nl_define_macro=false +nl_squeeze_ifdef=false +nl_ds_struct_enum_cmt=false +nl_ds_struct_enum_close_brace=false +nl_create_if_one_liner=false +nl_create_for_one_liner=false +nl_create_while_one_liner=false +ls_for_split_full=true +ls_func_split_full=true +nl_after_multiline_comment=false +eat_blanks_after_open_brace=true +eat_blanks_before_close_brace=true +mod_pawn_semicolon=false +mod_full_paren_if_bool=false +mod_remove_extra_semicolon=true +mod_sort_import=false +mod_sort_using=false +mod_sort_include=false +mod_move_case_break=false +mod_remove_empty_return=false +cmt_indent_multi=true +cmt_c_group=false +cmt_c_nl_start=false +cmt_c_nl_end=false +cmt_cpp_group=false +cmt_cpp_nl_start=false +cmt_cpp_nl_end=false +cmt_cpp_to_c=false +cmt_star_cont=true +cmt_multi_check_last=true +cmt_insert_before_preproc=false +pp_indent_at_level=false +pp_region_indent_code=false +pp_if_indent_code=false +pp_define_at_level=false +indent_columns=4 +indent_member=4 +indent_access_spec=-2 +code_width=80 +nl_max=2 +nl_before_access_spec=2 +cmt_width=80 +indent_with_tabs=0 +sp_arith=force +sp_assign=force +sp_enum_assign=force +sp_pp_concat=remove +sp_pp_stringify=remove +sp_bool=force +sp_compare=force +sp_paren_brace=force +sp_angle_paren=remove +sp_before_sparen=force +sp_inside_sparen=remove +sp_after_sparen=force +sp_sparen_brace=force +sp_before_semi=remove +sp_after_semi_for_empty=remove +sp_before_square=remove +sp_before_squares=remove +sp_inside_square=remove +sp_after_comma=force +sp_before_comma=remove +sp_after_class_colon=force +sp_before_class_colon=force +sp_before_case_colon=remove +sp_inside_braces=add +sp_inside_fparens=remove +sp_inside_fparen=remove +sp_func_call_paren=remove +sp_func_class_paren=remove +sp_else_brace=force +sp_brace_else=force +sp_catch_brace=force +sp_brace_catch=force +sp_try_brace=force +sp_before_dc=remove +sp_after_dc=remove +sp_not=remove +sp_inv=remove +sp_addr=remove +sp_member=remove +sp_deref=remove +sp_sign=remove +sp_incdec=remove +sp_cond_colon=force +sp_cond_question=force +sp_case_label=force +nl_assign_brace=remove +nl_if_brace=remove +nl_brace_else=remove +nl_elseif_brace=remove +nl_else_brace=remove +nl_else_if=remove +nl_try_brace=remove +nl_for_brace=remove +nl_catch_brace=remove +nl_brace_catch=remove +nl_while_brace=remove +nl_do_brace=remove +nl_brace_while=remove +nl_switch_brace=remove +nl_namespace_brace=remove +nl_class_brace=force +nl_fdef_brace=force +pos_class_comma=trail +pos_class_colon=trail +mod_full_brace_do=add +mod_full_brace_for=add +mod_full_brace_if=add +mod_full_brace_while=add diff --git a/uncrustify.sh b/uncrustify.sh new file mode 100755 index 0000000..49ad3cf --- /dev/null +++ b/uncrustify.sh @@ -0,0 +1 @@ +uncrustify -c uncrustify.cfg --no-backup `find . -regex "\(.*\.cpp\|.*\.h\|.*\.c\|.*\.cc\)" | grep -v "orm.h\|orm_generator.h\|3rdparty\|examples"` diff --git a/wrt-installer.manifest b/wrt-installer.manifest new file mode 100644 index 0000000..eccc16f --- /dev/null +++ b/wrt-installer.manifest @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/wrt-installer.rule b/wrt-installer.rule new file mode 100644 index 0000000..68ce76f --- /dev/null +++ b/wrt-installer.rule @@ -0,0 +1 @@ +wrt-installer aul::terminate x -- 2.7.4