summaryrefslogtreecommitdiffstats
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--desktop/AllLangMoTarget_dkt.mk13
-rw-r--r--desktop/CppunitTest_desktop_app.mk89
-rw-r--r--desktop/CppunitTest_desktop_dialogs_test.mk69
-rw-r--r--desktop/CppunitTest_desktop_lib.mk81
-rw-r--r--desktop/CppunitTest_desktop_lokinit.mk48
-rw-r--r--desktop/CppunitTest_desktop_version.mk23
-rw-r--r--desktop/CustomTarget_desktop_unopackages_install.mk19
-rw-r--r--desktop/CustomTarget_soffice.mk27
-rw-r--r--desktop/Executable_minidump_upload.mk25
-rw-r--r--desktop/Executable_oosplash.mk70
-rw-r--r--desktop/Executable_quickstart.mk44
-rw-r--r--desktop/Executable_sbase.mk30
-rw-r--r--desktop/Executable_scalc.mk30
-rw-r--r--desktop/Executable_sdraw.mk30
-rw-r--r--desktop/Executable_simpress.mk30
-rw-r--r--desktop/Executable_smath.mk30
-rw-r--r--desktop/Executable_soffice_bin.mk56
-rw-r--r--desktop/Executable_soffice_com.mk31
-rw-r--r--desktop/Executable_soffice_exe.mk31
-rw-r--r--desktop/Executable_soffice_safe.mk30
-rw-r--r--desktop/Executable_sweb.mk30
-rw-r--r--desktop/Executable_swriter.mk30
-rw-r--r--desktop/Executable_unoinfo.mk22
-rw-r--r--desktop/Executable_unopkg.mk29
-rw-r--r--desktop/Executable_unopkg_bin.mk32
-rw-r--r--desktop/Executable_unopkg_com.mk29
-rw-r--r--desktop/Extension_test-active.mk23
-rw-r--r--desktop/Extension_test-passive.mk35
-rw-r--r--desktop/GeneratedPackage_desktop_unopackages_install.mk16
-rw-r--r--desktop/IwyuFilter_desktop.yaml91
-rw-r--r--desktop/Jar_active_java.mk26
-rw-r--r--desktop/Jar_passive_java.mk28
-rw-r--r--desktop/Library_active_native.mk30
-rw-r--r--desktop/Library_crashreport.mk50
-rw-r--r--desktop/Library_deployment.mk82
-rw-r--r--desktop/Library_deploymentgui.mk71
-rw-r--r--desktop/Library_deploymentmisc.mk55
-rw-r--r--desktop/Library_migrationoo2.mk37
-rw-r--r--desktop/Library_migrationoo3.mk29
-rw-r--r--desktop/Library_offacc.mk28
-rw-r--r--desktop/Library_passive_native.mk32
-rw-r--r--desktop/Library_sofficeapp.mk180
-rw-r--r--desktop/Library_spl.mk40
-rw-r--r--desktop/Library_unopkgapp.mk46
-rw-r--r--desktop/Makefile14
-rw-r--r--desktop/Module_desktop.mk153
-rw-r--r--desktop/Package_branding.mk25
-rw-r--r--desktop/Package_branding_custom.mk20
-rw-r--r--desktop/Package_sbase_sh.mk14
-rw-r--r--desktop/Package_scalc_sh.mk14
-rw-r--r--desktop/Package_scripts.mk25
-rw-r--r--desktop/Package_sdraw_sh.mk14
-rw-r--r--desktop/Package_simpress_sh.mk14
-rw-r--r--desktop/Package_smath_sh.mk14
-rw-r--r--desktop/Package_soffice_sh.mk14
-rw-r--r--desktop/Package_swriter_sh.mk14
-rw-r--r--desktop/Pagein_calc.mk19
-rw-r--r--desktop/Pagein_common.mk72
-rw-r--r--desktop/Pagein_draw.mk19
-rw-r--r--desktop/Pagein_impress.mk19
-rw-r--r--desktop/Pagein_writer.mk19
-rw-r--r--desktop/Pyuno_passive_python.mk18
-rw-r--r--desktop/README36
-rw-r--r--desktop/README.vars15
-rw-r--r--desktop/Rdb_passive_generic.mk17
-rw-r--r--desktop/Rdb_passive_platform.mk16
-rw-r--r--desktop/StaticLibrary_winlauncher.mk17
-rw-r--r--desktop/StaticLibrary_winloader.mk21
-rw-r--r--desktop/UIConfig_deployment.mk24
-rw-r--r--desktop/WinResTarget_quickstart.mk24
-rw-r--r--desktop/WinResTarget_sbase.mk27
-rw-r--r--desktop/WinResTarget_scalc.mk27
-rw-r--r--desktop/WinResTarget_sdraw.mk28
-rw-r--r--desktop/WinResTarget_simpress.mk27
-rw-r--r--desktop/WinResTarget_smath.mk27
-rw-r--r--desktop/WinResTarget_soffice.mk27
-rw-r--r--desktop/WinResTarget_sofficebin.mk51
-rw-r--r--desktop/WinResTarget_sweb.mk27
-rw-r--r--desktop/WinResTarget_swriter.mk27
-rw-r--r--desktop/inc/app.hxx183
-rw-r--r--desktop/inc/bitmaps.hlst20
-rw-r--r--desktop/inc/dp_misc.h155
-rw-r--r--desktop/inc/dp_shared.hxx44
-rw-r--r--desktop/inc/lib/init.hxx188
-rw-r--r--desktop/inc/migration.hxx33
-rw-r--r--desktop/inc/pch/precompiled_deployment.cxx12
-rw-r--r--desktop/inc/pch/precompiled_deployment.hxx93
-rw-r--r--desktop/inc/pch/precompiled_deploymentgui.cxx12
-rw-r--r--desktop/inc/pch/precompiled_deploymentgui.hxx106
-rw-r--r--desktop/inc/pch/precompiled_deploymentmisc.cxx12
-rw-r--r--desktop/inc/pch/precompiled_deploymentmisc.hxx85
-rw-r--r--desktop/inc/pch/precompiled_sofficeapp.cxx12
-rw-r--r--desktop/inc/pch/precompiled_sofficeapp.hxx187
-rw-r--r--desktop/inc/strings.hrc195
-rw-r--r--desktop/inc/strings.hxx17
-rw-r--r--desktop/qa/data/2slides.odpbin0 -> 10993 bytes
-rw-r--r--desktop/qa/data/blank_presentation.odpbin0 -> 11358 bytes
-rw-r--r--desktop/qa/data/blank_text.docxbin0 -> 4065 bytes
-rw-r--r--desktop/qa/data/blank_text.odtbin0 -> 8295 bytes
-rw-r--r--desktop/qa/data/certificate.derbin0 -> 1308 bytes
-rw-r--r--desktop/qa/data/certificatePrivateKey.derbin0 -> 1218 bytes
-rw-r--r--desktop/qa/data/comments.odtbin0 -> 9694 bytes
-rw-r--r--desktop/qa/data/hidden-row.odsbin0 -> 7684 bytes
-rw-r--r--desktop/qa/data/intermediateRootCA.derbin0 -> 1462 bytes
-rw-r--r--desktop/qa/data/objects.odtbin0 -> 29400 bytes
-rw-r--r--desktop/qa/data/paste.jpgbin0 -> 696 bytes
-rw-r--r--desktop/qa/data/rootCA.derbin0 -> 1462 bytes
-rw-r--r--desktop/qa/data/search.odsbin0 -> 7636 bytes
-rw-r--r--desktop/qa/data/sheet_with_image.odsbin0 -> 10491 bytes
-rw-r--r--desktop/qa/data/sheets.odsbin0 -> 9669 bytes
-rw-r--r--desktop/qa/data/signed.odtbin0 -> 13528 bytes
-rw-r--r--desktop/qa/data/test-PK-signing.pem28
-rw-r--r--desktop/qa/data/test-cert-chain-1.pem24
-rw-r--r--desktop/qa/data/test-cert-chain-2.pem26
-rw-r--r--desktop/qa/data/test-cert-chain-3.pem23
-rw-r--r--desktop/qa/data/test-cert-signing.pem23
-rw-r--r--desktop/qa/deployment_misc/test_dp_version.cxx82
-rw-r--r--desktop/qa/desktop_app/test_desktop_app.cxx142
-rw-r--r--desktop/qa/desktop_lib/test_desktop_lib.cxx2971
-rw-r--r--desktop/qa/unit/data/desktop-dialogs-test.txt44
-rw-r--r--desktop/qa/unit/desktop-dialogs-test.cxx61
-rw-r--r--desktop/qa/unit/desktop-lok-init.cxx166
-rw-r--r--desktop/scripts/gdbtrace13
-rwxr-xr-xdesktop/scripts/sbase.sh4
-rwxr-xr-xdesktop/scripts/scalc.sh4
-rwxr-xr-xdesktop/scripts/sdraw.sh4
-rwxr-xr-xdesktop/scripts/simpress.sh4
-rwxr-xr-xdesktop/scripts/smath.sh4
-rwxr-xr-xdesktop/scripts/soffice.sh191
-rwxr-xr-xdesktop/scripts/swriter.sh4
-rwxr-xr-xdesktop/scripts/unoinfo-mac.sh46
-rwxr-xr-xdesktop/scripts/unoinfo.sh46
-rwxr-xr-xdesktop/scripts/unopkg.sh99
-rw-r--r--desktop/source/app/app.cxx2579
-rw-r--r--desktop/source/app/appinit.cxx274
-rw-r--r--desktop/source/app/check_ext_deps.cxx429
-rw-r--r--desktop/source/app/cmdlineargs.cxx787
-rw-r--r--desktop/source/app/cmdlineargs.hxx192
-rw-r--r--desktop/source/app/cmdlinehelp.cxx263
-rw-r--r--desktop/source/app/cmdlinehelp.hxx33
-rw-r--r--desktop/source/app/crashreport.cxx249
-rw-r--r--desktop/source/app/desktopcontext.cxx58
-rw-r--r--desktop/source/app/desktopcontext.hxx43
-rw-r--r--desktop/source/app/dispatchwatcher.cxx808
-rw-r--r--desktop/source/app/dispatchwatcher.hxx90
-rw-r--r--desktop/source/app/langselect.cxx150
-rw-r--r--desktop/source/app/langselect.hxx33
-rw-r--r--desktop/source/app/lockfile2.cxx60
-rw-r--r--desktop/source/app/main.c58
-rw-r--r--desktop/source/app/officeipcthread.cxx1375
-rw-r--r--desktop/source/app/officeipcthread.hxx159
-rw-r--r--desktop/source/app/opencl.cxx257
-rw-r--r--desktop/source/app/sofficemain.cxx108
-rw-r--r--desktop/source/app/sofficemain.h37
-rw-r--r--desktop/source/app/updater.cxx887
-rw-r--r--desktop/source/app/updater.hxx40
-rw-r--r--desktop/source/app/userinstall.cxx176
-rw-r--r--desktop/source/app/userinstall.hxx41
-rw-r--r--desktop/source/deployment/deployment.component55
-rw-r--r--desktop/source/deployment/dp_log.cxx121
-rw-r--r--desktop/source/deployment/dp_persmap.cxx307
-rw-r--r--desktop/source/deployment/dp_services.cxx49
-rw-r--r--desktop/source/deployment/dp_xml.cxx51
-rw-r--r--desktop/source/deployment/gui/deploymentgui.component31
-rw-r--r--desktop/source/deployment/gui/dp_gui.h31
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.cxx46
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.hxx51
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.cxx1371
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.hxx261
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx1126
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx103
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.cxx1144
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.hxx217
-rw-r--r--desktop/source/deployment/gui/dp_gui_service.cxx304
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.cxx533
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.hxx130
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedata.hxx76
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.cxx1006
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.hxx174
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx663
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx115
-rw-r--r--desktop/source/deployment/gui/license_dialog.cxx223
-rw-r--r--desktop/source/deployment/gui/license_dialog.hxx50
-rw-r--r--desktop/source/deployment/inc/dp_dependencies.hxx75
-rw-r--r--desktop/source/deployment/inc/dp_descriptioninfoset.hxx288
-rw-r--r--desktop/source/deployment/inc/dp_identifier.hxx85
-rw-r--r--desktop/source/deployment/inc/dp_interact.h142
-rw-r--r--desktop/source/deployment/inc/dp_misc_api.hxx34
-rw-r--r--desktop/source/deployment/inc/dp_package.hxx45
-rw-r--r--desktop/source/deployment/inc/dp_persmap.h66
-rw-r--r--desktop/source/deployment/inc/dp_platform.hxx48
-rw-r--r--desktop/source/deployment/inc/dp_registry.hxx43
-rw-r--r--desktop/source/deployment/inc/dp_resource.h34
-rw-r--r--desktop/source/deployment/inc/dp_services.hxx65
-rw-r--r--desktop/source/deployment/inc/dp_ucb.h97
-rw-r--r--desktop/source/deployment/inc/dp_update.hxx139
-rw-r--r--desktop/source/deployment/inc/dp_version.hxx39
-rw-r--r--desktop/source/deployment/inc/dp_xml.h45
-rw-r--r--desktop/source/deployment/inc/lockfile.hxx92
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.cxx216
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.hxx97
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.cxx245
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.hxx142
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.cxx1405
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.hxx223
-rw-r--r--desktop/source/deployment/manager/dp_informationprovider.cxx319
-rw-r--r--desktop/source/deployment/manager/dp_manager.cxx1603
-rw-r--r--desktop/source/deployment/manager/dp_manager.h235
-rw-r--r--desktop/source/deployment/manager/dp_managerfac.cxx165
-rw-r--r--desktop/source/deployment/manager/dp_properties.cxx149
-rw-r--r--desktop/source/deployment/manager/dp_properties.hxx64
-rw-r--r--desktop/source/deployment/misc/dp_dependencies.cxx195
-rw-r--r--desktop/source/deployment/misc/dp_descriptioninfoset.cxx809
-rw-r--r--desktop/source/deployment/misc/dp_identifier.cxx56
-rw-r--r--desktop/source/deployment/misc/dp_interact.cxx135
-rw-r--r--desktop/source/deployment/misc/dp_misc.cxx555
-rw-r--r--desktop/source/deployment/misc/dp_platform.cxx205
-rw-r--r--desktop/source/deployment/misc/dp_resource.cxx53
-rw-r--r--desktop/source/deployment/misc/dp_ucb.cxx306
-rw-r--r--desktop/source/deployment/misc/dp_update.cxx408
-rw-r--r--desktop/source/deployment/misc/dp_version.cxx63
-rw-r--r--desktop/source/deployment/misc/lockfile.cxx211
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.cxx131
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.hxx98
-rw-r--r--desktop/source/deployment/registry/component/dp_component.cxx1706
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configuration.cxx798
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx161
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx78
-rw-r--r--desktop/source/deployment/registry/dp_backend.cxx767
-rw-r--r--desktop/source/deployment/registry/dp_backenddb.cxx655
-rw-r--r--desktop/source/deployment/registry/dp_registry.cxx528
-rw-r--r--desktop/source/deployment/registry/executable/dp_executable.cxx314
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx62
-rw-r--r--desktop/source/deployment/registry/help/dp_help.cxx601
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.cxx126
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.hxx76
-rw-r--r--desktop/source/deployment/registry/inc/dp_backend.h289
-rw-r--r--desktop/source/deployment/registry/inc/dp_backenddb.hxx168
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.cxx111
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.hxx71
-rw-r--r--desktop/source/deployment/registry/package/dp_package.cxx1596
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.h55
-rw-r--r--desktop/source/deployment/registry/script/dp_script.cxx459
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx60
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx103
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx71
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_sfwk.cxx358
-rw-r--r--desktop/source/inc/helpids.h28
-rw-r--r--desktop/source/lib/init.cxx6189
-rw-r--r--desktop/source/lib/lokandroid.cxx422
-rw-r--r--desktop/source/lib/lokclipboard.cxx231
-rw-r--r--desktop/source/lib/lokclipboard.hxx117
-rw-r--r--desktop/source/lib/lokinteractionhandler.cxx368
-rw-r--r--desktop/source/lib/lokinteractionhandler.hxx104
-rw-r--r--desktop/source/migration/migration.cxx1114
-rw-r--r--desktop/source/migration/migration_impl.hxx198
-rw-r--r--desktop/source/migration/services/basicmigration.cxx219
-rw-r--r--desktop/source/migration/services/basicmigration.hxx85
-rw-r--r--desktop/source/migration/services/cexports.cxx62
-rw-r--r--desktop/source/migration/services/cexportsoo3.cxx47
-rw-r--r--desktop/source/migration/services/cppumaker.mk27
-rw-r--r--desktop/source/migration/services/jvmfwk.cxx395
-rw-r--r--desktop/source/migration/services/jvmfwk.hxx38
-rw-r--r--desktop/source/migration/services/migrationoo2.component28
-rw-r--r--desktop/source/migration/services/migrationoo3.component25
-rw-r--r--desktop/source/migration/services/misc.hxx42
-rw-r--r--desktop/source/migration/services/oo3extensionmigration.cxx428
-rw-r--r--desktop/source/migration/services/oo3extensionmigration.hxx128
-rw-r--r--desktop/source/migration/services/wordbookmigration.cxx252
-rw-r--r--desktop/source/migration/services/wordbookmigration.hxx85
-rw-r--r--desktop/source/minidump/minidump.cxx228
-rw-r--r--desktop/source/minidump/minidump_upload.cxx32
-rw-r--r--desktop/source/offacc/acceptor.cxx296
-rw-r--r--desktop/source/offacc/acceptor.hxx102
-rw-r--r--desktop/source/offacc/offacc.component25
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_app.cxx635
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx387
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_main.c28
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_main.h37
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_misc.cxx460
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_shared.h117
-rw-r--r--desktop/source/splash/services_spl.cxx56
-rw-r--r--desktop/source/splash/spl.component28
-rw-r--r--desktop/source/splash/splash.cxx644
-rw-r--r--desktop/source/splash/splash.hxx49
-rw-r--r--desktop/source/splash/unxsplash.cxx148
-rw-r--r--desktop/source/splash/unxsplash.hxx68
-rw-r--r--desktop/test/deployment/active/Addons.xcu58
-rw-r--r--desktop/test/deployment/active/MANIFEST.MF3
-rw-r--r--desktop/test/deployment/active/META-INF/manifest.xml34
-rw-r--r--desktop/test/deployment/active/ProtocolHandler.xcu39
-rw-r--r--desktop/test/deployment/active/active_native.cxx276
-rw-r--r--desktop/test/deployment/active/active_python.py112
-rw-r--r--desktop/test/deployment/active/com/sun/star/comp/test/deployment/Dispatch.java95
-rw-r--r--desktop/test/deployment/active/com/sun/star/comp/test/deployment/Provider.java74
-rw-r--r--desktop/test/deployment/active/com/sun/star/comp/test/deployment/Services.java65
-rw-r--r--desktop/test/deployment/active/description.xml27
-rw-r--r--desktop/test/deployment/dependencies/broken-dependency.oxtbin0 -> 1655 bytes
-rw-r--r--desktop/test/deployment/dependencies/double-dependencies.oxtbin0 -> 1651 bytes
-rw-r--r--desktop/test/deployment/dependencies/empty-dependencies.oxtbin0 -> 1624 bytes
-rw-r--r--desktop/test/deployment/dependencies/funny-dependency.oxtbin0 -> 1730 bytes
-rw-r--r--desktop/test/deployment/dependencies/license-dependency.oxtbin0 -> 1891 bytes
-rw-r--r--desktop/test/deployment/dependencies/loversion35.oxtbin0 -> 1736 bytes
-rw-r--r--desktop/test/deployment/dependencies/loversion36.oxtbin0 -> 1736 bytes
-rw-r--r--desktop/test/deployment/dependencies/many-dependencies.oxtbin0 -> 1702 bytes
-rw-r--r--desktop/test/deployment/dependencies/maxversion33.oxtbin0 -> 1740 bytes
-rw-r--r--desktop/test/deployment/dependencies/maxversion34.oxtbin0 -> 1741 bytes
-rw-r--r--desktop/test/deployment/dependencies/maxversion35.oxtbin0 -> 1741 bytes
-rw-r--r--desktop/test/deployment/dependencies/maxversion36.oxtbin0 -> 1739 bytes
-rw-r--r--desktop/test/deployment/dependencies/minattr22.oxtbin0 -> 1690 bytes
-rw-r--r--desktop/test/deployment/dependencies/minattr23.oxtbin0 -> 1690 bytes
-rw-r--r--desktop/test/deployment/dependencies/minattr24.oxtbin0 -> 1690 bytes
-rw-r--r--desktop/test/deployment/dependencies/no-dependencies.oxtbin0 -> 1611 bytes
-rw-r--r--desktop/test/deployment/dependencies/no-description.oxtbin0 -> 1360 bytes
-rw-r--r--desktop/test/deployment/dependencies/readme.txt73
-rw-r--r--desktop/test/deployment/dependencies/unknown-dependency.oxtbin0 -> 1633 bytes
-rw-r--r--desktop/test/deployment/dependencies/version10000.oxtbin0 -> 1668 bytes
-rw-r--r--desktop/test/deployment/dependencies/version21.oxtbin0 -> 1666 bytes
-rw-r--r--desktop/test/deployment/dependencies/version21ns.oxtbin0 -> 1661 bytes
-rw-r--r--desktop/test/deployment/dependencies/version21other.oxtbin0 -> 1679 bytes
-rw-r--r--desktop/test/deployment/dependencies/version22.oxtbin0 -> 1666 bytes
-rw-r--r--desktop/test/deployment/dependencies/version23.oxtbin0 -> 1666 bytes
-rw-r--r--desktop/test/deployment/dependencies/version34.oxtbin0 -> 1721 bytes
-rw-r--r--desktop/test/deployment/dependencies/version35.oxtbin0 -> 1721 bytes
-rw-r--r--desktop/test/deployment/dependencies/versionempty.oxtbin0 -> 1675 bytes
-rw-r--r--desktop/test/deployment/dependencies/versionnone.oxtbin0 -> 1674 bytes
-rw-r--r--desktop/test/deployment/description/desc1.oxtbin0 -> 2096 bytes
-rw-r--r--desktop/test/deployment/description/desc2.oxtbin0 -> 2091 bytes
-rw-r--r--desktop/test/deployment/description/desc3.oxtbin0 -> 2070 bytes
-rw-r--r--desktop/test/deployment/description/desc4.oxtbin0 -> 2061 bytes
-rw-r--r--desktop/test/deployment/description/desc5.oxtbin0 -> 2041 bytes
-rw-r--r--desktop/test/deployment/description/readme.txt23
-rw-r--r--desktop/test/deployment/display_name/name1.oxtbin0 -> 704 bytes
-rw-r--r--desktop/test/deployment/display_name/name2.oxtbin0 -> 699 bytes
-rw-r--r--desktop/test/deployment/display_name/name3.oxtbin0 -> 681 bytes
-rw-r--r--desktop/test/deployment/display_name/name4.oxtbin0 -> 675 bytes
-rw-r--r--desktop/test/deployment/display_name/name5.oxtbin0 -> 654 bytes
-rw-r--r--desktop/test/deployment/display_name/readme.txt26
-rw-r--r--desktop/test/deployment/executable_content/build/hello.c36
-rw-r--r--desktop/test/deployment/executable_content/build/makefile.mk42
-rw-r--r--desktop/test/deployment/executable_content/build/readme.txt2
-rw-r--r--desktop/test/deployment/executable_content/hello.oxtbin0 -> 35048 bytes
-rw-r--r--desktop/test/deployment/executable_content/readme.txt12
-rw-r--r--desktop/test/deployment/identifier/explicit/identifier.oxtbin0 -> 1660 bytes
-rw-r--r--desktop/test/deployment/identifier/legacy/identifier.oxtbin0 -> 1634 bytes
-rw-r--r--desktop/test/deployment/identifier/readme.txt24
-rw-r--r--desktop/test/deployment/locationtest/LocationTest.idl34
-rw-r--r--desktop/test/deployment/locationtest/LocationTest.java156
-rw-r--r--desktop/test/deployment/locationtest/LocationTest.odtbin0 -> 7681 bytes
-rw-r--r--desktop/test/deployment/locationtest/MANIFEST.MF2
-rw-r--r--desktop/test/deployment/locationtest/description.xml30
-rw-r--r--desktop/test/deployment/locationtest/makefile.mk76
-rw-r--r--desktop/test/deployment/locationtest/manifest.xml5
-rw-r--r--desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/MANIFEST.MF2
-rw-r--r--desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/OptionsEventHandler.java417
-rw-r--r--desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/makefile.mk44
-rw-r--r--desktop/test/deployment/options/leaf1.oxtbin0 -> 8308 bytes
-rw-r--r--desktop/test/deployment/options/leaf1mod.oxtbin0 -> 8310 bytes
-rw-r--r--desktop/test/deployment/options/leaf2.oxtbin0 -> 8338 bytes
-rw-r--r--desktop/test/deployment/options/leaves1.oxtbin0 -> 21158 bytes
-rw-r--r--desktop/test/deployment/options/leaves2.oxtbin0 -> 21153 bytes
-rw-r--r--desktop/test/deployment/options/leaves3.oxtbin0 -> 21080 bytes
-rw-r--r--desktop/test/deployment/options/modules1.oxtbin0 -> 24317 bytes
-rw-r--r--desktop/test/deployment/options/modules2.oxtbin0 -> 24196 bytes
-rw-r--r--desktop/test/deployment/options/nodes1.oxtbin0 -> 1882 bytes
-rw-r--r--desktop/test/deployment/options/nodes2.oxtbin0 -> 24287 bytes
-rw-r--r--desktop/test/deployment/options/nodes3.oxtbin0 -> 24315 bytes
-rw-r--r--desktop/test/deployment/options/nodes4.oxtbin0 -> 24318 bytes
-rw-r--r--desktop/test/deployment/options/nodes5.oxtbin0 -> 12616 bytes
-rw-r--r--desktop/test/deployment/options/readme.txt200
-rw-r--r--desktop/test/deployment/passive/Addons.xcu74
-rw-r--r--desktop/test/deployment/passive/MANIFEST.MF3
-rw-r--r--desktop/test/deployment/passive/META-INF/manifest.xml32
-rw-r--r--desktop/test/deployment/passive/ProtocolHandler.xcu39
-rw-r--r--desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Dispatch.java95
-rw-r--r--desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Provider.java74
-rw-r--r--desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Services.java42
-rw-r--r--desktop/test/deployment/passive/description.xml27
-rw-r--r--desktop/test/deployment/passive/help/en/help.tree16
-rw-r--r--desktop/test/deployment/passive/help/en/main.xhp35
-rw-r--r--desktop/test/deployment/passive/passive_java.component29
-rw-r--r--desktop/test/deployment/passive/passive_native.component29
-rw-r--r--desktop/test/deployment/passive/passive_native.cxx248
-rw-r--r--desktop/test/deployment/passive/passive_python.component29
-rw-r--r--desktop/test/deployment/passive/passive_python.py93
-rw-r--r--desktop/test/deployment/simple_license/BadDesc.oxtbin0 -> 9663 bytes
-rw-r--r--desktop/test/deployment/simple_license/BadNamespace.oxtbin0 -> 9736 bytes
-rw-r--r--desktop/test/deployment/simple_license/BadRoot.oxtbin0 -> 9073 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale1.oxtbin0 -> 2126 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale2.oxtbin0 -> 2121 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale3.oxtbin0 -> 2101 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale4.oxtbin0 -> 2094 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale5.oxtbin0 -> 2072 bytes
-rw-r--r--desktop/test/deployment/simple_license/Locale6.oxtbin0 -> 1397 bytes
-rw-r--r--desktop/test/deployment/simple_license/LongLic.oxtbin0 -> 9521 bytes
-rw-r--r--desktop/test/deployment/simple_license/MissingLic.oxtbin0 -> 9214 bytes
-rw-r--r--desktop/test/deployment/simple_license/MissingLicRef.oxtbin0 -> 9332 bytes
-rw-r--r--desktop/test/deployment/simple_license/NoDefLang.oxtbin0 -> 9360 bytes
-rw-r--r--desktop/test/deployment/simple_license/NoDesc.oxtbin0 -> 8722 bytes
-rw-r--r--desktop/test/deployment/simple_license/NoLang.oxtbin0 -> 9217 bytes
-rw-r--r--desktop/test/deployment/simple_license/Prefix.oxtbin0 -> 1112 bytes
-rw-r--r--desktop/test/deployment/simple_license/ShortLicense.oxtbin0 -> 9381 bytes
-rw-r--r--desktop/test/deployment/simple_license/ShortLicenseShared.oxtbin0 -> 9382 bytes
-rw-r--r--desktop/test/deployment/simple_license/suppress_license.oxtbin0 -> 2143 bytes
-rw-r--r--desktop/test/deployment/simple_license/tests_simple_license.odtbin0 -> 16629 bytes
-rw-r--r--desktop/test/deployment/update/changing_display_name/change1.oxtbin0 -> 1650 bytes
-rw-r--r--desktop/test/deployment/update/changing_display_name/change1_mod.oxtbin0 -> 1673 bytes
-rw-r--r--desktop/test/deployment/update/changing_display_name/readme.txt13
-rw-r--r--desktop/test/deployment/update/changing_display_name/update1/change1.oxtbin0 -> 1675 bytes
-rw-r--r--desktop/test/deployment/update/changing_display_name/update1/change1.update.xml27
-rw-r--r--desktop/test/deployment/update/changing_display_name/update2/change1.oxtbin0 -> 1687 bytes
-rw-r--r--desktop/test/deployment/update/changing_display_name/update2/change1.update.xml27
-rw-r--r--desktop/test/deployment/update/default_url/default1.oxtbin0 -> 1544 bytes
-rw-r--r--desktop/test/deployment/update/default_url/default2.oxtbin0 -> 1544 bytes
-rw-r--r--desktop/test/deployment/update/default_url/readme.txt9
-rw-r--r--desktop/test/deployment/update/default_url/update/default1.oxtbin0 -> 1546 bytes
-rw-r--r--desktop/test/deployment/update/default_url/update/default1.update.xml27
-rw-r--r--desktop/test/deployment/update/default_url/update/default2.oxtbin0 -> 1546 bytes
-rw-r--r--desktop/test/deployment/update/default_url/update/default2.update.xml27
-rw-r--r--desktop/test/deployment/update/default_url/update/feed1.xml50
-rw-r--r--desktop/test/deployment/update/defect/fail1.oxtbin0 -> 2189 bytes
-rw-r--r--desktop/test/deployment/update/defect/fail2.oxtbin0 -> 2188 bytes
-rw-r--r--desktop/test/deployment/update/defect/fail3.oxtbin0 -> 2188 bytes
-rw-r--r--desktop/test/deployment/update/defect/fail4.oxtbin0 -> 2189 bytes
-rw-r--r--desktop/test/deployment/update/defect/info1.oxtbin0 -> 2188 bytes
-rw-r--r--desktop/test/deployment/update/defect/info2.oxtbin0 -> 2187 bytes
-rw-r--r--desktop/test/deployment/update/defect/info3.oxtbin0 -> 2187 bytes
-rw-r--r--desktop/test/deployment/update/defect/readme.txt15
-rw-r--r--desktop/test/deployment/update/defect/update/fail1.oxtbin0 -> 2193 bytes
-rw-r--r--desktop/test/deployment/update/defect/update/fail1.update.xml27
-rw-r--r--desktop/test/deployment/update/defect/update/fail2.oxtbin0 -> 2436 bytes
-rw-r--r--desktop/test/deployment/update/defect/update/fail2.update.xml27
-rw-r--r--desktop/test/deployment/update/defect/update/fail3.oxtbin0 -> 2396 bytes
-rw-r--r--desktop/test/deployment/update/defect/update/fail3.update.xml27
-rw-r--r--desktop/test/deployment/update/defect/update/fail4.oxt0
-rw-r--r--desktop/test/deployment/update/defect/update/fail4.update.xml27
-rw-r--r--desktop/test/deployment/update/defect/update/info1.update.xml0
-rw-r--r--desktop/test/deployment/update/defect/update/info2.update.xml1
-rw-r--r--desktop/test/deployment/update/defect/update/info3.oxtbin0 -> 2189 bytes
-rw-r--r--desktop/test/deployment/update/defect/update/info3.update.xml27
-rw-r--r--desktop/test/deployment/update/dependencies/publisher_en.html9
-rw-r--r--desktop/test/deployment/update/dependencies/readme.txt23
-rw-r--r--desktop/test/deployment/update/dependencies/release-notes_en.html8
-rw-r--r--desktop/test/deployment/update/dependencies/update-dependencies.oxtbin0 -> 1751 bytes
-rw-r--r--desktop/test/deployment/update/dependencies/update/update-dependencies.update.xml62
-rw-r--r--desktop/test/deployment/update/license/lic1.oxtbin0 -> 3608 bytes
-rw-r--r--desktop/test/deployment/update/license/lic2.oxtbin0 -> 3625 bytes
-rw-r--r--desktop/test/deployment/update/license/lic3.oxtbin0 -> 3624 bytes
-rw-r--r--desktop/test/deployment/update/license/readme.txt9
-rw-r--r--desktop/test/deployment/update/license/update/lic1.oxtbin0 -> 3610 bytes
-rw-r--r--desktop/test/deployment/update/license/update/lic1.update.xml27
-rw-r--r--desktop/test/deployment/update/license/update/lic2.oxtbin0 -> 3627 bytes
-rw-r--r--desktop/test/deployment/update/license/update/lic2.update.xml27
-rw-r--r--desktop/test/deployment/update/license/update/lic3.oxtbin0 -> 3626 bytes
-rw-r--r--desktop/test/deployment/update/license/update/lic3.update.xml27
-rw-r--r--desktop/test/deployment/update/platform/all1.oxtbin0 -> 692 bytes
-rw-r--r--desktop/test/deployment/update/platform/all2.oxtbin0 -> 702 bytes
-rw-r--r--desktop/test/deployment/update/platform/all3.oxtbin0 -> 297 bytes
-rw-r--r--desktop/test/deployment/update/platform/freebsd_x86.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/freebsd_x86_64.oxtbin0 -> 711 bytes
-rw-r--r--desktop/test/deployment/update/platform/invalid1.oxtbin0 -> 653 bytes
-rw-r--r--desktop/test/deployment/update/platform/invalid2.oxtbin0 -> 653 bytes
-rw-r--r--desktop/test/deployment/update/platform/invalid3.oxtbin0 -> 655 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_arm_eabi.oxtbin0 -> 709 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_arm_oabi.oxtbin0 -> 710 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_ia64.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_mips64_eb.oxtbin0 -> 805 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_mips64_el.oxtbin0 -> 804 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_mips_eb.oxtbin0 -> 709 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_mips_el.oxtbin0 -> 708 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_powerpc.oxtbin0 -> 708 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_powerpc64.oxtbin0 -> 710 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_s390.oxtbin0 -> 705 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_s390x.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_sparc.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_x86.oxtbin0 -> 705 bytes
-rw-r--r--desktop/test/deployment/update/platform/linux_x86_64.oxtbin0 -> 708 bytes
-rw-r--r--desktop/test/deployment/update/platform/macosx_powerpc.oxtbin0 -> 710 bytes
-rw-r--r--desktop/test/deployment/update/platform/macosx_x86.oxtbin0 -> 707 bytes
-rw-r--r--desktop/test/deployment/update/platform/mul1.oxtbin0 -> 952 bytes
-rw-r--r--desktop/test/deployment/update/platform/os2_x86.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/readme.txt51
-rw-r--r--desktop/test/deployment/update/platform/solaris_sparc.oxtbin0 -> 709 bytes
-rw-r--r--desktop/test/deployment/update/platform/solaris_x86.oxtbin0 -> 706 bytes
-rw-r--r--desktop/test/deployment/update/platform/windows_x86.oxtbin0 -> 707 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub1.oxtbin0 -> 1882 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub10.oxtbin0 -> 1742 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub11.oxtbin0 -> 1601 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub2.oxtbin0 -> 1866 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub3.oxtbin0 -> 1829 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub4.oxtbin0 -> 1812 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub5.oxtbin0 -> 1769 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub6.oxtbin0 -> 1814 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub7.oxtbin0 -> 1769 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub8.oxtbin0 -> 1853 bytes
-rw-r--r--desktop/test/deployment/update/publisher/pub9.oxtbin0 -> 1779 bytes
-rw-r--r--desktop/test/deployment/update/publisher/publisher_de-DE-altmark.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_de-DE.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_de.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en-GB.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en-US-region1.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en-US-region2.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en-US.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en-region3.html9
-rw-r--r--desktop/test/deployment/update/publisher/publisher_en.html9
-rw-r--r--desktop/test/deployment/update/publisher/readme.txt212
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_de-DE-altmark.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_de-DE.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_de.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en-GB.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en-US-region1.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en-US-region2.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en-US.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en-region3.html8
-rw-r--r--desktop/test/deployment/update/publisher/release-notes_en.html8
-rw-r--r--desktop/test/deployment/update/publisher/update/pub1.oxtbin0 -> 1885 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub1.update.xml53
-rw-r--r--desktop/test/deployment/update/publisher/update/pub10.oxtbin0 -> 1744 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub11.oxtbin0 -> 1603 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub2.oxtbin0 -> 1871 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub2.update.xml51
-rw-r--r--desktop/test/deployment/update/publisher/update/pub3.oxtbin0 -> 1833 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub3.update.xml47
-rw-r--r--desktop/test/deployment/update/publisher/update/pub4.oxtbin0 -> 1815 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub4.update.xml45
-rw-r--r--desktop/test/deployment/update/publisher/update/pub5.oxtbin0 -> 1774 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub5.update.xml41
-rw-r--r--desktop/test/deployment/update/publisher/update/pub6.oxtbin0 -> 1816 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub6.update.xml41
-rw-r--r--desktop/test/deployment/update/publisher/update/pub7.oxtbin0 -> 1771 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub7.update.xml41
-rw-r--r--desktop/test/deployment/update/publisher/update/pub8.oxtbin0 -> 1855 bytes
-rw-r--r--desktop/test/deployment/update/publisher/update/pub9.oxtbin0 -> 1781 bytes
-rw-r--r--desktop/test/deployment/update/readme.txt58
-rw-r--r--desktop/test/deployment/update/simple/plain1.oxtbin0 -> 1642 bytes
-rw-r--r--desktop/test/deployment/update/simple/plain2.oxtbin0 -> 1643 bytes
-rw-r--r--desktop/test/deployment/update/simple/plain3.oxtbin0 -> 1643 bytes
-rw-r--r--desktop/test/deployment/update/simple/readme.txt31
-rw-r--r--desktop/test/deployment/update/simple/update/plain1.oxtbin0 -> 1645 bytes
-rw-r--r--desktop/test/deployment/update/simple/update/plain1.update.xml27
-rw-r--r--desktop/test/deployment/update/simple/update/plain2.oxtbin0 -> 1645 bytes
-rw-r--r--desktop/test/deployment/update/simple/update/plain2.update.xml27
-rw-r--r--desktop/test/deployment/update/simple/update/plain3.oxtbin0 -> 1645 bytes
-rw-r--r--desktop/test/deployment/update/simple/update/plain3.update.xml27
-rw-r--r--desktop/test/deployment/update/updatefeed/feed1.oxtbin0 -> 2184 bytes
-rw-r--r--desktop/test/deployment/update/updatefeed/feed2.oxtbin0 -> 2184 bytes
-rw-r--r--desktop/test/deployment/update/updatefeed/update/feed1.oxtbin0 -> 2184 bytes
-rw-r--r--desktop/test/deployment/update/updatefeed/update/feed1.update.xml27
-rw-r--r--desktop/test/deployment/update/updatefeed/update/feed1.xml50
-rw-r--r--desktop/test/deployment/update/updatefeed/update/feed2.oxtbin0 -> 2184 bytes
-rw-r--r--desktop/test/deployment/update/updatefeed/update/feed2.update.xml27
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/MANIFEST.MF2
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/TestExtension.idl34
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/TestExtension.java156
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/description.xml30
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/makefile.mk80
-rw-r--r--desktop/test/deployment/update/updateinfocreation/build/manifest.xml5
-rw-r--r--desktop/test/deployment/update/updateinfocreation/readme.txt38
-rw-r--r--desktop/test/deployment/update/updateinfocreation/update/updateinfo.oxtbin0 -> 4295 bytes
-rw-r--r--desktop/test/deployment/update/updateinfocreation/updateinfo.oxtbin0 -> 4295 bytes
-rw-r--r--desktop/test/deployment/update/website_update/readme.txt133
-rw-r--r--desktop/test/deployment/update/website_update/update/web1.oxtbin0 -> 1695 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web1.update.xml37
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_de-DE-altmark.html18
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_de-DE.html18
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_de.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en-GB.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en-US-region1.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en-US-region2.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en-US.html20
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en-region3.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web1_en.html19
-rw-r--r--desktop/test/deployment/update/website_update/update/web2.oxtbin0 -> 1695 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web2.update.xml36
-rw-r--r--desktop/test/deployment/update/website_update/update/web3.oxtbin0 -> 1695 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web3.update.xml34
-rw-r--r--desktop/test/deployment/update/website_update/update/web4.oxtbin0 -> 1695 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web4.update.xml33
-rw-r--r--desktop/test/deployment/update/website_update/update/web5.oxtbin0 -> 1695 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web5.update.xml31
-rw-r--r--desktop/test/deployment/update/website_update/update/web6.oxtbin0 -> 1640 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web6/description.xml28
-rw-r--r--desktop/test/deployment/update/website_update/update/web6/readme.txt5
-rw-r--r--desktop/test/deployment/update/website_update/update/web7.oxtbin0 -> 1897 bytes
-rw-r--r--desktop/test/deployment/update/website_update/update/web7/description.xml53
-rw-r--r--desktop/test/deployment/update/website_update/update/web7/readme.txt5
-rw-r--r--desktop/test/deployment/update/website_update/web1.oxtbin0 -> 1693 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web2.oxtbin0 -> 1693 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web3.oxtbin0 -> 1693 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web4.oxtbin0 -> 1693 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web5.oxtbin0 -> 1693 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web6.oxtbin0 -> 1638 bytes
-rw-r--r--desktop/test/deployment/update/website_update/web7.oxtbin0 -> 1894 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/readme.txt18
-rw-r--r--desktop/test/deployment/update/wrong_url/update/url1.oxtbin0 -> 2192 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/update/url1.update.xml28
-rw-r--r--desktop/test/deployment/update/wrong_url/update/url2.oxtbin0 -> 2206 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/update/url2.update.xml27
-rw-r--r--desktop/test/deployment/update/wrong_url/update/wrongdownload1.update.xml28
-rw-r--r--desktop/test/deployment/update/wrong_url/update/wrongdownload2.update.xml28
-rw-r--r--desktop/test/deployment/update/wrong_url/update/wrongdownload3.update.xml28
-rw-r--r--desktop/test/deployment/update/wrong_url/url1.oxtbin0 -> 2190 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/url2.oxtbin0 -> 2205 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/url3.oxtbin0 -> 2204 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/wrongdownload1.oxtbin0 -> 2194 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/wrongdownload2.oxtbin0 -> 2194 bytes
-rw-r--r--desktop/test/deployment/update/wrong_url/wrongdownload3.oxtbin0 -> 2194 bytes
-rw-r--r--desktop/test/deployment/version/readme.txt76
-rw-r--r--desktop/test/deployment/version/version_0.0/dependency.oxtbin0 -> 1657 bytes
-rw-r--r--desktop/test/deployment/version/version_0.0/license.oxtbin0 -> 1733 bytes
-rw-r--r--desktop/test/deployment/version/version_0.0/plain.oxtbin0 -> 1618 bytes
-rw-r--r--desktop/test/deployment/version/version_1.02.4.7.0/dependency.oxtbin0 -> 1662 bytes
-rw-r--r--desktop/test/deployment/version/version_1.02.4.7.0/license.oxtbin0 -> 1738 bytes
-rw-r--r--desktop/test/deployment/version/version_1.02.4.7.0/plain.oxtbin0 -> 1624 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.15.3/dependency.oxtbin0 -> 1662 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.15.3/license.oxtbin0 -> 1738 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.15.3/plain.oxtbin0 -> 1624 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.3/dependency.oxtbin0 -> 1659 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.3/license.oxtbin0 -> 1735 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.3/plain.oxtbin0 -> 1620 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.4.7/dependency.oxtbin0 -> 1661 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.4.7/license.oxtbin0 -> 1737 bytes
-rw-r--r--desktop/test/deployment/version/version_1.2.4.7/plain.oxtbin0 -> 1623 bytes
-rw-r--r--desktop/test/deployment/version/version_badelement/dependency.oxtbin0 -> 1654 bytes
-rw-r--r--desktop/test/deployment/version/version_badelement/license.oxtbin0 -> 1731 bytes
-rw-r--r--desktop/test/deployment/version/version_badelement/plain.oxtbin0 -> 1616 bytes
-rw-r--r--desktop/test/deployment/version/version_badvalue/dependency.oxtbin0 -> 1657 bytes
-rw-r--r--desktop/test/deployment/version/version_badvalue/license.oxtbin0 -> 1733 bytes
-rw-r--r--desktop/test/deployment/version/version_badvalue/plain.oxtbin0 -> 1618 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_0.0/dependency.oxtbin0 -> 1618 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_1.02.4.7.0/dependency.oxtbin0 -> 1624 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_1.2.15.3/dependency.oxtbin0 -> 1624 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_1.2.3/dependency.oxtbin0 -> 1620 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_1.2.4.7/dependency.oxtbin0 -> 1623 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_badelement/dependency.oxtbin0 -> 1616 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_badvalue/dependency.oxtbin0 -> 1618 bytes
-rw-r--r--desktop/test/deployment/version/version_nodependencies_none/dependency.oxtbin0 -> 1598 bytes
-rw-r--r--desktop/test/deployment/version/version_none/dependency.oxtbin0 -> 1645 bytes
-rw-r--r--desktop/test/deployment/version/version_none/license.oxtbin0 -> 1722 bytes
-rw-r--r--desktop/test/deployment/version/version_none/plain.oxtbin0 -> 1598 bytes
-rw-r--r--desktop/uiconfig/ui/dependenciesdialog.ui138
-rw-r--r--desktop/uiconfig/ui/extensionmanager.ui351
-rw-r--r--desktop/uiconfig/ui/extensionmenu.ui9
-rw-r--r--desktop/uiconfig/ui/installforalldialog.ui82
-rw-r--r--desktop/uiconfig/ui/licensedialog.ui227
-rw-r--r--desktop/uiconfig/ui/showlicensedialog.ui78
-rw-r--r--desktop/uiconfig/ui/updatedialog.ui376
-rw-r--r--desktop/uiconfig/ui/updateinstalldialog.ui177
-rw-r--r--desktop/uiconfig/ui/updaterequireddialog.ui192
-rw-r--r--desktop/unx/source/args.c138
-rw-r--r--desktop/unx/source/args.h28
-rw-r--r--desktop/unx/source/file_image.h72
-rw-r--r--desktop/unx/source/file_image_unx.c120
-rw-r--r--desktop/unx/source/pagein.c102
-rw-r--r--desktop/unx/source/pagein.h29
-rw-r--r--desktop/unx/source/splashx.c817
-rw-r--r--desktop/unx/source/splashx.h32
-rw-r--r--desktop/unx/source/start.c873
-rw-r--r--desktop/util/officeloader.rc36
-rw-r--r--desktop/win32/source/QuickStart/QuickStart.cxx104
-rw-r--r--desktop/win32/source/QuickStart/QuickStart.rc3
-rw-r--r--desktop/win32/source/QuickStart/resource.h5
-rw-r--r--desktop/win32/source/applauncher/launcher.cxx105
-rw-r--r--desktop/win32/source/applauncher/launcher.hxx27
-rw-r--r--desktop/win32/source/applauncher/launcher.rc23
-rw-r--r--desktop/win32/source/applauncher/sbase.cxx24
-rw-r--r--desktop/win32/source/applauncher/scalc.cxx24
-rw-r--r--desktop/win32/source/applauncher/sdraw.cxx24
-rw-r--r--desktop/win32/source/applauncher/simpress.cxx24
-rw-r--r--desktop/win32/source/applauncher/smath.cxx24
-rw-r--r--desktop/win32/source/applauncher/soffice_safe.cxx14
-rw-r--r--desktop/win32/source/applauncher/sweb.cxx24
-rw-r--r--desktop/win32/source/applauncher/swriter.cxx24
-rw-r--r--desktop/win32/source/loader.cxx432
-rw-r--r--desktop/win32/source/loader.hxx92
-rw-r--r--desktop/win32/source/officeloader/soffice_com.cxx19
-rw-r--r--desktop/win32/source/officeloader/soffice_exe.cxx19
-rw-r--r--desktop/win32/source/officeloader/unopkg_com.cxx19
-rw-r--r--desktop/win32/source/officeloader/unopkg_exe.cxx19
-rw-r--r--desktop/win32/source/unoinfo.cxx87
683 files changed, 68945 insertions, 0 deletions
diff --git a/desktop/AllLangMoTarget_dkt.mk b/desktop/AllLangMoTarget_dkt.mk
new file mode 100644
index 000000000..b951648db
--- /dev/null
+++ b/desktop/AllLangMoTarget_dkt.mk
@@ -0,0 +1,13 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,dkt))
+
+$(eval $(call gb_AllLangMoTarget_set_polocation,dkt,desktop))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CppunitTest_desktop_app.mk b/desktop/CppunitTest_desktop_app.mk
new file mode 100644
index 000000000..adb57add6
--- /dev/null
+++ b/desktop/CppunitTest_desktop_app.mk
@@ -0,0 +1,89 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,desktop_app))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,desktop_app, \
+ desktop/qa/desktop_app/test_desktop_app \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,desktop_app, \
+ $(if $(ENABLE_BREAKPAD),breakpad) \
+ dbus \
+ icu_headers \
+ icui18n \
+ icuuc \
+ $(if $(ENABLE_ONLINE_UPDATE_MAR),\
+ curl \
+ orcus-parser \
+ orcus )\
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,desktop_app, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ $(if $(ENABLE_BREAKPAD),crashreport) \
+ deploymentmisc \
+ editeng \
+ i18nlangtag \
+ $(if $(filter OPENCL,$(BUILD_TYPE)),opencl) \
+ sal \
+ salhelper \
+ sb \
+ sfx \
+ svl \
+ svx \
+ svxcore \
+ svt \
+ tk \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+))
+
+ifeq ($(OS),WNT)
+$(eval $(call gb_CppunitTest_use_static_libraries,desktop_app,\
+ $(if $(ENABLE_ONLINE_UPDATE_MAR),\
+ windows_process )\
+))
+endif
+
+
+ifeq ($(OS), $(filter LINUX %BSD SOLARIS, $(OS)))
+ifeq ($(USING_X11),TRUE)
+$(eval $(call gb_CppunitTest_use_static_libraries,desktop_app,\
+ glxtest \
+))
+endif
+
+$(eval $(call gb_CppunitTest_add_libs,desktop_app,\
+ -lm $(DLOPEN_LIBS) \
+ -lX11 \
+))
+endif
+
+$(eval $(call gb_CppunitTest_use_library_objects,desktop_app, \
+ sofficeapp \
+))
+
+ifeq ($(ENABLE_MACOSX_SANDBOX),TRUE)
+
+$(eval $(call gb_CppunitTest_use_system_darwin_frameworks,desktop_app,\
+ Foundation \
+))
+
+endif
+
+$(eval $(call gb_CppunitTest_use_external,desktop_app,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,desktop_app))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CppunitTest_desktop_dialogs_test.mk b/desktop/CppunitTest_desktop_dialogs_test.mk
new file mode 100644
index 000000000..ce26857ab
--- /dev/null
+++ b/desktop/CppunitTest_desktop_dialogs_test.mk
@@ -0,0 +1,69 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitScreenShot,desktop_dialogs_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,desktop_dialogs_test, \
+ desktop/qa/unit/desktop-dialogs-test \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,desktop_dialogs_test))
+
+$(eval $(call gb_CppunitTest_set_include,desktop_dialogs_test,\
+ -I$(SRCDIR)/desktop/source/inc \
+ -I$(SRCDIR)/desktop/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,desktop_dialogs_test, \
+ basegfx \
+ comphelper \
+ cppu \
+ cppuhelper \
+ drawinglayer \
+ editeng \
+ i18nlangtag \
+ i18nutil \
+ msfilter \
+ oox \
+ sal \
+ salhelper \
+ sax \
+ sfx \
+ sot \
+ svl \
+ svt \
+ test \
+ tl \
+ tk \
+ ucbhelper \
+ unotest \
+ utl \
+ vcl \
+ xo \
+))
+
+$(eval $(call gb_CppunitTest_use_external,desktop_dialogs_test,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,desktop_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_ure,desktop_dialogs_test))
+$(eval $(call gb_CppunitTest_use_vcl_non_headless_with_windows,desktop_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_rdb,desktop_dialogs_test,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,desktop_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,desktop_dialogs_test,\
+ desktop \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CppunitTest_desktop_lib.mk b/desktop/CppunitTest_desktop_lib.mk
new file mode 100644
index 000000000..8b375235b
--- /dev/null
+++ b/desktop/CppunitTest_desktop_lib.mk
@@ -0,0 +1,81 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,desktop_lib))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,desktop_lib, \
+ desktop/qa/desktop_lib/test_desktop_lib \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,desktop_lib, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ i18nlangtag \
+ sal \
+ sc \
+ scfilt \
+ sfx \
+ sofficeapp \
+ subsequenttest \
+ sw \
+ test \
+ unotest \
+ utl \
+ tl \
+ vcl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,desktop_lib, \
+ boost_headers \
+ cairo \
+))
+
+$(eval $(call gb_CppunitTest_set_include,desktop_lib,\
+ -I$(SRCDIR)/desktop/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,desktop_lib))
+
+$(eval $(call gb_CppunitTest_use_ure,desktop_lib))
+
+$(eval $(call gb_CppunitTest_use_vcl,desktop_lib))
+
+$(eval $(call gb_CppunitTest_use_rdb,desktop_lib,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,desktop_lib))
+
+$(eval $(call gb_CppunitTest_use_more_fonts,desktop_lib))
+
+$(eval $(call gb_CppunitTest_use_packages,desktop_lib, \
+ scripting_scriptbindinglib \
+ wizards_basicshare \
+ wizards_basicsrvaccess2base \
+ wizards_basicsrvdepot \
+ wizards_basicsrveuro \
+ wizards_basicsrvform \
+ wizards_basicsrvgimmicks \
+ wizards_basicsrvimport \
+ wizards_basicsrvtemplate \
+ wizards_basicsrvtools \
+))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,desktop_lib, \
+ cui \
+ modules/swriter \
+))
+
+$(eval $(call gb_CppunitTest_use_packages,desktop_lib, \
+ postprocess_images \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CppunitTest_desktop_lokinit.mk b/desktop/CppunitTest_desktop_lokinit.mk
new file mode 100644
index 000000000..1f648a1ec
--- /dev/null
+++ b/desktop/CppunitTest_desktop_lokinit.mk
@@ -0,0 +1,48 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,desktop_lok_init))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,desktop_lok_init, \
+ desktop/qa/unit/desktop-lok-init \
+))
+
+$(eval $(call gb_CppunitTest_use_external,desktop_lok_init,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,desktop_lok_init))
+
+$(eval $(call gb_CppunitTest_use_ure,desktop_lok_init))
+
+$(eval $(call gb_CppunitTest_set_include,desktop_lok_init,\
+ -I$(SRCDIR)/desktop/source/inc \
+ -I$(SRCDIR)/desktop/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,desktop_lok_init, \
+ comphelper \
+ cppu \
+ sal \
+ sofficeapp \
+ vcl \
+ $(gb_UWINAPI) \
+))
+
+ifeq ($(OS),LINUX)
+$(eval $(call gb_CppunitTest_add_libs,desktop_lok_init,\
+ -lm \
+ -ldl \
+))
+endif
+
+$(eval $(call gb_CppunitTest_use_configuration,desktop_lok_init))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CppunitTest_desktop_version.mk b/desktop/CppunitTest_desktop_version.mk
new file mode 100644
index 000000000..c200f39a7
--- /dev/null
+++ b/desktop/CppunitTest_desktop_version.mk
@@ -0,0 +1,23 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,desktop_version))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,desktop_version, \
+ desktop/qa/deployment_misc/test_dp_version \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,desktop_version, \
+ deploymentmisc \
+ sal \
+))
+
+$(eval $(call gb_CppunitTest_use_udk_api,desktop_version))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CustomTarget_desktop_unopackages_install.mk b/desktop/CustomTarget_desktop_unopackages_install.mk
new file mode 100644
index 000000000..f16845fe1
--- /dev/null
+++ b/desktop/CustomTarget_desktop_unopackages_install.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,desktop/unopackages_install))
+
+$(call gb_CustomTarget_get_target,desktop/unopackages_install) : | \
+ $(call gb_CustomTarget_get_workdir,desktop/unopackages_install)/uno_packages/cache/uno_packages
+
+$(call gb_CustomTarget_get_workdir,desktop/unopackages_install)/uno_packages/cache/uno_packages : | \
+ $(call gb_CustomTarget_get_workdir,desktop/unopackages_install)/.dir
+ mkdir -p $@
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/CustomTarget_soffice.mk b/desktop/CustomTarget_soffice.mk
new file mode 100644
index 000000000..1fa356db1
--- /dev/null
+++ b/desktop/CustomTarget_soffice.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,desktop/soffice))
+
+$(call gb_CustomTarget_get_target,desktop/soffice) : \
+ $(call gb_CustomTarget_get_workdir,desktop/soffice)/soffice.sh
+
+$(call gb_CustomTarget_get_workdir,desktop/soffice)/soffice.sh : \
+ $(SRCDIR)/desktop/scripts/soffice.sh \
+ | $(call gb_CustomTarget_get_workdir,desktop/soffice)/.dir
+ $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),SED,1)
+ $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),SED)
+ifneq ($(JITC_PROCESSOR_TYPE),)
+ sed -e "s/^#@JITC_PROCESSOR_TYPE_EXPORT@/export JITC_PROCESSOR_TYPE=$(JITC_PROCESSOR_TYPE)/" $< > $@
+else
+ cp $< $@
+endif
+ $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),SED)
+
+# vim:set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/desktop/Executable_minidump_upload.mk b/desktop/Executable_minidump_upload.mk
new file mode 100644
index 000000000..600c171e2
--- /dev/null
+++ b/desktop/Executable_minidump_upload.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,minidump_upload))
+
+$(eval $(call gb_Executable_use_libraries,minidump_upload,\
+ crashreport \
+ sal \
+))
+
+$(eval $(call gb_Executable_use_external,minidump_upload,curl))
+
+$(eval $(call gb_Executable_add_exception_objects,minidump_upload,\
+ desktop/source/minidump/minidump_upload \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,minidump_upload))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Executable_oosplash.mk b/desktop/Executable_oosplash.mk
new file mode 100644
index 000000000..a14acd626
--- /dev/null
+++ b/desktop/Executable_oosplash.mk
@@ -0,0 +1,70 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,oosplash))
+
+$(eval $(call gb_Executable_set_targettype_gui,oosplash,YES))
+
+$(eval $(call gb_Executable_use_libraries,oosplash,\
+ sal \
+))
+
+$(eval $(call gb_Executable_add_cobjects,oosplash,\
+ desktop/unx/source/args \
+ desktop/unx/source/file_image_unx \
+ desktop/unx/source/pagein \
+ desktop/unx/source/splashx \
+ desktop/unx/source/start \
+))
+
+ifneq ($(USE_XINERAMA),)
+
+$(eval $(call gb_Executable_add_defs,oosplash,\
+ -DUSE_XINERAMA \
+))
+
+$(eval $(call gb_Executable_add_libs,oosplash,\
+ -lXinerama \
+))
+
+endif
+
+ifneq ($(DISABLE_GUI),TRUE)
+
+ifneq ($(OS),WNT)
+$(eval $(call gb_Executable_add_libs,oosplash,\
+ -lX11 \
+))
+endif
+
+$(eval $(call gb_Executable_add_defs,oosplash,\
+ -DENABLE_QUICKSTART_LIBPNG \
+))
+
+$(eval $(call gb_Executable_use_externals,oosplash,\
+ libpng \
+))
+
+endif
+
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Executable_add_libs,oosplash,\
+ -lm \
+))
+endif
+
+ifeq ($(OS),SOLARIS)
+
+$(eval $(call gb_Executable_add_libs,oosplash,\
+ -lsocket \
+))
+
+endif
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_quickstart.mk b/desktop/Executable_quickstart.mk
new file mode 100644
index 000000000..f0df0bbe7
--- /dev/null
+++ b/desktop/Executable_quickstart.mk
@@ -0,0 +1,44 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,quickstart))
+
+$(eval $(call gb_Executable_set_targettype_gui,quickstart,YES))
+
+$(eval $(call gb_Executable_use_system_win32_libs,quickstart,\
+ comdlg32 \
+ gdi32 \
+ ole32 \
+ oleaut32 \
+ shell32 \
+))
+
+ifeq ($(COM),GCC)
+
+$(eval $(call gb_Executable_use_system_win32_libs,quickstart,\
+ uuid \
+))
+
+else
+
+$(eval $(call gb_Executable_use_system_win32_libs,quickstart,\
+ comsupp \
+))
+
+endif
+
+$(eval $(call gb_Executable_add_exception_objects,quickstart,\
+ desktop/win32/source/QuickStart/QuickStart \
+))
+
+$(eval $(call gb_Executable_add_nativeres,quickstart,quickstart/QuickStart))
+
+$(eval $(call gb_Executable_add_default_nativeres,quickstart))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_sbase.mk b/desktop/Executable_sbase.mk
new file mode 100644
index 000000000..9c36165b8
--- /dev/null
+++ b/desktop/Executable_sbase.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,sbase))
+
+$(eval $(call gb_Executable_set_targettype_gui,sbase,YES))
+
+$(eval $(call gb_Executable_add_ldflags,sbase,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,sbase,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,sbase,\
+ desktop/win32/source/applauncher/sbase \
+))
+
+$(eval $(call gb_Executable_add_nativeres,sbase,sbase/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,sbase,$(PRODUCTNAME) Base))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_scalc.mk b/desktop/Executable_scalc.mk
new file mode 100644
index 000000000..b6845690d
--- /dev/null
+++ b/desktop/Executable_scalc.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,scalc))
+
+$(eval $(call gb_Executable_set_targettype_gui,scalc,YES))
+
+$(eval $(call gb_Executable_add_ldflags,scalc,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,scalc,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,scalc,\
+ desktop/win32/source/applauncher/scalc \
+))
+
+$(eval $(call gb_Executable_add_nativeres,scalc,scalc/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,scalc,$(PRODUCTNAME) Calc))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_sdraw.mk b/desktop/Executable_sdraw.mk
new file mode 100644
index 000000000..164b65876
--- /dev/null
+++ b/desktop/Executable_sdraw.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,sdraw))
+
+$(eval $(call gb_Executable_set_targettype_gui,sdraw,YES))
+
+$(eval $(call gb_Executable_add_ldflags,sdraw,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,sdraw,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,sdraw,\
+ desktop/win32/source/applauncher/sdraw \
+))
+
+$(eval $(call gb_Executable_add_nativeres,sdraw,sdraw/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,sdraw,$(PRODUCTNAME) Draw))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_simpress.mk b/desktop/Executable_simpress.mk
new file mode 100644
index 000000000..8d3bdd054
--- /dev/null
+++ b/desktop/Executable_simpress.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,simpress))
+
+$(eval $(call gb_Executable_set_targettype_gui,simpress,YES))
+
+$(eval $(call gb_Executable_add_ldflags,simpress,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,simpress,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,simpress,\
+ desktop/win32/source/applauncher/simpress \
+))
+
+$(eval $(call gb_Executable_add_nativeres,simpress,simpress/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,simpress,$(PRODUCTNAME) Impress))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_smath.mk b/desktop/Executable_smath.mk
new file mode 100644
index 000000000..797354d47
--- /dev/null
+++ b/desktop/Executable_smath.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,smath))
+
+$(eval $(call gb_Executable_set_targettype_gui,smath,YES))
+
+$(eval $(call gb_Executable_add_ldflags,smath,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,smath,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,smath,\
+ desktop/win32/source/applauncher/smath \
+))
+
+$(eval $(call gb_Executable_add_nativeres,smath,smath/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,smath,$(PRODUCTNAME) Math))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk
new file mode 100644
index 000000000..5c895ed0e
--- /dev/null
+++ b/desktop/Executable_soffice_bin.mk
@@ -0,0 +1,56 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,soffice_bin))
+
+$(eval $(call gb_Executable_set_include,soffice_bin,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/source/inc \
+))
+
+$(eval $(call gb_Executable_use_libraries,soffice_bin,\
+ sal \
+ sofficeapp \
+))
+
+$(eval $(call gb_Executable_add_cobjects,soffice_bin,\
+ desktop/source/app/main \
+))
+
+ifeq ($(OS),MACOSX)
+# At least when building against SDK 10.15, changing the LC_VERSION_MIN_MACOSX load command's sdk
+# value from 10.15 to "n/a" (i.e., 0.0.0) is necessary to avoid blurry text in the LO UI (see
+# <https://github.com/llvm/llvm-project/commit/25ce33a6e4f3b13732c0f851e68390dc2acb9123>
+# "[driver][darwin] Pass -platform_version flag to the linker instead of the
+# -<platform>_version_min flag", clang/test/Driver/darwin-ld-platform-version-macos.c in particular,
+# for the -platform_version that Clang passes by default to new-enough ld):
+$(eval $(call gb_Executable_add_ldflags,soffice_bin, \
+ -Xlinker -platform_version -Xlinker macos -Xlinker $(MACOSX_DEPLOYMENT_TARGET) -Xlinker 0.0.0 \
+))
+endif
+
+ifeq ($(OS),WNT)
+
+$(eval $(call gb_Executable_set_targettype_gui,soffice_bin,NO))
+
+$(eval $(call gb_Executable_add_nativeres,soffice_bin,sofficebin/officeloader))
+
+$(eval $(call gb_Executable_add_default_nativeres,soffice_bin,$(PRODUCTNAME)))
+
+ifeq ($(COM),MSC)
+
+$(eval $(call gb_Executable_add_ldflags,soffice_bin,\
+ /STACK:10000000 \
+))
+
+endif
+
+endif
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_soffice_com.mk b/desktop/Executable_soffice_com.mk
new file mode 100644
index 000000000..bc1460634
--- /dev/null
+++ b/desktop/Executable_soffice_com.mk
@@ -0,0 +1,31 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,soffice_com))
+
+$(eval $(call gb_Executable_set_targettype_gui,soffice_com,NO))
+
+$(eval $(call gb_Executable_use_system_win32_libs,soffice_com,\
+ shell32 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,soffice_com,\
+ ooopathutils \
+ winloader \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,soffice_com,\
+ desktop/win32/source/officeloader/soffice_com \
+))
+
+$(eval $(call gb_Executable_add_nativeres,soffice_com,soffice/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,soffice_com,$(PRODUCTNAME)))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_soffice_exe.mk b/desktop/Executable_soffice_exe.mk
new file mode 100644
index 000000000..2803627bc
--- /dev/null
+++ b/desktop/Executable_soffice_exe.mk
@@ -0,0 +1,31 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,soffice_exe))
+
+$(eval $(call gb_Executable_set_targettype_gui,soffice_exe,YES))
+
+$(eval $(call gb_Executable_use_system_win32_libs,soffice_exe,\
+ shell32 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,soffice_exe,\
+ ooopathutils \
+ winloader \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,soffice_exe,\
+ desktop/win32/source/officeloader/soffice_exe \
+))
+
+$(eval $(call gb_Executable_add_nativeres,soffice_exe,soffice/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,soffice_exe,$(PRODUCTNAME)))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_soffice_safe.mk b/desktop/Executable_soffice_safe.mk
new file mode 100644
index 000000000..aa1f6bc82
--- /dev/null
+++ b/desktop/Executable_soffice_safe.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,soffice_safe))
+
+$(eval $(call gb_Executable_set_targettype_gui,soffice_safe,YES))
+
+$(eval $(call gb_Executable_add_ldflags,soffice_safe,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,soffice_safe,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,soffice_safe,\
+ desktop/win32/source/applauncher/soffice_safe \
+))
+
+$(eval $(call gb_Executable_add_nativeres,soffice_safe,soffice/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,soffice_safe,$(PRODUCTNAME)))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_sweb.mk b/desktop/Executable_sweb.mk
new file mode 100644
index 000000000..44cf70145
--- /dev/null
+++ b/desktop/Executable_sweb.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,sweb))
+
+$(eval $(call gb_Executable_set_targettype_gui,sweb,YES))
+
+$(eval $(call gb_Executable_add_ldflags,sweb,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,sweb,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,sweb,\
+ desktop/win32/source/applauncher/sweb \
+))
+
+$(eval $(call gb_Executable_add_nativeres,sweb,sweb/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,sweb,$(PRODUCTNAME) Writer (Web)))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_swriter.mk b/desktop/Executable_swriter.mk
new file mode 100644
index 000000000..5ced1b911
--- /dev/null
+++ b/desktop/Executable_swriter.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,swriter))
+
+$(eval $(call gb_Executable_set_targettype_gui,swriter,YES))
+
+$(eval $(call gb_Executable_add_ldflags,swriter,\
+ /ENTRY:wWinMainCRTStartup \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,swriter,\
+ winlauncher \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,swriter,\
+ desktop/win32/source/applauncher/swriter \
+))
+
+$(eval $(call gb_Executable_add_nativeres,swriter,swriter/launcher))
+
+$(eval $(call gb_Executable_add_default_nativeres,swriter,$(PRODUCTNAME) Writer))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_unoinfo.mk b/desktop/Executable_unoinfo.mk
new file mode 100644
index 000000000..77d1778e0
--- /dev/null
+++ b/desktop/Executable_unoinfo.mk
@@ -0,0 +1,22 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,unoinfo))
+
+$(eval $(call gb_Executable_use_static_libraries,unoinfo,\
+ ooopathutils \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,unoinfo,\
+ desktop/win32/source/unoinfo \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,unoinfo))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_unopkg.mk b/desktop/Executable_unopkg.mk
new file mode 100644
index 000000000..28a753a57
--- /dev/null
+++ b/desktop/Executable_unopkg.mk
@@ -0,0 +1,29 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,unopkg))
+
+$(eval $(call gb_Executable_set_targettype_gui,unopkg,YES))
+
+$(eval $(call gb_Executable_use_static_libraries,unopkg,\
+ ooopathutils \
+ winloader \
+))
+
+$(eval $(call gb_Executable_use_system_win32_libs,unopkg,\
+ shell32 \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,unopkg,\
+ desktop/win32/source/officeloader/unopkg_exe \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,unopkg))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_unopkg_bin.mk b/desktop/Executable_unopkg_bin.mk
new file mode 100644
index 000000000..f66d5ec25
--- /dev/null
+++ b/desktop/Executable_unopkg_bin.mk
@@ -0,0 +1,32 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,unopkg_bin))
+
+$(eval $(call gb_Executable_set_targettype_gui,unopkg_bin,NO))
+
+$(eval $(call gb_Executable_set_include,unopkg_bin,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/source/inc \
+))
+
+$(eval $(call gb_Executable_use_libraries,unopkg_bin,\
+ comphelper \
+ sal \
+ tl \
+ unopkgapp \
+))
+
+$(eval $(call gb_Executable_add_cobjects,unopkg_bin,\
+ desktop/source/pkgchk/unopkg/unopkg_main \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,unopkg_bin))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Executable_unopkg_com.mk b/desktop/Executable_unopkg_com.mk
new file mode 100644
index 000000000..022479f05
--- /dev/null
+++ b/desktop/Executable_unopkg_com.mk
@@ -0,0 +1,29 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,unopkg_com))
+
+$(eval $(call gb_Executable_set_targettype_gui,unopkg_com,NO))
+
+$(eval $(call gb_Executable_use_static_libraries,unopkg_com,\
+ ooopathutils \
+ winloader \
+))
+
+$(eval $(call gb_Executable_use_system_win32_libs,unopkg_com,\
+ shell32 \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,unopkg_com,\
+ desktop/win32/source/officeloader/unopkg_com \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,unopkg_com))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Extension_test-active.mk b/desktop/Extension_test-active.mk
new file mode 100644
index 000000000..fbb329f24
--- /dev/null
+++ b/desktop/Extension_test-active.mk
@@ -0,0 +1,23 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Extension_Extension,test-active,desktop/test/deployment/active,nodeliver))
+
+$(eval $(call gb_Extension_add_files,test-active,, \
+ $(SRCDIR)/desktop/test/deployment/active/Addons.xcu \
+ $(SRCDIR)/desktop/test/deployment/active/ProtocolHandler.xcu \
+ $(SRCDIR)/desktop/test/deployment/active/active_python.py \
+ $(call gb_Jar_get_target,active_java) \
+))
+
+$(eval $(call gb_Extension_add_libraries,test-active, \
+ active_native \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Extension_test-passive.mk b/desktop/Extension_test-passive.mk
new file mode 100644
index 000000000..ef42f3b1b
--- /dev/null
+++ b/desktop/Extension_test-passive.mk
@@ -0,0 +1,35 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Extension_Extension,test-passive,desktop/test/deployment/passive,nodeliver))
+
+$(eval $(call gb_Extension_add_file,test-passive,generic.components,$(call gb_Rdb_get_target,passive_generic)))
+
+$(eval $(call gb_Extension_add_file,test-passive,platform.components,$(call gb_Rdb_get_target,passive_platform)))
+
+$(eval $(call gb_Extension_add_files,test-passive,, \
+ $(SRCDIR)/desktop/test/deployment/passive/Addons.xcu \
+ $(SRCDIR)/desktop/test/deployment/passive/ProtocolHandler.xcu \
+ $(SRCDIR)/desktop/test/deployment/passive/passive_python.py \
+ $(call gb_Jar_get_target,passive_java) \
+))
+
+$(eval $(call gb_Extension_add_files,test-passive,help/en, \
+ $(SRCDIR)/desktop/test/deployment/passive/help/en/help.tree \
+))
+
+$(eval $(call gb_Extension_add_files,test-passive,help/en/org.openoffice%2Fframework%2Fdesktop%2Ftest%2Fdeployment%2Fpassive, \
+ $(SRCDIR)/desktop/test/deployment/passive/help/en/main.xhp \
+))
+
+$(eval $(call gb_Extension_add_libraries,test-passive, \
+ passive_native \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/GeneratedPackage_desktop_unopackages_install.mk b/desktop/GeneratedPackage_desktop_unopackages_install.mk
new file mode 100644
index 000000000..b3b3cc6fc
--- /dev/null
+++ b/desktop/GeneratedPackage_desktop_unopackages_install.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_GeneratedPackage_GeneratedPackage,desktop_unopackages_install,$(call gb_CustomTarget_get_workdir,desktop/unopackages_install)))
+
+$(eval $(call gb_GeneratedPackage_use_customtarget,desktop_unopackages_install,desktop/unopackages_install))
+
+$(eval $(call gb_GeneratedPackage_add_dir,desktop_unopackages_install,$(INSTROOT)/$(LIBO_SHARE_FOLDER)/uno_packages,uno_packages))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/IwyuFilter_desktop.yaml b/desktop/IwyuFilter_desktop.yaml
new file mode 100644
index 000000000..c424c9815
--- /dev/null
+++ b/desktop/IwyuFilter_desktop.yaml
@@ -0,0 +1,91 @@
+---
+assumeFilename: desktop/source/lib/init.cxx
+blacklist:
+ desktop/inc/lib/init.hxx:
+ # Complete type is needed
+ - boost/property_tree/ptree.hpp
+ desktop/source/offacc/acceptor.hxx:
+ # Don't propose hxx -> h change in URE libs
+ - osl/thread.hxx
+ desktop/qa/desktop_lib/test_desktop_lib.cxx:
+ # Actually used
+ - comphelper/scopeguard.hxx
+ desktop/source/app/app.cxx:
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ # Needed for HAVE_FEATURE_UPDATE_MAR
+ - com/sun/star/system/XSystemShellExecute.hpp
+ - com/sun/star/system/SystemShellExecute.hpp
+ - officecfg/Office/Update.hxx
+ desktop/source/app/appinit.cxx:
+ # Needed on IOS
+ - rtl/bootstrap.hxx
+ desktop/source/app/cmdlineargs.cxx:
+ # Don't propose hxx -> h change in URE libs
+ - osl/thread.hxx
+ desktop/source/app/cmdlinehelp.cxx:
+ # Needed on WIN32
+ - comphelper/string.hxx
+ desktop/source/app/crashreport.cxx:
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ desktop/source/app/dispatchwatcher.cxx:
+ # Don't propose hxx -> h change in URE libs
+ - osl/thread.hxx
+ desktop/source/app/sofficemain.cxx:
+ # Might be needed on WIN32
+ - prewin.h
+ - postwin.h
+ # Needed on ANDROID
+ - rtl/bootstrap.hxx
+ desktop/source/lib/init.cxx:
+ # Needed for boost::trim_copy
+ - boost/algorithm/string.hpp
+ # Actually used
+ - comphelper/scopeguard.hxx
+ # Needed on IOS & ANDROID
+ - officecfg/Office/Impress.hxx
+ desktop/source/minidump/minidump.cxx:
+ # Needed for ostringstream
+ - sstream
+ desktop/source/deployment/manager/dp_properties.cxx:
+ # Actually used
+ - com/sun/star/ucb/XCommandEnvironment.hpp
+ desktop/source/deployment/manager/dp_informationprovider.cxx:
+ # Needed for extern functions
+ - dp_services.hxx
+ desktop/source/deployment/manager/dp_extensionmanager.cxx:
+ # Actually used
+ - com/sun/star/deployment/XPackage.hpp
+ - com/sun/star/deployment/XPackageManager.hpp
+ - com/sun/star/uno/XComponentContext.hpp
+ # Needed for extern functions
+ - dp_services.hxx
+ desktop/source/deployment/misc/lockfile.cxx:
+ # Needed on WIN32
+ - memory
+ desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/component/dp_compbackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/package/dp_extbackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/help/dp_helpbackenddb.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ desktop/source/deployment/registry/script/dp_lib_container.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ - com/sun/star/ucb/XCommandEnvironment.hpp
+ desktop/source/deployment/registry/help/dp_help.cxx:
+ # Actually used
+ - com/sun/star/util/XMacroExpander.hpp
diff --git a/desktop/Jar_active_java.mk b/desktop/Jar_active_java.mk
new file mode 100644
index 000000000..48fb7425e
--- /dev/null
+++ b/desktop/Jar_active_java.mk
@@ -0,0 +1,26 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Jar_Jar,active_java))
+
+$(eval $(call gb_Jar_add_sourcefiles,active_java, \
+ desktop/test/deployment/active/com/sun/star/comp/test/deployment/Dispatch \
+ desktop/test/deployment/active/com/sun/star/comp/test/deployment/Provider \
+ desktop/test/deployment/active/com/sun/star/comp/test/deployment/Services \
+))
+
+$(eval $(call gb_Jar_set_manifest,active_java,$(SRCDIR)/desktop/test/deployment/active/MANIFEST.MF))
+
+$(eval $(call gb_Jar_set_packageroot,active_java,com))
+
+$(eval $(call gb_Jar_use_jars,active_java, \
+ libreoffice \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Jar_passive_java.mk b/desktop/Jar_passive_java.mk
new file mode 100644
index 000000000..e2408ae5a
--- /dev/null
+++ b/desktop/Jar_passive_java.mk
@@ -0,0 +1,28 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Jar_Jar,passive_java))
+
+$(eval $(call gb_Jar_add_sourcefiles,passive_java, \
+ desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Dispatch \
+ desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Provider \
+ desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Services \
+))
+
+$(eval $(call gb_Jar_set_componentfile,passive_java,desktop/test/deployment/passive/passive_java,OXT))
+
+$(eval $(call gb_Jar_set_manifest,passive_java,$(SRCDIR)/desktop/test/deployment/passive/MANIFEST.MF))
+
+$(eval $(call gb_Jar_set_packageroot,passive_java,com))
+
+$(eval $(call gb_Jar_use_jars,passive_java, \
+ libreoffice \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Library_active_native.mk b/desktop/Library_active_native.mk
new file mode 100644
index 000000000..ef4815f1e
--- /dev/null
+++ b/desktop/Library_active_native.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,active_native))
+
+$(eval $(call gb_Library_add_exception_objects,active_native, \
+ desktop/test/deployment/active/active_native \
+))
+
+$(eval $(call gb_Library_set_external_code,active_native))
+
+$(eval $(call gb_Library_use_externals,active_native, \
+ boost_headers \
+))
+
+$(eval $(call gb_Library_use_libraries,active_native, \
+ cppu \
+ cppuhelper \
+ sal \
+))
+
+$(eval $(call gb_Library_use_sdk_api,active_native))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Library_crashreport.mk b/desktop/Library_crashreport.mk
new file mode 100644
index 000000000..a2cef785f
--- /dev/null
+++ b/desktop/Library_crashreport.mk
@@ -0,0 +1,50 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,crashreport))
+
+$(eval $(call gb_Library_set_include,crashreport,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+))
+
+$(eval $(call gb_Library_use_externals,crashreport,\
+ breakpad \
+ curl \
+))
+
+$(eval $(call gb_Library_add_defs,crashreport,\
+ -DCRASHREPORT_DLLIMPLEMENTATION \
+))
+
+$(eval $(call gb_Library_add_libs,crashreport,\
+ $(if $(filter LINUX %BSD SOLARIS, $(OS)), \
+ $(DLOPEN_LIBS) \
+ ) \
+))
+
+$(eval $(call gb_Library_use_sdk_api,crashreport))
+
+$(eval $(call gb_Library_use_libraries,crashreport,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ ucbhelper \
+ utl \
+))
+
+$(eval $(call gb_Library_add_exception_objects,crashreport,\
+ desktop/source/app/crashreport \
+ desktop/source/minidump/minidump \
+))
+
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_deployment.mk b/desktop/Library_deployment.mk
new file mode 100644
index 000000000..887e9eefa
--- /dev/null
+++ b/desktop/Library_deployment.mk
@@ -0,0 +1,82 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,deployment))
+
+$(eval $(call gb_Library_set_include,deployment,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+ -I$(SRCDIR)/desktop/source/deployment/inc \
+ -I$(SRCDIR)/desktop/source/deployment/registry/inc \
+))
+
+$(eval $(call gb_Library_use_external,deployment,boost_headers))
+
+$(eval $(call gb_Library_set_precompiled_header,deployment,desktop/inc/pch/precompiled_deployment))
+
+$(eval $(call gb_Library_use_sdk_api,deployment))
+
+$(eval $(call gb_Library_use_libraries,deployment,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ deploymentmisc \
+ sal \
+ svl \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+ xmlscript \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_set_componentfile,deployment,desktop/source/deployment/deployment))
+
+$(eval $(call gb_Library_add_exception_objects,deployment,\
+ desktop/source/deployment/dp_log \
+ desktop/source/deployment/dp_persmap \
+ desktop/source/deployment/dp_services \
+ desktop/source/deployment/dp_xml \
+ desktop/source/deployment/manager/dp_activepackages \
+ desktop/source/deployment/manager/dp_commandenvironments \
+ desktop/source/deployment/manager/dp_extensionmanager \
+ desktop/source/deployment/manager/dp_informationprovider \
+ desktop/source/deployment/manager/dp_manager \
+ desktop/source/deployment/manager/dp_managerfac \
+ desktop/source/deployment/manager/dp_properties \
+ desktop/source/deployment/registry/component/dp_compbackenddb \
+ desktop/source/deployment/registry/component/dp_component \
+ desktop/source/deployment/registry/configuration/dp_configuration \
+ desktop/source/deployment/registry/configuration/dp_configurationbackenddb \
+ desktop/source/deployment/registry/dp_backend \
+ desktop/source/deployment/registry/dp_backenddb \
+ desktop/source/deployment/registry/dp_registry \
+ desktop/source/deployment/registry/executable/dp_executable \
+ desktop/source/deployment/registry/executable/dp_executablebackenddb \
+ desktop/source/deployment/registry/help/dp_help \
+ desktop/source/deployment/registry/help/dp_helpbackenddb \
+ desktop/source/deployment/registry/package/dp_extbackenddb \
+ desktop/source/deployment/registry/package/dp_package \
+ desktop/source/deployment/registry/script/dp_lib_container \
+ desktop/source/deployment/registry/script/dp_script \
+ desktop/source/deployment/registry/script/dp_scriptbackenddb \
+ desktop/source/deployment/registry/sfwk/dp_parceldesc \
+ desktop/source/deployment/registry/sfwk/dp_sfwk \
+))
+
+ifneq (,$(filter DESKTOP,$(BUILD_TYPE)))
+
+$(eval $(call gb_Library_use_libraries,deployment,\
+ helplinker \
+))
+
+endif
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_deploymentgui.mk b/desktop/Library_deploymentgui.mk
new file mode 100644
index 000000000..900406002
--- /dev/null
+++ b/desktop/Library_deploymentgui.mk
@@ -0,0 +1,71 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,deploymentgui))
+
+$(eval $(call gb_Library_set_include,deploymentgui,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+ -I$(SRCDIR)/desktop/source/deployment/inc \
+ -I$(SRCDIR)/desktop/source/inc \
+))
+
+$(eval $(call gb_Library_use_external,deploymentgui,boost_headers))
+
+$(eval $(call gb_Library_set_precompiled_header,deploymentgui,desktop/inc/pch/precompiled_deploymentgui))
+
+$(eval $(call gb_Library_use_custom_headers,deploymentgui,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_sdk_api,deploymentgui))
+
+$(eval $(call gb_Library_use_libraries,deploymentgui,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ deploymentmisc \
+ i18nlangtag \
+ sal \
+ salhelper \
+ sfx \
+ svl \
+ svt \
+ svxcore \
+ tk \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+))
+
+ifeq ($(OS),WNT)
+
+$(eval $(call gb_Library_use_system_win32_libs,deploymentgui,\
+ ole32 \
+))
+
+endif
+
+
+$(eval $(call gb_Library_set_componentfile,deploymentgui,desktop/source/deployment/gui/deploymentgui))
+
+$(eval $(call gb_Library_add_exception_objects,deploymentgui,\
+ desktop/source/deployment/gui/dp_gui_dependencydialog \
+ desktop/source/deployment/gui/dp_gui_dialog2 \
+ desktop/source/deployment/gui/dp_gui_extensioncmdqueue \
+ desktop/source/deployment/gui/dp_gui_extlistbox \
+ desktop/source/deployment/gui/dp_gui_service \
+ desktop/source/deployment/gui/dp_gui_theextmgr \
+ desktop/source/deployment/gui/dp_gui_updatedialog \
+ desktop/source/deployment/gui/dp_gui_updateinstalldialog \
+ desktop/source/deployment/gui/license_dialog \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_deploymentmisc.mk b/desktop/Library_deploymentmisc.mk
new file mode 100644
index 000000000..b7f4c09e1
--- /dev/null
+++ b/desktop/Library_deploymentmisc.mk
@@ -0,0 +1,55 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,deploymentmisc))
+
+$(eval $(call gb_Library_set_include,deploymentmisc,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+ -I$(SRCDIR)/desktop/source/deployment/inc \
+))
+
+$(eval $(call gb_Library_use_external,deploymentmisc,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,deploymentmisc))
+
+$(eval $(call gb_Library_add_defs,deploymentmisc,\
+ -DDESKTOP_DEPLOYMENTMISC_DLLIMPLEMENTATION \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,deploymentmisc,desktop/inc/pch/precompiled_deploymentmisc))
+
+$(eval $(call gb_Library_use_libraries,deploymentmisc,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+ xmlscript \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_add_exception_objects,deploymentmisc,\
+ desktop/source/deployment/misc/dp_dependencies \
+ desktop/source/deployment/misc/dp_descriptioninfoset \
+ desktop/source/deployment/misc/dp_identifier \
+ desktop/source/deployment/misc/dp_interact \
+ desktop/source/deployment/misc/dp_misc \
+ desktop/source/deployment/misc/dp_platform \
+ desktop/source/deployment/misc/dp_resource \
+ desktop/source/deployment/misc/dp_ucb \
+ desktop/source/deployment/misc/dp_update \
+ desktop/source/deployment/misc/dp_version \
+ desktop/source/deployment/misc/lockfile \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_migrationoo2.mk b/desktop/Library_migrationoo2.mk
new file mode 100644
index 000000000..21f5c1db1
--- /dev/null
+++ b/desktop/Library_migrationoo2.mk
@@ -0,0 +1,37 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,migrationoo2))
+
+$(eval $(call gb_Library_use_external,migrationoo2,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,migrationoo2))
+
+$(eval $(call gb_Library_use_libraries,migrationoo2,\
+ cppu \
+ cppuhelper \
+ i18nlangtag \
+ $(if $(ENABLE_JAVA), \
+ jvmfwk) \
+ sal \
+ tl \
+ utl \
+))
+
+$(eval $(call gb_Library_set_componentfile,migrationoo2,desktop/source/migration/services/migrationoo2))
+
+$(eval $(call gb_Library_add_exception_objects,migrationoo2,\
+ desktop/source/migration/services/basicmigration \
+ desktop/source/migration/services/cexports \
+ $(if $(ENABLE_JAVA), \
+ desktop/source/migration/services/jvmfwk) \
+ desktop/source/migration/services/wordbookmigration \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_migrationoo3.mk b/desktop/Library_migrationoo3.mk
new file mode 100644
index 000000000..73c38bb29
--- /dev/null
+++ b/desktop/Library_migrationoo3.mk
@@ -0,0 +1,29 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,migrationoo3))
+
+$(eval $(call gb_Library_use_sdk_api,migrationoo3))
+
+$(eval $(call gb_Library_use_libraries,migrationoo3,\
+ cppu \
+ cppuhelper \
+ sal \
+ tl \
+ utl \
+))
+
+$(eval $(call gb_Library_set_componentfile,migrationoo3,desktop/source/migration/services/migrationoo3))
+
+$(eval $(call gb_Library_add_exception_objects,migrationoo3,\
+ desktop/source/migration/services/cexportsoo3 \
+ desktop/source/migration/services/oo3extensionmigration \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_offacc.mk b/desktop/Library_offacc.mk
new file mode 100644
index 000000000..b50dd4441
--- /dev/null
+++ b/desktop/Library_offacc.mk
@@ -0,0 +1,28 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,offacc))
+
+$(eval $(call gb_Library_use_sdk_api,offacc))
+
+$(eval $(call gb_Library_use_libraries,offacc,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ tl \
+))
+
+$(eval $(call gb_Library_set_componentfile,offacc,desktop/source/offacc/offacc))
+
+$(eval $(call gb_Library_add_exception_objects,offacc,\
+ desktop/source/offacc/acceptor \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_passive_native.mk b/desktop/Library_passive_native.mk
new file mode 100644
index 000000000..8bc14b5c4
--- /dev/null
+++ b/desktop/Library_passive_native.mk
@@ -0,0 +1,32 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,passive_native))
+
+$(eval $(call gb_Library_add_exception_objects,passive_native, \
+ desktop/test/deployment/passive/passive_native \
+))
+
+$(eval $(call gb_Library_set_componentfile,passive_native,desktop/test/deployment/passive/passive_native))
+
+$(eval $(call gb_Library_set_external_code,passive_native))
+
+$(eval $(call gb_Library_use_externals,passive_native, \
+ boost_headers \
+))
+
+$(eval $(call gb_Library_use_libraries,passive_native, \
+ cppu \
+ cppuhelper \
+ sal \
+))
+
+$(eval $(call gb_Library_use_sdk_api,passive_native))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Library_sofficeapp.mk b/desktop/Library_sofficeapp.mk
new file mode 100644
index 000000000..9dd91971a
--- /dev/null
+++ b/desktop/Library_sofficeapp.mk
@@ -0,0 +1,180 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,sofficeapp))
+
+$(eval $(call gb_Library_set_include,sofficeapp,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+ -I$(SRCDIR)/desktop/source/inc \
+ -I$(SRCDIR)/desktop/source/deployment/inc \
+))
+
+$(eval $(call gb_Library_add_libs,sofficeapp,\
+ $(if $(filter LINUX %BSD SOLARIS, $(OS)), \
+ $(DLOPEN_LIBS) \
+ ) \
+))
+
+$(eval $(call gb_Library_use_externals,sofficeapp, \
+ $(if $(ENABLE_BREAKPAD),breakpad) \
+ $(if $(filter OPENCL,$(BUILD_TYPE)),clew) \
+ boost_headers \
+ dbus \
+ icu_headers \
+ icui18n \
+ icuuc \
+ $(if $(filter-out iOS,$(OS)), \
+ curl \
+ )\
+ $(if $(ENABLE_ONLINE_UPDATE_MAR),\
+ orcus-parser \
+ orcus )\
+))
+
+$(eval $(call gb_Library_use_custom_headers,sofficeapp,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_api,sofficeapp,\
+ udkapi \
+ offapi \
+))
+
+$(eval $(call gb_Library_add_defs,sofficeapp,\
+ -DDESKTOP_DLLIMPLEMENTATION \
+ $(if $(filter WNT,$(OS)),-DENABLE_QUICKSTART_APPLET) \
+ $(if $(filter MACOSX,$(OS)),-DENABLE_QUICKSTART_APPLET) \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,sofficeapp,desktop/inc/pch/precompiled_sofficeapp))
+
+$(eval $(call gb_Library_use_libraries,sofficeapp,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ $(if $(ENABLE_BREAKPAD), \
+ crashreport \
+ ) \
+ deploymentmisc \
+ editeng \
+ i18nlangtag \
+ $(if $(filter OPENCL,$(BUILD_TYPE)),opencl) \
+ sal \
+ salhelper \
+ sb \
+ sfx \
+ svl \
+ svx \
+ svxcore \
+ svt \
+ tk \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+))
+
+ifeq ($(OS),WNT)
+$(eval $(call gb_Library_use_static_libraries,sofficeapp,\
+ $(if $(ENABLE_ONLINE_UPDATE_MAR),\
+ windows_process )\
+))
+endif
+
+ifeq ($(OS),MACOSX)
+
+$(eval $(call gb_Library_add_cxxflags,sofficeapp,\
+ $(gb_OBJCXXFLAGS) \
+))
+
+$(eval $(call gb_Library_use_system_darwin_frameworks,sofficeapp,\
+ Foundation \
+))
+
+endif
+
+ifeq ($(OS),iOS)
+
+$(eval $(call gb_Library_add_cflags,sofficeapp,\
+ $(gb_OBJCFLAGS) \
+))
+
+$(eval $(call gb_Library_add_cxxflags,sofficeapp,\
+ $(gb_OBJCXXFLAGS) \
+))
+
+endif
+
+$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
+ desktop/source/app/app \
+ desktop/source/app/appinit \
+ desktop/source/app/check_ext_deps \
+ desktop/source/app/cmdlineargs \
+ desktop/source/app/cmdlinehelp \
+ desktop/source/app/desktopcontext \
+ desktop/source/app/dispatchwatcher \
+ desktop/source/app/langselect \
+ desktop/source/app/lockfile2 \
+ desktop/source/app/officeipcthread \
+ desktop/source/app/opencl \
+ desktop/source/app/sofficemain \
+ $(if $(ENABLE_ONLINE_UPDATE_MAR),\
+ desktop/source/app/updater )\
+ desktop/source/app/userinstall \
+ desktop/source/migration/migration \
+))
+
+ifeq ($(DISABLE_GUI),TRUE)
+$(eval $(call gb_Library_add_libs,sofficeapp,\
+ -lm $(DLOPEN_LIBS) \
+))
+else
+ifeq ($(OS), $(filter LINUX %BSD SOLARIS, $(OS)))
+ifeq ($(USING_X11),TRUE)
+$(eval $(call gb_Library_use_static_libraries,sofficeapp,\
+ glxtest \
+))
+endif
+
+$(eval $(call gb_Library_add_libs,sofficeapp,\
+ -lm $(DLOPEN_LIBS) \
+ -lX11 \
+))
+endif
+endif
+
+# LibreOfficeKit bits
+ifneq ($(filter $(OS),ANDROID iOS MACOSX WNT),)
+$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
+ desktop/source/lib/init \
+ desktop/source/lib/lokinteractionhandler \
+ $(if $(filter-out $(OS),IOS), \
+ desktop/source/lib/lokclipboard) \
+ $(if $(filter $(OS),ANDROID), \
+ desktop/source/lib/lokandroid) \
+))
+else
+ifeq ($(USING_X11),TRUE)
+$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
+ desktop/source/lib/init \
+ desktop/source/lib/lokinteractionhandler \
+ desktop/source/lib/lokclipboard \
+))
+endif
+ifeq ($(DISABLE_GUI),TRUE)
+$(eval $(call gb_Library_add_exception_objects,sofficeapp,\
+ desktop/source/lib/init \
+ desktop/source/lib/lokinteractionhandler \
+ desktop/source/lib/lokclipboard \
+))
+endif
+endif
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_spl.mk b/desktop/Library_spl.mk
new file mode 100644
index 000000000..89c8e53b9
--- /dev/null
+++ b/desktop/Library_spl.mk
@@ -0,0 +1,40 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,spl))
+
+$(eval $(call gb_Library_set_include,spl,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+))
+
+$(eval $(call gb_Library_use_external,spl,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,spl))
+
+$(eval $(call gb_Library_use_libraries,spl,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+))
+
+$(eval $(call gb_Library_set_componentfile,spl,desktop/source/splash/spl))
+
+$(eval $(call gb_Library_add_exception_objects,spl,\
+ desktop/source/splash/services_spl \
+ desktop/source/splash/splash \
+ desktop/source/splash/unxsplash \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Library_unopkgapp.mk b/desktop/Library_unopkgapp.mk
new file mode 100644
index 000000000..b8978625c
--- /dev/null
+++ b/desktop/Library_unopkgapp.mk
@@ -0,0 +1,46 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,unopkgapp))
+
+$(eval $(call gb_Library_set_include,unopkgapp,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/inc \
+ -I$(SRCDIR)/desktop/source/deployment/inc \
+ -I$(SRCDIR)/desktop/source/inc \
+))
+
+$(eval $(call gb_Library_use_external,unopkgapp,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,unopkgapp))
+
+$(eval $(call gb_Library_add_defs,unopkgapp,\
+ -DDESKTOP_DLLIMPLEMENTATION \
+))
+
+$(eval $(call gb_Library_use_libraries,unopkgapp,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ deploymentmisc \
+ sal \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_add_exception_objects,unopkgapp,\
+ desktop/source/pkgchk/unopkg/unopkg_app \
+ desktop/source/pkgchk/unopkg/unopkg_cmdenv \
+ desktop/source/pkgchk/unopkg/unopkg_misc \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Makefile b/desktop/Makefile
new file mode 100644
index 000000000..0997e6284
--- /dev/null
+++ b/desktop/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Module_desktop.mk b/desktop/Module_desktop.mk
new file mode 100644
index 000000000..1c8bdb569
--- /dev/null
+++ b/desktop/Module_desktop.mk
@@ -0,0 +1,153 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Module_Module,desktop))
+
+$(eval $(call gb_Module_add_targets,desktop,\
+ CustomTarget_desktop_unopackages_install \
+ GeneratedPackage_desktop_unopackages_install \
+ Library_deployment \
+ Library_deploymentgui \
+ Library_deploymentmisc \
+ Library_offacc \
+ Library_sofficeapp \
+ $(if $(ENABLE_BREAKPAD), \
+ Library_crashreport \
+ ) \
+ $(if $(DISABLE_GUI),,Library_spl) \
+ Package_branding \
+ $(if $(CUSTOM_BRAND_DIR),Package_branding_custom) \
+ UIConfig_deployment \
+))
+
+$(eval $(call gb_Module_add_l10n_targets,desktop,\
+ AllLangMoTarget_dkt \
+))
+
+ifneq (,$(filter DESKTOP,$(BUILD_TYPE)))
+$(eval $(call gb_Module_add_targets,desktop,\
+ Executable_soffice_bin \
+ Executable_unopkg_bin \
+ $(if $(ENABLE_BREAKPAD),Executable_minidump_upload) \
+ Library_migrationoo2 \
+ Library_migrationoo3 \
+ Library_unopkgapp \
+ Package_scripts \
+))
+
+ifneq ($(OS),MACOSX)
+ifneq ($(OS),WNT)
+$(eval $(call gb_Module_add_targets,desktop,\
+ Pagein_calc \
+ Pagein_common \
+ Pagein_draw \
+ Pagein_impress \
+ Pagein_writer \
+ CustomTarget_soffice \
+))
+
+ifeq ($(USING_X11), TRUE)
+$(eval $(call gb_Module_add_targets,desktop,\
+ Package_sbase_sh \
+ Package_scalc_sh \
+ Package_sdraw_sh \
+ Package_simpress_sh \
+ Package_smath_sh \
+ Package_swriter_sh \
+ Package_soffice_sh \
+))
+endif
+endif
+endif
+endif
+
+ifeq ($(OS),WNT)
+
+$(eval $(call gb_Module_add_targets,desktop,\
+ StaticLibrary_winloader \
+ StaticLibrary_winlauncher \
+ Executable_quickstart \
+ Executable_sbase \
+ Executable_scalc \
+ Executable_sdraw \
+ Executable_simpress \
+ Executable_smath \
+ Executable_soffice_exe \
+ Executable_soffice_com \
+ Executable_soffice_safe \
+ Executable_sweb \
+ Executable_swriter \
+ Executable_unoinfo \
+ Executable_unopkg \
+ Executable_unopkg_com \
+ WinResTarget_quickstart \
+ WinResTarget_sbase \
+ WinResTarget_scalc \
+ WinResTarget_sdraw \
+ WinResTarget_simpress \
+ WinResTarget_soffice \
+ WinResTarget_sofficebin \
+ WinResTarget_smath \
+ WinResTarget_sweb \
+ WinResTarget_swriter \
+))
+
+else ifeq ($(OS),MACOSX)
+
+else ifeq ($(OS),ANDROID)
+
+else ifeq ($(OS),iOS)
+
+else ifeq ($(OS),HAIKU)
+
+else
+
+$(eval $(call gb_Module_add_targets,desktop,\
+ Executable_oosplash \
+))
+
+endif
+
+ifneq (,$(filter Extension_test-active,$(MAKECMDGOALS)))
+$(eval $(call gb_Module_add_targets,desktop, \
+ Extension_test-active \
+ Jar_active_java \
+ Library_active_native \
+))
+endif
+
+ifneq (,$(filter Extension_test-passive,$(MAKECMDGOALS)))
+$(eval $(call gb_Module_add_targets,desktop, \
+ Extension_test-passive \
+ Jar_passive_java \
+ Library_passive_native \
+ Pyuno_passive_python \
+ Rdb_passive_generic \
+ Rdb_passive_platform \
+))
+endif
+
+$(eval $(call gb_Module_add_check_targets,desktop, \
+ CppunitTest_desktop_app \
+ CppunitTest_desktop_version \
+))
+
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Module_add_check_targets,desktop, \
+ CppunitTest_desktop_lib \
+ CppunitTest_desktop_lokinit \
+))
+endif
+
+# screenshots
+$(eval $(call gb_Module_add_screenshot_targets,desktop,\
+ CppunitTest_desktop_dialogs_test \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Package_branding.mk b/desktop/Package_branding.mk
new file mode 100644
index 000000000..bca87bab3
--- /dev/null
+++ b/desktop/Package_branding.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_branding,$(SRCDIR)/icon-themes/colibre))
+
+$(eval $(call gb_Package_add_files,desktop_branding,$(LIBO_ETC_FOLDER),\
+ $(foreach image,$(filter $(BRAND_INTRO_IMAGES),$(DEFAULT_BRAND_IMAGES)),\
+ $(if $(filter intro.png,$(image)),\
+ $(if $(ENABLE_RELEASE_BUILD),brand,brand_dev)/$(image),\
+ brand/$(image) \
+ ) \
+ ) \
+))
+
+$(eval $(call gb_Package_add_files,desktop_branding,$(LIBO_ETC_FOLDER)/shell,\
+ $(addprefix brand/shell/,$(filter-out $(BRAND_INTRO_IMAGES),$(DEFAULT_BRAND_IMAGES))) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Package_branding_custom.mk b/desktop/Package_branding_custom.mk
new file mode 100644
index 000000000..c2b6181f9
--- /dev/null
+++ b/desktop/Package_branding_custom.mk
@@ -0,0 +1,20 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_branding_custom,$(CUSTOM_BRAND_DIR)))
+
+$(eval $(call gb_Package_add_files,desktop_branding_custom,$(LIBO_ETC_FOLDER),\
+ $(filter $(BRAND_INTRO_IMAGES),$(CUSTOM_BRAND_IMAGES)) \
+))
+
+$(eval $(call gb_Package_add_files,desktop_branding_custom,$(LIBO_ETC_FOLDER)/shell,\
+ $(filter-out $(BRAND_INTRO_IMAGES),$(CUSTOM_BRAND_IMAGES)) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Package_sbase_sh.mk b/desktop/Package_sbase_sh.mk
new file mode 100644
index 000000000..ada63e2d5
--- /dev/null
+++ b/desktop/Package_sbase_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_sbase_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_sbase_sh,$(LIBO_BIN_FOLDER)/sbase,sbase.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_scalc_sh.mk b/desktop/Package_scalc_sh.mk
new file mode 100644
index 000000000..5d29e1625
--- /dev/null
+++ b/desktop/Package_scalc_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_scalc_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_scalc_sh,$(LIBO_BIN_FOLDER)/scalc,scalc.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_scripts.mk b/desktop/Package_scripts.mk
new file mode 100644
index 000000000..a806c72ef
--- /dev/null
+++ b/desktop/Package_scripts.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_scripts_install,$(SRCDIR)/desktop/scripts))
+
+ifeq (,$(filter MACOSX WNT,$(OS)))
+
+$(eval $(call gb_Package_add_file,desktop_scripts_install,$(LIBO_BIN_FOLDER)/gdbtrace,gdbtrace))
+$(eval $(call gb_Package_add_file,desktop_scripts_install,$(LIBO_BIN_FOLDER)/unopkg,unopkg.sh))
+
+endif
+
+ifneq ($(OS),WNT)
+
+$(eval $(call gb_Package_add_file,desktop_scripts_install,$(LIBO_BIN_FOLDER)/unoinfo,$(if $(filter MACOSX,$(OS)),unoinfo-mac.sh,unoinfo.sh)))
+
+endif
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_sdraw_sh.mk b/desktop/Package_sdraw_sh.mk
new file mode 100644
index 000000000..58ae0f5fd
--- /dev/null
+++ b/desktop/Package_sdraw_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_sdraw_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_sdraw_sh,$(LIBO_BIN_FOLDER)/sdraw,sdraw.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_simpress_sh.mk b/desktop/Package_simpress_sh.mk
new file mode 100644
index 000000000..bb315aa77
--- /dev/null
+++ b/desktop/Package_simpress_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_simpress_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_simpress_sh,$(LIBO_BIN_FOLDER)/simpress,simpress.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_smath_sh.mk b/desktop/Package_smath_sh.mk
new file mode 100644
index 000000000..67c113a20
--- /dev/null
+++ b/desktop/Package_smath_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_smath_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_smath_sh,$(LIBO_BIN_FOLDER)/smath,smath.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Package_soffice_sh.mk b/desktop/Package_soffice_sh.mk
new file mode 100644
index 000000000..046c95f45
--- /dev/null
+++ b/desktop/Package_soffice_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_soffice_sh,$(call gb_CustomTarget_get_workdir,desktop/soffice)))
+
+$(eval $(call gb_Package_add_file,desktop_soffice_sh,$(LIBO_BIN_FOLDER)/soffice,soffice.sh))
+
+# vim:set noet sw=4 ts=4:
diff --git a/desktop/Package_swriter_sh.mk b/desktop/Package_swriter_sh.mk
new file mode 100644
index 000000000..88720a932
--- /dev/null
+++ b/desktop/Package_swriter_sh.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,desktop_swriter_sh,$(SRCDIR)/desktop/scripts))
+
+$(eval $(call gb_Package_add_file,desktop_swriter_sh,$(LIBO_BIN_FOLDER)/swriter,swriter.sh))
+
+# vim: set ts=4 sw=4 noet:
diff --git a/desktop/Pagein_calc.mk b/desktop/Pagein_calc.mk
new file mode 100644
index 000000000..9d7ac0a6c
--- /dev/null
+++ b/desktop/Pagein_calc.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pagein_Pagein,calc))
+
+$(eval $(call gb_Pagein_add_objects,calc,\
+ sc \
+ scui \
+ svx \
+ svxcore \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Pagein_common.mk b/desktop/Pagein_common.mk
new file mode 100644
index 000000000..c1661a39b
--- /dev/null
+++ b/desktop/Pagein_common.mk
@@ -0,0 +1,72 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pagein_Pagein,common))
+
+# sorted in approx. reverse load order (ld.so.1)
+$(eval $(call gb_Pagein_add_objects,common,\
+ $(if $(MERGELIBS),merged) \
+ i18nlangtag \
+ $(if $(SYSTEM_ICU),,\
+ libicui18n$(gb_Library_DLLEXT).$(ICU_MAJOR) \
+ libicuuc$(gb_Library_DLLEXT).$(ICU_MAJOR) \
+ ) \
+ lng \
+ xo \
+ fwe \
+ fwk \
+ fwi \
+ fwl \
+ package2 \
+ ucpfile1 \
+ ucb1 \
+ configmgr \
+ vclplug_gen \
+ $(if $(findstring TRUE,$(ENABLE_GTK3)),vclplug_gtk3) \
+ basegfx \
+ sot \
+ xmlscript \
+ sb \
+ stocservices \
+ bootstrap \
+ reg \
+ store \
+ reflection \
+ cppuhelper \
+ cppu \
+ sal \
+ ucbhelper \
+ comphelper \
+ tl \
+ utl \
+ svl \
+ vcl \
+ tk \
+ types.rdb \
+ services/services.rdb \
+ types/oovbaapi.rdb \
+ deployment \
+ deploymentmisc \
+ xstor \
+ filterconfig \
+ uui \
+ svt \
+ spl \
+ avmedia \
+ helplinker \
+ sax \
+ fsstorage \
+ desktopbe1 \
+ localebe1 \
+ ucpexpand1 \
+ sfx \
+ sofficeapp \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Pagein_draw.mk b/desktop/Pagein_draw.mk
new file mode 100644
index 000000000..2fd0ef6ba
--- /dev/null
+++ b/desktop/Pagein_draw.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pagein_Pagein,draw))
+
+$(eval $(call gb_Pagein_add_objects,draw,\
+ sd \
+ sdui \
+ svx \
+ svxcore \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Pagein_impress.mk b/desktop/Pagein_impress.mk
new file mode 100644
index 000000000..f3e4ff7b6
--- /dev/null
+++ b/desktop/Pagein_impress.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pagein_Pagein,impress))
+
+$(eval $(call gb_Pagein_add_objects,impress,\
+ sd \
+ sdui \
+ svx \
+ svxcore \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Pagein_writer.mk b/desktop/Pagein_writer.mk
new file mode 100644
index 000000000..eed7403f4
--- /dev/null
+++ b/desktop/Pagein_writer.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pagein_Pagein,writer))
+
+$(eval $(call gb_Pagein_add_objects,writer,\
+ sw \
+ swui \
+ svx \
+ svxcore \
+))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/Pyuno_passive_python.mk b/desktop/Pyuno_passive_python.mk
new file mode 100644
index 000000000..348de4aac
--- /dev/null
+++ b/desktop/Pyuno_passive_python.mk
@@ -0,0 +1,18 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Pyuno_Pyuno,passive_python,$(SRCDIR)/desktop/test/deployment/passive))
+
+$(eval $(call gb_Pyuno_add_files,passive_python,,\
+ passive_python.py \
+))
+
+$(eval $(call gb_Pyuno_set_componentfile_full,passive_python,desktop/test/deployment/passive/passive_python,./,passive_python.py))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/README b/desktop/README
new file mode 100644
index 000000000..a49f0051a
--- /dev/null
+++ b/desktop/README
@@ -0,0 +1,36 @@
+What used to be the desktop in StarOffice 5 - now the binary.
+
+
+Stable Interface
+================
+
+Some of the artifacts built here are part of a LibreOffice installation set's
+stable interface, which (programmatic) clients can depend on. Among them are:
+
+soffice
+=======
+
+In the "program" directory ("program/" on Linux and Windows, "Contents/MacOS/"
+on macOS).
+
+unoinfo
+=======
+
+In the "program" directory ("program/" on Linux and Windows, "Contents/MacOS/"
+on macOS).
+
+When called with a sole argument of "c++", it prints to stdout an absolute
+pathname denoting the directory where the public URE libraries are found.
+
+When called with a sole argument of "java", it prints to stdout a marker
+character (either an ASCII '0' or '1') followed by a sequence of zero or more
+absolute pathnames denoting jars or directories that need to be included in a
+class loader's search locations.
+
+If the marker character is '0' (on Linux and macOS), the pathnames are
+encoded as bytes, and any two pathnames in the sequence are separated from each
+other by NUL bytes.
+
+If the marker character is '1' (on Windows), the pathnames are encoded as
+UTF-16-LE two-byte code units, and any two pathnames in the sequence are
+separated from each other by two-byte NUL code units.
diff --git a/desktop/README.vars b/desktop/README.vars
new file mode 100644
index 000000000..67c605698
--- /dev/null
+++ b/desktop/README.vars
@@ -0,0 +1,15 @@
+Environment variables in desktop:
+
+General
+-------
+
+DISPLAY - X11 display to use.
+OOO_DISABLE_RECOVERY - Disables the recovery dialog.
+OOO_EXIT_POST_STARTUP - Exit right after startup, for profiling purposes.
+SAL_DISABLE_USERMIGRATION - Disable automatic conversion of old user configurations.
+SAL_USE_VCLPLUGIN - Which VCL plugin to use instead of the auto-detected one.
+
+LibreOfficeKit
+--------------
+
+LOK_DEBUG - Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
diff --git a/desktop/Rdb_passive_generic.mk b/desktop/Rdb_passive_generic.mk
new file mode 100644
index 000000000..f05d9c26c
--- /dev/null
+++ b/desktop/Rdb_passive_generic.mk
@@ -0,0 +1,17 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Rdb_Rdb,passive_generic))
+
+$(eval $(call gb_Rdb_add_components,passive_generic, \
+ desktop/test/deployment/passive/passive_java \
+ desktop/test/deployment/passive/passive_python \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/Rdb_passive_platform.mk b/desktop/Rdb_passive_platform.mk
new file mode 100644
index 000000000..72ffe6c19
--- /dev/null
+++ b/desktop/Rdb_passive_platform.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Rdb_Rdb,passive_platform))
+
+$(eval $(call gb_Rdb_add_components,passive_platform, \
+ desktop/test/deployment/passive/passive_native \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/StaticLibrary_winlauncher.mk b/desktop/StaticLibrary_winlauncher.mk
new file mode 100644
index 000000000..1fed934f1
--- /dev/null
+++ b/desktop/StaticLibrary_winlauncher.mk
@@ -0,0 +1,17 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_StaticLibrary_StaticLibrary,winlauncher))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,winlauncher,\
+ desktop/win32/source/applauncher/launcher \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/desktop/StaticLibrary_winloader.mk b/desktop/StaticLibrary_winloader.mk
new file mode 100644
index 000000000..abee2aa3e
--- /dev/null
+++ b/desktop/StaticLibrary_winloader.mk
@@ -0,0 +1,21 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_StaticLibrary_StaticLibrary,winloader))
+
+$(eval $(call gb_StaticLibrary_use_externals,winloader,\
+ boost_headers \
+))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,winloader,\
+ desktop/win32/source/loader \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/desktop/UIConfig_deployment.mk b/desktop/UIConfig_deployment.mk
new file mode 100644
index 000000000..80e5e96c1
--- /dev/null
+++ b/desktop/UIConfig_deployment.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UIConfig_UIConfig,desktop))
+
+$(eval $(call gb_UIConfig_add_uifiles,desktop,\
+ desktop/uiconfig/ui/dependenciesdialog \
+ desktop/uiconfig/ui/extensionmanager \
+ desktop/uiconfig/ui/extensionmenu \
+ desktop/uiconfig/ui/installforalldialog \
+ desktop/uiconfig/ui/licensedialog \
+ desktop/uiconfig/ui/showlicensedialog \
+ desktop/uiconfig/ui/updatedialog \
+ desktop/uiconfig/ui/updateinstalldialog \
+ desktop/uiconfig/ui/updaterequireddialog \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/desktop/WinResTarget_quickstart.mk b/desktop/WinResTarget_quickstart.mk
new file mode 100644
index 000000000..0c06f5990
--- /dev/null
+++ b/desktop/WinResTarget_quickstart.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,quickstart/QuickStart))
+
+$(eval $(call gb_WinResTarget_set_include,quickstart/QuickStart,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/desktop/win32/source/QuickStart \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,quickstart/QuickStart,\
+ sysui/desktop/icons/soffice.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,quickstart/QuickStart,desktop/win32/source/QuickStart/QuickStart))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_sbase.mk b/desktop/WinResTarget_sbase.mk
new file mode 100644
index 000000000..8ff57b91a
--- /dev/null
+++ b/desktop/WinResTarget_sbase.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,sbase/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,sbase/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,sbase/launcher,\
+ -DRES_APP_ICON=icons/base_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,sbase/launcher,\
+ sysui/desktop/icons/base_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,sbase/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_scalc.mk b/desktop/WinResTarget_scalc.mk
new file mode 100644
index 000000000..7060dcb77
--- /dev/null
+++ b/desktop/WinResTarget_scalc.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,scalc/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,scalc/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,scalc/launcher,\
+ -DRES_APP_ICON=icons/calc_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,scalc/launcher,\
+ sysui/desktop/icons/calc_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,scalc/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_sdraw.mk b/desktop/WinResTarget_sdraw.mk
new file mode 100644
index 000000000..920a4625b
--- /dev/null
+++ b/desktop/WinResTarget_sdraw.mk
@@ -0,0 +1,28 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,sdraw/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,sdraw/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,sdraw/launcher,\
+ -DRES_APP_ICON=icons/draw_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,sdraw/launcher,\
+ sysui/desktop/icons/draw_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,sdraw/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
+
diff --git a/desktop/WinResTarget_simpress.mk b/desktop/WinResTarget_simpress.mk
new file mode 100644
index 000000000..f5d11a6a4
--- /dev/null
+++ b/desktop/WinResTarget_simpress.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,simpress/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,simpress/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,simpress/launcher,\
+ -DRES_APP_ICON=icons/impress_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,simpress/launcher,\
+ sysui/desktop/icons/impress_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,simpress/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_smath.mk b/desktop/WinResTarget_smath.mk
new file mode 100644
index 000000000..0ad3ee938
--- /dev/null
+++ b/desktop/WinResTarget_smath.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,smath/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,smath/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,smath/launcher,\
+ -DRES_APP_ICON=icons/math_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,smath/launcher,\
+ sysui/desktop/icons/math_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,smath/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_soffice.mk b/desktop/WinResTarget_soffice.mk
new file mode 100644
index 000000000..990eb5c98
--- /dev/null
+++ b/desktop/WinResTarget_soffice.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,soffice/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,soffice/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,soffice/launcher,\
+ -DRES_APP_ICON=icons/soffice.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,soffice/launcher,\
+ sysui/desktop/icons/soffice.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,soffice/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_sofficebin.mk b/desktop/WinResTarget_sofficebin.mk
new file mode 100644
index 000000000..139de9644
--- /dev/null
+++ b/desktop/WinResTarget_sofficebin.mk
@@ -0,0 +1,51 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,sofficebin/officeloader))
+
+$(eval $(call gb_WinResTarget_set_include,sofficebin/officeloader,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,sofficebin/officeloader,\
+ -DRES_APP_ICON=icons/soffice.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,sofficebin/officeloader,\
+ sysui/desktop/icons/soffice.ico \
+ sysui/desktop/icons/oasis-database.ico \
+ sysui/desktop/icons/oasis-drawing-template.ico \
+ sysui/desktop/icons/oasis-drawing.ico \
+ sysui/desktop/icons/oasis-formula.ico \
+ sysui/desktop/icons/oasis-master-document.ico \
+ sysui/desktop/icons/oasis-presentation-template.ico \
+ sysui/desktop/icons/oasis-presentation.ico \
+ sysui/desktop/icons/oasis-spreadsheet-template.ico \
+ sysui/desktop/icons/oasis-spreadsheet.ico \
+ sysui/desktop/icons/oasis-text-template.ico \
+ sysui/desktop/icons/oasis-text.ico \
+ sysui/desktop/icons/oasis-web-template.ico \
+ sysui/desktop/icons/database.ico \
+ sysui/desktop/icons/drawing-template.ico \
+ sysui/desktop/icons/drawing.ico \
+ sysui/desktop/icons/formula.ico \
+ sysui/desktop/icons/master-document.ico \
+ sysui/desktop/icons/presentation-template.ico \
+ sysui/desktop/icons/presentation.ico \
+ sysui/desktop/icons/spreadsheet-template.ico \
+ sysui/desktop/icons/spreadsheet.ico \
+ sysui/desktop/icons/text-template.ico \
+ sysui/desktop/icons/text.ico \
+ sysui/desktop/icons/oxt-extension.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,sofficebin/officeloader,desktop/util/officeloader))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_sweb.mk b/desktop/WinResTarget_sweb.mk
new file mode 100644
index 000000000..e6fd28343
--- /dev/null
+++ b/desktop/WinResTarget_sweb.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,sweb/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,sweb/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,sweb/launcher,\
+ -DRES_APP_ICON=icons/writer_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,sweb/launcher,\
+ sysui/desktop/icons/writer_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,sweb/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/WinResTarget_swriter.mk b/desktop/WinResTarget_swriter.mk
new file mode 100644
index 000000000..1136c61bb
--- /dev/null
+++ b/desktop/WinResTarget_swriter.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,swriter/launcher))
+
+$(eval $(call gb_WinResTarget_set_include,swriter/launcher,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sysui/desktop \
+))
+
+$(eval $(call gb_WinResTarget_add_defs,swriter/launcher,\
+ -DRES_APP_ICON=icons/writer_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_add_dependencies,swriter/launcher,\
+ sysui/desktop/icons/writer_app.ico \
+))
+
+$(eval $(call gb_WinResTarget_set_rcfile,swriter/launcher,desktop/win32/source/applauncher/launcher))
+
+# vim: set ts=4 sw=4 et:
diff --git a/desktop/inc/app.hxx b/desktop/inc/app.hxx
new file mode 100644
index 000000000..5b388493e
--- /dev/null
+++ b/desktop/inc/app.hxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_APP_HXX
+#define INCLUDED_DESKTOP_INC_APP_HXX
+
+#include <optional>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <unotools/bootstrap.hxx>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Reference.h>
+
+#include <memory>
+#include <thread>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace desktop
+{
+
+/*--------------------------------------------------------------------
+ Description: Application-class
+ --------------------------------------------------------------------*/
+class CommandLineArgs;
+class Lockfile;
+struct ConvertData;
+class Desktop final : public Application
+{
+ int doShutdown();
+
+ public:
+ enum BootstrapError
+ {
+ BE_OK,
+ BE_UNO_SERVICEMANAGER,
+ BE_UNO_SERVICE_CONFIG_MISSING,
+ BE_PATHINFO_MISSING,
+ BE_USERINSTALL_FAILED,
+ BE_LANGUAGE_MISSING,
+ BE_USERINSTALL_NOTENOUGHDISKSPACE,
+ BE_USERINSTALL_NOWRITEACCESS,
+ BE_OFFICECONFIG_BROKEN
+ };
+ enum BootstrapStatus
+ {
+ BS_OK,
+ BS_TERMINATE
+ };
+
+ Desktop();
+ virtual ~Desktop() override;
+ virtual int Main( ) override;
+ virtual void Init() override;
+ virtual void InitFinished() override;
+ virtual void DeInit() override;
+ virtual bool QueryExit() override;
+ virtual void Shutdown() override;
+ virtual void Exception(ExceptionCategory nCategory) override;
+ virtual void OverrideSystemSettings( AllSettings& rSettings ) override;
+ virtual void AppEvent( const ApplicationEvent& rAppEvent ) override;
+
+ DECL_LINK( OpenClients_Impl, void*, void );
+
+ static void OpenClients();
+ static void OpenDefault();
+ static void CheckOpenCLCompute(const css::uno::Reference<css::frame::XDesktop2> &);
+
+ DECL_STATIC_LINK( Desktop, EnableAcceptors_Impl, void*, void);
+
+ static void HandleAppEvent( const ApplicationEvent& rAppEvent );
+ static CommandLineArgs& GetCommandLineArgs();
+
+ static void HandleBootstrapErrors(
+ BootstrapError nError, OUString const & aMessage );
+ void SetBootstrapError(
+ BootstrapError nError, OUString const & aMessage )
+ {
+ if ( m_aBootstrapError == BE_OK )
+ {
+ SAL_INFO("desktop.app", "SetBootstrapError: " << nError << " '" << aMessage << "'");
+ m_aBootstrapError = nError;
+ m_aBootstrapErrorMessage = aMessage;
+ }
+ }
+
+ void SetBootstrapStatus( BootstrapStatus nStatus )
+ {
+ m_aBootstrapStatus = nStatus;
+ }
+ BootstrapStatus GetBootstrapStatus() const
+ {
+ return m_aBootstrapStatus;
+ }
+
+ // first-start (ever) related methods
+ static bool CheckExtensionDependencies();
+
+ static void SynchronizeExtensionRepositories(bool bCleanedExtensionCache, Desktop* pDesktop = nullptr);
+ void SetSplashScreenText( const OUString& rText );
+ void SetSplashScreenProgress( sal_Int32 );
+
+ // Bootstrap methods
+ static void InitApplicationServiceManager();
+ // throws an exception upon failure
+
+ private:
+ void RegisterServices(
+ css::uno::Reference< css::uno::XComponentContext > const & context);
+ static void DeregisterServices();
+
+ public:
+ static void CreateTemporaryDirectory();
+ static void RemoveTemporaryDirectory();
+
+ private:
+ static bool InitializeConfiguration();
+ static void FlushConfiguration();
+ static bool InitializeQuickstartMode( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ static void HandleBootstrapPathErrors( ::utl::Bootstrap::Status, const OUString& aMsg );
+
+ // Create an error message depending on bootstrap failure code and an optional file url
+ static OUString CreateErrorMsgString( utl::Bootstrap::FailureCode nFailureCode,
+ const OUString& aFileURL );
+
+ css::uno::Reference<css::task::XStatusIndicator> m_rSplashScreen;
+ void OpenSplashScreen();
+ void CloseSplashScreen();
+
+ DECL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, bool );
+ DECL_STATIC_LINK( Desktop, AsyncInitFirstRun, Timer*, void );
+ /** checks if the office is run the first time
+ <p>If so, <method>DoFirstRunInitializations</method> is called (asynchronously and delayed) and the
+ respective flag in the configuration is reset.</p>
+ */
+ void CheckFirstRun( );
+
+ static void ShowBackingComponent(Desktop * progress);
+
+ // on-demand acceptors
+ static void createAcceptor(const OUString& aDescription);
+ static void destroyAcceptor(const OUString& aDescription);
+
+ bool m_bCleanedExtensionCache;
+ bool m_bServicesRegistered;
+ BootstrapError m_aBootstrapError;
+ OUString m_aBootstrapErrorMessage;
+ BootstrapStatus m_aBootstrapStatus;
+
+ std::unique_ptr<Lockfile> m_xLockfile;
+ Timer m_firstRunTimer;
+ std::thread m_aUpdateThread;
+};
+
+OUString GetURL_Impl(
+ const OUString& rName, std::optional< OUString > const & cwdUrl );
+
+OUString ReplaceStringHookProc(const OUString& rStr);
+
+}
+
+#endif // INCLUDED_DESKTOP_INC_APP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/bitmaps.hlst b/desktop/inc/bitmaps.hlst
new file mode 100644
index 000000000..7a166d5e0
--- /dev/null
+++ b/desktop/inc/bitmaps.hlst
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_BITMAPS_HRC
+#define INCLUDED_DESKTOP_INC_BITMAPS_HRC
+
+#define RID_BMP_WARNING "desktop/res/caution_16.png"
+#define RID_BMP_LOCKED "desktop/res/lock_16.png"
+#define RID_BMP_SHARED "desktop/res/shared_16.png"
+#define RID_BMP_EXTENSION "desktop/res/extension_32.png"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/desktop/inc/dp_misc.h b/desktop/inc/dp_misc.h
new file mode 100644
index 000000000..204132210
--- /dev/null
+++ b/desktop/inc/dp_misc.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_DP_MISC_H
+#define INCLUDED_DESKTOP_INC_DP_MISC_H
+
+#include <osl/mutex.hxx>
+#include <osl/process.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <dp_misc_api.hxx>
+
+namespace dp_misc {
+
+const char CR = 0x0d;
+const char LF = 0x0a;
+
+
+class MutexHolder
+{
+ mutable ::osl::Mutex m_mutex;
+protected:
+ ::osl::Mutex & getMutex() const { return m_mutex; }
+};
+
+
+inline void try_dispose( css::uno::Reference< css::uno::XInterface> const & x )
+{
+ css::uno::Reference< css::lang::XComponent> xComp( x, css::uno::UNO_QUERY );
+ if (xComp.is())
+ xComp->dispose();
+}
+
+
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString expandUnoRcTerm( OUString const & term );
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString makeRcTerm( OUString const & url );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString expandUnoRcUrl( OUString const & url );
+
+
+
+/** appends a relative path to a url.
+
+ The relative path must already be correctly encoded for use in a URL.
+ If the URL starts with vnd.sun.star.expand then the relative path will
+ be again encoded for use in an "expand" URL.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString makeURL(
+ OUString const & baseURL, OUString const & relPath );
+
+
+/** appends a relative path to a url.
+
+ This is the same as makeURL, but the relative Path must me a segment
+ of an system path.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString makeURLAppendSysPathSegment(
+ OUString const & baseURL, OUString const & relPath );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateRandomPipeId();
+
+class AbortChannel;
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+css::uno::Reference< css::uno::XInterface> resolveUnoURL(
+ OUString const & connectString,
+ css::uno::Reference< css::uno::XComponentContext> const & xLocalContext,
+ AbortChannel const * abortChannel = nullptr );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool office_is_running();
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+oslProcess raiseProcess( OUString const & appURL,
+ css::uno::Sequence< OUString > const & args );
+
+
+
+/** writes the argument string to the console.
+ It converts the UTF16 string to an ANSI string using osl_getThreadTextEncoding()
+ as target encoding.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+void writeConsole(OUString const & sText);
+
+/** writes the argument to the console using the error stream.
+ Otherwise the same as writeConsole.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+void writeConsoleError(OUString const & sText);
+
+
+/** reads from the console.
+ It uses fgets to read char values and converts them to OUString using
+ osl_getThreadTextEncoding as target encoding. The returned string has a maximum size of
+ 1024 and does NOT include leading and trailing white space(applied OUString::trim())
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString readConsole();
+
+/** print the text to the console in a debug build.
+ The argument is forwarded to writeConsole. The function does not add new line.
+ The code is only executed if OSL_DEBUG_LEVEL > 1
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+void TRACE(OUString const & sText);
+
+/** registers or revokes shared or bundled extensions which have been
+ recently added or removed.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+void syncRepositories(
+ bool force,
+ css::uno::Reference<
+ css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+/** workaround: for some reason the bridge threads which communicate with the
+ uno.exe process are not released on time
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+void disposeBridges(
+ css::uno::Reference< css::uno::XComponentContext >
+ const & ctx);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/dp_shared.hxx b/desktop/inc/dp_shared.hxx
new file mode 100644
index 000000000..b4f86e445
--- /dev/null
+++ b/desktop/inc/dp_shared.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_SHARED_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_SHARED_HXX
+
+#include <rtl/instance.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace dp {
+
+struct DeploymentLocale :
+ public ::rtl::StaticWithInit<std::locale, DeploymentLocale > {
+ std::locale operator () () {
+ return Translate::Create("dkt");
+ }
+};
+
+} // namespace dp
+
+inline OUString DpResId(const char* pId)
+{
+ return Translate::get(pId, dp::DeploymentLocale::get());
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
new file mode 100644
index 000000000..568c1fe9e
--- /dev/null
+++ b/desktop/inc/lib/init.hxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_LIB_INIT_HXX
+#define INCLUDED_DESKTOP_INC_LIB_INIT_HXX
+
+#include <map>
+#include <unordered_map>
+#include <memory>
+#include <mutex>
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/variant.hpp>
+
+#include <osl/thread.h>
+#include <rtl/ref.hxx>
+#include <vcl/idle.hxx>
+#include <LibreOfficeKit/LibreOfficeKit.h>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <tools/gen.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <desktop/dllapi.h>
+
+class LOKInteractionHandler;
+
+namespace desktop {
+
+ /// Represents an invalidated rectangle inside a given document part.
+ struct RectangleAndPart
+ {
+ tools::Rectangle m_aRectangle;
+ int m_nPart;
+
+ RectangleAndPart()
+ : m_nPart(INT_MIN) // -1 is reserved to mean "all parts".
+ {
+ }
+
+ OString toString() const
+ {
+ std::stringstream ss;
+ ss << m_aRectangle.toString();
+ if (m_nPart >= -1)
+ ss << ", " << m_nPart;
+ return ss.str().c_str();
+ }
+
+ /// Infinite Rectangle is both sides are
+ /// equal or longer than SfxLokHelper::MaxTwips.
+ bool isInfinite() const
+ {
+ return m_aRectangle.GetWidth() >= SfxLokHelper::MaxTwips &&
+ m_aRectangle.GetHeight() >= SfxLokHelper::MaxTwips;
+ }
+
+ /// Empty Rectangle is when it has zero dimensions.
+ bool isEmpty() const
+ {
+ return m_aRectangle.IsEmpty();
+ }
+
+ static RectangleAndPart Create(const std::string& rPayload);
+ };
+
+ class DESKTOP_DLLPUBLIC CallbackFlushHandler final : public Idle
+ {
+ public:
+ explicit CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData);
+ virtual ~CallbackFlushHandler() override;
+ virtual void Invoke() override;
+ static void callback(const int type, const char* payload, void* data);
+ void queue(const int type, const char* data);
+
+ /// Disables callbacks on this handler. Must match with identical count
+ /// of enableCallbacks. Used during painting and changing views.
+ void disableCallbacks() { ++m_nDisableCallbacks; }
+ /// Enables callbacks on this handler. Must match with identical count
+ /// of disableCallbacks. Used during painting and changing views.
+ void enableCallbacks() { --m_nDisableCallbacks; }
+ /// Returns true iff callbacks are disabled.
+ bool callbacksDisabled() const { return m_nDisableCallbacks != 0; }
+
+ void addViewStates(int viewId);
+ void removeViewStates(int viewId);
+
+ struct CallbackData
+ {
+ CallbackData(int type, const std::string& payload)
+ : Type(type)
+ , PayloadString(payload)
+ {
+ }
+
+ /// Parse and set the RectangleAndPart object and return it. Clobbers PayloadString.
+ RectangleAndPart& setRectangleAndPart(const std::string& payload);
+ /// Set a RectangleAndPart object and update PayloadString.
+ void setRectangleAndPart(const RectangleAndPart& rRectAndPart);
+ /// Return the parsed RectangleAndPart instance.
+ const RectangleAndPart& getRectangleAndPart() const;
+ /// Parse and set the JSON object and return it. Clobbers PayloadString.
+ boost::property_tree::ptree& setJson(const std::string& payload);
+ /// Set a Json object and update PayloadString.
+ void setJson(const boost::property_tree::ptree& rTree);
+ /// Return the parsed JSON instance.
+ const boost::property_tree::ptree& getJson() const;
+
+ /// Validate that the payload and parsed object match.
+ bool validate() const;
+
+ /// Returns true iff there is cached data.
+ bool isCached() const { return PayloadObject.which() != 0; }
+
+ int Type;
+ std::string PayloadString;
+
+ private:
+ /// The parsed payload cache. Update validate() when changing this.
+ boost::variant<boost::blank, RectangleAndPart, boost::property_tree::ptree> PayloadObject;
+ };
+
+ typedef std::vector<CallbackData> queue_type;
+
+ private:
+ bool removeAll(const std::function<bool (const queue_type::value_type&)>& rTestFunc);
+ bool processInvalidateTilesEvent(CallbackData& aCallbackData);
+ bool processWindowEvent(CallbackData& aCallbackData);
+
+ queue_type m_queue;
+ std::map<int, std::string> m_states;
+ std::unordered_map<int, std::unordered_map<int, std::string>> m_viewStates;
+ LibreOfficeKitDocument* m_pDocument;
+ LibreOfficeKitCallback m_pCallback;
+ void *m_pData;
+ int m_nDisableCallbacks;
+ std::mutex m_mutex;
+ };
+
+ struct DESKTOP_DLLPUBLIC LibLODocument_Impl : public _LibreOfficeKitDocument
+ {
+ css::uno::Reference<css::lang::XComponent> mxComponent;
+ std::shared_ptr< LibreOfficeKitDocumentClass > m_pDocumentClass;
+ std::map<size_t, std::shared_ptr<CallbackFlushHandler>> mpCallbackFlushHandlers;
+
+ explicit LibLODocument_Impl(const css::uno::Reference <css::lang::XComponent> &xComponent);
+ ~LibLODocument_Impl();
+ };
+
+ struct DESKTOP_DLLPUBLIC LibLibreOffice_Impl : public _LibreOfficeKit
+ {
+ OUString maLastExceptionMsg;
+ std::shared_ptr< LibreOfficeKitClass > m_pOfficeClass;
+ oslThread maThread;
+ LibreOfficeKitCallback mpCallback;
+ void *mpCallbackData;
+ int64_t mOptionalFeatures;
+ std::map<OString, rtl::Reference<LOKInteractionHandler>> mInteractionMap;
+
+ LibLibreOffice_Impl();
+ ~LibLibreOffice_Impl();
+
+ bool hasOptionalFeature(LibreOfficeKitOptionalFeatures const feature)
+ {
+ return (mOptionalFeatures & feature) != 0;
+ }
+ };
+
+ /// Helper function to extract the value from parameters delimited by
+ /// comma, like: Name1=Value1,Name2=Value2,Name3=Value3.
+ /// @param rOptions When extracted, the Param=Value is removed from it.
+ DESKTOP_DLLPUBLIC OUString extractParameter(OUString& aOptions, const OUString& rName);
+
+ /// Helper function to convert JSON to a vector of PropertyValues.
+ /// Public to be unit-test-able.
+ DESKTOP_DLLPUBLIC std::vector<com::sun::star::beans::PropertyValue> jsonToPropertyValuesVector(const char* pJSON);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/migration.hxx b/desktop/inc/migration.hxx
new file mode 100644
index 000000000..e19340723
--- /dev/null
+++ b/desktop/inc/migration.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_MIGRATION_HXX
+#define INCLUDED_DESKTOP_INC_MIGRATION_HXX
+
+namespace desktop {
+
+class Migration
+{
+public:
+ static void migrateSettingsIfNecessary();
+};
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deployment.cxx b/desktop/inc/pch/precompiled_deployment.cxx
new file mode 100644
index 000000000..4a35415df
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deployment.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_deployment.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deployment.hxx b/desktop/inc/pch/precompiled_deployment.hxx
new file mode 100644
index 000000000..6878fb1e8
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deployment.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2020-04-21 11:14:33 using:
+ ./bin/update_pch desktop deployment --cutoff=3 --exclude:system --exclude:module --exclude:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./desktop/inc/pch/precompiled_deployment.hxx "make desktop.build" --find-conflicts
+*/
+
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <optional>
+#include <ostream>
+#include <unordered_map>
+#include <vector>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <sal/log.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/inettype.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/content.hxx>
+#include <unotools/unotoolsdllapi.h>
+#include <xmlscript/xml_helper.hxx>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <dp_backend.h>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_interact.h>
+#include <dp_misc_api.hxx>
+#include <dp_platform.hxx>
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deploymentgui.cxx b/desktop/inc/pch/precompiled_deploymentgui.cxx
new file mode 100644
index 000000000..6a5ec88eb
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deploymentgui.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_deploymentgui.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deploymentgui.hxx b/desktop/inc/pch/precompiled_deploymentgui.hxx
new file mode 100644
index 000000000..6ec6ce346
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deploymentgui.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2020-04-21 11:14:30 using:
+ ./bin/update_pch desktop deploymentgui --cutoff=3 --exclude:system --exclude:module --exclude:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./desktop/inc/pch/precompiled_deploymentgui.hxx "make desktop.build" --find-conflicts
+*/
+
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <initializer_list>
+#include <iomanip>
+#include <limits.h>
+#include <memory>
+#include <new>
+#include <optional>
+#include <ostream>
+#include <sstream>
+#include <stddef.h>
+#include <string>
+#include <utility>
+#include <vector>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/conditn.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/locale.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <sal/detail/log.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#include <vcl/dllapi.h>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <salhelper/thread.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <tools/solar.h>
+#include <tools/toolsdllapi.h>
+#include <typelib/typedescription.h>
+#include <uno/data.h>
+#include <uno/sequence2.h>
+#include <unotools/configmgr.hxx>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <dp_dependencies.hxx>
+#include <dp_identifier.hxx>
+#include <dp_misc_api.hxx>
+#include <dp_update.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deploymentmisc.cxx b/desktop/inc/pch/precompiled_deploymentmisc.cxx
new file mode 100644
index 000000000..1a2225a93
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deploymentmisc.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_deploymentmisc.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_deploymentmisc.hxx b/desktop/inc/pch/precompiled_deploymentmisc.hxx
new file mode 100644
index 000000000..abc7000aa
--- /dev/null
+++ b/desktop/inc/pch/precompiled_deploymentmisc.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2020-02-01 10:57:36 using:
+ ./bin/update_pch desktop deploymentmisc --cutoff=3 --exclude:system --exclude:module --exclude:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./desktop/inc/pch/precompiled_deploymentmisc.hxx "make desktop.build" --find-conflicts
+*/
+
+#if PCH_LEVEL >= 1
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/doublecheckedlocking.h>
+#include <osl/getglobalmutex.hxx>
+#include <osl/interlck.h>
+#include <osl/pipe.hxx>
+#include <osl/security.hxx>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/digest.h>
+#include <rtl/instance.hxx>
+#include <rtl/locale.h>
+#include <rtl/random.h>
+#include <rtl/ref.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <sal/detail/log.h>
+#include <sal/log.hxx>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/processfactory.hxx>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/implbase_ex.hxx>
+#include <cppuhelper/weak.hxx>
+#include <salhelper/linkhelper.hxx>
+#include <typelib/typedescription.h>
+#include <uno/data.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <dp_descriptioninfoset.hxx>
+#include <dp_misc_api.hxx>
+#include <dp_version.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_sofficeapp.cxx b/desktop/inc/pch/precompiled_sofficeapp.cxx
new file mode 100644
index 000000000..4ac33f45e
--- /dev/null
+++ b/desktop/inc/pch/precompiled_sofficeapp.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "precompiled_sofficeapp.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/pch/precompiled_sofficeapp.hxx b/desktop/inc/pch/precompiled_sofficeapp.hxx
new file mode 100644
index 000000000..e7790c586
--- /dev/null
+++ b/desktop/inc/pch/precompiled_sofficeapp.hxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ This file has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2020-04-25 20:54:59 using:
+ ./bin/update_pch desktop sofficeapp --cutoff=6 --exclude:system --include:module --include:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./desktop/inc/pch/precompiled_sofficeapp.hxx "make desktop.build" --find-conflicts
+*/
+
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <deque>
+#include <float.h>
+#include <functional>
+#include <initializer_list>
+#include <iomanip>
+#include <limits.h>
+#include <limits>
+#include <math.h>
+#include <memory>
+#include <new>
+#include <optional>
+#include <ostream>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include <boost/property_tree/ptree_fwd.hpp>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/conditn.hxx>
+#include <osl/diagnose.h>
+#include <osl/doublecheckedlocking.h>
+#include <osl/endian.h>
+#include <osl/file.hxx>
+#include <osl/getglobalmutex.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.h>
+#include <osl/mutex.hxx>
+#include <osl/pipe.h>
+#include <osl/pipe.hxx>
+#include <osl/security.h>
+#include <osl/security.hxx>
+#include <osl/thread.h>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/digest.h>
+#include <rtl/instance.hxx>
+#include <rtl/math.h>
+#include <rtl/process.h>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#include <vcl/Scanline.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/checksum.hxx>
+#include <vcl/dllapi.h>
+#include <vcl/errcode.hxx>
+#include <vcl/fntstyle.hxx>
+#include <vcl/font.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/region.hxx>
+#include <vcl/scopedbitmapaccess.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/vclreferencebase.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <basegfx/basegfxdllapi.h>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/basicrange.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <basegfx/tuple/b3dtuple.hxx>
+#include <basegfx/utils/common.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/vector/b2enums.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+#include <basic/basicdllapi.h>
+#include <basic/sbxcore.hxx>
+#include <basic/sbxdef.hxx>
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/KeyGroup.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/TypeClass.hdl>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/uno/genfunc.h>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/processfactory.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/implbase_ex_post.hxx>
+#include <cppuhelper/implbase_ex_pre.hxx>
+#include <i18nlangtag/lang.h>
+#include <o3tl/cow_wrapper.hxx>
+#include <o3tl/strong_int.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <salhelper/thread.hxx>
+#include <sfx2/dllapi.h>
+#include <svl/hint.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/svldllapi.h>
+#include <svl/typedwhich.hxx>
+#include <svtools/svtdllapi.h>
+#include <tools/color.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/fontenum.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/poly.hxx>
+#include <tools/ref.hxx>
+#include <tools/solar.h>
+#include <tools/toolsdllapi.h>
+#include <typelib/typeclass.h>
+#include <typelib/typedescription.h>
+#include <typelib/uik.h>
+#include <uno/any2.h>
+#include <uno/data.h>
+#include <uno/sequence2.h>
+#include <unotools/options.hxx>
+#include <unotools/unotoolsdllapi.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <app.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/strings.hrc b/desktop/inc/strings.hrc
new file mode 100644
index 000000000..d6d2c6244
--- /dev/null
+++ b/desktop/inc/strings.hrc
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_INC_STRINGS_HRC
+#define INCLUDED_VCL_INC_STRINGS_HRC
+
+#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
+
+#define RID_STR_COPYING_PACKAGE NC_("RID_STR_COPYING_PACKAGE", "Copying: ")
+#define RID_STR_ERROR_WHILE_ADDING NC_("RID_STR_ERROR_WHILE_ADDING", "Error while adding: ")
+#define RID_STR_ERROR_WHILE_REMOVING NC_("RID_STR_ERROR_WHILE_REMOVING", "Error while removing: ")
+#define RID_STR_PACKAGE_ALREADY_ADDED NC_("RID_STR_PACKAGE_ALREADY_ADDED", "Extension has already been added: ")
+#define RID_STR_NO_SUCH_PACKAGE NC_("RID_STR_NO_SUCH_PACKAGE", "There is no such extension deployed: ")
+#define RID_STR_SYNCHRONIZING_REPOSITORY NC_("RID_STR_SYNCHRONIZING_REPOSITORY", "Synchronizing repository for %NAME extensions")
+
+#define RID_STR_REGISTERING_PACKAGE NC_("RID_STR_REGISTERING_PACKAGE", "Enabling: ")
+#define RID_STR_REVOKING_PACKAGE NC_("RID_STR_REVOKING_PACKAGE", "Disabling: ")
+#define RID_STR_CANNOT_DETECT_MEDIA_TYPE NC_("RID_STR_CANNOT_DETECT_MEDIA_TYPE", "Cannot detect media-type: ")
+#define RID_STR_UNSUPPORTED_MEDIA_TYPE NC_("RID_STR_UNSUPPORTED_MEDIA_TYPE", "This media-type is not supported: ")
+#define RID_STR_ERROR_WHILE_REGISTERING NC_("RID_STR_ERROR_WHILE_REGISTERING", "An error occurred while enabling: ")
+#define RID_STR_ERROR_WHILE_REVOKING NC_("RID_STR_ERROR_WHILE_REVOKING", "An error occurred while disabling: ")
+
+#define RID_STR_CONF_SCHEMA NC_("RID_STR_CONF_SCHEMA", "Configuration Schema")
+#define RID_STR_CONF_DATA NC_("RID_STR_CONF_DATA", "Configuration Data")
+
+#define RID_STR_BASIC_LIB NC_("RID_STR_BASIC_LIB", "Basic Library")
+#define RID_STR_DIALOG_LIB NC_("RID_STR_DIALOG_LIB", "Dialog Library")
+#define RID_STR_CANNOT_DETERMINE_LIBNAME NC_("RID_STR_CANNOT_DETERMINE_LIBNAME", "The library name could not be determined.")
+
+#define RID_STR_PACKAGE_BUNDLE NC_("RID_STR_PACKAGE_BUNDLE", "Extension")
+
+#define RID_STR_DYN_COMPONENT NC_("RID_STR_DYN_COMPONENT", "UNO Dynamic Library Component")
+#define RID_STR_JAVA_COMPONENT NC_("RID_STR_JAVA_COMPONENT", "UNO Java Component")
+#define RID_STR_PYTHON_COMPONENT NC_("RID_STR_PYTHON_COMPONENT", "UNO Python Component")
+#define RID_STR_COMPONENTS NC_("RID_STR_COMPONENTS", "UNO Components")
+#define RID_STR_RDB_TYPELIB NC_("RID_STR_RDB_TYPELIB", "UNO RDB Type Library")
+#define RID_STR_JAVA_TYPELIB NC_("RID_STR_JAVA_TYPELIB", "UNO Java Type Library")
+
+#define RID_STR_SFWK_LIB NC_("RID_STR_SFWK_LIB", "%MACROLANG Library")
+
+#define RID_STR_HELP NC_("RID_STR_HELP", "Help")
+#define RID_STR_HELPPROCESSING_GENERAL_ERROR NC_("RID_STR_HELPPROCESSING_GENERAL_ERROR", "The extension cannot be installed because:\n")
+#define RID_STR_HELPPROCESSING_XMLPARSING_ERROR NC_("RID_STR_HELPPROCESSING_XMLPARSING_ERROR", "The extension will not be installed because an error occurred in the Help files:\n")
+
+#define RID_STR_ADD_PACKAGES NC_("RID_STR_ADD_PACKAGES", "Add Extension(s)")
+#define RID_CTX_ITEM_REMOVE NC_("RID_CTX_ITEM_REMOVE", "~Remove")
+#define RID_CTX_ITEM_ENABLE NC_("RID_CTX_ITEM_ENABLE", "~Enable")
+#define RID_CTX_ITEM_DISABLE NC_("RID_CTX_ITEM_DISABLE", "~Disable")
+#define RID_CTX_ITEM_CHECK_UPDATE NC_("RID_CTX_ITEM_CHECK_UPDATE", "~Update...")
+#define RID_STR_ADDING_PACKAGES NC_("RID_STR_ADDING_PACKAGES", "Adding %EXTENSION_NAME")
+#define RID_STR_REMOVING_PACKAGES NC_("RID_STR_REMOVING_PACKAGES", "Removing %EXTENSION_NAME")
+#define RID_STR_ENABLING_PACKAGES NC_("RID_STR_ENABLING_PACKAGES", "Enabling %EXTENSION_NAME")
+#define RID_STR_DISABLING_PACKAGES NC_("RID_STR_DISABLING_PACKAGES", "Disabling %EXTENSION_NAME")
+#define RID_STR_ACCEPT_LICENSE NC_("RID_STR_ACCEPT_LICENSE", "Accept license for %EXTENSION_NAME")
+#define RID_STR_ERROR_UNKNOWN_STATUS NC_("RID_STR_ERROR_UNKNOWN_STATUS", "Error: The status of this extension is unknown")
+#define RID_STR_CLOSE_BTN NC_("RID_STR_CLOSE_BTN", "Close")
+#define RID_STR_EXIT_BTN NC_("RID_STR_EXIT_BTN", "Quit")
+#define RID_STR_NO_ADMIN_PRIVILEGE NC_("RID_STR_NO_ADMIN_PRIVILEGE", "%PRODUCTNAME has been updated to a new version. " \
+ "Some shared %PRODUCTNAME extensions are not compatible with this version and need to be updated before %PRODUCTNAME can be started.\n\n" \
+ "Updating of shared extension requires administrator privileges. Contact your system administrator to update the following shared extensions:")
+#define RID_STR_ERROR_MISSING_DEPENDENCIES NC_("RID_STR_ERROR_MISSING_DEPENDENCIES", "The extension cannot be enabled as the following system dependencies are not fulfilled:")
+#define RID_STR_ERROR_MISSING_LICENSE NC_("RID_STR_ERROR_MISSING_LICENSE", "This extension is disabled because you haven't accepted the license yet.\n")
+#define RID_STR_SHOW_LICENSE_CMD NC_("RID_STR_SHOW_LICENSE_CMD", "Show license")
+#define RID_STR_WARNING_INSTALL_EXTENSION NC_("RID_STR_WARNING_INSTALL_EXTENSION", "You are about to install the extension '%NAME'.\n" \
+ "Click 'OK' to proceed with the installation.\n" \
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNING_INSTALL_EXTENSION_DISABLED NC_("RID_STR_WARNING_INSTALL_EXTENSION_DISABLED", "Extension installation is currently disabled. " \
+ "Please consult your system administrator for more information.")
+#define RID_STR_WARNING_REMOVE_EXTENSION_DISABLED NC_("RID_STR_WARNING_REMOVE_EXTENSION_DISABLED", "Extension removal is currently disabled. " \
+ "Please consult your system administrator for more information.")
+#define RID_STR_WARNING_REMOVE_EXTENSION NC_("RID_STR_WARNING_REMOVE_EXTENSION", "You are about to remove the extension '%NAME'.\n" \
+ "Click 'OK' to remove the extension.\n" \
+ "Click 'Cancel' to stop removing the extension.")
+#define RID_STR_WARNING_REMOVE_SHARED_EXTENSION NC_("RID_STR_WARNING_REMOVE_SHARED_EXTENSION", "Make sure that no further users are working with the same " \
+ "%PRODUCTNAME, when changing shared extensions in a multi user environment.\n" \
+ "Click 'OK' to remove the extension.\n" \
+ "Click 'Cancel' to stop removing the extension.")
+#define RID_STR_WARNING_ENABLE_SHARED_EXTENSION NC_("RID_STR_WARNING_ENABLE_SHARED_EXTENSION", "Make sure that no further users are working with the same " \
+ "%PRODUCTNAME, when changing shared extensions in a multi user environment.\n" \
+ "Click 'OK' to enable the extension.\n" \
+ "Click 'Cancel' to stop enabling the extension.")
+#define RID_STR_WARNING_DISABLE_SHARED_EXTENSION NC_("RID_STR_WARNING_DISABLE_SHARED_EXTENSION", "Make sure that no further users are working with the same " \
+ "%PRODUCTNAME, when changing shared extensions in a multi user environment.\n" \
+ "Click 'OK' to disable the extension.\n" \
+ "Click 'Cancel' to stop disabling the extension.")
+#define RID_STR_UNSUPPORTED_PLATFORM NC_("RID_STR_UNSUPPORTED_PLATFORM", "The extension '%Name' does not work on this computer.")
+
+#define RID_DLG_UPDATE_INSTALL_INSTALLING NC_("RID_DLG_UPDATE_INSTALL_INSTALLING", "Installing extensions...")
+#define RID_DLG_UPDATE_INSTALL_FINISHED NC_("RID_DLG_UPDATE_INSTALL_FINISHED", "Installation finished")
+#define RID_DLG_UPDATE_INSTALL_NO_ERRORS NC_("RID_DLG_UPDATE_INSTALL_NO_ERRORS", "No errors.")
+#define RID_DLG_UPDATE_INSTALL_ERROR_DOWNLOAD NC_("RID_DLG_UPDATE_INSTALL_ERROR_DOWNLOAD", "Error while downloading extension %NAME. ")
+#define RID_DLG_UPDATE_INSTALL_THIS_ERROR_OCCURRED NC_("RID_DLG_UPDATE_INSTALL_THIS_ERROR_OCCURRED", "The error message is: ")
+#define RID_DLG_UPDATE_INSTALL_ERROR_INSTALLATION NC_("RID_DLG_UPDATE_INSTALL_ERROR_INSTALLATION", "Error while installing extension %NAME. ")
+#define RID_DLG_UPDATE_INSTALL_ERROR_LIC_DECLINED NC_("RID_DLG_UPDATE_INSTALL_ERROR_LIC_DECLINED", "The license agreement for extension %NAME was refused. ")
+#define RID_DLG_UPDATE_INSTALL_EXTENSION_NOINSTALL NC_("RID_DLG_UPDATE_INSTALL_EXTENSION_NOINSTALL", "The extension will not be installed.")
+
+#define RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN NC_("RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN", "Unknown")
+#define RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN NC_("RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN", "Extension requires at least OpenOffice.org reference version %VERSION")
+#define RID_DEPLOYMENT_DEPENDENCIES_OOO_MAX NC_("RID_DEPLOYMENT_DEPENDENCIES_OOO_MAX", "Extension does not support OpenOffice.org reference versions greater than %VERSION")
+#define RID_DEPLOYMENT_DEPENDENCIES_LO_MIN NC_("RID_DEPLOYMENT_DEPENDENCIES_LO_MIN", "Extension requires at least %PRODUCTNAME version %VERSION")
+#define RID_DEPLOYMENT_DEPENDENCIES_LO_MAX NC_("RID_DEPLOYMENT_DEPENDENCIES_LO_MAX", "Extension does not support %PRODUCTNAME versions greater than %VERSION")
+
+#define RID_STR_WARNING_VERSION_LESS NC_("RID_STR_WARNING_VERSION_LESS", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "The newer version $DEPLOYED is already installed.\n"\
+ "Click 'OK' to replace the installed extension.\n"\
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES NC_("RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "The newer version $DEPLOYED, named '$OLDNAME', is already installed.\n" \
+ "Click 'OK' to replace the installed extension.\n" \
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNING_VERSION_EQUAL NC_("RID_STR_WARNING_VERSION_EQUAL", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "That version is already installed.\n" \
+ "Click 'OK' to replace the installed extension.\n" \
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES NC_("RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "That version, named '$OLDNAME', is already installed.\n" \
+ "Click 'OK' to replace the installed extension.\n" \
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNING_VERSION_GREATER NC_("RID_STR_WARNING_VERSION_GREATER", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "The older version $DEPLOYED is already installed.\n" \
+ "Click 'OK' to replace the installed extension.\n" \
+ "Click 'Cancel' to stop the installation.")
+#define RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES NC_("RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES", "You are about to install version $NEW of the extension '$NAME'.\n" \
+ "The older version $DEPLOYED, named '$OLDNAME', is already installed.\n" \
+ "Click 'OK' to replace the installed extension.\n" \
+ "Click 'Cancel' to stop the installation.")
+
+#define RID_DLG_UPDATE_NONE NC_("RID_DLG_UPDATE_NONE", "No new updates are available.")
+#define RID_DLG_UPDATE_NOINSTALLABLE NC_("RID_DLG_UPDATE_NOINSTALLABLE", "No installable updates are available. To see ignored or disabled updates, mark the check box 'Show all updates'.")
+#define RID_DLG_UPDATE_FAILURE NC_("RID_DLG_UPDATE_FAILURE", "An error occurred:")
+#define RID_DLG_UPDATE_UNKNOWNERROR NC_("RID_DLG_UPDATE_UNKNOWNERROR", "Unknown error.")
+#define RID_DLG_UPDATE_NODESCRIPTION NC_("RID_DLG_UPDATE_NODESCRIPTION", "No more details are available for this update.")
+#define RID_DLG_UPDATE_NOINSTALL NC_("RID_DLG_UPDATE_NOINSTALL", "The extension cannot be updated because:")
+#define RID_DLG_UPDATE_NODEPENDENCY NC_("RID_DLG_UPDATE_NODEPENDENCY", "Required %PRODUCTNAME version doesn't match:")
+#define RID_DLG_UPDATE_NODEPENDENCY_CUR_VER NC_("RID_DLG_UPDATE_NODEPENDENCY_CUR_VER", "You have %PRODUCTNAME %VERSION")
+#define RID_DLG_UPDATE_BROWSERBASED NC_("RID_DLG_UPDATE_BROWSERBASED", "browser based update")
+#define RID_DLG_UPDATE_VERSION NC_("RID_DLG_UPDATE_VERSION", "Version")
+#define RID_DLG_UPDATE_IGNORED_UPDATE NC_("RID_DLG_UPDATE_IGNORED_UPDATE", "This update will be ignored.\n")
+
+#define STR_BOOTSTRAP_ERR_CANNOT_START NC_("STR_BOOTSTRAP_ERR_CANNOT_START", "The application cannot be started. ")
+#define STR_BOOTSTRAP_ERR_DIR_MISSING NC_("STR_BOOTSTRAP_ERR_DIR_MISSING", "The configuration directory \"$1\" could not be found.")
+#define STR_BOOTSTRAP_ERR_PATH_INVALID NC_("STR_BOOTSTRAP_ERR_PATH_INVALID", "The installation path is invalid.")
+#define STR_BOOTSTRAP_ERR_INTERNAL NC_("STR_BOOTSTRAP_ERR_INTERNAL", "An internal error occurred.")
+#define STR_BOOTSTRAP_ERR_FILE_CORRUPT NC_("STR_BOOTSTRAP_ERR_FILE_CORRUPT", "The configuration file \"$1\" is corrupt.")
+#define STR_BOOTSTRAP_ERR_FILE_MISSING NC_("STR_BOOTSTRAP_ERR_FILE_MISSING", "The configuration file \"$1\" was not found.")
+#define STR_BOOTSTRAP_ERR_NO_SUPPORT NC_("STR_BOOTSTRAP_ERR_NO_SUPPORT", "The configuration file \"$1\" does not support the current version.")
+#define STR_BOOTSTRAP_ERR_LANGUAGE_MISSING NC_("STR_BOOTSTRAP_ERR_LANGUAGE_MISSING", "The user interface language cannot be determined.")
+#define STR_BOOTSTRAP_ERR_USERINSTALL_FAILED NC_("STR_BOOTSTRAP_ERR_USERINSTALL_FAILED", "User installation could not be completed. ")
+#define STR_BOOTSTRAP_ERR_NO_CFG_SERVICE NC_("STR_BOOTSTRAP_ERR_NO_CFG_SERVICE", "The configuration service is not available.")
+#define STR_ASK_START_SETUP_MANUALLY NC_("STR_ASK_START_SETUP_MANUALLY", "Start the setup application to repair the installation from the CD or the folder containing the installation packages.")
+#define STR_CONFIG_ERR_ACCESS_GENERAL NC_("STR_CONFIG_ERR_ACCESS_GENERAL", "A general error occurred while accessing your central configuration. ")
+#define STR_BOOTSTRAP_ERR_CFG_DATAACCESS NC_("STR_BOOTSTRAP_ERR_CFG_DATAACCESS", "%PRODUCTNAME cannot be started due to an error in accessing the %PRODUCTNAME configuration data.\n\nPlease contact your system administrator." )
+#define STR_INTERNAL_ERRMSG NC_("STR_INTERNAL_ERRMSG", "The following internal error has occurred: " )
+#define STR_LO_MUST_BE_RESTARTED NC_("STR_LO_MUST_BE_RESTARTED", "%PRODUCTNAME must unfortunately be manually restarted once after installation or update." )
+#define STR_QUERY_USERDATALOCKED NC_("STR_QUERY_USERDATALOCKED", "Either another instance of %PRODUCTNAME is accessing your personal settings or your personal settings are locked.\nSimultaneous access can lead to inconsistencies in your personal settings. Before continuing, you should make sure user '$u' closes %PRODUCTNAME on host '$h'.\n\nDo you really want to continue?")
+#define STR_TITLE_USERDATALOCKED NC_("STR_TITLE_USERDATALOCKED", "%PRODUCTNAME %PRODUCTVERSION")
+#define STR_ERR_PRINTDISABLED NC_("STR_ERR_PRINTDISABLED", "Printing is disabled. No documents can be printed.")
+#define STR_BOOTSTRAP_ERR_NO_PATHSET_SERVICE NC_("STR_BOOTSTRAP_ERR_NO_PATHSET_SERVICE", "The path manager is not available.\n")
+#define STR_BOOTSTRAP_ERR_NOTENOUGHDISKSPACE NC_("STR_BOOTSTRAP_ERR_NOTENOUGHDISKSPACE", "%PRODUCTNAME user installation could not be completed due to insufficient free disk space. Please free more disc space at the following location and restart %PRODUCTNAME:\n\n")
+#define STR_BOOTSTRAP_ERR_NOACCESSRIGHTS NC_("STR_BOOTSTRAP_ERR_NOACCESSRIGHTS", "%PRODUCTNAME user installation could not be processed due to missing access rights. Please make sure that you have sufficient access rights for the following location and restart %PRODUCTNAME:\n\n")
+
+#define RID_STR_UNOPKG_ACCEPT_LIC_1 NC_("RID_STR_UNOPKG_ACCEPT_LIC_1", "Extension Software License Agreement of $NAME:")
+#define RID_STR_UNOPKG_ACCEPT_LIC_2 NC_("RID_STR_UNOPKG_ACCEPT_LIC_2", "Read the complete License Agreement displayed above. " \
+ "Accept the License Agreement by typing \"yes\" on the console " \
+ "then press the Return key. Type \"no\" to decline and to abort the " \
+ "extension setup.")
+#define RID_STR_UNOPKG_ACCEPT_LIC_3 NC_("RID_STR_UNOPKG_ACCEPT_LIC_3", "[Enter \"yes\" or \"no\"]:")
+#define RID_STR_UNOPKG_ACCEPT_LIC_4 NC_("RID_STR_UNOPKG_ACCEPT_LIC_4", "Your input was not correct. Please enter \"yes\" or \"no\":")
+#define RID_STR_UNOPKG_ACCEPT_LIC_YES NC_("RID_STR_UNOPKG_ACCEPT_LIC_YES", "YES")
+#define RID_STR_UNOPKG_ACCEPT_LIC_Y NC_("RID_STR_UNOPKG_ACCEPT_LIC_Y", "Y")
+#define RID_STR_UNOPKG_ACCEPT_LIC_NO NC_("RID_STR_UNOPKG_ACCEPT_LIC_NO", "NO")
+#define RID_STR_UNOPKG_ACCEPT_LIC_N NC_("RID_STR_UNOPKG_ACCEPT_LIC_N", "N")
+#define RID_STR_CONCURRENTINSTANCE NC_("RID_STR_CONCURRENTINSTANCE", "unopkg cannot be started. The lock file indicates it is already running. " \
+ "If this does not apply, delete the lock file at:")
+#define RID_STR_UNOPKG_ERROR NC_("RID_STR_UNOPKG_ERROR", "ERROR: ")
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/inc/strings.hxx b/desktop/inc/strings.hxx
new file mode 100644
index 000000000..f89a3028c
--- /dev/null
+++ b/desktop/inc/strings.hxx
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_INC_STRINGS_HRC
+#define INCLUDED_DESKTOP_INC_STRINGS_HRC
+
+#define RID_APPTITLE "%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/desktop/qa/data/2slides.odp b/desktop/qa/data/2slides.odp
new file mode 100644
index 000000000..0e3f8758f
--- /dev/null
+++ b/desktop/qa/data/2slides.odp
Binary files differ
diff --git a/desktop/qa/data/blank_presentation.odp b/desktop/qa/data/blank_presentation.odp
new file mode 100644
index 000000000..a7d57a48e
--- /dev/null
+++ b/desktop/qa/data/blank_presentation.odp
Binary files differ
diff --git a/desktop/qa/data/blank_text.docx b/desktop/qa/data/blank_text.docx
new file mode 100644
index 000000000..028a35b6c
--- /dev/null
+++ b/desktop/qa/data/blank_text.docx
Binary files differ
diff --git a/desktop/qa/data/blank_text.odt b/desktop/qa/data/blank_text.odt
new file mode 100644
index 000000000..00b92d785
--- /dev/null
+++ b/desktop/qa/data/blank_text.odt
Binary files differ
diff --git a/desktop/qa/data/certificate.der b/desktop/qa/data/certificate.der
new file mode 100644
index 000000000..10e3ade13
--- /dev/null
+++ b/desktop/qa/data/certificate.der
Binary files differ
diff --git a/desktop/qa/data/certificatePrivateKey.der b/desktop/qa/data/certificatePrivateKey.der
new file mode 100644
index 000000000..7a5599c82
--- /dev/null
+++ b/desktop/qa/data/certificatePrivateKey.der
Binary files differ
diff --git a/desktop/qa/data/comments.odt b/desktop/qa/data/comments.odt
new file mode 100644
index 000000000..ee7f15f8b
--- /dev/null
+++ b/desktop/qa/data/comments.odt
Binary files differ
diff --git a/desktop/qa/data/hidden-row.ods b/desktop/qa/data/hidden-row.ods
new file mode 100644
index 000000000..25fe89865
--- /dev/null
+++ b/desktop/qa/data/hidden-row.ods
Binary files differ
diff --git a/desktop/qa/data/intermediateRootCA.der b/desktop/qa/data/intermediateRootCA.der
new file mode 100644
index 000000000..9adf7f82e
--- /dev/null
+++ b/desktop/qa/data/intermediateRootCA.der
Binary files differ
diff --git a/desktop/qa/data/objects.odt b/desktop/qa/data/objects.odt
new file mode 100644
index 000000000..45c2b39cc
--- /dev/null
+++ b/desktop/qa/data/objects.odt
Binary files differ
diff --git a/desktop/qa/data/paste.jpg b/desktop/qa/data/paste.jpg
new file mode 100644
index 000000000..ca9183e9d
--- /dev/null
+++ b/desktop/qa/data/paste.jpg
Binary files differ
diff --git a/desktop/qa/data/rootCA.der b/desktop/qa/data/rootCA.der
new file mode 100644
index 000000000..30fc66e26
--- /dev/null
+++ b/desktop/qa/data/rootCA.der
Binary files differ
diff --git a/desktop/qa/data/search.ods b/desktop/qa/data/search.ods
new file mode 100644
index 000000000..ea1d73153
--- /dev/null
+++ b/desktop/qa/data/search.ods
Binary files differ
diff --git a/desktop/qa/data/sheet_with_image.ods b/desktop/qa/data/sheet_with_image.ods
new file mode 100644
index 000000000..00c0019cb
--- /dev/null
+++ b/desktop/qa/data/sheet_with_image.ods
Binary files differ
diff --git a/desktop/qa/data/sheets.ods b/desktop/qa/data/sheets.ods
new file mode 100644
index 000000000..3f43fa3a3
--- /dev/null
+++ b/desktop/qa/data/sheets.ods
Binary files differ
diff --git a/desktop/qa/data/signed.odt b/desktop/qa/data/signed.odt
new file mode 100644
index 000000000..49bd9dd24
--- /dev/null
+++ b/desktop/qa/data/signed.odt
Binary files differ
diff --git a/desktop/qa/data/test-PK-signing.pem b/desktop/qa/data/test-PK-signing.pem
new file mode 100644
index 000000000..eabbaae18
--- /dev/null
+++ b/desktop/qa/data/test-PK-signing.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/x1dyX2PgJs6o
+e/BcBT5XTJXtipme29mtI/FmTq5vuAFopqvAEg084hNM7Q++wKyKG8F0ABF2pUOY
+vo6Pq6rXQqMbxUaO47NNefh4v/f6hBoMfSYmXtEPji8NjIcIrQ3U8yTWquaHi13h
+coG/cVf16Doem5AxUdQ6a9e/jA1VU2MmSZxthkABKPWdw1pfFJDLWhFrCjhePKrB
+xw2ArmNx66fdzpi5XK0fC2TbHD2NVc8cbYOzF4h3knGZo2sq4/MBevyVhzXZNn2P
+jf3PbOxp1O8CLx86MuDTLDkSVjmP0yXBf6rLFMWtEfv1Jmf3I61meIuOcw6wvXRK
+zPnAKpLRAgMBAAECggEAFQU1JooiGQg9OpEV0ArwbFfZTxy+qH+Rz70oJn3qohWE
+bK4SwTi5TrpmQdZHatxqN7EXIS8out0ebaLlXrCtnG6SEOcsoVjVPGFpb1ggnCAt
+TWp3lgO/3SPz2wOo1rXxKtguaivNR39qc4g+LyJYm9GFHU8RHPbKe6TPvw+2HmtB
+Qr2dfwgN+JJ/lBTIE0lUVuGcBcCRxbM6aG3WVpVrWcsGwRekcuuy9xrv+6fd1p2E
+7zdM0/18+tnCWF9kCO6ot+spKJGTKqiuaKrSB54gFnDEgjQKIW7lUuOcXKI8vOZb
+yO3owLE4mlNbE9sv8gPKoMXf2d6wzMRA6wBdaqOlKQKBgQD1XhyNDTpCoqcO82KY
+YCJOZ6d1jH27XhHplfmrYDlQuWiuJ/b3ckq5Pqw5J5pRO19tvb4RSonYtmOcLajR
+CkQtHapeH8mUzGsE/tgauB4KoZ1sVhzsag0Ill44P5/5oBlzhRrvJ7L80qlHn8xC
+a4Tzk5t0tWbCS6K3/rqvil0hXwKBgQDIFsMoujc8hW5jyC6rO41elWEDVmPs5P+5
+RYxH5+uVeByyz2R86CAFmHUn/1nD47KKouNhwU4Anf5lA8JHh9rNVF07a2dAfH1o
+yfkBGz8d3xb5hq3ahVGg9WyMRyfczGGA19uJrQ19dY4G4B4FPPz0J6oOnNkTFaQ9
+Bks/k/fJzwKBgGSBqVZJzcyPzbh9D6z06/iL0vd+ld4TGWlCKqP9ZVzgpbV431va
+sCsTNf6vbzHJDTzplRqGGtLvWvwVY+pEt0p3tVqa0LqnxUqljSXct0mJi+9dkrlw
+c2hKF8wYm9Hnt6UvJ6pA67tOG1MgbM3kNvCDTRFQYQhDbSLLL/NJzP4nAoGAJqhv
+MFE6FtFY0KJ+kcrBt4J46eIpED32Ql9ziPkABTLdqJZ1PcTDWxFnoUCuoTA+8JYk
+BGEKpwfffLjLMnLHDWC9WpuXqVfkCvjqyRHwkd7mW3Nv54ZWjRidzkR5KSm7tN7/
+pYvvzUuHE0D9y9lKrglzy7r2Hb/SqY+rvi7icvUCgYADV2kkky++OmCVLkEg3WUf
+SJkF6jUAVMqlMdjTbySEfCJbxpVwRAiUWWlDD07c5HCBEASi6/NSn06MDb9Fvxo0
+a3m24Aa2c+K4ENj+bj453gdxhtvpeyfSRK+gBEP64iBG92UFJjcwHz5kFCzppPuP
+p+ZtA6JAnV6QPT1EixAOCA==
+-----END PRIVATE KEY-----
diff --git a/desktop/qa/data/test-cert-chain-1.pem b/desktop/qa/data/test-cert-chain-1.pem
new file mode 100644
index 000000000..3a3407caf
--- /dev/null
+++ b/desktop/qa/data/test-cert-chain-1.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIUaS0j9S5ZbREDcPMsLlfFE0+oUE0wDQYJKoZIhvcNAQEL
+BQAwajELMAkGA1UEBhMCQ0gxDDAKBgNVBAgMA1p1ZzEMMAoGA1UEBwwDWnVnMRMw
+EQYDVQQKDApWZXJlaWduIEFHMRUwEwYDVQQLDAxCdXNzaW5lcyBEZXAxEzARBgNV
+BAMMCnZlcmVpZ24tY2EwHhcNMTgxMTI0MDAwMDAwWhcNMjMxMjE4MjM1OTU5WjCB
+jzELMAkGA1UEBhMCQ0gxDDAKBgNVBAgTA1p1ZzEMMAoGA1UEBxMDWnVnMRMwEQYD
+VQQKEwpWZXJlaWduIEFHMRUwEwYDVQQLEwxCdXNpbmVzcyBEZXAxODA2BgNVBAMT
+L2YxZmUxZGJhLWZiMDUtNDk5Ni04Zjk1LWE4MTQyMjE5OGRiOS1zZXJ2ZXJzaWRl
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzE7WuIkjvhWceJGwLoYN
+2+huR47jlqqb0ez5VMGuZqK8qn1uf0PB6j/OW9B0cdBs65nBTL2CrkZncKov5g/i
+yZADuiezqBtEUr6J74oRBVPWV+e9iNr3M2FMtG/lkpYDpkvNziibMLE4kyhgbbRd
+Q0OHz55mk4Wn3CYAA/a7zhMgCjvT+wPuLXJjLje+2bB4rMv/USlnTN3DINx4i/Vt
+klNNtK5NSaxlkYf1QuyxXeHEJUufVuFY7sG4xZBhh75yUF7Z7836Oi1++DNeuWc/
+YOmsrfu1lqDfYNjb5IpOMz9x2HtmG6V3ETeKQX8GIs34qhG6zA9Up3JkSQsd9qTP
+1QIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCAbYwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
+HSMEGDAWgBQlN+K7lesKXsDZYQUu4zkqtNBwrjAaBgNVHREEEzARgg93d3cudmVy
+ZWlnbi5jb20wEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBCwUAA4IBAQAx
+86wXlw779hT3Jad4EfuozQycLw4lXLMiGdHuLdRGNdg6faK2p5qaJXFEd13pE/Qn
+AI2z5SpuYr2G3NYJsT3deXi8Yh58AsBSF5UY61xCgITXOW2NaB2gb6L7sL8Uau8i
+BE5yn0r0V6wD3gxG7yJRNPgH7ksELfN1b++BHdvkcodC1H4vHzl0mAJvYaNlPBhd
+LDnGU3GKhZ1r2pF+eXIW7n3BbAi/a7226Or4eTWEjc0footKJeJLfsG4HOcSj+Bi
+LKAeftebGRRsZzuL6tLTzoSiRsEn6Y6GOdxRM21Uu1rvtWmfDyNRuCcKZU8Secqy
+0s1uSGkNr+6wgcWqA+VQ
+-----END CERTIFICATE-----
diff --git a/desktop/qa/data/test-cert-chain-2.pem b/desktop/qa/data/test-cert-chain-2.pem
new file mode 100644
index 000000000..a31db3f65
--- /dev/null
+++ b/desktop/qa/data/test-cert-chain-2.pem
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEdjCCA16gAwIBAgIEW7HqGDANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJD
+SDEMMAoGA1UECAwDWnVnMQwwCgYDVQQHDANadWcxEzARBgNVBAoMClZlcmVpZ24g
+QUcxFTATBgNVBAsMDEJ1c3NpbmVzIERlcDETMBEGA1UEAwwKdmVyZWlnbi1jYTAe
+Fw0xODEwMDEwOTM0MTZaFw0xOTEwMDEwOTM0MTZaMGoxCzAJBgNVBAYTAkNIMQww
+CgYDVQQIDANadWcxDDAKBgNVBAcMA1p1ZzETMBEGA1UECgwKVmVyZWlnbiBBRzEV
+MBMGA1UECwwMQnVzc2luZXMgRGVwMRMwEQYDVQQDDAp2ZXJlaWduLWNhMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsfpPjrblQuxrHiSLAAyyDgRd66gY
+PRo7lgKZH5NYcBO9VhJNwnvV+fBIVeJI49b+a12TPHjRzJYrkaBAcUxMM8FkZ01A
+mv6JSG4o2ZXV+GWpnWzEJzt9ZXmNZ1MSUlqIGzVZ/eUlXIj4gy57+SZoJURcQGhs
+jpoRgUpYnFsDJk2x77jiOa5ym/N+8HKsOabASMU6VkbIFvUqf62RXWpnQlOhFjGo
+0jvheRGBWbaYKHM3/d+u78w4tmvHqGVDDbsuOluZ39p2jCic9S7CnDkauZB0Afd/
+xgQ0CglpAgY8g4cfMl2zwRmm616PtutqjcE/NoA2JEVN5vP9QZsuXeRpJwIDAQAB
+o4IBIjCCAR4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwLwYDVR0R
+BCgwJoIPd3d3LnZlcmVpZ24uY29tgRNjb250YWN0QHZlcmVpZ24uY29tMBEGA1Ud
+IAQKMAgwBgYEVR0gADCBlwYDVR0jBIGPMIGMgBQlN+K7lesKXsDZYQUu4zkqtNBw
+rqFupGwwajELMAkGA1UEBhMCQ0gxDDAKBgNVBAgMA1p1ZzEMMAoGA1UEBwwDWnVn
+MRMwEQYDVQQKDApWZXJlaWduIEFHMRUwEwYDVQQLDAxCdXNzaW5lcyBEZXAxEzAR
+BgNVBAMMCnZlcmVpZ24tY2GCBFux6hgwHQYDVR0OBBYEFCU34ruV6wpewNlhBS7j
+OSq00HCuMA0GCSqGSIb3DQEBCwUAA4IBAQCG3tf8/tuCNJXby4B7decDNE6bff40
+1ybO17kzekrKj0IO2TatFIG+UDlxDfm2iydEQVoPuRTAgmJD1aq5g4C0ZLyUqmOg
+75Dve6W9+zzxbdI711WKxH+uSj4mTRkFD4Tb7r3VZ1ZyZYnCOMIGB4/lqUK6Ok3a
+2v8XaFcxHt5XhrQtgqd5bBGokQfwYPNVZW9FwXf/8cd59prEOnqlMbZJ7copgwYO
+97abhpy2FUoRWtvDjDLLfdiFQhVY8meDcS/h5mw2aEugew8hnfSEaD5ZcbOf0ZQe
+MOVxKbIzSeUDAFyRY6BPpGVPuJD6QAXRMW6KIWiGoF1taKp5G/nzbzJC
+-----END CERTIFICATE-----
diff --git a/desktop/qa/data/test-cert-chain-3.pem b/desktop/qa/data/test-cert-chain-3.pem
new file mode 100644
index 000000000..d02dbe0f6
--- /dev/null
+++ b/desktop/qa/data/test-cert-chain-3.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID5zCCAs+gAwIBAgIU0Ar2t+Zp1ZC0Jx1An2HK+e+ghR0wDQYJKoZIhvcNAQEL
+BQAwgY8xCzAJBgNVBAYTAkNIMQwwCgYDVQQIEwNadWcxDDAKBgNVBAcTA1p1ZzET
+MBEGA1UEChMKVmVyZWlnbiBBRzEVMBMGA1UECxMMQnVzaW5lc3MgRGVwMTgwNgYD
+VQQDEy9mMWZlMWRiYS1mYjA1LTQ5OTYtOGY5NS1hODE0MjIxOThkYjktc2VydmVy
+c2lkZTAeFw0xODExMjMyMzAwMDBaFw0yMzExMjQyMjU5NTlaMIGGMYGDMDYGA1UE
+AxMvZjFmZTFkYmEtZmIwNS00OTk2LThmOTUtYTgxNDIyMTk4ZGI5LXVzZXJkZXZp
+Y2UwCQYDVQQGEwJDSDAKBgNVBAcTA1p1ZzAKBgNVBAgTA1p1ZzARBgNVBAoTClZl
+cmVpZ24gQUcwEwYDVQQLEwxCdXNpbmVzcyBEZXAwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDFQ+2fHCdE9ksa/h0aMK/DHPdq3iGWHx6eFsG3yI7RSU/7
+IA/DPht7A4U/a4qw/8PqT9Df9CvNeURXDmIG1S4ZVn7oTlEua9Da5A+HByA6M4Vk
+4meDo/tY6hE8NZy25Q3l0dZvH34eocRe0xhjGajoUo6vGfzzQ+pQPerM1VivraVf
+EZva4b3rCN7XrZkzkBPjEZijLvlk1wKcG5R1teXlEMHiHKiIeECDXmBD406ngWlt
+KzlH2bXZYkfdBSoYXW2eyM/z0kJcY4M/75re0YGMRUPK6Cp1C4F+jIIv8np09bKS
+6lvr/niemJz0BPXapsLOWXsc0Gg9sPiMpwdNbjflAgMBAAGjQjBAMA4GA1UdDwEB
+/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSdM4fK3wQY6/nXpgZL
+5Nl5udSGGjANBgkqhkiG9w0BAQsFAAOCAQEAe/6URI5GLBee3OJo00iHXCYMxOoX
+Bi2V6IC1zS6mQlRYDtzbUQAKJrxRVns2+wjeCqKetqSuZ00hM/ipCrnxwb/X0CKY
+4az9Mf9BycYnkmGNKzzskefIlUciaThdc0Ju6RVlCgFXX8vP9iMO3iQ9ET7JS4jB
+oRNFnMeyy4+HG1RwYivi1YjaPNkp7xd8Lqq56VULGY4dKZUieJceFXtYQZHuVgWo
+woAulOZH0IYqTv16tHxLovWGJaWpoMwgWo/c8sk8CfYF5vv9SMxcFnWwNooCRtWI
+HJzs/1zxXIFy9D49PcH0gwkPM66F8bPS8TMpHcyjRDF/TQFzu1JwVyFY0g==
+-----END CERTIFICATE-----
diff --git a/desktop/qa/data/test-cert-signing.pem b/desktop/qa/data/test-cert-signing.pem
new file mode 100644
index 000000000..8f5c788b5
--- /dev/null
+++ b/desktop/qa/data/test-cert-signing.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3zCCAsmgAwIBAgIUV/4FfTrYMHCb7AxAeI/zAHowUjQwCwYJKoZIhvcNAQEL
+MIGGMYGDMDYGA1UEAxMvZjFmZTFkYmEtZmIwNS00OTk2LThmOTUtYTgxNDIyMTk4
+ZGI5LXVzZXJkZXZpY2UwCQYDVQQGEwJDSDAKBgNVBAcTA1p1ZzAKBgNVBAgTA1p1
+ZzARBgNVBAoTClZlcmVpZ24gQUcwEwYDVQQLEwxCdXNpbmVzcyBEZXAwHhcNMTgx
+MTIzMjMwMDAwWhcNMjMxMTI0MjI1OTU5WjCBjjGBizA+BgNVBAMTNzNub2JKdk9n
+ZkstZjFmZTFkYmEtZmIwNS00OTk2LThmOTUtYTgxNDIyMTk4ZGI5LW9uZXRpbWUw
+CQYDVQQGEwJDSDAKBgNVBAcTA1p1ZzAKBgNVBAgTA1p1ZzARBgNVBAoTClZlcmVp
+Z24gQUcwEwYDVQQLEwxCdXNpbmVzcyBEZXAwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC/x1dyX2PgJs6oe/BcBT5XTJXtipme29mtI/FmTq5vuAFopqvA
+Eg084hNM7Q++wKyKG8F0ABF2pUOYvo6Pq6rXQqMbxUaO47NNefh4v/f6hBoMfSYm
+XtEPji8NjIcIrQ3U8yTWquaHi13hcoG/cVf16Doem5AxUdQ6a9e/jA1VU2MmSZxt
+hkABKPWdw1pfFJDLWhFrCjhePKrBxw2ArmNx66fdzpi5XK0fC2TbHD2NVc8cbYOz
+F4h3knGZo2sq4/MBevyVhzXZNn2Pjf3PbOxp1O8CLx86MuDTLDkSVjmP0yXBf6rL
+FMWtEfv1Jmf3I61meIuOcw6wvXRKzPnAKpLRAgMBAAGjPzA9MAwGA1UdEwEB/wQC
+MAAwDgYDVR0PAQH/BAQDAgDwMB0GA1UdDgQWBBSjYCO+w2VQgpdkCvoHgdgZzOPz
+BTALBgkqhkiG9w0BAQsDggEBAD6MAP/l4jkJjIJinJ1uPuA6wOVBff1Beb6JV2jo
+ay1099VkFVHe/tEeyDcx2j+a2CSKsLdOoG/RjcBup3XsumxlqQS6r9GjZLwtNkxI
+nFNLqwv2PbXesSLTR1mjm/6BdcjGEgcxllkST2uYb8DMwbASNQT+rvmGy85b3D4m
+tk3scetFWozkfs+lRUql1dWf2aZTcN+IIZlSpxEZu+w1w8yMoDvVV0pCz49d7WIA
+RIXDE8QJpiAqszuCbqsNdAbr/pAyYzIAIfKKuteWaViesdY26pZfCe2JsfUTDWSi
+PDrFAlkbtB5LGYd4Tfliuvud6I/87GZsuWcCYoS4wXDkRS4=
+-----END CERTIFICATE-----
diff --git a/desktop/qa/deployment_misc/test_dp_version.cxx b/desktop/qa/deployment_misc/test_dp_version.cxx
new file mode 100644
index 000000000..e89668686
--- /dev/null
+++ b/desktop/qa/deployment_misc/test_dp_version.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cstddef>
+#include <sal/types.h>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <rtl/ustring.hxx>
+
+#include "../../source/deployment/inc/dp_version.hxx"
+
+namespace {
+
+class Test: public ::CppUnit::TestFixture {
+public:
+ void test();
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void Test::test() {
+ struct Data {
+ OUString version1;
+ OUString version2;
+ ::dp_misc::Order order;
+ };
+ static Data const data[] = {
+ { OUString(),
+ OUString("0.0000.00.0"),
+ ::dp_misc::EQUAL },
+ { OUString(".01"),
+ OUString("0.1"),
+ ::dp_misc::EQUAL },
+ { OUString("10"),
+ OUString("2"),
+ ::dp_misc::GREATER },
+ { OUString("9223372036854775808"),
+ // 2^63
+ OUString("9223372036854775807"),
+ ::dp_misc::GREATER }
+ };
+ for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) {
+ CPPUNIT_ASSERT_EQUAL(
+ data[i].order,
+ ::dp_misc::compareVersions(data[i].version1, data[i].version2));
+ static ::dp_misc::Order const reverse[3] = {
+ ::dp_misc::GREATER, ::dp_misc::EQUAL, ::dp_misc::LESS
+ };
+ CPPUNIT_ASSERT_EQUAL(
+ reverse[data[i].order],
+ ::dp_misc::compareVersions(data[i].version2, data[i].version1));
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/qa/desktop_app/test_desktop_app.cxx b/desktop/qa/desktop_app/test_desktop_app.cxx
new file mode 100644
index 000000000..0e86368e6
--- /dev/null
+++ b/desktop/qa/desktop_app/test_desktop_app.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include "../../source/app/cmdlineargs.hxx"
+
+namespace {
+
+class Test: public ::CppUnit::TestFixture {
+public:
+ void testTdf100837();
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testTdf100837);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+class TestSupplier : public desktop::CommandLineArgs::Supplier {
+public:
+ explicit TestSupplier(const std::initializer_list<OUString>& args) : m_args(args) {}
+
+ virtual std::optional< OUString > getCwdUrl() override { return std::optional< OUString >(); }
+ virtual bool next(OUString * argument) override {
+ CPPUNIT_ASSERT(argument != nullptr);
+ if (m_index < m_args.size()) {
+ *argument = m_args[m_index++];
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+private:
+ std::vector< OUString > m_args;
+ std::vector< OUString >::size_type m_index = 0;
+};
+
+// Test Office URI Schemes support
+void Test::testTdf100837() {
+ auto xContext = ::cppu::defaultBootstrap_InitialComponentContext();
+ ::css::uno::Reference<::css::lang::XMultiComponentFactory> xFactory(xContext->getServiceManager());
+ ::css::uno::Reference<::css::lang::XMultiServiceFactory> xSM(xFactory, ::css::uno::UNO_QUERY_THROW);
+ // Without this we're crashing because callees are using getProcessServiceFactory
+ ::comphelper::setProcessServiceFactory(xSM);
+
+ {
+ // 1. Test default behaviour: Office URIs define open mode
+ TestSupplier supplier{ "foo", "ms-word:ofe|u|bar1", "ms-word:ofv|u|bar2", "ms-word:nft|u|bar3", "baz" };
+ desktop::CommandLineArgs args(supplier);
+ auto vOpenList = args.GetOpenList();
+ auto vForceOpenList = args.GetForceOpenList();
+ auto vViewList = args.GetViewList();
+ auto vForceNewList = args.GetForceNewList();
+ // 2 documents go to Open list: foo; baz
+ CPPUNIT_ASSERT_EQUAL(decltype(vOpenList.size())(2), vOpenList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), vOpenList[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), vOpenList[1]);
+ // 1 document goes to ForceOpen list: bar1
+ CPPUNIT_ASSERT_EQUAL(decltype(vForceOpenList.size())(1), vForceOpenList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar1"), vForceOpenList[0]);
+ // 1 document goes to View list: bar2
+ CPPUNIT_ASSERT_EQUAL(decltype(vViewList.size())(1), vViewList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar2"), vViewList[0]);
+ // 1 document goes to ForceNew list: bar3
+ CPPUNIT_ASSERT_EQUAL(decltype(vForceNewList.size())(1), vForceNewList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar3"), vForceNewList[0]);
+ }
+
+ {
+ // 2. Test explicit open mode arguments. Office URI commands should have no effect
+ TestSupplier supplier{ "--view", "ms-word:ofe|u|foo", "-o", "ms-word:ofv|u|bar", "ms-word:nft|u|baz" };
+ desktop::CommandLineArgs args(supplier);
+ auto vViewList = args.GetViewList();
+ auto vForceOpenList = args.GetForceOpenList();
+ // 1 document goes to View list: foo
+ CPPUNIT_ASSERT_EQUAL(decltype(vViewList.size())(1), vViewList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), vViewList[0]);
+ // 2 documents go to ForceOpen list: bar, baz
+ CPPUNIT_ASSERT_EQUAL(decltype(vForceOpenList.size())(2), vForceOpenList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar"), vForceOpenList[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), vForceOpenList[1]);
+ }
+
+ {
+ // 3. Test encoded URLs
+ TestSupplier supplier{ "foo", "ms-word:ofe%7Cu%7cbar1", "ms-word:ofv%7cu%7Cbar2", "ms-word:nft%7Cu%7cbar3", "baz" };
+ desktop::CommandLineArgs args(supplier);
+ auto vOpenList = args.GetOpenList();
+ auto vForceOpenList = args.GetForceOpenList();
+ auto vViewList = args.GetViewList();
+ auto vForceNewList = args.GetForceNewList();
+ // 2 documents go to Open list: foo; baz
+ CPPUNIT_ASSERT_EQUAL(decltype(vOpenList.size())(2), vOpenList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), vOpenList[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), vOpenList[1]);
+ // 1 document goes to ForceOpen list: bar1
+ CPPUNIT_ASSERT_EQUAL(decltype(vForceOpenList.size())(1), vForceOpenList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar1"), vForceOpenList[0]);
+ // 1 document goes to View list: bar2
+ CPPUNIT_ASSERT_EQUAL(decltype(vViewList.size())(1), vViewList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar2"), vViewList[0]);
+ // 1 document goes to ForceNew list: bar3
+ CPPUNIT_ASSERT_EQUAL(decltype(vForceNewList.size())(1), vForceNewList.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("bar3"), vForceNewList[0]);
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
new file mode 100644
index 000000000..9ee4a596e
--- /dev/null
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -0,0 +1,2971 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/XReschedule.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <vcl/scheduler.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/window.hxx>
+#include <vcl/ctrl.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <comphelper/processfactory.hxx>
+#include <rtl/math.hxx>
+#include <rtl/uri.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <test/unoapi_test.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <osl/conditn.hxx>
+#include <osl/thread.hxx>
+#include <svl/srchitem.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <unotools/tempfile.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <unotools/datetime.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <cairo.h>
+#include <config_features.h>
+#include <config_mpl.h>
+
+#include <lib/init.hxx>
+#include <svx/svxids.hrc>
+
+using namespace com::sun::star;
+using namespace desktop;
+
+class DesktopLOKTest : public UnoApiTest
+{
+ class Resetter
+ {
+ private:
+ std::function<void ()> m_Func;
+
+ public:
+ Resetter(std::function<void ()> const& rFunc)
+ : m_Func(rFunc)
+ {
+ }
+ ~Resetter()
+ {
+ try
+ {
+ m_Func();
+ }
+ catch (...) // has to be reliable
+ {
+ fprintf(stderr, "resetter failed with exception\n");
+ abort();
+ }
+ }
+ };
+
+public:
+ DesktopLOKTest() : UnoApiTest("/desktop/qa/data/"),
+ m_nSelectionBeforeSearchResult(0),
+ m_nSelectionAfterSearchResult(0),
+ m_bModified(false),
+ m_nTrackChanges(0)
+ {
+ }
+
+ void readFileIntoByteVector(OUString const & sFilename, std::vector<sal_uInt8> & rByteVector);
+
+ virtual void setUp() override
+ {
+ comphelper::LibreOfficeKit::setActive(true);
+
+ UnoApiTest::setUp();
+ mxDesktop.set(frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
+ SfxApplication::GetOrCreate();
+ };
+
+ virtual void tearDown() override
+ {
+ if (m_pDocument)
+ m_pDocument->pClass->registerCallback(m_pDocument.get(), nullptr, nullptr);
+ closeDoc();
+
+ UnoApiTest::tearDown();
+
+ comphelper::LibreOfficeKit::setActive(false);
+ };
+
+ LibLODocument_Impl* loadDocUrl(const OUString& rFileURL, LibreOfficeKitDocumentType eType);
+ LibLODocument_Impl* loadDoc(const char* pName, LibreOfficeKitDocumentType eType = LOK_DOCTYPE_TEXT);
+ void closeDoc();
+ static void callback(int nType, const char* pPayload, void* pData);
+ void callbackImpl(int nType, const char* pPayload);
+
+ void testGetStyles();
+ void testGetFonts();
+ void testCreateView();
+ void testGetFilterTypes();
+ void testGetPartPageRectangles();
+ void testSearchCalc();
+ void testSearchAllNotificationsCalc();
+ void testPaintTile();
+ void testSaveAs();
+ void testSaveAsCalc();
+ void testPasteWriter();
+ void testPasteWriterJPEG();
+ void testUndoWriter();
+ void testRowColumnHeaders();
+ void testHiddenRowHeaders();
+ void testCellCursor();
+ void testCommandResult();
+ void testWriterComments();
+ void testSheetOperations();
+ void testSheetSelections();
+ void testContextMenuCalc();
+ void testContextMenuWriter();
+ void testContextMenuImpress();
+ void testNotificationCompression();
+ void testTileInvalidationCompression();
+ void testPartInInvalidation();
+ void testInput();
+ void testRedlineWriter();
+ void testTrackChanges();
+ void testRedlineCalc();
+ void testPaintPartTile();
+ void testWriterCommentInsertCursor();
+#if HAVE_MORE_FONTS
+ void testGetFontSubset();
+#endif
+ void testCommentsWriter();
+ void testCommentsCalc();
+ void testCommentsImpress();
+ void testCommentsCallbacksWriter();
+ void testRunMacro();
+ void testExtractParameter();
+ void testGetSignatureState_NonSigned();
+ void testGetSignatureState_Signed();
+ void testInsertCertificate_DER_ODT();
+ void testInsertCertificate_PEM_ODT();
+ void testInsertCertificate_PEM_DOCX();
+ void testSignDocument_PEM_PDF();
+ void testTextSelectionHandles();
+ void testComplexSelection();
+ void testSpellcheckerMultiView();
+ void testDialogPaste();
+ void testShowHideDialog();
+ void testDialogInput();
+ void testCalcSaveAs();
+ void testControlState();
+ void testMetricField();
+ void testABI();
+
+ CPPUNIT_TEST_SUITE(DesktopLOKTest);
+ CPPUNIT_TEST(testGetStyles);
+ CPPUNIT_TEST(testGetFonts);
+ CPPUNIT_TEST(testCreateView);
+ CPPUNIT_TEST(testGetFilterTypes);
+ CPPUNIT_TEST(testGetPartPageRectangles);
+ CPPUNIT_TEST(testSearchCalc);
+ CPPUNIT_TEST(testSearchAllNotificationsCalc);
+ CPPUNIT_TEST(testPaintTile);
+ CPPUNIT_TEST(testSaveAs);
+ CPPUNIT_TEST(testSaveAsCalc);
+ CPPUNIT_TEST(testPasteWriter);
+ CPPUNIT_TEST(testPasteWriterJPEG);
+ CPPUNIT_TEST(testUndoWriter);
+ CPPUNIT_TEST(testRowColumnHeaders);
+ CPPUNIT_TEST(testHiddenRowHeaders);
+ CPPUNIT_TEST(testCellCursor);
+ CPPUNIT_TEST(testCommandResult);
+ CPPUNIT_TEST(testWriterComments);
+ CPPUNIT_TEST(testSheetOperations);
+ CPPUNIT_TEST(testSheetSelections);
+ CPPUNIT_TEST(testContextMenuCalc);
+ CPPUNIT_TEST(testContextMenuWriter);
+ CPPUNIT_TEST(testContextMenuImpress);
+ CPPUNIT_TEST(testNotificationCompression);
+ CPPUNIT_TEST(testTileInvalidationCompression);
+ CPPUNIT_TEST(testPartInInvalidation);
+ CPPUNIT_TEST(testInput);
+ CPPUNIT_TEST(testRedlineWriter);
+ CPPUNIT_TEST(testTrackChanges);
+ CPPUNIT_TEST(testRedlineCalc);
+ CPPUNIT_TEST(testPaintPartTile);
+ CPPUNIT_TEST(testWriterCommentInsertCursor);
+#if HAVE_MORE_FONTS
+ CPPUNIT_TEST(testGetFontSubset);
+#endif
+ CPPUNIT_TEST(testCommentsWriter);
+ CPPUNIT_TEST(testCommentsCalc);
+ CPPUNIT_TEST(testCommentsImpress);
+ CPPUNIT_TEST(testCommentsCallbacksWriter);
+ CPPUNIT_TEST(testRunMacro);
+ CPPUNIT_TEST(testExtractParameter);
+ CPPUNIT_TEST(testGetSignatureState_Signed);
+ CPPUNIT_TEST(testGetSignatureState_NonSigned);
+#if !MPL_HAVE_SUBSET
+ CPPUNIT_TEST(testInsertCertificate_DER_ODT);
+ CPPUNIT_TEST(testInsertCertificate_PEM_ODT);
+ CPPUNIT_TEST(testInsertCertificate_PEM_DOCX);
+ CPPUNIT_TEST(testSignDocument_PEM_PDF);
+#endif
+ CPPUNIT_TEST(testTextSelectionHandles);
+ CPPUNIT_TEST(testComplexSelection);
+ CPPUNIT_TEST(testSpellcheckerMultiView);
+ CPPUNIT_TEST(testDialogPaste);
+ CPPUNIT_TEST(testShowHideDialog);
+ CPPUNIT_TEST(testDialogInput);
+ CPPUNIT_TEST(testCalcSaveAs);
+ CPPUNIT_TEST(testControlState);
+ CPPUNIT_TEST(testMetricField);
+ CPPUNIT_TEST(testABI);
+ CPPUNIT_TEST_SUITE_END();
+
+ uno::Reference<lang::XComponent> mxComponent;
+ OString m_aTextSelection;
+ OString m_aTextSelectionStart;
+ OString m_aTextSelectionEnd;
+ std::vector<OString> m_aSearchResultSelection;
+ std::vector<int> m_aSearchResultPart;
+ int m_nSelectionBeforeSearchResult;
+ int m_nSelectionAfterSearchResult;
+
+ // for testCommandResult
+ osl::Condition m_aCommandResultCondition;
+ OString m_aCommandResult;
+
+ // for testModifiedStatus
+ osl::Condition m_aStateChangedCondition;
+ bool m_bModified;
+ int m_nTrackChanges;
+
+ // for testContextMenu{Calc, Writer}
+ osl::Condition m_aContextMenuCondition;
+ boost::property_tree::ptree m_aContextMenuResult;
+
+ std::unique_ptr<LibLODocument_Impl> m_pDocument;
+};
+
+static Control* GetFocusControl(vcl::Window const * pParent)
+{
+ sal_uInt16 nChildren = pParent->GetChildCount();
+ for (sal_uInt16 nChild = 0; nChild < nChildren; ++nChild)
+ {
+ vcl::Window* pChild = pParent->GetChild( nChild );
+ Control* pCtrl = dynamic_cast<Control*>(pChild);
+ if (pCtrl && pCtrl->HasControlFocus())
+ return pCtrl;
+
+ Control* pSubCtrl = GetFocusControl( pChild );
+ if (pSubCtrl)
+ return pSubCtrl;
+ }
+ return nullptr;
+}
+
+LibLODocument_Impl* DesktopLOKTest::loadDocUrl(const OUString& rFileURL, LibreOfficeKitDocumentType eType)
+{
+ OUString aService;
+ switch (eType)
+ {
+ case LOK_DOCTYPE_TEXT:
+ aService = "com.sun.star.text.TextDocument";
+ break;
+ case LOK_DOCTYPE_SPREADSHEET:
+ aService = "com.sun.star.sheet.SpreadsheetDocument";
+ break;
+ case LOK_DOCTYPE_PRESENTATION:
+ aService = "com.sun.star.presentation.PresentationDocument";
+ break;
+ default:
+ CPPUNIT_ASSERT(false);
+ break;
+ }
+ mxComponent = loadFromDesktop(rFileURL, aService);
+ if (!mxComponent.is())
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ m_pDocument.reset(new LibLODocument_Impl(mxComponent));
+ return m_pDocument.get();
+}
+
+LibLODocument_Impl* DesktopLOKTest::loadDoc(const char* pName, LibreOfficeKitDocumentType eType)
+{
+ OUString aFileURL;
+ createFileURL(OUString::createFromAscii(pName), aFileURL);
+ return loadDocUrl(aFileURL, eType);
+}
+
+void DesktopLOKTest::closeDoc()
+{
+ if (mxComponent.is())
+ {
+ closeDocument(mxComponent);
+ mxComponent.clear();
+ }
+}
+
+void DesktopLOKTest::callback(int nType, const char* pPayload, void* pData)
+{
+ static_cast<DesktopLOKTest*>(pData)->callbackImpl(nType, pPayload);
+}
+
+void DesktopLOKTest::callbackImpl(int nType, const char* pPayload)
+{
+ switch (nType)
+ {
+ case LOK_CALLBACK_TEXT_SELECTION:
+ {
+ m_aTextSelection = pPayload;
+ if (m_aSearchResultSelection.empty())
+ ++m_nSelectionBeforeSearchResult;
+ else
+ ++m_nSelectionAfterSearchResult;
+ }
+ break;
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ m_aTextSelectionStart = pPayload;
+ break;
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ m_aTextSelectionEnd = pPayload;
+ break;
+ case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+ {
+ m_aSearchResultSelection.clear();
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, aTree);
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection"))
+ {
+ m_aSearchResultSelection.emplace_back(rValue.second.get<std::string>("rectangles").c_str());
+ m_aSearchResultPart.push_back(std::atoi(rValue.second.get<std::string>("part").c_str()));
+ }
+ }
+ break;
+ case LOK_CALLBACK_UNO_COMMAND_RESULT:
+ {
+ m_aCommandResult = pPayload;
+ m_aCommandResultCondition.set();
+ }
+ break;
+ case LOK_CALLBACK_STATE_CHANGED:
+ {
+ OString aPayload(pPayload);
+ OString aPrefix(".uno:ModifiedStatus=");
+ if (aPayload.startsWith(aPrefix))
+ {
+ m_bModified = aPayload.copy(aPrefix.getLength()).toBoolean();
+ m_aStateChangedCondition.set();
+ }
+ else if (aPayload.startsWith(".uno:TrackChanges=") && aPayload.endsWith("=true"))
+ ++m_nTrackChanges;
+ }
+ break;
+ case LOK_CALLBACK_CONTEXT_MENU:
+ {
+ m_aContextMenuResult.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aContextMenuResult);
+ m_aContextMenuCondition.set();
+ }
+ break;
+ }
+}
+
+void DesktopLOKTest::testGetStyles()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:StyleApply");
+ std::stringstream aStream(pJSON);
+ boost::property_tree::read_json(aStream, aTree);
+ CPPUNIT_ASSERT( !aTree.empty() );
+ CPPUNIT_ASSERT_EQUAL( std::string(".uno:StyleApply"), aTree.get_child("commandName").get_value<std::string>() );
+
+ boost::property_tree::ptree aValues = aTree.get_child("commandValues");
+ CPPUNIT_ASSERT( !aValues.empty() );
+ for (const auto& rPair : aValues)
+ {
+ if( rPair.first != "ClearStyle")
+ {
+ CPPUNIT_ASSERT( !rPair.second.empty());
+ }
+ if (rPair.first != "CharacterStyles" &&
+ rPair.first != "ParagraphStyles" &&
+ rPair.first != "FrameStyles" &&
+ rPair.first != "PageStyles" &&
+ rPair.first != "NumberingStyles" &&
+ rPair.first != "CellStyles" &&
+ rPair.first != "ShapeStyles" &&
+ rPair.first != "TableStyles" &&
+ rPair.first != "HeaderFooter" &&
+ rPair.first != "Commands")
+ {
+ CPPUNIT_FAIL("Unknown style family: " + rPair.first);
+ }
+ }
+}
+
+void DesktopLOKTest::testGetFonts()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_presentation.odp");
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:CharFontName");
+ std::stringstream aStream(pJSON);
+ boost::property_tree::read_json(aStream, aTree);
+ CPPUNIT_ASSERT( !aTree.empty() );
+ CPPUNIT_ASSERT_EQUAL( std::string(".uno:CharFontName"), aTree.get_child("commandName").get_value<std::string>() );
+
+ boost::property_tree::ptree aValues = aTree.get_child("commandValues");
+ CPPUNIT_ASSERT( !aValues.empty() );
+ for (const auto& rPair : aValues)
+ {
+ // check that we have font sizes available for each font
+ CPPUNIT_ASSERT( !rPair.second.empty());
+ }
+ free(pJSON);
+}
+
+void DesktopLOKTest::testCreateView()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+
+ int nId0 = pDocument->m_pDocumentClass->getView(pDocument);
+ int nId1 = pDocument->m_pDocumentClass->createView(pDocument);
+ CPPUNIT_ASSERT_EQUAL(2, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+
+ // Test getViewIds().
+ std::vector<int> aViewIds(2);
+ CPPUNIT_ASSERT(pDocument->m_pDocumentClass->getViewIds(pDocument, aViewIds.data(), aViewIds.size()));
+ CPPUNIT_ASSERT_EQUAL(nId0, aViewIds[0]);
+ CPPUNIT_ASSERT_EQUAL(nId1, aViewIds[1]);
+
+ // Make sure the created view is the active one, then switch to the old
+ // one.
+ CPPUNIT_ASSERT_EQUAL(nId1, pDocument->m_pDocumentClass->getView(pDocument));
+ pDocument->m_pDocumentClass->setView(pDocument, nId0);
+ CPPUNIT_ASSERT_EQUAL(nId0, pDocument->m_pDocumentClass->getView(pDocument));
+
+ pDocument->m_pDocumentClass->destroyView(pDocument, nId1);
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+}
+
+void DesktopLOKTest::testGetPartPageRectangles()
+{
+ // Test that we get as many page rectangles as expected: blank document is
+ // one page.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ char* pRectangles = pDocument->pClass->getPartPageRectangles(pDocument);
+ OUString sRectangles = OUString::fromUtf8(pRectangles);
+
+ std::vector<OUString> aRectangles;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aRectangle = sRectangles.getToken(0, ';', nIndex);
+ if (!aRectangle.isEmpty())
+ aRectangles.push_back(aRectangle);
+ }
+ while (nIndex >= 0);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aRectangles.size());
+
+ free(pRectangles);
+}
+
+void DesktopLOKTest::testGetFilterTypes()
+{
+ LibLibreOffice_Impl aOffice;
+ char* pJSON = aOffice.m_pOfficeClass->getFilterTypes(&aOffice);
+
+ std::stringstream aStream(pJSON);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ CPPUNIT_ASSERT(!aTree.empty());
+ CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.oasis.opendocument.text"), aTree.get_child("writer8").get_child("MediaType").get_value<std::string>());
+ free(pJSON);
+}
+
+void DesktopLOKTest::testSearchCalc()
+{
+ LibLibreOffice_Impl aOffice;
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
+ {"SearchItem.Backward", uno::makeAny(false)},
+ {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<OString> aSelections;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OString aToken = m_aTextSelection.getToken(0, ';', nIndex);
+ aSelections.push_back(aToken);
+ } while (nIndex >= 0);
+ // This was 1, find-all only found one match.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aSelections.size());
+ // Make sure that we get exactly as many rectangle lists as matches.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), m_aSearchResultSelection.size());
+ // Result is on the first sheet.
+ CPPUNIT_ASSERT_EQUAL(0, m_aSearchResultPart[0]);
+}
+
+void DesktopLOKTest::testSearchAllNotificationsCalc()
+{
+ LibLibreOffice_Impl aOffice;
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"SearchItem.SearchString", uno::makeAny(OUString("foo"))},
+ {"SearchItem.Backward", uno::makeAny(false)},
+ {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(SvxSearchCmd::FIND_ALL))},
+ }));
+ comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+ Scheduler::ProcessEventsToIdle();
+
+ // This was 1, make sure that we get no notifications about selection changes during search.
+ CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
+ // But we do get the selection afterwards.
+ CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
+}
+
+void DesktopLOKTest::testPaintTile()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ int nCanvasWidth = 100;
+ int nCanvasHeight = 300;
+ sal_Int32 nStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, nCanvasWidth);
+ std::vector<unsigned char> aBuffer(nStride * nCanvasHeight);
+ int nTilePosX = 0;
+ int nTilePosY = 0;
+ int nTileWidth = 1000;
+ int nTileHeight = 3000;
+
+ // This used to crash: paintTile() implementation did not handle
+ // nCanvasWidth != nCanvasHeight correctly, as usually both are just always
+ // 256.
+ pDocument->pClass->paintTile(pDocument, aBuffer.data(), nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+
+ // This crashed in OutputDevice::DrawDeviceAlphaBitmap().
+ nCanvasWidth = 200;
+ nCanvasHeight = 200;
+ nTileWidth = 4000;
+ nTileHeight = 4000;
+ aBuffer.resize(nCanvasWidth * nCanvasHeight * 4);
+ pDocument->pClass->paintTile(pDocument, aBuffer.data(), nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+}
+
+void DesktopLOKTest::testSaveAs()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "png", nullptr));
+}
+
+void DesktopLOKTest::testSaveAsCalc()
+{
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "png", nullptr));
+}
+
+void DesktopLOKTest::testPasteWriter()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ OString aText("hello");
+
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength()));
+
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT_EQUAL(OString("hello"), OString(pText));
+ free(pText);
+
+ // textt/plain should be rejected.
+ CPPUNIT_ASSERT(!pDocument->pClass->paste(pDocument, "textt/plain;charset=utf-8", aText.getStr(), aText.getLength()));
+ // Writer is expected to support text/html.
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/html", aText.getStr(), aText.getLength()));
+
+ // Overwrite doc contents with a HTML paste.
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ OString aComment("foo <!-- bar --> baz");
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/html", aComment.getStr(), aComment.getLength()));
+
+ // Check if we have a comment.
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xTextDocument->getText(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphEnumeration = xParagraphEnumerationAccess->createEnumeration();
+ uno::Reference<container::XEnumerationAccess> xParagraph(xParagraphEnumeration->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xTextPortionEnumeration = xParagraph->createEnumeration();
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortionEnumeration->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("Text"), xTextPortion->getPropertyValue("TextPortionType").get<OUString>());
+ // Without the accompanying fix in place, this test would have failed, as we had a comment
+ // between "foo" and "baz".
+ CPPUNIT_ASSERT(!xTextPortionEnumeration->hasMoreElements());
+}
+
+void DesktopLOKTest::testPasteWriterJPEG()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+
+ OUString aFileURL;
+ createFileURL("paste.jpg", aFileURL);
+ std::ifstream aImageStream(aFileURL.toUtf8().copy(strlen("file://")).getStr());
+ std::vector<char> aImageContents((std::istreambuf_iterator<char>(aImageStream)), std::istreambuf_iterator<char>());
+
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "image/jpeg", aImageContents.data(), aImageContents.size()));
+
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ // This was 0, JPEG was not handled as a format for clipboard paste.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xDrawPage->getCount());
+
+ uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ // This was text::TextContentAnchorType_AT_PARAGRAPH.
+ CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER, xShape->getPropertyValue("AnchorType").get<text::TextContentAnchorType>());
+
+ // Delete the pasted picture, and paste again with a custom anchor type.
+ uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY_THROW)->dispose();
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AT_CHARACTER))},
+ }));
+ comphelper::dispatchCommand(".uno:Paste", aPropertyValues);
+ xShape.set(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+ // This was text::TextContentAnchorType_AS_CHARACTER, AnchorType argument was ignored.
+ CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER, xShape->getPropertyValue("AnchorType").get<text::TextContentAnchorType>());
+}
+
+void DesktopLOKTest::testUndoWriter()
+{
+ // Load a Writer document and press a key.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 't', 0);
+ Scheduler::ProcessEventsToIdle();
+ // Get undo info.
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:Undo");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // Make sure that pressing a key creates exactly one undo action.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("actions").size());
+}
+
+void DesktopLOKTest::testRowColumnHeaders()
+{
+ /*
+ * Payload example:
+ *
+ * {
+ * "rows": [
+ * {
+ * "size": "254.987250637468",
+ * "text": "1"
+ * },
+ * {
+ * "size": "509.974501274936",
+ * "text": "2"
+ * }
+ * ],
+ * "columns": [
+ * {
+ * "size": "1274.93625318734",
+ * "text": "A"
+ * },
+ * {
+ * "size": "2549.87250637468",
+ * "text": "B"
+ * }
+ * ]
+ * }
+ *
+ * "size" defines the bottom/right boundary of a row/column in twips (size between 0 and boundary)
+ * "text" has the header label in UTF-8
+ */
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+
+ long nWidth = 0;
+ long nHeight = 0;
+ pDocument->m_pDocumentClass->getDocumentSize(pDocument, &nWidth, &nHeight);
+ long nX = rtl::math::round(nWidth / 4.0);
+ long nY = rtl::math::round(nHeight / 4.0);
+ nWidth = rtl::math::round(nWidth / 2.0);
+ nHeight = rtl::math::round(nHeight / 2.0);
+
+ std::stringstream aPayload;
+ aPayload << ".uno:ViewRowColumnHeaders?x=" << nX << "&y=" << nY << "&width=" << nWidth << "&height=" << nHeight;
+
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, aPayload.str().c_str());
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+
+ CPPUNIT_ASSERT(!aStream.str().empty());
+
+ boost::property_tree::read_json(aStream, aTree);
+ sal_Int32 nPrevious = 0;
+ bool bFirstHeader = true;
+ bool bNotEnoughHeaders = true;
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("rows"))
+ {
+ sal_Int32 nSize = OString(rValue.second.get<std::string>("size").c_str()).toInt32();
+ nSize *= 15; /* TWIPS_PER_PIXEL */
+ OString aText(rValue.second.get<std::string>("text").c_str());
+
+ if (bFirstHeader)
+ {
+ CPPUNIT_ASSERT(nSize <= nY);
+ CPPUNIT_ASSERT_EQUAL(OString("10"), aText);
+ bFirstHeader = false;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(nSize > 0);
+ CPPUNIT_ASSERT(nPrevious < nSize);
+ if (nSize > nY + nHeight)
+ {
+ bNotEnoughHeaders = false;
+ break;
+ }
+ }
+ nPrevious = nSize;
+ }
+ CPPUNIT_ASSERT(!bNotEnoughHeaders);
+
+ nPrevious = 0;
+ bFirstHeader = true;
+ bNotEnoughHeaders = true;
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("columns"))
+ {
+ sal_Int32 nSize = OString(rValue.second.get<std::string>("size").c_str()).toInt32();
+ nSize *= 15; /* TWIPS_PER_PIXEL */
+ OString aText(rValue.second.get<std::string>("text").c_str());
+ if (bFirstHeader)
+ {
+ CPPUNIT_ASSERT(nSize <= nX);
+ CPPUNIT_ASSERT_EQUAL(OString("3"), aText);
+ bFirstHeader = false;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(nSize > 0);
+ CPPUNIT_ASSERT(nPrevious < nSize);
+ if (nSize > nX + nWidth)
+ {
+ bNotEnoughHeaders = false;
+ break;
+ }
+ }
+ nPrevious = nSize;
+ }
+ CPPUNIT_ASSERT(!bNotEnoughHeaders);
+}
+
+void DesktopLOKTest::testHiddenRowHeaders()
+{
+ LibLODocument_Impl* pDocument = loadDoc("hidden-row.ods");
+
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+
+ long const nX = 0;
+ long const nY = 0;
+ long nWidth = 0;
+ long nHeight = 0;
+ pDocument->m_pDocumentClass->getDocumentSize(pDocument, &nWidth, &nHeight);
+
+ std::stringstream aPayload;
+ aPayload << ".uno:ViewRowColumnHeaders?x=" << nX << "&y=" << nY << "&width=" << nWidth << "&height=" << nHeight;
+
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, aPayload.str().c_str());
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+
+ boost::property_tree::read_json(aStream, aTree);
+ sal_Int32 nPrevious = 0;
+ sal_Int32 nIndex = 0;
+ for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("rows"))
+ {
+ sal_Int32 nSize = OString(rValue.second.get<std::string>("size").c_str()).toInt32();
+
+ if (nIndex++ == 2)
+ {
+ // nSize was 510, nPrevious was 255, i.e. hidden row wasn't reported as 0 height.
+ CPPUNIT_ASSERT_EQUAL(nPrevious, nSize);
+ break;
+ }
+ nPrevious = nSize;
+ }
+}
+
+void DesktopLOKTest::testCellCursor()
+{
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+
+ boost::property_tree::ptree aTree;
+
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:CellCursor?tileWidth=1&tileHeight=1&outputWidth=1&outputHeight=1");
+
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+
+ boost::property_tree::read_json(aStream, aTree);
+
+ OString aRectangle(aTree.get<std::string>("commandValues").c_str());
+ // cell cursor geometry + col + row
+ CPPUNIT_ASSERT_EQUAL(OString("0, 0, 1274, 254, 0, 0"), aRectangle);
+}
+
+void DesktopLOKTest::testCommandResult()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+
+ // the postUnoCommand() is supposed to be async, let's test it safely
+ // [no idea if it is async in reality - most probably we are operating
+ // under some solar mutex or something anyway ;-) - but...]
+ TimeValue aTimeValue = { 2 , 0 }; // 2 seconds max
+
+ // nothing is triggered when we have no callback yet, we just time out on
+ // the condition var.
+ m_aCommandResultCondition.reset();
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Bold", nullptr, true);
+ Scheduler::ProcessEventsToIdle();
+ m_aCommandResultCondition.wait(aTimeValue);
+
+ CPPUNIT_ASSERT(m_aCommandResult.isEmpty());
+
+ // but we get some real values when the callback is set up
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ m_aCommandResultCondition.reset();
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Bold", nullptr, true);
+ Scheduler::ProcessEventsToIdle();
+ m_aCommandResultCondition.wait(aTimeValue);
+
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(m_aCommandResult.getStr());
+ boost::property_tree::read_json(aStream, aTree);
+
+ CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), aTree.get_child("commandName").get_value<std::string>());
+ CPPUNIT_ASSERT_EQUAL(true, aTree.get_child("success").get_value<bool>());
+}
+
+void DesktopLOKTest::testWriterComments()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+ uno::Reference<awt::XReschedule> xToolkit = com::sun::star::awt::Toolkit::create(comphelper::getProcessComponentContext());
+
+ // Insert a comment at the beginning of the document and wait till the main
+ // loop grabs the focus, so characters end up in the annotation window.
+ TimeValue const aTimeValue = {2 , 0}; // 2 seconds max
+ m_aCommandResultCondition.reset();
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:InsertAnnotation", nullptr, true);
+ Scheduler::ProcessEventsToIdle();
+ m_aCommandResultCondition.wait(aTimeValue);
+ CPPUNIT_ASSERT(!m_aCommandResult.isEmpty());
+ xToolkit->reschedule();
+
+ // Test that we have a comment.
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xTextDocument->getText(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphEnumeration = xParagraphEnumerationAccess->createEnumeration();
+ uno::Reference<container::XEnumerationAccess> xParagraph(xParagraphEnumeration->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xTextPortionEnumeration = xParagraph->createEnumeration();
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortionEnumeration->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), xTextPortion->getPropertyValue("TextPortionType").get<OUString>());
+
+ // Type "test" and finish editing.
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'e', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 's', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, com::sun::star::awt::Key::ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+
+ // Test that the typed characters ended up in the right window.
+ auto xTextField = xTextPortion->getPropertyValue("TextField").get< uno::Reference<beans::XPropertySet> >();
+ // This was empty, typed characters ended up in the body text.
+ CPPUNIT_ASSERT_EQUAL(OUString("test"), xTextField->getPropertyValue("Content").get<OUString>());
+}
+
+void DesktopLOKTest::testTrackChanges()
+{
+ // Load a document and create two views.
+ LibLibreOffice_Impl aOffice;
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+ pDocument->pClass->createView(pDocument);
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+ Scheduler::ProcessEventsToIdle();
+
+ // Enable track changes and assert that both views get notified.
+ m_nTrackChanges = 0;
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:TrackChanges", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ // This was 1, only the active view was notified.
+ CPPUNIT_ASSERT_EQUAL(2, m_nTrackChanges);
+}
+
+void DesktopLOKTest::testSheetOperations()
+{
+ LibLODocument_Impl* pDocument = loadDoc("sheets.ods");
+
+ // insert the last sheet
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Insert",
+ "{ \"Name\": { \"type\": \"string\", \"value\": \"LastSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 0 } }", false);
+
+ // insert the first sheet
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Insert",
+ "{ \"Name\": { \"type\": \"string\", \"value\": \"FirstSheet\" }, \"Index\": { \"type\": \"long\", \"value\": 1 } }", false);
+
+ // rename the \"Sheet1\" (2nd now) to \"Renamed\"
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Name",
+ "{ \"Name\": { \"type\": \"string\", \"value\": \"Renamed\" }, \"Index\": { \"type\": \"long\", \"value\": 2 } }", false);
+
+ // delete the \"Sheet2\" (3rd)
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:Remove",
+ "{ \"Index\": { \"type\": \"long\", \"value\": 3 } }", false);
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(6, pDocument->pClass->getParts(pDocument));
+
+ std::vector<OString> aExpected = { "FirstSheet", "Renamed", "Sheet3", "Sheet4", "Sheet5", "LastSheet" };
+ for (int i = 0; i < 6; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(aExpected[i], OString(pDocument->pClass->getPartName(pDocument, i)));
+ }
+}
+
+void DesktopLOKTest::testSheetSelections()
+{
+ LibLODocument_Impl* pDocument = loadDoc("sheets.ods", LOK_DOCTYPE_SPREADSHEET);
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ /*
+ * Check if selection data is correct
+ */
+ // Values in twips
+ int row5 = 1150;
+ int col1 = 1100;
+ int const col2 = 2200;
+ int const col3 = 3300;
+ int col4 = 4400;
+ int col5 = 5500;
+
+ // Select row 5 from column 1 through column 5
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ col1, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEMOVE,
+ col2, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEMOVE,
+ col3, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEMOVE,
+ col4, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEMOVE,
+ col5, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ col5, row5,
+ 1, 1, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Copy the contents and check if matches expected data
+ {
+ char* pUsedMimeType = nullptr;
+ char* pCopiedContent = pDocument->pClass->getTextSelection(pDocument, nullptr, &pUsedMimeType);
+ std::vector<long> aExpected = {5, 6, 7, 8, 9};
+ std::istringstream iss(pCopiedContent);
+ for (size_t i = 0; i < aExpected.size(); i++)
+ {
+ std::string token;
+ iss >> token;
+ CPPUNIT_ASSERT_EQUAL(aExpected[i], strtol(token.c_str(), nullptr, 10));
+ }
+
+ free(pUsedMimeType);
+ free(pCopiedContent);
+ }
+
+ /*
+ * Check if clicking inside the selection deselects the whole selection
+ */
+ int const row10 = 2400;
+ // Select starting from row5, col1 to row10, col5
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ col1, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEMOVE,
+ col5, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ col5, row10,
+ 1, 1, 0);
+
+ // Click at row5, col4
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ col4, row5,
+ 1, 1, 0);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ col4, row5,
+ 1, 1, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Selected text should get deselected and copying should give us
+ // content of only one cell, now
+ {
+ char* pUsedMimeType = nullptr;
+ char* pCopiedContent = pDocument->pClass->getTextSelection(pDocument, nullptr, &pUsedMimeType);
+ std::vector<long> aExpected = { 8 };
+ std::istringstream iss(pCopiedContent);
+ for (size_t i = 0; i < aExpected.size(); i++)
+ {
+ std::string token;
+ iss >> token;
+ CPPUNIT_ASSERT_EQUAL(aExpected[i], strtol(token.c_str(), nullptr, 10));
+ }
+
+ free(pUsedMimeType);
+ free(pCopiedContent);
+ }
+}
+
+namespace {
+
+ void verifyContextMenuStructure(boost::property_tree::ptree& aRoot)
+ {
+ for (const auto& aItemPair: aRoot)
+ {
+ // This is an array, so no key
+ CPPUNIT_ASSERT_EQUAL(aItemPair.first, std::string(""));
+
+ boost::property_tree::ptree aItemValue = aItemPair.second;
+ boost::optional<boost::property_tree::ptree&> aText = aItemValue.get_child_optional("text");
+ boost::optional<boost::property_tree::ptree&> aType = aItemValue.get_child_optional("type");
+ boost::optional<boost::property_tree::ptree&> aCommand = aItemValue.get_child_optional("command");
+ boost::optional<boost::property_tree::ptree&> aSubmenu = aItemValue.get_child_optional("menu");
+ boost::optional<boost::property_tree::ptree&> aEnabled = aItemValue.get_child_optional("enabled");
+ boost::optional<boost::property_tree::ptree&> aChecktype = aItemValue.get_child_optional("checktype");
+ boost::optional<boost::property_tree::ptree&> aChecked = aItemValue.get_child_optional("checked");
+
+ // type is omnipresent
+ CPPUNIT_ASSERT( aType );
+
+ // separator doesn't have any other attribs
+ if ( aType.get().data() == "separator" )
+ {
+ CPPUNIT_ASSERT( !aText && !aCommand && !aSubmenu && !aEnabled && !aChecktype && !aChecked );
+ }
+ else if ( aType.get().data() == "command" )
+ {
+ CPPUNIT_ASSERT( aCommand && aText );
+ }
+ else if ( aType.get().data() == "menu")
+ {
+ CPPUNIT_ASSERT( aSubmenu && aText );
+ verifyContextMenuStructure( aSubmenu.get() );
+ }
+
+ if ( aChecktype )
+ {
+ CPPUNIT_ASSERT( aChecktype.get().data() == "radio" ||
+ aChecktype.get().data() == "checkmark" ||
+ aChecktype.get().data() == "auto" );
+
+ CPPUNIT_ASSERT( aChecked &&
+ ( aChecked.get().data() == "true" || aChecked.get().data() == "false" ) );
+ }
+ }
+
+ }
+
+ boost::optional<boost::property_tree::ptree>
+ getContextMenuItem(boost::property_tree::ptree& aMenu, std::string const & unoSelector)
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem;
+ for (const auto& aItemPair: aMenu)
+ {
+ boost::property_tree::ptree aItemValue = aItemPair.second;
+
+ boost::optional<boost::property_tree::ptree&> aCommand = aItemValue.get_child_optional("command");
+ if (aCommand && aCommand.get().data() == unoSelector )
+ {
+ aMenuItem = aItemValue;
+ break;
+ }
+ }
+
+ return aMenuItem;
+ }
+
+} // end anonymous namespace
+
+void DesktopLOKTest::testContextMenuCalc()
+{
+ LibLODocument_Impl* pDocument = loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET);
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ // Values in twips
+ Point aPointOnImage(1150, 1100);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ aPointOnImage.X(), aPointOnImage.Y(),
+ 1, 4, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ TimeValue const aTimeValue = {2 , 0}; // 2 seconds max
+ m_aContextMenuCondition.wait(aTimeValue);
+
+ CPPUNIT_ASSERT( !m_aContextMenuResult.empty() );
+ boost::optional<boost::property_tree::ptree&> aMenu = m_aContextMenuResult.get_child_optional("menu");
+ CPPUNIT_ASSERT( aMenu );
+ verifyContextMenuStructure( aMenu.get() );
+
+ // tests for calc specific context menu
+ // Cut is enabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Cut");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("true"));
+ }
+
+ // Copy is enabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Copy");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("true"));
+ }
+
+ // Paste is enabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Paste");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("true"));
+ }
+
+ // Remove hyperlink is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:DeleteShapeHyperlink");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // open hyperlink is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:OpenHyperlinkOnCursor");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // checkbutton tests
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:AnchorMenu");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aSubmenu = aMenuItem.get().get_child_optional("menu");
+ CPPUNIT_ASSERT(aSubmenu);
+
+ boost::optional<boost::property_tree::ptree> aMenuItemToPage = getContextMenuItem(aSubmenu.get(), ".uno:SetAnchorToPage");
+ CPPUNIT_ASSERT(aMenuItemToPage);
+
+ boost::optional<boost::property_tree::ptree> aMenuItemToCell = getContextMenuItem(aSubmenu.get(), ".uno:SetAnchorToCell");
+ CPPUNIT_ASSERT(aMenuItemToCell);
+
+ // these are radio buttons
+ boost::optional<boost::property_tree::ptree&> aChecktypeToPage = aMenuItemToPage.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktypeToPage);
+ CPPUNIT_ASSERT_EQUAL(aChecktypeToPage.get().data(), std::string("radio"));
+
+ boost::optional<boost::property_tree::ptree&> aChecktypeToCell = aMenuItemToCell.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktypeToCell);
+ CPPUNIT_ASSERT_EQUAL(aChecktypeToCell.get().data(), std::string("radio"));
+
+ // ToPage is checked
+ boost::optional<boost::property_tree::ptree&> aCheckedToPage = aMenuItemToPage.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aCheckedToPage);
+ CPPUNIT_ASSERT_EQUAL(aCheckedToPage.get().data(), std::string("true"));
+
+ // ToCell is unchecked
+ boost::optional<boost::property_tree::ptree&> aCheckedToCell = aMenuItemToCell.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aCheckedToCell);
+ CPPUNIT_ASSERT_EQUAL(aCheckedToCell.get().data(), std::string("false"));
+ }
+}
+
+void DesktopLOKTest::testContextMenuWriter()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ Point aRandomPoint(1150, 1100);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ aRandomPoint.X(), aRandomPoint.Y(),
+ 1, 4, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ TimeValue const aTimeValue = {2 , 0}; // 2 seconds max
+ m_aContextMenuCondition.wait(aTimeValue);
+
+ CPPUNIT_ASSERT( !m_aContextMenuResult.empty() );
+ boost::optional<boost::property_tree::ptree&> aMenu = m_aContextMenuResult.get_child_optional("menu");
+ CPPUNIT_ASSERT( aMenu );
+ verifyContextMenuStructure( aMenu.get() );
+
+ // tests for writer specific context menu
+ // Cut is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Cut");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // Copy is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Copy");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // Paste is enabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Paste");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("true"));
+ }
+}
+
+void DesktopLOKTest::testContextMenuImpress()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_presentation.odp", LOK_DOCTYPE_PRESENTATION);
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ // random point where we don't hit an underlying comment or text box
+ Point aRandomPoint(10, 1150);
+ pDocument->pClass->postMouseEvent(pDocument,
+ LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ aRandomPoint.X(), aRandomPoint.Y(),
+ 1, 4, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ TimeValue const aTimeValue = {2 , 0}; // 2 seconds max
+ m_aContextMenuCondition.wait(aTimeValue);
+
+ CPPUNIT_ASSERT( !m_aContextMenuResult.empty() );
+ boost::optional<boost::property_tree::ptree&> aMenu = m_aContextMenuResult.get_child_optional("menu");
+ CPPUNIT_ASSERT( aMenu );
+ verifyContextMenuStructure( aMenu.get() );
+
+ // tests for impress specific context menu
+ // Cut is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Cut");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // Copy is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Copy");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // Paste is enabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:Paste");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("true"));
+ }
+
+ // SaveBackground is disabled
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:SaveBackground");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aEnabled = aMenuItem.get().get_child_optional("enabled");
+ CPPUNIT_ASSERT(aEnabled);
+ CPPUNIT_ASSERT_EQUAL(aEnabled.get().data(), std::string("false"));
+ }
+
+ // checkbutton tests
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:ShowRuler");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aChecktype = aMenuItem.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktype);
+ CPPUNIT_ASSERT_EQUAL(aChecktype.get().data(), std::string("checkmark"));
+
+ boost::optional<boost::property_tree::ptree&> aChecked = aMenuItem.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aChecked);
+ CPPUNIT_ASSERT_EQUAL(aChecked.get().data(), std::string("false"));
+ }
+
+ // Checkbutton tests inside SnapLines submenu
+ {
+ boost::optional<boost::property_tree::ptree> aMenuItem = getContextMenuItem(aMenu.get(), ".uno:SnapLinesMenu");
+ CPPUNIT_ASSERT(aMenuItem);
+
+ boost::optional<boost::property_tree::ptree&> aSubmenu = aMenuItem.get().get_child_optional("menu");
+ CPPUNIT_ASSERT(aSubmenu);
+
+ boost::optional<boost::property_tree::ptree> aMenuItemHelpVis = getContextMenuItem(aSubmenu.get(), ".uno:HelplinesVisible");
+ CPPUNIT_ASSERT(aMenuItemHelpVis);
+
+ boost::optional<boost::property_tree::ptree> aMenuItemHelpUse = getContextMenuItem(aSubmenu.get(), ".uno:HelplinesUse");
+ CPPUNIT_ASSERT(aMenuItemHelpUse);
+
+ boost::optional<boost::property_tree::ptree> aMenuItemHelpFront = getContextMenuItem(aSubmenu.get(), ".uno:HelplinesFront");
+ CPPUNIT_ASSERT(aMenuItemHelpFront);
+
+ // these are checkmarks
+ boost::optional<boost::property_tree::ptree&> aChecktypeHelpVis = aMenuItemHelpVis.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktypeHelpVis);
+ CPPUNIT_ASSERT_EQUAL(aChecktypeHelpVis.get().data(), std::string("checkmark"));
+
+ boost::optional<boost::property_tree::ptree&> aChecktypeHelpUse = aMenuItemHelpUse.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktypeHelpUse);
+ CPPUNIT_ASSERT_EQUAL(aChecktypeHelpUse.get().data(), std::string("checkmark"));
+
+ boost::optional<boost::property_tree::ptree&> aChecktypeHelpFront = aMenuItemHelpFront.get().get_child_optional("checktype");
+ CPPUNIT_ASSERT(aChecktypeHelpFront);
+ CPPUNIT_ASSERT_EQUAL(aChecktypeHelpFront.get().data(), std::string("checkmark"));
+
+ // HelplineVisible is unchecked
+ boost::optional<boost::property_tree::ptree&> aCheckedHelpVis = aMenuItemHelpVis.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aCheckedHelpVis);
+ CPPUNIT_ASSERT_EQUAL(aCheckedHelpVis.get().data(), std::string("false"));
+
+ // HelplineUse is checked
+ boost::optional<boost::property_tree::ptree&> aCheckedHelpUse = aMenuItemHelpUse.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aCheckedHelpUse);
+ CPPUNIT_ASSERT_EQUAL(aCheckedHelpUse.get().data(), std::string("true"));
+
+ // HelplineFront is checked
+ boost::optional<boost::property_tree::ptree&> aCheckedHelpFront = aMenuItemHelpFront.get().get_child_optional("checked");
+ CPPUNIT_ASSERT(aCheckedHelpFront);
+ CPPUNIT_ASSERT_EQUAL(aCheckedHelpFront.get().data(), std::string("true"));
+ }
+}
+
+static void callbackCompressionTest(const int type, const char* payload, void* data)
+{
+ std::vector<std::tuple<int, std::string>>* notifs = static_cast<std::vector<std::tuple<int, std::string>>*>(data);
+ notifs->emplace_back(type, std::string(payload ? payload : "(nil)"));
+}
+
+void DesktopLOKTest::testNotificationCompression()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, ""); // 0
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Superseded.
+ handler->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, ""); // Should be dropped.
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // 1
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION, ""); // Superseded.
+ handler->queue(LOK_CALLBACK_STATE_CHANGED, ""); // 2
+ handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:Bold"); // 3
+ handler->queue(LOK_CALLBACK_STATE_CHANGED, ""); // 4
+ handler->queue(LOK_CALLBACK_MOUSE_POINTER, "text"); // 5
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_MOUSE_POINTER, "text"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // Superseded.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // Superseded.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Superseded.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION, ""); // 7
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // 8
+ handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // 9
+ handler->queue(LOK_CALLBACK_CELL_CURSOR, "15, 25, 15, 10"); // 10
+ handler->queue(LOK_CALLBACK_CURSOR_VISIBLE, ""); // 11
+ handler->queue(LOK_CALLBACK_CELL_CURSOR, "15, 25, 15, 10"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_CELL_FORMULA, "blah"); // 12
+ handler->queue(LOK_CALLBACK_SET_PART, "1"); // 13
+ handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:AssignLayout=20"); // Superseded
+ handler->queue(LOK_CALLBACK_CURSOR_VISIBLE, ""); // Should be dropped.
+ handler->queue(LOK_CALLBACK_CELL_FORMULA, "blah"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_SET_PART, "1"); // Should be dropped.
+ handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:AssignLayout=1"); // 14
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(".uno:Bold"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_MOUSE_POINTER), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_START), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_TEXT_SELECTION_END), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_CURSOR), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CURSOR_VISIBLE), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_CELL_FORMULA), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("blah"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_SET_PART), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_STATE_CHANGED), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get<1>(notifs[i++]));
+}
+
+void DesktopLOKTest::testTileInvalidationCompression()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+
+ comphelper::LibreOfficeKit::setPartInInvalidation(true);
+ comphelper::ScopeGuard aGuard([]()
+ {
+ comphelper::LibreOfficeKit::setPartInInvalidation(false);
+ });
+
+ // Single part merging
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-100, -50, 500, 650, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "100, 100, 200, 200, 0");
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0"), std::get<1>(notifs[i++]));
+ }
+
+ // Part Number
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 1"); // Different part
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, 2"); // Invalid
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 200, 200, 0"); // Inside first
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 1"); // Invalid
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0"), std::get<1>(notifs[i++]));
+ }
+
+ // All Parts
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0"); // 0
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 1"); // 1: Different part
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, -1"); // Invalid
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 200, 200, -1"); // 0: All parts
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, -1"); // Invalid
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-100, -100, 1200, 1200, -1"); // 0: All parts
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 3"); // Overlapped
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "1000, 1000, 1239, 1239, 2"); // 1: Unique region
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2"), std::get<1>(notifs[i++]));
+ }
+
+ // All Parts (partial)
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 0"); // 0
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 100, 100, 1"); // 1: Different part
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, -1"); // Invalid
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "150, 150, 50, 50, -1"); // 2: All-parts
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, -1"); // Invalid
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "150, 150, 40, 40, 3"); // Overlapped w/ 2
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 4"); // 3: Unique
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "1000, 1000, 1239, 1239, 1"); // 4: Unique
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4"), std::get<1>(notifs[i++]));
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 1"), std::get<1>(notifs[i++]));
+ }
+
+ // Merge with "EMPTY"
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "EMPTY, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 240, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 300, 300, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+ size_t i = 0;
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[i]));
+ CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get<1>(notifs[i++]));
+ }
+}
+
+void DesktopLOKTest::testPartInInvalidation()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ // No part in invalidation: merge.
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "10, 10, 20, 10");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "20, 10, 20, 10");
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+ CPPUNIT_ASSERT_EQUAL(int(LOK_CALLBACK_INVALIDATE_TILES), std::get<0>(notifs[0]));
+ CPPUNIT_ASSERT_EQUAL(std::string("10, 10, 30, 10"), std::get<1>(notifs[0]));
+ }
+ // No part in invalidation: don't merge.
+ {
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "10, 10, 20, 10");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "40, 10, 20, 10");
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+ }
+
+ // Part in invalidation, intersection and parts match -> merge.
+ {
+ comphelper::LibreOfficeKit::setPartInInvalidation(true);
+ comphelper::ScopeGuard aGuard([]()
+ {
+ comphelper::LibreOfficeKit::setPartInInvalidation(false);
+ });
+
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "10, 10, 20, 10, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "20, 10, 20, 10, 0");
+
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+ }
+ // Part in invalidation, intersection and parts don't match -> don't merge.
+ {
+ comphelper::LibreOfficeKit::setPartInInvalidation(true);
+ comphelper::ScopeGuard aGuard([]()
+ {
+ comphelper::LibreOfficeKit::setPartInInvalidation(false);
+ });
+
+ std::vector<std::tuple<int, std::string>> notifs;
+ std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "10, 10, 20, 10, 0");
+ handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "20, 10, 20, 10, 1");
+
+ Scheduler::ProcessEventsToIdle();
+
+ // This failed as RectangleAndPart::Create() always assumed no part in
+ // payload, so this was merged -> it was 1.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+ }
+}
+
+void DesktopLOKTest::testDialogInput()
+{
+ comphelper::LibreOfficeKit::setActive();
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:HyperlinkDialog", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ pViewShell->GetViewFrame()->GetBindings().Update();
+
+ VclPtr<vcl::Window> pWindow(Application::GetActiveTopWindow());
+ CPPUNIT_ASSERT(pWindow);
+
+ Control* pCtrlFocused = GetFocusControl(pWindow.get());
+ CPPUNIT_ASSERT(pCtrlFocused);
+ CPPUNIT_ASSERT_EQUAL(WindowType::COMBOBOX, pCtrlFocused->GetType());
+ CPPUNIT_ASSERT_EQUAL(OUString(""), pCtrlFocused->GetText());
+
+ vcl::LOKWindowId nDialogId = pWindow->GetLOKWindowId();
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, nDialogId, LOK_EXT_TEXTINPUT, "wiki.");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, nDialogId, LOK_EXT_TEXTINPUT_END, "wiki.");
+ pDocument->pClass->removeTextContext(pDocument, nDialogId, 1, 0);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(OUString("wiki"), pCtrlFocused->GetText());
+
+ static_cast<SystemWindow*>(pWindow.get())->Close();
+ Scheduler::ProcessEventsToIdle();
+}
+
+void DesktopLOKTest::testInput()
+{
+ // Load a Writer document, enable change recording and press a key.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+
+ Scheduler::ProcessEventsToIdle(); // Get focus & other bits setup.
+
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, "far");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, "far");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, " ");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, " ");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, "beyond");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, "beyond");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, " ");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, " ");
+ // Mis-spelled ...
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, "kovely");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, "kovely");
+ // Remove it again
+ pDocument->pClass->removeTextContext(pDocument, 0, 6, 0);
+ // Replace it with lovely
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, "lovely");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, "lovely");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT, " ");
+ pDocument->pClass->postWindowExtTextInputEvent(pDocument, 0, LOK_EXT_TEXTINPUT_END, " ");
+
+ // get the text ...
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT(pText != nullptr);
+ OString aLovely("far beyond lovely ");
+ CPPUNIT_ASSERT_EQUAL(aLovely, OString(pText));
+ free(pText);
+}
+
+void DesktopLOKTest::testRedlineWriter()
+{
+ // Load a Writer document, enable change recording and press a key.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 't', 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Get redline info.
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:AcceptTrackedChanges");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // Make sure that pressing a key creates exactly one redline.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("redlines").size());
+
+ for (const boost::property_tree::ptree::value_type& rRedline : aTree.get_child("redlines"))
+ // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
+ CPPUNIT_ASSERT_EQUAL(std::string("Insert \xE2\x80\x9Ct\xE2\x80\x9D"), rRedline.second.get<std::string>("description"));
+ // U+201C LEFT DOUBLE QUOTATION MARK, U+201D RIGHT DOUBLE QUOTATION
+ // MARK
+}
+
+void DesktopLOKTest::testRedlineCalc()
+{
+ // Load a Writer document, enable change recording and press a key.
+ LibLODocument_Impl* pDocument = loadDoc("sheets.ods");
+ uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RecordChanges", uno::makeAny(true));
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 't', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 0, KEY_RETURN);
+ Scheduler::ProcessEventsToIdle();
+
+ // Get redline info.
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:AcceptTrackedChanges");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // Make sure that pressing a key creates exactly one redline.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("redlines").size());
+
+ for (const boost::property_tree::ptree::value_type& rRedline : aTree.get_child("redlines"))
+ // This failed with boost::property_tree::ptree_bad_path, as there were no description field.
+ CPPUNIT_ASSERT_EQUAL(std::string("Cell B4 changed from '5' to 't'"), rRedline.second.get<std::string>("description"));
+}
+
+namespace {
+
+class ViewCallback
+{
+ LibLODocument_Impl* mpDocument;
+ int mnView;
+public:
+ OString m_aCellFormula;
+ bool m_bTilesInvalidated;
+ tools::Rectangle m_aOwnCursor;
+ boost::property_tree::ptree m_aCommentCallbackResult;
+ boost::property_tree::ptree m_aCallbackWindowResult;
+ bool m_bWindowHidden;
+
+ ViewCallback(LibLODocument_Impl* pDocument)
+ : mpDocument(pDocument),
+ m_bTilesInvalidated(false)
+ {
+ mnView = SfxLokHelper::getView();
+ mpDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, this);
+ }
+
+ ~ViewCallback()
+ {
+ mpDocument->m_pDocumentClass->setView(mpDocument, mnView);
+ mpDocument->m_pDocumentClass->registerCallback(mpDocument, nullptr, nullptr);
+ }
+
+ static void callback(int nType, const char* pPayload, void* pData)
+ {
+ static_cast<ViewCallback*>(pData)->callbackImpl(nType, pPayload);
+ }
+
+ void callbackImpl(int nType, const char* pPayload)
+ {
+ OString aPayload(pPayload);
+ switch (nType)
+ {
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ {
+ m_bTilesInvalidated = true;
+ }
+ break;
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ {
+ uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload));
+ if (OString("EMPTY") == pPayload)
+ return;
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
+ m_aOwnCursor.setX(aSeq[0].toInt32());
+ m_aOwnCursor.setY(aSeq[1].toInt32());
+ m_aOwnCursor.setWidth(aSeq[2].toInt32());
+ m_aOwnCursor.setHeight(aSeq[3].toInt32());
+ }
+ break;
+ case LOK_CALLBACK_COMMENT:
+ {
+ m_aCommentCallbackResult.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aCommentCallbackResult);
+ m_aCommentCallbackResult = m_aCommentCallbackResult.get_child("comment");
+ }
+ break;
+ case LOK_CALLBACK_WINDOW:
+ {
+ m_aCallbackWindowResult.clear();
+ std::stringstream aStream(pPayload);
+ boost::property_tree::read_json(aStream, m_aCallbackWindowResult);
+
+ std::string sAction = m_aCallbackWindowResult.get<std::string>("action");
+ if (sAction == "hide")
+ m_bWindowHidden = true;
+ }
+ break;
+ case LOK_CALLBACK_CELL_FORMULA:
+ {
+ m_aCellFormula = aPayload;
+ }
+ break;
+ }
+ }
+};
+
+}
+
+void DesktopLOKTest::testPaintPartTile()
+{
+ // Load an impress doc of 2 slides.
+// ViewCallback aView1;
+// ViewCallback aView2;
+ LibLODocument_Impl* pDocument = loadDoc("2slides.odp");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+// pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView1);
+ int nView1 = pDocument->m_pDocumentClass->getView(pDocument);
+
+ // Create a second view.
+ pDocument->m_pDocumentClass->createView(pDocument);
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+// pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView2);
+
+ // Go to the second slide in the second view.
+ pDocument->m_pDocumentClass->setPart(pDocument, 1);
+
+ // Switch back to the first view and start typing.
+ pDocument->m_pDocumentClass->setView(pDocument, nView1);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, awt::Key::TAB);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'x', 0);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 'x', 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Call paintPartTile() to paint the second part (in whichever view it finds suitable for this).
+ unsigned char pPixels[256 * 256 * 4];
+ pDocument->m_pDocumentClass->paintPartTile(pDocument, pPixels, 1, 256, 256, 0, 0, 256, 256);
+
+ // Type again.
+ Scheduler::ProcessEventsToIdle();
+// aView1.m_bTilesInvalidated = false;
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'x', 0);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 'x', 0);
+ Scheduler::ProcessEventsToIdle();
+ // This failed: paintPartTile() (as a side-effect) ended the text edit of
+ // the first view, so there were no invalidations.
+ //CPPUNIT_ASSERT(aView1.m_bTilesInvalidated);
+}
+
+void DesktopLOKTest::testWriterCommentInsertCursor()
+{
+ // Load a document and type a character into the body text of the second view.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ ViewCallback aView1(pDocument);
+ pDocument->m_pDocumentClass->createView(pDocument);
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ ViewCallback aView2(pDocument);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'x', 0);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 'x', 0);
+ Scheduler::ProcessEventsToIdle();
+ tools::Rectangle aBodyCursor = aView2.m_aOwnCursor;
+
+ // Now insert a comment and make sure that the comment's cursor is shown,
+ // not the body text's one.
+ aView1.m_aOwnCursor.SetEmpty();
+ const int nCtrlAltC = KEY_MOD1 + KEY_MOD2 + 512 + 'c' - 'a';
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'c', nCtrlAltC);
+ pDocument->m_pDocumentClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 'c', nCtrlAltC);
+ Scheduler::ProcessEventsToIdle();
+ // Wait for SfxBindings to actually update the state, which updated the
+ // cursor as well.
+ osl::Thread::wait(std::chrono::seconds(1));
+ Scheduler::ProcessEventsToIdle();
+ // This failed: the body cursor was shown right after inserting a comment.
+ CPPUNIT_ASSERT(aView2.m_aOwnCursor.getX() > aBodyCursor.getX());
+ // This failed, the first view's cursor also jumped when the second view
+ // inserted the comment.
+ CPPUNIT_ASSERT(aView1.m_aOwnCursor.IsEmpty());
+
+ Scheduler::ProcessEventsToIdle();
+}
+
+#if HAVE_MORE_FONTS
+void DesktopLOKTest::testGetFontSubset()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ OUString aFontName = rtl::Uri::encode(
+ OUString("Liberation Sans"),
+ rtl_UriCharClassRelSegment,
+ rtl_UriEncodeKeepEscapes,
+ RTL_TEXTENCODING_UTF8
+ );
+ OString aCommand = OUStringToOString(".uno:FontSubset&name=" + aFontName, RTL_TEXTENCODING_UTF8);
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, aCommand.getStr());
+ std::stringstream aStream(pJSON);
+ boost::property_tree::read_json(aStream, aTree);
+ CPPUNIT_ASSERT( !aTree.empty() );
+ CPPUNIT_ASSERT_EQUAL( std::string(".uno:FontSubset"), aTree.get_child("commandName").get_value<std::string>() );
+ boost::property_tree::ptree aValues = aTree.get_child("commandValues");
+ CPPUNIT_ASSERT( !aValues.empty() );
+ free(pJSON);
+}
+#endif
+
+void DesktopLOKTest::testCommentsWriter()
+{
+ // Disable tiled rendering for comments
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+
+ LibLODocument_Impl* pDocument = loadDoc("comments.odt");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, nullptr);
+ long nWidth, nHeight;
+ pDocument->m_pDocumentClass->getDocumentSize(pDocument, &nWidth, &nHeight);
+
+ // Document width alongwith without sidebar comes to be < 13000
+ CPPUNIT_ASSERT( nWidth < 13000 );
+
+ // Can we get all the comments using .uno:ViewAnnotations command ?
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:ViewAnnotations");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // There are 3 comments in the document already
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aTree.get_child("comments").size());
+
+ int nComment2Id = 0;
+ // Check if all comment fields have valid data
+ for (const auto& rComment : aTree.get_child("comments"))
+ {
+ CPPUNIT_ASSERT(rComment.second.get<int>("id") > 0);
+ CPPUNIT_ASSERT(!rComment.second.get<std::string>("author").empty());
+ CPPUNIT_ASSERT(!rComment.second.get<std::string>("text").empty());
+ // Has a valid iso 8601 date time string
+ css::util::DateTime aDateTime;
+ OUString aDateTimeString = OUString::createFromAscii(rComment.second.get<std::string>("dateTime").c_str());
+ CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString, aDateTime));
+
+ // This comment has a marked text range
+ if (rComment.second.get<std::string>("text") == "Comment 2")
+ {
+ CPPUNIT_ASSERT(!rComment.second.get<std::string>("textRange").empty());
+ nComment2Id = rComment.second.get<int>("id");
+ }
+ // This is a reply comment
+ else if (rComment.second.get<std::string>("text") == "Reply to Comment 2")
+ {
+ CPPUNIT_ASSERT_EQUAL(nComment2Id, rComment.second.get<int>("parent"));
+ }
+ }
+
+ comphelper::LibreOfficeKit::setTiledAnnotations(true);
+}
+
+
+void DesktopLOKTest::testCommentsCalc()
+{
+ // Disable tiled rendering for comments
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+
+ LibLODocument_Impl* pDocument = loadDoc("sheets.ods");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, nullptr);
+
+ // Can we get all the comments using .uno:ViewAnnotations command ?
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:ViewAnnotations");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // There are 2 comments in the document already
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree.get_child("comments").size());
+
+ // Check if all comment fields have valid data
+ int nIdx = 0;
+ for (const auto& rComment : aTree.get_child("comments"))
+ {
+ switch(nIdx)
+ {
+ case 0:
+ {
+ CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment.second.get<std::string>("tab"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Comment1"), rComment.second.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("7650, 3570, 1274, 254"), rComment.second.get<std::string>("cellPos"));
+ }
+ break;
+ case 1:
+ {
+ CPPUNIT_ASSERT_EQUAL(std::string("4"), rComment.second.get<std::string>("tab"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Comment2"), rComment.second.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("8925, 4335, 1274, 254"), rComment.second.get<std::string>("cellPos"));
+ }
+ break;
+ }
+
+ ++nIdx;
+ }
+
+ // We checked all the comments
+ CPPUNIT_ASSERT_EQUAL(2, nIdx);
+
+ comphelper::LibreOfficeKit::setTiledAnnotations(true);
+}
+
+
+void DesktopLOKTest::testCommentsImpress()
+{
+ // Disable tiled rendering for comments
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+
+ LibLODocument_Impl* pDocument = loadDoc("blank_presentation.odp");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, nullptr);
+
+ // Can we get all the comments using .uno:ViewAnnotations command ?
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:ViewAnnotations");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ // There are 2 comments in the document already
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree.get_child("comments").size());
+
+ // Check if all comment fields have valid data
+ int nIdx = 0;
+ for (const auto& rComment : aTree.get_child("comments"))
+ {
+ switch(nIdx)
+ {
+ case 0:
+ {
+ CPPUNIT_ASSERT(rComment.second.get<int>("id") > 0);
+ CPPUNIT_ASSERT_EQUAL(std::string("This is comment1"), rComment.second.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("LOK User1"), rComment.second.get<std::string>("author"));
+ css::util::DateTime aDateTime;
+ OUString aDateTimeString = OUString::createFromAscii(rComment.second.get<std::string>("dateTime").c_str());
+ CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString, aDateTime));
+ }
+ break;
+ case 1:
+ {
+ CPPUNIT_ASSERT(rComment.second.get<int>("id") > 0);
+ CPPUNIT_ASSERT_EQUAL(std::string("This is comment2"), rComment.second.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("LOK User2"), rComment.second.get<std::string>("author"));
+ css::util::DateTime aDateTime;
+ OUString aDateTimeString = OUString::createFromAscii(rComment.second.get<std::string>("dateTime").c_str());
+ CPPUNIT_ASSERT(utl::ISO8601parseDateTime(aDateTimeString, aDateTime));
+ }
+ break;
+ }
+
+ ++nIdx;
+ }
+
+ // We checked all the comments
+ CPPUNIT_ASSERT_EQUAL(2, nIdx);
+
+ comphelper::LibreOfficeKit::setTiledAnnotations(true);
+}
+
+void DesktopLOKTest::testCommentsCallbacksWriter()
+{
+ // Comments callback are emitted only if tiled annotations are off
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+ LibLODocument_Impl* pDocument = loadDoc("comments.odt");
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ ViewCallback aView1(pDocument);
+ pDocument->m_pDocumentClass->createView(pDocument);
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ ViewCallback aView2(pDocument);
+
+ // Add a new comment
+ OString aCommandArgs("{ \"Text\": { \"type\": \"string\", \"value\": \"Additional comment\" }, \"Author\": { \"type\": \"string\", \"value\": \"LOK User1\" } }");
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:InsertAnnotation", aCommandArgs.getStr(), false);
+ Scheduler::ProcessEventsToIdle();
+
+ // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
+ int nCommentId1 = aView1.m_aCommentCallbackResult.get<int>("id");
+
+ // Reply to a comment just added
+ aCommandArgs = "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment\" } }";
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:ReplyComment", aCommandArgs.getStr(), false);
+ Scheduler::ProcessEventsToIdle();
+
+ // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView1.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView2.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Reply comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
+ int nCommentId2 = aView1.m_aCommentCallbackResult.get<int>("id");
+
+ // Edit the previously added comment
+ aCommandArgs = "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Edited comment\" } }";
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:EditAnnotation", aCommandArgs.getStr(), false);
+ Scheduler::ProcessEventsToIdle();
+
+ // We received a LOK_CALLBACK_COMMENT callback with comment 'Modify' action
+ CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Modify"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
+ // parent is unchanged still
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView1.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView2.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Edited comment"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
+
+ // Delete the reply comment just added
+ aCommandArgs = "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId2) + "\" } }";
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:DeleteComment", aCommandArgs.getStr(), false);
+ Scheduler::ProcessEventsToIdle();
+
+ // We received a LOK_CALLBACK_COMMENT callback with comment 'Remove' action
+ CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Remove"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId2, aView1.m_aCommentCallbackResult.get<int>("id"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId2, aView2.m_aCommentCallbackResult.get<int>("id"));
+
+ // Reply to nCommentId1 again
+ aCommandArgs = "{ \"Id\": { \"type\": \"string\", \"value\": \"" + OString::number(nCommentId1) + "\" }, \"Text\": { \"type\": \"string\", \"value\": \"Reply comment again\" } }";
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:ReplyComment", aCommandArgs.getStr(), false);
+ Scheduler::ProcessEventsToIdle();
+
+ // We received a LOK_CALLBACK_COMMENT callback with comment 'Add' action and linked to its parent comment
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView1.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Add"), aView2.m_aCommentCallbackResult.get<std::string>("action"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView1.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(nCommentId1, aView2.m_aCommentCallbackResult.get<int>("parent"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView1.m_aCommentCallbackResult.get<std::string>("text"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Reply comment again"), aView2.m_aCommentCallbackResult.get<std::string>("text"));
+
+ // .uno:ViewAnnotations returns total of 5 comments
+ boost::property_tree::ptree aTree;
+ char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:ViewAnnotations");
+ std::stringstream aStream(pJSON);
+ free(pJSON);
+ CPPUNIT_ASSERT(!aStream.str().empty());
+ boost::property_tree::read_json(aStream, aTree);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aTree.get_child("comments").size());
+}
+
+void DesktopLOKTest::testRunMacro()
+{
+ LibLibreOffice_Impl aOffice;
+ bool bGoodMacro, bNonExistentMacro;
+
+ // Tools macros come pre-installed in system share/basic folder,
+ bGoodMacro = aOffice.m_pOfficeClass->runMacro(&aOffice, OString("macro:///Tools.Debug.ActivateReadOnlyFlag()").getStr());
+ CPPUNIT_ASSERT(bGoodMacro);
+
+ bNonExistentMacro = aOffice.m_pOfficeClass->runMacro(&aOffice, OString("macro:///I.Am.Not(There)").getStr());
+ CPPUNIT_ASSERT(!bNonExistentMacro);
+}
+
+void DesktopLOKTest::testExtractParameter()
+{
+ OUString aOptions("Language=de-DE");
+ OUString aValue = extractParameter(aOptions, "Language");
+ CPPUNIT_ASSERT_EQUAL(OUString("de-DE"), aValue);
+ CPPUNIT_ASSERT_EQUAL(OUString(), aOptions);
+
+ aOptions = "Language=en-US,Something";
+ aValue = extractParameter(aOptions, "Language");
+ CPPUNIT_ASSERT_EQUAL(OUString("en-US"), aValue);
+ CPPUNIT_ASSERT_EQUAL(OUString("Something"), aOptions);
+
+ aOptions = "SomethingElse,Language=cs-CZ";
+ aValue = extractParameter(aOptions, "Language");
+ CPPUNIT_ASSERT_EQUAL(OUString("cs-CZ"), aValue);
+ CPPUNIT_ASSERT_EQUAL(OUString("SomethingElse"), aOptions);
+
+ aOptions = "Something1,Language=hu-HU,Something2";
+ aValue = extractParameter(aOptions, "Language");
+ CPPUNIT_ASSERT_EQUAL(OUString("hu-HU"), aValue);
+ CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2"), aOptions);
+
+ aOptions = "Something1,Something2=blah,Something3";
+ aValue = extractParameter(aOptions, "Language");
+ CPPUNIT_ASSERT_EQUAL(OUString(), aValue);
+ CPPUNIT_ASSERT_EQUAL(OUString("Something1,Something2=blah,Something3"), aOptions);
+}
+
+void DesktopLOKTest::readFileIntoByteVector(OUString const & sFilename, std::vector<unsigned char> & rByteVector)
+{
+ rByteVector.clear();
+ OUString aURL;
+ createFileURL(sFilename, aURL);
+ SvFileStream aStream(aURL, StreamMode::READ);
+ rByteVector.resize(aStream.remainingSize());
+ aStream.ReadBytes(rByteVector.data(), aStream.remainingSize());
+}
+
+void DesktopLOKTest::testGetSignatureState_Signed()
+{
+ LibLODocument_Impl* pDocument = loadDoc("signed.odt");
+ Scheduler::ProcessEventsToIdle();
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ int nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(4), nState);
+
+ std::vector<unsigned char> aCertificate;
+ {
+ readFileIntoByteVector("rootCA.der", aCertificate);
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("intermediateRootCA.der", aCertificate);
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(1), nState);
+}
+
+void DesktopLOKTest::testGetSignatureState_NonSigned()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ Scheduler::ProcessEventsToIdle();
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ int nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(0), nState);
+}
+
+void DesktopLOKTest::testInsertCertificate_DER_ODT()
+{
+ // Load the document, save it into a temp file and load that file again
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "odt", nullptr));
+ closeDoc();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+ pDocument = new LibLODocument_Impl(mxComponent);
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(mxComponent.is());
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<unsigned char> aCertificate;
+ std::vector<unsigned char> aPrivateKey;
+
+ {
+ readFileIntoByteVector("rootCA.der", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("intermediateRootCA.der", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("certificate.der", aCertificate);
+ readFileIntoByteVector("certificatePrivateKey.der", aPrivateKey);
+
+ bool bResult = pDocument->m_pDocumentClass->insertCertificate(pDocument,
+ aCertificate.data(), int(aCertificate.size()),
+ aPrivateKey.data(), int(aPrivateKey.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ int nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(1), nState);
+}
+
+
+void DesktopLOKTest::testInsertCertificate_PEM_ODT()
+{
+ // Load the document, save it into a temp file and load that file again
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "odt", nullptr));
+ closeDoc();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+ pDocument = new LibLODocument_Impl(mxComponent);
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(mxComponent.is());
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<unsigned char> aCertificate;
+ std::vector<unsigned char> aPrivateKey;
+
+ {
+ readFileIntoByteVector("test-cert-chain-1.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-2.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-3.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-signing.pem", aCertificate);
+ readFileIntoByteVector("test-PK-signing.pem", aPrivateKey);
+
+ bool bResult = pDocument->m_pDocumentClass->insertCertificate(pDocument,
+ aCertificate.data(), int(aCertificate.size()),
+ aPrivateKey.data(), int(aPrivateKey.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ int nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(1), nState);
+}
+
+void DesktopLOKTest::testInsertCertificate_PEM_DOCX()
+{
+ // Load the document, save it into a temp file and load that file again
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.docx");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "docx", nullptr));
+ closeDoc();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+ pDocument = new LibLODocument_Impl(mxComponent);
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(mxComponent.is());
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<unsigned char> aCertificate;
+ std::vector<unsigned char> aPrivateKey;
+
+ {
+ readFileIntoByteVector("test-cert-chain-1.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-2.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-3.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-signing.pem", aCertificate);
+ readFileIntoByteVector("test-PK-signing.pem", aPrivateKey);
+
+ bool bResult = pDocument->m_pDocumentClass->insertCertificate(pDocument,
+ aCertificate.data(), int(aCertificate.size()),
+ aPrivateKey.data(), int(aPrivateKey.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ int nState = pDocument->m_pDocumentClass->getSignatureState(pDocument);
+ CPPUNIT_ASSERT_EQUAL(int(5), nState);
+}
+
+void DesktopLOKTest::testSignDocument_PEM_PDF()
+{
+ // Load the document, save it into a temp file and load that file again
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(mxComponent.is());
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ Scheduler::ProcessEventsToIdle();
+
+ std::vector<unsigned char> aCertificate;
+ std::vector<unsigned char> aPrivateKey;
+
+ {
+ readFileIntoByteVector("test-cert-chain-1.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-2.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ {
+ readFileIntoByteVector("test-cert-chain-3.pem", aCertificate);
+
+ bool bResult = pDocument->m_pDocumentClass->addCertificate(
+ pDocument, aCertificate.data(), int(aCertificate.size()));
+ CPPUNIT_ASSERT(bResult);
+ }
+
+ CPPUNIT_ASSERT(pDocument->pClass->saveAs(pDocument, aTempFile.GetURL().toUtf8().getStr(), "pdf", nullptr));
+
+ closeDoc();
+
+ Scheduler::ProcessEventsToIdle();
+
+ readFileIntoByteVector("test-cert-signing.pem", aCertificate);
+ readFileIntoByteVector("test-PK-signing.pem", aPrivateKey);
+
+ LibLibreOffice_Impl aOffice;
+ bool bResult = aOffice.m_pOfficeClass->signDocument(&aOffice, aTempFile.GetURL().toUtf8().getStr(),
+ aCertificate.data(), int(aCertificate.size()),
+ aPrivateKey.data(), int(aPrivateKey.size()));
+
+ CPPUNIT_ASSERT(bResult);
+}
+
+void DesktopLOKTest::testTextSelectionHandles()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ OString aText("hello");
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength()));
+
+ // select the inserted text
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT_EQUAL(aText, OString(pText));
+ free(pText);
+ CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart);
+ CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd);
+
+ // deselect & check
+ m_aTextSelectionStart = "";
+ m_aTextSelectionEnd = "";
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, com::sun::star::awt::Key::ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+ pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT_EQUAL(OString(), OString(pText));
+ free(pText);
+ CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionStart);
+ CPPUNIT_ASSERT_EQUAL(OString(), m_aTextSelectionEnd);
+
+ // select again; the positions of the selection handles have to be sent
+ // again
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+ pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT_EQUAL(aText, OString(pText));
+ free(pText);
+ CPPUNIT_ASSERT_EQUAL(OString("1418, 1418, 0, 275"), m_aTextSelectionStart);
+ CPPUNIT_ASSERT_EQUAL(OString("1898, 1418, 0, 275"), m_aTextSelectionEnd);
+}
+
+void DesktopLOKTest::testDialogPaste()
+{
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:HyperlinkDialog", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ pViewShell->GetViewFrame()->GetBindings().Update();
+
+ VclPtr<vcl::Window> pWindow(Application::GetActiveTopWindow());
+ CPPUNIT_ASSERT(pWindow);
+
+ pDocument->pClass->postWindow(pDocument, pWindow->GetLOKWindowId(), LOK_WINDOW_PASTE,
+ "{ \"MimeType\" : { \"type\" : \"string\", \"value\" : \"text/plain;charset=utf-8\" }, \"Data\" : { \"type\" : \"[]byte\", \"value\" : \"www.softwarelibre.org.bo\" } }");
+ Scheduler::ProcessEventsToIdle();
+
+ Control* pCtrlFocused = GetFocusControl(pWindow.get());
+ CPPUNIT_ASSERT(pCtrlFocused);
+ CPPUNIT_ASSERT_EQUAL(WindowType::COMBOBOX, pCtrlFocused->GetType());
+ CPPUNIT_ASSERT_EQUAL(OUString("www.softwarelibre.org.bo"), pCtrlFocused->GetText());
+
+ static_cast<SystemWindow*>(pWindow.get())->Close();
+ Scheduler::ProcessEventsToIdle();
+}
+
+void DesktopLOKTest::testShowHideDialog()
+{
+
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ ViewCallback aView(pDocument);
+
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:HyperlinkDialog", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ VclPtr<vcl::Window> pWindow(Application::GetActiveTopWindow());
+ CPPUNIT_ASSERT(pWindow);
+
+ aView.m_bWindowHidden = false;
+
+ pWindow->Hide();
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(true, aView.m_bWindowHidden);
+
+ static_cast<SystemWindow*>(pWindow.get())->Close();
+ Scheduler::ProcessEventsToIdle();
+}
+
+void DesktopLOKTest::testComplexSelection()
+{
+ // Start with a blank text file and add contents.
+ LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+ static const OString aText("hello world");
+
+ // Certainly not complex.
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE), pDocument->pClass->getSelectionType(pDocument));
+
+ // Paste text.
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength()));
+
+ // No selection.
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_NONE), pDocument->pClass->getSelectionType(pDocument));
+
+ // Paste an image.
+ OUString aFileURL;
+ createFileURL("paste.jpg", aFileURL);
+ std::ifstream aImageStream(aFileURL.toUtf8().copy(strlen("file://")).getStr());
+ std::vector<char> aImageContents((std::istreambuf_iterator<char>(aImageStream)), std::istreambuf_iterator<char>());
+ CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "image/jpeg", aImageContents.data(), aImageContents.size()));
+
+ // Now select-all.
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ // Export as plain text, we should get only the text part "hello".
+ char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr);
+ CPPUNIT_ASSERT(pText != nullptr);
+ CPPUNIT_ASSERT_EQUAL(aText, OString(pText));
+ free(pText);
+
+ // Export as rtf, we should also get the image.
+ pText = pDocument->pClass->getTextSelection(pDocument, "text/rtf", nullptr);
+ CPPUNIT_ASSERT(pText != nullptr);
+ CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text.
+ CPPUNIT_ASSERT(std::string(pText).find("pict{") != std::string::npos); // Must have the image as well.
+ free(pText);
+
+ // Export as html, we should also get the image.
+ pText = pDocument->pClass->getTextSelection(pDocument, "text/html", nullptr);
+ CPPUNIT_ASSERT(pText != nullptr);
+ CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text.
+ CPPUNIT_ASSERT(std::string(pText).find("<img") != std::string::npos); // Must have the image as well.
+ free(pText);
+
+ // We expect this to be complex.
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(LOK_SELTYPE_COMPLEX), pDocument->pClass->getSelectionType(pDocument));
+}
+
+void DesktopLOKTest::testCalcSaveAs()
+{
+ comphelper::LibreOfficeKit::setActive();
+
+ LibLODocument_Impl* pDocument = loadDoc("sheets.ods");
+ CPPUNIT_ASSERT(pDocument);
+
+ // Enter some text, but don't commit.
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'X', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 'X', 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // Save as a new file.
+ OUString aNewFileUrl = "file:///tmp/saveas.ods";
+ pDocument->pClass->saveAs(pDocument, aNewFileUrl.toUtf8().getStr(), nullptr, nullptr);
+ closeDoc();
+
+ // Load the new document and verify that the in-flight changes are saved.
+ pDocument = loadDocUrl(aNewFileUrl, LOK_DOCTYPE_SPREADSHEET);
+ CPPUNIT_ASSERT(pDocument);
+
+ ViewCallback aView(pDocument);
+ pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+ pDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, &aView);
+
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, KEY_LEFT);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 0, KEY_LEFT);
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(OString("X"), aView.m_aCellFormula);
+}
+
+void DesktopLOKTest::testSpellcheckerMultiView()
+{
+ static const OUString aLangISO("en-US");
+ SvtSysLocaleOptions aSysLocaleOptions;
+ aSysLocaleOptions.SetLocaleConfigString(aLangISO);
+ aSysLocaleOptions.SetUILocaleConfigString(aLangISO);
+ comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLangISO, true));
+
+ auto aSavedSettings = Application::GetSettings();
+ std::unique_ptr<Resetter> pResetter(
+ new Resetter([&]() { Application::SetSettings(aSavedSettings); }));
+ AllSettings aSettings(aSavedSettings);
+ aSettings.SetLanguageTag(aLangISO, true);
+ Application::SetSettings(aSettings);
+
+ LibLODocument_Impl* pDocument = loadDoc("sheet_with_image.ods", LOK_DOCTYPE_SPREADSHEET);
+ pDocument->pClass->setViewLanguage(pDocument, 0, "en-US"); // For spellchecking.
+ pDocument->pClass->initializeForRendering(pDocument, nullptr);
+ pDocument->pClass->registerCallback(pDocument, &DesktopLOKTest::callback, this);
+
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'a', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'a', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 'a', 0);
+ pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 0, com::sun::star::awt::Key::ESCAPE);
+
+ // Start spellchecking.
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:SpellDialog", nullptr, false);
+
+ // Uncommenting this will result in a deadlock.
+ // Because the language configuration above is not effective, and no
+ // language is actually set, the spell-dialog finds no misspelled
+ // words, and displays a message box, which must be dismissed to
+ // continue.
+ // Need to fix the language configuration issue to enable this.
+ // Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+
+ // Now create another view.
+ const int nViewId = pDocument->m_pDocumentClass->createView(pDocument);
+ CPPUNIT_ASSERT_EQUAL(2, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+
+ // And destroy it.
+ pDocument->m_pDocumentClass->destroyView(pDocument, nViewId);
+
+ // We should survive the destroyed view.
+ CPPUNIT_ASSERT_EQUAL(1, pDocument->m_pDocumentClass->getViewsCount(pDocument));
+}
+
+void DesktopLOKTest::testControlState()
+{
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:StarShapes", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ boost::property_tree::ptree aState;
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ pViewShell->GetViewFrame()->GetBindings().Update();
+ pViewShell->GetViewFrame()->GetBindings().QueryControlState(SID_ATTR_TRANSFORM_WIDTH, aState);
+ CPPUNIT_ASSERT(!aState.empty());
+}
+
+void DesktopLOKTest::testMetricField()
+{
+ LibLODocument_Impl* pDocument = loadDoc("search.ods");
+ pDocument->pClass->postUnoCommand(pDocument, ".uno:StarShapes", nullptr, false);
+ Scheduler::ProcessEventsToIdle();
+
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ CPPUNIT_ASSERT(pViewShell);
+
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+ CPPUNIT_ASSERT(pViewFrame);
+
+ SfxChildWindow* pSideBar = pViewFrame->GetChildWindow(SID_SIDEBAR);
+ CPPUNIT_ASSERT(pSideBar);
+
+ vcl::Window* pWin = pSideBar->GetWindow();
+ CPPUNIT_ASSERT(pWin);
+
+ WindowUIObject aWinUI(pWin);
+ std::unique_ptr<UIObject> pUIWin(aWinUI.get_child("selectwidth"));
+ CPPUNIT_ASSERT(pUIWin.get());
+
+ StringMap aMap;
+ aMap["VALUE"] = "75.06";
+ pUIWin->execute("VALUE", aMap);
+
+ StringMap aRet = pUIWin->get_state();
+ CPPUNIT_ASSERT_EQUAL(aMap["VALUE"], aRet["Value"]);
+}
+
+namespace {
+
+constexpr size_t classOffset(int i)
+{
+ return sizeof(static_cast<struct _LibreOfficeKitClass*>(nullptr)->nSize) + i * sizeof(void*);
+}
+
+constexpr size_t documentClassOffset(int i)
+{
+ return sizeof(static_cast<struct _LibreOfficeKitDocumentClass*>(nullptr)->nSize) + i * sizeof(void*);
+}
+
+}
+
+void DesktopLOKTest::testABI()
+{
+ // STABLE ABI, NEVER CHANGE (unless there's a very good reason, agreed by ESC, etc.)
+ CPPUNIT_ASSERT_EQUAL(classOffset(0), offsetof(struct _LibreOfficeKitClass, destroy));
+ CPPUNIT_ASSERT_EQUAL(classOffset(1), offsetof(struct _LibreOfficeKitClass, documentLoad));
+ CPPUNIT_ASSERT_EQUAL(classOffset(2), offsetof(struct _LibreOfficeKitClass, getError));
+ CPPUNIT_ASSERT_EQUAL(classOffset(3), offsetof(struct _LibreOfficeKitClass, documentLoadWithOptions));
+ CPPUNIT_ASSERT_EQUAL(classOffset(4), offsetof(struct _LibreOfficeKitClass, freeError));
+ CPPUNIT_ASSERT_EQUAL(classOffset(5), offsetof(struct _LibreOfficeKitClass, registerCallback));
+ CPPUNIT_ASSERT_EQUAL(classOffset(6), offsetof(struct _LibreOfficeKitClass, getFilterTypes));
+ CPPUNIT_ASSERT_EQUAL(classOffset(7), offsetof(struct _LibreOfficeKitClass, setOptionalFeatures));
+ CPPUNIT_ASSERT_EQUAL(classOffset(8), offsetof(struct _LibreOfficeKitClass, setDocumentPassword));
+ CPPUNIT_ASSERT_EQUAL(classOffset(9), offsetof(struct _LibreOfficeKitClass, getVersionInfo));
+ CPPUNIT_ASSERT_EQUAL(classOffset(10), offsetof(struct _LibreOfficeKitClass, runMacro));
+ CPPUNIT_ASSERT_EQUAL(classOffset(11), offsetof(struct _LibreOfficeKitClass, signDocument));
+
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass, destroy));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass, saveAs));
+
+ // Unstable ABI, but still think twice before changing this
+ // Eg. can't you add your new member at the end of the struct instead of
+ // in the middle? The thing you are changing - is it already part of some
+ // release?
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(2), offsetof(struct _LibreOfficeKitDocumentClass, getDocumentType));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(3), offsetof(struct _LibreOfficeKitDocumentClass, getParts));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(4), offsetof(struct _LibreOfficeKitDocumentClass, getPartPageRectangles));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(5), offsetof(struct _LibreOfficeKitDocumentClass, getPart));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(6), offsetof(struct _LibreOfficeKitDocumentClass, setPart));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(7), offsetof(struct _LibreOfficeKitDocumentClass, getPartName));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(8), offsetof(struct _LibreOfficeKitDocumentClass, setPartMode));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(9), offsetof(struct _LibreOfficeKitDocumentClass, paintTile));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(10), offsetof(struct _LibreOfficeKitDocumentClass, getTileMode));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(11), offsetof(struct _LibreOfficeKitDocumentClass, getDocumentSize));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(12), offsetof(struct _LibreOfficeKitDocumentClass, initializeForRendering));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(13), offsetof(struct _LibreOfficeKitDocumentClass, registerCallback));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(14), offsetof(struct _LibreOfficeKitDocumentClass, postKeyEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(15), offsetof(struct _LibreOfficeKitDocumentClass, postMouseEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(16), offsetof(struct _LibreOfficeKitDocumentClass, postUnoCommand));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(17), offsetof(struct _LibreOfficeKitDocumentClass, setTextSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(18), offsetof(struct _LibreOfficeKitDocumentClass, getTextSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(19), offsetof(struct _LibreOfficeKitDocumentClass, paste));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(20), offsetof(struct _LibreOfficeKitDocumentClass, setGraphicSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(21), offsetof(struct _LibreOfficeKitDocumentClass, resetSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(22), offsetof(struct _LibreOfficeKitDocumentClass, getCommandValues));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(23), offsetof(struct _LibreOfficeKitDocumentClass, setClientZoom));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(24), offsetof(struct _LibreOfficeKitDocumentClass, setClientVisibleArea));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(25), offsetof(struct _LibreOfficeKitDocumentClass, createView));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(26), offsetof(struct _LibreOfficeKitDocumentClass, destroyView));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(27), offsetof(struct _LibreOfficeKitDocumentClass, setView));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(28), offsetof(struct _LibreOfficeKitDocumentClass, getView));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(29), offsetof(struct _LibreOfficeKitDocumentClass, getViewsCount));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(30), offsetof(struct _LibreOfficeKitDocumentClass, renderFont));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(31), offsetof(struct _LibreOfficeKitDocumentClass, getPartHash));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(32), offsetof(struct _LibreOfficeKitDocumentClass, paintPartTile));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(33), offsetof(struct _LibreOfficeKitDocumentClass, getViewIds));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(34), offsetof(struct _LibreOfficeKitDocumentClass, setOutlineState));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(35), offsetof(struct _LibreOfficeKitDocumentClass, paintWindow));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(36), offsetof(struct _LibreOfficeKitDocumentClass, postWindow));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(37), offsetof(struct _LibreOfficeKitDocumentClass, postWindowKeyEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(38), offsetof(struct _LibreOfficeKitDocumentClass, postWindowMouseEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass, setViewLanguage));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass, postWindowExtTextInputEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass, getPartInfo));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass, paintWindowDPI));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass, insertCertificate));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass, addCertificate));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), offsetof(struct _LibreOfficeKitDocumentClass, getSignatureState));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(46), offsetof(struct _LibreOfficeKitDocumentClass, renderShapeSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(47), offsetof(struct _LibreOfficeKitDocumentClass, postWindowGestureEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass, createViewWithOptions));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass, selectPart));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass, moveSelectedParts));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass, resizeWindow));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass, getClipboard));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass, setClipboard));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), offsetof(struct _LibreOfficeKitDocumentClass, getSelectionType));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(55), offsetof(struct _LibreOfficeKitDocumentClass, removeTextContext));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(56), offsetof(struct _LibreOfficeKitDocumentClass, sendDialogEvent));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(57), offsetof(struct _LibreOfficeKitDocumentClass, renderFontOrientation));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(58), offsetof(struct _LibreOfficeKitDocumentClass, paintWindowForView));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(59), offsetof(struct _LibreOfficeKitDocumentClass, completeFunction));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(60), offsetof(struct _LibreOfficeKitDocumentClass, setWindowTextSelection));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(61), offsetof(struct _LibreOfficeKitDocumentClass, sendFormFieldEvent));
+
+ // Extending is fine, update this, and add new assert for the offsetof the
+ // new method
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(62), sizeof(struct _LibreOfficeKitDocumentClass));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/qa/unit/data/desktop-dialogs-test.txt b/desktop/qa/unit/data/desktop-dialogs-test.txt
new file mode 100644
index 000000000..f54db2a7b
--- /dev/null
+++ b/desktop/qa/unit/data/desktop-dialogs-test.txt
@@ -0,0 +1,44 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# This file contains all dialogs that the unit tests in the module
+# will work on if it is in script mode. It will read one-by-one,
+# try to open it and create a screenshot that will be saved in
+# workdir/screenshots using the pattern of the ui-file name.
+#
+# Syntax:
+# - empty lines are allowed
+# - lines starting with '#' are treated as comment
+# - all other lines should contain a *.ui filename in the same
+# notation as in the dialog constructors (see code)
+
+#
+# The 'known' dialogs which have a hard-coded representation
+# in registerKnownDialogsByID/createDialogByID
+#
+
+# No known dialogs in desktop for now
+
+#
+# Dialogs without a hard-coded representation. These will
+# be visualized using a fallback based on VclBuilder
+#
+
+# currently deactivated, leads to problems and the test to not work
+# This is typically a hint that these should be hard-coded in the
+# test case since they need some document and model data to work
+# desktop/ui/extensionmanager.ui
+
+desktop/ui/dependenciesdialog.ui
+desktop/ui/updaterequireddialog.ui
+desktop/ui/showlicensedialog.ui
+desktop/ui/updatedialog.ui
+desktop/ui/updateinstalldialog.ui
+desktop/ui/licensedialog.ui
+desktop/ui/installforalldialog.ui
diff --git a/desktop/qa/unit/desktop-dialogs-test.cxx b/desktop/qa/unit/desktop-dialogs-test.cxx
new file mode 100644
index 000000000..54cadc337
--- /dev/null
+++ b/desktop/qa/unit/desktop-dialogs-test.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+#include <test/screenshot_test.hxx>
+#include <vcl/abstdlg.hxx>
+
+using namespace ::com::sun::star;
+
+/// Test opening a dialog in desktop
+class DesktopDialogsTest : public ScreenshotTest
+{
+private:
+ /// helper method to populate KnownDialogs, called in setUp(). Needs to be
+ /// written and has to add entries to KnownDialogs
+ virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override;
+
+ /// dialog creation for known dialogs by ID. Has to be implemented for
+ /// each registered known dialog
+ virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override;
+
+public:
+ DesktopDialogsTest();
+
+ // try to open a dialog
+ void openAnyDialog();
+
+ CPPUNIT_TEST_SUITE(DesktopDialogsTest);
+ CPPUNIT_TEST(openAnyDialog);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+DesktopDialogsTest::DesktopDialogsTest() {}
+
+void DesktopDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/)
+{
+ // fill map of known dialogs
+}
+
+VclPtr<VclAbstractDialog> DesktopDialogsTest::createDialogByID(sal_uInt32 /*nID*/)
+{
+ return nullptr;
+}
+
+void DesktopDialogsTest::openAnyDialog()
+{
+ /// process input file containing the UXMLDescriptions of the dialogs to dump
+ processDialogBatchFile("desktop/qa/unit/data/desktop-dialogs-test.txt");
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DesktopDialogsTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/qa/unit/desktop-lok-init.cxx b/desktop/qa/unit/desktop-lok-init.cxx
new file mode 100644
index 000000000..7e6e266cf
--- /dev/null
+++ b/desktop/qa/unit/desktop-lok-init.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <tools/color.hxx>
+
+#include <lib/init.hxx>
+
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+
+using namespace css;
+
+/// Unit tests for desktop/source/lib/init.cxx internals.
+class LOKInitTest : public ::CppUnit::TestFixture
+{
+public:
+ LOKInitTest() {}
+
+ void testJsonToPropertyValues();
+ void testJsonToPropertyValuesBorder();
+
+ CPPUNIT_TEST_SUITE(LOKInitTest);
+ CPPUNIT_TEST(testJsonToPropertyValues);
+ CPPUNIT_TEST(testJsonToPropertyValuesBorder);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+namespace
+{
+void assertSequencesEqual(const uno::Sequence<beans::PropertyValue>& expected,
+ const uno::Sequence<beans::PropertyValue>& actual)
+{
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The sequences should have the same length", expected.getLength(),
+ actual.getLength());
+ for (int i = 0; i < expected.getLength(); ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(expected[i].Name, actual[i].Name);
+ CPPUNIT_ASSERT_EQUAL(comphelper::anyToString(expected[i].Value),
+ comphelper::anyToString(actual[i].Value));
+ }
+}
+} // namespace
+
+void LOKInitTest::testJsonToPropertyValues()
+{
+ const char arguments[] = "{"
+ "\"FileName\":{"
+ "\"type\":\"string\","
+ "\"value\":\"something.odt\""
+ "}}";
+
+ uno::Sequence<beans::PropertyValue> aArgs(1);
+ aArgs[0].Name = "FileName";
+ aArgs[0].Value <<= OUString("something.odt");
+
+ assertSequencesEqual(
+ aArgs, comphelper::containerToSequence(desktop::jsonToPropertyValuesVector(arguments)));
+}
+
+void LOKInitTest::testJsonToPropertyValuesBorder()
+{
+ const char arguments[]
+ = "{"
+ "\"OuterBorder\": {"
+ "\"type\" : \"[]any\","
+ "\"value\" : ["
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"long\", \"value\" : 0 },"
+ "{ \"type\" : \"long\", \"value\" : 0 },"
+ "{ \"type\" : \"long\", \"value\" : 0 },"
+ "{ \"type\" : \"long\", \"value\" : 0 },"
+ "{ \"type\" : \"long\", \"value\" : 0 }"
+ "]"
+ "},"
+ "\"InnerBorder\":{"
+ "\"type\" : \"[]any\","
+ "\"value\" : ["
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"com.sun.star.table.BorderLine2\", \"value\" : { \"Color\" : { \"type\" : "
+ "\"com.sun.star.util.Color\", \"value\" : 0 }, \"InnerLineWidth\" : { \"type\" : "
+ "\"short\", \"value\" : 0 }, \"OuterLineWidth\" : { \"type\" : \"short\", \"value\" : 1 "
+ "}, \"LineDistance\" : { \"type\" : \"short\", \"value\" : 0 }, \"LineStyle\" : { "
+ "\"type\" : \"short\", \"value\" : 0 }, \"LineWidth\" : { \"type\" : \"unsigned long\", "
+ "\"value\" : 1 } } },"
+ "{ \"type\" : \"short\", \"value\" : 0 },"
+ "{ \"type\" : \"short\", \"value\" : 127 },"
+ "{ \"type\" : \"long\", \"value\" : 0 }"
+ "]"
+ "}}";
+
+ // see SvxBoxItem::QueryValue for details
+ uno::Sequence<uno::Any> aOuterSeq(9);
+ table::BorderLine2 aLine(sal_Int32(COL_BLACK), 0, 1, 0, table::BorderLineStyle::SOLID, 1);
+ aOuterSeq[0] <<= aLine; // left
+ aOuterSeq[1] <<= aLine; // right
+ aOuterSeq[2] <<= aLine; // bottom
+ aOuterSeq[3] <<= aLine; // top
+ aOuterSeq[4] <<= static_cast<sal_Int32>(0);
+ aOuterSeq[5] <<= static_cast<sal_Int32>(0);
+ aOuterSeq[6] <<= static_cast<sal_Int32>(0);
+ aOuterSeq[7] <<= static_cast<sal_Int32>(0);
+ aOuterSeq[8] <<= static_cast<sal_Int32>(0);
+
+ // see SvxBoxInfoItem::QueryValue() for details
+ uno::Sequence<uno::Any> aInnerSeq(5);
+ aInnerSeq[0] <<= aLine; // horizontal
+ aInnerSeq[1] <<= aLine; // vertical
+ aInnerSeq[2] <<= static_cast<sal_Int16>(0);
+ aInnerSeq[3] <<= static_cast<sal_Int16>(0x7F);
+ aInnerSeq[4] <<= static_cast<sal_Int32>(0);
+
+ uno::Sequence<beans::PropertyValue> aArgs(2);
+ aArgs[0].Name = "OuterBorder";
+ aArgs[0].Value <<= aOuterSeq;
+ aArgs[1].Name = "InnerBorder";
+ aArgs[1].Value <<= aInnerSeq;
+
+ assertSequencesEqual(
+ aArgs, comphelper::containerToSequence(desktop::jsonToPropertyValuesVector(arguments)));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LOKInitTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/scripts/gdbtrace b/desktop/scripts/gdbtrace
new file mode 100644
index 000000000..f5fbf6325
--- /dev/null
+++ b/desktop/scripts/gdbtrace
@@ -0,0 +1,13 @@
+set pagination off
+echo log will be saved as gdbtrace.log, this will take some time, patience...\n
+handle SIGPIPE SIGXCPU SIG33 SIG35 SIGPWR nostop noprint
+set logging redirect on
+set logging file gdbtrace.log
+set logging on
+set logging overwrite on
+run
+bt
+thread apply all bt
+quit
+set logging off
+echo log is saved as gdbtrace.log\n
diff --git a/desktop/scripts/sbase.sh b/desktop/scripts/sbase.sh
new file mode 100755
index 000000000..82e5e4ba2
--- /dev/null
+++ b/desktop/scripts/sbase.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --base "$@"
diff --git a/desktop/scripts/scalc.sh b/desktop/scripts/scalc.sh
new file mode 100755
index 000000000..ff3d59795
--- /dev/null
+++ b/desktop/scripts/scalc.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --calc "$@"
diff --git a/desktop/scripts/sdraw.sh b/desktop/scripts/sdraw.sh
new file mode 100755
index 000000000..9f7c1e4ed
--- /dev/null
+++ b/desktop/scripts/sdraw.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --draw "$@"
diff --git a/desktop/scripts/simpress.sh b/desktop/scripts/simpress.sh
new file mode 100755
index 000000000..a1808c3cb
--- /dev/null
+++ b/desktop/scripts/simpress.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --impress "$@"
diff --git a/desktop/scripts/smath.sh b/desktop/scripts/smath.sh
new file mode 100755
index 000000000..9c05223b4
--- /dev/null
+++ b/desktop/scripts/smath.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --math "$@"
diff --git a/desktop/scripts/soffice.sh b/desktop/scripts/soffice.sh
new file mode 100755
index 000000000..5538c968e
--- /dev/null
+++ b/desktop/scripts/soffice.sh
@@ -0,0 +1,191 @@
+#!/bin/sh
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+# use POSIX locale for well-defined tool output
+LO_SAVE_LC_ALL="$LC_ALL"
+LC_ALL=C
+export LC_ALL
+
+#
+# STAR_PROFILE_LOCKING_DISABLED=1
+# export STAR_PROFILE_LOCKING_DISABLED
+#
+
+# file locking now enabled by default
+SAL_ENABLE_FILE_LOCKING=1
+export SAL_ENABLE_FILE_LOCKING
+
+# uncomment line below to disable anti aliasing of fonts
+# SAL_ANTIALIAS_DISABLE=true; export SAL_ANTIALIAS_DISABLE
+
+# uncomment line below if you encounter problems starting soffice on your system
+# SAL_NO_XINITTHREADS=true; export SAL_NO_XINITTHREADS
+
+#@JITC_PROCESSOR_TYPE_EXPORT@
+
+# resolve installation directory
+sd_cwd=$(pwd)
+sd_res="$0"
+while [ -h "$sd_res" ] ; do
+ sd_dirname=$(dirname "$sd_res")
+ cd "$sd_dirname" || exit $?
+ sd_basename=$(basename "$sd_res")
+ sd_res=$(ls -l "$sd_basename" | sed "s/.*$sd_basename -> //g")
+done
+sd_dirname=$(dirname "$sd_res")
+cd "$sd_dirname" || exit $?
+sd_prog=$(pwd)
+cd "$sd_cwd" || exit $?
+
+# linked build needs additional settings
+if [ -e "${sd_prog}/ooenv" ] ; then
+ # shellcheck source=../../instsetoo_native/ooenv
+ . "${sd_prog}/ooenv"
+fi
+
+# try to get some debug output?
+GDBTRACECHECK=
+STRACECHECK=
+VALGRINDCHECK=
+RRCHECK=
+
+# count number of selected checks; only one is allowed
+checks=
+EXTRAOPT=
+# force the --valgrind option if the VALGRIND variable is set
+test -n "$VALGRIND" && EXTRAOPT="--valgrind"
+
+# force the --record option if the RR variable is set
+test -n "$RR" && EXTRAOPT="--record"
+
+for arg in "$@" $EXTRAOPT ; do
+ case "$arg" in
+ --record)
+ if which rr >/dev/null 2>&1 ; then
+ # smoketest may already be recorded => use ignore-nested
+ RRCHECK="rr record --ignore-nested"
+ checks="c$checks"
+ else
+ echo "Error: Can't find the tool \"rr\", --record option will be ignored."
+ exit 1
+ fi
+ ;;
+ --backtrace)
+ if which gdb >/dev/null 2>&1 ; then
+ GDBTRACECHECK="gdb -nx --command=$sd_prog/gdbtrace --args"
+ checks="c$checks"
+ else
+ echo "Error: Can't find the tool \"gdb\", --backtrace option will be ignored."
+ exit 1
+ fi
+ ;;
+ --strace)
+ if which strace >/dev/null 2>&1 ; then
+ STRACECHECK="strace -o strace.log -f -tt -s 256"
+ checks="c$checks"
+ else
+ echo "Error: Can't find the tool \"strace\", --strace option will be ignored."
+ exit 1;
+ fi
+ ;;
+ --valgrind)
+ test -n "$VALGRINDCHECK" && continue;
+ if which valgrind >/dev/null 2>&1 ; then
+ # another valgrind tool might be forced via the environment variable
+ test -z "$VALGRIND" && VALGRIND="memcheck"
+ # --trace-children-skip is pretty useful but supported only with valgrind >= 3.6.0
+ valgrind_ver=$(valgrind --version | sed -e "s/valgrind-//")
+ valgrind_ver_maj=$(echo "$valgrind_ver" | awk -F. '{ print $1 }')
+ valgrind_ver_min=$(echo "$valgrind_ver" | awk -F. '{ print $2 }')
+ valgrind_skip=
+ if [ "$valgrind_ver_maj" -gt 3 ] || ( [ "$valgrind_ver_maj" -eq 3 ] && [ "$valgrind_ver_min" -ge 6 ] ) ; then
+ valgrind_skip='--trace-children-skip=*/java,*/gij'
+ fi
+ # finally set the valgrind check
+ VALGRINDCHECK="valgrind --tool=$VALGRIND --trace-children=yes $valgrind_skip --num-callers=50 --error-limit=no"
+ echo "use kill -SIGUSR2 pid to dump traces of active allocations"
+ checks="c$checks"
+ case "$VALGRIND" in
+ helgrind|memcheck|massif|exp-dhat)
+ export G_SLICE=always-malloc
+ export GLIBCXX_FORCE_NEW=1
+ ;;
+ callgrind)
+ unset MALLOC_CHECK_ MALLOC_PERTURB_ G_SLICE
+ export SAL_DISABLE_FLOATGRAB=1
+ export OOO_DISABLE_RECOVERY=1
+ export SAL_DISABLE_WATCHDOG=1
+ export LD_BIND_NOW=1
+ ;;
+ esac
+ else
+ echo "Error: Can't find the tool \"valgrind\", --valgrind option will be ignored"
+ exit 1
+ fi
+ ;;
+ esac
+done
+
+if echo "$checks" | grep -q "cc" ; then
+ echo "Error: The debug options --record, --backtrace, --strace, and --valgrind cannot be used together."
+ echo " Please, use them one by one."
+ exit 1;
+fi
+
+case "$(uname -s)" in
+OpenBSD)
+# this is a temporary hack until we can live with the default search paths
+ LD_LIBRARY_PATH="$sd_prog${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
+ JAVA_HOME=$(javaPathHelper -h libreoffice-java 2> /dev/null)
+ export LD_LIBRARY_PATH
+ if [ -n "${JAVA_HOME}" ]; then
+ export JAVA_HOME
+ fi
+ ;;
+NetBSD|DragonFly)
+# this is a temporary hack until we can live with the default search paths
+ LD_LIBRARY_PATH="$sd_prog${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
+ export LD_LIBRARY_PATH
+ ;;
+AIX)
+ LIBPATH="$sd_prog${LIBPATH:+:$LIBPATH}"
+ export LIBPATH
+ ;;
+esac
+
+# restore locale setting, avoiding to export empty LC_ALL, s. tdf#130080
+if [ -n "$LO_SAVE_LC_ALL" ]; then
+ LC_ALL="$LO_SAVE_LC_ALL"
+else
+ unset LC_ALL
+fi
+
+# run soffice.bin directly when you want to get the backtrace
+if [ -n "$GDBTRACECHECK" ] ; then
+ exec $GDBTRACECHECK "$sd_prog/soffice.bin" "$@"
+fi
+
+# valgrind --log-file=valgrind.log does not work well with --trace-children=yes
+if [ -n "$VALGRINDCHECK" ] && [ -z "$VALGRIND" ] ; then
+ echo "redirecting the standard and the error output to valgrind.log"
+ exec > valgrind.log 2>&1
+fi
+
+# oosplash does the rest: forcing pages in, javaldx etc. are
+exec $RRCHECK $VALGRINDCHECK $STRACECHECK "$sd_prog/oosplash" "$@"
diff --git a/desktop/scripts/swriter.sh b/desktop/scripts/swriter.sh
new file mode 100755
index 000000000..19a7c9ed4
--- /dev/null
+++ b/desktop/scripts/swriter.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd=$(dirname "$0")/soffice
+exec "$cmd" --writer "$@"
diff --git a/desktop/scripts/unoinfo-mac.sh b/desktop/scripts/unoinfo-mac.sh
new file mode 100755
index 000000000..b67882db0
--- /dev/null
+++ b/desktop/scripts/unoinfo-mac.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+set -e
+
+# resolve installation directory
+sd_res="$0"
+while [ -h "$sd_res" ] ; do
+ sd_dirname=$(dirname "$sd_res")
+ cd "$sd_dirname"
+ sd_basename=$(basename "$sd_res")
+ sd_res=$(ls -l "$sd_basename" | sed "s/.*$sd_basename -> //g")
+done
+sd_dirname=$(dirname "$sd_res")
+cd "$sd_dirname"
+sd_prog=$(pwd)
+
+case "$1" in
+c++)
+ printf '%s' "$sd_prog/../Frameworks"
+ ;;
+java)
+ printf '0%s\0%s' \
+ "$sd_prog/../Resources/java/libreoffice.jar" \
+ "$sd_prog"
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/desktop/scripts/unoinfo.sh b/desktop/scripts/unoinfo.sh
new file mode 100755
index 000000000..14cba8064
--- /dev/null
+++ b/desktop/scripts/unoinfo.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+set -e
+
+# resolve installation directory
+sd_res="$0"
+while [ -h "$sd_res" ] ; do
+ sd_dirname=$(dirname "$sd_res")
+ cd "$sd_dirname"
+ sd_basename=$(basename "$sd_res")
+ sd_res=$(ls -l "$sd_basename" | sed "s/.*$sd_basename -> //g")
+done
+sd_dirname=$(dirname "$sd_res")
+cd "$sd_dirname"
+sd_prog=$(pwd)
+
+case "$1" in
+c++)
+ printf '%s' "$sd_prog"
+ ;;
+java)
+ printf '0%s\0%s' \
+ "$sd_prog/classes/libreoffice.jar" \
+ "$sd_prog"
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/desktop/scripts/unopkg.sh b/desktop/scripts/unopkg.sh
new file mode 100755
index 000000000..3adf69c2e
--- /dev/null
+++ b/desktop/scripts/unopkg.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+# enable file locking
+SAL_ENABLE_FILE_LOCKING=1
+export SAL_ENABLE_FILE_LOCKING
+
+# resolve installation directory
+sd_cwd=$(pwd)
+sd_res="$0"
+while [ -h "$sd_res" ] ; do
+ sd_dirname=$(dirname "$sd_res")
+ cd "$sd_dirname" || exit $?
+ sd_basename=$(basename "$sd_res")
+ sd_res=$(ls -l "$sd_basename" | sed "s/.*$sd_basename -> //g")
+done
+sd_dirname=$(dirname "$sd_res")
+cd "$sd_dirname" || exit $?
+sd_prog=$(pwd)
+cd "$sd_cwd" || exit $?
+
+# this is a temporary hack until we can live with the default search paths
+case "$(uname -s)" in
+OpenBSD)
+ LD_LIBRARY_PATH="$sd_prog${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
+ JAVA_HOME=$(javaPathHelper -h libreoffice-java 2> /dev/null)
+ export LD_LIBRARY_PATH
+ if [ -n "${JAVA_HOME}" ]; then
+ export JAVA_HOME
+ fi
+ ;;
+NetBSD|FreeBSD|DragonFly)
+ LD_LIBRARY_PATH="$sd_prog${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
+ export LD_LIBRARY_PATH
+ ;;
+AIX)
+ LIBPATH="$sd_prog${LIBPATH:+:${LIBPATH}}"
+ export LIBPATH
+ ;;
+esac
+
+for arg in "$@"
+do
+ case "$arg" in
+ #collect all bootstrap variables specified on the command line
+ #so that they can be passed as arguments to javaldx later on
+ -env:*) BOOTSTRAPVARS=$BOOTSTRAPVARS" ""$arg";;
+
+ # make sure shared extensions will be readable by all users
+ --shared) umask 0022;;
+ esac
+done
+
+# extend the ld_library_path for java: javaldx checks the sofficerc for us
+if [ -x "${sd_prog}/javaldx" ] ; then
+ my_path=$("${sd_prog}/javaldx" "$BOOTSTRAPVARS" \
+ "-env:INIFILENAME=vnd.sun.star.pathname:$sd_prog/redirectrc")
+ if [ -n "$my_path" ] ; then
+ sd_platform=$(uname -s)
+ case "$sd_platform" in
+ AIX)
+ LIBPATH="$my_path${LIBPATH:+:$LIBPATH}"
+ export LIBPATH
+ ;;
+ *)
+ LD_LIBRARY_PATH="$my_path${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
+ export LD_LIBRARY_PATH
+ ;;
+ esac
+ fi
+fi
+
+unset XENVIRONMENT
+
+# uncomment line below to disable anti aliasing of fonts
+# SAL_ANTIALIAS_DISABLE=true; export SAL_ANTIALIAS_DISABLE
+
+# uncomment line below if you encounter problems starting soffice on your system
+# SAL_NO_XINITTHREADS=true; export SAL_NO_XINITTHREADS
+
+# execute binary
+exec "$sd_prog/unopkg.bin" "$@" \
+ "-env:INIFILENAME=vnd.sun.star.pathname:$sd_prog/redirectrc"
diff --git a/desktop/source/app/app.cxx b/desktop/source/app/app.cxx
new file mode 100644
index 000000000..e5ed6e491
--- /dev/null
+++ b/desktop/source/app/app.cxx
@@ -0,0 +1,2579 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <config_features.h>
+#include <config_feature_desktop.h>
+#include <config_feature_opencl.h>
+#include <config_java.h>
+#include <config_folders.h>
+#include <config_extensions.h>
+
+#include <sal/config.h>
+
+#include <iostream>
+
+#include <app.hxx>
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include "cmdlineargs.hxx"
+#include <lockfile.hxx>
+#include "userinstall.hxx"
+#include "desktopcontext.hxx"
+#include <migration.hxx>
+#include "officeipcthread.hxx"
+#if HAVE_FEATURE_UPDATE_MAR
+#include "updater.hxx"
+#endif
+
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/runtimetooustring.hxx>
+#include <svl/languageoptions.hxx>
+#include <svtools/javacontext.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/theAutoRecovery.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/SessionListener.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/document/CorruptedFilterConfigurationException.hpp>
+#include <com/sun/star/configuration/CorruptedConfigurationException.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/util/XFlushable.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/configuration/MissingBootstrapFileException.hpp>
+#include <com/sun/star/configuration/InvalidBootstrapFileException.hpp>
+#include <com/sun/star/configuration/InstallationIncompleteException.hpp>
+#include <com/sun/star/configuration/backend/BackendSetupException.hpp>
+#include <com/sun/star/configuration/backend/BackendAccessException.hpp>
+#include <com/sun/star/task/theJobExecutor.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/task/XRestartManager.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/office/Quickstart.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+
+#include <desktop/exithelper.h>
+#include <sal/log.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/configuration.hxx>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/threadpool.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/backupfilehelper.hxx>
+#include <uno/current_context.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Recovery.hxx>
+#include <officecfg/Office/Update.hxx>
+#include <officecfg/Setup.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/byteseq.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/menuoptions.hxx>
+#include <rtl/bootstrap.hxx>
+#include <vcl/glxtestprocess.hxx>
+#include <vcl/help.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/flatpak.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/app.hxx>
+#include <sfx2/safemode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <basic/sbstar.hxx>
+#include <desktop/crashreport.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <svtools/fontsubstconfig.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svtools/apearcfg.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/window.hxx>
+#include "langselect.hxx"
+
+#if defined MACOSX
+#include <errno.h>
+#include <sys/wait.h>
+#endif
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#if defined(_WIN32)
+#include <process.h>
+#define GETPID _getpid
+#else
+#include <unistd.h>
+#define GETPID getpid
+#endif
+
+#include <strings.hxx>
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::system;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::container;
+
+namespace desktop
+{
+
+static oslSignalHandler pSignalHandler = nullptr;
+
+namespace {
+
+#if HAVE_FEATURE_EXTENSIONS
+
+// Remove any existing UserInstallation's extensions cache data remaining from
+// old installations. This addresses at least two problems:
+//
+// For one, apparently due to the old share/prereg/bundled mechanism (disabled
+// since 5c47e5f63a79a9e72ec4a100786b1bbf65137ed4 "fdo#51252 Disable copying
+// share/prereg/bundled to avoid startup crashes"), the user/extensions/bundled
+// cache could contain corrupted information (like a UNO component registered
+// twice, which got changed from active to passive registration in one LO
+// version, but the version of the corresponding bundled extension only
+// incremented in a later LO version).
+//
+// For another, UserInstallations have been seen in the wild where no extensions
+// were installed per-user (any longer), but user/uno_packages/cache/registry/
+// com.sun.star.comp.deployment.component.PackageRegistryBackend/*.rdb files
+// contained data nevertheless.
+//
+// When a LO upgrade is detected (i.e., no user/extensions/buildid or one
+// containing an old build ID), then user/extensions and
+// user/uno_packages/cache/registry/
+// com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc are
+// removed. That should prevent any problems starting the service manager due
+// to old junk. Later on in Desktop::SynchronizeExtensionRepositories, the
+// removed cache data is recreated.
+//
+// Multiple instances of soffice.bin can execute this code in parallel for a
+// single UserInstallation, as it is called before RequestHandler is set up.
+// Therefore, any errors here only lead to SAL_WARNs.
+//
+// At least in theory, this function could be removed again once no
+// UserInstallation can be poisoned by old junk any more.
+bool cleanExtensionCache() {
+ OUString buildId(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(buildId); //TODO: detect failure
+ OUString extDir(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
+ ":UserInstallation}/user/extensions");
+ rtl::Bootstrap::expandMacros(extDir); //TODO: detect failure
+ OUString buildIdFile(extDir + "/buildid");
+ osl::File fr(buildIdFile);
+ osl::FileBase::RC rc = fr.open(osl_File_OpenFlag_Read);
+ switch (rc) {
+ case osl::FileBase::E_None:
+ {
+ rtl::ByteSequence s1;
+ rc = fr.readLine(s1);
+ osl::FileBase::RC rc2 = fr.close();
+ SAL_WARN_IF(
+ rc2 != osl::FileBase::E_None, "desktop.app",
+ "cannot close " << fr.getURL() << " after reading: " << +rc2);
+ // readLine returns E_AGAIN for a zero-size file:
+ if (rc != osl::FileBase::E_None && rc != osl::FileBase::E_AGAIN) {
+ SAL_WARN( "desktop.app", "cannot read from " << fr.getURL() << ": " << +rc);
+ break;
+ }
+ OUString s2(
+ reinterpret_cast< char const * >(s1.getConstArray()),
+ s1.getLength(), RTL_TEXTENCODING_ISO_8859_1);
+ // using ISO 8859-1 avoids any and all conversion errors; the
+ // content should only be a subset of ASCII, anyway
+ if (s2 == buildId) {
+ return false;
+ }
+ break;
+ }
+ case osl::FileBase::E_NOENT:
+ break;
+ default:
+ SAL_WARN( "desktop.app", "cannot open " << fr.getURL() << " for reading: " << +rc);
+ break;
+ }
+ utl::removeTree(extDir);
+ OUString userRcFile(
+ "$UNO_USER_PACKAGES_CACHE/registry/"
+ "com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc");
+ rtl::Bootstrap::expandMacros(userRcFile); //TODO: detect failure
+ rc = osl::File::remove(userRcFile);
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None && rc != osl::FileBase::E_NOENT, "desktop.app",
+ "cannot remove file " << userRcFile << ": " << +rc);
+ rc = osl::Directory::createPath(extDir);
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None && rc != osl::FileBase::E_EXIST, "desktop.app",
+ "cannot create path " << extDir << ": " << +rc);
+ osl::File fw(buildIdFile);
+ rc = fw.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+ if (rc != osl::FileBase::E_None) {
+ SAL_WARN( "desktop.app", "cannot open " << fw.getURL() << " for writing: " << +rc);
+ return true;
+ }
+ OString buf(OUStringToOString(buildId, RTL_TEXTENCODING_UTF8));
+ // using UTF-8 avoids almost all conversion errors (and buildid
+ // containing single surrogate halves should never happen, anyway); the
+ // content should only be a subset of ASCII, anyway
+ sal_uInt64 n = 0;
+ rc = fw.write(buf.getStr(), buf.getLength(), n);
+ SAL_WARN_IF(
+ (rc != osl::FileBase::E_None
+ || n != static_cast< sal_uInt32 >(buf.getLength())),
+ "desktop.app",
+ "cannot write to " << fw.getURL() << ": " << +rc << ", " << n);
+ rc = fw.close();
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None, "desktop.app",
+ "cannot close " << fw.getURL() << " after writing: " << +rc);
+ return true;
+}
+
+#endif
+
+bool shouldLaunchQuickstart()
+{
+ bool bQuickstart = Desktop::GetCommandLineArgs().IsQuickstart();
+ if (!bQuickstart)
+ {
+ const SfxPoolItem* pItem=nullptr;
+ SfxItemSet aQLSet(SfxGetpApp()->GetPool(), svl::Items<SID_ATTR_QUICKLAUNCHER, SID_ATTR_QUICKLAUNCHER>{});
+ SfxGetpApp()->GetOptions(aQLSet);
+ SfxItemState eState = aQLSet.GetItemState(SID_ATTR_QUICKLAUNCHER, false, &pItem);
+ if (SfxItemState::SET == eState)
+ bQuickstart = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ return bQuickstart;
+}
+
+void SetRestartState() {
+ try {
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Office::OfficeRestartInProgress::set(true, batch);
+ batch->commit();
+ } catch (css::uno::Exception) {
+ TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
+ }
+}
+
+void DoRestartActionsIfNecessary(bool quickstart) {
+ if (!quickstart)
+ return;
+
+ try {
+ if (officecfg::Setup::Office::OfficeRestartInProgress::get()) {
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Office::OfficeRestartInProgress::set(
+ false, batch);
+ batch->commit();
+ css::office::Quickstart::createStart(
+ comphelper::getProcessComponentContext(),
+ shouldLaunchQuickstart());
+ }
+ } catch (css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
+ }
+}
+
+void RemoveIconCacheDirectory()
+{
+ // See getIconCacheUrl in vcl/source/image/ImplImageTree.cxx
+ OUString sUrl = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
+ "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache";
+ rtl::Bootstrap::expandMacros(sUrl);
+ utl::UCBContentHelper::Kill(sUrl);
+}
+
+}
+
+namespace {
+
+
+OUString MakeStartupErrorMessage(OUString const & aErrorMessage)
+{
+ return DpResId(STR_BOOTSTRAP_ERR_CANNOT_START) + "\n" + aErrorMessage;
+}
+
+OUString MakeStartupConfigAccessErrorMessage( OUString const & aInternalErrMsg )
+{
+ OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_CFG_DATAACCESS);
+ if ( !aInternalErrMsg.isEmpty() )
+ {
+ aDiagnosticMessage += "\n\n"
+ + DpResId(STR_INTERNAL_ERRMSG)
+ + aInternalErrMsg;
+ }
+ return aDiagnosticMessage;
+}
+
+
+// shows a simple error box with the given message ... but exits from these process !
+// Fatal errors can't be solved by the process ... nor any recovery can help.
+// Mostly the installation was damaged and must be repaired manually .. or by calling
+// setup again.
+// On the other side we must make sure that no further actions will be possible within
+// the current office process ! No pipe requests, no menu/toolbar/shortcut actions
+// are allowed. Otherwise we will force a "crash inside a crash".
+// That's why we have to use a special native message box here which does not use yield :-)
+
+void FatalError(const OUString& sMessage)
+{
+ OUString sProductKey = ::utl::Bootstrap::getProductKey();
+ if ( sProductKey.isEmpty())
+ {
+ osl_getExecutableFile( &sProductKey.pData );
+
+ ::sal_uInt32 nLastIndex = sProductKey.lastIndexOf('/');
+ if ( nLastIndex > 0 )
+ sProductKey = sProductKey.copy( nLastIndex+1 );
+ }
+
+ OUString sTitle = sProductKey + " - Fatal Error";
+ Application::ShowNativeErrorBox (sTitle, sMessage);
+ std::cerr << sTitle << ": " << sMessage << std::endl;
+ _exit(EXITHELPER_FATAL_ERROR);
+}
+
+struct theCommandLineArgs : public rtl::Static< CommandLineArgs, theCommandLineArgs > {};
+
+}
+
+CommandLineArgs& Desktop::GetCommandLineArgs()
+{
+ return theCommandLineArgs::get();
+}
+
+OUString ReplaceStringHookProc( const OUString& rStr )
+{
+ const static OUString sBuildId(utl::Bootstrap::getBuildIdData("development")),
+ sBrandName(utl::ConfigManager::getProductName()),
+ sVersion(utl::ConfigManager::getProductVersion()),
+ sAboutBoxVersion(utl::ConfigManager::getAboutBoxProductVersion()),
+ sAboutBoxVersionSuffix(utl::ConfigManager::getAboutBoxProductVersionSuffix()),
+ sExtension(utl::ConfigManager::getProductExtension());
+
+ OUString sRet(rStr);
+ if (sRet.indexOf("%PRODUCT") != -1 || sRet.indexOf("%ABOUTBOX") != -1)
+ {
+ sRet = sRet.replaceAll( "%PRODUCTNAME", sBrandName );
+ sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
+ sRet = sRet.replaceAll( "%BUILDID", sBuildId );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
+ sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
+ }
+
+ if ( sRet.indexOf( "%OOOVENDOR" ) != -1 )
+ {
+ const static OUString sOOOVendor = utl::ConfigManager::getVendor();
+ sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
+ }
+
+ return sRet;
+}
+
+Desktop::Desktop()
+ : m_bCleanedExtensionCache(false)
+ , m_bServicesRegistered(false)
+ , m_aBootstrapError(BE_OK)
+ , m_aBootstrapStatus(BS_OK)
+{
+ m_firstRunTimer.SetTimeout(3000); // 3 sec.
+ m_firstRunTimer.SetInvokeHandler(LINK(this, Desktop, AsyncInitFirstRun));
+ m_firstRunTimer.SetDebugName( "desktop::Desktop m_firstRunTimer" );
+}
+
+Desktop::~Desktop()
+{
+}
+
+void Desktop::Init()
+{
+ SetBootstrapStatus(BS_OK);
+
+#if HAVE_FEATURE_EXTENSIONS
+ m_bCleanedExtensionCache = cleanExtensionCache();
+#endif
+
+ // We need to have service factory before going further, but see fdo#37195.
+ // Doing this will mmap common.rdb, making it not overwritable on windows,
+ // so this can't happen before the synchronization above. Lets rework this
+ // so that the above is called *from* CreateApplicationServiceManager or
+ // something to enforce this gotcha
+ try
+ {
+ InitApplicationServiceManager();
+ }
+ catch (css::uno::Exception & e)
+ {
+ SetBootstrapError( BE_UNO_SERVICEMANAGER, e.Message );
+ }
+
+ // Check whether safe mode is enabled
+ const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+ // Check if we are restarting from safe mode - in that case we don't want to enter it again
+ if (sfx2::SafeMode::hasRestartFlag())
+ sfx2::SafeMode::removeRestartFlag();
+ else if (rCmdLineArgs.IsSafeMode() || sfx2::SafeMode::hasFlag())
+ Application::EnableSafeMode();
+
+ // When we are in SafeMode we need to do changes before the configuration
+ // gets read (langselect::prepareLocale() by UNO API -> Components::Components)
+ // This may prepare SafeMode or restore from it by moving data in
+ // the UserConfiguration directory
+ comphelper::BackupFileHelper::reactOnSafeMode(Application::IsSafeModeEnabled());
+
+ if ( m_aBootstrapError == BE_OK )
+ {
+ try
+ {
+ if (!langselect::prepareLocale())
+ {
+ SetBootstrapError( BE_LANGUAGE_MISSING, OUString() );
+ }
+ }
+ catch (css::uno::Exception & e)
+ {
+ SetBootstrapError( BE_OFFICECONFIG_BROKEN, e.Message );
+ }
+
+ // test code for ProfileSafeMode to allow testing the fail
+ // of loading the office configuration initially. To use,
+ // either set to true and compile, or set a breakpoint
+ // in debugger and change the local bool
+ static bool bTryHardOfficeconfigBroken(false); // loplugin:constvars:ignore
+
+ if (bTryHardOfficeconfigBroken)
+ {
+ SetBootstrapError(BE_OFFICECONFIG_BROKEN, OUString());
+ }
+ }
+
+ if ( !(true) )
+ return;
+
+ // start ipc thread only for non-remote offices
+ RequestHandler::Status aStatus = RequestHandler::Enable(true);
+ if ( aStatus == RequestHandler::IPC_STATUS_PIPE_ERROR )
+ {
+#if defined ANDROID
+ // Ignore crack pipe errors on Android
+#else
+ // Keep using this oddly named BE_PATHINFO_MISSING value
+ // for pipe-related errors on other platforms. Of course
+ // this crack with two (if not more) levels of our own
+ // error codes hiding the actual system error code is
+ // broken, but that is done all over the code, let's leave
+ // reengineering that to another year.
+ SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
+#endif
+ }
+ else if ( aStatus == RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR )
+ {
+ SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
+ }
+ else if ( aStatus == RequestHandler::IPC_STATUS_2ND_OFFICE )
+ {
+ // 2nd office startup should terminate after sending cmdlineargs through pipe
+ SetBootstrapStatus(BS_TERMINATE);
+ }
+ else if ( !rCmdLineArgs.GetUnknown().isEmpty()
+ || rCmdLineArgs.IsHelp() || rCmdLineArgs.IsVersion() )
+ {
+ // disable IPC thread in an instance that is just showing a help message
+ RequestHandler::Disable();
+ }
+ pSignalHandler = osl_addSignalHandler(SalMainPipeExchangeSignal_impl, nullptr);
+}
+
+void Desktop::InitFinished()
+{
+ CloseSplashScreen();
+}
+
+void Desktop::DeInit()
+{
+ try {
+ // instead of removing of the configManager just let it commit all the changes
+ utl::ConfigManager::storeConfigItems();
+ FlushConfiguration();
+
+ // close splashscreen if it's still open
+ CloseSplashScreen();
+ Reference< XComponent >(
+ comphelper::getProcessComponentContext(), UNO_QUERY_THROW )->
+ dispose();
+ // nobody should get a destroyed service factory...
+ ::comphelper::setProcessServiceFactory( nullptr );
+
+ // clear lockfile
+ m_xLockfile.reset();
+
+ RequestHandler::Disable();
+ if( pSignalHandler )
+ osl_removeSignalHandler( pSignalHandler );
+ } catch (const RuntimeException&) {
+ // someone threw an exception during shutdown
+ // this will leave some garbage behind...
+ }
+}
+
+bool Desktop::QueryExit()
+{
+ try
+ {
+ utl::ConfigManager::storeConfigItems();
+ }
+ catch ( const RuntimeException& )
+ {
+ }
+
+ const char SUSPEND_QUICKSTARTVETO[] = "SuspendQuickstartVeto";
+
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XPropertySet > xPropertySet(xDesktop, UNO_QUERY_THROW);
+ xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(true) );
+
+ bool bExit = xDesktop->terminate();
+
+ if ( !bExit )
+ {
+ xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(false) );
+ }
+ else if (!Application::IsEventTestingModeEnabled())
+ {
+ FlushConfiguration();
+ try
+ {
+ // it is no problem to call RequestHandler::Disable() more than once
+ // it also looks to be threadsafe
+ RequestHandler::Disable();
+ }
+ catch ( const RuntimeException& )
+ {
+ }
+
+ m_xLockfile.reset();
+
+ }
+
+ return bExit;
+}
+
+void Desktop::Shutdown()
+{
+ Reference<XDesktop2> xDesktop = css::frame::Desktop::create(::comphelper::getProcessComponentContext());
+ Reference<XJob> xDesktopInternal(xDesktop, UNO_QUERY_THROW);
+ xDesktopInternal->execute({{"shutdown", {}}});
+}
+
+void Desktop::HandleBootstrapPathErrors( ::utl::Bootstrap::Status aBootstrapStatus, const OUString& aDiagnosticMessage )
+{
+ if ( aBootstrapStatus == ::utl::Bootstrap::DATA_OK )
+ return;
+
+ OUString aProductKey;
+ OUString aTemp;
+
+ osl_getExecutableFile( &aProductKey.pData );
+ sal_uInt32 lastIndex = aProductKey.lastIndexOf('/');
+ if ( lastIndex > 0 )
+ aProductKey = aProductKey.copy( lastIndex+1 );
+
+ aTemp = ::utl::Bootstrap::getProductKey( aProductKey );
+ if ( !aTemp.isEmpty() )
+ aProductKey = aTemp;
+
+ OUString const aMessage(aDiagnosticMessage + "\n");
+
+ std::unique_ptr<weld::MessageDialog> xBootstrapFailedBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, aMessage));
+ xBootstrapFailedBox->set_title(aProductKey);
+ xBootstrapFailedBox->run();
+}
+
+// Create an error message depending on bootstrap failure code and an optional file url
+OUString Desktop::CreateErrorMsgString(
+ utl::Bootstrap::FailureCode nFailureCode,
+ const OUString& aFileURL )
+{
+ OUString aMsg;
+ OUString aFilePath;
+ bool bFileInfo = true;
+
+ switch ( nFailureCode )
+ {
+ /// the shared installation directory could not be located
+ case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_PATH_INVALID);
+ bFileInfo = false;
+ }
+ break;
+
+ /// the bootstrap INI file could not be found or read
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
+ }
+ break;
+
+ /// the bootstrap INI is missing a required entry
+ /// the bootstrap INI contains invalid data
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_CORRUPT);
+ }
+ break;
+
+ /// the version locator INI file could not be found or read
+ case ::utl::Bootstrap::MISSING_VERSION_FILE:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
+ }
+ break;
+
+ /// the version locator INI has no entry for this version
+ case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_NO_SUPPORT);
+ }
+ break;
+
+ /// the user installation directory does not exist
+ case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_DIR_MISSING);
+ }
+ break;
+
+ /// some bootstrap data was invalid in unexpected ways
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
+ {
+ aMsg = DpResId(STR_BOOTSTRAP_ERR_INTERNAL);
+ bFileInfo = false;
+ }
+ break;
+
+ case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
+ {
+ // This needs to be improved, see #i67575#:
+ aMsg = "Invalid version file entry";
+ bFileInfo = false;
+ }
+ break;
+
+ case ::utl::Bootstrap::NO_FAILURE:
+ {
+ OSL_ASSERT(false);
+ }
+ break;
+ }
+
+ if ( bFileInfo )
+ {
+ OUString aMsgString( aMsg );
+
+ osl::File::getSystemPathFromFileURL( aFileURL, aFilePath );
+
+ aMsgString = aMsgString.replaceFirst( "$1", aFilePath );
+ aMsg = aMsgString;
+ }
+
+ return MakeStartupErrorMessage( aMsg );
+}
+
+void Desktop::HandleBootstrapErrors(
+ BootstrapError aBootstrapError, OUString const & aErrorMessage )
+{
+ if ( aBootstrapError == BE_PATHINFO_MISSING )
+ {
+ OUString aErrorMsg;
+ OUString aBuffer;
+ utl::Bootstrap::Status aBootstrapStatus;
+ utl::Bootstrap::FailureCode nFailureCode;
+
+ aBootstrapStatus = ::utl::Bootstrap::checkBootstrapStatus( aBuffer, nFailureCode );
+ if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
+ {
+ switch ( nFailureCode )
+ {
+ case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
+ {
+ aErrorMsg = CreateErrorMsgString( nFailureCode, OUString() );
+ }
+ break;
+
+ /// the bootstrap INI file could not be found or read
+ /// the bootstrap INI is missing a required entry
+ /// the bootstrap INI contains invalid data
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
+ {
+ OUString aBootstrapFileURL;
+
+ utl::Bootstrap::locateBootstrapFile( aBootstrapFileURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aBootstrapFileURL );
+ }
+ break;
+
+ /// the version locator INI file could not be found or read
+ /// the version locator INI has no entry for this version
+ /// the version locator INI entry is not a valid directory URL
+ case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_VERSION_FILE:
+ {
+ OUString aVersionFileURL;
+
+ utl::Bootstrap::locateVersionFile( aVersionFileURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aVersionFileURL );
+ }
+ break;
+
+ /// the user installation directory does not exist
+ case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
+ {
+ OUString aUserInstallationURL;
+
+ utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aUserInstallationURL );
+ }
+ break;
+
+ case ::utl::Bootstrap::NO_FAILURE:
+ {
+ OSL_ASSERT(false);
+ }
+ break;
+ }
+
+ HandleBootstrapPathErrors( aBootstrapStatus, aErrorMsg );
+ }
+ }
+ else if ( aBootstrapError == BE_UNO_SERVICEMANAGER || aBootstrapError == BE_UNO_SERVICE_CONFIG_MISSING )
+ {
+ // UNO service manager is not available. VCL needs a UNO service manager to display a message box!!!
+ // Currently we are not able to display a message box with a service manager due to this limitations inside VCL.
+
+ // When UNO is not properly initialized, all kinds of things can fail
+ // and cause the process to crash. To give the user a hint even if
+ // generating and displaying a message box below crashes, print a
+ // hard-coded message on stderr first:
+ std::cerr
+ << "The application cannot be started.\n"
+ // STR_BOOTSTRAP_ERR_CANNOT_START
+ << (aBootstrapError == BE_UNO_SERVICEMANAGER
+ ? "The component manager is not available.\n"
+ // STR_BOOTSTRAP_ERR_NO_SERVICE
+ : "The configuration service is not available.\n");
+ // STR_BOOTSTRAP_ERR_NO_CFG_SERVICE
+ if ( !aErrorMessage.isEmpty() )
+ {
+ std::cerr << "(\"" << aErrorMessage << "\")\n";
+ }
+
+ // First sentence. We cannot bootstrap office further!
+ OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NO_CFG_SERVICE) + "\n";
+ if ( !aErrorMessage.isEmpty() )
+ {
+ aDiagnosticMessage += "(\"" + aErrorMessage + "\")\n";
+ }
+
+ // Due to the fact the we haven't a backup applicat.rdb file anymore it is not possible to
+ // repair the installation with the setup executable besides the office executable. Now
+ // we have to ask the user to start the setup on CD/installation directory manually!!
+ aDiagnosticMessage += DpResId(STR_ASK_START_SETUP_MANUALLY);
+
+ FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
+ }
+ else if ( aBootstrapError == BE_OFFICECONFIG_BROKEN )
+ {
+ // set flag at BackupFileHelper to be able to know if _exit was called and
+ // actions are executed after this. This method we are in will not return,
+ // but end up in a _exit() call
+ comphelper::BackupFileHelper::setExitWasCalled();
+
+ // enter safe mode, too
+ sfx2::SafeMode::putFlag();
+
+ OUString msg(DpResId(STR_CONFIG_ERR_ACCESS_GENERAL));
+ if (!aErrorMessage.isEmpty()) {
+ msg += "\n(\"" + aErrorMessage + "\")";
+ }
+ FatalError(MakeStartupErrorMessage(msg));
+ }
+ else if ( aBootstrapError == BE_USERINSTALL_FAILED )
+ {
+ OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_USERINSTALL_FAILED);
+ FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
+ }
+ else if ( aBootstrapError == BE_LANGUAGE_MISSING )
+ {
+ OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_LANGUAGE_MISSING);
+ FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
+ }
+ else if (( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE ) ||
+ ( aBootstrapError == BE_USERINSTALL_NOWRITEACCESS ))
+ {
+ OUString aUserInstallationURL;
+ OUString aUserInstallationPath;
+ utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
+ osl::File::getSystemPathFromFileURL( aUserInstallationURL, aUserInstallationPath );
+
+ OUString aDiagnosticMessage;
+ if ( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE )
+ aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOTENOUGHDISKSPACE);
+ else
+ aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOACCESSRIGHTS);
+ aDiagnosticMessage += aUserInstallationPath;
+
+ FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
+ }
+}
+
+
+namespace {
+
+
+#if HAVE_FEATURE_BREAKPAD
+void handleCrashReport()
+{
+ static const char SERVICENAME_CRASHREPORT[] = "com.sun.star.comp.svx.CrashReportUI";
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XSynchronousDispatch > xRecoveryUI(
+ xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_CRASHREPORT, xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ Reference< css::util::XURLTransformer > xURLParser =
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
+
+ css::util::URL aURL;
+ css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+}
+#endif
+
+#if !defined ANDROID
+void handleSafeMode()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XSynchronousDispatch > xSafeModeUI(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.SafeModeUI", xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ css::util::URL aURL;
+ css::uno::Any aRet = xSafeModeUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+}
+#endif
+
+/** @short check if recovery must be started or not.
+
+ @param bCrashed [boolean ... out!]
+ the office crashed last times.
+ But may be there are no recovery data.
+ Useful to trigger the error report tool without
+ showing the recovery UI.
+
+ @param bRecoveryDataExists [boolean ... out!]
+ there exists some recovery data.
+
+ @param bSessionDataExists [boolean ... out!]
+ there exists some session data.
+ Because the user may be logged out last time from its
+ unix session...
+*/
+void impl_checkRecoveryState(bool& bCrashed ,
+ bool& bRecoveryDataExists,
+ bool& bSessionDataExists )
+{
+ bCrashed = officecfg::Office::Recovery::RecoveryInfo::Crashed::get()
+#if HAVE_FEATURE_BREAKPAD
+ || CrashReporter::crashReportInfoExists();
+#else
+ ;
+#endif
+ bool elements = officecfg::Office::Recovery::RecoveryList::get()->
+ hasElements();
+ bool session
+ = officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
+ bRecoveryDataExists = elements && !session;
+ bSessionDataExists = elements && session;
+}
+
+Reference< css::frame::XSynchronousDispatch > g_xRecoveryUI;
+
+template <class Ref>
+struct RefClearGuard
+{
+ Ref& m_Ref;
+ RefClearGuard(Ref& ref) : m_Ref(ref) {}
+ ~RefClearGuard() { m_Ref.clear(); }
+};
+
+/* @short start the recovery wizard.
+
+ @param bEmergencySave
+ differs between EMERGENCY_SAVE and RECOVERY
+*/
+bool impl_callRecoveryUI(bool bEmergencySave ,
+ bool bExistsRecoveryData)
+{
+ static const char COMMAND_EMERGENCYSAVE[] = "vnd.sun.star.autorecovery:/doEmergencySave";
+ static const char COMMAND_RECOVERY[] = "vnd.sun.star.autorecovery:/doAutoRecovery";
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ g_xRecoveryUI.set(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.RecoveryUI", xContext),
+ css::uno::UNO_QUERY_THROW);
+ RefClearGuard<Reference< css::frame::XSynchronousDispatch >> refClearGuard(g_xRecoveryUI);
+
+ Reference< css::util::XURLTransformer > xURLParser =
+ css::util::URLTransformer::create(xContext);
+
+ css::util::URL aURL;
+ if (bEmergencySave)
+ aURL.Complete = COMMAND_EMERGENCYSAVE;
+ else if (bExistsRecoveryData)
+ aURL.Complete = COMMAND_RECOVERY;
+ else
+ return false;
+
+ xURLParser->parseStrict(aURL);
+
+ css::uno::Any aRet = g_xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+ return bRet;
+}
+
+bool impl_bringToFrontRecoveryUI()
+{
+ Reference< css::frame::XSynchronousDispatch > xRecoveryUI(g_xRecoveryUI);
+ if (!xRecoveryUI.is())
+ return false;
+
+ css::util::URL aURL;
+ aURL.Complete = "vnd.sun.star.autorecovery:/doBringToFront";
+ Reference< css::util::XURLTransformer > xURLParser =
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
+ xURLParser->parseStrict(aURL);
+
+ css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+ return bRet;
+}
+
+}
+
+namespace {
+
+void restartOnMac(bool passArguments) {
+#if defined MACOSX
+ RequestHandler::Disable();
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ (void) passArguments; // avoid warnings
+ OUString aMessage = DpResId(STR_LO_MUST_BE_RESTARTED);
+
+ std::unique_ptr<weld::MessageDialog> xRestartBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, aMessage));
+ xRestartBox->run();
+#else
+ OUString execUrl;
+ OSL_VERIFY(osl_getExecutableFile(&execUrl.pData) == osl_Process_E_None);
+ OUString execPath;
+ OString execPath8;
+ if ((osl::FileBase::getSystemPathFromFileURL(execUrl, execPath)
+ != osl::FileBase::E_None) ||
+ !execPath.convertToString(
+ &execPath8, osl_getThreadTextEncoding(),
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::abort();
+ }
+ std::vector< OString > args;
+ args.push_back(execPath8);
+ bool wait = false;
+ if (passArguments) {
+ sal_uInt32 n = osl_getCommandArgCount();
+ for (sal_uInt32 i = 0; i < n; ++i) {
+ OUString arg;
+ osl_getCommandArg(i, &arg.pData);
+ if (arg.match("--accept=")) {
+ wait = true;
+ }
+ OString arg8;
+ if (!arg.convertToString(
+ &arg8, osl_getThreadTextEncoding(),
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::abort();
+ }
+ args.push_back(arg8);
+ }
+ }
+ std::vector< char const * > argPtrs;
+ for (auto const& elem : args)
+ {
+ argPtrs.push_back(elem.getStr());
+ }
+ argPtrs.push_back(nullptr);
+ execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
+ if (errno == ENOTSUP) { // happens when multithreaded on macOS < 10.6
+ pid_t pid = fork();
+ if (pid == 0) {
+ execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
+ } else if (pid > 0) {
+ // Two simultaneously running soffice processes lead to two dock
+ // icons, so avoid waiting here unless it must be assumed that the
+ // process invoking soffice itself wants to wait for soffice to
+ // finish:
+ if (!wait) {
+ return;
+ }
+ int stat;
+ if (waitpid(pid, &stat, 0) == pid && WIFEXITED(stat)) {
+ _exit(WEXITSTATUS(stat));
+ }
+ }
+ }
+ std::abort();
+#endif
+#else
+ (void) passArguments; // avoid warnings
+#endif
+}
+
+#if HAVE_FEATURE_UPDATE_MAR
+bool isTimeForUpdateCheck()
+{
+ sal_uInt64 nLastUpdate = officecfg::Office::Update::Update::LastUpdateTime::get();
+ sal_uInt64 nNow = tools::Time::GetSystemTicks();
+
+ sal_uInt64 n7DayInMS = 1000 * 60 * 60 * 12 * 1; // 12 hours in ms
+ if (nNow - n7DayInMS >= nLastUpdate)
+ return true;
+
+ return false;
+}
+#endif
+
+}
+
+void Desktop::Exception(ExceptionCategory nCategory)
+{
+ // protect against recursive calls
+ static bool bInException = false;
+
+#if HAVE_FEATURE_BREAKPAD
+ CrashReporter::removeExceptionHandler(); // disallow re-entry
+#endif
+
+ SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
+ Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE );
+ if ( bInException )
+ {
+ Application::Abort( OUString() );
+ }
+
+ bInException = true;
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+
+ // save all modified documents ... if it's allowed doing so.
+ bool bRestart = false;
+ bool bAllowRecoveryAndSessionManagement = (
+ ( !rArgs.IsNoRestore() ) && // some use cases of office must work without recovery
+ ( !rArgs.IsHeadless() ) &&
+ ( nCategory != ExceptionCategory::UserInterface ) && // recovery can't work without UI ... but UI layer seems to be the reason for this crash
+ ( Application::IsInExecute() ) // crashes during startup and shutdown should be ignored (they indicate a corrupted installation...)
+ );
+ if ( bAllowRecoveryAndSessionManagement )
+ {
+ // Save all open documents so they will be reopened
+ // the next time the application is started
+ // returns true if at least one document could be saved...
+ bRestart = impl_callRecoveryUI(
+ true , // force emergency save
+ false);
+ }
+
+ FlushConfiguration();
+
+ switch( nCategory )
+ {
+ case ExceptionCategory::ResourceNotLoaded:
+ {
+ Application::Abort( OUString() );
+ break;
+ }
+
+ default:
+ {
+ m_xLockfile.reset();
+
+ if( bRestart )
+ {
+ RequestHandler::Disable();
+ if( pSignalHandler )
+ osl_removeSignalHandler( pSignalHandler );
+
+ restartOnMac(false);
+ if ( m_rSplashScreen.is() )
+ m_rSplashScreen->reset();
+
+ _exit( EXITHELPER_CRASH_WITH_RESTART );
+ }
+ else
+ {
+ Application::Abort( OUString() );
+ }
+
+ break;
+ }
+ }
+
+ OSL_ASSERT(false); // unreachable
+}
+
+void Desktop::AppEvent( const ApplicationEvent& rAppEvent )
+{
+ HandleAppEvent( rAppEvent );
+}
+
+namespace {
+
+struct ExecuteGlobals
+{
+ Reference < css::document::XDocumentEventListener > xGlobalBroadcaster;
+ bool bRestartRequested;
+ bool bUseSystemFileDialog;
+ std::unique_ptr<SvtLanguageOptions> pLanguageOptions;
+ std::unique_ptr<SvtPathOptions> pPathOptions;
+
+ ExecuteGlobals()
+ : bRestartRequested( false )
+ , bUseSystemFileDialog( true )
+ {}
+};
+
+}
+
+static ExecuteGlobals* pExecGlobals = nullptr;
+
+int Desktop::Main()
+{
+ pExecGlobals = new ExecuteGlobals();
+
+ // Remember current context object
+ css::uno::ContextLayer layer( css::uno::getCurrentContext() );
+
+ if ( m_aBootstrapError != BE_OK )
+ {
+ HandleBootstrapErrors( m_aBootstrapError, m_aBootstrapErrorMessage );
+ return EXIT_FAILURE;
+ }
+
+ BootstrapStatus eStatus = GetBootstrapStatus();
+ if (eStatus == BS_TERMINATE) {
+ return EXIT_SUCCESS;
+ }
+
+ // Detect desktop environment - need to do this as early as possible
+ css::uno::setCurrentContext( new DesktopContext( css::uno::getCurrentContext() ) );
+
+ CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+
+ Translate::SetReadStringHook(ReplaceStringHookProc);
+
+ // Startup screen
+ OpenSplashScreen();
+
+ SetSplashScreenProgress(10);
+
+ userinstall::Status inst_fin = userinstall::finalize();
+ if (inst_fin != userinstall::EXISTED && inst_fin != userinstall::CREATED)
+ {
+ SAL_WARN( "desktop.app", "userinstall failed");
+ if ( inst_fin == userinstall::ERROR_NO_SPACE )
+ HandleBootstrapErrors(
+ BE_USERINSTALL_NOTENOUGHDISKSPACE, OUString() );
+ else if ( inst_fin == userinstall::ERROR_CANT_WRITE )
+ HandleBootstrapErrors( BE_USERINSTALL_NOWRITEACCESS, OUString() );
+ else
+ HandleBootstrapErrors( BE_USERINSTALL_FAILED, OUString() );
+ return EXIT_FAILURE;
+ }
+ // refresh path information
+ utl::Bootstrap::reloadData();
+ SetSplashScreenProgress(20);
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< XRestartManager > xRestartManager( OfficeRestartManager::get(xContext) );
+
+ Reference< XDesktop2 > xDesktop;
+ try
+ {
+ RegisterServices(xContext);
+
+ SetSplashScreenProgress(25);
+
+#if HAVE_FEATURE_DESKTOP
+ // check user installation directory for lockfile so we can be sure
+ // there is no other instance using our data files from a remote host
+
+ bool bMustLockProfile = ( getenv( "SAL_NOLOCK_PROFILE" ) == nullptr );
+ if ( bMustLockProfile )
+ {
+ m_xLockfile.reset(new Lockfile);
+
+ if ( !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsInvisible() &&
+ !rCmdLineArgs.IsNoLockcheck() && !m_xLockfile->check( Lockfile_execWarning ))
+ {
+ // Lockfile exists, and user clicked 'no'
+ return EXIT_FAILURE;
+ }
+ }
+
+ // check if accessibility is enabled but not working and allow to quit
+ if( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() )
+ {
+ if( !InitAccessBridge() )
+ return EXIT_FAILURE;
+ }
+#endif
+
+ // terminate if requested...
+ if( rCmdLineArgs.IsTerminateAfterInit() )
+ return EXIT_SUCCESS;
+
+ // Read the common configuration items for optimization purpose
+ if ( !InitializeConfiguration() )
+ return EXIT_FAILURE;
+
+#if HAVE_FEATURE_UPDATE_MAR
+ const char* pUpdaterTestEnable = std::getenv("LIBO_UPDATER_TEST_ENABLE");
+ if (pUpdaterTestEnable || officecfg::Office::Update::Update::Enabled::get())
+ {
+ // check if we just updated
+ const char* pUpdaterRunning = std::getenv("LIBO_UPDATER_TEST_RUNNING");
+ bool bUpdateRunning = officecfg::Office::Update::Update::UpdateRunning::get() || pUpdaterRunning;
+ if (bUpdateRunning)
+ {
+ OUString aSeeAlso = officecfg::Office::Update::Update::SeeAlso::get();
+ OUString aOldBuildID = officecfg::Office::Update::Update::OldBuildID::get();
+
+ OUString aBuildID = Updater::getBuildID();
+ if (aOldBuildID == aBuildID)
+ {
+ Updater::log("Old and new Build ID are the same. No Updating took place.");
+ }
+ else
+ {
+ if (!aSeeAlso.isEmpty())
+ {
+ SAL_INFO("desktop.updater", "See also: " << aSeeAlso);
+ Reference< css::system::XSystemShellExecute > xSystemShell(
+ SystemShellExecute::create(::comphelper::getProcessComponentContext()) );
+
+ xSystemShell->execute( aSeeAlso, OUString(), SystemShellExecuteFlags::URIS_ONLY );
+ }
+ }
+
+ // reset all the configuration values,
+ // all values need to be read before this code
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Update::Update::UpdateRunning::set(false, batch);
+ officecfg::Office::Update::Update::SeeAlso::set(OUString(), batch);
+ officecfg::Office::Update::Update::OldBuildID::set(OUString(), batch);
+ batch->commit();
+
+ Updater::removeUpdateFiles();
+ }
+
+ osl::DirectoryItem aUpdateFile;
+ osl::DirectoryItem::get(Updater::getUpdateFileURL(), aUpdateFile);
+
+ const char* pUpdaterTestUpdate = std::getenv("LIBO_UPDATER_TEST_UPDATE");
+ const char* pForcedUpdateCheck = std::getenv("LIBO_UPDATER_TEST_UPDATE_CHECK");
+ if (pUpdaterTestUpdate || aUpdateFile.is())
+ {
+ OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(aBuildID);
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Update::Update::OldBuildID::set(aBuildID, batch);
+ officecfg::Office::Update::Update::UpdateRunning::set(true, batch);
+ batch->commit();
+
+ // make sure the change is written to the configuration before we start the update
+ css::uno::Reference<css::util::XFlushable> xFlushable(css::configuration::theDefaultProvider::get(xContext), UNO_QUERY);
+ xFlushable->flush();
+ // avoid the old oosplash staying around
+ CloseSplashScreen();
+ bool bSuccess = update();
+ if (bSuccess)
+ return EXIT_SUCCESS;
+ }
+ else if (isTimeForUpdateCheck() || pForcedUpdateCheck)
+ {
+ sal_uInt64 nNow = tools::Time::GetSystemTicks();
+ Updater::log("Update Check Time: " + OUString::number(nNow));
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Update::Update::LastUpdateTime::set(nNow, batch);
+ batch->commit();
+ m_aUpdateThread = std::thread(update_checker);
+ }
+ }
+#endif
+
+ SetSplashScreenProgress(30);
+
+ // create title string
+ OUString aTitle(ReplaceStringHookProc(RID_APPTITLE));
+
+#ifdef DBG_UTIL
+ //include buildid in non product builds
+ aTitle += " [" + utl::Bootstrap::getBuildIdData("development") + "]";
+#endif
+
+ SetDisplayName( aTitle );
+ SetSplashScreenProgress(35);
+ pExecGlobals->pPathOptions.reset( new SvtPathOptions);
+ SetSplashScreenProgress(40);
+
+ xDesktop = css::frame::Desktop::create( xContext );
+
+ // create service for loading SFX (still needed in startup)
+ pExecGlobals->xGlobalBroadcaster = Reference < css::document::XDocumentEventListener >
+ ( css::frame::theGlobalEventBroadcaster::get(xContext), UNO_SET_THROW );
+
+ /* ensure existence of a default window that messages can be dispatched to
+ This is for the benefit of testtool which uses PostUserEvent extensively
+ and else can deadlock while creating this window from another thread while
+ the main thread is not yet in the event loop.
+ */
+ Application::GetDefaultDevice();
+
+#if HAVE_FEATURE_EXTENSIONS
+ // Check if bundled or shared extensions were added /removed
+ // and process those extensions (has to be done before checking
+ // the extension dependencies!
+ SynchronizeExtensionRepositories(m_bCleanedExtensionCache, this);
+ bool bAbort = CheckExtensionDependencies();
+ if ( bAbort )
+ return EXIT_FAILURE;
+
+ if (inst_fin == userinstall::CREATED)
+ {
+ Migration::migrateSettingsIfNecessary();
+ }
+#endif
+
+ // keep a language options instance...
+ pExecGlobals->pLanguageOptions.reset( new SvtLanguageOptions(true));
+
+ css::document::DocumentEvent aEvent;
+ aEvent.EventName = "OnStartApp";
+ pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
+
+ SetSplashScreenProgress(50);
+
+ // Backing Component
+ bool bCrashed = false;
+ bool bExistsRecoveryData = false;
+ bool bExistsSessionData = false;
+
+ impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
+
+ OUString pidfileName = rCmdLineArgs.GetPidfileName();
+ if ( !pidfileName.isEmpty() )
+ {
+ OUString pidfileURL;
+
+ if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
+ {
+ osl::File pidfile( pidfileURL );
+ osl::FileBase::RC rc;
+
+ osl::File::remove( pidfileURL );
+ if ( (rc = pidfile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ) == osl::File::E_None )
+ {
+ OString pid( OString::number( GETPID() ) );
+ sal_uInt64 written = 0;
+ if ( pidfile.write(pid.getStr(), pid.getLength(), written) != osl::File::E_None )
+ {
+ SAL_WARN("desktop.app", "cannot write pidfile " << pidfile.getURL());
+ }
+ pidfile.close();
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "cannot open pidfile " << pidfile.getURL() << rc);
+ }
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "cannot get pidfile URL from path" << pidfileName);
+ }
+ }
+
+ if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
+ {
+ // Ensure that we use not the system file dialogs as
+ // headless mode relies on Application::EnableHeadlessMode()
+ // which does only work for VCL dialogs!!
+ SvtMiscOptions aMiscOptions;
+ pExecGlobals->bUseSystemFileDialog = aMiscOptions.UseSystemFileDialog();
+ aMiscOptions.SetUseSystemFileDialog( false );
+ }
+
+ pExecGlobals->bRestartRequested = xRestartManager->isRestartRequested(
+ true);
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ if ((!rCmdLineArgs.WantsToLoadDocument() && !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsQuickstart()) &&
+ (SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) &&
+ (!bExistsRecoveryData ) &&
+ (!bExistsSessionData ) &&
+ (!Application::AnyInput( VclInputFlags::APPEVENT ) ))
+ {
+ ShowBackingComponent(this);
+ }
+ }
+ }
+ catch ( const css::lang::WrappedTargetException& wte )
+ {
+ css::uno::Exception te;
+ wte.TargetException >>= te;
+ FatalError( MakeStartupConfigAccessErrorMessage(wte.Message + te.Message) );
+ }
+ catch ( const css::uno::Exception& e )
+ {
+ FatalError( MakeStartupErrorMessage(e.Message) );
+ }
+ SetSplashScreenProgress(55);
+
+ SvtFontSubstConfig().Apply();
+
+ SvtTabAppearanceCfg aAppearanceCfg;
+ SvtTabAppearanceCfg::SetInitialized();
+ aAppearanceCfg.SetApplicationDefaults( this );
+ SvtAccessibilityOptions aOptions;
+ aOptions.SetVCLSettings();
+ SetSplashScreenProgress(60);
+
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ Application::SetFilterHdl( LINK( this, Desktop, ImplInitFilterHdl ) );
+
+ // Preload function depends on an initialized sfx application!
+ SetSplashScreenProgress(75);
+
+ // use system window dialogs
+ Application::SetSystemWindowMode( SystemWindowFlags::DIALOG );
+
+ SetSplashScreenProgress(80);
+
+ if ( !rCmdLineArgs.IsInvisible() &&
+ !rCmdLineArgs.IsNoQuickstart() )
+ InitializeQuickstartMode( xContext );
+
+ try
+ {
+ if ( xDesktop.is() )
+ xDesktop->addTerminateListener( new RequestHandlerController );
+ SetSplashScreenProgress(100);
+ }
+ catch ( const css::uno::Exception& e )
+ {
+ FatalError( MakeStartupErrorMessage(e.Message) );
+ }
+
+ // FIXME: move this somewhere sensible.
+#if HAVE_FEATURE_OPENCL
+ CheckOpenCLCompute(xDesktop);
+#endif
+
+ // In headless mode, reap the process started by fire_glxtest_process() early in soffice_main
+ // (desktop/source/app/sofficemain.cxx).
+ if (rCmdLineArgs.IsHeadless()) {
+ reap_glxtest_process();
+ }
+
+ // Release solar mutex just before we wait for our client to connect
+ {
+ SolarMutexReleaser aReleaser;
+
+ // Post user event to startup first application component window
+ // We have to send this OpenClients message short before execute() to
+ // minimize the risk that this message overtakes type detection construction!!
+ Application::PostUserEvent( LINK( this, Desktop, OpenClients_Impl ) );
+
+ // Post event to enable acceptors
+ Application::PostUserEvent( LINK( this, Desktop, EnableAcceptors_Impl) );
+
+ // Acquire solar mutex just before we enter our message loop
+ }
+
+ // call Application::Execute to process messages in vcl message loop
+#ifndef IOS
+ try
+#endif
+ {
+#if HAVE_FEATURE_JAVA
+ // The JavaContext contains an interaction handler which is used when
+ // the creation of a Java Virtual Machine fails
+ css::uno::ContextLayer layer2(
+ new svt::JavaContext( css::uno::getCurrentContext() ) );
+#endif
+ // check whether the shutdown is caused by restart just before entering the Execute
+ pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
+ xRestartManager->isRestartRequested(true);
+
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ // if this run of the office is triggered by restart, some additional actions should be done
+ DoRestartActionsIfNecessary( !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsNoQuickstart() );
+
+ Execute();
+ }
+ }
+#ifndef IOS
+ catch(const css::document::CorruptedFilterConfigurationException& exFilterCfg)
+ {
+ RequestHandler::SetDowning();
+ FatalError( MakeStartupErrorMessage(exFilterCfg.Message) );
+ }
+ catch(const css::configuration::CorruptedConfigurationException& exAnyCfg)
+ {
+ RequestHandler::SetDowning();
+ FatalError( MakeStartupErrorMessage(exAnyCfg.Message) );
+ }
+ catch( const css::uno::Exception& exUNO)
+ {
+ RequestHandler::SetDowning();
+ FatalError( exUNO.Message);
+ }
+ catch( const std::exception& exSTD)
+ {
+ RequestHandler::SetDowning();
+ FatalError(o3tl::runtimeToOUString(exSTD.what()));
+ }
+ catch( ...)
+ {
+ RequestHandler::SetDowning();
+ FatalError( "Caught Unknown Exception: Aborting!");
+ }
+#endif
+ }
+ else
+ {
+ if (xDesktop.is())
+ xDesktop->terminate();
+ }
+ // CAUTION: you do not necessarily get here e.g. on the Mac.
+ // please put all deinitialization code into doShutdown
+ return doShutdown();
+}
+
+int Desktop::doShutdown()
+{
+ if( ! pExecGlobals )
+ return EXIT_SUCCESS;
+
+ if (m_aUpdateThread.joinable())
+ m_aUpdateThread.join();
+
+ pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
+ OfficeRestartManager::get(comphelper::getProcessComponentContext())->
+ isRestartRequested(true);
+ if ( pExecGlobals->bRestartRequested )
+ SetRestartState();
+
+ // Restore old value
+ const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+ if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
+ SvtMiscOptions().SetUseSystemFileDialog( pExecGlobals->bUseSystemFileDialog );
+
+ OUString pidfileName = rCmdLineArgs.GetPidfileName();
+ if ( !pidfileName.isEmpty() )
+ {
+ OUString pidfileURL;
+
+ if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
+ {
+ if ( osl::File::remove( pidfileURL ) != osl::FileBase::E_None )
+ {
+ SAL_WARN("desktop.app", "shutdown: cannot remove pidfile " << pidfileURL);
+ }
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "shutdown: cannot get pidfile URL from path" << pidfileName);
+ }
+ }
+
+ // remove temp directory
+ RemoveTemporaryDirectory();
+ flatpak::removeTemporaryHtmlDirectory();
+
+ // flush evtl. configuration changes so that all config files in user
+ // dir are written
+ FlushConfiguration();
+
+ if (pExecGlobals->bRestartRequested)
+ {
+ // tdf#128523
+ RemoveIconCacheDirectory();
+
+ // a restart is already requested, usually due to a configuration change
+ // that needs a restart to get active. If this is the case, do not try
+ // to use SecureUserConfig to safe this still untested new configuration
+ }
+ else
+ {
+ // Test if SecureUserConfig is active. If yes and we are at this point, regular shutdown
+ // is in progress and the currently used configuration was working. Try to secure this
+ // working configuration for later eventually necessary restores
+ comphelper::BackupFileHelper aBackupFileHelper;
+
+ aBackupFileHelper.tryPush();
+ aBackupFileHelper.tryPushExtensionInfo();
+ }
+
+ // The acceptors in the AcceptorMap must be released (in DeregisterServices)
+ // with the solar mutex unlocked, to avoid deadlock:
+ {
+ SolarMutexReleaser aReleaser;
+ DeregisterServices();
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::DetachAllDocBasicItems();
+#endif
+ }
+
+ // be sure that path/language options gets destroyed before
+ // UCB is deinitialized
+ pExecGlobals->pLanguageOptions.reset();
+ pExecGlobals->pPathOptions.reset();
+
+ comphelper::ThreadPool::getSharedOptimalPool().shutdown();
+
+ bool bRR = pExecGlobals->bRestartRequested;
+ delete pExecGlobals;
+ pExecGlobals = nullptr;
+
+ if ( bRR )
+ {
+ restartOnMac(true);
+ if ( m_rSplashScreen.is() )
+ m_rSplashScreen->reset();
+
+ return EXITHELPER_NORMAL_RESTART;
+ }
+ return EXIT_SUCCESS;
+}
+
+IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool )
+{
+ return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData );
+}
+
+bool Desktop::InitializeConfiguration()
+{
+ try
+ {
+ css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext() );
+ return true;
+ }
+ catch( css::lang::ServiceNotRegisteredException & e )
+ {
+ HandleBootstrapErrors(
+ Desktop::BE_UNO_SERVICE_CONFIG_MISSING, e.Message );
+ }
+ catch( const css::configuration::MissingBootstrapFileException& e )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::MISSING_BOOTSTRAP_FILE,
+ e.BootstrapFileURL ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_USER_INSTALL, aMsg );
+ }
+ catch( const css::configuration::InvalidBootstrapFileException& e )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY,
+ e.BootstrapFileURL ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ catch( const css::configuration::InstallationIncompleteException& )
+ {
+ OUString aVersionFileURL;
+ OUString aMsg;
+ utl::Bootstrap::PathStatus aPathStatus = utl::Bootstrap::locateVersionFile( aVersionFileURL );
+ if ( aPathStatus == utl::Bootstrap::PATH_EXISTS )
+ aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE_ENTRY, aVersionFileURL );
+ else
+ aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE, aVersionFileURL );
+
+ HandleBootstrapPathErrors( ::utl::Bootstrap::MISSING_USER_INSTALL, aMsg );
+ }
+ catch ( const css::configuration::backend::BackendAccessException& exception)
+ {
+ // [cm122549] It is assumed in this case that the message
+ // coming from InitConfiguration (in fact CreateApplicationConf...)
+ // is suitable for display directly.
+ FatalError( MakeStartupErrorMessage( exception.Message ) );
+ }
+ catch ( const css::configuration::backend::BackendSetupException& exception)
+ {
+ // [cm122549] It is assumed in this case that the message
+ // coming from InitConfiguration (in fact CreateApplicationConf...)
+ // is suitable for display directly.
+ FatalError( MakeStartupErrorMessage( exception.Message ) );
+ }
+ catch ( const css::configuration::CannotLoadConfigurationException& )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
+ OUString() ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ catch( const css::uno::Exception& )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
+ OUString() ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ return false;
+}
+
+void Desktop::FlushConfiguration()
+{
+ css::uno::Reference< css::util::XFlushable >(
+ css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()),
+ css::uno::UNO_QUERY_THROW)->flush();
+}
+
+bool Desktop::InitializeQuickstartMode( const Reference< XComponentContext >& rxContext )
+{
+ try
+ {
+ // the shutdown icon sits in the systray and allows the user to keep
+ // the office instance running for quicker restart
+ // this will only be activated if --quickstart was specified on cmdline
+
+ bool bQuickstart = shouldLaunchQuickstart();
+
+ // Try to instantiate quickstart service. This service is not mandatory, so
+ // do nothing if service is not available
+
+ // #i105753# the following if was invented for performance
+ // unfortunately this broke the Mac behavior which is to always run
+ // in quickstart mode since Mac applications do not usually quit
+ // when the last document closes.
+ // Note that this claim that on macOS we "always run in quickstart mode"
+ // has nothing to do with (quick) *starting* (i.e. starting automatically
+ // when the user logs in), though, but with not quitting when no documents
+ // are open.
+ #ifndef MACOSX
+ if ( bQuickstart )
+ #endif
+ {
+ css::office::Quickstart::createStart(rxContext, bQuickstart);
+ }
+ return true;
+ }
+ catch( const css::uno::Exception& )
+ {
+ return false;
+ }
+}
+
+void Desktop::OverrideSystemSettings( AllSettings& rSettings )
+{
+ if ( !SvtTabAppearanceCfg::IsInitialized () )
+ return;
+
+ StyleSettings hStyleSettings = rSettings.GetStyleSettings();
+ MouseSettings hMouseSettings = rSettings.GetMouseSettings();
+
+ DragFullOptions nDragFullOptions = hStyleSettings.GetDragFullOptions();
+
+ SvtTabAppearanceCfg aAppearanceCfg;
+ DragMode nDragMode = aAppearanceCfg.GetDragMode();
+ switch ( nDragMode )
+ {
+ case DragMode::FullWindow:
+ nDragFullOptions |= DragFullOptions::All;
+ break;
+ case DragMode::Frame:
+ nDragFullOptions &= ~DragFullOptions::All;
+ break;
+ case DragMode::SystemDep:
+ default:
+ break;
+ }
+
+ MouseFollowFlags nFollow = hMouseSettings.GetFollow();
+ hMouseSettings.SetFollow( aAppearanceCfg.IsMenuMouseFollow() ? (nFollow|MouseFollowFlags::Menu) : (nFollow&~MouseFollowFlags::Menu));
+ rSettings.SetMouseSettings(hMouseSettings);
+
+ SvtMenuOptions aMenuOpt;
+ hStyleSettings.SetUseImagesInMenus(aMenuOpt.GetMenuIconsState());
+ hStyleSettings.SetContextMenuShortcuts(aMenuOpt.GetContextMenuShortcuts());
+ hStyleSettings.SetDragFullOptions( nDragFullOptions );
+ rSettings.SetStyleSettings ( hStyleSettings );
+}
+
+namespace {
+
+class ExitTimer : public Timer
+{
+ public:
+ ExitTimer()
+ {
+ SetTimeout(500);
+ Start();
+ }
+ virtual void Invoke() override
+ {
+ _exit(42);
+ }
+};
+
+}
+
+IMPL_LINK_NOARG(Desktop, OpenClients_Impl, void*, void)
+{
+ try {
+ // #i114963#
+ // Enable IPC thread before OpenClients
+ //
+ // This is because it is possible for another client to connect during the OpenClients() call.
+ // This can happen on Windows when document is printed (not opened) and another client wants to print (when printing multiple documents).
+ // If the IPC thread is enabled after OpenClients, then the client will not be processed because the application will exit after printing. i.e RequestHandler::AreRequestsPending() will always return false
+ //
+ // ALSO:
+ //
+ // Multiple clients may request simultaneous connections.
+ // When this server closes down it attempts to recreate the pipe (in RequestHandler::Disable()).
+ // It's possible that the client has a pending connection request.
+ // When the IPC thread is not running, this connection locks (because maPipe.accept()) is never called
+ RequestHandler::SetReady(true);
+ OpenClients();
+
+ CloseSplashScreen();
+ CheckFirstRun( );
+#ifdef _WIN32
+ // Registers a COM class factory of the service manager with the windows operating system.
+ Reference< XMultiServiceFactory > xSMgr= comphelper::getProcessServiceFactory();
+ xSMgr->createInstance("com.sun.star.bridge.OleApplicationRegistration");
+ xSMgr->createInstance("com.sun.star.comp.ole.EmbedServer");
+#endif
+ const char *pExitPostStartup = getenv ("OOO_EXIT_POST_STARTUP");
+ if (pExitPostStartup && *pExitPostStartup)
+ new ExitTimer();
+ } catch (const css::uno::Exception &e) {
+ Application::Abort( "UNO exception during client open: " + e.Message );
+ }
+}
+
+void Desktop::OpenClients()
+{
+
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+
+ if (!rArgs.IsQuickstart())
+ {
+ OUString aHelpModule;
+ if (rArgs.IsHelpWriter()) {
+ aHelpModule = "swriter";
+ } else if (rArgs.IsHelpCalc()) {
+ aHelpModule = "scalc";
+ } else if (rArgs.IsHelpDraw()) {
+ aHelpModule = "sdraw";
+ } else if (rArgs.IsHelpImpress()) {
+ aHelpModule = "simpress";
+ } else if (rArgs.IsHelpBase()) {
+ aHelpModule = "sdatabase";
+ } else if (rArgs.IsHelpBasic()) {
+ aHelpModule = "sbasic";
+ } else if (rArgs.IsHelpMath()) {
+ aHelpModule = "smath";
+ }
+ if (!aHelpModule.isEmpty()) {
+ OUString aHelpURL = "vnd.sun.star.help://"
+ + aHelpModule
+ + "/start?Language="
+ + utl::ConfigManager::getUILocale();
+#if defined UNX
+ aHelpURL += "&System=UNX";
+#elif defined _WIN32
+ aHelpURL += "&System=WIN";
+#endif
+ Application::GetHelp()->Start(aHelpURL, static_cast<const vcl::Window*>(nullptr));
+ return;
+ }
+ }
+
+ // Disable AutoSave feature in case "--norestore" or a similar command line switch is set on the command line.
+ // The reason behind: AutoSave/EmergencySave/AutoRecovery share the same data.
+ // But the require that all documents, which are saved as backup should exists inside
+ // memory. May be this mechanism will be inconsistent if the configuration exists...
+ // but no document inside memory corresponds to this data.
+ // Further it's not acceptable to recover such documents without any UI. It can
+ // need some time, where the user won't see any results and wait for finishing the office startup...
+ bool bAllowRecoveryAndSessionManagement = ( !rArgs.IsNoRestore() ) && ( !rArgs.IsHeadless() );
+
+#if !defined ANDROID
+ // Enter safe mode if requested
+ if (Application::IsSafeModeEnabled()) {
+ handleSafeMode();
+ }
+#endif
+
+#if HAVE_FEATURE_BREAKPAD
+ if (officecfg::Office::Common::Misc::CrashReport::get() && CrashReporter::crashReportInfoExists())
+ handleCrashReport();
+#endif
+
+ if ( ! bAllowRecoveryAndSessionManagement )
+ {
+ try
+ {
+ Reference< XDispatch > xRecovery = css::frame::theAutoRecovery::get( ::comphelper::getProcessComponentContext() );
+ Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
+
+ css::util::URL aCmd;
+ aCmd.Complete = "vnd.sun.star.autorecovery:/disableRecovery";
+ xParser->parseStrict(aCmd);
+
+ xRecovery->dispatch(aCmd, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Could not disable AutoRecovery.");
+ }
+ }
+ else
+ {
+ bool bCrashed = false;
+ bool bExistsRecoveryData = false;
+ bool bExistsSessionData = false;
+ bool const bDisableRecovery
+ = getenv("OOO_DISABLE_RECOVERY") != nullptr
+ || !officecfg::Office::Recovery::RecoveryInfo::Enabled::get();
+
+ impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
+
+ if ( !bDisableRecovery &&
+ (
+ bExistsRecoveryData || // => crash with files => recovery
+ bCrashed // => crash without files => error report
+ )
+ )
+ {
+ try
+ {
+ impl_callRecoveryUI(
+ false , // false => force recovery instead of emergency save
+ bExistsRecoveryData);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Error during recovery");
+ }
+ }
+
+ Reference< XSessionManagerListener2 > xSessionListener;
+ try
+ {
+ // specifies whether the UI-interaction on Session shutdown is allowed
+ bool bUIOnSessionShutdownAllowed = officecfg::Office::Recovery::SessionShutdown::DocumentStoreUIEnabled::get();
+ xSessionListener = SessionListener::createWithOnQuitFlag(
+ ::comphelper::getProcessComponentContext(), bUIOnSessionShutdownAllowed);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Registration of session listener failed");
+ }
+
+ if ( !bExistsRecoveryData && xSessionListener.is() )
+ {
+ // session management
+ try
+ {
+ xSessionListener->doRestore();
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Error in session management");
+ }
+ }
+ }
+
+ // write this information here to avoid depending on vcl in the crash reporter lib
+ CrashReporter::addKeyValue("Language", Application::GetSettings().GetLanguageTag().getBcp47(), CrashReporter::Create);
+
+ RequestHandler::EnableRequests();
+
+ ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
+ aRequest.aOpenList = rArgs.GetOpenList();
+ aRequest.aViewList = rArgs.GetViewList();
+ aRequest.aStartList = rArgs.GetStartList();
+ aRequest.aPrintList = rArgs.GetPrintList();
+ aRequest.aPrintToList = rArgs.GetPrintToList();
+ aRequest.aPrinterName = rArgs.GetPrinterName();
+ aRequest.aForceOpenList = rArgs.GetForceOpenList();
+ aRequest.aForceNewList = rArgs.GetForceNewList();
+ aRequest.aConversionList = rArgs.GetConversionList();
+ aRequest.aConversionParams = rArgs.GetConversionParams();
+ aRequest.aConversionOut = rArgs.GetConversionOut();
+ aRequest.aImageConversionType = rArgs.GetImageConversionType();
+ aRequest.aInFilter = rArgs.GetInFilter();
+ aRequest.bTextCat = rArgs.IsTextCat();
+ aRequest.bScriptCat = rArgs.IsScriptCat();
+
+ if ( !aRequest.aOpenList.empty() ||
+ !aRequest.aViewList.empty() ||
+ !aRequest.aStartList.empty() ||
+ !aRequest.aPrintList.empty() ||
+ !aRequest.aForceOpenList.empty() ||
+ !aRequest.aForceNewList.empty() ||
+ ( !aRequest.aPrintToList.empty() && !aRequest.aPrinterName.isEmpty() ) ||
+ !aRequest.aConversionList.empty() )
+ {
+ if ( rArgs.HasModuleParam() )
+ {
+ SvtModuleOptions aOpt;
+
+ // Support command line parameters to start a module (as preselection)
+ if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
+ else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
+ else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
+ }
+
+ // check for printing disabled
+ if( ( !(aRequest.aPrintList.empty() && aRequest.aPrintToList.empty()) )
+ && Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
+ {
+ aRequest.aPrintList.clear();
+ aRequest.aPrintToList.clear();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ DpResId(STR_ERR_PRINTDISABLED)));
+ xBox->run();
+ }
+
+ // Process request
+ if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) )
+ {
+ // Don't do anything if we have successfully called terminate at desktop:
+ return;
+ }
+ }
+
+ // no default document if a document was loaded by recovery or by command line or if soffice is used as server
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XElementAccess > xList( xDesktop->getFrames(), UNO_QUERY_THROW );
+ if ( xList->hasElements() )
+ return;
+
+ if ( rArgs.IsQuickstart() || rArgs.IsInvisible() || Application::AnyInput( VclInputFlags::APPEVENT ) )
+ // soffice was started as tray icon ...
+ return;
+
+ OpenDefault();
+}
+
+void Desktop::OpenDefault()
+{
+ OUString aName;
+ SvtModuleOptions aOpt;
+
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+ if ( rArgs.IsNoDefault() ) return;
+ if ( rArgs.HasModuleParam() )
+ {
+ // Support new command line parameters to start a module
+ if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
+ else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
+ else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( rArgs.IsBase() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
+ else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
+ else if ( rArgs.IsMath() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::MATH );
+ else if ( rArgs.IsGlobal() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERGLOBAL );
+ else if ( rArgs.IsWeb() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERWEB );
+ }
+
+ if ( aName.isEmpty() )
+ {
+ if (aOpt.IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
+ {
+ ShowBackingComponent(nullptr);
+ return;
+ }
+
+ // Old way to create a default document
+ if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
+ else
+ return;
+ }
+
+ ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
+ aRequest.aOpenList.push_back(aName);
+ RequestHandler::ExecuteCmdLineRequests(aRequest, false);
+}
+
+
+OUString GetURL_Impl(
+ const OUString& rName, std::optional< OUString > const & cwdUrl )
+{
+ // if rName is a vnd.sun.star.script URL do not attempt to parse it
+ // as INetURLObj does not handle URLs there
+ if (rName.startsWith("vnd.sun.star.script"))
+ {
+ return rName;
+ }
+
+ // don't touch file urls, those should already be in internal form
+ // they won't get better here (#112849#)
+ if (comphelper::isFileUrl(rName))
+ {
+ return rName;
+ }
+
+ if ( rName.startsWith("service:"))
+ {
+ return rName;
+ }
+
+ // Add path separator to these directory and make given URL (rName) absolute by using of current working directory
+ // Attention: "setFinalSlash()" is necessary for calling "smartRel2Abs()"!!!
+ // Otherwise last part will be ignored and wrong result will be returned!!!
+ // "smartRel2Abs()" interpret given URL as file not as path. So he truncate last element to get the base path ...
+ // But if we add a separator - he doesn't do it anymore.
+ INetURLObject aObj;
+ if (cwdUrl) {
+ aObj.SetURL(*cwdUrl);
+ aObj.setFinalSlash();
+ }
+
+ // Use the provided parameters for smartRel2Abs to support the usage of '%' in system paths.
+ // Otherwise this char won't get encoded and we are not able to load such files later,
+ bool bWasAbsolute;
+ INetURLObject aURL = aObj.smartRel2Abs( rName, bWasAbsolute, false, INetURLObject::EncodeMechanism::WasEncoded,
+ RTL_TEXTENCODING_UTF8, true );
+ OUString aFileURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ ::osl::FileStatus aStatus( osl_FileStatus_Mask_FileURL );
+ ::osl::DirectoryItem aItem;
+ if( ::osl::FileBase::E_None == ::osl::DirectoryItem::get( aFileURL, aItem ) &&
+ ::osl::FileBase::E_None == aItem.getFileStatus( aStatus ) )
+ aFileURL = aStatus.getFileURL();
+
+ return aFileURL;
+}
+
+void Desktop::HandleAppEvent( const ApplicationEvent& rAppEvent )
+{
+ switch ( rAppEvent.GetEvent() )
+ {
+ case ApplicationEvent::Type::Accept:
+ // every time an accept parameter is used we create an acceptor
+ // with the corresponding accept-string
+ createAcceptor(rAppEvent.GetStringData());
+ break;
+ case ApplicationEvent::Type::Appear:
+ if ( !GetCommandLineArgs().IsInvisible() && !impl_bringToFrontRecoveryUI() )
+ {
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ // find active task - the active task is always a visible task
+ Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+ Reference< css::frame::XFrame > xTask = xDesktop->getActiveFrame();
+ if ( !xTask.is() )
+ {
+ // get any task if there is no active one
+ Reference< css::container::XIndexAccess > xList = xDesktop->getFrames();
+ if ( xList->getCount() > 0 )
+ xList->getByIndex(0) >>= xTask;
+ }
+
+ if ( xTask.is() )
+ {
+ Reference< css::awt::XTopWindow > xTop( xTask->getContainerWindow(), UNO_QUERY );
+ xTop->toFront();
+ }
+ else
+ {
+ // no visible task that could be activated found
+ Reference< css::awt::XWindow > xContainerWindow;
+ Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
+ if (xBackingFrame.is())
+ xContainerWindow = xBackingFrame->getContainerWindow();
+ if (xContainerWindow.is())
+ {
+ Reference< XController > xStartModule = StartModule::createWithParentWindow(xContext, xContainerWindow);
+ Reference< css::awt::XWindow > xBackingWin(xStartModule, UNO_QUERY);
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ // Because the backing component set the property "IsBackingMode" of the frame
+ // to true inside attachFrame(). But setComponent() reset this state every time ...
+ xBackingFrame->setComponent(xBackingWin, xStartModule);
+ xStartModule->attachFrame(xBackingFrame);
+ xContainerWindow->setVisible(true);
+
+ VclPtr<vcl::Window> pCompWindow = VCLUnoHelper::GetWindow(xBackingFrame->getComponentWindow());
+ if (pCompWindow)
+ pCompWindow->PaintImmediately();
+ }
+ }
+ }
+ break;
+ case ApplicationEvent::Type::Open:
+ {
+ const CommandLineArgs& rCmdLine = GetCommandLineArgs();
+ if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
+ {
+ ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
+ std::vector<OUString> const & data(rAppEvent.GetStringsData());
+ docsRequest.aOpenList.insert(
+ docsRequest.aOpenList.end(), data.begin(), data.end());
+ RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
+ }
+ }
+ break;
+ case ApplicationEvent::Type::OpenHelpUrl:
+ // start help for a specific URL
+ Application::GetHelp()->Start(rAppEvent.GetStringData(), static_cast<vcl::Window*>(nullptr));
+ break;
+ case ApplicationEvent::Type::Print:
+ {
+ const CommandLineArgs& rCmdLine = GetCommandLineArgs();
+ if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
+ {
+ ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
+ std::vector<OUString> const & data(rAppEvent.GetStringsData());
+ docsRequest.aPrintList.insert(
+ docsRequest.aPrintList.end(), data.begin(), data.end());
+ RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
+ }
+ }
+ break;
+ case ApplicationEvent::Type::PrivateDoShutdown:
+ {
+ Desktop* pD = dynamic_cast<Desktop*>(GetpApp());
+ OSL_ENSURE( pD, "no desktop ?!?" );
+ if( pD )
+ pD->doShutdown();
+ }
+ break;
+ case ApplicationEvent::Type::QuickStart:
+ if ( !GetCommandLineArgs().IsInvisible() )
+ {
+ // If the office has been started the second time its command line arguments are sent through a pipe
+ // connection to the first office. We want to reuse the quickstart option for the first office.
+ // NOTICE: The quickstart service must be initialized inside the "main thread", so we use the
+ // application events to do this (they are executed inside main thread)!!!
+ // Don't start quickstart service if the user specified "--invisible" on the command line!
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::office::Quickstart::createStart(xContext, true/*Quickstart*/);
+ }
+ break;
+ case ApplicationEvent::Type::ShowDialog:
+ // ignore all errors here. It's clicking a menu entry only ...
+ // The user will try it again, in case nothing happens .-)
+ try
+ {
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+
+ Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext);
+ css::util::URL aCommand;
+ if( rAppEvent.GetStringData() == "PREFERENCES" )
+ aCommand.Complete = ".uno:OptionsTreeDialog";
+ else if( rAppEvent.GetStringData() == "ABOUT" )
+ aCommand.Complete = ".uno:About";
+ if( !aCommand.Complete.isEmpty() )
+ {
+ xParser->parseStrict(aCommand);
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch = xDesktop->queryDispatch(aCommand, OUString(), 0);
+ if (xDispatch.is())
+ xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ }
+ catch(const css::uno::Exception&)
+ {}
+ break;
+ case ApplicationEvent::Type::Unaccept:
+ // try to remove corresponding acceptor
+ destroyAcceptor(rAppEvent.GetStringData());
+ break;
+ default:
+ SAL_WARN( "desktop.app", "this cannot happen");
+ break;
+ }
+}
+
+void Desktop::OpenSplashScreen()
+{
+ const CommandLineArgs &rCmdLine = GetCommandLineArgs();
+ // Show intro only if this is normal start (e.g. no server, no quickstart, no printing )
+ if ( !(!rCmdLine.IsInvisible() &&
+ !rCmdLine.IsHeadless() &&
+ !rCmdLine.IsQuickstart() &&
+ !rCmdLine.IsMinimized() &&
+ !rCmdLine.IsNoLogo() &&
+ !rCmdLine.IsTerminateAfterInit() &&
+ rCmdLine.GetPrintList().empty() &&
+ rCmdLine.GetPrintToList().empty() &&
+ rCmdLine.GetConversionList().empty()) )
+ return;
+
+ // Determine application name from command line parameters
+ OUString aAppName;
+ if ( rCmdLine.IsWriter() )
+ aAppName = "writer";
+ else if ( rCmdLine.IsCalc() )
+ aAppName = "calc";
+ else if ( rCmdLine.IsDraw() )
+ aAppName = "draw";
+ else if ( rCmdLine.IsImpress() )
+ aAppName = "impress";
+ else if ( rCmdLine.IsBase() )
+ aAppName = "base";
+ else if ( rCmdLine.IsGlobal() )
+ aAppName = "global";
+ else if ( rCmdLine.IsMath() )
+ aAppName = "math";
+ else if ( rCmdLine.IsWeb() )
+ aAppName = "web";
+
+ // Which splash to use
+ OUString aSplashService( "com.sun.star.office.SplashScreen" );
+ if ( rCmdLine.HasSplashPipe() )
+ aSplashService = "com.sun.star.office.PipeSplashScreen";
+
+ Sequence< Any > aSeq( 2 );
+ aSeq[0] <<= true; // bVisible
+ aSeq[1] <<= aAppName;
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ m_rSplashScreen.set(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aSplashService, aSeq, xContext),
+ UNO_QUERY);
+
+ if(m_rSplashScreen.is())
+ m_rSplashScreen->start("SplashScreen", 100);
+
+}
+
+void Desktop::SetSplashScreenProgress(sal_Int32 iProgress)
+{
+ if(m_rSplashScreen.is())
+ {
+ m_rSplashScreen->setValue(iProgress);
+ }
+}
+
+void Desktop::SetSplashScreenText( const OUString& rText )
+{
+ if( m_rSplashScreen.is() )
+ {
+ m_rSplashScreen->setText( rText );
+ }
+}
+
+void Desktop::CloseSplashScreen()
+{
+ if(m_rSplashScreen.is())
+ {
+ m_rSplashScreen->end();
+ m_rSplashScreen = nullptr;
+ }
+}
+
+
+IMPL_STATIC_LINK_NOARG(Desktop, AsyncInitFirstRun, Timer *, void)
+{
+ // does initializations which are necessary for the first run of the office
+ try
+ {
+ Reference< XJobExecutor > xExecutor = theJobExecutor::get( ::comphelper::getProcessComponentContext() );
+ xExecutor->trigger( "onFirstRunInitialization" );
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Desktop::DoFirstRunInitializations: caught an exception while trigger job executor" );
+ }
+}
+
+void Desktop::ShowBackingComponent(Desktop * progress)
+{
+ if (GetCommandLineArgs().IsNoDefault())
+ {
+ return;
+ }
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(60);
+ }
+ Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
+ Reference< css::awt::XWindow > xContainerWindow;
+
+ if (xBackingFrame.is())
+ xContainerWindow = xBackingFrame->getContainerWindow();
+ if (!xContainerWindow.is())
+ return;
+
+ // set the WindowExtendedStyle::Document style. Normally, this is done by the TaskCreator service when a "_blank"
+ // frame/window is created. Since we do not use the TaskCreator here, we need to mimic its behavior,
+ // otherwise documents loaded into this frame will later on miss functionality depending on the style.
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ SAL_WARN_IF( !pContainerWindow, "desktop.app", "Desktop::Main: no implementation access to the frame's container window!" );
+ pContainerWindow->SetExtendedStyle( pContainerWindow->GetExtendedStyle() | WindowExtendedStyle::Document );
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(75);
+ }
+
+ Reference< XController > xStartModule = StartModule::createWithParentWindow( xContext, xContainerWindow);
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ // Because the backing component set the property "IsBackingMode" of the frame
+ // to true inside attachFrame(). But setComponent() reset this state everytimes ...
+ xBackingFrame->setComponent(Reference< XWindow >(xStartModule, UNO_QUERY), xStartModule);
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(100);
+ }
+ xStartModule->attachFrame(xBackingFrame);
+ if (progress != nullptr)
+ {
+ progress->CloseSplashScreen();
+ }
+ xContainerWindow->setVisible(true);
+}
+
+
+void Desktop::CheckFirstRun( )
+{
+ if (!officecfg::Office::Common::Misc::FirstRun::get())
+ return;
+
+ // use VCL timer, which won't trigger during shutdown if the
+ // application exits before timeout
+ m_firstRunTimer.Start();
+
+#ifdef _WIN32
+ // Check if Quickstarter should be started (on Windows only)
+ OUString sRootKey = ReplaceStringHookProc("Software\\%OOOVENDOR\\%PRODUCTNAME\\%PRODUCTVERSION");
+ WCHAR szValue[8192];
+ DWORD nValueSize = sizeof(szValue);
+ HKEY hKey;
+ if (ERROR_SUCCESS == RegOpenKeyW(HKEY_LOCAL_MACHINE, o3tl::toW(sRootKey.getStr()), &hKey))
+ {
+ if ( ERROR_SUCCESS == RegQueryValueExW( hKey, L"RunQuickstartAtFirstStart", nullptr, nullptr, reinterpret_cast<LPBYTE>(szValue), &nValueSize ) )
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::office::Quickstart::createAutoStart(xContext, true/*Quickstart*/, true/*bAutostart*/);
+ RegCloseKey( hKey );
+ }
+ }
+#endif
+
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::FirstRun::set(false, batch);
+ batch->commit();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/appinit.cxx b/desktop/source/app/appinit.cxx
new file mode 100644
index 000000000..cff0a684c
--- /dev/null
+++ b/desktop/source/app/appinit.cxx
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <algorithm>
+
+#include <app.hxx>
+#include <dp_shared.hxx>
+#include "cmdlineargs.hxx"
+#include <strings.hrc>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <cppuhelper/bootstrap.hxx>
+#include <officecfg/Setup.hxx>
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <rtl/instance.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/tempfile.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/pathoptions.hxx>
+#include <map>
+
+using namespace desktop;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::registry;
+using namespace ::com::sun::star::ucb;
+
+namespace desktop
+{
+
+
+static void configureUcb()
+{
+ // For backwards compatibility, in case some code still uses plain
+ // createInstance w/o args directly to obtain an instance:
+ UniversalContentBroker::create(comphelper::getProcessComponentContext());
+}
+
+void Desktop::InitApplicationServiceManager()
+{
+ Reference<XMultiServiceFactory> sm;
+#ifdef ANDROID
+ OUString aUnoRc( "file:///assets/program/unorc" );
+ sm.set(
+ cppu::defaultBootstrap_InitialComponentContext( aUnoRc )->getServiceManager(),
+ UNO_QUERY_THROW);
+#elif defined(IOS)
+ OUString uri( "$APP_DATA_DIR" );
+ rtl_bootstrap_expandMacros( &uri.pData );
+ OUString aUnoRc("file://" + uri + "/unorc");
+ sm.set(
+ cppu::defaultBootstrap_InitialComponentContext( aUnoRc )->getServiceManager(),
+ UNO_QUERY_THROW);
+#else
+ sm.set(
+ cppu::defaultBootstrap_InitialComponentContext()->getServiceManager(),
+ UNO_QUERY_THROW);
+#endif
+ comphelper::setProcessServiceFactory(sm);
+}
+
+void Desktop::RegisterServices(Reference< XComponentContext > const & context)
+{
+ if( m_bServicesRegistered )
+ return;
+
+ // interpret command line arguments
+ CommandLineArgs& rCmdLine = GetCommandLineArgs();
+
+ // Headless mode for FAT Office, auto cancels any dialogs that popup
+ if (rCmdLine.IsEventTesting())
+ Application::EnableEventTestingMode();
+ else if (rCmdLine.IsHeadless())
+ Application::EnableHeadlessMode(false);
+
+ // read accept string from configuration
+ OUString conDcpCfg(
+ officecfg::Setup::Office::ooSetupConnectionURL::get(context));
+ if (!conDcpCfg.isEmpty()) {
+ createAcceptor(conDcpCfg);
+ }
+
+ std::vector< OUString > const & conDcp = rCmdLine.GetAccept();
+ for (auto const& elem : conDcp)
+ {
+ createAcceptor(elem);
+ }
+
+ configureUcb();
+
+ CreateTemporaryDirectory();
+ m_bServicesRegistered = true;
+}
+
+typedef std::map< OUString, css::uno::Reference<css::lang::XInitialization> > AcceptorMap;
+
+namespace
+{
+ struct acceptorMap : public rtl::Static< AcceptorMap, acceptorMap > {};
+ struct CurrentTempURL : public rtl::Static< OUString, CurrentTempURL > {};
+}
+
+static bool bAccept = false;
+
+void Desktop::createAcceptor(const OUString& aAcceptString)
+{
+ // check whether the requested acceptor already exists
+ AcceptorMap &rMap = acceptorMap::get();
+ AcceptorMap::const_iterator pIter = rMap.find(aAcceptString);
+ if (pIter != rMap.end() )
+ return;
+
+ Sequence< Any > aSeq( 2 );
+ aSeq[0] <<= aAcceptString;
+ aSeq[1] <<= bAccept;
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference<XInitialization> rAcceptor(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.office.Acceptor", xContext),
+ UNO_QUERY );
+ if ( rAcceptor.is() )
+ {
+ try
+ {
+ rAcceptor->initialize( aSeq );
+ rMap.emplace(aAcceptString, rAcceptor);
+ }
+ catch (const css::uno::Exception&)
+ {
+ // no error handling needed...
+ // acceptor just won't come up
+ TOOLS_WARN_EXCEPTION( "desktop.app", "Acceptor could not be created");
+ }
+ }
+ else
+ {
+ // there is already an acceptor with this description
+ SAL_WARN( "desktop.app", "Acceptor already exists.");
+ }
+}
+
+namespace {
+
+class enable
+{
+ private:
+ Sequence<Any> m_aSeq;
+ public:
+ enable() : m_aSeq(1) {
+ m_aSeq[0] <<= true;
+ }
+ void operator() (const AcceptorMap::value_type& val) {
+ if (val.second.is()) {
+ val.second->initialize(m_aSeq);
+ }
+ }
+};
+
+}
+
+// enable acceptors
+IMPL_STATIC_LINK_NOARG(Desktop, EnableAcceptors_Impl, void*, void)
+{
+ if (!bAccept)
+ {
+ // from now on, all new acceptors are enabled
+ bAccept = true;
+ // enable existing acceptors by calling initialize(true)
+ // on all existing acceptors
+ AcceptorMap &rMap = acceptorMap::get();
+ std::for_each(rMap.begin(), rMap.end(), enable());
+ }
+}
+
+void Desktop::destroyAcceptor(const OUString& aAcceptString)
+{
+ // special case stop all acceptors
+ AcceptorMap &rMap = acceptorMap::get();
+ if (aAcceptString == "all") {
+ rMap.clear();
+
+ } else {
+ // try to remove acceptor from map
+ AcceptorMap::const_iterator pIter = rMap.find(aAcceptString);
+ if (pIter != rMap.end() ) {
+ // remove reference from map
+ // this is the last reference and the acceptor will be destructed
+ rMap.erase(aAcceptString);
+ } else {
+ SAL_WARN( "desktop.app", "Found no acceptor to remove");
+ }
+ }
+}
+
+
+void Desktop::DeregisterServices()
+{
+ // stop all acceptors by clearing the map
+ acceptorMap::get().clear();
+}
+
+void Desktop::CreateTemporaryDirectory()
+{
+ OUString aTempBaseURL;
+ try
+ {
+ SvtPathOptions aOpt;
+ aTempBaseURL = aOpt.GetTempPath();
+ }
+ catch (RuntimeException& e)
+ {
+ // Catch runtime exception here: We have to add language dependent info
+ // to the exception message. Fallback solution uses hard coded string.
+ OUString aMsg = DpResId(STR_BOOTSTRAP_ERR_NO_PATHSET_SERVICE);
+ e.Message = aMsg + e.Message;
+ throw;
+ }
+
+ // create new current temporary directory
+ OUString aTempPath = ::utl::TempFile::SetTempNameBaseDirectory( aTempBaseURL );
+ if ( aTempPath.isEmpty()
+ && ::osl::File::getTempDirURL( aTempBaseURL ) == osl::FileBase::E_None )
+ {
+ aTempPath = ::utl::TempFile::SetTempNameBaseDirectory( aTempBaseURL );
+ }
+
+ // set new current temporary directory
+ OUString aRet;
+ if (osl::FileBase::getFileURLFromSystemPath( aTempPath, aRet )
+ != osl::FileBase::E_None)
+ {
+ aRet.clear();
+ }
+ CurrentTempURL::get() = aRet;
+}
+
+void Desktop::RemoveTemporaryDirectory()
+{
+ // remove current temporary directory
+ OUString &rCurrentTempURL = CurrentTempURL::get();
+ if ( !rCurrentTempURL.isEmpty() )
+ {
+ ::utl::UCBContentHelper::Kill( rCurrentTempURL );
+ }
+}
+
+} // namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/check_ext_deps.cxx b/desktop/source/app/check_ext_deps.cxx
new file mode 100644
index 000000000..cb32e3b03
--- /dev/null
+++ b/desktop/source/app/check_ext_deps.cxx
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+#include <config_features.h>
+
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/ui/LicenseDialog.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+
+#include <app.hxx>
+
+#include <dp_misc.h>
+
+using namespace desktop;
+using namespace com::sun::star;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::task;
+using namespace com::sun::star::uno;
+
+namespace
+{
+//For use with XExtensionManager.synchronize
+class SilentCommandEnv
+ : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
+ task::XInteractionHandler,
+ ucb::XProgressHandler >
+{
+ uno::Reference<uno::XComponentContext> mxContext;
+ Desktop *mpDesktop;
+ sal_Int32 mnLevel;
+ sal_Int32 mnProgress;
+
+public:
+ SilentCommandEnv(
+ uno::Reference<uno::XComponentContext> const & xContext,
+ Desktop* pDesktop );
+ virtual ~SilentCommandEnv() override;
+
+ // XCommandEnvironment
+ virtual uno::Reference<task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual uno::Reference<ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ uno::Reference<task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( uno::Any const & Status ) override;
+ virtual void SAL_CALL update( uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+SilentCommandEnv::SilentCommandEnv(
+ uno::Reference<uno::XComponentContext> const & xContext,
+ Desktop* pDesktop ):
+ mxContext( xContext ),
+ mpDesktop( pDesktop ),
+ mnLevel( 0 ),
+ mnProgress( 25 )
+{}
+
+
+SilentCommandEnv::~SilentCommandEnv()
+{
+ if (mpDesktop)
+ mpDesktop->SetSplashScreenText(OUString());
+}
+
+
+Reference<task::XInteractionHandler> SilentCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference<ucb::XProgressHandler> SilentCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+
+// XInteractionHandler
+void SilentCommandEnv::handle( Reference< task::XInteractionRequest> const & xRequest )
+{
+ deployment::LicenseException licExc;
+
+ uno::Any request( xRequest->getRequest() );
+ bool bApprove = true;
+
+ if ( request >>= licExc )
+ {
+ uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
+ deployment::ui::LicenseDialog::create(
+ mxContext, VCLUnoHelper::GetInterface( nullptr ),
+ licExc.ExtensionName, licExc.Text ) );
+ sal_Int16 res = xDialog->execute();
+ if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
+ bApprove = false;
+ else if ( res == ui::dialogs::ExecutableDialogResults::OK )
+ bApprove = true;
+ else
+ {
+ OSL_ASSERT(false);
+ }
+ }
+
+ // We approve everything here
+ uno::Sequence< Reference< task::XInteractionContinuation > > conts( xRequest->getContinuations() );
+ Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if ( bApprove )
+ {
+ uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
+ if ( xInteractionApprove.is() )
+ xInteractionApprove->select();
+ }
+ else
+ {
+ uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
+ if ( xInteractionAbort.is() )
+ xInteractionAbort->select();
+ }
+ }
+}
+
+
+// XProgressHandler
+void SilentCommandEnv::push( uno::Any const & rStatus )
+{
+ OUString sText;
+ mnLevel += 1;
+
+ if (mpDesktop && rStatus.hasValue() && (rStatus >>= sText))
+ {
+ if ( mnLevel <= 3 )
+ mpDesktop->SetSplashScreenText( sText );
+ else
+ mpDesktop->SetSplashScreenProgress( ++mnProgress );
+ }
+}
+
+
+void SilentCommandEnv::update( uno::Any const & rStatus )
+{
+ OUString sText;
+ if (mpDesktop && rStatus.hasValue() && (rStatus >>= sText))
+ {
+ mpDesktop->SetSplashScreenText( sText );
+ }
+}
+
+
+void SilentCommandEnv::pop()
+{
+ mnLevel -= 1;
+}
+
+} // end namespace
+
+
+static const char aAccessSrvc[] = "com.sun.star.configuration.ConfigurationUpdateAccess";
+
+static sal_Int16 impl_showExtensionDialog( uno::Reference< uno::XComponentContext > const &xContext )
+{
+ OUString sServiceName = "com.sun.star.deployment.ui.UpdateRequiredDialog";
+ uno::Reference< uno::XInterface > xService;
+ sal_Int16 nRet = 0;
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager( xContext->getServiceManager() );
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException(
+ "impl_showExtensionDialog(): unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
+
+ xService = xServiceManager->createInstanceWithContext( sServiceName, xContext );
+ uno::Reference< ui::dialogs::XExecutableDialog > xExecuteable( xService, uno::UNO_QUERY );
+ if ( xExecuteable.is() )
+ nRet = xExecuteable->execute();
+
+ return nRet;
+}
+
+
+// Check dependencies of all packages
+
+static bool impl_checkDependencies( const uno::Reference< uno::XComponentContext > &xContext )
+{
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+ uno::Reference< deployment::XExtensionManager > xExtensionManager = deployment::ExtensionManager::get( xContext );
+
+ if ( !xExtensionManager.is() )
+ {
+ SAL_WARN( "desktop.app", "Could not get the Extension Manager!" );
+ return true;
+ }
+
+ try {
+ xAllPackages = xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException & ) { return true; }
+ catch ( const ucb::CommandFailedException & ) { return true; }
+ catch ( const ucb::CommandAbortedException & ) { return true; }
+ catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+#ifdef DEBUG
+ sal_Int32 const nMax = 3;
+#else
+ sal_Int32 const nMax = 2;
+#endif
+
+ for ( uno::Sequence< uno::Reference< deployment::XPackage > > const & xPackageList : std::as_const(xAllPackages) )
+ {
+ for ( sal_Int32 j = 0; (j<nMax) && (j < xPackageList.getLength()); ++j )
+ {
+ uno::Reference< deployment::XPackage > xPackage = xPackageList[j];
+ if ( xPackage.is() )
+ {
+ bool bRegistered = false;
+ try {
+ beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() ) );
+ if ( option.IsPresent )
+ {
+ ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
+ if ( reg.IsAmbiguous )
+ bRegistered = false;
+ else
+ bRegistered = reg.Value;
+ }
+ else
+ bRegistered = false;
+ }
+ catch ( const uno::RuntimeException & ) { throw; }
+ catch (const uno::Exception & ) {
+ TOOLS_WARN_EXCEPTION( "desktop.app", "" );
+ }
+
+ if ( bRegistered )
+ {
+ bool bDependenciesValid = false;
+ try {
+ bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException & ) {}
+ if ( ! bDependenciesValid )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+// resets the 'check needed' flag (needed, if aborted)
+
+static void impl_setNeedsCompatCheck()
+{
+ try {
+ Reference< XMultiServiceFactory > theConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext() ) );
+
+ Sequence< Any > theArgs(1);
+ beans::NamedValue v( "nodepath",
+ makeAny( OUString("org.openoffice.Setup/Office") ) );
+ theArgs[0] <<= v;
+ Reference< beans::XPropertySet > pset(
+ theConfigProvider->createInstanceWithArguments( aAccessSrvc, theArgs ), UNO_QUERY_THROW );
+
+ Any value = makeAny( OUString("never") );
+
+ pset->setPropertyValue("LastCompatibilityCheckID", value );
+ Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
+ }
+ catch (const Exception&) {}
+}
+
+
+// to check if we need checking the dependencies of the extensions again, we compare
+// the build id of the office with the one of the last check
+
+static bool impl_needsCompatCheck()
+{
+ bool bNeedsCheck = false;
+ OUString aLastCheckBuildID;
+ OUString aCurrentBuildID( "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}" );
+ rtl::Bootstrap::expandMacros( aCurrentBuildID );
+
+ try {
+ Reference< XMultiServiceFactory > theConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext() ) );
+
+ Sequence< Any > theArgs(1);
+ beans::NamedValue v( "nodepath",
+ makeAny( OUString("org.openoffice.Setup/Office") ) );
+ theArgs[0] <<= v;
+ Reference< beans::XPropertySet > pset(
+ theConfigProvider->createInstanceWithArguments( aAccessSrvc, theArgs ), UNO_QUERY_THROW );
+
+ Any result = pset->getPropertyValue("LastCompatibilityCheckID");
+
+ result >>= aLastCheckBuildID;
+ if ( aLastCheckBuildID != aCurrentBuildID )
+ {
+ bNeedsCheck = true;
+ result <<= aCurrentBuildID;
+ pset->setPropertyValue("LastCompatibilityCheckID", result );
+ Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
+ }
+#ifdef DEBUG
+ bNeedsCheck = true;
+#endif
+ }
+ catch (const css::uno::Exception&) {}
+
+ return bNeedsCheck;
+}
+
+
+// Do we need to check the dependencies of the extensions?
+// When there are unresolved issues, we can't continue with startup
+bool Desktop::CheckExtensionDependencies()
+{
+ if (!impl_needsCompatCheck())
+ {
+ return false;
+ }
+
+ uno::Reference< uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext());
+
+ bool bDependenciesValid = impl_checkDependencies( xContext );
+
+ short nRet = 0;
+
+ if ( !bDependenciesValid )
+ nRet = impl_showExtensionDialog( xContext );
+
+ if ( nRet == -1 )
+ {
+ impl_setNeedsCompatCheck();
+ return true;
+ }
+ else
+ return false;
+}
+
+void Desktop::SynchronizeExtensionRepositories(bool bCleanedExtensionCache, Desktop* pDesktop)
+{
+ uno::Reference< uno::XComponentContext > context(
+ comphelper::getProcessComponentContext());
+ uno::Reference< ucb::XCommandEnvironment > silent(
+ new SilentCommandEnv(context, pDesktop));
+ if (bCleanedExtensionCache) {
+ deployment::ExtensionManager::get(context)->reinstallDeployedExtensions(
+ true, "user", Reference<task::XAbortChannel>(), silent);
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if (!comphelper::LibreOfficeKit::isActive())
+ task::OfficeRestartManager::get(context)->requestRestart(
+ silent->getInteractionHandler());
+#endif
+ } else {
+ // reinstallDeployedExtensions above already calls syncRepositories internally
+
+ // Force syncing repositories on startup. There are cases where the extension
+ // registration becomes invalid which leads to extensions not starting up, although
+ // installed and active. Syncing extension repos on startup fixes that.
+ dp_misc::syncRepositories(/*force=*/true, silent);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/cmdlineargs.cxx b/desktop/source/app/cmdlineargs.cxx
new file mode 100644
index 000000000..e58ee2f6e
--- /dev/null
+++ b/desktop/source/app/cmdlineargs.cxx
@@ -0,0 +1,787 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+#include <premac.h>
+#include <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+#include "cmdlineargs.hxx"
+#include <osl/thread.hxx>
+#include <tools/stream.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/process.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
+#include <unotools/bootstrap.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uri;
+using namespace com::sun::star::uno;
+
+namespace desktop
+{
+
+namespace {
+
+OUString translateExternalUris(OUString const & input) {
+ OUString t(
+ css::uri::ExternalUriReferenceTranslator::create(
+ comphelper::getProcessComponentContext())->
+ translateToInternal(input));
+ return t.isEmpty() ? input : t;
+}
+
+std::vector< OUString > translateExternalUris(
+ std::vector< OUString > const & input)
+{
+ std::vector< OUString > t;
+ t.reserve(input.size());
+ for (auto const& elem : input)
+ {
+ t.push_back(translateExternalUris(elem));
+ }
+ return t;
+}
+
+class ExtCommandLineSupplier: public CommandLineArgs::Supplier {
+public:
+ explicit ExtCommandLineSupplier():
+ m_count(
+ comphelper::LibreOfficeKit::isActive()
+ ? 0 : rtl_getAppCommandArgCount()),
+ m_index(0)
+ {
+ OUString url;
+ if (utl::Bootstrap::getProcessWorkingDir(url)) {
+ m_cwdUrl = url;
+ }
+ }
+
+ virtual std::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
+
+ virtual bool next(OUString * argument) override {
+ OSL_ASSERT(argument != nullptr);
+ if (m_index < m_count) {
+ rtl_getAppCommandArg(m_index++, &argument->pData);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+private:
+ std::optional< OUString > m_cwdUrl;
+ sal_uInt32 m_count;
+ sal_uInt32 m_index;
+};
+
+enum class CommandLineEvent {
+ Open, Print, View, Start, PrintTo,
+ ForceOpen, ForceNew, Conversion, BatchPrint
+};
+
+// Office URI Schemes: see https://msdn.microsoft.com/en-us/library/dn906146
+// This functions checks if the arg is an Office URI.
+// If applicable, it updates arg to inner URI.
+// If no event argument is explicitly set in command line,
+// then it returns updated command line event,
+// according to Office URI command.
+CommandLineEvent CheckOfficeURI(/* in,out */ OUString& arg, CommandLineEvent curEvt)
+{
+ // 1. Strip the scheme name
+ OUString rest1;
+ bool isOfficeURI = ( arg.startsWithIgnoreAsciiCase("vnd.libreoffice.command:", &rest1) // Proposed extended schema
+ || arg.startsWithIgnoreAsciiCase("ms-word:", &rest1)
+ || arg.startsWithIgnoreAsciiCase("ms-powerpoint:", &rest1)
+ || arg.startsWithIgnoreAsciiCase("ms-excel:", &rest1)
+ || arg.startsWithIgnoreAsciiCase("ms-visio:", &rest1)
+ || arg.startsWithIgnoreAsciiCase("ms-access:", &rest1));
+ if (!isOfficeURI)
+ return curEvt;
+
+ OUString rest2;
+ long nURIlen = -1;
+
+ // URL might be encoded
+ OUString decoded_rest = rest1.replaceAll("%7C", "|").replaceAll("%7c", "|");
+
+ // 2. Discriminate by command name (incl. 1st command argument descriptor)
+ // Extract URI: everything up to possible next argument
+ if (decoded_rest.startsWith("ofv|u|", &rest2))
+ {
+ // Open for view - override only in default mode
+ if (curEvt == CommandLineEvent::Open)
+ curEvt = CommandLineEvent::View;
+ nURIlen = rest2.indexOf("|");
+ }
+ else if (decoded_rest.startsWith("ofe|u|", &rest2))
+ {
+ // Open for editing - override only in default mode
+ if (curEvt == CommandLineEvent::Open)
+ curEvt = CommandLineEvent::ForceOpen;
+ nURIlen = rest2.indexOf("|");
+ }
+ else if (decoded_rest.startsWith("nft|u|", &rest2))
+ {
+ // New from template - override only in default mode
+ if (curEvt == CommandLineEvent::Open)
+ curEvt = CommandLineEvent::ForceNew;
+ nURIlen = rest2.indexOf("|");
+ // TODO: process optional second argument (default save-to location)
+ // For now, we just ignore it
+ }
+ else
+ {
+ // Abbreviated scheme: <scheme-name>:URI
+ // "ofv|u|" implied
+ // override only in default mode
+ if (curEvt == CommandLineEvent::Open)
+ curEvt = CommandLineEvent::View;
+ rest2 = rest1;
+ }
+ if (nURIlen < 0)
+ nURIlen = rest2.getLength();
+ arg = rest2.copy(0, nURIlen);
+ return curEvt;
+}
+
+// Skip single newline (be it *NIX LF, MacOS CR, of Win CRLF)
+// Changes the offset, and returns true if moved
+bool SkipNewline(const char* & pStr)
+{
+ if ((*pStr != '\r') && (*pStr != '\n'))
+ return false;
+ if (*pStr == '\r')
+ ++pStr;
+ if (*pStr == '\n')
+ ++pStr;
+ return true;
+}
+
+// Web query: http://support.microsoft.com/kb/157482
+CommandLineEvent CheckWebQuery(/* in,out */ OUString& arg, CommandLineEvent curEvt)
+{
+ // Only handle files with extension .iqy
+ if (!arg.endsWithIgnoreAsciiCase(".iqy"))
+ return curEvt;
+
+ static osl::Mutex aMutex;
+ osl::MutexGuard aGuard(aMutex);
+
+ try
+ {
+ OUString sFileURL;
+ // Cannot use translateExternalUris yet, because process service factory is not yet available
+ if (osl::FileBase::getFileURLFromSystemPath(arg, sFileURL) != osl::FileBase::RC::E_None)
+ return curEvt;
+ SvFileStream stream(sFileURL, StreamMode::READ);
+
+ const sal_Int32 nBufLen = 32000;
+ char sBuffer[nBufLen];
+ size_t nRead = stream.ReadBytes(sBuffer, nBufLen);
+ if (nRead < 8) // WEB\n1\n...
+ return curEvt;
+
+ const char* pPos = sBuffer;
+ if (strncmp(pPos, "WEB", 3) != 0)
+ return curEvt;
+ pPos += 3;
+ if (!SkipNewline(pPos))
+ return curEvt;
+ if (*pPos != '1')
+ return curEvt;
+ ++pPos;
+ if (!SkipNewline(pPos))
+ return curEvt;
+
+ OStringBuffer aResult(static_cast<unsigned int>(nRead));
+ do
+ {
+ const char* pPos1 = pPos;
+ const char* pEnd = sBuffer + nRead;
+ while ((pPos1 < pEnd) && (*pPos1 != '\r') && (*pPos1 != '\n'))
+ ++pPos1;
+ aResult.append(pPos, pPos1 - pPos);
+ if (pPos1 < pEnd) // newline
+ break;
+ pPos = sBuffer;
+ } while ((nRead = stream.ReadBytes(sBuffer, nBufLen)) > 0);
+
+ stream.Close();
+
+ arg = OStringToOUString(aResult.makeStringAndClear(), osl_getThreadTextEncoding());
+ return CommandLineEvent::ForceNew;
+ }
+ catch (...)
+ {
+ SAL_WARN("desktop.app", "An error processing Web Query file: " << arg);
+ }
+
+ return curEvt;
+}
+
+} // namespace
+
+CommandLineArgs::Supplier::Exception::Exception() {}
+
+CommandLineArgs::Supplier::Exception::Exception(Exception const &) {}
+
+CommandLineArgs::Supplier::Exception::~Exception() {}
+
+CommandLineArgs::Supplier::Exception &
+CommandLineArgs::Supplier::Exception::operator =(Exception const &)
+{ return *this; }
+
+CommandLineArgs::Supplier::~Supplier() {}
+
+// initialize class with command line parameters from process environment
+CommandLineArgs::CommandLineArgs()
+{
+ InitParamValues();
+ ExtCommandLineSupplier s;
+ ParseCommandLine_Impl( s );
+}
+
+CommandLineArgs::CommandLineArgs( Supplier& supplier )
+{
+ InitParamValues();
+ ParseCommandLine_Impl( supplier );
+}
+
+void CommandLineArgs::ParseCommandLine_Impl( Supplier& supplier )
+{
+ m_cwdUrl = supplier.getCwdUrl();
+ CommandLineEvent eCurrentEvent = CommandLineEvent::Open;
+
+ for (;;)
+ {
+ OUString aArg;
+ if ( !supplier.next( &aArg ) )
+ {
+ break;
+ }
+
+ if ( !aArg.isEmpty() )
+ {
+ m_bEmpty = false;
+ OUString oArg;
+ OUString oDeprecatedArg;
+ if (!aArg.startsWith("--", &oArg) && aArg.startsWith("-", &oArg)
+ && aArg.getLength() > 2) // -h, -?, -n, -o, -p are still valid
+ {
+ oDeprecatedArg = aArg; // save here, since aArg can change later
+ }
+
+ OUString rest;
+ if ( oArg == "minimized" )
+ {
+ m_minimized = true;
+ }
+ else if ( oArg == "invisible" )
+ {
+ m_invisible = true;
+ }
+ else if ( oArg == "norestore" )
+ {
+ m_norestore = true;
+ }
+ else if ( oArg == "nodefault" )
+ {
+ m_nodefault = true;
+ }
+ else if ( oArg == "headless" )
+ {
+ setHeadless();
+ }
+ else if ( oArg == "eventtesting" )
+ {
+ m_eventtesting = true;
+ }
+ else if ( oArg == "safe-mode" )
+ {
+ m_safemode = true;
+ }
+ else if ( oArg == "cat" )
+ {
+ m_textcat = true;
+ m_conversionparams = "txt:Text";
+ eCurrentEvent = CommandLineEvent::Conversion;
+ setHeadless();
+ }
+ else if ( oArg == "script-cat" )
+ {
+ m_scriptcat = true;
+ eCurrentEvent = CommandLineEvent::Conversion;
+ setHeadless();
+ }
+ else if ( oArg == "quickstart" )
+ {
+#if defined(ENABLE_QUICKSTART_APPLET)
+ m_quickstart = true;
+#endif
+ m_noquickstart = false;
+ }
+ else if ( oArg == "quickstart=no" )
+ {
+ m_noquickstart = true;
+ m_quickstart = false;
+ }
+ else if ( oArg == "terminate_after_init" )
+ {
+ m_terminateafterinit = true;
+ }
+ else if ( oArg == "nofirststartwizard" )
+ {
+ // Do nothing, accept only for backward compatibility
+ }
+ else if ( oArg == "nologo" )
+ {
+ m_nologo = true;
+ }
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ else if ( oArg == "nolockcheck" )
+ {
+ m_nolockcheck = true;
+ }
+#endif
+ else if ( oArg == "help" || aArg == "-h" || aArg == "-?" )
+ {
+ m_help = true;
+ }
+ else if ( oArg == "helpwriter" )
+ {
+ m_helpwriter = true;
+ }
+ else if ( oArg == "helpcalc" )
+ {
+ m_helpcalc = true;
+ }
+ else if ( oArg == "helpdraw" )
+ {
+ m_helpdraw = true;
+ }
+ else if ( oArg == "helpimpress" )
+ {
+ m_helpimpress = true;
+ }
+ else if ( oArg == "helpbase" )
+ {
+ m_helpbase = true;
+ }
+ else if ( oArg == "helpbasic" )
+ {
+ m_helpbasic = true;
+ }
+ else if ( oArg == "helpmath" )
+ {
+ m_helpmath = true;
+ }
+ else if ( oArg == "protector" )
+ {
+ // Not relevant for us here, but can be used in unit tests.
+ // Usually unit tests would not end up here, but e.g. the
+ // LOK Tiled Rendering tests end up running a full soffice
+ // process, and we can't bail on the use of --protector.
+
+ // We specifically need to consume the following 2 arguments
+ // for --protector
+ if ((!supplier.next(&aArg) || !supplier.next(&aArg)) && m_unknown.isEmpty())
+ m_unknown = "--protector must be followed by two arguments";
+ }
+ else if ( oArg == "version" )
+ {
+ m_version = true;
+ }
+ else if ( oArg.startsWith("splash-pipe=") )
+ {
+ m_splashpipe = true;
+ }
+#ifdef MACOSX
+ /* #i84053# ignore -psn on Mac
+ Platform dependent #ifdef here is ugly, however this is currently
+ the only platform dependent parameter. Should more appear
+ we should find a better solution
+ */
+ else if ( aArg.startsWith("-psn") )
+ {
+ oDeprecatedArg.clear();
+ }
+#endif
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ else if ( oArg == "nstemporarydirectory" )
+ {
+ printf("%s\n", [NSTemporaryDirectory() UTF8String]);
+ exit(0);
+ }
+#endif
+#ifdef _WIN32
+ /* fdo#57203 ignore -Embedding on Windows
+ when LibreOffice is launched by COM+
+ */
+ else if ( oArg == "Embedding" )
+ {
+ oDeprecatedArg.clear();
+ }
+#endif
+ else if ( oArg.startsWith("infilter=", &rest))
+ {
+ m_infilter.push_back(rest);
+ }
+ else if ( oArg.startsWith("accept=", &rest))
+ {
+ m_accept.push_back(rest);
+ }
+ else if ( oArg.startsWith("unaccept=", &rest))
+ {
+ m_unaccept.push_back(rest);
+ }
+ else if ( oArg.startsWith("language=", &rest))
+ {
+ m_language = rest;
+ }
+ else if ( oArg.startsWith("pidfile=", &rest))
+ {
+ m_pidfile = rest;
+ }
+ else if ( oArg == "writer" )
+ {
+ m_writer = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "calc" )
+ {
+ m_calc = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "draw" )
+ {
+ m_draw = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "impress" )
+ {
+ m_impress = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "base" )
+ {
+ m_base = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "global" )
+ {
+ m_global = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "math" )
+ {
+ m_math = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( oArg == "web" )
+ {
+ m_web = true;
+ m_bDocumentArgs = true;
+ }
+ else if ( aArg == "-n" )
+ {
+ // force new documents based on the following documents
+ eCurrentEvent = CommandLineEvent::ForceNew;
+ }
+ else if ( aArg == "-o" )
+ {
+ // force open documents regardless if they are templates or not
+ eCurrentEvent = CommandLineEvent::ForceOpen;
+ }
+ else if ( oArg == "pt" )
+ {
+ // Print to special printer
+ eCurrentEvent = CommandLineEvent::PrintTo;
+ // first argument after "-pt" must be the printer name
+ if (supplier.next(&aArg))
+ m_printername = aArg;
+ else if (m_unknown.isEmpty())
+ m_unknown = "--pt must be followed by printername";
+ }
+ else if ( aArg == "-p" )
+ {
+ // Print to default printer
+ eCurrentEvent = CommandLineEvent::Print;
+ }
+ else if ( oArg == "view")
+ {
+ // open in viewmode
+ eCurrentEvent = CommandLineEvent::View;
+ }
+ else if ( oArg == "show" )
+ {
+ // open in viewmode
+ eCurrentEvent = CommandLineEvent::Start;
+ }
+ else if ( oArg == "display" )
+ {
+ // The command line argument following --display should
+ // always be treated as the argument of --display.
+ // --display and its argument are handled "out of line"
+ // in Unix-only desktop/unx/source/splashx.c and vcl/unx/*,
+ // and just ignored here
+ (void)supplier.next(&aArg);
+ }
+ else if ( oArg == "convert-to" )
+ {
+ eCurrentEvent = CommandLineEvent::Conversion;
+ // first argument must be the params
+ if (supplier.next(&aArg))
+ {
+ m_conversionparams = aArg;
+ // It doesn't make sense to use convert-to without headless.
+ setHeadless();
+ }
+ else if (m_unknown.isEmpty())
+ m_unknown = "--convert-to must be followed by output_file_extension[:output_filter_name]";
+ }
+ else if ( oArg == "print-to-file" )
+ {
+ eCurrentEvent = CommandLineEvent::BatchPrint;
+ }
+ else if ( oArg == "printer-name" )
+ {
+ if (eCurrentEvent == CommandLineEvent::BatchPrint)
+ {
+ // first argument is the printer name
+ if (supplier.next(&aArg))
+ m_printername = aArg;
+ else if (m_unknown.isEmpty())
+ m_unknown = "--printer-name must be followed by printername";
+ }
+ else if (m_unknown.isEmpty())
+ {
+ m_unknown = "--printer-name must directly follow --print-to-file";
+ }
+ }
+ else if ( oArg == "outdir" )
+ {
+ if (eCurrentEvent == CommandLineEvent::Conversion ||
+ eCurrentEvent == CommandLineEvent::BatchPrint)
+ {
+ if (supplier.next(&aArg))
+ m_conversionout = aArg;
+ else if (m_unknown.isEmpty())
+ m_unknown = "--outdir must be followed by output directory path";
+ }
+ else if (m_unknown.isEmpty())
+ {
+ m_unknown = "--outdir must directly follow either output filter specification of --convert-to, or --print-to-file or its printer specification";
+ }
+ }
+ else if ( eCurrentEvent == CommandLineEvent::Conversion
+ && oArg == "convert-images-to" )
+ {
+ if (supplier.next(&aArg))
+ m_convertimages = aArg;
+ else if (m_unknown.isEmpty())
+ m_unknown = "--convert-images-to must be followed by an image type";
+ }
+ else if ( aArg.startsWith("-") )
+ {
+ // because it's impossible to filter these options that
+ // are handled in the soffice shell script with the
+ // primitive tools that /bin/sh offers, ignore them here
+ if (
+#if defined UNX
+ oArg != "record" &&
+ oArg != "backtrace" &&
+ oArg != "strace" &&
+ oArg != "valgrind" &&
+ // for X Session Management, handled in
+ // vcl/unx/generic/app/sm.cxx:
+ oArg != "session=" &&
+#endif
+ //ignore additional legacy options that don't do anything anymore
+ oArg != "nocrashreport" &&
+ m_unknown.isEmpty())
+ {
+ m_unknown = aArg;
+ }
+ oDeprecatedArg.clear();
+ }
+ else
+ {
+ // handle this argument as a filename
+
+ // First check if this is an Office URI
+ // This will possibly adjust event for this argument
+ // and put real URI to aArg
+ CommandLineEvent eThisEvent = CheckOfficeURI(aArg, eCurrentEvent);
+
+ // Now check if this is a Web Query file
+ eThisEvent = CheckWebQuery(aArg, eThisEvent);
+
+ switch (eThisEvent)
+ {
+ case CommandLineEvent::Open:
+ m_openlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::View:
+ m_viewlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::Start:
+ m_startlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::Print:
+ m_printlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::PrintTo:
+ m_printtolist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::ForceNew:
+ m_forcenewlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::ForceOpen:
+ m_forceopenlist.push_back(aArg);
+ m_bDocumentArgs = true;
+ break;
+ case CommandLineEvent::Conversion:
+ case CommandLineEvent::BatchPrint:
+ m_conversionlist.push_back(aArg);
+ break;
+ }
+ }
+
+ if (!oDeprecatedArg.isEmpty())
+ {
+ OString sArg(OUStringToOString(oDeprecatedArg, osl_getThreadTextEncoding()));
+ fprintf(stderr, "Warning: %s is deprecated. Use -%s instead.\n", sArg.getStr(), sArg.getStr());
+ }
+ }
+ }
+}
+
+void CommandLineArgs::InitParamValues()
+{
+ m_minimized = false;
+ m_norestore = false;
+#if HAVE_FEATURE_UI
+ m_invisible = false;
+ m_headless = false;
+#else
+ m_invisible = true;
+ m_headless = true;
+#endif
+ m_eventtesting = false;
+ m_quickstart = false;
+ m_noquickstart = false;
+ m_terminateafterinit = false;
+ m_nologo = false;
+ m_nolockcheck = false;
+ m_nodefault = false;
+ m_help = false;
+ m_writer = false;
+ m_calc = false;
+ m_draw = false;
+ m_impress = false;
+ m_global = false;
+ m_math = false;
+ m_web = false;
+ m_base = false;
+ m_helpwriter = false;
+ m_helpcalc = false;
+ m_helpdraw = false;
+ m_helpbasic = false;
+ m_helpmath = false;
+ m_helpimpress = false;
+ m_helpbase = false;
+ m_version = false;
+ m_splashpipe = false;
+ m_bEmpty = true;
+ m_bDocumentArgs = false;
+ m_textcat = false;
+ m_scriptcat = false;
+ m_safemode = false;
+}
+
+bool CommandLineArgs::HasModuleParam() const
+{
+ return m_writer || m_calc || m_draw || m_impress || m_global || m_math
+ || m_web || m_base;
+}
+
+std::vector< OUString > CommandLineArgs::GetOpenList() const
+{
+ return translateExternalUris(m_openlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetViewList() const
+{
+ return translateExternalUris(m_viewlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetStartList() const
+{
+ return translateExternalUris(m_startlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetForceOpenList() const
+{
+ return translateExternalUris(m_forceopenlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetForceNewList() const
+{
+ return translateExternalUris(m_forcenewlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetPrintList() const
+{
+ return translateExternalUris(m_printlist);
+}
+
+std::vector< OUString > CommandLineArgs::GetPrintToList() const
+{
+ return translateExternalUris(m_printtolist);
+}
+
+std::vector< OUString > CommandLineArgs::GetConversionList() const
+{
+ return translateExternalUris(m_conversionlist);
+}
+
+OUString CommandLineArgs::GetConversionOut() const
+{
+ return translateExternalUris(m_conversionout);
+}
+
+} // namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/cmdlineargs.hxx b/desktop/source/app/cmdlineargs.hxx
new file mode 100644
index 000000000..577b50e1a
--- /dev/null
+++ b/desktop/source/app/cmdlineargs.hxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_CMDLINEARGS_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_CMDLINEARGS_HXX
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <optional>
+
+namespace desktop
+{
+
+class CommandLineArgs
+{
+ public:
+ struct Supplier
+ {
+ // Thrown from constructors and next:
+ class Exception final
+ {
+ public:
+ Exception();
+ Exception(Exception const &);
+ ~Exception();
+ Exception & operator =(Exception const &);
+ };
+
+ virtual ~Supplier();
+ virtual std::optional< OUString > getCwdUrl() = 0;
+ virtual bool next(OUString * argument) = 0;
+ };
+
+ CommandLineArgs();
+ explicit CommandLineArgs( Supplier& supplier );
+
+ CommandLineArgs(const CommandLineArgs&) = delete;
+ const CommandLineArgs& operator=(const CommandLineArgs&) = delete;
+
+ const std::optional< OUString >& getCwdUrl() const { return m_cwdUrl; }
+
+ // Access to bool parameters
+ bool IsMinimized() const { return m_minimized;}
+ bool IsInvisible() const
+ {
+ return m_invisible || (m_headless && !m_eventtesting);
+ }
+ bool IsNoRestore() const { return m_norestore;}
+ bool IsNoDefault() const { return m_nodefault;}
+ bool IsHeadless() const { return m_headless;}
+ bool IsEventTesting() const { return m_eventtesting;}
+ bool IsQuickstart() const { return m_quickstart;}
+ bool IsNoQuickstart() const { return m_noquickstart;}
+ bool IsTerminateAfterInit() const { return m_terminateafterinit;}
+ bool IsNoLogo() const { return m_nologo;}
+ bool IsNoLockcheck() const { return m_nolockcheck;}
+ bool IsHelp() const { return m_help;}
+ bool IsHelpWriter() const { return m_helpwriter;}
+ bool IsHelpCalc() const { return m_helpcalc;}
+ bool IsHelpDraw() const { return m_helpdraw;}
+ bool IsHelpImpress() const { return m_helpimpress;}
+ bool IsHelpBase() const { return m_helpbase;}
+ bool IsHelpMath() const { return m_helpmath;}
+ bool IsHelpBasic() const { return m_helpbasic;}
+ bool IsWriter() const { return m_writer;}
+ bool IsCalc() const { return m_calc;}
+ bool IsDraw() const { return m_draw;}
+ bool IsImpress() const { return m_impress;}
+ bool IsBase() const { return m_base;}
+ bool IsGlobal() const { return m_global;}
+ bool IsMath() const { return m_math;}
+ bool IsWeb() const { return m_web;}
+ bool IsVersion() const { return m_version;}
+ bool HasModuleParam() const;
+ bool WantsToLoadDocument() const { return m_bDocumentArgs;}
+ bool IsTextCat() const { return m_textcat;}
+ bool IsScriptCat() const { return m_scriptcat;}
+ bool IsSafeMode() const { return m_safemode; }
+
+ const OUString& GetUnknown() const { return m_unknown;}
+
+ // Access to string parameters
+ bool HasSplashPipe() const { return m_splashpipe;}
+ std::vector< OUString > const & GetAccept() const { return m_accept;}
+ std::vector< OUString > const & GetUnaccept() const { return m_unaccept;}
+ std::vector< OUString > GetOpenList() const;
+ std::vector< OUString > GetViewList() const;
+ std::vector< OUString > GetStartList() const;
+ std::vector< OUString > GetForceOpenList() const;
+ std::vector< OUString > GetForceNewList() const;
+ std::vector< OUString > GetPrintList() const;
+ std::vector< OUString > GetPrintToList() const;
+ const OUString& GetPrinterName() const { return m_printername;}
+ const OUString& GetLanguage() const { return m_language;}
+ std::vector< OUString > const & GetInFilter() const { return m_infilter;}
+ std::vector< OUString > GetConversionList() const;
+ const OUString& GetConversionParams() const { return m_conversionparams;}
+ OUString GetConversionOut() const;
+ OUString const & GetImageConversionType() const { return m_convertimages; }
+ const OUString& GetPidfileName() const { return m_pidfile;}
+
+ // Special analyzed states (does not match directly to a command line parameter!)
+ bool IsEmpty() const { return m_bEmpty;}
+
+ void setHeadless() { m_headless = true; }
+
+ private:
+ void ParseCommandLine_Impl( Supplier& supplier );
+ void InitParamValues();
+
+ std::optional< OUString > m_cwdUrl;
+
+ bool m_minimized;
+ bool m_invisible;
+ bool m_norestore;
+ bool m_headless;
+ bool m_eventtesting;
+ bool m_quickstart;
+ bool m_noquickstart;
+ bool m_terminateafterinit;
+ bool m_nologo;
+ bool m_nolockcheck;
+ bool m_nodefault;
+ bool m_help;
+ bool m_writer;
+ bool m_calc;
+ bool m_draw;
+ bool m_impress;
+ bool m_global;
+ bool m_math;
+ bool m_web;
+ bool m_base;
+ bool m_helpwriter;
+ bool m_helpcalc;
+ bool m_helpdraw;
+ bool m_helpbasic;
+ bool m_helpmath;
+ bool m_helpimpress;
+ bool m_helpbase;
+ bool m_version;
+ bool m_splashpipe;
+ bool m_textcat;
+ bool m_scriptcat;
+ bool m_safemode;
+
+ OUString m_unknown;
+
+ bool m_bEmpty; // No Args at all
+ bool m_bDocumentArgs; // A document creation/open/load arg is used
+ std::vector< OUString > m_accept;
+ std::vector< OUString > m_unaccept;
+ std::vector< OUString > m_openlist; // contains external URIs
+ std::vector< OUString > m_viewlist; // contains external URIs
+ std::vector< OUString > m_startlist; // contains external URIs
+ std::vector< OUString > m_forceopenlist; // contains external URIs
+ std::vector< OUString > m_forcenewlist; // contains external URIs
+ std::vector< OUString > m_printlist; // contains external URIs
+ std::vector< OUString > m_printtolist; // contains external URIs
+ OUString m_printername;
+ std::vector< OUString > m_conversionlist; // contains external URIs
+ OUString m_conversionparams;
+ OUString m_conversionout; // contains external URIs
+ OUString m_convertimages; // The format in which images should be converted
+ std::vector< OUString > m_infilter;
+ OUString m_language;
+ OUString m_pidfile;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/cmdlinehelp.cxx b/desktop/source/app/cmdlinehelp.cxx
new file mode 100644
index 000000000..1d7fe7613
--- /dev/null
+++ b/desktop/source/app/cmdlinehelp.cxx
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <stdio.h>
+#include <comphelper/string.hxx>
+#include <app.hxx>
+
+#include "cmdlinehelp.hxx"
+
+#ifdef _WIN32
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+namespace desktop
+{
+ const char aCmdLineHelp_version[] =
+ "%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION %BUILDID\n"
+ "\n";
+ const char aCmdLineHelp[] =
+ "Usage: %CMDNAME [argument...]\n"
+ " argument - switches, switch parameters and document URIs (filenames). \n\n"
+ "Using without special arguments: \n"
+ "Opens the start center, if it is used without any arguments. \n"
+ " {file} Tries to open the file (files) in the components \n"
+ " suitable for them. \n"
+ " {file} {macro:///Library.Module.MacroName} \n"
+ " Opens the file and runs specified macros from \n"
+ " the file. \n\n"
+ "Getting help and information: \n"
+ " --help | -h | -? Shows this help and quits. \n"
+ " --helpwriter Opens built-in or online Help on Writer. \n"
+ " --helpcalc Opens built-in or online Help on Calc. \n"
+ " --helpdraw Opens built-in or online Help on Draw. \n"
+ " --helpimpress Opens built-in or online Help on Impress. \n"
+ " --helpbase Opens built-in or online Help on Base. \n"
+ " --helpbasic Opens built-in or online Help on Basic scripting \n"
+ " language. \n"
+ " --helpmath Opens built-in or online Help on Math. \n"
+ " --version Shows the version and quits. \n"
+ " --nstemporarydirectory \n"
+ " (MacOS X sandbox only) Returns path of the temporary \n"
+ " directory for the current user and exits. Overrides \n"
+ " all other arguments. \n\n"
+ "General arguments: \n"
+ " --quickstart[=no] Activates[Deactivates] the Quickstarter service. \n"
+ " --nolockcheck Disables check for remote instances using one \n"
+ " installation. \n"
+ " --infilter={filter} Force an input filter type if possible. For example: \n"
+ " --infilter=\"Calc Office Open XML\" \n"
+ " --infilter=\"Text (encoded):UTF8,LF,,,\" \n"
+ " --pidfile={file} Store soffice.bin pid to {file}. \n"
+ " --display {display} Sets the DISPLAY environment variable on UNIX-like \n"
+ " platforms to the value {display} (only supported by a \n"
+ " start script). \n\n"
+ "User/programmatic interface control: \n"
+ " --nologo Disables the splash screen at program start. \n"
+ " --minimized Starts minimized. The splash screen is not displayed. \n"
+ " --nodefault Starts without displaying anything except the splash \n"
+ " screen (do not display initial window). \n"
+ " --invisible Starts in invisible mode. Neither the start-up logo nor \n"
+ " the initial program window will be visible. Application \n"
+ " can be controlled, and documents and dialogs can be \n"
+ " controlled and opened via the API. Using the parameter, \n"
+ " the process can only be ended using the taskmanager \n"
+ " (Windows) or the kill command (UNIX-like systems). It \n"
+ " cannot be used in conjunction with --quickstart. \n"
+ " --headless Starts in \"headless mode\" which allows using the \n"
+ " application without GUI. This special mode can be used \n"
+ " when the application is controlled by external clients \n"
+ " via the API. \n"
+ " --norestore Disables restart and file recovery after a system crash.\n"
+ " --safe-mode Starts in a safe mode, i.e. starts temporarily with a \n"
+ " fresh user profile and helps to restore a broken \n"
+ " configuration. \n"
+ " --accept={connect-string} Specifies a UNO connect-string to create a UNO \n"
+ " acceptor through which other programs can connect to \n"
+ " access the API. Note that API access allows execution \n"
+ " of arbitrary commands. \n"
+ " The syntax of the {connect-string} is: \n"
+ " connection-type,params;protocol-name,params \n"
+ " e.g. pipe,name={some name};urp \n"
+ " or socket,host=localhost,port=54321;urp \n"
+ " --unaccept={connect-string} Closes an acceptor that was created with \n"
+ " --accept. Use --unaccept=all to close all acceptors. \n"
+ " --language={lang} Uses specified language, if language is not selected \n"
+ " yet for UI. The lang is a tag of the language in IETF \n"
+ " language tag. \n\n"
+ "Developer arguments: \n"
+ " --terminate_after_init \n"
+ " Exit after initialization complete (no documents loaded)\n"
+ " --eventtesting Exit after loading documents. \n\n"
+ "New document creation arguments: \n"
+ "The arguments create an empty document of specified kind. Only one of them may \n"
+ "be used in one command line. If filenames are specified after an argument, \n"
+ "then it tries to open those files in the specified component. \n"
+ " --writer Creates an empty Writer document. \n"
+ " --calc Creates an empty Calc document. \n"
+ " --draw Creates an empty Draw document. \n"
+ " --impress Creates an empty Impress document. \n"
+ " --base Creates a new database. \n"
+ " --global Creates an empty Writer master (global) document. \n"
+ " --math Creates an empty Math document (formula). \n"
+ " --web Creates an empty HTML document. \n\n"
+ "File open arguments: \n"
+ "The arguments define how following filenames are treated. New treatment begins \n"
+ "after the argument and ends at the next argument. The default treatment is to \n"
+ "open documents for editing, and create new documents from document templates. \n"
+ " -n Treats following files as templates for creation of new \n"
+ " documents. \n"
+ " -o Opens following files for editing, regardless whether \n"
+ " they are templates or not. \n"
+ " --pt {Printername} Prints following files to the printer {Printername}, \n"
+ " after which those files are closed. The splash screen \n"
+ " does not appear. If used multiple times, only last \n"
+ " {Printername} is effective for all documents of all \n"
+ " --pt runs. Also, --printer-name argument of \n"
+ " --print-to-file switch interferes with {Printername}. \n"
+ " -p Prints following files to the default printer, after \n"
+ " which those files are closed. The splash screen does \n"
+ " not appear. If the file name contains spaces, then it \n"
+ " must be enclosed in quotation marks. \n"
+ " --view Opens following files in viewer mode (read-only). \n"
+ " --show Opens and starts the following presentation documents \n"
+ " of each immediately. Files are closed after the showing.\n"
+ " Files other than Impress documents are opened in \n"
+ " default mode , regardless of previous mode. \n"
+ " --convert-to OutputFileExtension[:OutputFilterName] \\ \n"
+ " [--outdir output_dir] [--convert-images-to] \n"
+ " Batch convert files (implies --headless). If --outdir \n"
+ " isn't specified, then current working directory is used \n"
+ " as output_dir. If --convert-images-to is given, its \n"
+ " parameter is taken as the target filter format for *all*\n"
+ " images written to the output format. If --convert-to is \n"
+ " used more than once, the last value of \n"
+ " OutputFileExtension[:OutputFilterName] is effective. If \n"
+ " --outdir is used more than once, only its last value is \n"
+ " effective. For example: \n"
+ " --convert-to pdf *.odt \n"
+ " --convert-to epub *.doc \n"
+ " --convert-to pdf:writer_pdf_Export --outdir /home/user *.doc\n"
+ " --convert-to \"html:XHTML Writer File:UTF8\" \\ \n"
+ " --convert-images-to \"jpg\" *.doc \n"
+ " --convert-to \"txt:Text (encoded):UTF8\" *.doc \n"
+ " --print-to-file [--printer-name printer_name] [--outdir output_dir] \n"
+ " Batch print files to file. If --outdir is not specified,\n"
+ " then current working directory is used as output_dir. \n"
+ " If --printer-name or --outdir used multiple times, only \n"
+ " last value of each is effective. Also, {Printername} of \n"
+ " --pt switch interferes with --printer-name. \n"
+ " --cat Dump text content of the following files to console \n"
+ " (implies --headless). Cannot be used with --convert-to. \n"
+ " --script-cat Dump text content of any scripts embedded in the files \n"
+ " to console (implies --headless). Cannot be used with \n"
+ " --convert-to. \n"
+ " -env:<VAR>[=<VALUE>] Set a bootstrap variable. For example: to set \n"
+ " a non-default user profile path: \n"
+ " -env:UserInstallation=file:///tmp/test \n\n"
+ "Ignored switches: \n"
+ " -psn Ignored (MacOS X only). \n"
+ " -Embedding Ignored (COM+ related; Windows only). \n"
+ " --nofirststartwizard Does nothing, accepted only for backward compatibility.\n"
+ " --protector {arg1} {arg2} \n"
+ " Used only in unit tests and should have two arguments. \n\n";
+#ifdef _WIN32
+ namespace{
+ // This class is only used to create a console when soffice.bin is run without own console
+ // (like using soffice.exe launcher as opposed to soffice.com), and either --version or
+ // --help command line options were specified, or an error in a command line option was
+ // detected, which requires to output strings to user.
+ class lcl_Console {
+ public:
+ explicit lcl_Console(short nBufHeight)
+ : m_bOwnConsole(AllocConsole() != FALSE)
+ {
+ if (m_bOwnConsole)
+ {
+ HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Ensure that console buffer is enough to hold required data
+ CONSOLE_SCREEN_BUFFER_INFO cinfo;
+ GetConsoleScreenBufferInfo(hOut, &cinfo);
+ if (cinfo.dwSize.Y < nBufHeight)
+ {
+ cinfo.dwSize.Y = nBufHeight;
+ SetConsoleScreenBufferSize(hOut, cinfo.dwSize);
+ }
+
+ (void)freopen("CON", "r", stdin);
+ (void)freopen("CON", "w", stdout);
+ (void)freopen("CON", "w", stderr);
+
+ std::ios::sync_with_stdio(true);
+ }
+ }
+
+ ~lcl_Console()
+ {
+ if (m_bOwnConsole)
+ {
+ fflush(stdout);
+ fprintf(stdout, "Press Enter to continue...");
+ fgetc(stdin);
+ FreeConsole();
+ }
+ }
+ private:
+ bool m_bOwnConsole;
+ };
+ }
+#endif
+
+ void displayCmdlineHelp(OUString const & unknown)
+ {
+ OUString aHelpMessage_version = ReplaceStringHookProc(aCmdLineHelp_version);
+ OUString aHelpMessage(OUString(aCmdLineHelp).replaceFirst("%CMDNAME", "soffice"));
+ if (!unknown.isEmpty())
+ {
+ aHelpMessage = "Error in option: " + unknown + "\n\n"
+ + aHelpMessage;
+ }
+#ifdef _WIN32
+ sal_Int32 n = comphelper::string::getTokenCount(aHelpMessage, '\n');
+ lcl_Console aConsole(short(n*2));
+#endif
+ fprintf(stdout, "%s%s",
+ OUStringToOString(aHelpMessage_version, RTL_TEXTENCODING_ASCII_US).getStr(),
+ OUStringToOString(aHelpMessage, RTL_TEXTENCODING_ASCII_US).getStr());
+ }
+
+ void displayVersion()
+ {
+ OUString aVersionMsg(aCmdLineHelp_version);
+ aVersionMsg = ReplaceStringHookProc(aVersionMsg);
+#ifdef _WIN32
+ lcl_Console aConsole(short(10));
+#endif
+ fprintf(stdout, "%s", OUStringToOString(aVersionMsg, RTL_TEXTENCODING_ASCII_US).getStr());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/cmdlinehelp.hxx b/desktop/source/app/cmdlinehelp.hxx
new file mode 100644
index 000000000..e1a153605
--- /dev/null
+++ b/desktop/source/app/cmdlinehelp.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_CMDLINEHELP_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_CMDLINEHELP_HXX
+
+#include <rtl/ustring.hxx>
+
+namespace desktop
+{
+ void displayCmdlineHelp( OUString const & unknown );
+ void displayVersion();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/crashreport.cxx b/desktop/source/app/crashreport.cxx
new file mode 100644
index 000000000..432be67cd
--- /dev/null
+++ b/desktop/source/app/crashreport.cxx
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <desktop/crashreport.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/file.hxx>
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/proxydecider.hxx>
+#include <unotools/bootstrap.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <desktop/minidump.hxx>
+
+#include <config_version.h>
+#include <config_folders.h>
+
+#include <string>
+
+
+#if HAVE_FEATURE_BREAKPAD
+
+#include <fstream>
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+#include <client/linux/handler/exception_handler.h>
+#elif defined _WIN32
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmicrosoft-enum-value"
+#endif
+#include <client/windows/handler/exception_handler.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <locale>
+#include <codecvt>
+#endif
+
+osl::Mutex CrashReporter::maMutex;
+std::unique_ptr<google_breakpad::ExceptionHandler> CrashReporter::mpExceptionHandler;
+bool CrashReporter::mbInit = false;
+CrashReporter::vmaKeyValues CrashReporter::maKeyValues;
+
+
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* /*context*/, bool succeeded)
+{
+ CrashReporter::addKeyValue("DumpFile", OStringToOUString(descriptor.path(), RTL_TEXTENCODING_UTF8), CrashReporter::Write);
+ SAL_WARN("desktop", "minidump generated: " << descriptor.path());
+
+ return succeeded;
+}
+#elif defined _WIN32
+static bool dumpCallback(const wchar_t* path, const wchar_t* id,
+ void* /*context*/, EXCEPTION_POINTERS* /*exinfo*/,
+ MDRawAssertionInfo* /*assertion*/,
+ bool succeeded)
+{
+ // TODO: moggi: can we avoid this conversion
+#ifdef _MSC_VER
+#pragma warning (disable: 4996)
+#endif
+ std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1;
+ std::string aPath = conv1.to_bytes(std::wstring(path)) + conv1.to_bytes(std::wstring(id)) + ".dmp";
+ CrashReporter::addKeyValue("DumpFile", OStringToOUString(aPath.c_str(), RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
+ CrashReporter::addKeyValue("GDIHandles", OUString::number(::GetGuiResources(::GetCurrentProcess(), GR_GDIOBJECTS)), CrashReporter::Write);
+ SAL_WARN("desktop", "minidump generated: " << aPath);
+ return succeeded;
+}
+#endif
+
+
+void CrashReporter::writeToFile(std::ios_base::openmode Openmode)
+{
+ std::ofstream ini_file(getIniFileName(), Openmode);
+
+ for (auto& keyValue : maKeyValues)
+ {
+ ini_file << OUStringToOString(keyValue.first, RTL_TEXTENCODING_UTF8) << "=";
+ ini_file << OUStringToOString(keyValue.second, RTL_TEXTENCODING_UTF8) << "\n";
+ }
+
+ maKeyValues.clear();
+ ini_file.close();
+}
+
+void CrashReporter::addKeyValue(const OUString& rKey, const OUString& rValue, tAddKeyHandling AddKeyHandling)
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ if (IsDumpEnable())
+ {
+ if (!rKey.isEmpty())
+ maKeyValues.push_back(mpair(rKey, rValue));
+
+ if (AddKeyHandling != AddItem)
+ {
+ if (mbInit)
+ writeToFile(std::ios_base::app);
+ else if (AddKeyHandling == Create)
+ writeCommonInfo();
+ }
+ }
+}
+
+void CrashReporter::writeCommonInfo()
+{
+ ucbhelper::InternetProxyDecider proxy_decider(::comphelper::getProcessComponentContext());
+
+ const OUString protocol = "https";
+ const OUString url = "crashreport.libreoffice.org";
+ const sal_Int32 port = 443;
+
+ const ucbhelper::InternetProxyServer proxy_server = proxy_decider.getProxy(protocol, url, port);
+
+ // save the new Keys
+ vmaKeyValues atlast = maKeyValues;
+ // clear the keys, the following Keys should be at the begin
+ maKeyValues.clear();
+
+ // limit the amount of code that needs to be executed before the crash reporting
+ addKeyValue("ProductName", "LibreOffice", AddItem);
+ addKeyValue("Version", LIBO_VERSION_DOTTED, AddItem);
+ addKeyValue("BuildID", utl::Bootstrap::getBuildIdData(""), AddItem);
+ addKeyValue("URL", protocol + "://" + url + "/submit/", AddItem);
+
+ if (proxy_server.aName != OUString())
+ {
+ addKeyValue("Proxy", proxy_server.aName + ":" + OUString::number(proxy_server.nPort), AddItem);
+ }
+
+ // write the new keys at the end
+ maKeyValues.insert(maKeyValues.end(), atlast.begin(), atlast.end());
+
+ mbInit = true;
+
+ writeToFile(std::ios_base::trunc);
+
+ updateMinidumpLocation();
+}
+
+
+namespace {
+
+OUString getCrashDirectory()
+{
+ OUString aCrashURL;
+ rtl::Bootstrap::get("CrashDirectory", aCrashURL);
+ // Need to convert to URL in case of user-defined path
+ osl::FileBase::getFileURLFromSystemPath(aCrashURL, aCrashURL);
+
+ if (aCrashURL.isEmpty()) { // Fall back to user profile
+ aCrashURL = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/crash/";
+ rtl::Bootstrap::expandMacros(aCrashURL);
+ }
+
+ if (!aCrashURL.endsWith("/"))
+ aCrashURL += "/";
+
+ osl::Directory::create(aCrashURL);
+ OUString aCrashPath;
+ osl::FileBase::getSystemPathFromFileURL(aCrashURL, aCrashPath);
+ return aCrashPath;
+}
+
+}
+
+void CrashReporter::updateMinidumpLocation()
+{
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+ OUString aURL = getCrashDirectory();
+ OString aOStringUrl = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
+ google_breakpad::MinidumpDescriptor descriptor(aOStringUrl.getStr());
+ mpExceptionHandler->set_minidump_descriptor(descriptor);
+#elif defined _WIN32
+ OUString aURL = getCrashDirectory();
+ mpExceptionHandler->set_dump_path(o3tl::toW(aURL.getStr()));
+#endif
+}
+
+bool CrashReporter::crashReportInfoExists()
+{
+ static bool first = true;
+ static bool InfoExist = false;
+
+ if (first)
+ {
+ first = false;
+ InfoExist = crashreport::readConfig(CrashReporter::getIniFileName(), nullptr);
+ }
+
+ return InfoExist;
+}
+
+bool CrashReporter::readSendConfig(std::string& response)
+{
+ return crashreport::readConfig(CrashReporter::getIniFileName(), &response);
+}
+
+void CrashReporter::installExceptionHandler()
+{
+ if (!IsDumpEnable())
+ return;
+#if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
+ google_breakpad::MinidumpDescriptor descriptor("/tmp");
+ mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(descriptor, nullptr, dumpCallback, nullptr, true, -1);
+#elif defined _WIN32
+ mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(L".", nullptr, dumpCallback, nullptr, google_breakpad::ExceptionHandler::HANDLER_ALL);
+#endif
+}
+
+void CrashReporter::removeExceptionHandler()
+{
+ mpExceptionHandler.reset();
+}
+
+
+
+bool CrashReporter::IsDumpEnable()
+{
+ OUString sToken;
+ OString sEnvVar(std::getenv("CRASH_DUMP_ENABLE"));
+ bool bEnable = true; // default, always on
+ // read configuration item 'CrashDumpEnable' -> bool on/off
+ if (rtl::Bootstrap::get("CrashDumpEnable", sToken) && sEnvVar.isEmpty())
+ {
+ bEnable = sToken.toBoolean();
+ }
+
+ return bEnable;
+}
+
+
+std::string CrashReporter::getIniFileName()
+{
+ OUString url = getCrashDirectory() + "dump.ini";
+ OString aUrl = OUStringToOString(url, RTL_TEXTENCODING_UTF8);
+ std::string aRet(aUrl.getStr());
+ return aRet;
+}
+
+
+#endif //HAVE_FEATURE_BREAKPAD
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/desktopcontext.cxx b/desktop/source/app/desktopcontext.cxx
new file mode 100644
index 000000000..efabac383
--- /dev/null
+++ b/desktop/source/app/desktopcontext.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_java.h>
+
+#include "desktopcontext.hxx"
+
+#include <svtools/javainteractionhandler.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::task;
+
+
+namespace desktop
+{
+
+DesktopContext::DesktopContext( const Reference< XCurrentContext > & ctx )
+ : m_xNextContext( ctx )
+{
+}
+
+Any SAL_CALL DesktopContext::getValueByName( const OUString& Name)
+{
+ Any retVal;
+
+ if ( Name == JAVA_INTERACTION_HANDLER_NAME )
+ {
+#if HAVE_FEATURE_JAVA
+ retVal <<= Reference< XInteractionHandler >( new svt::JavaInteractionHandler());
+#endif
+ }
+ else if( m_xNextContext.is() )
+ {
+ // Call next context in chain if found
+ retVal = m_xNextContext->getValueByName( Name );
+ }
+ return retVal;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/desktopcontext.hxx b/desktop/source/app/desktopcontext.hxx
new file mode 100644
index 000000000..2deeb4ba8
--- /dev/null
+++ b/desktop/source/app/desktopcontext.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_DESKTOPCONTEXT_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_DESKTOPCONTEXT_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/XCurrentContext.hpp>
+
+namespace desktop
+{
+ class DesktopContext: public cppu::WeakImplHelper< css::uno::XCurrentContext >
+ {
+ public:
+ explicit DesktopContext( const css::uno::Reference< css::uno::XCurrentContext > & ctx);
+
+ // XCurrentContext
+ virtual css::uno::Any SAL_CALL getValueByName( const OUString& Name ) override;
+
+ private:
+ css::uno::Reference< css::uno::XCurrentContext > m_xNextContext;
+ };
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_APP_DESKTOPCONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/dispatchwatcher.cxx b/desktop/source/app/dispatchwatcher.cxx
new file mode 100644
index 000000000..450fd0e1e
--- /dev/null
+++ b/desktop/source/app/dispatchwatcher.cxx
@@ -0,0 +1,808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svl/fstathelper.hxx>
+
+#include <app.hxx>
+#include "dispatchwatcher.hxx"
+#include "officeipcthread.hxx"
+#include <rtl/ustring.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/synchronousdispatch.hxx>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/script/XLibraryContainer2.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <osl/thread.hxx>
+#include <osl/file.hxx>
+#include <iostream>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::document;
+
+namespace document = ::com::sun::star::document;
+
+namespace desktop
+{
+
+namespace {
+
+struct DispatchHolder
+{
+ DispatchHolder( const URL& rURL, Reference< XDispatch > const & rDispatch ) :
+ aURL( rURL ), xDispatch( rDispatch ) {}
+
+ URL aURL;
+ Reference< XDispatch > xDispatch;
+};
+
+std::shared_ptr<const SfxFilter> impl_lookupExportFilterForUrl( const OUString& rUrl, const OUString& rFactory )
+{
+ // create the list of filters
+ OUStringBuffer sQuery(256);
+ sQuery.append("getSortedFilterList()");
+ sQuery.append(":module=");
+ sQuery.append(rFactory); // use long name here !
+ sQuery.append(":iflags=");
+ sQuery.append(OUString::number(static_cast<sal_Int32>(SfxFilterFlags::EXPORT)));
+ sQuery.append(":eflags=");
+ sQuery.append(OUString::number(static_cast<int>(SFX_FILTER_NOTINSTALLED)));
+
+ const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ const Reference< XContainerQuery > xFilterFactory(
+ xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.FilterFactory", xContext ),
+ UNO_QUERY_THROW );
+
+ std::shared_ptr<const SfxFilter> pBestMatch;
+
+ const Reference< XEnumeration > xFilterEnum(
+ xFilterFactory->createSubSetEnumerationByQuery( sQuery.makeStringAndClear() ), UNO_SET_THROW );
+ while ( xFilterEnum->hasMoreElements() )
+ {
+ comphelper::SequenceAsHashMap aFilterProps( xFilterEnum->nextElement() );
+ const OUString aName( aFilterProps.getUnpackedValueOrDefault( "Name", OUString() ) );
+ if ( !aName.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter( SfxFilter::GetFilterByName( aName ) );
+ if ( pFilter && pFilter->CanExport() && pFilter->GetWildcard().Matches( rUrl ) )
+ {
+ if ( !pBestMatch || ( SfxFilterFlags::PREFERED & pFilter->GetFilterFlags() ) )
+ pBestMatch = pFilter;
+ }
+ }
+ }
+
+ return pBestMatch;
+}
+
+std::shared_ptr<const SfxFilter> impl_getExportFilterFromUrl(
+ const OUString& rUrl, const OUString& rFactory)
+{
+ try
+ {
+ const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ const Reference< document::XTypeDetection > xTypeDetector(
+ xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.TypeDetection", xContext ),
+ UNO_QUERY_THROW );
+ const OUString aTypeName( xTypeDetector->queryTypeByURL( rUrl ) );
+
+ std::shared_ptr<const SfxFilter> pFilter( SfxFilterMatcher( rFactory ).GetFilter4EA( aTypeName, SfxFilterFlags::EXPORT ) );
+ if ( !pFilter )
+ pFilter = impl_lookupExportFilterForUrl( rUrl, rFactory );
+ if ( !pFilter )
+ {
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL( rUrl, aTempName );
+ OString aSource = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
+ std::cerr << "Error: no export filter for " << aSource << " found, aborting." << std::endl;
+ }
+
+ return pFilter;
+ }
+ catch ( const Exception& )
+ {
+ return nullptr;
+ }
+}
+
+OUString impl_GuessFilter( const OUString& rUrlOut, const OUString& rDocService )
+{
+ OUString aOutFilter;
+ std::shared_ptr<const SfxFilter> pOutFilter = impl_getExportFilterFromUrl( rUrlOut, rDocService );
+ if (pOutFilter)
+ aOutFilter = pOutFilter->GetFilterName();
+
+ return aOutFilter;
+}
+
+/// dump scripts in a document to the console.
+void scriptCat(const Reference< XModel >& xDoc )
+{
+ Reference< XEmbeddedScripts > xScriptAccess( xDoc, UNO_QUERY );
+ if (!xScriptAccess)
+ {
+ std::cout << "No script access\n";
+ return;
+ }
+
+ // ignore xScriptAccess->getDialogLibraries() for now
+ Reference< css::script::XLibraryContainer2 > xLibraries(
+ xScriptAccess->getBasicLibraries() );
+
+ if ( !xLibraries.is() )
+ {
+ std::cout << "No script libraries\n";
+ return;
+ }
+
+ const Sequence< OUString > aLibNames = xLibraries->getElementNames();
+ std::cout << "Libraries: " << aLibNames.getLength() << "\n";
+ for (OUString const & libName : aLibNames)
+ {
+ std::cout << "Library: '" << libName << "' children: ";
+ Reference< XNameContainer > xContainer;
+ try {
+ if (!xLibraries->isLibraryLoaded( libName ))
+ xLibraries->loadLibrary( libName );
+ xContainer = Reference< XNameContainer >(
+ xLibraries->getByName( libName ), UNO_QUERY );
+ }
+ catch (const css::uno::Exception &e)
+ {
+ std::cout << "[" << libName << "] - failed to load library: " << e.Message << "\n";
+ continue;
+ }
+ if( !xContainer.is() )
+ std::cout << "0\n";
+ else
+ {
+ Sequence< OUString > aObjectNames = xContainer->getElementNames();
+
+ std::cout << aObjectNames.getLength() << "\n\n";
+ for ( sal_Int32 j = 0 ; j < aObjectNames.getLength() ; ++j )
+ {
+ OUString &rObjectName = aObjectNames[j];
+
+ OUString aCodeString;
+ try
+ {
+ Any aCode = xContainer->getByName( rObjectName );
+
+ if (! (aCode >>= aCodeString ) )
+ std::cout << "[" << rObjectName << "] - error fetching code\n";
+ else
+ std::cout << "[" << rObjectName << "]\n"
+ << aCodeString.trim()
+ << "\n[/" << rObjectName << "]\n";
+ }
+ catch (const css::uno::Exception &e)
+ {
+ std::cout << "[" << rObjectName << "] - exception " << e.Message << " fetching code\n";
+ }
+
+ if (j < aObjectNames.getLength() - 1)
+ std::cout << "\n----------------------------------------------------------\n";
+ std::cout << "\n";
+ }
+ }
+ }
+}
+
+// Perform batch print
+void batchPrint( const OUString &rPrinterName, const Reference< XPrintable > &xDoc,
+ const INetURLObject &aObj, const OUString &aName )
+{
+ OUString aFilterOut;
+ OUString aPrinterName;
+ sal_Int32 nPathIndex = rPrinterName.lastIndexOf( ';' );
+ if( nPathIndex != -1 )
+ aFilterOut=rPrinterName.copy( nPathIndex+1 );
+ if( nPathIndex != 0 )
+ aPrinterName=rPrinterName.copy( 0, nPathIndex );
+
+ INetURLObject aOutFilename( aObj );
+ aOutFilename.SetExtension( "pdf" );
+ FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
+ OUString aOutFile = aFilterOut + "/" + aOutFilename.getName();
+
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL( aName, aTempName );
+ OString aSource8 = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
+ FileBase::getSystemPathFromFileURL( aOutFile, aTempName );
+ OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding() );
+
+ std::cout << "print " << aSource8 << " -> " << aTargetURL8;
+ std::cout << " using " << (aPrinterName.isEmpty() ? "<default_printer>" : OUStringToOString( aPrinterName, osl_getThreadTextEncoding() ));
+ std::cout << std::endl;
+
+ // create the custom printer, if given
+ Sequence < PropertyValue > aPrinterArgs( 1 );
+ if( !aPrinterName.isEmpty() )
+ {
+ aPrinterArgs[0].Name = "Name";
+ aPrinterArgs[0].Value <<= aPrinterName;
+ xDoc->setPrinter( aPrinterArgs );
+ }
+
+ // print ( also without user interaction )
+ aPrinterArgs.realloc(2);
+ aPrinterArgs[0].Name = "FileName";
+ aPrinterArgs[0].Value <<= aOutFile;
+ aPrinterArgs[1].Name = "Wait";
+ aPrinterArgs[1].Value <<= true;
+ xDoc->print( aPrinterArgs );
+}
+
+} // anonymous namespace
+
+DispatchWatcher::DispatchWatcher()
+ : m_nRequestCount(0)
+{
+}
+
+
+DispatchWatcher::~DispatchWatcher()
+{
+}
+
+
+bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate )
+{
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ std::vector< DispatchHolder > aDispatches;
+ bool bSetInputFilter = false;
+ OUString aForcedInputFilter;
+
+ for (auto const & aDispatchRequest: aDispatchRequestsList)
+ {
+ // Set Input Filter
+ if ( aDispatchRequest.aRequestType == REQUEST_INFILTER )
+ {
+ bSetInputFilter = true;
+ aForcedInputFilter = aDispatchRequest.aURL;
+ RequestHandler::RequestsCompleted();
+ continue;
+ }
+
+ // create parameter array
+ std::vector<PropertyValue> aArgs;
+
+ // mark request as user interaction from outside
+ aArgs.emplace_back("Referer", 0, Any(OUString("private:OpenEvent")),
+ PropertyState_DIRECT_VALUE);
+
+ OUString aTarget("_default");
+
+ if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
+ aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
+ aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
+ aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT ||
+ aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT)
+ {
+ // documents opened for printing are opened readonly because they must be opened as a
+ // new document and this document could be open already
+ aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // always open a new document for printing, because it must be disposed afterwards
+ aArgs.emplace_back("OpenNewView", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // printing is done in a hidden view
+ aArgs.emplace_back("Hidden", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // load document for printing without user interaction
+ aArgs.emplace_back("Silent", 0, Any(true), PropertyState_DIRECT_VALUE);
+
+ // hidden documents should never be put into open tasks
+ aTarget = "_blank";
+ }
+ else
+ {
+ Reference < XInteractionHandler2 > xInteraction(
+ InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), nullptr) );
+
+ aArgs.emplace_back("InteractionHandler", 0, Any(xInteraction),
+ PropertyState_DIRECT_VALUE);
+
+ aArgs.emplace_back("MacroExecutionMode", 0,
+ Any(css::document::MacroExecMode::USE_CONFIG),
+ PropertyState_DIRECT_VALUE);
+
+ aArgs.emplace_back("UpdateDocMode", 0,
+ Any(css::document::UpdateDocMode::ACCORDING_TO_CONFIG),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ if ( !aDispatchRequest.aPreselectedFactory.isEmpty() )
+ {
+ aArgs.emplace_back(utl::MediaDescriptor::PROP_DOCUMENTSERVICE(), 0,
+ Any(aDispatchRequest.aPreselectedFactory),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ OUString aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) );
+
+ // load the document ... if they are loadable!
+ // Otherwise try to dispatch it ...
+ Reference < XPrintable > xDoc;
+ if(
+ ( aName.startsWith( ".uno" ) ) ||
+ ( aName.startsWith( "slot:" ) ) ||
+ ( aName.startsWith( "macro:" ) ) ||
+ ( aName.startsWith("vnd.sun.star.script") )
+ )
+ {
+ // Attention: URL must be parsed full. Otherwise some detections on it will fail!
+ // It doesn't matter, if parser isn't available. Because; We try loading of URL then ...
+ URL aURL ;
+ aURL.Complete = aName;
+
+ Reference < XDispatch > xDispatcher ;
+ Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+
+ if( xParser.is() )
+ xParser->parseStrict( aURL );
+
+ xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
+ SAL_WARN_IF(
+ !xDispatcher.is(), "desktop.app",
+ "unsupported dispatch request <" << aName << ">");
+ if( xDispatcher.is() )
+ {
+ {
+ osl::MutexGuard aGuard(m_mutex);
+ // Remember request so we can find it in statusChanged!
+ m_nRequestCount++;
+ }
+
+ // Use local vector to store dispatcher because we have to fill our request container before
+ // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
+ aDispatches.emplace_back( aURL, xDispatcher );
+ }
+ }
+ else if ( aName.startsWith( "service:" ) )
+ {
+ // TODO: the dispatch has to be done for loadComponentFromURL as well.
+ URL aURL ;
+ aURL.Complete = aName;
+
+ Reference < XDispatch > xDispatcher ;
+ Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+
+ if( xParser.is() )
+ xParser->parseStrict( aURL );
+
+ xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
+
+ if( xDispatcher.is() )
+ {
+ try
+ {
+ // We have to be listener to catch errors during dispatching URLs.
+ // Otherwise it would be possible to have an office running without an open
+ // window!!
+ Sequence < PropertyValue > aArgs2(1);
+ aArgs2[0].Name = "SynchronMode";
+ aArgs2[0].Value <<= true;
+ Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY );
+ if ( xDisp.is() )
+ xDisp->dispatchWithNotification( aURL, aArgs2, this );
+ else
+ xDispatcher->dispatch( aURL, aArgs2 );
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Desktop::OpenDefault() ignoring Exception while calling XNotifyingDispatch");
+ }
+ }
+ }
+ else
+ {
+ INetURLObject aObj( aName );
+ if ( aObj.GetProtocol() == INetProtocol::PrivSoffice )
+ aTarget = "_default";
+
+ // Set "AsTemplate" argument according to request type
+ if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ||
+ aDispatchRequest.aRequestType == REQUEST_FORCEOPEN )
+ {
+ aArgs.emplace_back("AsTemplate", 0,
+ Any(aDispatchRequest.aRequestType == REQUEST_FORCENEW),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ // if we are called in viewmode, open document read-only
+ if(aDispatchRequest.aRequestType == REQUEST_VIEW) {
+ aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
+ }
+
+ // if we are called with -start set Start in mediadescriptor
+ if(aDispatchRequest.aRequestType == REQUEST_START) {
+ aArgs.emplace_back("StartPresentation", 0, Any(true), PropertyState_DIRECT_VALUE);
+ }
+
+ // Force input filter, if possible
+ if( bSetInputFilter )
+ {
+ sal_Int32 nFilterOptionsIndex = 0;
+ aArgs.emplace_back("FilterName", 0,
+ Any(aForcedInputFilter.getToken(0, ':', nFilterOptionsIndex)),
+ PropertyState_DIRECT_VALUE);
+
+ if (0 < nFilterOptionsIndex)
+ {
+ aArgs.emplace_back("FilterOptions", 0,
+ Any(aForcedInputFilter.copy(nFilterOptionsIndex)),
+ PropertyState_DIRECT_VALUE);
+ }
+ }
+
+ // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
+ try
+ {
+ xDoc.set(comphelper::SynchronousDispatch::dispatch(
+ xDesktop, aName, aTarget, comphelper::containerToSequence(aArgs)),
+ UNO_QUERY);
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL");
+ }
+ catch (const css::io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Dispatchwatcher IOException while calling loadComponentFromURL");
+ }
+ if ( aDispatchRequest.aRequestType == REQUEST_OPEN ||
+ aDispatchRequest.aRequestType == REQUEST_VIEW ||
+ aDispatchRequest.aRequestType == REQUEST_START ||
+ aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ||
+ aDispatchRequest.aRequestType == REQUEST_FORCENEW )
+ {
+ // request is completed
+ RequestHandler::RequestsCompleted();
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
+ aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
+ aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
+ aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT ||
+ aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
+ {
+ if ( xDoc.is() )
+ {
+ // Do we need to save the document in a different format?
+ if ( aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT )
+ {
+// FIXME: factor out into a method ...
+ Reference< XStorable > xStorable( xDoc, UNO_QUERY );
+ if ( xStorable.is() ) {
+ OUString aParam = aDispatchRequest.aPrinterName;
+ sal_Int32 nPathIndex = aParam.lastIndexOf( ';' );
+ sal_Int32 nFilterIndex = aParam.indexOf( ':' );
+ sal_Int32 nImgFilterIndex = aParam.lastIndexOf( '|' );
+ if( nPathIndex < nFilterIndex )
+ nFilterIndex = -1;
+
+ OUString aFilterOut;
+ OUString aImgOut;
+ OUString aFilter;
+ OUString aFilterExt;
+ bool bGuess = false;
+
+ if( nFilterIndex >= 0 )
+ {
+ aFilter = aParam.copy( nFilterIndex+1, nPathIndex-nFilterIndex-1 );
+ aFilterExt = aParam.copy( 0, nFilterIndex );
+ }
+ else
+ {
+ // Guess
+ bGuess = true;
+ aFilterExt = aParam.copy( 0, nPathIndex );
+ }
+
+ if( nImgFilterIndex >= 0 )
+ {
+ aImgOut = aParam.copy( nImgFilterIndex+1 );
+ aFilterOut = aParam.copy( nPathIndex+1, nImgFilterIndex-nPathIndex-1 );
+ }
+ else
+ aFilterOut = aParam.copy( nPathIndex+1 );
+
+ FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
+ INetURLObject aOutFilename(aFilterOut);
+ aOutFilename.Append(aObj.getName(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::NONE));
+ aOutFilename.SetExtension(aFilterExt);
+ OUString aOutFile
+ = aOutFilename.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ std::unique_ptr<utl::TempFile> fileForCat;
+ if( aDispatchRequest.aRequestType == REQUEST_CAT )
+ {
+ fileForCat = std::make_unique<utl::TempFile>();
+ if (fileForCat->IsValid())
+ fileForCat->EnableKillingFile();
+ else
+ std::cerr << "Error: Cannot create temporary file..." << std::endl ;
+ aOutFile = fileForCat->GetURL();
+ }
+
+ if ( bGuess )
+ {
+ OUString aDocService;
+ Reference< XModel > xModel( xDoc, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ utl::MediaDescriptor aMediaDesc( xModel->getArgs() );
+ aDocService = aMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE(), OUString() );
+ }
+ aFilter = impl_GuessFilter( aOutFile, aDocService );
+ }
+
+ if (aFilter.isEmpty())
+ {
+ std::cerr << "Error: no export filter" << std::endl;
+ }
+ else
+ {
+ sal_Int32 nFilterOptionsIndex = aFilter.indexOf(':');
+ sal_Int32 nProps = ( 0 < nFilterOptionsIndex ) ? 3 : 2;
+
+ if ( !aImgOut.isEmpty() )
+ nProps +=1;
+ Sequence<PropertyValue> conversionProperties( nProps );
+ conversionProperties[0].Name = "Overwrite";
+ conversionProperties[0].Value <<= true;
+
+ conversionProperties[1].Name = "FilterName";
+ if( 0 < nFilterOptionsIndex )
+ {
+ conversionProperties[1].Value <<= aFilter.copy(0, nFilterOptionsIndex);
+
+ conversionProperties[2].Name = "FilterOptions";
+ conversionProperties[2].Value <<= aFilter.copy(nFilterOptionsIndex + 1);
+ }
+ else
+ {
+ conversionProperties[1].Value <<= aFilter;
+ }
+
+ if ( !aImgOut.isEmpty() )
+ {
+ conversionProperties[nProps-1].Name = "ImageFilter";
+ conversionProperties[nProps-1].Value <<= aImgOut;
+ }
+
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL(aName, aTempName);
+ OString aSource8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
+ FileBase::getSystemPathFromFileURL(aOutFile, aTempName);
+ OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
+ if (aDispatchRequest.aRequestType != REQUEST_CAT)
+ {
+ std::cout << "convert " << aSource8 << " -> " << aTargetURL8;
+ std::cout << " using filter : " << OUStringToOString(aFilter, osl_getThreadTextEncoding()) << std::endl;
+ if (FStatHelper::IsDocument(aOutFile))
+ std::cout << "Overwriting: " << OUStringToOString(aTempName, osl_getThreadTextEncoding()) << std::endl ;
+ }
+ try
+ {
+ xStorable->storeToURL(aOutFile, conversionProperties);
+ }
+ catch (const Exception& rException)
+ {
+ std::cerr << "Error: Please verify input parameters...";
+ if (!rException.Message.isEmpty())
+ std::cerr << " (" << rException.Message << ")";
+ std::cerr << std::endl;
+ }
+
+ if (fileForCat && fileForCat->IsValid())
+ {
+ SvStream* aStream = fileForCat->GetStream(StreamMode::STD_READ);
+ while (aStream->good())
+ {
+ OString aStr;
+ aStream->ReadLine(aStr, SAL_MAX_INT32);
+ for (sal_Int32 i = 0; i < aStr.getLength(); ++i)
+ {
+ std::cout << aStr[i];
+ }
+ std::cout << std::endl;
+ }
+ }
+ }
+ }
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
+ {
+ Reference< XModel > xModel( xDoc, UNO_QUERY );
+ if( xModel.is() )
+ scriptCat( xModel );
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_BATCHPRINT )
+ {
+ batchPrint( aDispatchRequest.aPrinterName, xDoc, aObj, aName );
+ }
+ else
+ {
+ if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO )
+ {
+ // create the printer
+ Sequence < PropertyValue > aPrinterArgs( 1 );
+ aPrinterArgs[0].Name = "Name";
+ aPrinterArgs[0].Value <<= aDispatchRequest.aPrinterName;
+ xDoc->setPrinter( aPrinterArgs );
+ }
+
+ // print ( also without user interaction )
+ Sequence < PropertyValue > aPrinterArgs( 1 );
+ aPrinterArgs[0].Name = "Wait";
+ aPrinterArgs[0].Value <<= true;
+ xDoc->print( aPrinterArgs );
+ }
+ }
+ else
+ {
+ std::cerr << "Error: source file could not be loaded" << std::endl;
+ }
+
+ // remove the document
+ try
+ {
+ Reference < XCloseable > xClose( xDoc, UNO_QUERY );
+ if ( xClose.is() )
+ xClose->close( true );
+ else
+ {
+ Reference < XComponent > xComp( xDoc, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->dispose();
+ }
+ }
+ catch (const css::util::CloseVetoException&)
+ {
+ }
+
+ // request is completed
+ RequestHandler::RequestsCompleted();
+ }
+ }
+ }
+
+ if ( !aDispatches.empty() )
+ {
+ // Execute all asynchronous dispatches now after we placed them into our request container!
+ Sequence < PropertyValue > aArgs( 2 );
+ aArgs[0].Name = "Referer";
+ aArgs[0].Value <<= OUString("private:OpenEvent");
+ aArgs[1].Name = "SynchronMode";
+ aArgs[1].Value <<= true;
+
+ for (const DispatchHolder & aDispatche : aDispatches)
+ {
+ Reference< XDispatch > xDispatch = aDispatche.xDispatch;
+ Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY );
+ if ( xDisp.is() )
+ xDisp->dispatchWithNotification( aDispatche.aURL, aArgs, this );
+ else
+ {
+ {
+ osl::MutexGuard aGuard(m_mutex);
+ m_nRequestCount--;
+ }
+ xDispatch->dispatch( aDispatche.aURL, aArgs );
+ }
+ }
+ }
+
+ ::osl::ClearableMutexGuard aGuard(m_mutex);
+ bool bEmpty = (m_nRequestCount == 0);
+ aGuard.clear();
+
+ // No more asynchronous requests?
+ // The requests are removed from the request container after they called back to this
+ // implementation via statusChanged!!
+ if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ )
+ {
+ // We have to check if we have an open task otherwise we have to shutdown the office.
+ Reference< XElementAccess > xList = xDesktop->getFrames();
+
+ if ( !xList->hasElements() )
+ {
+ // We don't have any task open so we have to shutdown ourself!!
+ return xDesktop->terminate();
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL DispatchWatcher::disposing( const css::lang::EventObject& )
+{
+}
+
+
+void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& )
+{
+ osl::ClearableMutexGuard aGuard(m_mutex);
+ sal_Int16 nCount = --m_nRequestCount;
+ aGuard.clear();
+ RequestHandler::RequestsCompleted();
+ if ( !nCount && !RequestHandler::AreRequestsPending() )
+ {
+ // We have to check if we have an open task otherwise we have to shutdown the office.
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XElementAccess > xList = xDesktop->getFrames();
+
+ if ( !xList->hasElements() )
+ {
+ // We don't have any task open so we have to shutdown ourself!!
+ xDesktop->terminate();
+ }
+ }
+}
+
+} // namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/dispatchwatcher.hxx b/desktop/source/app/dispatchwatcher.hxx
new file mode 100644
index 000000000..ca66d33bf
--- /dev/null
+++ b/desktop/source/app/dispatchwatcher.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_DISPATCHWATCHER_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_DISPATCHWATCHER_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <optional>
+
+#include <vector>
+
+namespace desktop
+{
+
+/*
+ Class for controls dispatching of command URL through office command line. There
+ are "dangerous" command URLs, that can result in a running office without UI. To prevent
+ this situation the implementation monitors all dispatches and looks for an open task if
+ there is arose a problem. If there is none the office will be shutdown to prevent a
+ running office without UI.
+*/
+class DispatchWatcher : public ::cppu::WeakImplHelper< css::frame::XDispatchResultListener >
+{
+ public:
+ enum RequestType
+ {
+ REQUEST_OPEN,
+ REQUEST_VIEW,
+ REQUEST_START,
+ REQUEST_PRINT,
+ REQUEST_PRINTTO,
+ REQUEST_FORCEOPEN,
+ REQUEST_FORCENEW,
+ REQUEST_CONVERSION,
+ REQUEST_INFILTER,
+ REQUEST_BATCHPRINT,
+ REQUEST_CAT,
+ REQUEST_SCRIPT_CAT
+ };
+
+ struct DispatchRequest
+ {
+ RequestType aRequestType;
+ OUString aURL;
+ std::optional< OUString > aCwdUrl;
+ OUString aPrinterName; // also conversion params
+ OUString aPreselectedFactory;
+ };
+
+ DispatchWatcher();
+
+ virtual ~DispatchWatcher() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XDispachResultListener
+ virtual void SAL_CALL dispatchFinished( const css::frame::DispatchResultEvent& aEvent ) override;
+
+ // execute new dispatch request
+ bool executeDispatchRequests( const std::vector<DispatchRequest>& aDispatches, bool bNoTerminate );
+
+ private:
+ osl::Mutex m_mutex;
+
+ sal_Int16 m_nRequestCount;
+};
+
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_APP_DISPATCHWATCHER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/langselect.cxx b/desktop/source/app/langselect.cxx
new file mode 100644
index 000000000..468ba9368
--- /dev/null
+++ b/desktop/source/app/langselect.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/configuration.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <officecfg/Office/Linguistic.hxx>
+#include <officecfg/Setup.hxx>
+#include <officecfg/System.hxx>
+#include <rtl/ustring.hxx>
+#include <svl/languageoptions.hxx>
+#include <svtools/langhelp.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <app.hxx>
+
+#include "cmdlineargs.hxx"
+#include "langselect.hxx"
+
+namespace desktop::langselect {
+
+namespace {
+
+OUString foundLocale;
+
+void setMsLangIdFallback(OUString const & locale) {
+ // #i32939# setting of default document language
+ // See #i42730# for rules for determining source of settings
+ if (locale.isEmpty())
+ return;
+
+ LanguageType type = LanguageTag::convertToLanguageTypeWithFallback(locale);
+ switch (SvtLanguageOptions::GetScriptTypeOfLanguage(type)) {
+ case SvtScriptType::ASIAN:
+ MsLangId::setConfiguredAsianFallback(type);
+ break;
+ case SvtScriptType::COMPLEX:
+ MsLangId::setConfiguredComplexFallback(type);
+ break;
+ default:
+ MsLangId::setConfiguredWesternFallback(type);
+ break;
+ }
+}
+
+}
+
+bool prepareLocale() {
+ // #i42730# Get the windows 16Bit locale, it should be preferred over the UI
+ // locale:
+ setMsLangIdFallback(officecfg::System::L10N::SystemLocale::get());
+ // #i32939# Use system locale to set document default locale:
+ setMsLangIdFallback(officecfg::System::L10N::Locale::get());
+ css::uno::Sequence<OUString> inst(
+ officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
+ OUString locale(officecfg::Office::Linguistic::General::UILocale::get());
+ if (!locale.isEmpty()) {
+ locale = getInstalledLocaleForLanguage(inst, locale);
+ if (locale.isEmpty()) {
+ // Selected language is not/no longer installed:
+ try {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Linguistic::General::UILocale::set(
+ "", batch);
+ batch->commit();
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
+ }
+ }
+ }
+ bool cmdLanguage = false;
+ if (locale.isEmpty()) {
+ locale = getInstalledLocaleForLanguage(
+ inst, Desktop::GetCommandLineArgs().GetLanguage());
+ if (!locale.isEmpty()) {
+ cmdLanguage = true;
+ }
+ }
+ if (locale.isEmpty()) {
+ locale = getInstalledLocaleForSystemUILanguage(inst, true);
+ }
+ if (locale.isEmpty()) {
+ return false;
+ }
+ LanguageTag tag(locale);
+ // Prepare default config provider by localizing it to the selected
+ // locale this will ensure localized configuration settings to be
+ // selected according to the UI language:
+ css::uno::Reference<css::lang::XLocalizable>(
+ css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()),
+ css::uno::UNO_QUERY_THROW)->setLocale(tag.getLocale(false));
+ if (!cmdLanguage) {
+ try {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::L10N::ooLocale::set(locale, batch);
+ batch->commit();
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
+ }
+ }
+ MsLangId::setConfiguredSystemUILanguage(tag.getLanguageType(false));
+
+ OUString setupSysLoc(officecfg::Setup::L10N::ooSetupSystemLocale::get());
+ LanguageTag::setConfiguredSystemLanguage(
+ setupSysLoc.isEmpty()
+ ? MsLangId::getSystemLanguage()
+ : LanguageTag(setupSysLoc).getLanguageType(false));
+ // #i32939# setting of default document locale
+ // #i32939# this should not be based on the UI language
+ // So obtain the system locale now configured just above and pass it on,
+ // resolved of course.
+ LanguageTag docTag(LANGUAGE_SYSTEM);
+ setMsLangIdFallback(docTag.getBcp47());
+
+ foundLocale = locale;
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/langselect.hxx b/desktop/source/app/langselect.hxx
new file mode 100644
index 000000000..ccc19d3a1
--- /dev/null
+++ b/desktop/source/app/langselect.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_LANGSELECT_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_LANGSELECT_HXX
+
+#include <sal/config.h>
+
+namespace desktop::langselect {
+
+bool prepareLocale();
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/lockfile2.cxx b/desktop/source/app/lockfile2.cxx
new file mode 100644
index 000000000..44447927f
--- /dev/null
+++ b/desktop/source/app/lockfile2.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include <tools/config.hxx>
+#include <lockfile.hxx>
+
+namespace desktop {
+
+bool Lockfile_execWarning( Lockfile const * that )
+{
+ // read information from lock
+ OUString aLockname = that->m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup( LOCKFILE_GROUP );
+ OString aHost = aConfig.ReadKey( LOCKFILE_HOSTKEY );
+ OString aUser = aConfig.ReadKey( LOCKFILE_USERKEY );
+ OString aTime = aConfig.ReadKey( LOCKFILE_TIMEKEY );
+
+ // display warning and return response
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo, DpResId(STR_QUERY_USERDATALOCKED)));
+ // set box title
+ OUString aTitle = DpResId(STR_TITLE_USERDATALOCKED);
+ xBox->set_title( aTitle );
+ // insert values...
+ OUString aMsgText = xBox->get_primary_text();
+ aMsgText = aMsgText.replaceFirst(
+ "$u", OStringToOUString( aUser, RTL_TEXTENCODING_ASCII_US) );
+ aMsgText = aMsgText.replaceFirst(
+ "$h", OStringToOUString( aHost, RTL_TEXTENCODING_ASCII_US) );
+ aMsgText = aMsgText.replaceFirst(
+ "$t", OStringToOUString( aTime, RTL_TEXTENCODING_ASCII_US) );
+ xBox->set_primary_text(aMsgText);
+ // do it
+ return xBox->run() == RET_YES;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/main.c b/desktop/source/app/main.c
new file mode 100644
index 000000000..643c42a7a
--- /dev/null
+++ b/desktop/source/app/main.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/main.h>
+
+#include "sofficemain.h"
+
+#ifdef DBG_UTIL
+#ifdef __gnu_linux__
+#include <stdio.h>
+#include <stdlib.h>
+
+static int g_Exiting = 0;
+
+/* HACK: detect calls to xmlCleanupParser, which causes hard to debug crashes */
+__attribute__ ((visibility("default"))) void xmlCleanupParser(void)
+{
+ /* there are libraries that register xmlCleanupParser as an atexit handler,
+ which is not entirely sound (another atexit handler could want to
+ use libxml), but not enough of a problem to complain.
+ (example found by llunak: KDE's Strigi library) */
+ if (!g_Exiting)
+ {
+ fprintf(stderr, "\n*** ERROR: DO NOT call xmlCleanupParser()\n\n");
+ abort();
+ }
+}
+#endif
+#endif
+
+SAL_IMPLEMENT_MAIN() {
+ int ret = soffice_main();
+#ifdef DBG_UTIL
+#ifdef __gnu_linux__
+ g_Exiting = 1;
+#endif
+#endif
+ return ret;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/officeipcthread.cxx b/desktop/source/app/officeipcthread.cxx
new file mode 100644
index 000000000..18c9f4e40
--- /dev/null
+++ b/desktop/source/app/officeipcthread.cxx
@@ -0,0 +1,1375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <config_dbus.h>
+#include <config_features.h>
+#include <config_feature_desktop.h>
+
+#include <app.hxx>
+#include "officeipcthread.hxx"
+#include "cmdlineargs.hxx"
+#include "dispatchwatcher.hxx"
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <salhelper/thread.hxx>
+#include <sal/log.hxx>
+#include <unotools/bootstrap.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/configmgr.hxx>
+#include <osl/pipe.hxx>
+#include <rtl/digest.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/instance.hxx>
+#include <osl/conditn.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <rtl/strbuf.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/file.hxx>
+#include <rtl/process.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <memory>
+
+#if ENABLE_DBUS
+#include <dbus/dbus.h>
+#include <sys/socket.h>
+#endif
+
+using namespace desktop;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+
+namespace {
+
+static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
+static char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
+static char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
+
+// Receives packets from the pipe until a packet ends in a NUL character (that
+// will not be included in the returned string) or it cannot read anything (due
+// to error or closed pipe, in which case an empty string will be returned to
+// signal failure):
+OString readStringFromPipe(osl::StreamPipe const & pipe) {
+ for (OStringBuffer str;;) {
+ char buf[1024];
+ sal_Int32 n = pipe.recv(buf, SAL_N_ELEMENTS(buf));
+ if (n <= 0) {
+ SAL_INFO("desktop.app", "read empty string");
+ return "";
+ }
+ bool end = false;
+ if (buf[n - 1] == '\0') {
+ end = true;
+ --n;
+ }
+ str.append(buf, n);
+ //TODO: how does OStringBuffer.append handle overflow?
+ if (end) {
+ auto s = str.makeStringAndClear();
+ SAL_INFO("desktop.app", "read <" << s << ">");
+ return s;
+ }
+ }
+}
+
+}
+
+namespace desktop
+{
+
+namespace {
+
+class Parser: public CommandLineArgs::Supplier {
+public:
+ explicit Parser(OString const & input): m_input(input) {
+ if (!m_input.match(ARGUMENT_PREFIX) ||
+ m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
+ {
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
+ switch (m_input[m_index++]) {
+ case '0':
+ break;
+ case '1':
+ {
+ OUString url;
+ if (!next(&url, false)) {
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ m_cwdUrl = url;
+ break;
+ }
+ case '2':
+ {
+ OUString path;
+ if (!next(&path, false)) {
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ OUString url;
+ if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
+ osl::FileBase::E_None)
+ {
+ m_cwdUrl = url;
+ }
+ break;
+ }
+ default:
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ }
+
+ virtual std::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
+
+ virtual bool next(OUString * argument) override { return next(argument, true); }
+
+private:
+ bool next(OUString * argument, bool prefix) {
+ OSL_ASSERT(argument != nullptr);
+ if (m_index < m_input.getLength()) {
+ if (prefix) {
+ if (m_input[m_index] != ',') {
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ ++m_index;
+ }
+ OStringBuffer b;
+ while (m_index < m_input.getLength()) {
+ char c = m_input[m_index];
+ if (c == ',') {
+ break;
+ }
+ ++m_index;
+ if (c == '\\') {
+ if (m_index >= m_input.getLength())
+ throw CommandLineArgs::Supplier::Exception();
+ c = m_input[m_index++];
+ switch (c) {
+ case '0':
+ c = '\0';
+ break;
+ case ',':
+ case '\\':
+ break;
+ default:
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ }
+ b.append(c);
+ }
+ OString b2(b.makeStringAndClear());
+ if (!rtl_convertStringToUString(
+ &argument->pData, b2.getStr(), b2.getLength(),
+ RTL_TEXTENCODING_UTF8,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ throw CommandLineArgs::Supplier::Exception();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ std::optional< OUString > m_cwdUrl;
+ OString m_input;
+ sal_Int32 m_index;
+};
+
+bool addArgument(OStringBuffer &rArguments, char prefix,
+ const OUString &rArgument)
+{
+ OString utf8;
+ if (!rArgument.convertToString(
+ &utf8, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ return false;
+ }
+ rArguments.append(prefix);
+ for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
+ char c = utf8[i];
+ switch (c) {
+ case '\0':
+ rArguments.append("\\0");
+ break;
+ case ',':
+ rArguments.append("\\,");
+ break;
+ case '\\':
+ rArguments.append("\\\\");
+ break;
+ default:
+ rArguments.append(c);
+ break;
+ }
+ }
+ return true;
+}
+
+}
+
+rtl::Reference< RequestHandler > RequestHandler::pGlobal;
+
+// Turns a string in aMsg such as file:///home/foo/.libreoffice/3
+// Into a hex string of well known length ff132a86...
+static OUString CreateMD5FromString( const OUString& aMsg )
+{
+ SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'");
+
+ rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
+ if ( handle )
+ {
+ const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr());
+ sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode );
+ sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
+ std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
+
+ rtl_digest_init( handle, pData, nSize );
+ rtl_digest_update( handle, pData, nSize );
+ rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
+ rtl_digest_destroy( handle );
+
+ // Create hex-value string from the MD5 value to keep the string size minimal
+ OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
+ for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
+ aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 );
+
+ return aBuffer.makeStringAndClear();
+ }
+
+ return OUString();
+}
+
+namespace {
+
+class ProcessEventsClass_Impl
+{
+public:
+ DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, void );
+ DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, void );
+};
+
+}
+
+IMPL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, pEvent, void )
+{
+ // Application events are processed by the Desktop::HandleAppEvent implementation.
+ Desktop::HandleAppEvent( *static_cast<ApplicationEvent*>(pEvent) );
+ delete static_cast<ApplicationEvent*>(pEvent);
+}
+
+IMPL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent, void )
+{
+ // Documents requests are processed by the RequestHandler implementation
+ ProcessDocumentsRequest* pDocsRequest = static_cast<ProcessDocumentsRequest*>(pEvent);
+ RequestHandler::ExecuteCmdLineRequests(*pDocsRequest, false);
+ delete pDocsRequest;
+}
+
+static void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
+{
+ Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, CallEvent ), pEvent );
+}
+
+static void ImplPostProcessDocumentsEvent( std::unique_ptr<ProcessDocumentsRequest> pEvent )
+{
+ Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent.release() );
+}
+
+oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void* /*pData*/, oslSignalInfo* pInfo)
+{
+ if( pInfo->Signal == osl_Signal_Terminate )
+ RequestHandler::SetDowning();
+ return osl_Signal_ActCallNextHdl;
+}
+
+
+// The RequestHandlerController implementation is a bookkeeper for all pending requests
+// that were created by the RequestHandler. The requests are waiting to be processed by
+// our framework loadComponentFromURL function (e.g. open/print request).
+// During shutdown the framework is asking RequestHandlerController about pending requests.
+// If there are pending requests framework has to stop the shutdown process. It is waiting
+// for these requests because framework is not able to handle shutdown and open a document
+// concurrently.
+
+
+// XServiceInfo
+OUString SAL_CALL RequestHandlerController::getImplementationName()
+{
+ return "com.sun.star.comp.RequestHandlerController";
+}
+
+sal_Bool RequestHandlerController::supportsService(
+ OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL RequestHandlerController::getSupportedServiceNames()
+{
+ return { };
+}
+
+// XEventListener
+void SAL_CALL RequestHandlerController::disposing( const EventObject& )
+{
+}
+
+// XTerminateListener
+void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
+{
+ // Desktop ask about pending request through our office ipc pipe. We have to
+ // be sure that no pending request is waiting because framework is not able to
+ // handle shutdown and open a document concurrently.
+
+ if ( RequestHandler::AreRequestsPending() )
+ throw TerminationVetoException();
+ RequestHandler::SetDowning();
+}
+
+void SAL_CALL RequestHandlerController::notifyTermination( const EventObject& )
+{
+}
+
+class IpcThread: public salhelper::Thread {
+public:
+ void start(RequestHandler * handler) {
+ m_handler = handler;
+ launch();
+ }
+
+ virtual void close() = 0;
+
+protected:
+ explicit IpcThread(char const * name): Thread(name), m_handler(nullptr) {}
+
+ virtual ~IpcThread() override {}
+
+ bool process(OString const & arguments, bool * waitProcessed);
+
+ RequestHandler * m_handler;
+};
+
+class PipeIpcThread: public IpcThread {
+public:
+ static RequestHandler::Status enable(rtl::Reference<IpcThread> * thread);
+
+private:
+ explicit PipeIpcThread(osl::Pipe const & pipe):
+ IpcThread("PipeIPC"), pipe_(pipe)
+ {}
+
+ virtual ~PipeIpcThread() override {}
+
+ void execute() override;
+
+ void close() override { pipe_.close(); }
+
+ osl::Pipe pipe_;
+};
+
+#if ENABLE_DBUS
+
+namespace {
+
+struct DbusConnectionHolder {
+ explicit DbusConnectionHolder(DBusConnection * theConnection):
+ connection(theConnection)
+ {}
+
+ DbusConnectionHolder(DbusConnectionHolder && other): connection(nullptr)
+ { std::swap(connection, other.connection); }
+
+ ~DbusConnectionHolder() {
+ if (connection != nullptr) {
+ dbus_connection_close(connection);
+ dbus_connection_unref(connection);
+ }
+ }
+
+ DBusConnection * connection;
+};
+
+struct DbusMessageHolder {
+ explicit DbusMessageHolder(DBusMessage * theMessage): message(theMessage) {}
+
+ ~DbusMessageHolder() { clear(); }
+
+ void clear() {
+ if (message != nullptr) {
+ dbus_message_unref(message);
+ }
+ message = nullptr;
+ }
+
+ DBusMessage * message;
+
+private:
+ DbusMessageHolder(DbusMessageHolder const &) = delete;
+ DbusMessageHolder& operator =(DbusMessageHolder const &) = delete;
+};
+
+}
+
+class DbusIpcThread: public IpcThread {
+public:
+ static RequestHandler::Status enable(rtl::Reference<IpcThread> * thread);
+
+private:
+ explicit DbusIpcThread(DbusConnectionHolder && connection):
+ IpcThread("DbusIPC"), connection_(std::move(connection))
+ {}
+
+ virtual ~DbusIpcThread() override {}
+
+ void execute() override;
+
+ void close() override;
+
+ DbusConnectionHolder connection_;
+};
+
+RequestHandler::Status DbusIpcThread::enable(rtl::Reference<IpcThread> * thread)
+{
+ assert(thread != nullptr);
+ if (!dbus_threads_init_default()) {
+ SAL_WARN("desktop.app", "dbus_threads_init_default failed");
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ DBusError e;
+ dbus_error_init(&e);
+ DbusConnectionHolder con(dbus_bus_get_private(DBUS_BUS_SESSION, &e));
+ assert((con.connection == nullptr) == bool(dbus_error_is_set(&e)));
+ if (con.connection == nullptr) {
+ SAL_WARN(
+ "desktop.app",
+ "dbus_bus_get_private failed with: " << e.name << ": "
+ << e.message);
+ dbus_error_free(&e);
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ for (;;) {
+ int n = dbus_bus_request_name(
+ con.connection, "org.libreoffice.LibreOfficeIpc0",
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &e);
+ assert((n == -1) == bool(dbus_error_is_set(&e)));
+ switch (n) {
+ case -1:
+ SAL_WARN(
+ "desktop.app",
+ "dbus_bus_request_name failed with: " << e.name << ": "
+ << e.message);
+ dbus_error_free(&e);
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+ *thread = new DbusIpcThread(std::move(con));
+ return RequestHandler::IPC_STATUS_OK;
+ case DBUS_REQUEST_NAME_REPLY_EXISTS:
+ {
+ OStringBuffer buf(ARGUMENT_PREFIX);
+ OUString arg;
+ if (!(utl::Bootstrap::getProcessWorkingDir(arg)
+ && addArgument(buf, '1', arg)))
+ {
+ buf.append('0');
+ }
+ sal_uInt32 narg = rtl_getAppCommandArgCount();
+ for (sal_uInt32 i = 0; i != narg; ++i) {
+ rtl_getAppCommandArg(i, &arg.pData);
+ if (!addArgument(buf, ',', arg)) {
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ }
+ char const * argstr = buf.getStr();
+ DbusMessageHolder msg(
+ dbus_message_new_method_call(
+ "org.libreoffice.LibreOfficeIpc0",
+ "/org/libreoffice/LibreOfficeIpc0",
+ "org.libreoffice.LibreOfficeIpcIfc0", "Execute"));
+ if (msg.message == nullptr) {
+ SAL_WARN(
+ "desktop.app", "dbus_message_new_method_call failed");
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ DBusMessageIter it;
+ dbus_message_iter_init_append(msg.message, &it);
+ if (!dbus_message_iter_append_basic(
+ &it, DBUS_TYPE_STRING, &argstr))
+ {
+ SAL_WARN(
+ "desktop.app", "dbus_message_iter_append_basic failed");
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ DbusMessageHolder repl(
+ dbus_connection_send_with_reply_and_block(
+ con.connection, msg.message, 0x7FFFFFFF, &e));
+ assert(
+ (repl.message == nullptr) == bool(dbus_error_is_set(&e)));
+ if (repl.message == nullptr) {
+ SAL_INFO(
+ "desktop.app",
+ "dbus_connection_send_with_reply_and_block failed"
+ " with: " << e.name << ": " << e.message);
+ dbus_error_free(&e);
+ break;
+ }
+ return RequestHandler::IPC_STATUS_2ND_OFFICE;
+ }
+ case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+ case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+ SAL_WARN(
+ "desktop.app",
+ "dbus_bus_request_name failed with unexpected " << +n);
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ default:
+ for (;;) std::abort();
+ }
+ }
+}
+
+void DbusIpcThread::execute()
+{
+ assert(m_handler != nullptr);
+ m_handler->cReady.wait();
+ for (;;) {
+ {
+ osl::MutexGuard g(RequestHandler::GetMutex());
+ if (m_handler->mState == RequestHandler::State::Downing) {
+ break;
+ }
+ }
+ if (!dbus_connection_read_write(connection_.connection, -1)) {
+ break;
+ }
+ for (;;) {
+ DbusMessageHolder msg(
+ dbus_connection_pop_message(connection_.connection));
+ if (msg.message == nullptr) {
+ break;
+ }
+ if (!dbus_message_is_method_call(
+ msg.message, "org.libreoffice.LibreOfficeIpcIfc0",
+ "Execute"))
+ {
+ SAL_INFO("desktop.app", "unknown DBus message ignored");
+ continue;
+ }
+ DBusMessageIter it;
+ if (!dbus_message_iter_init(msg.message, &it)) {
+ SAL_WARN(
+ "desktop.app", "DBus message without argument ignored");
+ continue;
+ }
+ if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
+ SAL_WARN(
+ "desktop.app",
+ "DBus message with non-string argument ignored");
+ continue;
+ }
+ char const * argstr;
+ dbus_message_iter_get_basic(&it, &argstr);
+ bool waitProcessed = false;
+ {
+ osl::MutexGuard g(RequestHandler::GetMutex());
+ if (!process(argstr, &waitProcessed)) {
+ continue;
+ }
+ }
+ if (waitProcessed) {
+ m_handler->cProcessed.wait();
+ }
+ DbusMessageHolder repl(dbus_message_new_method_return(msg.message));
+ if (repl.message == nullptr) {
+ SAL_WARN(
+ "desktop.app", "dbus_message_new_method_return failed");
+ continue;
+ }
+ dbus_uint32_t serial = 0;
+ if (!dbus_connection_send(
+ connection_.connection, repl.message, &serial)) {
+ SAL_WARN("desktop.app", "dbus_connection_send failed");
+ continue;
+ }
+ dbus_connection_flush(connection_.connection);
+ }
+ }
+}
+
+void DbusIpcThread::close() {
+ assert(connection_.connection != nullptr);
+ // Make dbus_connection_read_write fall out of internal poll call blocking
+ // on POLLIN:
+ int fd;
+ if (!dbus_connection_get_socket(connection_.connection, &fd)) {
+ SAL_WARN("desktop.app", "dbus_connection_get_socket failed");
+ return;
+ }
+ if (shutdown(fd, SHUT_RD) == -1) {
+ auto const e = errno;
+ SAL_WARN("desktop.app", "shutdown failed with errno " << e);
+ }
+}
+
+#endif
+
+namespace
+{
+ class theRequestHandlerMutex
+ : public rtl::Static<osl::Mutex, theRequestHandlerMutex> {};
+}
+
+::osl::Mutex& RequestHandler::GetMutex()
+{
+ return theRequestHandlerMutex::get();
+}
+
+void RequestHandler::SetDowning()
+{
+ // We have the order to block all incoming requests. Framework
+ // wants to shutdown and we have to make sure that no loading/printing
+ // requests are executed anymore.
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ if ( pGlobal.is() )
+ pGlobal->mState = State::Downing;
+}
+
+void RequestHandler::EnableRequests()
+{
+ // switch between just queueing the requests and executing them
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ if ( pGlobal.is() )
+ {
+ if (pGlobal->mState != State::Downing) {
+ pGlobal->mState = State::RequestsEnabled;
+ }
+ // hit the compiler over the head - this avoids GCC -Werror=maybe-uninitialized
+ std::optional<OUString> tmp;
+ ProcessDocumentsRequest aEmptyReq(tmp);
+ // trigger already queued requests
+ RequestHandler::ExecuteCmdLineRequests(aEmptyReq, true);
+ }
+}
+
+bool RequestHandler::AreRequestsPending()
+{
+ // Give info about pending requests
+ ::osl::MutexGuard aGuard( GetMutex() );
+ if ( pGlobal.is() )
+ return ( pGlobal->mnPendingRequests > 0 );
+ else
+ return false;
+}
+
+void RequestHandler::RequestsCompleted()
+{
+ // Remove nCount pending requests from our internal counter
+ ::osl::MutexGuard aGuard( GetMutex() );
+ if ( pGlobal.is() )
+ {
+ if ( pGlobal->mnPendingRequests > 0 )
+ pGlobal->mnPendingRequests --;
+ }
+}
+
+RequestHandler::Status RequestHandler::Enable(bool ipc)
+{
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ if( pGlobal.is() )
+ return IPC_STATUS_OK;
+
+#if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX
+ ipc = false;
+#endif
+
+ if (!ipc) {
+ pGlobal = new RequestHandler;
+ return IPC_STATUS_OK;
+ }
+
+ enum class Kind { Pipe, Dbus };
+ Kind kind;
+#if ENABLE_DBUS
+ kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
+#else
+ kind = Kind::Pipe;
+#endif
+ rtl::Reference<IpcThread> thread;
+ Status stat = Status(); // silence bogus potentially-uninitialized warnings
+ switch (kind) {
+ case Kind::Pipe:
+ stat = PipeIpcThread::enable(&thread);
+ break;
+ case Kind::Dbus:
+#if ENABLE_DBUS
+ stat = DbusIpcThread::enable(&thread);
+ break;
+#endif
+ default:
+ assert(false);
+ }
+ assert(thread.is() == (stat == IPC_STATUS_OK));
+ if (stat == IPC_STATUS_OK) {
+ pGlobal = new RequestHandler;
+ pGlobal->mIpcThread = thread;
+ pGlobal->mIpcThread->start(pGlobal.get());
+ }
+ return stat;
+}
+
+RequestHandler::Status PipeIpcThread::enable(rtl::Reference<IpcThread> * thread)
+{
+ assert(thread != nullptr);
+
+ // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
+ // this information from a unotools implementation.
+ OUString aUserInstallPath;
+ ::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
+ if (aLocateResult != utl::Bootstrap::PATH_EXISTS
+ && aLocateResult != utl::Bootstrap::PATH_VALID)
+ {
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+
+ // Try to determine if we are the first office or not! This should prevent multiple
+ // access to the user directory !
+ // First we try to create our pipe if this fails we try to connect. We have to do this
+ // in a loop because the other office can crash or shutdown between createPipe
+ // and connectPipe!!
+ auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
+
+ // Check result to create a hash code from the user install path
+ if ( aUserInstallPathHashCode.isEmpty() )
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
+
+ osl::Pipe pipe;
+ enum PipeMode
+ {
+ PIPEMODE_DONTKNOW,
+ PIPEMODE_CREATED,
+ PIPEMODE_CONNECTED
+ };
+ PipeMode nPipeMode = PIPEMODE_DONTKNOW;
+
+ OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
+ do
+ {
+ osl::Security security;
+
+ // Try to create pipe
+ if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
+ {
+ // Pipe created
+ nPipeMode = PIPEMODE_CREATED;
+ }
+ else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
+ {
+ osl::StreamPipe aStreamPipe(pipe.getHandle());
+ if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
+ {
+ // Pipe connected to first office
+ nPipeMode = PIPEMODE_CONNECTED;
+ }
+ else
+ {
+ // Pipe connection failed (other office exited or crashed)
+ TimeValue tval;
+ tval.Seconds = 0;
+ tval.Nanosec = 500000000;
+ salhelper::Thread::wait( tval );
+ }
+ }
+ else
+ {
+ oslPipeError eReason = pipe.getError();
+ if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
+ return RequestHandler::IPC_STATUS_PIPE_ERROR;
+
+ // Wait for second office to be ready
+ TimeValue aTimeValue;
+ aTimeValue.Seconds = 0;
+ aTimeValue.Nanosec = 10000000; // 10ms
+ salhelper::Thread::wait( aTimeValue );
+ }
+
+ } while ( nPipeMode == PIPEMODE_DONTKNOW );
+
+ if ( nPipeMode == PIPEMODE_CREATED )
+ {
+ // Seems we are the one and only, so create listening thread
+ *thread = new PipeIpcThread(pipe);
+ return RequestHandler::IPC_STATUS_OK;
+ }
+ else
+ {
+ // Seems another office is running. Pipe arguments to it and self terminate
+ osl::StreamPipe aStreamPipe(pipe.getHandle());
+
+ OStringBuffer aArguments(ARGUMENT_PREFIX);
+ OUString cwdUrl;
+ if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
+ addArgument(aArguments, '1', cwdUrl)))
+ {
+ aArguments.append('0');
+ }
+ sal_uInt32 nCount = rtl_getAppCommandArgCount();
+ for( sal_uInt32 i=0; i < nCount; i++ )
+ {
+ rtl_getAppCommandArg( i, &aUserInstallPath.pData );
+ if (!addArgument(aArguments, ',', aUserInstallPath)) {
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+ }
+ aArguments.append('\0');
+ // finally, write the string onto the pipe
+ SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
+ sal_Int32 n = aStreamPipe.write(
+ aArguments.getStr(), aArguments.getLength());
+ if (n != aArguments.getLength()) {
+ SAL_INFO("desktop.app", "short write: " << n);
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+
+ if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
+ {
+ // something went wrong
+ return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
+ }
+
+ return RequestHandler::IPC_STATUS_2ND_OFFICE;
+ }
+}
+
+void RequestHandler::Disable()
+{
+ osl::ClearableMutexGuard aMutex( GetMutex() );
+
+ if( !pGlobal.is() )
+ return;
+
+ rtl::Reference< RequestHandler > handler(pGlobal);
+ pGlobal.clear();
+
+ handler->mState = State::Downing;
+ if (handler->mIpcThread.is()) {
+ handler->mIpcThread->close();
+ }
+
+ // release mutex to avoid deadlocks
+ aMutex.clear();
+
+ handler->cReady.set();
+
+ // exit gracefully and join
+ if (handler->mIpcThread.is())
+ {
+ handler->mIpcThread->join();
+ handler->mIpcThread.clear();
+ }
+
+ handler->cReady.reset();
+}
+
+RequestHandler::RequestHandler() :
+ mState( State::Starting ),
+ mnPendingRequests( 0 )
+{
+}
+
+RequestHandler::~RequestHandler()
+{
+ assert(!mIpcThread.is());
+}
+
+void RequestHandler::SetReady(bool bIsReady)
+{
+ osl::MutexGuard g(GetMutex());
+ if (pGlobal.is())
+ {
+ if (bIsReady)
+ pGlobal->cReady.set();
+ else
+ pGlobal->cReady.reset();
+ }
+}
+
+void RequestHandler::WaitForReady()
+{
+ rtl::Reference<RequestHandler> t;
+ {
+ osl::MutexGuard g(GetMutex());
+ t = pGlobal;
+ }
+ if (t.is())
+ {
+ t->cReady.wait();
+ }
+}
+
+bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
+ assert(waitProcessed != nullptr);
+
+ std::unique_ptr< CommandLineArgs > aCmdLineArgs;
+ try
+ {
+ Parser p(arguments);
+ aCmdLineArgs.reset( new CommandLineArgs( p ) );
+ }
+ catch ( const CommandLineArgs::Supplier::Exception & )
+ {
+ SAL_WARN("desktop.app", "Error in received command line arguments");
+ return false;
+ }
+
+ bool bDocRequestSent = false;
+
+ OUString aUnknown( aCmdLineArgs->GetUnknown() );
+ if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
+ {
+ const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
+
+ if ( aCmdLineArgs->IsQuickstart() )
+ {
+ // we have to use application event, because we have to start quickstart service in main thread!!
+ ApplicationEvent* pAppEvent =
+ new ApplicationEvent(ApplicationEvent::Type::QuickStart);
+ ImplPostForeignAppEvent( pAppEvent );
+ }
+
+ // handle request for acceptor
+ std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
+ for (auto const& elem : accept)
+ {
+ ApplicationEvent* pAppEvent = new ApplicationEvent(
+ ApplicationEvent::Type::Accept, elem);
+ ImplPostForeignAppEvent( pAppEvent );
+ }
+ // handle acceptor removal
+ std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
+ for (auto const& elem : unaccept)
+ {
+ ApplicationEvent* pAppEvent = new ApplicationEvent(
+ ApplicationEvent::Type::Unaccept, elem);
+ ImplPostForeignAppEvent( pAppEvent );
+ }
+
+ std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
+ aCmdLineArgs->getCwdUrl()));
+ m_handler->cProcessed.reset();
+ pRequest->pcProcessed = &m_handler->cProcessed;
+ m_handler->mbSuccess = false;
+ pRequest->mpbSuccess = &m_handler->mbSuccess;
+
+ // Print requests are not dependent on the --invisible cmdline argument as they are
+ // loaded with the "hidden" flag! So they are always checked.
+ pRequest->aPrintList = aCmdLineArgs->GetPrintList();
+ bDocRequestSent |= !pRequest->aPrintList.empty();
+ pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
+ pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
+ bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
+ pRequest->aConversionList = aCmdLineArgs->GetConversionList();
+ pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
+ pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
+ pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
+ pRequest->aInFilter = aCmdLineArgs->GetInFilter();
+ pRequest->bTextCat = aCmdLineArgs->IsTextCat();
+ pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
+ bDocRequestSent |= !pRequest->aConversionList.empty();
+
+ if ( !rCurrentCmdLineArgs.IsInvisible() )
+ {
+ // Read cmdline args that can open/create documents. As they would open a window
+ // they are only allowed if the "--invisible" is currently not used!
+ pRequest->aOpenList = aCmdLineArgs->GetOpenList();
+ bDocRequestSent |= !pRequest->aOpenList.empty();
+ pRequest->aViewList = aCmdLineArgs->GetViewList();
+ bDocRequestSent |= !pRequest->aViewList.empty();
+ pRequest->aStartList = aCmdLineArgs->GetStartList();
+ bDocRequestSent |= !pRequest->aStartList.empty();
+ pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
+ bDocRequestSent |= !pRequest->aForceOpenList.empty();
+ pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
+ bDocRequestSent |= !pRequest->aForceNewList.empty();
+
+ // Special command line args to create an empty document for a given module
+
+ // #i18338# (lo)
+ // we only do this if no document was specified on the command line,
+ // since this would be inconsistent with the behaviour of
+ // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
+ if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
+ {
+ SvtModuleOptions aOpt;
+ SvtModuleOptions::EFactory eFactory = SvtModuleOptions::EFactory::WRITER;
+ if ( aCmdLineArgs->IsWriter() )
+ eFactory = SvtModuleOptions::EFactory::WRITER;
+ else if ( aCmdLineArgs->IsCalc() )
+ eFactory = SvtModuleOptions::EFactory::CALC;
+ else if ( aCmdLineArgs->IsDraw() )
+ eFactory = SvtModuleOptions::EFactory::DRAW;
+ else if ( aCmdLineArgs->IsImpress() )
+ eFactory = SvtModuleOptions::EFactory::IMPRESS;
+ else if ( aCmdLineArgs->IsBase() )
+ eFactory = SvtModuleOptions::EFactory::DATABASE;
+ else if ( aCmdLineArgs->IsMath() )
+ eFactory = SvtModuleOptions::EFactory::MATH;
+ else if ( aCmdLineArgs->IsGlobal() )
+ eFactory = SvtModuleOptions::EFactory::WRITERGLOBAL;
+ else if ( aCmdLineArgs->IsWeb() )
+ eFactory = SvtModuleOptions::EFactory::WRITERWEB;
+
+ if ( !pRequest->aOpenList.empty() )
+ pRequest->aModule = aOpt.GetFactoryName( eFactory );
+ else
+ pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
+ bDocRequestSent = true;
+ }
+ }
+
+ if ( !aCmdLineArgs->IsQuickstart() ) {
+ bool bShowHelp = false;
+ OUStringBuffer aHelpURLBuffer;
+ if (aCmdLineArgs->IsHelpWriter()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
+ } else if (aCmdLineArgs->IsHelpCalc()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
+ } else if (aCmdLineArgs->IsHelpDraw()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
+ } else if (aCmdLineArgs->IsHelpImpress()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
+ } else if (aCmdLineArgs->IsHelpBase()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
+ } else if (aCmdLineArgs->IsHelpBasic()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
+ } else if (aCmdLineArgs->IsHelpMath()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
+ }
+ if (bShowHelp) {
+ aHelpURLBuffer.append("?Language=");
+ aHelpURLBuffer.append(utl::ConfigManager::getUILocale());
+#if defined UNX
+ aHelpURLBuffer.append("&System=UNX");
+#elif defined _WIN32
+ aHelpURLBuffer.append("&System=WIN");
+#endif
+ ApplicationEvent* pAppEvent = new ApplicationEvent(
+ ApplicationEvent::Type::OpenHelpUrl,
+ aHelpURLBuffer.makeStringAndClear());
+ ImplPostForeignAppEvent( pAppEvent );
+ }
+ }
+
+ if ( bDocRequestSent )
+ {
+ // Send requests to dispatch watcher if we have at least one. The receiver
+ // is responsible to delete the request after processing it.
+ if ( aCmdLineArgs->HasModuleParam() )
+ {
+ SvtModuleOptions aOpt;
+
+ // Support command line parameters to start a module (as preselection)
+ if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
+ else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
+ else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
+ }
+
+ ImplPostProcessDocumentsEvent( std::move(pRequest) );
+ }
+ else
+ {
+ // delete not used request again
+ pRequest.reset();
+ }
+ if (aCmdLineArgs->IsEmpty())
+ {
+ // no document was sent, just bring Office to front
+ ApplicationEvent* pAppEvent =
+ new ApplicationEvent(ApplicationEvent::Type::Appear);
+ ImplPostForeignAppEvent( pAppEvent );
+ }
+ }
+ *waitProcessed = bDocRequestSent;
+ return true;
+}
+
+void PipeIpcThread::execute()
+{
+ assert(m_handler != nullptr);
+ do
+ {
+ osl::StreamPipe aStreamPipe;
+ oslPipeError nError = pipe_.accept( aStreamPipe );
+
+
+ if( nError == osl_Pipe_E_None )
+ {
+ // if we receive a request while the office is displaying some dialog or error during
+ // bootstrap, that dialogs event loop might get events that are dispatched by this thread
+ // we have to wait for cReady to be set by the real main loop.
+ // only requests that don't dispatch events may be processed before cReady is set.
+ m_handler->cReady.wait();
+
+ // we might have decided to shutdown while we were sleeping
+ if (!RequestHandler::pGlobal.is()) return;
+
+ // only lock the mutex when processing starts, otherwise we deadlock when the office goes
+ // down during wait
+ osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
+
+ if (m_handler->mState == RequestHandler::State::Downing)
+ {
+ break;
+ }
+
+ // notify client we're ready to process its args:
+ SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
+ sal_Int32 n = aStreamPipe.write(
+ SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
+ // incl. terminating NUL
+ if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
+ SAL_WARN("desktop.app", "short write: " << n);
+ continue;
+ }
+
+ OString aArguments = readStringFromPipe(aStreamPipe);
+
+ // Is this a lookup message from another application? if so, ignore
+ if (aArguments.isEmpty())
+ continue;
+
+ bool waitProcessed = false;
+ if (!process(aArguments, &waitProcessed)) {
+ continue;
+ }
+
+ // we don't need the mutex any longer...
+ aGuard.clear();
+ bool bSuccess = true;
+ // wait for processing to finish
+ if (waitProcessed)
+ {
+ m_handler->cProcessed.wait();
+ bSuccess = m_handler->mbSuccess;
+ }
+ if (bSuccess)
+ {
+ // processing finished, inform the requesting end:
+ SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
+ n = aStreamPipe.write(PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
+ // incl. terminating NUL
+ if (n != SAL_N_ELEMENTS(PROCESSING_DONE))
+ {
+ SAL_WARN("desktop.app", "short write: " << n);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ {
+ osl::MutexGuard aGuard( RequestHandler::GetMutex() );
+ if (m_handler->mState == RequestHandler::State::Downing)
+ {
+ break;
+ }
+ }
+
+ SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
+ TimeValue tval;
+ tval.Seconds = 1;
+ tval.Nanosec = 0;
+ salhelper::Thread::wait( tval );
+ }
+ } while( schedule() );
+}
+
+static void AddToDispatchList(
+ std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
+ std::optional< OUString > const & cwdUrl,
+ std::vector< OUString > const & aRequestList,
+ DispatchWatcher::RequestType nType,
+ const OUString& aParam,
+ const OUString& aFactory )
+{
+ for (auto const& request : aRequestList)
+ {
+ rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
+ }
+}
+
+static void AddConversionsToDispatchList(
+ std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
+ std::optional< OUString > const & cwdUrl,
+ std::vector< OUString > const & rRequestList,
+ const OUString& rParam,
+ const OUString& rPrinterName,
+ const OUString& rFactory,
+ const OUString& rParamOut,
+ const OUString& rImgOut,
+ const bool isTextCat,
+ const bool isScriptCat )
+{
+ DispatchWatcher::RequestType nType;
+ OUString aParam( rParam );
+
+ if( !rParam.isEmpty() )
+ {
+ if ( isTextCat )
+ nType = DispatchWatcher::REQUEST_CAT;
+ else
+ nType = DispatchWatcher::REQUEST_CONVERSION;
+ aParam = rParam;
+ }
+ else
+ {
+ if ( isScriptCat )
+ nType = DispatchWatcher::REQUEST_SCRIPT_CAT;
+ else
+ {
+ nType = DispatchWatcher::REQUEST_BATCHPRINT;
+ aParam = rPrinterName;
+ }
+ }
+
+ OUString aOutDir( rParamOut.trim() );
+ OUString aImgOut( rImgOut.trim() );
+ OUString aPWD;
+ if (cwdUrl)
+ {
+ aPWD = *cwdUrl;
+ }
+ else
+ {
+ utl::Bootstrap::getProcessWorkingDir( aPWD );
+ }
+
+ if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
+ ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
+
+ if( !rParamOut.trim().isEmpty() )
+ {
+ aParam += ";" + aOutDir;
+ }
+ else
+ {
+ ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
+ aParam += ";" + aPWD;
+ }
+
+ if( !rImgOut.trim().isEmpty() )
+ aParam += "|" + aImgOut;
+
+ for (auto const& request : rRequestList)
+ {
+ rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
+ }
+}
+
+namespace {
+
+struct ConditionSetGuard
+{
+ osl::Condition* m_pCondition;
+ ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
+ ~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
+};
+
+}
+
+bool RequestHandler::ExecuteCmdLineRequests(
+ ProcessDocumentsRequest& aRequest, bool noTerminate)
+{
+ // protect the dispatch list
+ osl::ClearableMutexGuard aGuard( GetMutex() );
+
+ // ensure that Processed flag (if exists) is signaled in any outcome
+ ConditionSetGuard aSetGuard(aRequest.pcProcessed);
+
+ static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
+
+ // Create dispatch list for dispatch watcher
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, "", aRequest.aModule );
+ AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, "", aRequest.aModule );
+ AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
+ bool bShutdown( false );
+
+ if ( pGlobal.is() )
+ {
+ if( ! pGlobal->AreRequestsEnabled() )
+ {
+ // Either starting, or downing - do not process the request, just try to bring Office to front
+ ApplicationEvent* pAppEvent =
+ new ApplicationEvent(ApplicationEvent::Type::Appear);
+ ImplPostForeignAppEvent(pAppEvent);
+ return bShutdown;
+ }
+
+ pGlobal->mnPendingRequests += aDispatchList.size();
+ if ( !pGlobal->mpDispatchWatcher.is() )
+ {
+ pGlobal->mpDispatchWatcher = new DispatchWatcher;
+ }
+ rtl::Reference<DispatchWatcher> dispatchWatcher(
+ pGlobal->mpDispatchWatcher);
+
+ // copy for execute
+ std::vector<DispatchWatcher::DispatchRequest> aTempList;
+ aTempList.swap( aDispatchList );
+
+ aGuard.clear();
+
+ // Execute dispatch requests
+ bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
+ if (aRequest.mpbSuccess)
+ *aRequest.mpbSuccess = true; // signal that we have actually succeeded
+ }
+
+ return bShutdown;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/officeipcthread.hxx b/desktop/source/app/officeipcthread.hxx
new file mode 100644
index 000000000..e4f3edaf9
--- /dev/null
+++ b/desktop/source/app/officeipcthread.hxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_OFFICEIPCTHREAD_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_OFFICEIPCTHREAD_HXX
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <osl/signal.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <osl/conditn.hxx>
+#include <optional>
+
+namespace desktop
+{
+
+oslSignalAction SalMainPipeExchangeSignal_impl(void* /*pData*/, oslSignalInfo* pInfo);
+
+// A request for the current office
+// that was given by command line or by IPC pipe communication.
+struct ProcessDocumentsRequest
+{
+ explicit ProcessDocumentsRequest(std::optional< OUString > const & cwdUrl):
+ aCwdUrl(cwdUrl), pcProcessed( nullptr ), bTextCat( false ), bScriptCat( false ) {}
+
+ std::optional< OUString > aCwdUrl;
+ OUString aModule;
+ std::vector< OUString > aOpenList; // Documents that should be opened in the default way
+ std::vector< OUString > aViewList; // Documents that should be opened in viewmode
+ std::vector< OUString > aStartList; // Documents/Presentations that should be started
+ std::vector< OUString > aPrintList; // Documents that should be printed on default printer
+ std::vector< OUString > aForceOpenList; // Documents that should be forced to open for editing (even templates)
+ std::vector< OUString > aForceNewList; // Documents that should be forced to create a new document
+ OUString aPrinterName; // The printer name that should be used for printing
+ std::vector< OUString > aPrintToList; // Documents that should be printed on the given printer
+ std::vector< OUString > aConversionList;
+ OUString aConversionParams;
+ OUString aConversionOut;
+ OUString aImageConversionType;
+ std::vector< OUString > aInFilter;
+ ::osl::Condition *pcProcessed; // pointer condition to be set when the request has been processed
+ bool* mpbSuccess = nullptr; // pointer to boolean receiving if the processing was successful
+ bool bTextCat; // boolean flag indicating whether to dump text content to console
+ bool bScriptCat; // boolean flag indicating whether to dump script content to console
+};
+
+class DispatchWatcher;
+class IpcThread;
+class PipeIpcThread;
+class DbusIpcThread;
+
+class RequestHandler: public salhelper::SimpleReferenceObject
+{
+ friend IpcThread;
+ friend PipeIpcThread;
+ friend DbusIpcThread;
+
+ private:
+ static rtl::Reference< RequestHandler > pGlobal;
+
+ enum class State { Starting, RequestsEnabled, Downing };
+
+ State mState;
+ int mnPendingRequests;
+ rtl::Reference<DispatchWatcher> mpDispatchWatcher;
+ rtl::Reference<IpcThread> mIpcThread;
+
+ /* condition to be set when the request has been processed */
+ ::osl::Condition cProcessed;
+ /* receives if the processing was successful (may be false e.g. when shutting down) */
+ bool mbSuccess = false;
+
+ /* condition to be set when the main event loop is ready
+ otherwise an error dialogs event loop could eat away
+ requests from a 2nd office */
+ ::osl::Condition cReady;
+
+ static ::osl::Mutex& GetMutex();
+
+ RequestHandler();
+
+ virtual ~RequestHandler() override;
+
+ public:
+ enum Status
+ {
+ IPC_STATUS_OK,
+ IPC_STATUS_2ND_OFFICE,
+ IPC_STATUS_PIPE_ERROR,
+ IPC_STATUS_BOOTSTRAP_ERROR
+ };
+
+ // controlling pipe communication during shutdown
+ static void SetDowning();
+ static void EnableRequests();
+ static bool AreRequestsPending();
+ static void RequestsCompleted();
+ static bool ExecuteCmdLineRequests(
+ ProcessDocumentsRequest&, bool noTerminate);
+
+ // return sal_False if second office
+ static Status Enable(bool ipc);
+ static void Disable();
+ // start dispatching events...
+ static void SetReady(bool bIsReady);
+ static void WaitForReady();
+
+ bool AreRequestsEnabled() const { return mState == State::RequestsEnabled; }
+};
+
+
+class RequestHandlerController : public ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::frame::XTerminateListener >
+{
+ public:
+ RequestHandlerController() {}
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL notifyTermination( const css::lang::EventObject& aEvent ) override;
+};
+
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_APP_OFFICEIPCTHREAD_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/opencl.cxx b/desktop/source/app/opencl.cxx
new file mode 100644
index 000000000..0ea322e02
--- /dev/null
+++ b/desktop/source/app/opencl.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/*
+ * This module exists to validate the OpenCL implementation,
+ * where necessary during startup; and before we load or
+ * calculate using OpenCL.
+ */
+
+#include <app.hxx>
+
+#include <config_version.h>
+#include <config_feature_opencl.h>
+#include <config_folders.h>
+
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <svl/documentlockfile.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/table/XCell2.hpp>
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheets.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+
+#if HAVE_FEATURE_OPENCL
+#include <opencl/openclwrapper.hxx>
+#endif
+#include <opencl/OpenCLZone.hxx>
+
+#include <osl/file.hxx>
+#include <osl/process.h>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace desktop {
+
+#if HAVE_FEATURE_OPENCL
+
+static bool testOpenCLDriver()
+{
+ // A simple OpenCL test run in a separate process in order to test
+ // whether the driver crashes (asserts,etc.) when trying to use OpenCL.
+ SAL_INFO("opencl", "Starting CL driver test");
+
+ OUString testerURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/opencltest");
+ rtl::Bootstrap::expandMacros(testerURL); //TODO: detect failure
+
+ OUString deviceName, platformName;
+ openclwrapper::getOpenCLDeviceName( deviceName, platformName );
+ rtl_uString* args[] = { deviceName.pData, platformName.pData };
+ sal_Int32 numArgs = 2;
+
+ oslProcess process;
+ oslSecurity security = osl_getCurrentSecurity();
+ oslProcessError error = osl_executeProcess(testerURL.pData, args, numArgs,
+ osl_Process_SEARCHPATH | osl_Process_HIDDEN, security,
+ nullptr, nullptr, 0, &process );
+ osl_freeSecurityHandle( security );
+ if( error != osl_Process_E_None )
+ {
+ SAL_WARN( "opencl", "failed to start CL driver test: " << error );
+ return false;
+ }
+ // If the driver takes more than 10 seconds, it's probably broken/useless.
+ TimeValue timeout( 10, 0 );
+ error = osl_joinProcessWithTimeout( process, &timeout );
+ if( error == osl_Process_E_None )
+ {
+ oslProcessInfo info;
+ info.Size = sizeof( info );
+ error = osl_getProcessInfo( process, osl_Process_EXITCODE, &info );
+ if( error == osl_Process_E_None )
+ {
+ if( info.Code == 0 )
+ {
+ SAL_INFO( "opencl", "CL driver test passed" );
+ osl_freeProcessHandle( process );
+ return true;
+ }
+ else
+ {
+ SAL_WARN( "opencl", "CL driver test failed - disabling: " << info.Code );
+ osl_freeProcessHandle( process );
+ return false;
+ }
+ }
+ }
+ SAL_WARN( "opencl", "CL driver test did not finish - disabling: " << error );
+ osl_terminateProcess( process );
+ osl_freeProcessHandle( process );
+ return false;
+}
+
+static bool testOpenCLCompute(const Reference< XDesktop2 > &xDesktop, const OUString &rURL)
+{
+ bool bSuccess = false;
+ css::uno::Reference< css::lang::XComponent > xComponent;
+
+ sal_uInt64 nKernelFailures = openclwrapper::kernelFailures;
+
+ SAL_INFO("opencl", "Starting CL test spreadsheet");
+
+ // A stale lock file would make the loading fail, so make sure to remove it.
+ try {
+ ::svt::DocumentLockFile lockFile( rURL );
+ lockFile.RemoveFileDirectly();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ try {
+ css::uno::Reference< css::frame::XComponentLoader > xLoader(xDesktop, css::uno::UNO_QUERY_THROW);
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs(1);
+ aArgs[0].Name = "Hidden";
+ aArgs[0].Value <<= true;
+
+ xComponent.set(xLoader->loadComponentFromURL(rURL, "_blank", 0, aArgs));
+
+ // What an unpleasant API to use.
+ css::uno::Reference< css::sheet::XCalculatable > xCalculatable( xComponent, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::sheet::XSpreadsheetDocument > xSpreadDoc( xComponent, css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), css::uno::UNO_SET_THROW );
+ css::uno::Reference< css::container::XIndexAccess > xIndex( xSheets, css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::sheet::XSpreadsheet > xSheet( xIndex->getByIndex(0), css::uno::UNO_QUERY_THROW);
+
+ // So we insert our MAX call at the end on a named range.
+ css::uno::Reference< css::table::XCell2 > xThresh( xSheet->getCellByPosition(1,1), css::uno::UNO_QUERY_THROW ); // B2
+ double fThreshold = xThresh->getValue();
+
+ // We need pure OCL formulae all the way through the
+ // dependency chain, or we fall-back.
+ xCalculatable->calculateAll();
+
+ // So we insert our MAX call at the end on a named range.
+ css::uno::Reference< css::table::XCell2 > xCell( xSheet->getCellByPosition(1,0), css::uno::UNO_QUERY_THROW );
+ xCell->setFormula("=MAX(results)");
+ double fResult = xCell->getValue();
+
+ // Ensure the maximum variance is below our tolerance.
+ if (fResult > fThreshold)
+ {
+ SAL_WARN("opencl", "OpenCL results unstable - disabling; result: "
+ << fResult << " vs. " << fThreshold);
+ }
+ else
+ {
+ SAL_INFO("opencl", "calculating smoothly; result: " << fResult);
+ bSuccess = true;
+ }
+ }
+ catch (const css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("opencl", "OpenCL testing failed - disabling");
+ }
+
+ if (nKernelFailures != openclwrapper::kernelFailures)
+ {
+ // tdf#100883 - defeat SEH exception handling fallbacks.
+ SAL_WARN("opencl", "OpenCL kernels failed to compile, "
+ "or took SEH exceptions "
+ << nKernelFailures << " != " << openclwrapper::kernelFailures);
+ bSuccess = false;
+ }
+
+ if (!bSuccess)
+ OpenCLZone::hardDisable();
+ if (xComponent.is())
+ xComponent->dispose();
+
+
+ return bSuccess;
+}
+
+void Desktop::CheckOpenCLCompute(const Reference< XDesktop2 > &xDesktop)
+{
+ if (!openclwrapper::canUseOpenCL() || Application::IsSafeModeEnabled())
+ return;
+
+ SAL_INFO("opencl", "Initiating test of OpenCL device");
+ OpenCLZone aZone;
+ OpenCLInitialZone aInitialZone;
+
+ OUString aDevice = officecfg::Office::Calc::Formula::Calculation::OpenCLDevice::get();
+ OUString aSelectedCLDeviceVersionID;
+ if (!openclwrapper::switchOpenCLDevice(
+ &aDevice,
+ officecfg::Office::Calc::Formula::Calculation::OpenCLAutoSelect::get(),
+ false /* bForceEvaluation */,
+ aSelectedCLDeviceVersionID))
+ {
+ SAL_WARN("opencl", "Failed to initialize OpenCL for test");
+ OpenCLZone::hardDisable();
+ return;
+ }
+
+ // Append our app version as well.
+ aSelectedCLDeviceVersionID += "--" LIBO_VERSION_DOTTED;
+
+ // Append timestamp of the file.
+ OUString aURL("$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/opencl/cl-test.ods");
+ rtl::Bootstrap::expandMacros(aURL);
+
+ DirectoryItem aItem;
+ (void)DirectoryItem::get( aURL, aItem );
+ FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
+ (void)aItem.getFileStatus( aFileStatus );
+ TimeValue aTimeVal = aFileStatus.getModifyTime();
+ aSelectedCLDeviceVersionID += "--" +
+ OUString::number(aTimeVal.Seconds);
+
+ if (aSelectedCLDeviceVersionID == officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::get())
+ return;
+
+ // OpenCL device changed - sanity check it and disable if bad.
+
+ sal_Int32 nOrigMinimumSize = officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get();
+ { // set the minimum group size to something small for quick testing.
+ std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(3 /* small */, xBatch);
+ xBatch->commit();
+ }
+
+ // Hopefully at least basic functionality always works and broken OpenCL implementations break
+ // only when they are used to compute something. If this assumptions turns out to be not true,
+ // the driver check needs to be moved sooner.
+ bool bSucceeded = testOpenCLDriver() && testOpenCLCompute(xDesktop, aURL);
+
+ { // restore the minimum group size
+ std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(nOrigMinimumSize, xBatch);
+ officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::set(aSelectedCLDeviceVersionID, xBatch);
+ xBatch->commit();
+ }
+
+ if (!bSucceeded)
+ OpenCLZone::hardDisable();
+}
+#endif // HAVE_FEATURE_OPENCL
+
+} // end namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/desktop/source/app/sofficemain.cxx b/desktop/source/app/sofficemain.cxx
new file mode 100644
index 000000000..d95356fd2
--- /dev/null
+++ b/desktop/source/app/sofficemain.cxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <config_features.h>
+
+#include <desktop/dllapi.h>
+
+#include <app.hxx>
+#include "cmdlineargs.hxx"
+#include "cmdlinehelp.hxx"
+
+// needed before sal/main.h to avoid redefinition of macros
+#include <prewin.h>
+
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <sal/main.h>
+#include <tools/extendapplicationenvironment.hxx>
+#include <vcl/glxtestprocess.hxx>
+#include <vcl/svmain.hxx>
+
+#if HAVE_FEATURE_BREAKPAD
+#include <desktop/crashreport.hxx>
+#endif
+
+#include <postwin.h>
+
+#ifdef ANDROID
+# include <jni.h>
+# include <android/log.h>
+# include <salhelper/thread.hxx>
+
+# define LOGTAG "LibreOffice/sofficemain"
+# define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOGTAG, __VA_ARGS__))
+#endif
+
+extern "C" int DESKTOP_DLLPUBLIC soffice_main()
+{
+ sal_detail_initialize(sal::detail::InitializeSoffice, nullptr);
+
+#if HAVE_FEATURE_BREAKPAD
+ CrashReporter::installExceptionHandler();
+#endif
+
+ bool bSuccess = fire_glxtest_process();
+ SAL_WARN_IF(!bSuccess, "desktop.opengl", "problems with glxtest");
+
+#if defined ANDROID
+ try {
+ rtl::Bootstrap::setIniFilename("file:///assets/program/lofficerc");
+#endif
+ tools::extendApplicationEnvironment();
+
+ desktop::Desktop aDesktop;
+ // This string is used during initialization of the Gtk+ VCL module
+ Application::SetAppName( "soffice" );
+
+ // handle --version and --help already here, otherwise they would be handled
+ // after VCL initialization that might fail if $DISPLAY is not set
+ const desktop::CommandLineArgs& rCmdLineArgs = desktop::Desktop::GetCommandLineArgs();
+ const OUString& aUnknown( rCmdLineArgs.GetUnknown() );
+ if ( !aUnknown.isEmpty() )
+ {
+ desktop::Desktop::InitApplicationServiceManager();
+ desktop::displayCmdlineHelp( aUnknown );
+ return EXIT_FAILURE;
+ }
+ if ( rCmdLineArgs.IsHelp() )
+ {
+ desktop::Desktop::InitApplicationServiceManager();
+ desktop::displayCmdlineHelp( OUString() );
+ return EXIT_SUCCESS;
+ }
+ if ( rCmdLineArgs.IsVersion() )
+ {
+ desktop::Desktop::InitApplicationServiceManager();
+ desktop::displayVersion();
+ return EXIT_SUCCESS;
+ }
+
+ return SVMain();
+#if defined ANDROID
+ } catch (const css::uno::Exception &e) {
+ LOGI("Unhandled UNO exception: '%s'",
+ OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
+ throw; // to get exception type printed
+ }
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/sofficemain.h b/desktop/source/app/sofficemain.h
new file mode 100644
index 000000000..c291df7af
--- /dev/null
+++ b/desktop/source/app/sofficemain.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_SOFFICEMAIN_H
+#define INCLUDED_DESKTOP_SOURCE_APP_SOFFICEMAIN_H
+
+#include <desktop/dllapi.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+int DESKTOP_DLLPUBLIC soffice_main(void);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/updater.cxx b/desktop/source/app/updater.cxx
new file mode 100644
index 000000000..2d99856b2
--- /dev/null
+++ b/desktop/source/app/updater.cxx
@@ -0,0 +1,887 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "updater.hxx"
+
+#if UNX
+#include <unistd.h>
+#include <errno.h>
+
+#endif
+
+#ifdef _WIN32
+#include <comphelper/windowsStart.hxx>
+#endif
+
+#include <fstream>
+#include <config_folders.h>
+#include <rtl/bootstrap.hxx>
+
+#include <officecfg/Office/Update.hxx>
+
+#include <rtl/ustring.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/configmgr.hxx>
+#include <osl/file.hxx>
+#include <rtl/process.h>
+#include <sal/log.hxx>
+
+#include <curl/curl.h>
+
+#include <orcus/json_document_tree.hpp>
+#include <orcus/config.hpp>
+#include <orcus/pstring.hpp>
+#include <comphelper/hash.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+
+#include <officecfg/Setup.hxx>
+
+#include <set>
+
+namespace {
+
+class error_updater : public std::exception
+{
+ OString maStr;
+public:
+
+ error_updater(const OString& rStr):
+ maStr(rStr)
+ {
+ }
+
+ virtual const char* what() const throw() override
+ {
+ return maStr.getStr();
+ }
+};
+
+#ifdef UNX
+static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
+#else
+static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
+#endif
+
+#ifdef UNX
+const char* const pUpdaterName = "updater";
+const char* const pSofficeExeName = "soffice";
+#elif defined(_WIN32)
+const char* pUpdaterName = "updater.exe";
+const char* pSofficeExeName = "soffice.exe";
+#else
+#error "Need implementation"
+#endif
+
+OUString normalizePath(const OUString& rPath)
+{
+ OUString aPath = rPath.replaceAll("//", "/");
+
+ // remove final /
+ if (aPath.endsWith("/"))
+ {
+ aPath = aPath.copy(0, aPath.getLength() - 1);
+ }
+
+ while (aPath.indexOf("/..") != -1)
+ {
+ sal_Int32 nIndex = aPath.indexOf("/..");
+ sal_Int32 i = nIndex - 1;
+ for (; i > 0; --i)
+ {
+ if (aPath[i] == '/')
+ break;
+ }
+
+ OUString aTempPath = aPath;
+ aPath = aTempPath.copy(0, i) + aPath.copy(nIndex + 3);
+ }
+
+ return aPath.replaceAll("\\", "/");
+}
+
+void CopyFileToDir(const OUString& rTempDirURL, const OUString & rFileName, const OUString& rOldDir)
+{
+ OUString aSourceURL = rOldDir + "/" + rFileName;
+ OUString aDestURL = rTempDirURL + "/" + rFileName;
+
+ osl::File::RC eError = osl::File::copy(aSourceURL, aDestURL);
+ if (eError != osl::File::E_None)
+ {
+ SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName);
+ throw std::exception();
+ }
+}
+
+OUString getPathFromURL(const OUString& rURL)
+{
+ OUString aPath;
+ osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
+
+ return normalizePath(aPath);
+}
+
+void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
+{
+ OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
+ CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
+}
+
+#ifdef UNX
+typedef char CharT;
+#define tstrncpy std::strncpy
+#elif defined(_WIN32)
+typedef wchar_t CharT;
+#define tstrncpy std::wcsncpy
+#else
+#error "Need an implementation"
+#endif
+
+void createStr(const OUString& rStr, CharT** pArgs, size_t i)
+{
+#ifdef UNX
+ OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
+#elif defined(_WIN32)
+ OUString aStr = rStr;
+#else
+#error "Need an implementation"
+#endif
+ CharT* pStr = new CharT[aStr.getLength() + 1];
+ tstrncpy(pStr, (CharT*)aStr.getStr(), aStr.getLength());
+ pStr[aStr.getLength()] = '\0';
+ pArgs[i] = pStr;
+}
+
+CharT** createCommandLine()
+{
+ OUString aInstallDir = Updater::getInstallationPath();
+
+ size_t nCommandLineArgs = rtl_getAppCommandArgCount();
+ size_t nArgs = 8 + nCommandLineArgs;
+ CharT** pArgs = new CharT*[nArgs];
+ {
+ OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
+ createStr(aUpdaterName, pArgs, 0);
+ }
+ {
+ // directory with the patch log
+ OUString aPatchDir = Updater::getPatchDirURL();
+ rtl::Bootstrap::expandMacros(aPatchDir);
+ OUString aTempDirPath = getPathFromURL(aPatchDir);
+ Updater::log("Patch Dir: " + aTempDirPath);
+ createStr(aTempDirPath, pArgs, 1);
+ }
+ {
+ // the actual update directory
+ Updater::log("Install Dir: " + aInstallDir);
+ createStr(aInstallDir, pArgs, 2);
+ }
+ {
+ // the temporary updated build
+ Updater::log("Working Dir: " + aInstallDir);
+ createStr(aInstallDir, pArgs, 3);
+ }
+ {
+#ifdef UNX
+ OUString aPID("0");
+#elif defined(_WIN32)
+ oslProcessInfo aInfo;
+ aInfo.Size = sizeof(oslProcessInfo);
+ osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aInfo);
+ OUString aPID = OUString::number(aInfo.Ident);
+#else
+#error "Need an implementation"
+#endif
+ createStr(aPID, pArgs, 4);
+ }
+ {
+ OUString aExeDir = Updater::getExecutableDirURL();
+ OUString aSofficePath = getPathFromURL(aExeDir);
+ Updater::log("soffice Path: " + aSofficePath);
+ createStr(aSofficePath, pArgs, 5);
+ }
+ {
+ // the executable to start after the successful update
+ OUString aExeDir = Updater::getExecutableDirURL();
+ OUString aSofficePathURL = aExeDir + OUString::fromUtf8(pSofficeExeName);
+ OUString aSofficePath = getPathFromURL(aSofficePathURL);
+ createStr(aSofficePath, pArgs, 6);
+ }
+
+ // add the command line arguments from the soffice list
+ for (size_t i = 0; i < nCommandLineArgs; ++i)
+ {
+ OUString aCommandLineArg;
+ rtl_getAppCommandArg(i, &aCommandLineArg.pData);
+ createStr(aCommandLineArg, pArgs, 7 + i);
+ }
+
+ pArgs[nArgs - 1] = nullptr;
+
+ return pArgs;
+}
+
+struct update_file
+{
+ OUString aURL;
+ OUString aHash;
+ size_t nSize;
+};
+
+struct language_file
+{
+ update_file aUpdateFile;
+ OUString aLangCode;
+};
+
+struct update_info
+{
+ OUString aFromBuildID;
+ OUString aSeeAlsoURL;
+ OUString aMessage;
+
+ update_file aUpdateFile;
+ std::vector<language_file> aLanguageFiles;
+};
+
+bool isUserWritable(const OUString& rFileURL)
+{
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ osl::DirectoryItem aDirectoryItem;
+
+ osl::FileBase::RC eRes = osl::DirectoryItem::get(rFileURL, aDirectoryItem);
+ if (eRes != osl::FileBase::E_None)
+ {
+ Updater::log("Could not get the directory item for: " + rFileURL);
+ return false;
+ }
+
+ osl::FileBase::RC eResult = aDirectoryItem.getFileStatus(aStatus);
+ if (eResult != osl::FileBase::E_None)
+ {
+ Updater::log("Could not get the file status for: " + rFileURL);
+ return false;
+ }
+
+ bool bReadOnly = (aStatus.getAttributes() & static_cast<sal_uInt64>(osl_File_Attribute_ReadOnly)) != 0;
+ if (bReadOnly)
+ {
+ Updater::log("Update location as determined by: " + rFileURL + " is read-only.");
+ return false;
+ }
+
+ return true;
+}
+
+}
+
+bool update()
+{
+ utl::TempFile aTempDir(nullptr, true);
+ OUString aTempDirURL = aTempDir.GetURL();
+ CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL);
+
+ OUString aUpdaterPath = getPathFromURL(aTempDirURL + "/" + OUString::fromUtf8(pUpdaterName));
+
+ Updater::log("Calling the updater with parameters: ");
+ CharT** pArgs = createCommandLine();
+
+ bool bSuccess = true;
+ const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
+ if (!pUpdaterTestReplace)
+ {
+#if UNX
+ OString aPath = OUStringToOString(aUpdaterPath, RTL_TEXTENCODING_UTF8);
+ if (execv(aPath.getStr(), pArgs))
+ {
+ printf("execv failed with error %d %s\n",errno,strerror(errno));
+ bSuccess = false;
+ }
+#elif defined(_WIN32)
+ bSuccess = WinLaunchChild((wchar_t*)aUpdaterPath.getStr(), 8, pArgs);
+#endif
+ }
+ else
+ {
+ SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath);
+ for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
+ {
+ SAL_WARN("desktop.updater", pArgs[i]);
+ }
+ bSuccess = false;
+ }
+
+ for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
+ {
+ delete[] pArgs[i];
+ }
+ delete[] pArgs;
+
+ return bSuccess;
+}
+
+namespace {
+
+// Callback to get the response data from server.
+size_t WriteCallback(void *ptr, size_t size,
+ size_t nmemb, void *userp)
+{
+ if (!userp)
+ return 0;
+
+ std::string* response = static_cast<std::string *>(userp);
+ size_t real_size = size * nmemb;
+ response->append(static_cast<char *>(ptr), real_size);
+ return real_size;
+}
+
+
+
+class invalid_update_info : public std::exception
+{
+};
+
+class invalid_hash : public std::exception
+{
+ OString maMessage;
+public:
+
+ invalid_hash(const OUString& rExpectedHash, const OUString& rReceivedHash)
+ : maMessage(
+ OUStringToOString(
+ OUString("Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash),
+ RTL_TEXTENCODING_UTF8)
+ )
+ {
+ }
+
+ const char* what() const noexcept override
+ {
+ return maMessage.getStr();
+ }
+};
+
+class invalid_size : public std::exception
+{
+ OString maMessage;
+public:
+
+ invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
+ : maMessage(
+ OUStringToOString(
+ OUString("Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize)),
+ RTL_TEXTENCODING_UTF8)
+ )
+ {
+ }
+
+ const char* what() const noexcept override
+ {
+ return maMessage.getStr();
+ }
+};
+
+OUString toOUString(const std::string& rStr)
+{
+ return OUString::fromUtf8(rStr.c_str());
+}
+
+update_file parse_update_file(orcus::json::node& rNode)
+{
+ if (rNode.type() != orcus::json::node_t::object)
+ {
+ SAL_WARN("desktop.updater", "invalid update or language file entry");
+ throw invalid_update_info();
+ }
+
+ if (rNode.child_count() < 4)
+ {
+ SAL_WARN("desktop.updater", "invalid update or language file entry");
+ throw invalid_update_info();
+ }
+
+ orcus::json::node aURLNode = rNode.child("url");
+ orcus::json::node aHashNode = rNode.child("hash");
+ orcus::json::node aHashTypeNode = rNode.child("hash_function");
+ orcus::json::node aSizeNode = rNode.child("size");
+
+ if (aHashTypeNode.string_value() != "sha512")
+ {
+ SAL_WARN("desktop.updater", "invalid hash type");
+ throw invalid_update_info();
+ }
+
+ update_file aUpdateFile;
+ aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
+
+ if (aUpdateFile.aURL.isEmpty())
+ throw invalid_update_info();
+
+ aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
+ aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
+ return aUpdateFile;
+}
+
+update_info parse_response(const std::string& rResponse)
+{
+ orcus::json::document_tree aJsonDoc;
+ orcus::json_config aConfig;
+ aJsonDoc.load(rResponse, aConfig);
+
+ auto aDocumentRoot = aJsonDoc.get_document_root();
+ if (aDocumentRoot.type() != orcus::json::node_t::object)
+ {
+ SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
+ throw invalid_update_info();
+ }
+
+ auto aRootKeys = aDocumentRoot.keys();
+ if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
+ {
+ throw invalid_update_info();
+ }
+ else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
+ {
+ update_info aUpdateInfo;
+ auto aMsgNode = aDocumentRoot.child("response");
+ aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
+ return aUpdateInfo;
+ }
+
+ orcus::json::node aFromNode = aDocumentRoot.child("from");
+ if (aFromNode.type() != orcus::json::node_t::string)
+ {
+ throw invalid_update_info();
+ }
+
+ orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
+ if (aSeeAlsoNode.type() != orcus::json::node_t::string)
+ {
+ throw invalid_update_info();
+ }
+
+ orcus::json::node aUpdateNode = aDocumentRoot.child("update");
+ if (aUpdateNode.type() != orcus::json::node_t::object)
+ {
+ throw invalid_update_info();
+ }
+
+ orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
+ if (aUpdateNode.type() != orcus::json::node_t::object)
+ {
+ throw invalid_update_info();
+ }
+
+ update_info aUpdateInfo;
+ aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
+ aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
+
+ aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
+
+ std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
+ for (auto const& language : aLanguages)
+ {
+ language_file aLanguageFile;
+ auto aLangEntry = aLanguageNode.child(language);
+ aLanguageFile.aLangCode = toOUString(language.str());
+ aLanguageFile.aUpdateFile = parse_update_file(aLangEntry);
+ aUpdateInfo.aLanguageFiles.push_back(aLanguageFile);
+ }
+
+ return aUpdateInfo;
+}
+
+struct WriteDataFile
+{
+ comphelper::Hash maHash;
+ SvStream* mpStream;
+
+ WriteDataFile(SvStream* pStream):
+ maHash(comphelper::HashType::SHA512),
+ mpStream(pStream)
+ {
+ }
+
+ OUString getHash()
+ {
+ auto final_hash = maHash.finalize();
+ std::stringstream aStrm;
+ for (auto& i: final_hash)
+ {
+ aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
+ }
+
+ return toOUString(aStrm.str());
+ }
+};
+
+// Callback to get the response data from server to a file.
+size_t WriteCallbackFile(void *ptr, size_t size,
+ size_t nmemb, void *userp)
+{
+ if (!userp)
+ return 0;
+
+ WriteDataFile* response = static_cast<WriteDataFile *>(userp);
+ size_t real_size = size * nmemb;
+ response->mpStream->WriteBytes(ptr, real_size);
+ response->maHash.update(static_cast<const unsigned char*>(ptr), real_size);
+ return real_size;
+}
+
+std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
+{
+ Updater::log("Download: " + rURL);
+ CURL* curl = curl_easy_init();
+
+ if (!curl)
+ return std::string();
+
+ curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
+ bool bUseProxy = false;
+ if (bUseProxy)
+ {
+ /*
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
+ */
+ }
+
+ char buf[] = "Expect:";
+ curl_slist* headerlist = nullptr;
+ headerlist = curl_slist_append(headerlist, buf);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // follow redirects
+ // only allow redirect to http:// and https://
+ curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+
+ std::string response_body;
+ utl::TempFile aTempFile;
+ WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
+ if (!bFile)
+ {
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA,
+ static_cast<void *>(&response_body));
+
+ aTempFile.EnableKillingFile(true);
+ }
+ else
+ {
+ OUString aTempFileURL = aTempFile.GetURL();
+ OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
+ response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
+
+ aTempFile.EnableKillingFile(false);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA,
+ static_cast<void *>(&aFile));
+ }
+
+ // Fail if 400+ is returned from the web server.
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+
+ CURLcode cc = curl_easy_perform(curl);
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if (http_code != 200)
+ {
+ SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
+ throw error_updater("download did not succeed");
+ }
+
+ if (cc != CURLE_OK)
+ {
+ SAL_WARN("desktop.updater", "curl error: " << cc);
+ throw error_updater("curl error");
+ }
+
+ if (bFile)
+ rHash = aFile.getHash();
+
+ return response_body;
+}
+
+void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
+{
+ switch (eError)
+ {
+ case osl::FileBase::E_None:
+ break;
+ default:
+ SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
+ throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
+ }
+}
+
+void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
+{
+ Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
+ OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
+ OUString aHash;
+ std::string temp_file = download_content(aURL, true, aHash);
+ if (temp_file.empty())
+ throw error_updater("empty temp file string");
+
+ OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
+ Updater::log("TempFile: " + aTempFile);
+ osl::File aDownloadedFile(aTempFile);
+ osl::FileBase::RC eError = aDownloadedFile.open(1);
+ handle_file_error(eError, "Could not open the download file: " + aTempFile);
+
+ sal_uInt64 nSize = 0;
+ eError = aDownloadedFile.getSize(nSize);
+ handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
+ if (nSize != nFileSize)
+ {
+ SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
+ throw invalid_size(nFileSize, nSize);
+ }
+
+ if (aHash != rHash)
+ {
+ SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
+ throw invalid_hash(rHash, aHash);
+ }
+
+ OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
+ rtl::Bootstrap::expandMacros(aPatchDirURL);
+ osl::Directory::create(aPatchDirURL);
+
+ OUString aDestFile = aPatchDirURL + aFileName;
+ Updater::log("Destination File: " + aDestFile);
+ aDownloadedFile.close();
+ eError = osl::File::move(aTempFile, aDestFile);
+ handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
+}
+
+}
+
+void update_checker()
+{
+ OUString aBrandBaseDir("${BRAND_BASE_DIR}");
+ rtl::Bootstrap::expandMacros(aBrandBaseDir);
+ bool bUserWritable = isUserWritable(aBrandBaseDir);
+ if (!bUserWritable)
+ {
+ Updater::log("Can't update as the update location is not user writable");
+ return;
+ }
+
+ OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
+ static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
+ if (pDownloadCheckBaseURLEnv)
+ {
+ aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
+ }
+
+ OUString aProductName = utl::ConfigManager::getProductName();
+ OUString aBuildID = Updater::getBuildID();
+
+ static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
+ if (pBuildIdEnv)
+ {
+ aBuildID = OUString::createFromAscii(pBuildIdEnv);
+ }
+
+ OUString aBuildTarget = "${_OS}_${_ARCH}";
+ rtl::Bootstrap::expandMacros(aBuildTarget);
+ OUString aChannel = Updater::getUpdateChannel();
+ static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
+ if (pUpdateChannelEnv)
+ {
+ aChannel = OUString::createFromAscii(pUpdateChannelEnv);
+ }
+
+ OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
+ "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
+ OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
+ Updater::log("Update check: " + aURL);
+
+ try
+ {
+ OUString aHash;
+ std::string response_body = download_content(aURL, false, aHash);
+ if (!response_body.empty())
+ {
+
+ update_info aUpdateInfo = parse_response(response_body);
+ if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
+ {
+ // No update currently available
+ // add entry to updating.log with the message
+ SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
+ Updater::log("Server response: " + aUpdateInfo.aMessage);
+ }
+ else
+ {
+ css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
+ std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
+ download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
+ for (auto& lang_update : aUpdateInfo.aLanguageFiles)
+ {
+ // only download the language packs for installed languages
+ if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
+ {
+ OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
+ download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
+ }
+ }
+ OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
+ batch->commit();
+ }
+ }
+ }
+ catch (const invalid_update_info&)
+ {
+ SAL_WARN("desktop.updater", "invalid update information");
+ Updater::log(OString("warning: invalid update info"));
+ }
+ catch (const error_updater& e)
+ {
+ SAL_WARN("desktop.updater", "error during the update check: " << e.what());
+ Updater::log(OString("warning: error by the updater") + e.what());
+ }
+ catch (const invalid_size& e)
+ {
+ SAL_WARN("desktop.updater", e.what());
+ Updater::log(OString("warning: invalid size"));
+ }
+ catch (const invalid_hash& e)
+ {
+ SAL_WARN("desktop.updater", e.what());
+ Updater::log(OString("warning: invalid hash"));
+ }
+ catch (...)
+ {
+ SAL_WARN("desktop.updater", "unknown error during the update check");
+ Updater::log(OString("warning: unknown exception"));
+ }
+}
+
+OUString Updater::getUpdateInfoLog()
+{
+ OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
+ rtl::Bootstrap::expandMacros(aUpdateInfoURL);
+
+ return aUpdateInfoURL;
+}
+
+OUString Updater::getPatchDirURL()
+{
+ OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
+ rtl::Bootstrap::expandMacros(aPatchDirURL);
+
+ return aPatchDirURL;
+}
+
+OUString Updater::getUpdateFileURL()
+{
+ return getPatchDirURL() + "update.mar";
+}
+
+OUString Updater::getInstallationPath()
+{
+ OUString aInstallDir( "$BRAND_BASE_DIR/");
+ rtl::Bootstrap::expandMacros(aInstallDir);
+
+ return getPathFromURL(aInstallDir);
+}
+
+OUString Updater::getExecutableDirURL()
+{
+ OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
+ rtl::Bootstrap::expandMacros(aExeDir);
+
+ return aExeDir;
+}
+
+void Updater::log(const OUString& rMessage)
+{
+ SAL_INFO("desktop.updater", rMessage);
+ OUString aUpdateLog = getUpdateInfoLog();
+ SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
+ aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
+ aLog.WriteLine(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
+}
+
+void Updater::log(const OString& rMessage)
+{
+ SAL_INFO("desktop.updater", rMessage);
+ OUString aUpdateLog = getUpdateInfoLog();
+ SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
+ aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
+ aLog.WriteLine(rMessage);
+}
+
+void Updater::log(const char* pMessage)
+{
+ SAL_INFO("desktop.updater", pMessage);
+ OUString aUpdateLog = getUpdateInfoLog();
+ SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
+ aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
+ aLog.WriteCharPtr(pMessage);
+}
+
+OUString Updater::getBuildID()
+{
+ OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(aBuildID);
+
+ return aBuildID;
+}
+
+OUString Updater::getUpdateChannel()
+{
+ OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
+ rtl::Bootstrap::expandMacros(aUpdateChannel);
+
+ return aUpdateChannel;
+}
+
+void Updater::removeUpdateFiles()
+{
+ Updater::log("Removing: " + getUpdateFileURL());
+ osl::File::remove(getUpdateFileURL());
+
+ OUString aPatchDirURL = getPatchDirURL();
+ osl::Directory aDir(aPatchDirURL);
+ aDir.open();
+
+ osl::FileBase::RC eRC;
+ do
+ {
+ osl::DirectoryItem aItem;
+ eRC = aDir.getNextItem(aItem);
+ if (eRC == osl::FileBase::E_None)
+ {
+ osl::FileStatus aStatus(osl_FileStatus_Mask_All);
+ if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
+ continue;
+
+ if (!aStatus.isRegular())
+ continue;
+
+ OUString aURL = aStatus.getFileURL();
+ if (!aURL.endsWith(".mar"))
+ continue;
+
+ Updater::log("Removing. " + aURL);
+ osl::File::remove(aURL);
+ }
+ }
+ while (eRC == osl::FileBase::E_None);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/updater.hxx b/desktop/source/app/updater.hxx
new file mode 100644
index 000000000..d0a55df6b
--- /dev/null
+++ b/desktop/source/app/updater.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_UPDATER_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_UPDATER_HXX
+
+#include <rtl/ustring.hxx>
+
+bool update();
+
+void update_checker();
+
+class Updater
+{
+public:
+ static OUString getUpdateInfoLog();
+ static OUString getPatchDirURL();
+ static OUString getUpdateFileURL();
+ static OUString getExecutableDirURL();
+ static OUString getInstallationPath();
+
+ static OUString getBuildID();
+ static OUString getUpdateChannel();
+
+ static void log(const OUString& rMessage);
+ static void log(const OString& rMessage);
+ static void log(const char* pMessage);
+
+ static void removeUpdateFiles();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/userinstall.cxx b/desktop/source/app/userinstall.cxx
new file mode 100644
index 000000000..bcfd7e3e5
--- /dev/null
+++ b/desktop/source/app/userinstall.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <com/sun/star/uno/Exception.hpp>
+#include <comphelper/configuration.hxx>
+#include <config_folders.h>
+#include <officecfg/Setup.hxx>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#if defined ANDROID || defined IOS
+#include <rtl/bootstrap.hxx>
+#endif
+#include <rtl/ustring.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/bootstrap.hxx>
+
+#include "userinstall.hxx"
+
+namespace desktop::userinstall {
+
+namespace {
+
+#if !(defined ANDROID || defined IOS)
+osl::FileBase::RC copyRecursive(
+ OUString const & srcUri, OUString const & dstUri)
+{
+ osl::DirectoryItem item;
+ osl::FileBase::RC e = osl::DirectoryItem::get(srcUri, item);
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ osl::FileStatus stat1(osl_FileStatus_Mask_Type);
+ e = item.getFileStatus(stat1);
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ if (stat1.getFileType() == osl::FileStatus::Directory) {
+ e = osl::Directory::create(dstUri);
+ if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
+ return e;
+ }
+ osl::Directory dir(srcUri);
+ e = dir.open();
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ for (;;) {
+ e = dir.getNextItem(item);
+ if (e == osl::FileBase::E_NOENT) {
+ break;
+ }
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ osl::FileStatus stat2(
+ osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_FileURL);
+ e = item.getFileStatus(stat2);
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ assert(!dstUri.endsWith("/"));
+ e = copyRecursive(
+ stat2.getFileURL(), dstUri + "/" + stat2.getFileName());
+ // assumes that all files under presets/ have names that can be
+ // copied unencoded into file URLs
+ if (e != osl::FileBase::E_None) {
+ return e;
+ }
+ }
+ e = dir.close();
+ } else {
+ e = osl::File::copy(srcUri, dstUri);
+ if (e == osl::FileBase::E_EXIST) {
+ // Assume an earlier attempt failed half-way through:
+ e = osl::FileBase::E_None;
+ }
+ }
+ return e;
+}
+#endif
+
+Status create(OUString const & uri) {
+ osl::FileBase::RC e = osl::Directory::createPath(uri);
+ if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
+ return ERROR_OTHER;
+ }
+#if !(defined ANDROID || defined IOS)
+#if defined UNIX
+ // Set safer permissions for the user directory by default:
+ osl::File::setAttributes(
+ uri,
+ (osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead
+ | osl_File_Attribute_OwnExe));
+#endif
+ // As of now osl_copyFile does not work on Android => don't do this:
+ OUString baseUri;
+ if (utl::Bootstrap::locateBaseInstallation(baseUri)
+ != utl::Bootstrap::PATH_EXISTS)
+ {
+ return ERROR_OTHER;
+ }
+ switch (copyRecursive(
+ baseUri + "/" LIBO_SHARE_PRESETS_FOLDER, uri + "/user"))
+ {
+ case osl::FileBase::E_None:
+ break;
+ case osl::FileBase::E_ACCES:
+ return ERROR_CANT_WRITE;
+ case osl::FileBase::E_NOSPC:
+ return ERROR_NO_SPACE;
+ default:
+ return ERROR_OTHER;
+ }
+#else
+ // On (Android and) iOS, just create the user directory. Later code fails mysteriously if it
+ // doesn't exist.
+ OUString userDir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/user");
+ rtl::Bootstrap::expandMacros(userDir);
+ osl::Directory::createPath(userDir);
+#endif
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Office::ooSetupInstCompleted::set(true, batch);
+ batch->commit();
+ return CREATED;
+}
+
+bool isCreated() {
+ try {
+ return officecfg::Setup::Office::ooSetupInstCompleted::get();
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
+ return false;
+ }
+}
+
+}
+
+Status finalize() {
+ OUString uri;
+ switch (utl::Bootstrap::locateUserInstallation(uri)) {
+ case utl::Bootstrap::PATH_EXISTS:
+ if (isCreated()) {
+ return EXISTED;
+ }
+ [[fallthrough]];
+ case utl::Bootstrap::PATH_VALID:
+ return create(uri);
+ default:
+ return ERROR_OTHER;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/app/userinstall.hxx b/desktop/source/app/userinstall.hxx
new file mode 100644
index 000000000..add45aed9
--- /dev/null
+++ b/desktop/source/app/userinstall.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_APP_USERINSTALL_HXX
+#define INCLUDED_DESKTOP_SOURCE_APP_USERINSTALL_HXX
+
+#include <sal/config.h>
+
+namespace desktop::userinstall {
+
+enum Status {
+ EXISTED,
+ CREATED,
+ ERROR_NO_SPACE,
+ ERROR_CANT_WRITE,
+ ERROR_OTHER
+};
+
+Status finalize();
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/deployment.component b/desktop/source/deployment/deployment.component
new file mode 100644
index 000000000..0016f1f9e
--- /dev/null
+++ b/desktop/source/deployment/deployment.component
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="deployment" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.deployment.ExtensionManager">
+ <service name="com.sun.star.comp.deployment.ExtensionManager"/>
+ <singleton name="com.sun.star.deployment.ExtensionManager"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.PackageInformationProvider">
+ <service name="com.sun.star.comp.deployment.PackageInformationProvider"/>
+ <singleton name="com.sun.star.deployment.PackageInformationProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.PackageManagerFactory">
+ <service name="com.sun.star.comp.deployment.PackageManagerFactory"/>
+ <singleton name="com.sun.star.deployment.thePackageManagerFactory"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ProgressLog">
+ <service name="com.sun.star.comp.deployment.ProgressLog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.component.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.configuration.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.executable.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.help.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.script.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.sfwk.PackageRegistryBackend">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+</component>
diff --git a/desktop/source/deployment/dp_log.cxx b/desktop/source/deployment/dp_log.cxx
new file mode 100644
index 000000000..1d68709ba
--- /dev/null
+++ b/desktop/source/deployment/dp_log.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_misc.h>
+#include <dp_services.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <comphelper/logging.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::logging;
+
+namespace dp_log {
+
+typedef ::cppu::WeakComponentImplHelper<ucb::XProgressHandler> t_log_helper;
+
+namespace {
+
+class ProgressLogImpl : public ::dp_misc::MutexHolder, public t_log_helper
+{
+ std::unique_ptr<comphelper::EventLogger> m_logger;
+
+protected:
+ virtual void SAL_CALL disposing() override;
+ virtual ~ProgressLogImpl() override;
+
+public:
+ ProgressLogImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext );
+
+ // XProgressHandler
+ virtual void SAL_CALL push( Any const & Status ) override;
+ virtual void SAL_CALL update( Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+}
+
+ProgressLogImpl::~ProgressLogImpl()
+{
+}
+
+
+void ProgressLogImpl::disposing()
+{
+}
+
+
+ProgressLogImpl::ProgressLogImpl(
+ Sequence<Any> const & /* args */,
+ Reference<XComponentContext> const & xContext )
+ : t_log_helper( getMutex() )
+{
+ // Use the logger created by unopkg app
+ m_logger.reset(new comphelper::EventLogger(xContext, "unopkg"));
+}
+
+// XProgressHandler
+
+void ProgressLogImpl::push( Any const & Status )
+{
+ update( Status );
+}
+
+void ProgressLogImpl::update( Any const & Status )
+{
+ if (! Status.hasValue())
+ return;
+
+ OUStringBuffer buf;
+
+ OUString msg;
+ sal_Int32 logLevel = LogLevel::INFO;
+ if (Status >>= msg) {
+ buf.append( msg );
+ }
+ else {
+ logLevel = LogLevel::SEVERE;
+ buf.append( ::comphelper::anyToString(Status) );
+ }
+ m_logger->log(logLevel, buf.makeStringAndClear());
+}
+
+
+void ProgressLogImpl::pop()
+{
+}
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<ProgressLogImpl, sdecl::with_args<true> > const servicePLI;
+sdecl::ServiceDecl const serviceDecl(
+ servicePLI,
+ // a private one:
+ "com.sun.star.comp.deployment.ProgressLog",
+ "com.sun.star.comp.deployment.ProgressLog" );
+
+} // namespace dp_log
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/dp_persmap.cxx b/desktop/source/deployment/dp_persmap.cxx
new file mode 100644
index 000000000..14e1f2076
--- /dev/null
+++ b/desktop/source/deployment/dp_persmap.cxx
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dp_misc.h>
+#include <dp_persmap.h>
+#include <rtl/byteseq.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+using namespace ::rtl;
+
+// the persistent map is used to manage a handful of key-value string pairs
+// this implementation replaces a rather heavy-weight berkeleydb integration
+
+// the file backing up a persistent map consists of line pairs with
+// - a key string (encoded with chars 0x00..0x0F being escaped)
+// - a value string (encoded with chars 0x00..0x0F being escaped)
+
+namespace dp_misc
+{
+
+static const char PmapMagic[4] = {'P','m','p','1'};
+
+PersistentMap::PersistentMap( OUString const & url_ )
+: m_MapFile( expandUnoRcUrl(url_) )
+, m_bIsOpen( false )
+, m_bToBeCreated( true )
+, m_bIsDirty( false )
+{
+ open();
+}
+
+PersistentMap::PersistentMap()
+: m_MapFile( OUString() )
+, m_bIsOpen( false )
+, m_bToBeCreated( false )
+, m_bIsDirty( false )
+{}
+
+PersistentMap::~PersistentMap()
+{
+ if( m_bIsDirty )
+ flush();
+ if( m_bIsOpen )
+ m_MapFile.close();
+}
+
+
+// replace 0x00..0x0F with "%0".."%F"
+// replace "%" with "%%"
+static OString encodeString( const OString& rStr)
+{
+ const char* pChar = rStr.getStr();
+ const sal_Int32 nLen = rStr.getLength();
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
+ {
+ const unsigned char c = static_cast<unsigned char>(*(pChar++));
+ if( c <= 0x0F )
+ break;
+ if( c == '%')
+ break;
+ }
+ if( i < 0)
+ return rStr;
+
+ // escape chars 0x00..0x0F with "%0".."%F"
+ OStringBuffer aEncStr( nLen + 32);
+ aEncStr.append( pChar - (nLen-i), nLen - i);
+ while( --i >= 0)
+ {
+ unsigned char c = static_cast<unsigned char>(*(pChar++));
+ if( c <= 0x0F )
+ {
+ aEncStr.append( '%');
+ c += (c <= 0x09) ? '0' : 'A'-10;
+ } else if( c == '%')
+ aEncStr.append( '%');
+ aEncStr.append( char(c) );
+ }
+
+ return aEncStr.makeStringAndClear();
+}
+
+// replace "%0".."%F" with 0x00..0x0F
+// replace "%%" with "%"
+static OString decodeString( const char* pEncChars, int nLen)
+{
+ const char* pChar = pEncChars;
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
+ if( *(pChar++) == '%')
+ break;
+ if( i < 0)
+ return OString( pEncChars, nLen);
+
+ // replace escaped chars with their decoded counterparts
+ OStringBuffer aDecStr( nLen);
+ pChar = pEncChars;
+ for( i = nLen; --i >= 0;)
+ {
+ char c = *(pChar++);
+ // handle escaped character
+ if( c == '%')
+ {
+ --i;
+ OSL_ASSERT( i >= 0);
+ c = *(pChar++);
+ if( ('0' <= c) && (c <= '9'))
+ c -= '0';
+ else
+ {
+ OSL_ASSERT( ('A' <= c) && (c <= 'F'));
+ c -= ('A'-10);
+ }
+ }
+ aDecStr.append( c);
+ }
+
+ return aDecStr.makeStringAndClear();
+}
+
+void PersistentMap::open()
+{
+ // open the existing file
+ sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
+
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+
+ // or create later if needed
+ m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
+
+ if( !m_bIsOpen)
+ return;
+
+ readAll();
+}
+
+
+void PersistentMap::readAll()
+{
+ // prepare for re-reading the map-file
+ m_entries.clear();
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ if (nRes != osl::FileBase::E_None)
+ {
+ SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
+ return;
+ }
+
+ // read header and check magic
+ char aHeaderBytes[ sizeof(PmapMagic)];
+ sal_uInt64 nBytesRead = 0;
+ m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
+ OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
+ if( nBytesRead != sizeof(aHeaderBytes))
+ return;
+ // check header magic
+ for( int i = 0; i < int(sizeof(PmapMagic)); ++i)
+ if( aHeaderBytes[i] != PmapMagic[i])
+ return;
+
+ // read key value pairs and add them to the map
+ ByteSequence aKeyLine;
+ ByteSequence aValLine;
+ for(;;)
+ {
+ // read key-value line pair
+ // an empty key name indicates the end of the line pairs
+ if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
+ return;
+ if( !aKeyLine.getLength())
+ break;
+ if( m_MapFile.readLine( aValLine) != osl::File::E_None)
+ return;
+ // decode key and value strings
+ const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength());
+ const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength());
+ // insert key-value pair into map
+ add( aKeyName, aValName );
+ // check end-of-file status
+ sal_Bool bIsEOF = true;
+ if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
+ return;
+ if( bIsEOF )
+ break;
+ }
+
+ m_bIsDirty = false;
+}
+
+void PersistentMap::flush()
+{
+ if( !m_bIsDirty)
+ return;
+ if( m_bToBeCreated && !m_entries.empty())
+ {
+ const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+ m_bToBeCreated = !m_bIsOpen;
+ }
+ if( !m_bIsOpen)
+ return;
+
+ // write header magic
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ if (nRes != osl::FileBase::E_None)
+ {
+ SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
+ return;
+ }
+ sal_uInt64 nBytesWritten = 0;
+ m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
+
+ // write key value pairs
+ for (auto const& entry : m_entries)
+ {
+ // write line for key
+ const OString aKeyString = encodeString( entry.first);
+ const sal_Int32 nKeyLen = aKeyString.getLength();
+ m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
+ OSL_ASSERT( nKeyLen == static_cast<sal_Int32>(nBytesWritten));
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // write line for value
+ const OString& rValString = encodeString( entry.second);
+ const sal_Int32 nValLen = rValString.getLength();
+ m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
+ OSL_ASSERT( nValLen == static_cast<sal_Int32>(nBytesWritten));
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ }
+
+ // write a file delimiter (an empty key-string)
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // truncate file here
+ sal_uInt64 nNewFileSize;
+ if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
+ m_MapFile.setSize( nNewFileSize);
+ // flush to disk
+ m_MapFile.sync();
+ // the in-memory map now matches to the file on disk
+ m_bIsDirty = false;
+}
+
+bool PersistentMap::has( OString const & key ) const
+{
+ return get( nullptr, key );
+}
+
+bool PersistentMap::get( OString * value, OString const & key ) const
+{
+ t_string2string_map::const_iterator it = m_entries.find( key);
+ if( it == m_entries.end())
+ return false;
+ if( value)
+ *value = it->second;
+ return true;
+}
+
+void PersistentMap::add( OString const & key, OString const & value )
+{
+ auto r = m_entries.emplace(key,value);
+ m_bIsDirty = r.second;
+}
+
+
+void PersistentMap::put( OString const & key, OString const & value )
+{
+ add( key, value);
+ // HACK: flush now as the extension manager does not seem
+ // to properly destruct this object in some situations
+ if(m_bIsDirty)
+ flush();
+}
+
+bool PersistentMap::erase( OString const & key )
+{
+ size_t nCount = m_entries.erase( key);
+ if( !nCount)
+ return false;
+ m_bIsDirty = true;
+ flush();
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/dp_services.cxx b/desktop/source/deployment/dp_services.cxx
new file mode 100644
index 000000000..bfaeb8011
--- /dev/null
+++ b/desktop/source/deployment/dp_services.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <comphelper/servicedecl.hxx>
+
+#include <dp_services.hxx>
+
+using namespace com::sun::star;
+namespace sdecl = comphelper::service_decl;
+
+extern "C" {
+
+SAL_DLLPUBLIC_EXPORT void * deployment_component_getFactory(
+ char const * pImplName, void *, void *)
+{
+ return sdecl::component_getFactoryHelper(
+ pImplName,
+ {&dp_registry::backend::configuration::serviceDecl,
+ &dp_registry::backend::component::serviceDecl,
+ &dp_registry::backend::help::serviceDecl,
+ &dp_registry::backend::script::serviceDecl,
+ &dp_registry::backend::sfwk::serviceDecl,
+ &dp_registry::backend::executable::serviceDecl,
+ &dp_manager::factory::serviceDecl,
+ &dp_log::serviceDecl,
+ &dp_info::serviceDecl,
+ &dp_manager::serviceDecl});
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/dp_xml.cxx b/desktop/source/deployment/dp_xml.cxx
new file mode 100644
index 000000000..d61267e2f
--- /dev/null
+++ b/desktop/source/deployment/dp_xml.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_xml.h>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/xml/sax/Parser.hpp>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc
+{
+
+
+void xml_parse(
+ Reference<xml::sax::XDocumentHandler> const & xDocHandler,
+ ::ucbhelper::Content & ucb_content,
+ Reference<XComponentContext> const & xContext )
+{
+ // raise sax parser:
+ Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext);
+
+ // error handler, entity resolver omitted
+ xParser->setDocumentHandler( xDocHandler );
+ xml::sax::InputSource source;
+ source.aInputStream = ucb_content.openStream();
+ source.sSystemId = ucb_content.getURL();
+ xParser->parseStream( source );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/deploymentgui.component b/desktop/source/deployment/gui/deploymentgui.component
new file mode 100644
index 000000000..abe2c77f5
--- /dev/null
+++ b/desktop/source/deployment/gui/deploymentgui.component
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="deploymentgui" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.deployment.ui.LicenseDialog">
+ <service name="com.sun.star.deployment.ui.LicenseDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.PackageManagerDialog">
+ <service name="com.sun.star.deployment.ui.PackageManagerDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.UpdateRequiredDialog">
+ <service name="com.sun.star.deployment.ui.UpdateRequiredDialog"/>
+ </implementation>
+</component>
diff --git a/desktop/source/deployment/gui/dp_gui.h b/desktop/source/deployment/gui/dp_gui.h
new file mode 100644
index 000000000..713700f44
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_H
+
+namespace dp_gui {
+
+enum PackageState { REGISTERED, NOT_REGISTERED, AMBIGUOUS, NOT_AVAILABLE };
+
+} // namespace dp_gui
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
new file mode 100644
index 000000000..2f02a991a
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_dependencydialog.hxx"
+
+using dp_gui::DependencyDialog;
+
+DependencyDialog::DependencyDialog(
+ weld::Window * parent, std::vector< OUString > const & dependencies)
+ : GenericDialogController(parent, "desktop/ui/dependenciesdialog.ui", "Dependencies")
+ , m_xList(m_xBuilder->weld_tree_view("depListTreeview"))
+{
+ m_xList->set_size_request(-1, m_xList->get_height_rows(10));
+ for (auto const& dependency : dependencies)
+ {
+ m_xList->append_text(dependency);
+ }
+}
+
+DependencyDialog::~DependencyDialog()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
new file mode 100644
index 000000000..6b974ae9e
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_DEPENDENCYDIALOG_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_DEPENDENCYDIALOG_HXX
+
+#include <sal/config.h>
+
+#include <vcl/weld.hxx>
+
+#include <vector>
+
+namespace vcl { class Window; }
+
+namespace dp_gui {
+
+class DependencyDialog : public weld::GenericDialogController
+{
+public:
+ DependencyDialog(
+ weld::Window* parent, std::vector< OUString > const & dependencies);
+ virtual ~DependencyDialog() override;
+
+private:
+ DependencyDialog(DependencyDialog const &) = delete;
+ DependencyDialog& operator =(DependencyDialog const &) = delete;
+
+ std::unique_ptr<weld::TreeView> m_xList;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.cxx b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
new file mode 100644
index 000000000..d018edad0
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
@@ -0,0 +1,1371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_extensions.h>
+
+#include <strings.hrc>
+#include <helpids.h>
+
+#include "dp_gui.h"
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extlistbox.hxx"
+#include <dp_shared.hxx>
+#include "dp_gui_theextmgr.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <dp_misc.h>
+#include <dp_update.hxx>
+#include <dp_identifier.hxx>
+
+#include <fpicker/strings.hrc>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <svtools/restartdialog.hxx>
+
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/sfxdlg.hxx>
+
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/configmgr.hxx>
+
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+
+#include <officecfg/Office/ExtensionManager.hxx>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::system;
+
+
+namespace dp_gui {
+
+#define USER_PACKAGE_MANAGER "user"
+#define SHARED_PACKAGE_MANAGER "shared"
+#define BUNDLED_PACKAGE_MANAGER "bundled"
+
+namespace {
+
+struct StrAllFiles : public rtl::StaticWithInit< OUString, StrAllFiles >
+{
+ OUString operator () () {
+ const SolarMutexGuard guard;
+ std::locale loc = Translate::Create("fps");
+ return Translate::get(STR_FILTERNAME_ALL, loc);
+ }
+};
+
+}
+
+// ExtBoxWithBtns_Impl
+class ExtBoxWithBtns_Impl : public ExtensionBox_Impl
+{
+ bool m_bInterfaceLocked;
+
+ ExtMgrDialog* m_pParent;
+
+ void SetButtonStatus( const TEntry_Impl& rEntry );
+ OString ShowPopupMenu( const Point &rPos, const long nPos );
+
+public:
+ explicit ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+
+ void InitFromDialog(ExtMgrDialog *pParentDialog);
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool Command( const CommandEvent& rCEvt ) override;
+
+ virtual void RecalcAll() override;
+ virtual void selectEntry( const long nPos ) override;
+
+ void enableButtons( bool bEnable );
+};
+
+ExtBoxWithBtns_Impl::ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : ExtensionBox_Impl(std::move(xScroll))
+ , m_bInterfaceLocked(false)
+ , m_pParent(nullptr)
+{
+}
+
+void ExtBoxWithBtns_Impl::InitFromDialog(ExtMgrDialog *pParentDialog)
+{
+ setExtensionManager(pParentDialog->getExtensionManager());
+
+ m_pParent = pParentDialog;
+}
+
+void ExtBoxWithBtns_Impl::RecalcAll()
+{
+ const sal_Int32 nActive = getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ SetButtonStatus( GetEntryData( nActive) );
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ m_pParent->enableEnableButton( false );
+ }
+
+ ExtensionBox_Impl::RecalcAll();
+}
+
+
+//This function may be called with nPos < 0
+void ExtBoxWithBtns_Impl::selectEntry( const long nPos )
+{
+ if ( HasActive() && ( nPos == getSelIndex() ) )
+ return;
+
+ ExtensionBox_Impl::selectEntry( nPos );
+}
+
+void ExtBoxWithBtns_Impl::SetButtonStatus(const TEntry_Impl& rEntry)
+{
+ bool bShowOptionBtn = true;
+
+ rEntry->m_bHasButtons = false;
+ if ( ( rEntry->m_eState == REGISTERED ) || ( rEntry->m_eState == NOT_AVAILABLE ) )
+ {
+ m_pParent->enableButtontoEnable( false );
+ }
+ else
+ {
+ m_pParent->enableButtontoEnable( true );
+ bShowOptionBtn = false;
+ }
+
+ if ( ( !rEntry->m_bUser || ( rEntry->m_eState == NOT_AVAILABLE ) || rEntry->m_bMissingDeps )
+ && !rEntry->m_bMissingLic )
+ {
+ m_pParent->enableEnableButton( false );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+
+ if ( rEntry->m_bHasOptions && bShowOptionBtn )
+ {
+ m_pParent->enableOptionsButton( true );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ }
+
+ if ( rEntry->m_bUser || rEntry->m_bShared )
+ {
+ m_pParent->enableRemoveButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+bool ExtBoxWithBtns_Impl::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return ExtensionBox_Impl::Command(rCEvt);
+
+ const Point aMousePos(rCEvt.GetMousePosPixel());
+ const auto nPos = PointToPos(aMousePos);
+ OString sCommand = ShowPopupMenu(aMousePos, nPos);
+
+ if (sCommand == "CMD_ENABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, true );
+ else if (sCommand == "CMD_DISABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, false );
+ else if (sCommand == "CMD_UPDATE")
+ m_pParent->updatePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_REMOVE")
+ m_pParent->removePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_SHOW_LICENSE")
+ {
+ m_pParent->incBusy();
+ ShowLicenseDialog aLicenseDlg(m_pParent->getDialog(), GetEntryData(nPos)->m_xPackage);
+ aLicenseDlg.run();
+ m_pParent->decBusy();
+ }
+
+ return true;
+}
+
+OString ExtBoxWithBtns_Impl::ShowPopupMenu( const Point & rPos, const long nPos )
+{
+ if ( nPos >= static_cast<long>(getItemCount()) )
+ return "CMD_NONE";
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "desktop/ui/extensionmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+#if ENABLE_EXTENSION_UPDATE
+ xPopup->append("CMD_UPDATE", DpResId( RID_CTX_ITEM_CHECK_UPDATE ) );
+#endif
+
+ if ( ! GetEntryData( nPos )->m_bLocked )
+ {
+ if ( GetEntryData( nPos )->m_bUser )
+ {
+ if ( GetEntryData( nPos )->m_eState == REGISTERED )
+ xPopup->append("CMD_DISABLE", DpResId(RID_CTX_ITEM_DISABLE));
+ else if ( GetEntryData( nPos )->m_eState != NOT_AVAILABLE )
+ xPopup->append("CMD_ENABLE", DpResId(RID_CTX_ITEM_ENABLE));
+ }
+ if (!officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ xPopup->append("CMD_REMOVE", DpResId(RID_CTX_ITEM_REMOVE));
+ }
+ }
+
+ if ( !GetEntryData( nPos )->m_sLicenseText.isEmpty() )
+ xPopup->append("CMD_SHOW_LICENSE", DpResId(RID_STR_SHOW_LICENSE_CMD));
+
+ return xPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
+}
+
+bool ExtBoxWithBtns_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (m_bInterfaceLocked)
+ return false;
+ return ExtensionBox_Impl::MouseButtonDown(rMEvt);
+}
+
+void ExtBoxWithBtns_Impl::enableButtons( bool bEnable )
+{
+ m_bInterfaceLocked = ! bEnable;
+
+ if ( bEnable )
+ {
+ sal_Int32 nIndex = getSelIndex();
+ if ( nIndex != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ SetButtonStatus( GetEntryData( nIndex ) );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( false );
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+// DialogHelper
+
+DialogHelper::DialogHelper(const uno::Reference< uno::XComponentContext > &xContext,
+ weld::Window* pWindow)
+ : m_pWindow(pWindow)
+ , m_nEventID(nullptr)
+{
+ m_xContext = xContext;
+}
+
+DialogHelper::~DialogHelper()
+{
+ if ( m_nEventID )
+ Application::RemoveUserEvent( m_nEventID );
+}
+
+
+bool DialogHelper::IsSharedPkgMgr( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ return xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER;
+}
+
+bool DialogHelper::continueOnSharedExtension( const uno::Reference< deployment::XPackage > &xPackage,
+ weld::Widget* pParent,
+ const char* pResID,
+ bool &bHadWarning )
+{
+ if ( !bHadWarning && IsSharedPkgMgr( xPackage ) )
+ {
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(pResID)));
+ bHadWarning = true;
+
+ bool bRet = RET_OK == xBox->run();
+ xBox.reset();
+ decBusy();
+ return bRet;
+ }
+ else
+ return true;
+}
+
+void DialogHelper::openWebBrowser(const OUString& sURL, const OUString& sTitle)
+{
+ if ( sURL.isEmpty() ) // Nothing to do, when the URL is empty
+ return;
+
+ try
+ {
+ uno::Reference< XSystemShellExecute > xSystemShellExecute(
+ SystemShellExecute::create(m_xContext));
+ //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
+ xSystemShellExecute->execute( sURL, OUString(), SystemShellExecuteFlags::URIS_ONLY );
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any exc( ::cppu::getCaughtException() );
+ OUString msg( ::comphelper::anyToString( exc ) );
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, msg));
+ xErrorBox->set_title(sTitle);
+ xErrorBox->run();
+ xErrorBox.reset();
+ decBusy();
+ }
+}
+
+bool DialogHelper::installExtensionWarn(const OUString &rExtensionName)
+{
+ const SolarMutexGuard guard;
+
+ // Check if extension installation is disabled in the expert configurations
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xWarnBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED)));
+ xWarnBox->run();
+ xWarnBox.reset();
+ decBusy();
+
+ return false;
+ }
+
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ DpResId(RID_STR_WARNING_INSTALL_EXTENSION)));
+ OUString sText(xInfoBox->get_primary_text());
+ sText = sText.replaceAll("%NAME", rExtensionName);
+ xInfoBox->set_primary_text(sText);
+
+ bool bRet = RET_OK == xInfoBox->run();
+ xInfoBox.reset();
+ decBusy();
+ return bRet;
+}
+
+bool DialogHelper::installForAllUsers(bool &bInstallForAll)
+{
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "desktop/ui/installforalldialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InstallForAllDialog"));
+ short nRet = xQuery->run();
+ xQuery.reset();
+ decBusy();
+ if (nRet == RET_CANCEL)
+ return false;
+
+ bInstallForAll = ( nRet == RET_NO );
+ return true;
+}
+
+void DialogHelper::PostUserEvent( const Link<void*,void>& rLink, void* pCaller )
+{
+ if ( m_nEventID )
+ Application::RemoveUserEvent( m_nEventID );
+
+ m_nEventID = Application::PostUserEvent(rLink, pCaller);
+}
+
+// ExtMgrDialog
+ExtMgrDialog::ExtMgrDialog(weld::Window *pParent, TheExtensionManager *pManager)
+ : GenericDialogController(pParent, "desktop/ui/extensionmanager.ui", "ExtensionManagerDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , m_sAddPackages(DpResId(RID_STR_ADD_PACKAGES))
+ , m_bHasProgress(false)
+ , m_bProgressChanged(false)
+ , m_bStartProgress(false)
+ , m_bStopProgress(false)
+ , m_bEnableWarning(false)
+ , m_bDisableWarning(false)
+ , m_bDeleteWarning(false)
+ , m_bClosed(false)
+ , m_nProgress(0)
+ , m_pManager(pManager)
+ , m_xExtensionBox(new ExtBoxWithBtns_Impl(m_xBuilder->weld_scrolled_window("scroll")))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xOptionsBtn(m_xBuilder->weld_button("optionsbtn"))
+ , m_xAddBtn(m_xBuilder->weld_button("addbtn"))
+ , m_xRemoveBtn(m_xBuilder->weld_button("removebtn"))
+ , m_xEnableBtn(m_xBuilder->weld_button("enablebtn"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("updatebtn"))
+ , m_xCloseBtn(m_xBuilder->weld_button("close"))
+ , m_xBundledCbx(m_xBuilder->weld_check_button("bundled"))
+ , m_xSharedCbx(m_xBuilder->weld_check_button("shared"))
+ , m_xUserCbx(m_xBuilder->weld_check_button("user"))
+ , m_xGetExtensions(m_xBuilder->weld_link_button("getextensions"))
+ , m_xProgressText(m_xBuilder->weld_label("progressft"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progressbar"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+{
+ m_xExtensionBox->InitFromDialog(this);
+
+ m_xEnableBtn->set_help_id(HID_EXTENSION_MANAGER_LISTBOX_ENABLE);
+
+ m_xOptionsBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleOptionsBtn ) );
+ m_xAddBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleAddBtn ) );
+ m_xRemoveBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleRemoveBtn ) );
+ m_xEnableBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleEnableBtn ) );
+ m_xCloseBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCloseBtn ) );
+
+ m_xCancelBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCancelBtn ) );
+
+ m_xBundledCbx->connect_clicked( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+ m_xSharedCbx->connect_clicked( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+ m_xUserCbx->connect_clicked( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+
+ m_xBundledCbx->set_active(true);
+ m_xSharedCbx->set_active(true);
+ m_xUserCbx->set_active(true);
+
+ m_xProgressBar->hide();
+
+#if ENABLE_EXTENSION_UPDATE
+ m_xUpdateBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleUpdateBtn ) );
+ m_xUpdateBtn->set_sensitive(false);
+#else
+ m_xUpdateBtn->hide();
+#endif
+
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ m_xAddBtn->set_sensitive(false);
+ m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
+ }
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ m_xRemoveBtn->set_sensitive(false);
+ m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
+ }
+
+ m_aIdle.SetPriority(TaskPriority::LOWEST);
+ m_aIdle.SetDebugName( "ExtMgrDialog m_aIdle TimeOutHdl" );
+ m_aIdle.SetInvokeHandler( LINK( this, ExtMgrDialog, TimeOutHdl ) );
+}
+
+ExtMgrDialog::~ExtMgrDialog()
+{
+ m_aIdle.Stop();
+}
+
+void ExtMgrDialog::setGetExtensionsURL( const OUString &rURL )
+{
+ m_xGetExtensions->set_uri( rURL );
+}
+
+void ExtMgrDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ const SolarMutexGuard aGuard;
+ m_xUpdateBtn->set_sensitive(true);
+
+ if (m_xBundledCbx->get_active() && (xPackage->getRepositoryName() == BUNDLED_PACKAGE_MANAGER) )
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+ else if (m_xSharedCbx->get_active() && (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER) )
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+ else if (m_xUserCbx->get_active() && (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER ))
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+}
+
+void ExtMgrDialog::prepareChecking()
+{
+ m_xExtensionBox->prepareChecking();
+}
+
+void ExtMgrDialog::checkEntries()
+{
+ const SolarMutexGuard guard;
+ m_xExtensionBox->checkEntries();
+}
+
+bool ExtMgrDialog::removeExtensionWarn(const OUString &rExtensionName)
+{
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ DpResId(RID_STR_WARNING_REMOVE_EXTENSION)));
+
+ OUString sText(xInfoBox->get_primary_text());
+ sText = sText.replaceAll("%NAME", rExtensionName);
+ xInfoBox->set_primary_text(sText);
+
+ bool bRet = RET_OK == xInfoBox->run();
+ xInfoBox.reset();
+ decBusy();
+
+ return bRet;
+}
+
+void ExtMgrDialog::enablePackage( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bEnable )
+{
+ if ( !xPackage.is() )
+ return;
+
+ if ( bEnable )
+ {
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_ENABLE_SHARED_EXTENSION, m_bEnableWarning))
+ return;
+ }
+ else
+ {
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_DISABLE_SHARED_EXTENSION, m_bDisableWarning))
+ return;
+ }
+
+ m_pManager->getCmdQueue()->enableExtension( xPackage, bEnable );
+}
+
+
+void ExtMgrDialog::removePackage( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ if ( !IsSharedPkgMgr( xPackage ) || m_bDeleteWarning )
+ {
+ if ( ! removeExtensionWarn( xPackage->getDisplayName() ) )
+ return;
+ }
+
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_REMOVE_SHARED_EXTENSION, m_bDeleteWarning))
+ return;
+
+ m_pManager->getCmdQueue()->removeExtension( xPackage );
+}
+
+
+void ExtMgrDialog::updatePackage( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ // get the extension with highest version
+ uno::Sequence<uno::Reference<deployment::XPackage> > seqExtensions =
+ m_pManager->getExtensionManager()->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(xPackage), xPackage->getName(), uno::Reference<ucb::XCommandEnvironment>());
+ uno::Reference<deployment::XPackage> extension =
+ dp_misc::getExtensionWithHighestVersion(seqExtensions);
+ OSL_ASSERT(extension.is());
+ std::vector< css::uno::Reference< css::deployment::XPackage > > vEntries;
+ vEntries.push_back(extension);
+
+ m_pManager->getCmdQueue()->checkForUpdates( vEntries );
+}
+
+
+bool ExtMgrDialog::acceptLicense( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return false;
+
+ m_pManager->getCmdQueue()->acceptLicense( xPackage );
+
+ return true;
+}
+
+
+uno::Sequence< OUString > ExtMgrDialog::raiseAddPicker()
+{
+ sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, m_xDialog.get());
+ const uno::Reference<ui::dialogs::XFilePicker3>& xFilePicker = aDlgHelper.GetFilePicker();
+ xFilePicker->setTitle( m_sAddPackages );
+
+ if ( !m_sLastFolderURL.isEmpty() )
+ xFilePicker->setDisplayDirectory( m_sLastFolderURL );
+
+ // collect and set filter list:
+ typedef std::map< OUString, OUString > t_string2string;
+ t_string2string title2filter;
+ OUString sDefaultFilter( StrAllFiles::get() );
+
+ const uno::Sequence< uno::Reference< deployment::XPackageTypeInfo > > packageTypes(
+ m_pManager->getExtensionManager()->getSupportedPackageTypes() );
+
+ for ( uno::Reference< deployment::XPackageTypeInfo > const & xPackageType : packageTypes )
+ {
+ const OUString filter( xPackageType->getFileFilter() );
+ if (!filter.isEmpty())
+ {
+ const OUString title( xPackageType->getShortDescription() );
+ const std::pair< t_string2string::iterator, bool > insertion(
+ title2filter.emplace( title, filter ) );
+ if ( ! insertion.second )
+ { // already existing, append extensions:
+ OUStringBuffer buf;
+ buf.append( insertion.first->second );
+ buf.append( ';' );
+ buf.append( filter );
+ insertion.first->second = buf.makeStringAndClear();
+ }
+ if ( xPackageType->getMediaType() == "application/vnd.sun.star.package-bundle" )
+ sDefaultFilter = title;
+ }
+ }
+
+ // All files at top:
+ xFilePicker->appendFilter( StrAllFiles::get(), "*.*" );
+ // then supported ones:
+ for (auto const& elem : title2filter)
+ {
+ try
+ {
+ xFilePicker->appendFilter( elem.first, elem.second );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ xFilePicker->setCurrentFilter( sDefaultFilter );
+
+ if ( xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK )
+ return uno::Sequence<OUString>(); // cancelled
+
+ m_sLastFolderURL = xFilePicker->getDisplayDirectory();
+ uno::Sequence< OUString > files( xFilePicker->getSelectedFiles() );
+ OSL_ASSERT( files.hasElements() );
+ return files;
+}
+
+void ExtMgrDialog::enableOptionsButton( bool bEnable )
+{
+ m_xOptionsBtn->set_sensitive( bEnable );
+}
+
+void ExtMgrDialog::enableRemoveButton( bool bEnable )
+{
+ m_xRemoveBtn->set_sensitive( bEnable && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get());
+
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
+ }
+ else
+ {
+ m_xRemoveBtn->set_tooltip_text("");
+ }
+}
+
+void ExtMgrDialog::enableEnableButton( bool bEnable )
+{
+ m_xEnableBtn->set_sensitive( bEnable );
+}
+
+void ExtMgrDialog::enableButtontoEnable( bool bEnable )
+{
+ if (bEnable)
+ {
+ m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_ENABLE ) );
+ m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_ENABLE );
+ }
+ else
+ {
+ m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_DISABLE ) );
+ m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_DISABLE );
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleCancelBtn, weld::Button&, void)
+{
+ if ( m_xAbortChannel.is() )
+ {
+ try
+ {
+ m_xAbortChannel->sendAbort();
+ }
+ catch ( const uno::RuntimeException & )
+ {
+ OSL_FAIL( "### unexpected RuntimeException!" );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleCloseBtn, weld::Button&, void)
+{
+ bool bCallClose = true;
+
+ //only suggest restart if modified and this is the first close attempt
+ if (!m_bClosed && m_pManager->isModified())
+ {
+ m_pManager->clearModified();
+
+ //only suggest restart if we're actually running, e.g. not from standalone unopkg gui
+ if (dp_misc::office_is_running())
+ {
+ SolarMutexGuard aGuard;
+ bCallClose = !::svtools::executeRestartDialog(comphelper::getProcessComponentContext(),
+ m_xDialog.get(),
+ svtools::RESTART_REASON_EXTENSION_INSTALL);
+ }
+ }
+
+ if (bCallClose)
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK( ExtMgrDialog, startProgress, void*, _bLockInterface, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ bool bLockInterface = static_cast<bool>(_bLockInterface);
+
+ if ( m_bStartProgress && !m_bHasProgress )
+ m_aIdle.Start();
+
+ if ( m_bStopProgress )
+ {
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( 100 );
+ m_xAbortChannel.clear();
+
+ SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
+ }
+ else
+ {
+ SAL_INFO( "desktop.deployment", " startProgress handler: start" );
+ }
+
+ m_xCancelBtn->set_sensitive( bLockInterface );
+ m_xAddBtn->set_sensitive( !bLockInterface && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get());
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
+ }
+ else
+ {
+ m_xAddBtn->set_tooltip_text("");
+ }
+
+ m_xUpdateBtn->set_sensitive( !bLockInterface && m_xExtensionBox->getItemCount() );
+ m_xExtensionBox->enableButtons( !bLockInterface );
+
+ clearEventID();
+}
+
+
+void ExtMgrDialog::showProgress( bool _bStart )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bStart = _bStart;
+
+ if ( bStart )
+ {
+ m_nProgress = 0;
+ m_bStartProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress start" );
+ }
+ else
+ {
+ m_nProgress = 100;
+ m_bStopProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress stop!" );
+ }
+
+ DialogHelper::PostUserEvent( LINK( this, ExtMgrDialog, startProgress ), reinterpret_cast<void*>(bStart) );
+ m_aIdle.Start();
+}
+
+
+void ExtMgrDialog::updateProgress( const long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void ExtMgrDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_xAbortChannel = xAbortChannel;
+ m_sProgressText = rText;
+ m_bProgressChanged = true;
+ m_aIdle.Start();
+}
+
+
+void ExtMgrDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ const SolarMutexGuard aGuard;
+ m_xExtensionBox->updateEntry( xPackage );
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleOptionsBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+
+ OUString sExtensionId = m_xExtensionBox->GetEntryData( nActive )->m_xPackage->getIdentifier().Value;
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateOptionsDialog(m_xDialog.get(), sExtensionId));
+
+ pDlg->Execute();
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleAddBtn, weld::Button&, void)
+{
+ incBusy();
+
+ uno::Sequence< OUString > aFileList = raiseAddPicker();
+
+ if ( aFileList.hasElements() )
+ {
+ m_pManager->installPackage( aFileList[0] );
+ }
+
+ decBusy();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleRemoveBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
+ removePackage( pEntry->m_xPackage );
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleEnableBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
+
+ if ( pEntry->m_bMissingLic )
+ acceptLicense( pEntry->m_xPackage );
+ else
+ {
+ const bool bEnable( pEntry->m_eState != REGISTERED );
+ enablePackage( pEntry->m_xPackage, bEnable );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleExtTypeCbx, weld::Button&, void)
+{
+ // re-creates the list of packages with addEntry selecting the packages
+ prepareChecking();
+ m_pManager->createPackageList();
+ checkEntries();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleUpdateBtn, weld::Button&, void)
+{
+#if ENABLE_EXTENSION_UPDATE
+ m_pManager->checkUpdates();
+#else
+ (void) this;
+#endif
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, TimeOutHdl, Timer *, void)
+{
+ if ( m_bStopProgress )
+ {
+ m_bHasProgress = false;
+ m_bStopProgress = false;
+ m_xProgressText->hide();
+ m_xProgressBar->hide();
+ m_xCancelBtn->hide();
+ }
+ else
+ {
+ if ( m_bProgressChanged )
+ {
+ m_bProgressChanged = false;
+ m_xProgressText->set_label(m_sProgressText);
+ }
+
+ if ( m_bStartProgress )
+ {
+ m_bStartProgress = false;
+ m_bHasProgress = true;
+ m_xProgressBar->show();
+ m_xProgressText->show();
+ m_xCancelBtn->set_sensitive(true);
+ m_xCancelBtn->show();
+ }
+
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( static_cast<sal_uInt16>(m_nProgress) );
+ }
+}
+
+void ExtMgrDialog::Close()
+{
+ m_pManager->terminateDialog();
+ m_bClosed = true;
+}
+
+//UpdateRequiredDialog
+UpdateRequiredDialog::UpdateRequiredDialog(weld::Window *pParent, TheExtensionManager *pManager)
+ : GenericDialogController(pParent, "desktop/ui/updaterequireddialog.ui", "UpdateRequiredDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , m_sCloseText(DpResId(RID_STR_CLOSE_BTN))
+ , m_bHasProgress(false)
+ , m_bProgressChanged(false)
+ , m_bStartProgress(false)
+ , m_bStopProgress(false)
+ , m_bHasLockedEntries(false)
+ , m_nProgress(0)
+ , m_pManager(pManager)
+ , m_xExtensionBox(new ExtensionBox_Impl(m_xBuilder->weld_scrolled_window("scroll")))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xUpdateNeeded(m_xBuilder->weld_label("updatelabel"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("ok"))
+ , m_xCloseBtn(m_xBuilder->weld_button("disable"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xProgressText(m_xBuilder->weld_label("progresslabel"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+{
+ m_xExtensionBox->setExtensionManager(pManager);
+
+ m_xUpdateBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleUpdateBtn ) );
+ m_xCloseBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCloseBtn ) );
+ m_xCancelBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCancelBtn ) );
+
+ OUString aText = m_xUpdateNeeded->get_label();
+ aText = aText.replaceAll(
+ "%PRODUCTNAME", utl::ConfigManager::getProductName());
+ m_xUpdateNeeded->set_label(aText);
+
+ m_xProgressBar->hide();
+ m_xUpdateBtn->set_sensitive( false );
+ m_xCloseBtn->grab_focus();
+
+ m_aIdle.SetPriority( TaskPriority::LOWEST );
+ m_aIdle.SetDebugName( "UpdateRequiredDialog m_aIdle TimeOutHdl" );
+ m_aIdle.SetInvokeHandler( LINK( this, UpdateRequiredDialog, TimeOutHdl ) );
+}
+
+UpdateRequiredDialog::~UpdateRequiredDialog()
+{
+ m_aIdle.Stop();
+}
+
+void UpdateRequiredDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ // We will only add entries to the list with unsatisfied dependencies
+ if ( !bLicenseMissing && !checkDependencies( xPackage ) )
+ {
+ m_bHasLockedEntries |= m_pManager->isReadOnly( xPackage );
+ const SolarMutexGuard aGuard;
+ m_xUpdateBtn->set_sensitive(true);
+ m_xExtensionBox->addEntry( xPackage );
+ }
+}
+
+
+void UpdateRequiredDialog::prepareChecking()
+{
+ m_xExtensionBox->prepareChecking();
+}
+
+
+void UpdateRequiredDialog::checkEntries()
+{
+ const SolarMutexGuard guard;
+ m_xExtensionBox->checkEntries();
+
+ if ( ! hasActiveEntries() )
+ {
+ m_xCloseBtn->set_label( m_sCloseText );
+ m_xCloseBtn->grab_focus();
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCancelBtn, weld::Button&, void)
+{
+ if ( m_xAbortChannel.is() )
+ {
+ try
+ {
+ m_xAbortChannel->sendAbort();
+ }
+ catch ( const uno::RuntimeException & )
+ {
+ OSL_FAIL( "### unexpected RuntimeException!" );
+ }
+ }
+}
+
+
+IMPL_LINK( UpdateRequiredDialog, startProgress, void*, _bLockInterface, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ bool bLockInterface = static_cast<bool>(_bLockInterface);
+
+ if ( m_bStartProgress && !m_bHasProgress )
+ m_aIdle.Start();
+
+ if ( m_bStopProgress )
+ {
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( 100 );
+ m_xAbortChannel.clear();
+ SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
+ }
+ else
+ {
+ SAL_INFO( "desktop.deployment", " startProgress handler: start" );
+ }
+
+ m_xCancelBtn->set_sensitive( bLockInterface );
+ m_xUpdateBtn->set_sensitive( false );
+ clearEventID();
+}
+
+
+void UpdateRequiredDialog::showProgress( bool _bStart )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bStart = _bStart;
+
+ if ( bStart )
+ {
+ m_nProgress = 0;
+ m_bStartProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress start" );
+ }
+ else
+ {
+ m_nProgress = 100;
+ m_bStopProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress stop!" );
+ }
+
+ DialogHelper::PostUserEvent( LINK( this, UpdateRequiredDialog, startProgress ), reinterpret_cast<void*>(bStart) );
+ m_aIdle.Start();
+}
+
+
+void UpdateRequiredDialog::updateProgress( const long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void UpdateRequiredDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_xAbortChannel = xAbortChannel;
+ m_sProgressText = rText;
+ m_bProgressChanged = true;
+ m_aIdle.Start();
+}
+
+
+void UpdateRequiredDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ // We will remove all updated packages with satisfied dependencies, but
+ // we will show all disabled entries so the user sees the result
+ // of the 'disable all' button
+ const SolarMutexGuard aGuard;
+ if ( isEnabled( xPackage ) && checkDependencies( xPackage ) )
+ m_xExtensionBox->removeEntry( xPackage );
+ else
+ m_xExtensionBox->updateEntry( xPackage );
+
+ if ( ! hasActiveEntries() )
+ {
+ m_xCloseBtn->set_label( m_sCloseText );
+ m_xCloseBtn->grab_focus();
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleUpdateBtn, weld::Button&, void)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ std::vector< uno::Reference< deployment::XPackage > > vUpdateEntries;
+ sal_Int32 nCount = m_xExtensionBox->GetEntryCount();
+
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( i );
+ vUpdateEntries.push_back( pEntry->m_xPackage );
+ }
+
+ aGuard.clear();
+
+ m_pManager->getCmdQueue()->checkForUpdates( vUpdateEntries );
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCloseBtn, weld::Button&, void)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !isBusy() )
+ {
+ if ( m_bHasLockedEntries )
+ m_xDialog->response(-1);
+ else if ( hasActiveEntries() )
+ disableAllEntries();
+ else
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, TimeOutHdl, Timer *, void)
+{
+ if ( m_bStopProgress )
+ {
+ m_bHasProgress = false;
+ m_bStopProgress = false;
+ m_xProgressText->hide();
+ m_xProgressBar->hide();
+ m_xCancelBtn->hide();
+ }
+ else
+ {
+ if ( m_bProgressChanged )
+ {
+ m_bProgressChanged = false;
+ m_xProgressText->set_label( m_sProgressText );
+ }
+
+ if ( m_bStartProgress )
+ {
+ m_bStartProgress = false;
+ m_bHasProgress = true;
+ m_xProgressBar->show();
+ m_xProgressText->show();
+ m_xCancelBtn->set_sensitive(true);
+ m_xCancelBtn->show();
+ }
+
+ if (m_xProgressBar->get_visible())
+ m_xProgressBar->set_percentage(m_nProgress);
+ }
+}
+
+// VCL::Dialog
+short UpdateRequiredDialog::run()
+{
+ //ToDo
+ //I believe m_bHasLockedEntries was used to prevent showing extensions which cannot
+ //be disabled because they are in a read only repository. However, disabling extensions
+ //is now always possible because the registration data of all repositories
+ //are in the user installation.
+ //Therefore all extensions could be displayed and all the handling around m_bHasLockedEntries
+ //could be removed.
+ if ( m_bHasLockedEntries )
+ {
+ // Set other text, disable update btn, remove not shared entries from list;
+ m_xUpdateNeeded->set_label( DpResId( RID_STR_NO_ADMIN_PRIVILEGE ) );
+ m_xCloseBtn->set_label( DpResId( RID_STR_EXIT_BTN ) );
+ m_xUpdateBtn->set_sensitive( false );
+ m_xExtensionBox->RemoveUnlocked();
+ }
+
+ return GenericDialogController::run();
+}
+
+// Check dependencies of all packages
+
+bool UpdateRequiredDialog::isEnabled( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ bool bRegistered = false;
+ try {
+ beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() ) );
+ if ( option.IsPresent )
+ {
+ ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
+ if ( reg.IsAmbiguous )
+ bRegistered = false;
+ else
+ bRegistered = reg.Value;
+ }
+ else
+ bRegistered = false;
+ }
+ catch ( const uno::RuntimeException & ) { throw; }
+ catch (const uno::Exception & ) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ bRegistered = false;
+ }
+
+ return bRegistered;
+}
+
+// Checks the dependencies no matter if the extension is enabled or disabled!
+bool UpdateRequiredDialog::checkDependencies( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ bool bDependenciesValid = false;
+ try {
+ bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException & ) {}
+ return bDependenciesValid;
+}
+
+
+bool UpdateRequiredDialog::hasActiveEntries()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bRet = false;
+ long nCount = m_xExtensionBox->GetEntryCount();
+ for ( long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
+
+ if ( isEnabled(pEntry->m_xPackage) && !checkDependencies( pEntry->m_xPackage ) )
+ {
+ bRet = true;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+
+void UpdateRequiredDialog::disableAllEntries()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ incBusy();
+
+ long nCount = m_xExtensionBox->GetEntryCount();
+ for ( long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
+ m_pManager->getCmdQueue()->enableExtension( pEntry->m_xPackage, false );
+ }
+
+ decBusy();
+
+ if ( ! hasActiveEntries() )
+ m_xCloseBtn->set_label( m_sCloseText );
+}
+
+// ShowLicenseDialog
+ShowLicenseDialog::ShowLicenseDialog(weld::Window* pParent,
+ const uno::Reference< deployment::XPackage> &xPackage)
+ : GenericDialogController(pParent, "desktop/ui/showlicensedialog.ui", "ShowLicenseDialog")
+ , m_xLicenseText(m_xBuilder->weld_text_view("textview"))
+{
+ m_xLicenseText->set_size_request(m_xLicenseText->get_approximate_digit_width() * 72,
+ m_xLicenseText->get_height_rows(21));
+ m_xLicenseText->set_text(xPackage->getLicenseText());
+}
+
+ShowLicenseDialog::~ShowLicenseDialog()
+{
+}
+
+// UpdateRequiredDialogService
+
+UpdateRequiredDialogService::UpdateRequiredDialogService( SAL_UNUSED_PARAMETER uno::Sequence< uno::Any > const&,
+ uno::Reference< uno::XComponentContext > const& xComponentContext )
+ : m_xComponentContext( xComponentContext )
+{
+}
+
+
+// XExecutableDialog
+
+void UpdateRequiredDialogService::setTitle( OUString const & )
+{
+}
+
+
+sal_Int16 UpdateRequiredDialogService::execute()
+{
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > xManager( TheExtensionManager::get(
+ m_xComponentContext) );
+ xManager->createDialog( true );
+ sal_Int16 nRet = xManager->execute();
+
+ return nRet;
+}
+
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.hxx b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
new file mode 100644
index 000000000..b3d348c84
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_DIALOG2_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_DIALOG2_HXX
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/waitobj.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+#include <osl/mutex.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+
+namespace dp_gui {
+
+
+class ExtBoxWithBtns_Impl;
+class ExtensionBox_Impl;
+class TheExtensionManager;
+
+
+class DialogHelper
+{
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ weld::Window* m_pWindow;
+ ImplSVEvent * m_nEventID;
+ TopLevelWindowLocker m_aBusy;
+
+public:
+ DialogHelper(const css::uno::Reference< css::uno::XComponentContext > &,
+ weld::Window* pWindow);
+ virtual ~DialogHelper();
+
+ void openWebBrowser(const OUString& rURL, const OUString& rTitle);
+ weld::Window* getFrameWeld() const { return m_pWindow; }
+ void PostUserEvent( const Link<void*,void>& rLink, void* pCaller );
+ void clearEventID() { m_nEventID = nullptr; }
+
+ virtual void showProgress( bool bStart ) = 0;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) = 0;
+ virtual void updateProgress( const long nProgress ) = 0;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) = 0;
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bLicenseMissing = false ) = 0;
+
+ virtual void prepareChecking() = 0;
+ virtual void checkEntries() = 0;
+
+ static bool IsSharedPkgMgr( const css::uno::Reference< css::deployment::XPackage > &);
+ bool continueOnSharedExtension( const css::uno::Reference< css::deployment::XPackage > &,
+ weld::Widget* pParent,
+ const char* pResID,
+ bool &bHadWarning );
+
+ void incBusy() { m_aBusy.incBusy(m_pWindow); }
+ void decBusy() { m_aBusy.decBusy(); }
+ bool isBusy() const { return m_aBusy.isBusy(); }
+ bool installExtensionWarn(const OUString &rExtensionURL);
+ bool installForAllUsers(bool &bInstallForAll);
+};
+
+class ExtMgrDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sAddPackages;
+ OUString m_sProgressText;
+ OUString m_sLastFolderURL;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bEnableWarning;
+ bool m_bDisableWarning;
+ bool m_bDeleteWarning;
+ bool m_bClosed;
+ long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtBoxWithBtns_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Button> m_xOptionsBtn;
+ std::unique_ptr<weld::Button> m_xAddBtn;
+ std::unique_ptr<weld::Button> m_xRemoveBtn;
+ std::unique_ptr<weld::Button> m_xEnableBtn;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::CheckButton> m_xBundledCbx;
+ std::unique_ptr<weld::CheckButton> m_xSharedCbx;
+ std::unique_ptr<weld::CheckButton> m_xUserCbx;
+ std::unique_ptr<weld::LinkButton> m_xGetExtensions;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+
+ bool removeExtensionWarn(const OUString &rExtensionTitle);
+
+ DECL_LINK( HandleOptionsBtn, weld::Button&, void );
+ DECL_LINK( HandleAddBtn, weld::Button&, void );
+ DECL_LINK( HandleRemoveBtn, weld::Button&, void );
+ DECL_LINK( HandleEnableBtn, weld::Button&, void );
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleExtTypeCbx, weld::Button&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+public:
+ ExtMgrDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~ExtMgrDialog() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ void setGetExtensionsURL( const OUString &rURL );
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+ void enablePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bEnable );
+ void removePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ void updatePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool acceptLicense(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+
+ void Close();
+
+ TheExtensionManager* getExtensionManager() const { return m_pManager; }
+
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+
+ css::uno::Sequence< OUString > raiseAddPicker();
+
+ void enableOptionsButton( bool bEnable );
+ void enableRemoveButton( bool bEnable );
+ void enableEnableButton( bool bEnable );
+ /*
+ * Transform the button to "Enable", or to "Disable"
+ * based on the value of bEnable.
+ */
+ void enableButtontoEnable( bool bEnable );
+};
+
+
+class UpdateRequiredDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sCloseText;
+ OUString m_sProgressText;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bHasLockedEntries;
+ long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtensionBox_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Label> m_xUpdateNeeded;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+ static bool isEnabled( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ static bool checkDependencies( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool hasActiveEntries();
+ void disableAllEntries();
+
+public:
+ UpdateRequiredDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~UpdateRequiredDialog() override;
+
+ virtual short run() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+};
+
+
+class ShowLicenseDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::TextView> m_xLicenseText;
+public:
+ ShowLicenseDialog(weld::Window * pParent, const css::uno::Reference< css::deployment::XPackage > &xPackage);
+ virtual ~ShowLicenseDialog() override;
+};
+
+class UpdateRequiredDialogService : public ::cppu::WeakImplHelper< css::ui::dialogs::XExecutableDialog >
+{
+ css::uno::Reference< css::uno::XComponentContext > const m_xComponentContext;
+public:
+ UpdateRequiredDialogService( css::uno::Sequence< css::uno::Any > const & args,
+ css::uno::Reference< css::uno::XComponentContext> const & xComponentContext );
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle( OUString const & title ) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+
+} // namespace dp_gui
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
new file mode 100644
index 000000000..00ed69d51
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
@@ -0,0 +1,1126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+
+#include <com/sun/star/deployment/ui/LicenseDialog.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/TypeClass.hpp>
+#include <o3tl/any.hxx>
+#include <osl/conditn.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_dependencydialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui_theextmgr.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_dependencies.hxx>
+#include <dp_misc.h>
+#include <dp_identifier.hxx>
+#include <dp_version.hxx>
+
+#include <queue>
+#include <memory>
+
+#ifdef _WIN32
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objbase.h>
+#endif
+
+
+using namespace ::com::sun::star;
+
+namespace {
+
+OUString getVersion( OUString const & sVersion )
+{
+ return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
+}
+
+OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ return getVersion( rPackage->getVersion());
+}
+}
+
+
+namespace dp_gui {
+
+namespace {
+
+class ProgressCmdEnv
+ : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
+ task::XInteractionHandler,
+ ucb::XProgressHandler >
+{
+ uno::Reference< task::XInteractionHandler2> m_xHandler;
+ uno::Reference< uno::XComponentContext > m_xContext;
+
+ DialogHelper* m_pDialogHelper;
+ OUString m_sTitle;
+ bool m_bWarnUser;
+ sal_Int32 m_nCurrentProgress;
+
+ void updateProgress();
+
+ /// @throws uno::RuntimeException
+ void update_( uno::Any const & Status );
+
+public:
+ /** When param bAskWhenInstalling = true, then the user is asked if he
+ agrees to install this extension. In case this extension is already installed
+ then the user is also notified and asked if he wants to replace that existing
+ extension. In first case an interaction request with an InstallException
+ will be handled and in the second case a VersionException will be handled.
+ */
+
+ ProgressCmdEnv( const uno::Reference< uno::XComponentContext >& rContext,
+ DialogHelper* pDialogHelper,
+ const OUString& rTitle )
+ : m_xContext( rContext )
+ , m_pDialogHelper( pDialogHelper )
+ , m_sTitle( rTitle )
+ , m_bWarnUser( false )
+ , m_nCurrentProgress(0)
+ {}
+
+ weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
+
+ void startProgress();
+ void stopProgress();
+ void progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel );
+ void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
+
+ // XCommandEnvironment
+ virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
+ virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( uno::Any const & Status ) override;
+ virtual void SAL_CALL update( uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+struct ExtensionCmd
+{
+ enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
+
+ E_CMD_TYPE m_eCmdType;
+ bool m_bWarnUser;
+ OUString m_sExtensionURL;
+ OUString m_sRepository;
+ uno::Reference< deployment::XPackage > m_xPackage;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( bWarnUser ),
+ m_sExtensionURL( rExtensionURL ),
+ m_sRepository( rRepository ) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ const uno::Reference< deployment::XPackage > &rPackage )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_xPackage( rPackage ) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_vExtensionList( vExtensionList ) {};
+};
+
+}
+
+typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
+
+
+class ExtensionCmdQueue::Thread: public salhelper::Thread
+{
+public:
+ Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ const uno::Reference< uno::XComponentContext > & rContext );
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
+ void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
+ void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
+ void stop();
+ bool isBusy();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void _insert(const TExtensionCmd& rExtCmd);
+
+ void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
+ void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+
+ enum Input { NONE, START, STOP };
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ std::queue< TExtensionCmd > m_queue;
+
+ DialogHelper *m_pDialogHelper;
+ TheExtensionManager *m_pManager;
+
+ const OUString m_sEnablingPackages;
+ const OUString m_sDisablingPackages;
+ const OUString m_sAddingPackages;
+ const OUString m_sRemovingPackages;
+ const OUString m_sDefaultCmd;
+ const OUString m_sAcceptLicense;
+ osl::Condition m_wakeup;
+ osl::Mutex m_mutex;
+ Input m_eInput;
+ bool m_bStopped;
+ bool m_bWorking;
+};
+
+
+void ProgressCmdEnv::startProgress()
+{
+ m_nCurrentProgress = 0;
+
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( true );
+}
+
+
+void ProgressCmdEnv::stopProgress()
+{
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( false );
+}
+
+
+void ProgressCmdEnv::progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel )
+{
+ m_nCurrentProgress = 0;
+ if ( m_pDialogHelper )
+ {
+ m_pDialogHelper->updateProgress( rText, xAbortChannel );
+ m_pDialogHelper->updateProgress( 5 );
+ }
+}
+
+
+void ProgressCmdEnv::updateProgress()
+{
+ long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updateProgress( nProgress );
+}
+
+// XCommandEnvironment
+
+uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
+{
+ return this;
+}
+
+
+// XInteractionHandler
+
+void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n");
+
+ lang::WrappedTargetException wtExc;
+ deployment::DependencyException depExc;
+ deployment::LicenseException licExc;
+ deployment::VersionException verExc;
+ deployment::InstallException instExc;
+ deployment::PlatformException platExc;
+
+ // selections:
+ bool approve = false;
+ bool abort = false;
+
+ if (request >>= wtExc) {
+ // handable deployment error signalled, e.g.
+ // bundle item registration failed, notify cause only:
+ uno::Any cause;
+ deployment::DeploymentException dpExc;
+ if (wtExc.TargetException >>= dpExc)
+ cause = dpExc.Cause;
+ else {
+ ucb::CommandFailedException cfExc;
+ if (wtExc.TargetException >>= cfExc)
+ cause = cfExc.Reason;
+ else
+ cause = wtExc.TargetException;
+ }
+ update_( cause );
+
+ // ignore intermediate errors of legacy packages, i.e.
+ // former pkgchk behaviour:
+ const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
+ OSL_ASSERT( xPackage.is() );
+ if ( xPackage.is() )
+ {
+ const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ approve = ( xPackage->isBundle() &&
+ xPackageType->getMediaType().match(
+ "application/vnd.sun.star.legacy-package-bundle" ));
+ }
+ }
+ abort = !approve;
+ }
+ else if (request >>= depExc)
+ {
+ std::vector< OUString > deps;
+ deps.reserve(depExc.UnsatisfiedDependencies.getLength());
+ for (auto const & i : std::as_const(depExc.UnsatisfiedDependencies))
+ {
+ deps.push_back( dp_misc::Dependencies::getErrorText(i) );
+ }
+ {
+ SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ DependencyDialog aDlg(activeDialog(), deps);
+ short n = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ // Distinguish between closing the dialog and programmatically
+ // canceling the dialog (headless VCL):
+ approve = n == RET_OK
+ || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
+ }
+ }
+ else if (request >>= licExc)
+ {
+ SolarMutexGuard guard;
+
+ weld::Window *pTopLevel = activeDialog();
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
+ deployment::ui::LicenseDialog::create(
+ m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
+ licExc.ExtensionName, licExc.Text ) );
+ sal_Int16 res = xDialog->execute();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
+ abort = true;
+ else if ( res == ui::dialogs::ExecutableDialogResults::OK )
+ approve = true;
+ else
+ {
+ OSL_ASSERT(false);
+ }
+ }
+ else if (request >>= verExc)
+ {
+ const char* id;
+ switch (dp_misc::compareVersions(
+ verExc.NewVersion, verExc.Deployed->getVersion() ))
+ {
+ case dp_misc::LESS:
+ id = RID_STR_WARNING_VERSION_LESS;
+ break;
+ case dp_misc::EQUAL:
+ id = RID_STR_WARNING_VERSION_EQUAL;
+ break;
+ default: // dp_misc::GREATER
+ id = RID_STR_WARNING_VERSION_GREATER;
+ break;
+ }
+ OSL_ASSERT( verExc.Deployed.is() );
+ bool bEqualNames = verExc.NewDisplayName ==
+ verExc.Deployed->getDisplayName();
+ {
+ SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
+ OUString s;
+ if (bEqualNames)
+ {
+ s = xBox->get_primary_text();
+ }
+ else if (!strcmp(id, RID_STR_WARNING_VERSION_EQUAL))
+ {
+ //hypothetical: requires two instances of an extension with the same
+ //version to have different display names. Probably the developer forgot
+ //to change the version.
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
+ }
+ else if (!strcmp(id, RID_STR_WARNING_VERSION_LESS))
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
+ }
+ else if (!strcmp(id, RID_STR_WARNING_VERSION_GREATER))
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
+ }
+ s = s.replaceAll("$NAME", verExc.NewDisplayName);
+ s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
+ s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
+ s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
+ xBox->set_primary_text(s);
+ approve = xBox->run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ abort = !approve;
+ }
+ }
+ else if (request >>= instExc)
+ {
+ if ( ! m_bWarnUser )
+ {
+ approve = true;
+ }
+ else
+ {
+ if ( m_pDialogHelper )
+ {
+ SolarMutexGuard guard;
+
+ approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
+ }
+ else
+ approve = false;
+ abort = !approve;
+ }
+ }
+ else if (request >>= platExc)
+ {
+ SolarMutexGuard guard;
+ OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
+ sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, sMsg));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ approve = true;
+ }
+
+ if (!approve && !abort)
+ {
+ // forward to UUI handler:
+ if (! m_xHandler.is()) {
+ // late init:
+ m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
+ }
+ m_xHandler->handle( xRequest );
+ }
+ else
+ {
+ // select:
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ else if (abort) {
+ uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionAbort.is()) {
+ xInteractionAbort->select();
+ // don't query again for ongoing continuations:
+ abort = false;
+ }
+ }
+ }
+ }
+}
+
+
+// XProgressHandler
+
+void ProgressCmdEnv::push( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::update_( uno::Any const & rStatus )
+{
+ OUString text;
+ if ( rStatus.hasValue() && !( rStatus >>= text) )
+ {
+ if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
+ text = e->Message;
+ if ( text.isEmpty() )
+ text = ::comphelper::anyToString( rStatus ); // fallback
+
+ const SolarMutexGuard aGuard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, text));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ }
+ ++m_nCurrentProgress;
+ updateProgress();
+}
+
+
+void ProgressCmdEnv::update( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::pop()
+{
+ update_( uno::Any() ); // no message
+}
+
+
+ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ const uno::Reference< uno::XComponentContext > & rContext ) :
+ salhelper::Thread( "dp_gui_extensioncmdqueue" ),
+ m_xContext( rContext ),
+ m_pDialogHelper( pDialogHelper ),
+ m_pManager( pManager ),
+ m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
+ m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
+ m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
+ m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
+ m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
+ m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
+ m_eInput( NONE ),
+ m_bStopped( false ),
+ m_bWorking( false )
+{
+ OSL_ASSERT( pDialogHelper );
+}
+
+
+void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ if ( !rExtensionURL.isEmpty() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::REMOVE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ACCEPT_LICENSE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( bEnable ? ExtensionCmd::ENABLE :
+ ExtensionCmd::DISABLE,
+ rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::checkForUpdates(
+ const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
+{
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::CHECK_FOR_UPDATES, vExtensionList );
+ _insert( pEntry );
+}
+
+
+//Stopping this thread will not abort the installation of extensions.
+void ExtensionCmdQueue::Thread::stop()
+{
+ osl::MutexGuard aGuard( m_mutex );
+ m_bStopped = true;
+ m_eInput = STOP;
+ m_wakeup.set();
+}
+
+
+bool ExtensionCmdQueue::Thread::isBusy()
+{
+ osl::MutexGuard aGuard( m_mutex );
+ return m_bWorking;
+}
+
+
+ExtensionCmdQueue::Thread::~Thread() {}
+
+
+void ExtensionCmdQueue::Thread::execute()
+{
+#ifdef _WIN32
+ //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
+ //DialogHelper::openWebBrowser
+ CoUninitialize();
+ (void) CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+#endif
+ for (;;)
+ {
+ if ( m_wakeup.wait() != osl::Condition::result_ok )
+ {
+ dp_misc::TRACE( "dp_gui::ExtensionCmdQueue::Thread::run: ignored "
+ "osl::Condition::wait failure\n" );
+ }
+ m_wakeup.reset();
+
+ int nSize;
+ Input eInput;
+ {
+ osl::MutexGuard aGuard( m_mutex );
+ eInput = m_eInput;
+ m_eInput = NONE;
+ nSize = m_queue.size();
+ m_bWorking = false;
+ }
+
+ // If this thread has been woken up by anything else except start, stop
+ // then input is NONE and we wait again.
+ // We only install the extension which are currently in the queue.
+ // The progressbar will be set to show the progress of the current number
+ // of extensions. If we allowed to add extensions now then the progressbar may
+ // have reached the end while we still install newly added extensions.
+ if ( ( eInput == NONE ) || ( nSize == 0 ) )
+ continue;
+ if ( eInput == STOP )
+ break;
+
+ ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
+
+ // Do not lock the following part with addExtension. addExtension may be called in the main thread.
+ // If the message box "Do you want to install the extension (or similar)" is shown and then
+ // addExtension is called, which then blocks the main thread, then we deadlock.
+ bool bStartProgress = true;
+
+ while ( --nSize >= 0 )
+ {
+ {
+ osl::MutexGuard aGuard( m_mutex );
+ m_bWorking = true;
+ }
+
+ try
+ {
+ TExtensionCmd pEntry;
+ {
+ ::osl::MutexGuard queueGuard( m_mutex );
+ pEntry = m_queue.front();
+ m_queue.pop();
+ }
+
+ if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
+ {
+ currentCmdEnv->startProgress();
+ bStartProgress = false;
+ }
+
+ switch ( pEntry->m_eCmdType ) {
+ case ExtensionCmd::ADD :
+ _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
+ break;
+ case ExtensionCmd::REMOVE :
+ _removeExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::ENABLE :
+ _enableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::DISABLE :
+ _disableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::CHECK_FOR_UPDATES :
+ _checkForUpdates( pEntry->m_vExtensionList );
+ break;
+ case ExtensionCmd::ACCEPT_LICENSE :
+ _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ }
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ //This exception is thrown when the user clicks cancel on the progressbar.
+ //Then we cancel the installation of all extensions and remove them from
+ //the queue.
+ {
+ ::osl::MutexGuard queueGuard2(m_mutex);
+ while ( --nSize >= 0 )
+ m_queue.pop();
+ }
+ break;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ //This exception is thrown when a user clicked cancel in the messagebox which was
+ //started by the interaction handler. For example the user will be asked if he/she
+ //really wants to install the extension.
+ //These interactions run for exactly one extension at a time. Therefore we continue
+ //with installing the remaining extensions.
+ continue;
+ }
+ catch ( const uno::Exception & )
+ {
+ //Todo display the user an error
+ //see also DialogImpl::SyncPushButton::Click()
+ uno::Any exc( ::cppu::getCaughtException() );
+ OUString msg;
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc)
+ {
+ if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
+ {
+ // notify error cause only:
+ msg = e->Message;
+ }
+ }
+ if (msg.isEmpty()) // fallback for debugging purposes
+ msg = ::comphelper::anyToString(exc);
+
+ const SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, msg));
+ if (m_pDialogHelper)
+ xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ //Continue with installation of the remaining extensions
+ }
+ {
+ osl::MutexGuard aGuard( m_mutex );
+ m_bWorking = false;
+ }
+ }
+
+ {
+ // when leaving the while loop with break, we should set working to false, too
+ osl::MutexGuard aGuard( m_mutex );
+ m_bWorking = false;
+ }
+
+ if ( !bStartProgress )
+ currentCmdEnv->stopProgress();
+ }
+ //end for
+#ifdef _WIN32
+ CoUninitialize();
+#endif
+}
+
+
+void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
+ //and anyTitle.get<OUString> throws as RuntimeException.
+ uno::Any anyTitle;
+ try
+ {
+ anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv.get(), m_xContext ).getPropertyValue( "Title" );
+ }
+ catch ( const uno::Exception & )
+ {
+ return;
+ }
+
+ OUString sName;
+ if ( ! (anyTitle >>= sName) )
+ {
+ OSL_FAIL("Could not get file name for extension.");
+ return;
+ }
+
+ rCmdEnv->setWarnUser( bWarnUser );
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
+ rRepository, xAbortChannel, rCmdEnv.get() );
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
+ // cancel this exception is thrown.
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ // User clicked the cancel button
+ // TODO: handle cancel
+ }
+ rCmdEnv->setWarnUser( false );
+}
+
+
+void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ OUString id( dp_misc::getIdentifier( xPackage ) );
+ try
+ {
+ xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv.get() );
+ }
+ catch ( const deployment::DeploymentException & )
+ {}
+ catch ( const ucb::CommandFailedException & )
+ {}
+ catch ( const ucb::CommandAbortedException & )
+ {}
+
+ // Check, if there are still updates to be notified via menu bar icon
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+ UpdateDialog::createNotifyJob( false, aItemList );
+}
+
+
+void ExtensionCmdQueue::Thread::_checkForUpdates(
+ const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
+{
+ const SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::vector< UpdateData > vData;
+ UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, vExtensionList, &vData);
+
+ aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
+
+ bool bOk = aUpdateDialog.run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+
+ if (bOk && !vData.empty())
+ {
+ // If there is at least one directly downloadable extension then we
+ // open the install dialog.
+ std::vector< UpdateData > dataDownload;
+
+ for (auto const& data : vData)
+ {
+ if ( data.sWebsiteURL.isEmpty() )
+ dataDownload.push_back(data);
+ }
+
+ short nDialogResult = RET_OK;
+ if ( !dataDownload.empty() )
+ {
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
+ nDialogResult = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
+
+ //Now start the webbrowser and navigate to the websites where we get the updates
+ if ( RET_OK == nDialogResult )
+ {
+ for (auto const& data : vData)
+ {
+ if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
+ m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
+ }
+ }
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
+}
+
+
+void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv.get() );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
+{
+ ::osl::MutexGuard aGuard( m_mutex );
+
+ // If someone called stop then we do not process the command -> game over!
+ if ( m_bStopped )
+ return;
+
+ m_queue.push( rExtCmd );
+ m_eInput = START;
+ m_wakeup.set();
+}
+
+
+ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const uno::Reference< uno::XComponentContext > &rContext )
+ : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
+{
+ m_thread->launch();
+}
+
+ExtensionCmdQueue::~ExtensionCmdQueue() {
+ stop();
+}
+
+void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
+ const OUString & repository,
+ const bool bWarnUser )
+{
+ m_thread->addExtension( extensionURL, repository, bWarnUser );
+}
+
+void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->removeExtension( rPackage );
+}
+
+void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ m_thread->enableExtension( rPackage, bEnable );
+}
+
+void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
+{
+ m_thread->checkForUpdates( vExtensionList );
+}
+
+void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->acceptLicense( rPackage );
+}
+
+void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
+{
+ dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+}
+
+void ExtensionCmdQueue::stop()
+{
+ m_thread->stop();
+}
+
+bool ExtensionCmdQueue::isBusy()
+{
+ return m_thread->isBusy();
+}
+
+void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< task::XInteractionRequest > & xRequest )
+{
+ ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+ xCmdEnv->handle( xRequest );
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
new file mode 100644
index 000000000..9bc9b608e
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_EXTENSIONCMDQUEUE_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_EXTENSIONCMDQUEUE_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+
+#include <vector>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+namespace com::sun::star {
+ namespace task { class XInteractionRequest; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+
+class DialogHelper;
+class TheExtensionManager;
+
+/**
+ Manages installing of extensions in the GUI mode. Requests for installing
+ Extensions can be asynchronous. For example, the Extension Manager is running
+ in an office process and someone uses the system integration to install an Extension.
+ That is, the user double clicks an extension symbol in a file browser, which then
+ causes an invocation of "unopkg gui ext". When at that time the Extension Manager
+ already performs a task, triggered by the user (for example, add, update, disable,
+ enable) then adding of the extension will be postponed until the user has finished
+ the task.
+
+ This class also ensures that the extensions are not installed in the main thread.
+ Doing so would cause a deadlock because of the progress bar which needs to be constantly
+ updated.
+*/
+class ExtensionCmdQueue {
+
+public:
+ /**
+ Create an instance.
+ */
+ ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const css::uno::Reference< css::uno::XComponentContext > & rContext);
+
+ ~ExtensionCmdQueue();
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ void enableExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates(const std::vector< css::uno::Reference<
+ css::deployment::XPackage > > &vList );
+ void acceptLicense( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ static void syncRepositories( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+ /**
+ This call does not block. It signals the internal thread
+ that it should install the remaining extensions and then terminate.
+ */
+ void stop();
+
+ bool isBusy();
+private:
+ ExtensionCmdQueue(ExtensionCmdQueue const &) = delete;
+ ExtensionCmdQueue& operator =(ExtensionCmdQueue const &) = delete;
+
+ class Thread;
+
+ rtl::Reference< Thread > m_thread;
+};
+
+void handleInteractionRequest( const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::task::XInteractionRequest > & xRequest );
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.cxx b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
new file mode 100644
index 000000000..89aaed148
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
@@ -0,0 +1,1144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui.h"
+#include "dp_gui_extlistbox.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_dependencies.hxx>
+#include <bitmaps.hlst>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <cppuhelper/weakref.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <algorithm>
+
+#define USER_PACKAGE_MANAGER "user"
+#define SHARED_PACKAGE_MANAGER "shared"
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+namespace {
+
+struct FindWeakRef
+{
+ const uno::Reference<deployment::XPackage> m_extension;
+
+ explicit FindWeakRef( uno::Reference<deployment::XPackage> const & ext): m_extension(ext) {}
+ bool operator () (uno::WeakReference< deployment::XPackage > const & ref);
+};
+
+bool FindWeakRef::operator () (uno::WeakReference< deployment::XPackage > const & ref)
+{
+ const uno::Reference<deployment::XPackage> ext(ref);
+ return ext == m_extension;
+}
+
+} // end namespace
+
+// struct Entry_Impl
+
+Entry_Impl::Entry_Impl( const uno::Reference< deployment::XPackage > &xPackage,
+ const PackageState eState, const bool bReadOnly ) :
+ m_bActive( false ),
+ m_bLocked( bReadOnly ),
+ m_bHasOptions( false ),
+ m_bUser( false ),
+ m_bShared( false ),
+ m_bNew( false ),
+ m_bChecked( false ),
+ m_bMissingDeps( false ),
+ m_bHasButtons( false ),
+ m_bMissingLic( false ),
+ m_eState( eState ),
+ m_xPackage( xPackage )
+{
+ try
+ {
+ m_sTitle = xPackage->getDisplayName();
+ m_sVersion = xPackage->getVersion();
+ m_sDescription = xPackage->getDescription();
+ m_sLicenseText = xPackage->getLicenseText();
+
+ beans::StringPair aInfo( m_xPackage->getPublisherInfo() );
+ m_sPublisher = aInfo.First;
+ m_sPublisherURL = aInfo.Second;
+
+ // get the icons for the package if there are any
+ uno::Reference< graphic::XGraphic > xGraphic = xPackage->getIcon( false );
+ if ( xGraphic.is() )
+ m_aIcon = Image( xGraphic );
+
+ if ( eState == AMBIGUOUS )
+ m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( eState == NOT_REGISTERED )
+ checkDependencies();
+ }
+ catch (const deployment::ExtensionRemovedException &) {}
+ catch (const uno::RuntimeException &) {}
+}
+
+
+Entry_Impl::~Entry_Impl()
+{}
+
+
+sal_Int32 Entry_Impl::CompareTo( const CollatorWrapper *pCollator, const TEntry_Impl& rEntry ) const
+{
+ sal_Int32 eCompare = pCollator->compareString( m_sTitle, rEntry->m_sTitle );
+ if ( eCompare == 0 )
+ {
+ eCompare = m_sVersion.compareTo( rEntry->m_sVersion );
+ if ( eCompare == 0 )
+ {
+ sal_Int32 nCompare = m_xPackage->getRepositoryName().compareTo( rEntry->m_xPackage->getRepositoryName() );
+ if ( nCompare < 0 )
+ eCompare = -1;
+ else if ( nCompare > 0 )
+ eCompare = 1;
+ }
+ }
+ return eCompare;
+}
+
+
+void Entry_Impl::checkDependencies()
+{
+ try {
+ m_xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException &e )
+ {
+ deployment::DependencyException depExc;
+ if ( e.Cause >>= depExc )
+ {
+ OUStringBuffer aMissingDep( DpResId( RID_STR_ERROR_MISSING_DEPENDENCIES ) );
+ for ( const auto& i : std::as_const(depExc.UnsatisfiedDependencies) )
+ {
+ aMissingDep.append("\n");
+ aMissingDep.append(dp_misc::Dependencies::getErrorText(i));
+ }
+ aMissingDep.append("\n");
+ m_sErrorText = aMissingDep.makeStringAndClear();
+ m_bMissingDeps = true;
+ }
+ }
+}
+
+// ExtensionRemovedListener
+
+void ExtensionRemovedListener::disposing( lang::EventObject const & rEvt )
+{
+ uno::Reference< deployment::XPackage > xPackage( rEvt.Source, uno::UNO_QUERY );
+
+ if ( xPackage.is() )
+ {
+ m_pParent->removeEntry( xPackage );
+ }
+}
+
+
+ExtensionRemovedListener::~ExtensionRemovedListener()
+{
+}
+
+
+// ExtensionBox_Impl
+ExtensionBox_Impl::ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : m_bHasScrollBar( false )
+ , m_bHasActive( false )
+ , m_bNeedsRecalc( true )
+ , m_bInCheckMode( false )
+ , m_bAdjustActive( false )
+ , m_bInDelete( false )
+ , m_nActive( 0 )
+ , m_nTopIndex( 0 )
+ , m_nStdHeight( 0 )
+ , m_nActiveHeight( 0 )
+ , m_aSharedImage(StockImage::Yes, RID_BMP_SHARED)
+ , m_aLockedImage(StockImage::Yes, RID_BMP_LOCKED)
+ , m_aWarningImage(StockImage::Yes, RID_BMP_WARNING)
+ , m_aDefaultImage(StockImage::Yes, RID_BMP_EXTENSION)
+ , m_pManager( nullptr )
+ , m_xScrollBar(std::move(xScroll))
+{
+}
+
+void ExtensionBox_Impl::Init()
+{
+ m_xScrollBar->set_user_managed_scrolling();
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ExtensionBox_Impl, ScrollHdl ) );
+
+ auto nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ auto nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ m_nStdHeight = nTitleHeight;
+ else
+ m_nStdHeight = nIconHeight;
+ m_nStdHeight += GetTextHeight() + TOP_OFFSET;
+
+ nIconHeight = ICON_HEIGHT + 2*TOP_OFFSET + 1;
+ if ( m_nStdHeight < nIconHeight )
+ m_nStdHeight = nIconHeight;
+
+ m_nActiveHeight = m_nStdHeight;
+
+ m_xRemoveListener = new ExtensionRemovedListener( this );
+
+ m_pLocale.reset( new lang::Locale( Application::GetSettings().GetLanguageTag().getLocale() ) );
+ m_pCollator.reset( new CollatorWrapper( ::comphelper::getProcessComponentContext() ) );
+ m_pCollator->loadDefaultCollator( *m_pLocale, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+}
+
+ExtensionBox_Impl::~ExtensionBox_Impl()
+{
+ if ( ! m_bInDelete )
+ DeleteRemoved();
+
+ m_bInDelete = true;
+
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_xPackage->removeEventListener( m_xRemoveListener.get() );
+ }
+
+ m_vEntries.clear();
+
+ m_xRemoveListener.clear();
+
+ m_pLocale.reset();
+ m_pCollator.reset();
+}
+
+sal_Int32 ExtensionBox_Impl::getItemCount() const
+{
+ return static_cast< sal_Int32 >( m_vEntries.size() );
+}
+
+
+sal_Int32 ExtensionBox_Impl::getSelIndex() const
+{
+ if ( m_bHasActive )
+ {
+ OSL_ASSERT( m_nActive >= -1);
+ return static_cast< sal_Int32 >( m_nActive );
+ }
+ else
+ return ENTRY_NOTFOUND;
+}
+
+
+// Title + description
+void ExtensionBox_Impl::CalcActiveHeight( const long nPos )
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ // get title height
+ long aTextHeight;
+ long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // calc description height
+ Size aSize = GetOutputSizePixel();
+
+ aSize.AdjustWidth( -(ICON_OFFSET) );
+ aSize.setHeight( 10000 );
+
+ OUString aText( m_vEntries[ nPos ]->m_sErrorText );
+ if ( !aText.isEmpty() )
+ aText += "\n";
+ aText += m_vEntries[ nPos ]->m_sDescription;
+
+ tools::Rectangle aRect = GetDrawingArea()->get_ref_device().GetTextRect(tools::Rectangle( Point(), aSize ), aText,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak);
+ aTextHeight += aRect.GetHeight();
+
+ if ( aTextHeight < m_nStdHeight )
+ aTextHeight = m_nStdHeight;
+
+ m_nActiveHeight = aTextHeight;
+
+ if ( m_vEntries[ nPos ]->m_bHasButtons )
+ m_nActiveHeight += 2;
+}
+
+tools::Rectangle ExtensionBox_Impl::GetEntryRect( const long nPos ) const
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ Size aSize( GetOutputSizePixel() );
+
+ if ( m_vEntries[ nPos ]->m_bActive )
+ aSize.setHeight( m_nActiveHeight );
+ else
+ aSize.setHeight( m_nStdHeight );
+
+ Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight );
+ if ( m_bHasActive && ( nPos < m_nActive ) )
+ aPos.AdjustY(m_nActiveHeight - m_nStdHeight );
+
+ return tools::Rectangle( aPos, aSize );
+}
+
+
+void ExtensionBox_Impl::DeleteRemoved()
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ m_bInDelete = true;
+
+ m_vRemovedEntries.clear();
+
+ m_bInDelete = false;
+}
+
+
+//This function may be called with nPos < 0
+void ExtensionBox_Impl::selectEntry( const long nPos )
+{
+ bool invalidate = false;
+ {
+ //ToDo we should not use the guard at such a big scope here.
+ //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
+ //modified in this function.
+ //It would be probably best to always use a copy of m_vEntries
+ //and some other state variables from ExtensionBox_Impl for
+ //the whole painting operation. See issue i86993
+ ::osl::MutexGuard guard(m_entriesMutex);
+
+ if ( m_bInCheckMode )
+ return;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos == m_nActive )
+ return;
+
+ m_bHasActive = false;
+ m_vEntries[ m_nActive ]->m_bActive = false;
+ }
+
+ if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
+ {
+ m_bHasActive = true;
+ m_nActive = nPos;
+ m_vEntries[ nPos ]->m_bActive = true;
+
+ if ( IsReallyVisible() )
+ {
+ m_bAdjustActive = true;
+ }
+ }
+
+ if ( IsReallyVisible() )
+ {
+ m_bNeedsRecalc = true;
+ invalidate = true;
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (rEntry->m_bActive)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else if ((rEntry->m_eState != REGISTERED) && (rEntry->m_eState != NOT_AVAILABLE))
+ rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
+
+ if (rEntry->m_bActive)
+ {
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
+ rRenderContext.DrawRect(rRect);
+ }
+ else
+ {
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.Erase(rRect);
+ }
+
+ // Draw extension icon
+ Point aPos( rRect.TopLeft() );
+ aPos += Point(TOP_OFFSET, TOP_OFFSET);
+ Image aImage;
+ if (!rEntry->m_aIcon)
+ aImage = m_aDefaultImage;
+ else
+ aImage = rEntry->m_aIcon;
+ Size aImageSize = aImage.GetSizePixel();
+ if ((aImageSize.Width() <= ICON_WIDTH ) && ( aImageSize.Height() <= ICON_HEIGHT ) )
+ rRenderContext.DrawImage(Point(aPos.X() + ((ICON_WIDTH - aImageSize.Width()) / 2),
+ aPos.Y() + ((ICON_HEIGHT - aImageSize.Height()) / 2)),
+ aImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(ICON_WIDTH, ICON_HEIGHT), aImage);
+
+ // Setup fonts
+ // expand the point size of the desired font to the equivalent pixel size
+ if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice()))
+ pDefaultDevice->SetPointFont(rRenderContext, GetDrawingArea()->get_font());
+ vcl::Font aStdFont(rRenderContext.GetFont());
+ vcl::Font aBoldFont(aStdFont);
+ aBoldFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aBoldFont);
+ auto aTextHeight = rRenderContext.GetTextHeight();
+
+ // Get max title width
+ auto nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
+ nMaxTitleWidth -= (2 * SMALL_ICON_SIZE) + (4 * SPACE_BETWEEN);
+ rRenderContext.SetFont(aStdFont);
+ long nLinkWidth = 0;
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ nLinkWidth = rRenderContext.GetTextWidth(rEntry->m_sPublisher);
+ nMaxTitleWidth -= nLinkWidth + (2 * SPACE_BETWEEN);
+ }
+ long aVersionWidth = rRenderContext.GetTextWidth(rEntry->m_sVersion);
+
+ aPos = rRect.TopLeft() + Point(ICON_OFFSET, TOP_OFFSET);
+
+ rRenderContext.SetFont(aBoldFont);
+ long aTitleWidth = rRenderContext.GetTextWidth(rEntry->m_sTitle) + (aTextHeight / 3);
+ if (aTitleWidth > nMaxTitleWidth - aVersionWidth)
+ {
+ aTitleWidth = nMaxTitleWidth - aVersionWidth - (aTextHeight / 3);
+ OUString aShortTitle = rRenderContext.GetEllipsisString(rEntry->m_sTitle, aTitleWidth);
+ rRenderContext.DrawText(aPos, aShortTitle);
+ aTitleWidth += (aTextHeight / 3);
+ }
+ else
+ rRenderContext.DrawText(aPos, rEntry->m_sTitle);
+
+ rRenderContext.SetFont(aStdFont);
+ rRenderContext.DrawText(Point(aPos.X() + aTitleWidth, aPos.Y()), rEntry->m_sVersion);
+
+ long nIconHeight = TOP_OFFSET + SMALL_ICON_SIZE;
+ long nTitleHeight = TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // draw description
+ OUString sDescription;
+ if (!rEntry->m_sErrorText.isEmpty())
+ {
+ if (rEntry->m_bActive)
+ sDescription = rEntry->m_sErrorText + "\n" + rEntry->m_sDescription;
+ else
+ sDescription = rEntry->m_sErrorText;
+ }
+ else
+ sDescription = rEntry->m_sDescription;
+
+ aPos.AdjustY(aTextHeight );
+ if (rEntry->m_bActive)
+ {
+ long nExtraHeight = 0;
+
+ if (rEntry->m_bHasButtons)
+ nExtraHeight = 2;
+
+ rRenderContext.DrawText(tools::Rectangle(aPos.X(), aPos.Y(), rRect.Right(), rRect.Bottom() - nExtraHeight),
+ sDescription, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+ }
+ else
+ {
+ //replace LF to space, so words do not stick together in one line view
+ sDescription = sDescription.replace(0x000A, ' ');
+ const long nWidth = rRenderContext.GetTextWidth( sDescription );
+ if (nWidth > rRect.GetWidth() - aPos.X())
+ sDescription = rRenderContext.GetEllipsisString(sDescription, rRect.GetWidth() - aPos.X());
+ rRenderContext.DrawText(aPos, sDescription);
+ }
+
+ // Draw publisher link
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ aPos = rRect.TopLeft() + Point( ICON_OFFSET + nMaxTitleWidth + (2*SPACE_BETWEEN), TOP_OFFSET );
+
+ rRenderContext.Push(PushFlags::FONT | PushFlags::TEXTCOLOR | PushFlags::TEXTFILLCOLOR);
+ rRenderContext.SetTextColor(rStyleSettings.GetLinkColor());
+ rRenderContext.SetTextFillColor(rStyleSettings.GetFieldColor());
+ vcl::Font aFont = rRenderContext.GetFont();
+ // to underline
+ aFont.SetUnderline(LINESTYLE_SINGLE);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.DrawText(aPos, rEntry->m_sPublisher);
+ rEntry->m_aLinkRect = tools::Rectangle(aPos, Size(nLinkWidth, aTextHeight));
+ rRenderContext.Pop();
+ }
+
+ // Draw status icons
+ if (!rEntry->m_bUser)
+ {
+ aPos = rRect.TopRight() + Point( -(RIGHT_ICON_OFFSET + SMALL_ICON_SIZE), TOP_OFFSET );
+ if (rEntry->m_bLocked)
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aLockedImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aSharedImage);
+ }
+ if ((rEntry->m_eState == AMBIGUOUS ) || rEntry->m_bMissingDeps || rEntry->m_bMissingLic)
+ {
+ aPos = rRect.TopRight() + Point(-(RIGHT_ICON_OFFSET + SPACE_BETWEEN + 2 * SMALL_ICON_SIZE), TOP_OFFSET);
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aWarningImage);
+ }
+
+ rRenderContext.SetLineColor(COL_LIGHTGRAY);
+ rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
+}
+
+
+void ExtensionBox_Impl::RecalcAll()
+{
+ if ( m_bHasActive )
+ CalcActiveHeight( m_nActive );
+
+ SetupScrollBar();
+
+ if ( m_bHasActive )
+ {
+ tools::Rectangle aEntryRect = GetEntryRect( m_nActive );
+
+ if ( m_bAdjustActive )
+ {
+ m_bAdjustActive = false;
+
+ // If the top of the selected entry isn't visible, make it visible
+ if ( aEntryRect.Top() < 0 )
+ {
+ m_nTopIndex += aEntryRect.Top();
+ aEntryRect.Move( 0, -aEntryRect.Top() );
+ }
+
+ // If the bottom of the selected entry isn't visible, make it visible even if now the top
+ // isn't visible any longer ( the buttons are more important )
+ Size aOutputSize = GetOutputSizePixel();
+ if ( aEntryRect.Bottom() > aOutputSize.Height() )
+ {
+ m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() );
+ aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) );
+ }
+
+ // If there is unused space below the last entry but all entries don't fit into the box,
+ // move the content down to use the whole space
+ const long nTotalHeight = GetTotalHeight();
+ if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) )
+ {
+ long nOffset = m_nTopIndex;
+ m_nTopIndex = nTotalHeight - aOutputSize.Height();
+ nOffset -= m_nTopIndex;
+ aEntryRect.Move( 0, nOffset );
+ }
+
+ if ( m_bHasScrollBar )
+ m_xScrollBar->vadjustment_set_value( m_nTopIndex );
+ }
+ }
+
+ m_bNeedsRecalc = false;
+}
+
+
+bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode )
+{
+ if ( m_vEntries.empty() )
+ return true;
+
+ long nSelect = 0;
+
+ if ( m_bHasActive )
+ {
+ long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight;
+ if ( nPageSize < 2 )
+ nPageSize = 2;
+
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) )
+ nSelect = m_nActive + 1;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) )
+ nSelect = m_nActive - 1;
+ else if ( nKeyCode == KEY_HOME )
+ nSelect = 0;
+ else if ( nKeyCode == KEY_END )
+ nSelect = m_vEntries.size() - 1;
+ else if ( nKeyCode == KEY_PAGEUP )
+ nSelect = m_nActive - nPageSize + 1;
+ else if ( nKeyCode == KEY_PAGEDOWN )
+ nSelect = m_nActive + nPageSize - 1;
+ }
+ else // when there is no selected entry, we will select the first or the last.
+ {
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) )
+ nSelect = 0;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
+ nSelect = m_vEntries.size() - 1;
+ }
+
+ if ( nSelect < 0 )
+ nSelect = 0;
+ if ( nSelect >= static_cast<long>(m_vEntries.size()) )
+ nSelect = m_vEntries.size() - 1;
+
+ selectEntry( nSelect );
+
+ return true;
+}
+
+
+void ExtensionBox_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rPaintRect*/)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ if ( m_bNeedsRecalc )
+ RecalcAll();
+
+ Point aStart( 0, -m_nTopIndex );
+ Size aSize(GetOutputSizePixel());
+
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ aSize.setHeight( entry->m_bActive ? m_nActiveHeight : m_nStdHeight );
+ tools::Rectangle aEntryRect( aStart, aSize );
+ DrawRow(rRenderContext, aEntryRect, entry);
+ aStart.AdjustY(aSize.Height() );
+ }
+}
+
+
+long ExtensionBox_Impl::GetTotalHeight() const
+{
+ long nHeight = m_vEntries.size() * m_nStdHeight;
+
+ if ( m_bHasActive )
+ {
+ nHeight += m_nActiveHeight - m_nStdHeight;
+ }
+
+ return nHeight;
+}
+
+
+void ExtensionBox_Impl::SetupScrollBar()
+{
+ const Size aSize = GetOutputSizePixel();
+ const auto nTotalHeight = GetTotalHeight();
+ const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() );
+
+ if ( bNeedsScrollBar )
+ {
+ if ( m_nTopIndex + aSize.Height() > nTotalHeight )
+ m_nTopIndex = nTotalHeight - aSize.Height();
+
+ m_xScrollBar->vadjustment_configure(m_nTopIndex, 0, nTotalHeight,
+ m_nStdHeight, ( aSize.Height() * 4 ) / 5,
+ aSize.Height());
+
+ if (!m_bHasScrollBar)
+ m_xScrollBar->set_vpolicy(VclPolicyType::ALWAYS);
+ }
+ else if ( m_bHasScrollBar )
+ {
+ m_xScrollBar->set_vpolicy(VclPolicyType::NEVER);
+ m_nTopIndex = 0;
+ }
+
+ m_bHasScrollBar = bNeedsScrollBar;
+}
+
+
+void ExtensionBox_Impl::Resize()
+{
+ RecalcAll();
+ Invalidate();
+}
+
+void ExtensionBox_Impl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(250, 150), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+
+ Init();
+}
+
+long ExtensionBox_Impl::PointToPos( const Point& rPos )
+{
+ long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight;
+
+ if ( m_bHasActive && ( nPos > m_nActive ) )
+ {
+ if ( rPos.Y() + m_nTopIndex <= m_nActive*m_nStdHeight + m_nActiveHeight )
+ nPos = m_nActive;
+ else
+ nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight;
+ }
+
+ return nPos;
+}
+
+bool ExtensionBox_Impl::MouseMove( const MouseEvent& rMEvt )
+{
+ bool bOverHyperlink = false;
+
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+ if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.IsInside(rMEvt.GetPosPixel());
+ }
+
+ if (bOverHyperlink)
+ SetPointer(PointerStyle::RefHand);
+ else
+ SetPointer(PointerStyle::Arrow);
+
+ return false;
+}
+
+OUString ExtensionBox_Impl::RequestHelp(tools::Rectangle& rRect)
+{
+ auto nPos = PointToPos( rRect.TopLeft() );
+ if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bool bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.IsInside(rRect);
+ if (bOverHyperlink)
+ {
+ rRect = rEntry->m_aLinkRect;
+ return rEntry->m_sPublisherURL;
+ }
+ }
+
+ return OUString();
+}
+
+bool ExtensionBox_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( rMEvt.IsLeft() )
+ {
+ if (rMEvt.IsMod1() && m_bHasActive)
+ selectEntry(ExtensionBox_Impl::ENTRY_NOTFOUND); // Selecting a not existing entry will deselect the current one
+ else
+ {
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+
+ if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ if (!rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.IsInside(rMEvt.GetPosPixel()))
+ {
+ try
+ {
+ css::uno::Reference<css::system::XSystemShellExecute> xSystemShellExecute(
+ css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
+ //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
+ xSystemShellExecute->execute(rEntry->m_sPublisherURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ catch (...)
+ {
+ }
+ return true;
+ }
+ }
+
+ selectEntry( nPos );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool ExtensionBox_Impl::KeyInput(const KeyEvent& rKEvt)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ bool bHandled = false;
+ if (nKeyCode != KEY_TAB && aKeyCode.GetGroup() == KEYGROUP_CURSOR)
+ bHandled = HandleCursorKey(nKeyCode);
+
+ return bHandled;
+}
+
+bool ExtensionBox_Impl::FindEntryPos( const TEntry_Impl& rEntry, const long nStart,
+ const long nEnd, long &nPos )
+{
+ nPos = nStart;
+ if ( nStart > nEnd )
+ return false;
+
+ sal_Int32 eCompare;
+
+ if ( nStart == nEnd )
+ {
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nStart ] );
+ if ( eCompare < 0 )
+ return false;
+ else if ( eCompare == 0 )
+ {
+ //Workaround. See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nStart]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nStart ]->m_bChecked = true;
+ return true;
+ }
+ else
+ {
+ nPos = nStart + 1;
+ return false;
+ }
+ }
+
+ const long nMid = nStart + ( ( nEnd - nStart ) / 2 );
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nMid ] );
+
+ if ( eCompare < 0 )
+ return FindEntryPos( rEntry, nStart, nMid-1, nPos );
+ else if ( eCompare > 0 )
+ return FindEntryPos( rEntry, nMid+1, nEnd, nPos );
+ else
+ {
+ //Workaround.See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nMid]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nMid ]->m_bChecked = true;
+ nPos = nMid;
+ return true;
+ }
+}
+
+void ExtensionBox_Impl::cleanVecListenerAdded()
+{
+ m_vListenerAdded.erase(std::remove_if(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ [](const uno::WeakReference<deployment::XPackage>& rxListener) {
+ const uno::Reference<deployment::XPackage> hardRef(rxListener);
+ return !hardRef.is();
+ }),
+ m_vListenerAdded.end());
+}
+
+void ExtensionBox_Impl::addEventListenerOnce(
+ uno::Reference<deployment::XPackage > const & extension)
+{
+ //make sure to only add the listener once
+ cleanVecListenerAdded();
+ if ( std::none_of(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ FindWeakRef(extension)) )
+ {
+ extension->addEventListener( m_xRemoveListener.get() );
+ m_vListenerAdded.emplace_back(extension);
+ }
+}
+
+
+void ExtensionBox_Impl::addEntry( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ long nPos = 0;
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ bool bLocked = m_pManager->isReadOnly( xPackage );
+
+ TEntry_Impl pEntry = std::make_shared<Entry_Impl>( xPackage, eState, bLocked );
+
+ // Don't add empty entries
+ if ( pEntry->m_sTitle.isEmpty() )
+ return;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ if (m_vEntries.empty())
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.push_back(pEntry);
+ }
+ else
+ {
+ if (!FindEntryPos(pEntry, 0, m_vEntries.size() - 1, nPos))
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.insert(m_vEntries.begin() + nPos, pEntry);
+ }
+ else if (!m_bInCheckMode)
+ {
+ OSL_FAIL("ExtensionBox_Impl::addEntry(): Will not add duplicate entries");
+ }
+ }
+
+ pEntry->m_bHasOptions = m_pManager->supportsOptions(xPackage);
+ pEntry->m_bUser = (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER);
+ pEntry->m_bShared = (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER);
+ pEntry->m_bNew = m_bInCheckMode;
+ pEntry->m_bMissingLic = bLicenseMissing;
+
+ if (bLicenseMissing)
+ pEntry->m_sErrorText = DpResId(RID_STR_ERROR_MISSING_LICENSE);
+
+ //access to m_nActive must be guarded
+ if (!m_bInCheckMode && m_bHasActive && (m_nActive >= nPos))
+ m_nActive += 1;
+ }
+
+ if ( IsReallyVisible() )
+ Invalidate();
+
+ m_bNeedsRecalc = true;
+}
+
+void ExtensionBox_Impl::updateEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ for (auto const& entry : m_vEntries)
+ {
+ if ( entry->m_xPackage == xPackage )
+ {
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ entry->m_bHasOptions = m_pManager->supportsOptions( xPackage );
+ entry->m_eState = eState;
+ entry->m_sTitle = xPackage->getDisplayName();
+ entry->m_sVersion = xPackage->getVersion();
+ entry->m_sDescription = xPackage->getDescription();
+
+ if ( eState == REGISTERED )
+ entry->m_bMissingLic = false;
+
+ if ( eState == AMBIGUOUS )
+ entry->m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( ! entry->m_bMissingLic )
+ entry->m_sErrorText.clear();
+
+ if ( IsReallyVisible() )
+ Invalidate();
+ break;
+ }
+ }
+}
+
+//This function is also called as a result of removing an extension.
+//see PackageManagerImpl::removePackage
+//The gui is a registered as listener on the package. Removing it will cause the
+//listeners to be notified and then this function is called. At this moment xPackage
+//is in the disposing state and all calls on it may result in a DisposedException.
+void ExtensionBox_Impl::removeEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( m_bInDelete )
+ return;
+
+ bool invalidate = false;
+ {
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ auto iIndex = std::find_if(m_vEntries.begin(), m_vEntries.end(),
+ [&xPackage](const TEntry_Impl& rxEntry) { return rxEntry->m_xPackage == xPackage; });
+ if (iIndex != m_vEntries.end())
+ {
+ long nPos = iIndex - m_vEntries.begin();
+
+ // Entries mustn't be removed here, because they contain a hyperlink control
+ // which can only be deleted when the thread has the solar mutex. Therefore
+ // the entry will be moved into the m_vRemovedEntries list which will be
+ // cleared on the next paint event
+ m_vRemovedEntries.push_back( *iIndex );
+ (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener.get());
+ m_vEntries.erase( iIndex );
+
+ m_bNeedsRecalc = true;
+
+ if ( IsReallyVisible() )
+ invalidate = true;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos < m_nActive )
+ m_nActive -= 1;
+ else if ( ( nPos == m_nActive ) &&
+ ( nPos == static_cast<long>(m_vEntries.size()) ) )
+ m_nActive -= 1;
+
+ m_bHasActive = false;
+ //clear before calling out of this method
+ aGuard.clear();
+ selectEntry( m_nActive );
+ }
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::RemoveUnlocked()
+{
+ bool bAllRemoved = false;
+
+ while ( ! bAllRemoved )
+ {
+ bAllRemoved = true;
+
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ if ( !entry->m_bLocked )
+ {
+ bAllRemoved = false;
+ uno::Reference< deployment::XPackage> xPackage = entry->m_xPackage;
+ aGuard.clear();
+ removeEntry( xPackage );
+ break;
+ }
+ }
+ }
+}
+
+
+void ExtensionBox_Impl::prepareChecking()
+{
+ m_bInCheckMode = true;
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_bChecked = false;
+ entry->m_bNew = false;
+ }
+}
+
+
+void ExtensionBox_Impl::checkEntries()
+{
+ long nNewPos = -1;
+ long nChangedActivePos = -1;
+ long nPos = 0;
+ bool bNeedsUpdate = false;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ auto iIndex = m_vEntries.begin();
+ while (iIndex != m_vEntries.end())
+ {
+ if (!(*iIndex)->m_bChecked)
+ {
+ (*iIndex)->m_bChecked = true;
+ bNeedsUpdate = true;
+ nPos = iIndex - m_vEntries.begin();
+ if ((*iIndex)->m_bNew)
+ { // add entry to list and correct active pos
+ if (nNewPos == -1)
+ nNewPos = nPos;
+ if (nPos <= m_nActive)
+ m_nActive += 1;
+ ++iIndex;
+ }
+ else
+ { // remove entry from list
+ if (nPos < nNewPos)
+ {
+ --nNewPos;
+ }
+ if (nPos < nChangedActivePos)
+ {
+ --nChangedActivePos;
+ }
+ if (nPos < m_nActive)
+ m_nActive -= 1;
+ else if (nPos == m_nActive)
+ {
+ nChangedActivePos = nPos;
+ m_nActive = -1;
+ m_bHasActive = false;
+ }
+ m_vRemovedEntries.push_back(*iIndex);
+ iIndex = m_vEntries.erase(iIndex);
+ }
+ }
+ else
+ ++iIndex;
+ }
+ }
+
+ m_bInCheckMode = false;
+
+ if ( nNewPos != - 1)
+ selectEntry( nNewPos );
+ else if (nChangedActivePos != -1) {
+ selectEntry(nChangedActivePos);
+ }
+
+ if ( bNeedsUpdate )
+ {
+ m_bNeedsRecalc = true;
+ if ( IsReallyVisible() )
+ Invalidate();
+ }
+}
+
+IMPL_LINK(ExtensionBox_Impl, ScrollHdl, weld::ScrolledWindow&, rScrBar, void)
+{
+ m_nTopIndex = rScrBar.vadjustment_get_value();
+ Invalidate();
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.hxx b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
new file mode 100644
index 000000000..9295cb09a
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_EXTLISTBOX_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_EXTLISTBOX_HXX
+
+#include <rtl/ustring.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/image.hxx>
+#include <vcl/weld.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <memory>
+
+#include "dp_gui.h"
+
+namespace dp_gui {
+
+#define SMALL_ICON_SIZE 16
+#define TOP_OFFSET 5
+#define ICON_HEIGHT 42
+#define ICON_WIDTH 47
+#define ICON_OFFSET 72
+#define RIGHT_ICON_OFFSET 5
+#define SPACE_BETWEEN 3
+
+class TheExtensionManager;
+
+
+struct Entry_Impl;
+
+typedef std::shared_ptr< Entry_Impl > TEntry_Impl;
+
+struct Entry_Impl
+{
+ bool m_bActive :1;
+ bool m_bLocked :1;
+ bool m_bHasOptions :1;
+ bool m_bUser :1;
+ bool m_bShared :1;
+ bool m_bNew :1;
+ bool m_bChecked :1;
+ bool m_bMissingDeps :1;
+ bool m_bHasButtons :1;
+ bool m_bMissingLic :1;
+ PackageState m_eState;
+ OUString m_sTitle;
+ OUString m_sVersion;
+ OUString m_sDescription;
+ OUString m_sPublisher;
+ OUString m_sPublisherURL;
+ OUString m_sErrorText;
+ OUString m_sLicenseText;
+ Image m_aIcon;
+ tools::Rectangle m_aLinkRect;
+
+ css::uno::Reference<css::deployment::XPackage> m_xPackage;
+
+ Entry_Impl(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ const PackageState eState, const bool bReadOnly);
+ ~Entry_Impl();
+
+ sal_Int32 CompareTo(const CollatorWrapper *pCollator, const TEntry_Impl& rEntry) const;
+ void checkDependencies();
+};
+
+class ExtensionBox_Impl;
+
+
+class ExtensionRemovedListener : public ::cppu::WeakImplHelper<css::lang::XEventListener>
+{
+ ExtensionBox_Impl* m_pParent;
+
+public:
+
+ explicit ExtensionRemovedListener( ExtensionBox_Impl *pParent ) { m_pParent = pParent; }
+ virtual ~ExtensionRemovedListener() override;
+
+
+ // XEventListener
+ virtual void SAL_CALL disposing(css::lang::EventObject const& evt) override;
+};
+
+class ExtensionBox_Impl : public weld::CustomWidgetController
+{
+ bool m_bHasScrollBar : 1;
+ bool m_bHasActive : 1;
+ bool m_bNeedsRecalc : 1;
+ bool m_bInCheckMode : 1;
+ bool m_bAdjustActive : 1;
+ bool m_bInDelete : 1;
+ //Must be guarded together with m_vEntries to ensure a valid index at all times.
+ //Use m_entriesMutex as guard.
+ long m_nActive;
+ long m_nTopIndex;
+ long m_nStdHeight;
+ long m_nActiveHeight;
+ Image m_aSharedImage;
+ Image m_aLockedImage;
+ Image m_aWarningImage;
+ Image m_aDefaultImage;
+
+ rtl::Reference<ExtensionRemovedListener> m_xRemoveListener;
+
+ TheExtensionManager *m_pManager;
+ //This mutex is used for synchronizing access to m_vEntries.
+ //Currently it is used to synchronize adding, removing entries and
+ //functions like getItemName, getItemDescription, etc. to prevent
+ //that m_vEntries is accessed at an invalid index.
+ //ToDo: There are many more places where m_vEntries is read and which may
+ //fail. For example the Paint method is probable called from the main thread
+ //while new entries are added / removed in a separate thread.
+ mutable ::osl::Mutex m_entriesMutex;
+ std::vector< TEntry_Impl > m_vEntries;
+ std::vector< TEntry_Impl > m_vRemovedEntries;
+
+ std::unique_ptr<css::lang::Locale> m_pLocale;
+ std::unique_ptr<CollatorWrapper> m_pCollator;
+
+ //Holds weak references to extensions to which is we have added an XEventListener
+ std::vector< css::uno::WeakReference<
+ css::deployment::XPackage> > m_vListenerAdded;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+
+ //Removes the dead weak references from m_vListenerAdded
+ void cleanVecListenerAdded();
+ void addEventListenerOnce(css::uno::Reference<css::deployment::XPackage> const & extension);
+
+ void CalcActiveHeight( const long nPos );
+ long GetTotalHeight() const;
+ void SetupScrollBar();
+ void DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry);
+ bool HandleCursorKey( sal_uInt16 nKeyCode );
+ bool FindEntryPos( const TEntry_Impl& rEntry, long nStart, long nEnd, long &nFound );
+ void DeleteRemoved();
+
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void );
+
+ void Init();
+public:
+ explicit ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+ virtual ~ExtensionBox_Impl() override;
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect ) override;
+ virtual void Resize() override;
+ virtual OUString RequestHelp(tools::Rectangle& rRect) override;
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ TEntry_Impl const & GetEntryData( long nPos ) { return m_vEntries[ nPos ]; }
+ long GetEntryCount() const { return static_cast<long>(m_vEntries.size()); }
+ tools::Rectangle GetEntryRect( const long nPos ) const;
+ bool HasActive() const { return m_bHasActive; }
+ long PointToPos( const Point& rPos );
+ virtual void RecalcAll();
+ void RemoveUnlocked();
+
+
+ virtual void selectEntry( const long nPos );
+ void addEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ bool bLicenseMissing = false );
+ void updateEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+ void removeEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+
+ void prepareChecking();
+ void checkEntries();
+
+ void setExtensionManager(TheExtensionManager* pManager) { m_pManager = pManager; }
+
+ //These functions are used for automatic testing
+public:
+ enum { ENTRY_NOTFOUND = -1 };
+
+ /** @return The count of the entries in the list box. */
+ sal_Int32 getItemCount() const;
+
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ sal_Int32 getSelIndex() const;
+};
+
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_EXTLISTBOX_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_service.cxx b/desktop/source/deployment/gui/dp_gui_service.cxx
new file mode 100644
index 000000000..8acfdc5a3
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_service.cxx
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include "dp_gui_theextmgr.hxx"
+#include <osl/diagnose.h>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <comphelper/unwrapargs.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+
+#include <optional>
+#include "license_dialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <dp_misc.h>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdecl = comphelper::service_decl;
+
+namespace dp_gui {
+
+namespace {
+
+class MyApp : public Application
+{
+public:
+ MyApp();
+
+ MyApp(const MyApp&) = delete;
+ const MyApp& operator=(const MyApp&) = delete;
+
+ // Application
+ virtual int Main() override;
+ virtual void DeInit() override;
+};
+
+}
+
+MyApp::MyApp()
+{
+}
+
+
+int MyApp::Main()
+{
+ return EXIT_SUCCESS;
+}
+
+void MyApp::DeInit()
+{
+ css::uno::Reference< css::uno::XComponentContext > context(
+ comphelper::getProcessComponentContext());
+ dp_misc::disposeBridges(context);
+ css::uno::Reference< css::lang::XComponent >(
+ context, css::uno::UNO_QUERY_THROW)->dispose();
+ comphelper::setProcessServiceFactory(nullptr);
+}
+
+static OUString ReplaceProductNameHookProc( const OUString& rStr )
+{
+ if (rStr.indexOf( "%PRODUCT" ) == -1)
+ return rStr;
+
+ static const OUString sProductName = utl::ConfigManager::getProductName();
+ static const OUString sVersion = utl::ConfigManager::getProductVersion();
+ static const OUString sAboutBoxVersion = utl::ConfigManager::getAboutBoxProductVersion();
+ static const OUString sAboutBoxVersionSuffix = utl::ConfigManager::getAboutBoxProductVersionSuffix();
+ static const OUString sExtension = utl::ConfigManager::getProductExtension();
+ static const OUString sOOOVendor = utl::ConfigManager::getVendor();
+
+ OUString sRet = rStr.replaceAll( "%PRODUCTNAME", sProductName );
+ sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
+ sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
+ sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
+ return sRet;
+}
+
+namespace {
+
+class ServiceImpl
+ : public ::cppu::WeakImplHelper<ui::dialogs::XAsynchronousExecutableDialog,
+ task::XJobExecutor>
+{
+ Reference<XComponentContext> const m_xComponentContext;
+ std::optional< Reference<awt::XWindow> > /* const */ m_parent;
+ std::optional<OUString> m_extensionURL;
+ OUString m_initialTitle;
+ bool m_bShowUpdateOnly;
+
+public:
+ ServiceImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XAsynchronousExecutableDialog
+ virtual void SAL_CALL setDialogTitle( OUString const & aTitle ) override;
+ virtual void SAL_CALL startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener ) override;
+
+ // XJobExecutor
+ virtual void SAL_CALL trigger( OUString const & event ) override;
+};
+
+}
+
+ServiceImpl::ServiceImpl( Sequence<Any> const& args,
+ Reference<XComponentContext> const& xComponentContext)
+ : m_xComponentContext(xComponentContext),
+ m_bShowUpdateOnly( false )
+{
+ /* if true then this service is running in a unopkg process and not in an office process */
+ std::optional<sal_Bool> unopkg;
+ std::optional<OUString> view;
+ try {
+ comphelper::unwrapArgs( args, m_parent, view, unopkg );
+ return;
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+ try {
+ comphelper::unwrapArgs( args, m_extensionURL);
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+
+ ResHookProc pProc = Translate::GetReadStringHook();
+ if ( !pProc )
+ Translate::SetReadStringHook(ReplaceProductNameHookProc);
+}
+
+// XAsynchronousExecutableDialog
+
+void ServiceImpl::setDialogTitle( OUString const & title )
+{
+ if ( dp_gui::TheExtensionManager::s_ExtMgr.is() )
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > dialog(
+ ::dp_gui::TheExtensionManager::get( m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ dialog->SetText( title );
+ }
+ else
+ m_initialTitle = title;
+}
+
+
+void ServiceImpl::startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener )
+{
+ bool bCloseDialog = true; // only used if m_bShowUpdateOnly is true
+ std::unique_ptr<Application> app;
+ //ToDo: synchronize access to s_dialog !!!
+ if (! dp_gui::TheExtensionManager::s_ExtMgr.is())
+ {
+ const bool bAppUp = (GetpApp() != nullptr);
+ bool bOfficePipePresent;
+ try {
+ bOfficePipePresent = dp_misc::office_is_running();
+ }
+ catch (const Exception & exc) {
+ if (bAppUp) {
+ const SolarMutexGuard guard;
+ vcl::Window* pWin = Application::GetActiveTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, exc.Message));
+ xBox->run();
+ }
+ throw;
+ }
+
+ if (! bOfficePipePresent) {
+ OSL_ASSERT( ! bAppUp );
+ app.reset( new MyApp );
+ if (! InitVCL() )
+ throw RuntimeException( "Cannot initialize VCL!",
+ static_cast<OWeakObject *>(this) );
+ Application::SetDisplayName(
+ utl::ConfigManager::getProductName() +
+ " " +
+ utl::ConfigManager::getProductVersion());
+ ExtensionCmdQueue::syncRepositories( m_xComponentContext );
+ }
+ }
+ else
+ {
+ // When m_bShowUpdateOnly is set, we are inside the office and the user clicked
+ // the update notification icon in the menu bar. We must not close the extensions
+ // dialog after displaying the update dialog when it has been visible before
+ if ( m_bShowUpdateOnly )
+ bCloseDialog = ! dp_gui::TheExtensionManager::s_ExtMgr->isVisible();
+ }
+
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > myExtMgr(
+ ::dp_gui::TheExtensionManager::get(
+ m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ myExtMgr->createDialog( false );
+ if (!m_initialTitle.isEmpty()) {
+ myExtMgr->SetText( m_initialTitle );
+ m_initialTitle.clear();
+ }
+ if ( m_bShowUpdateOnly )
+ {
+ myExtMgr->checkUpdates();
+ if ( bCloseDialog )
+ myExtMgr->Close();
+ else
+ myExtMgr->ToTop();
+ }
+ else
+ {
+ myExtMgr->Show();
+ myExtMgr->ToTop();
+ }
+ }
+
+ if (app != nullptr)
+ {
+ Application::Execute();
+ DeInitVCL();
+ }
+
+ if (xListener.is())
+ xListener->dialogClosed(
+ ui::dialogs::DialogClosedEvent(
+ static_cast< ::cppu::OWeakObject * >(this),
+ sal_Int16(0)) );
+}
+
+// XJobExecutor
+
+void ServiceImpl::trigger( OUString const &rEvent )
+{
+ if ( rEvent == "SHOW_UPDATE_DIALOG" )
+ m_bShowUpdateOnly = true;
+ else
+ m_bShowUpdateOnly = false;
+
+ startExecuteModal( Reference< ui::dialogs::XDialogClosedListener >() );
+}
+
+sdecl::class_<ServiceImpl, sdecl::with_args<true> > const serviceSI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceSI,
+ "com.sun.star.comp.deployment.ui.PackageManagerDialog",
+ "com.sun.star.deployment.ui.PackageManagerDialog" );
+
+sdecl::class_<LicenseDialog, sdecl::with_args<true> > const licenseSI;
+sdecl::ServiceDecl const licenseDecl(
+ licenseSI,
+ "com.sun.star.comp.deployment.ui.LicenseDialog",
+ "com.sun.star.deployment.ui.LicenseDialog" );
+
+sdecl::class_<UpdateRequiredDialogService, sdecl::with_args<true> > const updateSI;
+sdecl::ServiceDecl const updateDecl(
+ updateSI,
+ "com.sun.star.comp.deployment.ui.UpdateRequiredDialog",
+ "com.sun.star.deployment.ui.UpdateRequiredDialog" );
+} // namespace dp_gui
+
+extern "C" {
+
+SAL_DLLPUBLIC_EXPORT void * deploymentgui_component_getFactory(
+ char const * pImplName, void *, void *)
+{
+ return sdecl::component_getFactoryHelper(
+ pImplName,
+ {&dp_gui::serviceDecl, &dp_gui::licenseDecl, &dp_gui::updateDecl});
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.cxx b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
new file mode 100644
index 000000000..1f3d8d710
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
@@ -0,0 +1,533 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+#define USER_PACKAGE_MANAGER "user"
+#define SHARED_PACKAGE_MANAGER "shared"
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::s_ExtMgr;
+
+
+// TheExtensionManager
+
+
+TheExtensionManager::TheExtensionManager( const uno::Reference< awt::XWindow > &xParent,
+ const uno::Reference< uno::XComponentContext > &xContext ) :
+ m_xContext( xContext ),
+ m_xParent( xParent ),
+ m_bModified(false),
+ m_bExtMgrDialogExecuting(false)
+{
+ m_xExtensionManager = deployment::ExtensionManager::get( xContext );
+ m_xExtensionManager->addModifyListener( this );
+
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(xContext));
+ uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.OptionsDialog/Nodes"))}
+ }));
+ m_xNameAccessNodes.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args),
+ uno::UNO_QUERY_THROW);
+
+ // get the 'get more extensions here' url
+ uno::Sequence<uno::Any> args2(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.ExtensionManager/ExtensionRepositories"))}
+ }));
+ uno::Reference< container::XNameAccess > xNameAccessRepositories;
+ xNameAccessRepositories.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args2),
+ uno::UNO_QUERY_THROW);
+ try
+ { //throws css::container::NoSuchElementException, css::lang::WrappedTargetException
+ uno::Any value = xNameAccessRepositories->getByName("WebsiteLink");
+ m_sGetExtensionsURL = value.get< OUString > ();
+ }
+ catch ( const uno::Exception& )
+ {}
+
+ if ( dp_misc::office_is_running() )
+ {
+ // the registration should be done after the construction has been ended
+ // otherwise an exception prevents object creation, but it is registered as a listener
+ m_xDesktop.set( frame::Desktop::create(xContext), uno::UNO_SET_THROW );
+ m_xDesktop->addTerminateListener( this );
+ }
+}
+
+TheExtensionManager::~TheExtensionManager()
+{
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+}
+
+void TheExtensionManager::createDialog( const bool bCreateUpdDlg )
+{
+ const SolarMutexGuard guard;
+
+ if ( bCreateUpdDlg )
+ {
+ if ( !m_xUpdReqDialog )
+ {
+ m_xUpdReqDialog.reset(new UpdateRequiredDialog(Application::GetFrameWeld(m_xParent), this));
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xUpdReqDialog.get(), this, m_xContext ) );
+ createPackageList();
+ }
+ }
+ else if ( !m_xExtMgrDialog )
+ {
+ m_xExtMgrDialog = std::make_shared<ExtMgrDialog>(Application::GetFrameWeld(m_xParent), this);
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xExtMgrDialog.get(), this, m_xContext ) );
+ m_xExtMgrDialog->setGetExtensionsURL( m_sGetExtensionsURL );
+ createPackageList();
+ }
+}
+
+void TheExtensionManager::Show()
+{
+ const SolarMutexGuard guard;
+
+ m_bExtMgrDialogExecuting = true;
+
+ weld::DialogController::runAsync(m_xExtMgrDialog, [this](sal_Int32 /*nResult*/) {
+ m_bExtMgrDialogExecuting = false;
+ auto xExtMgrDialog = m_xExtMgrDialog;
+ m_xExtMgrDialog.reset();
+ xExtMgrDialog->Close();
+ });
+}
+
+void TheExtensionManager::SetText( const OUString &rTitle )
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->set_title( rTitle );
+}
+
+
+void TheExtensionManager::ToTop()
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->present();
+}
+
+void TheExtensionManager::Close()
+{
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ m_xExtMgrDialog->Close();
+ }
+ else if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+}
+
+sal_Int16 TheExtensionManager::execute()
+{
+ sal_Int16 nRet = 0;
+
+ if ( m_xUpdReqDialog )
+ {
+ nRet = m_xUpdReqDialog->run();
+ m_xUpdReqDialog.reset();
+ }
+
+ return nRet;
+}
+
+bool TheExtensionManager::isVisible()
+{
+ weld::Window* pDialog = getDialog();
+ return pDialog && pDialog->get_visible();
+}
+
+void TheExtensionManager::checkUpdates()
+{
+ std::vector< uno::Reference< deployment::XPackage > > vEntries;
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( auto const & i : std::as_const(xAllPackages) )
+ {
+ uno::Reference< deployment::XPackage > xPackage = dp_misc::getExtensionWithHighestVersion(i);
+ OSL_ASSERT(xPackage.is());
+ if ( xPackage.is() )
+ {
+ vEntries.push_back( xPackage );
+ }
+ }
+
+ m_xExecuteCmdQueue->checkForUpdates( vEntries );
+}
+
+
+bool TheExtensionManager::installPackage( const OUString &rPackageURL, bool bWarnUser )
+{
+ if ( rPackageURL.isEmpty() )
+ return false;
+
+ createDialog( false );
+
+ bool bInstall = true;
+ bool bInstallForAll = false;
+
+ // DV! missing function is read only repository from extension manager
+ if ( !bWarnUser && ! m_xExtensionManager->isReadOnlyRepository( SHARED_PACKAGE_MANAGER ) )
+ bInstall = getDialogHelper()->installForAllUsers( bInstallForAll );
+
+ if ( !bInstall )
+ return false;
+
+ if ( bInstallForAll )
+ m_xExecuteCmdQueue->addExtension( rPackageURL, SHARED_PACKAGE_MANAGER, false );
+ else
+ m_xExecuteCmdQueue->addExtension( rPackageURL, USER_PACKAGE_MANAGER, bWarnUser );
+
+ return true;
+}
+
+
+void TheExtensionManager::terminateDialog()
+{
+ if ( dp_misc::office_is_running() )
+ return;
+
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ Application::Quit();
+}
+
+
+void TheExtensionManager::createPackageList()
+{
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( uno::Sequence< uno::Reference< deployment::XPackage > > const & xPackageList : std::as_const(xAllPackages) )
+ {
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xPackageList )
+ {
+ if ( xPackage.is() )
+ {
+ PackageState eState = getPackageState( xPackage );
+ getDialogHelper()->addPackageToList( xPackage );
+ // When the package is enabled, we can stop here, otherwise we have to look for
+ // another version of this package
+ if ( ( eState == REGISTERED ) || ( eState == NOT_AVAILABLE ) )
+ break;
+ }
+ }
+ }
+
+ const uno::Sequence< uno::Reference< deployment::XPackage > > xNoLicPackages = m_xExtensionManager->getExtensionsWithUnacceptedLicenses( SHARED_PACKAGE_MANAGER,
+ uno::Reference< ucb::XCommandEnvironment >() );
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xNoLicPackages )
+ {
+ if ( xPackage.is() )
+ {
+ getDialogHelper()->addPackageToList( xPackage, true );
+ }
+ }
+}
+
+
+PackageState TheExtensionManager::getPackageState( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ try {
+ beans::Optional< beans::Ambiguous< sal_Bool > > option(
+ xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() ) );
+ if ( option.IsPresent )
+ {
+ ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
+ if ( reg.IsAmbiguous )
+ return AMBIGUOUS;
+ else
+ return reg.Value ? REGISTERED : NOT_REGISTERED;
+ }
+ else
+ return NOT_AVAILABLE;
+ }
+ catch ( const uno::RuntimeException & ) {
+ throw;
+ }
+ catch (const uno::Exception &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ return NOT_AVAILABLE;
+ }
+}
+
+
+bool TheExtensionManager::isReadOnly( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ if ( m_xExtensionManager.is() && xPackage.is() )
+ {
+ return m_xExtensionManager->isReadOnlyRepository( xPackage->getRepositoryName() );
+ }
+ else
+ return true;
+}
+
+
+// The function investigates if the extension supports options.
+bool TheExtensionManager::supportsOptions( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ bool bOptions = false;
+
+ if ( ! xPackage->isBundle() )
+ return false;
+
+ beans::Optional< OUString > aId = xPackage->getIdentifier();
+
+ //a bundle must always have an id
+ OSL_ASSERT( aId.IsPresent );
+
+ //iterate over all available nodes
+ const uno::Sequence< OUString > seqNames = m_xNameAccessNodes->getElementNames();
+
+ for ( OUString const & nodeName : seqNames )
+ {
+ uno::Any anyNode = m_xNameAccessNodes->getByName( nodeName );
+ //If we have a node then it must contain the set of leaves. This is part of OptionsDialog.xcs
+ uno::Reference< XInterface> xIntNode = anyNode.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xNode( xIntNode, uno::UNO_QUERY_THROW );
+
+ uno::Any anyLeaves = xNode->getByName("Leaves");
+ uno::Reference< XInterface > xIntLeaves = anyLeaves.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xLeaves( xIntLeaves, uno::UNO_QUERY_THROW );
+
+ //iterate over all available leaves
+ const uno::Sequence< OUString > seqLeafNames = xLeaves->getElementNames();
+ for ( OUString const & leafName : seqLeafNames )
+ {
+ uno::Any anyLeaf = xLeaves->getByName( leafName );
+ uno::Reference< XInterface > xIntLeaf = anyLeaf.get< uno::Reference< XInterface > >();
+ uno::Reference< beans::XPropertySet > xLeaf( xIntLeaf, uno::UNO_QUERY_THROW );
+ //investigate the Id property if it matches the extension identifier which
+ //has been passed in.
+ uno::Any anyValue = xLeaf->getPropertyValue("Id");
+
+ OUString sId = anyValue.get< OUString >();
+ if ( sId == aId.Value )
+ {
+ bOptions = true;
+ break;
+ }
+ }
+ if ( bOptions )
+ break;
+ }
+ return bOptions;
+}
+
+
+// XEventListener
+void TheExtensionManager::disposing( lang::EventObject const & rEvt )
+{
+ bool shutDown = (rEvt.Source == m_xDesktop);
+
+ if ( shutDown && m_xDesktop.is() )
+ {
+ m_xDesktop->removeTerminateListener( this );
+ m_xDesktop.clear();
+ }
+
+ if ( !shutDown )
+ return;
+
+ if ( dp_misc::office_is_running() )
+ {
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ }
+ s_ExtMgr.clear();
+}
+
+// XTerminateListener
+void TheExtensionManager::queryTermination( ::lang::EventObject const & )
+{
+ DialogHelper *pDialogHelper = getDialogHelper();
+
+ if ( m_xExecuteCmdQueue->isBusy() || ( pDialogHelper && pDialogHelper->isBusy() ) )
+ {
+ ToTop();
+ throw frame::TerminationVetoException(
+ "The office cannot be closed while the Extension Manager is running",
+ static_cast<frame::XTerminateListener*>(this));
+ }
+ else
+ {
+ clearModified();
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ }
+}
+
+void TheExtensionManager::notifyTermination( ::lang::EventObject const & rEvt )
+{
+ disposing( rEvt );
+}
+
+// XModifyListener
+void TheExtensionManager::modified( ::lang::EventObject const & /*rEvt*/ )
+{
+ m_bModified = true;
+ getDialogHelper()->prepareChecking();
+ createPackageList();
+ getDialogHelper()->checkEntries();
+}
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::get( const uno::Reference< uno::XComponentContext > &xContext,
+ const uno::Reference< awt::XWindow > &xParent,
+ const OUString & extensionURL )
+{
+ if ( s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+ return s_ExtMgr;
+ }
+
+ ::rtl::Reference<TheExtensionManager> that( new TheExtensionManager( xParent, xContext ) );
+
+ const SolarMutexGuard guard;
+ if ( ! s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ s_ExtMgr = that;
+ }
+
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+
+ return s_ExtMgr;
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.hxx b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
new file mode 100644
index 000000000..8595fb3c8
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_THEEXTMGR_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_THEEXTMGR_HXX
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include "dp_gui.h"
+#include "dp_gui_dialog2.hxx"
+
+
+namespace dp_gui {
+
+
+class ExtensionCmdQueue;
+
+
+class TheExtensionManager :
+ public ::cppu::WeakImplHelper< css::frame::XTerminateListener,
+ css::util::XModifyListener >
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XDesktop2 > m_xDesktop;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ css::uno::Reference< css::container::XNameAccess > m_xNameAccessNodes;
+ css::uno::Reference< css::awt::XWindow > m_xParent;
+ std::shared_ptr<ExtMgrDialog> m_xExtMgrDialog;
+ std::unique_ptr<UpdateRequiredDialog> m_xUpdReqDialog;
+ std::unique_ptr<ExtensionCmdQueue> m_xExecuteCmdQueue;
+
+ OUString m_sGetExtensionsURL;
+ bool m_bModified;
+ bool m_bExtMgrDialogExecuting;
+
+public:
+ static ::rtl::Reference<TheExtensionManager> s_ExtMgr;
+
+ TheExtensionManager( const css::uno::Reference< css::awt::XWindow > &xParent,
+ const css::uno::Reference< css::uno::XComponentContext > &xContext );
+ virtual ~TheExtensionManager() override;
+
+ void createDialog( const bool bCreateUpdDlg );
+ sal_Int16 execute();
+
+ bool isModified() const { return m_bModified; }
+ void clearModified() { m_bModified = false; }
+
+ weld::Window* getDialog()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog->getDialog();
+ if (m_xUpdReqDialog)
+ return m_xUpdReqDialog->getDialog();
+ return nullptr;
+ }
+ DialogHelper* getDialogHelper()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog.get();
+ return m_xUpdReqDialog.get();
+ }
+ ExtensionCmdQueue* getCmdQueue() const { return m_xExecuteCmdQueue.get(); }
+
+ void SetText( const OUString &rTitle );
+ void Show();
+ void ToTop();
+ void Close();
+ bool isVisible();
+
+
+ void checkUpdates();
+ bool installPackage( const OUString &rPackageURL, bool bWarnUser = false );
+ void createPackageList();
+
+ void terminateDialog();
+
+ // Tools
+ bool supportsOptions( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+ static PackageState getPackageState( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ const css::uno::Reference< css::uno::XComponentContext >& getContext() const { return m_xContext; }
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const { return m_xExtensionManager; }
+ bool isReadOnly( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+
+
+ static ::rtl::Reference<TheExtensionManager> get(
+ css::uno::Reference< css::uno::XComponentContext> const & xContext,
+ css::uno::Reference< css::awt::XWindow> const & xParent = nullptr,
+ OUString const & view = OUString() );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( css::lang::EventObject const & evt ) override;
+ virtual void SAL_CALL notifyTermination( css::lang::EventObject const & evt ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( css::lang::EventObject const & evt ) override;
+};
+
+} // namespace dp_gui
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedata.hxx b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
new file mode 100644
index 000000000..2edf071df
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEDATA_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEDATA_HXX
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star::deployment {
+ class XPackage;
+}
+namespace com::sun::star::xml::dom {
+ class XNode;
+}
+
+
+namespace dp_gui {
+
+struct UpdateData
+{
+ explicit UpdateData( css::uno::Reference< css::deployment::XPackage > const & aExt):
+ bIsShared(false), aInstalledPackage(aExt) {};
+
+ //When entries added to the listbox then there can be one for the user update and one
+ //for the shared update. However, both list entries will contain the same UpdateData.
+ //isShared is used to indicate which one is used for the shared entry.
+ bool bIsShared;
+
+ //The currently installed extension which is going to be updated. If the extension exist in
+ //multiple repositories then it is the one with the highest version.
+ css::uno::Reference< css::deployment::XPackage > aInstalledPackage;
+
+ //The version of the update
+ OUString updateVersion;
+
+ //For online update
+
+ // The content of the update information.
+ //Only if aUpdateInfo is set then there is an online update available with a better version
+ //than any of the currently installed extensions with the same identifier.
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+ //The URL of the locally downloaded extension. It will only be set if there were no errors
+ //during the download
+ OUString sLocalURL;
+ //The URL of the website where the download can be obtained.
+ OUString sWebsiteURL;
+
+ //For local update
+
+ //The locale extension which is used as update for the user or shared repository.
+ //If set then the data for the online update (aUpdateInfo, sLocalURL, sWebsiteURL)
+ //are to be ignored.
+ css::uno::Reference< css::deployment::XPackage > aUpdateSource;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.cxx b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
new file mode 100644
index 000000000..60a033711
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
@@ -0,0 +1,1006 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <utility>
+#include <vector>
+
+
+#include <optional>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/svapp.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+#include <strings.hrc>
+#include "dp_gui_updatedata.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include <dp_shared.hxx>
+
+class KeyEvent;
+class MouseEvent;
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+using namespace ::com::sun::star;
+using dp_gui::UpdateDialog;
+
+namespace {
+
+static sal_Unicode const LF = 0x000A;
+static sal_Unicode const CR = 0x000D;
+
+#define IGNORED_UPDATES OUString("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates")
+#define PROPERTY_VERSION "Version"
+
+enum Kind { ENABLED_UPDATE, DISABLED_UPDATE, SPECIFIC_ERROR };
+
+OUString confineToParagraph(OUString const & text) {
+ // Confine arbitrary text to a single paragraph in a VclMultiLineEdit
+ // This assumes that U+000A and U+000D are the only paragraph separators in
+ // a VclMultiLineEdit, and that replacing them with a single space
+ // each is acceptable:
+ return text.replace(LF, ' ').replace(CR, ' ');
+}
+}
+
+struct UpdateDialog::DisabledUpdate {
+ OUString name;
+ uno::Sequence< OUString > unsatisfiedDependencies;
+ // We also want to show release notes and publisher for disabled updates
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+};
+
+struct UpdateDialog::SpecificError {
+ OUString name;
+ OUString message;
+};
+
+
+struct UpdateDialog::IgnoredUpdate {
+ OUString sExtensionID;
+ OUString sVersion;
+
+ IgnoredUpdate( const OUString &rExtensionID, const OUString &rVersion );
+};
+
+
+UpdateDialog::IgnoredUpdate::IgnoredUpdate( const OUString &rExtensionID, const OUString &rVersion ):
+ sExtensionID( rExtensionID ),
+ sVersion( rVersion )
+{}
+
+
+struct UpdateDialog::Index
+{
+ Kind m_eKind;
+ bool m_bIgnored;
+ sal_uInt16 m_nIndex;
+ OUString m_aName;
+
+ Index( Kind theKind, sal_uInt16 nIndex, const OUString &rName ) :
+ m_eKind( theKind ),
+ m_bIgnored( false ),
+ m_nIndex( nIndex ),
+ m_aName( rName ) {}
+};
+
+
+class UpdateDialog::Thread: public salhelper::Thread {
+public:
+ Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ const std::vector< uno::Reference< deployment::XPackage > > & vExtensionList);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const;
+
+ OUString getUpdateDisplayString(
+ dp_gui::UpdateData const & data, OUString const & version = OUString()) const;
+
+ void prepareUpdateData(
+ css::uno::Reference< css::xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const;
+
+ bool update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const;
+
+ uno::Reference< uno::XComponentContext > m_context;
+ UpdateDialog & m_dialog;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+ uno::Reference< deployment::XUpdateInformationProvider > m_updateInformation;
+ uno::Reference< task::XInteractionHandler > m_xInteractionHdl;
+
+ // guarded by Application::GetSolarMutex():
+ bool m_stop;
+};
+
+UpdateDialog::Thread::Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ const std::vector< uno::Reference< deployment::XPackage > > &vExtensionList):
+ salhelper::Thread("dp_gui_updatedialog"),
+ m_context(context),
+ m_dialog(dialog),
+ m_vExtensionList(vExtensionList),
+ m_updateInformation(
+ deployment::UpdateInformationProvider::create(context)),
+ m_stop(false)
+{
+ if( m_context.is() )
+ {
+ m_xInteractionHdl =
+ task::InteractionHandler::createWithParent(m_context, dialog.getDialog()->GetXWindow());
+ m_updateInformation->setInteractionHandler( m_xInteractionHdl );
+ }
+}
+
+void UpdateDialog::Thread::stop() {
+ {
+ SolarMutexGuard g;
+ m_stop = true;
+ }
+ m_updateInformation->cancel();
+}
+
+UpdateDialog::Thread::~Thread()
+{
+ if ( m_xInteractionHdl.is() )
+ m_updateInformation->setInteractionHandler( uno::Reference< task::XInteractionHandler > () );
+}
+
+void UpdateDialog::Thread::execute()
+{
+ {
+ SolarMutexGuard g;
+ if ( m_stop ) {
+ return;
+ }
+ }
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(m_context);
+
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+
+ dp_misc::UpdateInfoMap updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ m_context, extMgr, m_updateInformation, &m_vExtensionList, errors);
+
+ for (auto const& elem : errors)
+ handleSpecificError(elem.first, elem.second);
+
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+ UpdateData updateData(info.extension);
+ DisabledUpdate disableUpdate;
+ //determine if online updates meet the requirements
+ prepareUpdateData(info.info, disableUpdate, updateData);
+
+ //determine if the update is installed in the user or shared repository
+ OUString sOnlineVersion;
+ if (info.info.is())
+ sOnlineVersion = info.version;
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<ucb::XCommandEnvironment>());
+ } catch ( const lang::IllegalArgumentException& ) {
+ OSL_ASSERT(false);
+ continue;
+ } catch ( const css::ucb::CommandFailedException& ) {
+ OSL_ASSERT(false);
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceUser == dp_misc::UPDATE_SOURCE_SHARED)
+ {
+ updateData.aUpdateSource = extensions[1];
+ updateData.updateVersion = extensions[1]->getVersion();
+ }
+ else if (sourceUser == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceShared == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ updateData.bIsShared = true;
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+ }
+
+
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.checkingDone();
+ }
+}
+
+//Parameter package can be null
+void UpdateDialog::Thread::handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const
+{
+ UpdateDialog::SpecificError data;
+ if (package.is())
+ data.name = package->getDisplayName();
+ uno::Exception e;
+ if (exception >>= e) {
+ data.message = e.Message;
+ }
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addSpecificError(data);
+ }
+}
+
+OUString UpdateDialog::Thread::getUpdateDisplayString(
+ dp_gui::UpdateData const & data, OUString const & version) const
+{
+ OSL_ASSERT(data.aInstalledPackage.is());
+ OUStringBuffer b(data.aInstalledPackage->getDisplayName());
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_version);
+ }
+ b.append(' ');
+ if (!version.isEmpty())
+ b.append(version);
+ else
+ b.append(data.updateVersion);
+
+ if (!data.sWebsiteURL.isEmpty())
+ {
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_browserbased);
+ }
+ }
+ return b.makeStringAndClear();
+}
+
+/** out_data will only be filled if all dependencies are ok.
+ */
+void UpdateDialog::Thread::prepareUpdateData(
+ uno::Reference< xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const
+{
+ if (!updateInfo.is())
+ return;
+ dp_misc::DescriptionInfoset infoset(m_context, updateInfo);
+ OSL_ASSERT(!infoset.getVersion().isEmpty());
+ uno::Sequence< uno::Reference< xml::dom::XElement > > ds(
+ dp_misc::Dependencies::check(infoset));
+
+ out_du.aUpdateInfo = updateInfo;
+ out_du.unsatisfiedDependencies.realloc(ds.getLength());
+ for (sal_Int32 i = 0; i < ds.getLength(); ++i) {
+ out_du.unsatisfiedDependencies[i] = dp_misc::Dependencies::getErrorText(ds[i]);
+ }
+
+ const ::std::optional< OUString> updateWebsiteURL(infoset.getLocalizedUpdateWebsiteURL());
+
+ out_du.name = getUpdateDisplayString(out_data, infoset.getVersion());
+
+ if (!out_du.unsatisfiedDependencies.hasElements())
+ {
+ out_data.aUpdateInfo = updateInfo;
+ out_data.updateVersion = infoset.getVersion();
+ if (updateWebsiteURL)
+ out_data.sWebsiteURL = *updateWebsiteURL;
+ }
+}
+
+bool UpdateDialog::Thread::update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const
+{
+ bool ret = false;
+ if (!du.unsatisfiedDependencies.hasElements())
+ {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addEnabledUpdate(getUpdateDisplayString(data), data);
+ }
+ ret = !m_stop;
+ } else {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addDisabledUpdate(du);
+ }
+ ret = !m_stop;
+ }
+ return ret;
+}
+
+// UpdateDialog ----------------------------------------------------------
+UpdateDialog::UpdateDialog(
+ uno::Reference< uno::XComponentContext > const & context,
+ weld::Window * parent, const std::vector<uno::Reference< deployment::XPackage > > &vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData)
+ : GenericDialogController(parent, "desktop/ui/updatedialog.ui", "UpdateDialog")
+ , m_context(context)
+ , m_none(DpResId(RID_DLG_UPDATE_NONE))
+ , m_noInstallable(DpResId(RID_DLG_UPDATE_NOINSTALLABLE))
+ , m_failure(DpResId(RID_DLG_UPDATE_FAILURE))
+ , m_unknownError(DpResId(RID_DLG_UPDATE_UNKNOWNERROR))
+ , m_noDescription(DpResId(RID_DLG_UPDATE_NODESCRIPTION))
+ , m_noInstall(DpResId(RID_DLG_UPDATE_NOINSTALL))
+ , m_noDependency(DpResId(RID_DLG_UPDATE_NODEPENDENCY))
+ , m_noDependencyCurVer(DpResId(RID_DLG_UPDATE_NODEPENDENCY_CUR_VER))
+ , m_browserbased(DpResId(RID_DLG_UPDATE_BROWSERBASED))
+ , m_version(DpResId(RID_DLG_UPDATE_VERSION))
+ , m_ignoredUpdate(DpResId(RID_DLG_UPDATE_IGNORED_UPDATE))
+ , m_updateData(*updateData)
+ , m_thread(new UpdateDialog::Thread(context, *this, vExtensionList))
+ , m_xChecking(m_xBuilder->weld_label("UPDATE_CHECKING"))
+ , m_xThrobber(m_xBuilder->weld_spinner("THROBBER"))
+ , m_xUpdate(m_xBuilder->weld_label("UPDATE_LABEL"))
+ , m_xUpdates(m_xBuilder->weld_tree_view("checklist"))
+ , m_xAll(m_xBuilder->weld_check_button("UPDATE_ALL"))
+ , m_xDescription(m_xBuilder->weld_label("DESCRIPTION_LABEL"))
+ , m_xPublisherLabel(m_xBuilder->weld_label("PUBLISHER_LABEL"))
+ , m_xPublisherLink(m_xBuilder->weld_link_button("PUBLISHER_LINK"))
+ , m_xReleaseNotesLabel(m_xBuilder->weld_label("RELEASE_NOTES_LABEL"))
+ , m_xReleaseNotesLink(m_xBuilder->weld_link_button("RELEASE_NOTES_LINK"))
+ , m_xDescriptions(m_xBuilder->weld_text_view("DESCRIPTIONS"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xClose(m_xBuilder->weld_button("close"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+{
+ auto nWidth = m_xDescriptions->get_approximate_digit_width() * 62;
+ auto nHeight = m_xDescriptions->get_height_rows(8);
+ m_xDescriptions->set_size_request(nWidth, nHeight);
+ m_xUpdates->set_size_request(nWidth, nHeight);
+
+ std::vector<int> aWidths;
+ aWidths.push_back(m_xUpdates->get_checkbox_column_width());
+ m_xUpdates->set_column_fixed_widths(aWidths);
+
+ OSL_ASSERT(updateData != nullptr);
+
+ m_xExtensionManager = deployment::ExtensionManager::get( context );
+
+ m_xUpdates->connect_changed(LINK(this, UpdateDialog, selectionHandler));
+ m_xUpdates->connect_toggled(LINK(this, UpdateDialog, entryToggled));
+ m_xAll->connect_toggled(LINK(this, UpdateDialog, allHandler));
+ m_xOk->connect_clicked(LINK(this, UpdateDialog, okHandler));
+ m_xClose->connect_clicked(LINK(this, UpdateDialog, closeHandler));
+ if (!dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+
+ initDescription();
+ getIgnoredUpdates();
+}
+
+UpdateDialog::~UpdateDialog()
+{
+}
+
+short UpdateDialog::run() {
+ m_xThrobber->start();
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+IMPL_LINK(UpdateDialog, entryToggled, const row_col&, rRowCol, void)
+{
+ int nRow = rRowCol.first;
+
+ // error's can't be enabled
+ const UpdateDialog::Index* p = reinterpret_cast<UpdateDialog::Index const *>(m_xUpdates->get_id(rRowCol.first).toInt64());
+ if (p->m_eKind == SPECIFIC_ERROR)
+ m_xUpdates->set_toggle(nRow, TRISTATE_FALSE, 0);
+
+ enableOk();
+}
+
+void UpdateDialog::insertItem(UpdateDialog::Index *pEntry, bool bEnabledCheckBox)
+{
+ int nEntry = m_xUpdates->n_children();
+ m_xUpdates->append();
+ m_xUpdates->set_toggle(nEntry, bEnabledCheckBox ? TRISTATE_TRUE : TRISTATE_FALSE, 0);
+ m_xUpdates->set_text(nEntry, pEntry->m_aName, 1);
+ m_xUpdates->set_id(nEntry, OUString::number(reinterpret_cast<sal_Int64>(pEntry)));
+}
+
+void UpdateDialog::addAdditional(UpdateDialog::Index * index, bool bEnabledCheckBox)
+{
+ m_xAll->set_sensitive(true);
+ if (m_xAll->get_active())
+ {
+ insertItem(index, bEnabledCheckBox);
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+ }
+}
+
+void UpdateDialog::addEnabledUpdate( OUString const & name,
+ dp_gui::UpdateData const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_enabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( ENABLED_UPDATE, nIndex, name );
+
+ m_enabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ if (!isIgnoredUpdate(pEntry))
+ {
+ insertItem(pEntry, true);
+ }
+ else
+ addAdditional(pEntry, false);
+
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+}
+
+void UpdateDialog::addDisabledUpdate( UpdateDialog::DisabledUpdate const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_disabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( DISABLED_UPDATE, nIndex, data.name );
+
+ m_disabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ isIgnoredUpdate( pEntry );
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::addSpecificError( UpdateDialog::SpecificError const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_specificErrors.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( SPECIFIC_ERROR, nIndex, data.name );
+
+ m_specificErrors.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::checkingDone() {
+ m_xChecking->hide();
+ m_xThrobber->stop();
+ m_xThrobber->hide();
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ if ( m_disabledUpdates.empty() && m_specificErrors.empty() && m_ignoredUpdates.empty() )
+ showDescription( m_none );
+ else
+ showDescription( m_noInstallable );
+ }
+
+ enableOk();
+}
+
+void UpdateDialog::enableOk() {
+ if (!m_xChecking->get_visible()) {
+ int nChecked = 0;
+ for (int i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i) {
+ if (m_xUpdates->get_toggle(i, 0) == TRISTATE_TRUE)
+ ++nChecked;
+ }
+ m_xOk->set_sensitive(nChecked != 0);
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::createNotifyJob( bool bPrepareOnly,
+ uno::Sequence< uno::Sequence< OUString > > const &rItemList )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ // notify update check job
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()));
+
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath";
+ aProperty.Value <<= OUString("org.openoffice.Office.Addons/AddonUI/OfficeHelp/UpdateCheckJob");
+
+ uno::Sequence< uno::Any > aArgumentList( 1 );
+ aArgumentList[0] <<= aProperty;
+
+ uno::Reference< container::XNameAccess > xNameAccess(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgumentList ),
+ uno::UNO_QUERY_THROW );
+
+ util::URL aURL;
+ xNameAccess->getByName("URL") >>= aURL.Complete;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < util::XURLTransformer > xTransformer = util::URLTransformer::create(xContext);
+
+ xTransformer->parseStrict(aURL);
+
+ uno::Reference < frame::XDesktop2 > xDesktop = frame::Desktop::create( xContext );
+ uno::Reference< frame::XDispatchProvider > xDispatchProvider( xDesktop->getCurrentFrame(),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XDispatch > xDispatch = xDispatchProvider->queryDispatch(aURL, OUString(), 0);
+
+ if( xDispatch.is() )
+ {
+ uno::Sequence< beans::PropertyValue > aPropList(2);
+ aProperty.Name = "updateList";
+ aProperty.Value <<= rItemList;
+ aPropList[0] = aProperty;
+ aProperty.Name = "prepareOnly";
+ aProperty.Value <<= bPrepareOnly;
+ aPropList[1] = aProperty;
+
+ xDispatch->dispatch(aURL, aPropList );
+ }
+ }
+ catch( const uno::Exception& e )
+ {
+ dp_misc::TRACE( "Caught exception: "
+ + e.Message + "\n thread terminated.\n\n");
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::notifyMenubar( bool bPrepareOnly, bool bRecheckOnly )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+
+ if ( ! bRecheckOnly )
+ {
+ sal_Int32 nCount = 0;
+ for (sal_uInt16 i = 0, nItemCount = m_xUpdates->n_children(); i < nItemCount; ++i)
+ {
+ uno::Sequence< OUString > aItem(2);
+
+ UpdateDialog::Index const * p = reinterpret_cast< UpdateDialog::Index const * >(m_xUpdates->get_id(i).toInt64());
+
+ if ( p->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ p->m_nIndex ];
+ aItem[0] = dp_misc::getIdentifier( aUpdData.aInstalledPackage );
+
+ dp_misc::DescriptionInfoset aInfoset( m_context, aUpdData.aUpdateInfo );
+ aItem[1] = aInfoset.getVersion();
+ }
+ else
+ continue;
+
+ aItemList.realloc( nCount + 1 );
+ aItemList[ nCount ] = aItem;
+ nCount += 1;
+ }
+ }
+
+ createNotifyJob( bPrepareOnly, aItemList );
+}
+
+// *********************************************************************************
+
+void UpdateDialog::initDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+}
+
+void UpdateDialog::clearDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xPublisherLink->set_label("");
+ m_xPublisherLink->set_uri("");
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+ m_xReleaseNotesLink->set_uri( "" );
+ m_xDescriptions->set_text("");
+}
+
+bool UpdateDialog::showDescription(uno::Reference< xml::dom::XNode > const & aUpdateInfo)
+{
+ dp_misc::DescriptionInfoset infoset(m_context, aUpdateInfo);
+ return showDescription(infoset.getLocalizedPublisherNameAndURL(),
+ infoset.getLocalizedReleaseNotesURL());
+}
+
+bool UpdateDialog::showDescription(uno::Reference< deployment::XPackage > const & aExtension)
+{
+ OSL_ASSERT(aExtension.is());
+ beans::StringPair pubInfo = aExtension->getPublisherInfo();
+ return showDescription(std::make_pair(pubInfo.First, pubInfo.Second),
+ "");
+}
+
+bool UpdateDialog::showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes)
+{
+ OUString sPub = pairPublisher.first;
+ OUString sURL = pairPublisher.second;
+
+ if ( sPub.isEmpty() && sURL.isEmpty() && sReleaseNotes.isEmpty() )
+ // nothing to show
+ return false;
+
+ if ( !sPub.isEmpty() )
+ {
+ m_xPublisherLabel->show();
+ m_xPublisherLink->show();
+ m_xPublisherLink->set_label(sPub);
+ m_xPublisherLink->set_uri(sURL);
+ }
+
+ if ( !sReleaseNotes.isEmpty() )
+ {
+ m_xReleaseNotesLabel->show();
+ m_xReleaseNotesLink->show();
+ m_xReleaseNotesLink->set_uri( sReleaseNotes );
+ }
+ return true;
+}
+
+bool UpdateDialog::showDescription( const OUString& rDescription)
+{
+ if ( rDescription.isEmpty() )
+ // nothing to show
+ return false;
+
+ m_xDescriptions->set_text(rDescription);
+ return true;
+}
+
+void UpdateDialog::getIgnoredUpdates()
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(m_context));
+ beans::NamedValue aValue( "nodepath", uno::Any( IGNORED_UPDATES ) );
+ uno::Sequence< uno::Any > args(1);
+ args[0] <<= aValue;
+
+ uno::Reference< container::XNameAccess > xNameAccess( xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args), uno::UNO_QUERY_THROW );
+ const uno::Sequence< OUString > aElementNames = xNameAccess->getElementNames();
+
+ for ( OUString const & aIdentifier : aElementNames )
+ {
+ OUString aVersion;
+
+ uno::Any aPropValue( uno::Reference< beans::XPropertySet >( xNameAccess->getByName( aIdentifier ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aPropValue >>= aVersion;
+ IgnoredUpdate *pData = new IgnoredUpdate( aIdentifier, aVersion );
+ m_ignoredUpdates.emplace_back( pData );
+ }
+}
+
+
+bool UpdateDialog::isIgnoredUpdate( UpdateDialog::Index * index )
+{
+ bool bIsIgnored = false;
+
+ if (! m_ignoredUpdates.empty() )
+ {
+ OUString aExtensionID;
+ OUString aVersion;
+
+ if ( index->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ index->m_nIndex ];
+ aExtensionID = dp_misc::getIdentifier( aUpdData.aInstalledPackage );
+ aVersion = aUpdData.updateVersion;
+ }
+ else if ( index->m_eKind == DISABLED_UPDATE )
+ {
+ DisabledUpdate &rData = m_disabledUpdates[ index->m_nIndex ];
+ dp_misc::DescriptionInfoset aInfoset( m_context, rData.aUpdateInfo );
+ ::std::optional< OUString > aID( aInfoset.getIdentifier() );
+ if ( aID )
+ aExtensionID = *aID;
+ aVersion = aInfoset.getVersion();
+ }
+
+ for (auto const& ignoredUpdate : m_ignoredUpdates)
+ {
+ if ( ignoredUpdate->sExtensionID == aExtensionID )
+ {
+ if ( ( !ignoredUpdate->sVersion.isEmpty() ) || ( ignoredUpdate->sVersion == aVersion ) )
+ {
+ bIsIgnored = true;
+ index->m_bIgnored = true;
+ }
+ break;
+ }
+ }
+ }
+
+ return bIsIgnored;
+}
+
+
+IMPL_LINK_NOARG(UpdateDialog, selectionHandler, weld::TreeView&, void)
+{
+ OUStringBuffer b;
+ int nSelectedPos = m_xUpdates->get_selected_index();
+ clearDescription();
+
+ const UpdateDialog::Index* p = nullptr;
+ if (nSelectedPos != -1)
+ p = reinterpret_cast<UpdateDialog::Index const *>(m_xUpdates->get_id(nSelectedPos).toInt64());
+ if (p != nullptr)
+ {
+ sal_uInt16 pos = p->m_nIndex;
+
+ switch (p->m_eKind)
+ {
+ case ENABLED_UPDATE:
+ {
+ if ( m_enabledUpdates[ pos ].aUpdateSource.is() )
+ showDescription( m_enabledUpdates[ pos ].aUpdateSource );
+ else
+ showDescription( m_enabledUpdates[ pos ].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ break;
+ }
+ case DISABLED_UPDATE:
+ {
+ if ( !m_disabledUpdates.empty() )
+ showDescription( m_disabledUpdates[pos].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ if ( m_disabledUpdates.empty() )
+ break;
+
+ UpdateDialog::DisabledUpdate & data = m_disabledUpdates[ pos ];
+ if (data.unsatisfiedDependencies.hasElements())
+ {
+ // create error string for version mismatch
+ OUString sVersion( "%VERSION" );
+ OUString sProductName( "%PRODUCTNAME" );
+ sal_Int32 nPos = m_noDependencyCurVer.indexOf( sVersion );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sVersion.getLength(), utl::ConfigManager::getAboutBoxProductVersion() );
+ }
+ nPos = m_noDependencyCurVer.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+ nPos = m_noDependency.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependency = m_noDependency.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+
+ b.append(m_noInstall);
+ b.append(LF);
+ b.append(m_noDependency);
+ for (sal_Int32 i = 0;
+ i < data.unsatisfiedDependencies.getLength(); ++i)
+ {
+ b.append(LF);
+ b.append(" ");
+ // U+2003 EM SPACE would be better than two spaces,
+ // but some fonts do not contain it
+ b.append(
+ confineToParagraph(
+ data.unsatisfiedDependencies[i]));
+ }
+ b.append(LF);
+ b.append(" ");
+ b.append(m_noDependencyCurVer);
+ }
+ break;
+ }
+ case SPECIFIC_ERROR:
+ {
+ UpdateDialog::SpecificError & data = m_specificErrors[ pos ];
+ b.append(m_failure);
+ b.append(LF);
+ b.append( data.message.isEmpty() ? m_unknownError : data.message );
+ break;
+ }
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+
+ if ( b.isEmpty() )
+ b.append( m_noDescription );
+
+ showDescription( b.makeStringAndClear() );
+}
+
+IMPL_LINK_NOARG(UpdateDialog, allHandler, weld::ToggleButton&, void)
+{
+ if (m_xAll->get_active())
+ {
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ for (auto const& listboxEntry : m_ListboxEntries)
+ {
+ if ( listboxEntry->m_bIgnored || ( listboxEntry->m_eKind != ENABLED_UPDATE ) )
+ insertItem(listboxEntry.get(), false);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = m_xUpdates->n_children(); i != 0 ;)
+ {
+ i -= 1;
+ UpdateDialog::Index const * p = reinterpret_cast< UpdateDialog::Index const * >( m_xUpdates->get_id(i).toInt64() );
+ if ( p->m_bIgnored || ( p->m_eKind != ENABLED_UPDATE ) )
+ {
+ m_xUpdates->remove(i);
+ }
+ }
+
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xUpdate->set_sensitive(false);
+ m_xUpdates->set_sensitive(false);
+ if (m_xChecking->get_visible())
+ m_xDescription->set_sensitive(false);
+ else
+ showDescription(m_noInstallable);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(UpdateDialog, okHandler, weld::Button&, void)
+{
+ //If users are going to update a shared extension then we need
+ //to warn them
+ for (auto const& enableUpdate : m_enabledUpdates)
+ {
+ OSL_ASSERT(enableUpdate.aInstalledPackage.is());
+ //If the user has no write access to the shared folder then the update
+ //for a shared extension is disable, that is it cannot be in m_enabledUpdates
+ }
+
+
+ for (sal_uInt16 i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i)
+ {
+ UpdateDialog::Index const * p =
+ reinterpret_cast< UpdateDialog::Index const * >(
+ m_xUpdates->get_id(i).toInt64());
+ if (p->m_eKind == ENABLED_UPDATE && m_xUpdates->get_toggle(i, 0) == TRISTATE_TRUE) {
+ m_updateData.push_back( m_enabledUpdates[ p->m_nIndex ] );
+ }
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(UpdateDialog, closeHandler, weld::Button&, void)
+{
+ m_thread->stop();
+ m_xDialog->response(RET_CANCEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.hxx b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
new file mode 100644
index 000000000..291f02850
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEDIALOG_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEDIALOG_HXX
+
+#include <sal/config.h>
+
+#include <vector>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+class Image;
+class KeyEvent;
+class MouseEvent;
+class ResId;
+namespace vcl { class Window; }
+
+namespace com::sun::star {
+ namespace deployment { class XExtensionManager;
+ class XPackage; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+/**
+ The modal &ldquo;Check for Updates&rdquo; dialog.
+*/
+class UpdateDialog: public weld::GenericDialogController {
+public:
+ /**
+ Create an instance.
+
+ <p>Exactly one of <code>selectedPackages</code> and
+ <code>packageManagers</code> must be non-null.</p>
+
+ @param context
+ a non-null component context
+
+ @param parent
+ the parent window, may be null
+
+ @param vExtensionList
+ check for updates for the contained extensions. There must only be one extension with
+ a particular identifier. If one extension is installed in several repositories, then the
+ one with the highest version must be used, because it contains the latest known update
+ information.
+ */
+ UpdateDialog(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ weld::Window * parent,
+ const std::vector< css::uno::Reference<
+ css::deployment::XPackage > > & vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData);
+
+ virtual ~UpdateDialog() override;
+
+ virtual short run() override;
+
+ void notifyMenubar( bool bPrepareOnly, bool bRecheckOnly );
+ static void createNotifyJob( bool bPrepareOnly,
+ css::uno::Sequence< css::uno::Sequence< OUString > > const &rItemList );
+
+private:
+ UpdateDialog(UpdateDialog const &) = delete;
+ UpdateDialog& operator =(UpdateDialog const &) = delete;
+
+ struct DisabledUpdate;
+ struct SpecificError;
+ struct IgnoredUpdate;
+ struct Index;
+ friend struct Index;
+ class Thread;
+ friend class Thread;
+
+ friend class CheckListBox;
+
+ void insertItem(UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ void addAdditional(UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ bool isIgnoredUpdate( UpdateDialog::Index *pIndex );
+
+ void addEnabledUpdate( OUString const & name, dp_gui::UpdateData const & data );
+ void addDisabledUpdate( UpdateDialog::DisabledUpdate const & data );
+ void addSpecificError( UpdateDialog::SpecificError const & data );
+
+ void checkingDone();
+
+ void enableOk();
+
+ void getIgnoredUpdates();
+
+ void initDescription();
+ void clearDescription();
+ bool showDescription(css::uno::Reference<
+ css::deployment::XPackage > const & aExtension);
+ bool showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes);
+ bool showDescription( css::uno::Reference<
+ css::xml::dom::XNode > const & aUpdateInfo);
+ bool showDescription( const OUString& rDescription);
+
+ DECL_LINK(selectionHandler, weld::TreeView&, void);
+ DECL_LINK(allHandler, weld::ToggleButton&, void);
+ DECL_LINK(okHandler, weld::Button&, void);
+ DECL_LINK(closeHandler, weld::Button&, void);
+ typedef std::pair<int, int> row_col;
+ DECL_LINK(entryToggled, const row_col&, void);
+
+ css::uno::Reference< css::uno::XComponentContext > m_context;
+ OUString m_none;
+ OUString m_noInstallable;
+ OUString m_failure;
+ OUString m_unknownError;
+ OUString m_noDescription;
+ OUString m_noInstall;
+ OUString m_noDependency;
+ OUString m_noDependencyCurVer;
+ OUString m_browserbased;
+ OUString m_version;
+ OUString m_ignoredUpdate;
+ std::vector< dp_gui::UpdateData > m_enabledUpdates;
+ std::vector< UpdateDialog::DisabledUpdate > m_disabledUpdates;
+ std::vector< UpdateDialog::SpecificError > m_specificErrors;
+ std::vector< std::unique_ptr<UpdateDialog::IgnoredUpdate> > m_ignoredUpdates;
+ std::vector< std::unique_ptr<Index> > m_ListboxEntries;
+ std::vector< dp_gui::UpdateData > & m_updateData;
+ rtl::Reference< UpdateDialog::Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+
+ std::unique_ptr<weld::Label> m_xChecking;
+ std::unique_ptr<weld::Spinner> m_xThrobber;
+ std::unique_ptr<weld::Label> m_xUpdate;
+ std::unique_ptr<weld::TreeView> m_xUpdates;
+ std::unique_ptr<weld::CheckButton> m_xAll;
+ std::unique_ptr<weld::Label> m_xDescription;
+ std::unique_ptr<weld::Label> m_xPublisherLabel;
+ std::unique_ptr<weld::LinkButton> m_xPublisherLink;
+ std::unique_ptr<weld::Label> m_xReleaseNotesLabel;
+ std::unique_ptr<weld::LinkButton> m_xReleaseNotesLink;
+ std::unique_ptr<weld::TextView> m_xDescriptions;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xClose;
+ std::unique_ptr<weld::Button> m_xHelp;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
new file mode 100644
index 000000000..f38a853de
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "dp_gui_updatedata.hxx"
+
+#include <sal/config.h>
+#include <osl/file.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <dp_descriptioninfoset.hxx>
+#include <strings.hrc>
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_shared.hxx>
+#include <dp_ucb.h>
+#include <dp_misc.h>
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <ucbhelper/content.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <comphelper/anytostring.hxx>
+
+#include <vector>
+
+using dp_misc::StrTitle;
+
+namespace dp_gui {
+
+class UpdateInstallDialog::Thread: public salhelper::Thread {
+ friend class UpdateCommandEnv;
+public:
+ Thread(css::uno::Reference< css::uno::XComponentContext > const & ctx,
+ UpdateInstallDialog & dialog, std::vector< dp_gui::UpdateData > & aVecUpdateData);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+ void downloadExtensions();
+ bool download(OUString const & aUrls, UpdateData & aUpdatData);
+ void installExtensions();
+ void removeTempDownloads();
+
+ UpdateInstallDialog & m_dialog;
+
+ // guarded by Application::GetSolarMutex():
+ css::uno::Reference< css::task::XAbortChannel > m_abort;
+ css::uno::Reference< css::uno::XComponentContext > m_xComponentContext;
+ std::vector< dp_gui::UpdateData > & m_aVecUpdateData;
+ ::rtl::Reference<UpdateCommandEnv> m_updateCmdEnv;
+
+ //A folder which is created in the temp directory in which then the updates are downloaded
+ OUString m_sDownloadFolder;
+
+ bool m_stop;
+
+};
+
+class UpdateCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ friend class UpdateInstallDialog::Thread;
+
+ ::rtl::Reference<UpdateInstallDialog::Thread> m_installThread;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+public:
+ UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > const & xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread>const & thread);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+UpdateInstallDialog::Thread::Thread(
+ css::uno::Reference< css::uno::XComponentContext> const & xCtx,
+ UpdateInstallDialog & dialog,
+ std::vector< dp_gui::UpdateData > & aVecUpdateData):
+ salhelper::Thread("dp_gui_updateinstalldialog"),
+ m_dialog(dialog),
+ m_xComponentContext(xCtx),
+ m_aVecUpdateData(aVecUpdateData),
+ m_updateCmdEnv(new UpdateCommandEnv(xCtx, this)),
+ m_stop(false)
+{}
+
+void UpdateInstallDialog::Thread::stop() {
+ css::uno::Reference< css::task::XAbortChannel > abort;
+ {
+ SolarMutexGuard g;
+ abort = m_abort;
+ m_stop = true;
+ }
+ if (abort.is()) {
+ abort->sendAbort();
+ }
+}
+
+UpdateInstallDialog::Thread::~Thread() {}
+
+void UpdateInstallDialog::Thread::execute()
+{
+ try {
+ downloadExtensions();
+ installExtensions();
+ }
+ catch (...)
+ {
+ }
+
+ //clean up the temp directories
+ try {
+ removeTempDownloads();
+ } catch( ... ) {
+ }
+
+ {
+ //make sure m_dialog is still alive
+ SolarMutexGuard g;
+ if (! m_stop)
+ m_dialog.updateDone();
+ }
+ //UpdateCommandEnv keeps a reference to Thread and prevents destruction. Therefore remove it.
+ m_updateCmdEnv->m_installThread.clear();
+}
+
+UpdateInstallDialog::UpdateInstallDialog(
+ weld::Window* pParent,
+ std::vector<dp_gui::UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx)
+ : GenericDialogController(pParent, "desktop/ui/updateinstalldialog.ui",
+ "UpdateInstallDialog")
+ , m_thread(new Thread(xCtx, *this, aVecUpdateData))
+ , m_bError(false)
+ , m_bNoEntry(true)
+ , m_sInstalling(DpResId(RID_DLG_UPDATE_INSTALL_INSTALLING))
+ , m_sFinished(DpResId(RID_DLG_UPDATE_INSTALL_FINISHED))
+ , m_sNoErrors(DpResId(RID_DLG_UPDATE_INSTALL_NO_ERRORS))
+ , m_sErrorDownload(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_DOWNLOAD))
+ , m_sErrorInstallation(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_INSTALLATION))
+ , m_sErrorLicenseDeclined(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_LIC_DECLINED))
+ , m_sNoInstall(DpResId(RID_DLG_UPDATE_INSTALL_EXTENSION_NOINSTALL))
+ , m_sThisErrorOccurred(DpResId(RID_DLG_UPDATE_INSTALL_THIS_ERROR_OCCURRED))
+ , m_xFt_action(m_xBuilder->weld_label("DOWNLOADING"))
+ , m_xStatusbar(m_xBuilder->weld_progress_bar("STATUSBAR"))
+ , m_xFt_extension_name(m_xBuilder->weld_label("EXTENSION_NAME"))
+ , m_xMle_info(m_xBuilder->weld_text_view("INFO"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xMle_info->set_size_request(m_xMle_info->get_approximate_digit_width() * 52,
+ m_xMle_info->get_height_rows(5));
+
+ m_xExtensionManager = css::deployment::ExtensionManager::get( xCtx );
+
+ m_xCancel->connect_clicked(LINK(this, UpdateInstallDialog, cancelHandler));
+ if ( ! dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+}
+
+UpdateInstallDialog::~UpdateInstallDialog()
+{
+}
+
+short UpdateInstallDialog::run()
+{
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+// make sure the solar mutex is locked before calling
+void UpdateInstallDialog::updateDone()
+{
+ if (!m_bError)
+ m_xMle_info->set_text(m_xMle_info->get_text() + m_sNoErrors);
+ m_xOk->set_sensitive(true);
+ m_xOk->grab_focus();
+ m_xCancel->set_sensitive(false);
+}
+
+// make sure the solar mutex is locked before calling
+//sets an error message in the text area
+void UpdateInstallDialog::setError(INSTALL_ERROR err, OUString const & sExtension,
+ OUString const & exceptionMessage)
+{
+ OUString sError;
+ m_bError = true;
+
+ switch (err)
+ {
+ case ERROR_DOWNLOAD:
+ sError = m_sErrorDownload;
+ break;
+ case ERROR_INSTALLATION:
+ sError = m_sErrorInstallation;
+ break;
+ case ERROR_LICENSE_DECLINED:
+ sError = m_sErrorLicenseDeclined;
+ break;
+
+ default:
+ OSL_ASSERT(false);
+ }
+
+ OUString sMsg(m_xMle_info->get_text());
+ sError = sError.replaceFirst("%NAME", sExtension);
+ //We want to have an empty line between the error messages. However,
+ //there shall be no empty line after the last entry.
+ if (m_bNoEntry)
+ m_bNoEntry = false;
+ else
+ sMsg += "\n";
+ sMsg += sError;
+ //Insert more information about the error
+ if (!exceptionMessage.isEmpty())
+ sMsg += m_sThisErrorOccurred + exceptionMessage + "\n";
+
+ sMsg += m_sNoInstall + "\n";
+
+ m_xMle_info->set_text(sMsg);
+}
+
+void UpdateInstallDialog::setError(OUString const & exceptionMessage)
+{
+ m_bError = true;
+ m_xMle_info->set_text(m_xMle_info->get_text() + exceptionMessage + "\n");
+}
+
+IMPL_LINK_NOARG(UpdateInstallDialog, cancelHandler, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+void UpdateInstallDialog::Thread::downloadExtensions()
+{
+ try
+ {
+ //create the download directory in the temp folder
+ OUString sTempDir;
+ if (::osl::FileBase::getTempDirURL(sTempDir) != ::osl::FileBase::E_None)
+ throw css::uno::Exception("Could not get URL for the temp directory. No extensions will be installed.", nullptr);
+
+ //create a unique name for the directory
+ OUString tempEntry, destFolder;
+ if (::osl::File::createTempFile(&sTempDir, nullptr, &tempEntry ) != ::osl::File::E_None)
+ throw css::uno::Exception("Could not create a temporary file in " + sTempDir +
+ ". No extensions will be installed", nullptr );
+
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( sTempDir, tempEntry ) + "_";
+ m_sDownloadFolder = destFolder;
+ try
+ {
+ dp_misc::create_folder(nullptr, destFolder, m_updateCmdEnv.get() );
+ } catch (const css::uno::Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException( e.Message + " No extensions will be installed",
+ nullptr, anyEx );
+ }
+
+
+ sal_uInt16 count = 0;
+ for (auto & updateData : m_aVecUpdateData)
+ {
+ if (!updateData.aUpdateInfo.is() || updateData.aUpdateSource.is())
+ continue;
+ //We assume that m_aVecUpdateData contains only information about extensions which
+ //can be downloaded directly.
+ OSL_ASSERT(updateData.sWebsiteURL.isEmpty());
+
+ //update the name of the extension which is to be downloaded
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ sal_uInt16 prog = (sal::static_int_cast<sal_uInt16>(100) * ++count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size());
+ m_dialog.m_xStatusbar->set_percentage(prog);
+ }
+ dp_misc::DescriptionInfoset info(m_xComponentContext, updateData.aUpdateInfo);
+ //remember occurring exceptions in case we need to print out error information
+ std::vector< std::pair<OUString, css::uno::Exception> > vecExceptions;
+ css::uno::Sequence<OUString> seqDownloadURLs = info.getUpdateDownloadUrls();
+ OSL_ENSURE(seqDownloadURLs.hasElements(), "No download URL provided!");
+ for (sal_Int32 j = 0; j < seqDownloadURLs.getLength(); j++)
+ {
+ try
+ {
+ OSL_ENSURE(!seqDownloadURLs[j].isEmpty(), "Download URL is empty!");
+ bool bCancelled = download(seqDownloadURLs[j], updateData);
+ if (bCancelled || !updateData.sLocalURL.isEmpty())
+ break;
+ }
+ catch ( css::uno::Exception & e )
+ {
+ vecExceptions.emplace_back(seqDownloadURLs[j], e);
+ //There can be several different errors, for example, the URL is wrong, webserver cannot be reached,
+ //name cannot be resolved. The UCB helper API does not specify different special exceptions for these
+ //cases. Therefore ignore and continue.
+ continue;
+ }
+ }
+ //update the progress and display download error
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ if (updateData.sLocalURL.isEmpty())
+ {
+ //Construct a string of all messages contained in the exceptions plus the respective download URLs
+ OUStringBuffer buf(256);
+ size_t nPos = 0;
+ for (auto const& elem : vecExceptions)
+ {
+ if (nPos)
+ buf.append("\n");
+ buf.append("Could not download ");
+ buf.append(elem.first);
+ buf.append(". ");
+ buf.append(elem.second.Message);
+ ++nPos;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_DOWNLOAD, updateData.aInstalledPackage->getDisplayName(),
+ buf.makeStringAndClear());
+ }
+ }
+
+ }
+ }
+ catch (const css::uno::Exception & e)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(e.Message);
+ }
+}
+
+void UpdateInstallDialog::Thread::installExtensions()
+{
+ //Update the fix text in the dialog to "Installing extensions..."
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sInstalling);
+ m_dialog.m_xStatusbar->set_percentage(0);
+ }
+
+ sal_uInt16 count = 0;
+ for (auto const& updateData : m_aVecUpdateData)
+ {
+ //update the name of the extension which is to be installed
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ //we only show progress after an extension has been installed.
+ if (count > 0) {
+ m_dialog.m_xStatusbar->set_percentage(
+ (sal::static_int_cast<sal_uInt16>(100) * count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size()));
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ }
+ bool bError = false;
+ bool bLicenseDeclined = false;
+ css::uno::Reference<css::deployment::XPackage> xExtension;
+ css::uno::Exception exc;
+ try
+ {
+ css::uno::Reference< css::task::XAbortChannel > xAbortChannel(
+ updateData.aInstalledPackage->createAbortChannel() );
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_abort = xAbortChannel;
+ }
+ if (!updateData.aUpdateSource.is() && !updateData.sLocalURL.isEmpty())
+ {
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::makeAny(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv.get());
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv.get());
+ }
+ else if (updateData.aUpdateSource.is())
+ {
+ OSL_ASSERT(updateData.aUpdateSource.is());
+ //I am not sure if we should obtain the install properties and pass them into
+ //add extension. Currently it contains only "SUPPRESS_LICENSE". So it could happen
+ //that a license is displayed when updating from the shared repository, although the
+ //shared extension was installed using "SUPPRESS_LICENSE".
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::makeAny(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv.get());
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv.get());
+ }
+ }
+ catch (css::deployment::DeploymentException & de)
+ {
+ if (de.Cause.has<css::deployment::LicenseException>())
+ {
+ bLicenseDeclined = true;
+ }
+ else
+ {
+ exc = de.Cause.get<css::uno::Exception>();
+ bError = true;
+ }
+ }
+ catch (css::uno::Exception& e)
+ {
+ exc = e;
+ bError = true;
+ }
+
+ if (bLicenseDeclined)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_LICENSE_DECLINED,
+ updateData.aInstalledPackage->getDisplayName(), OUString());
+ }
+ else if (!xExtension.is() || bError)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_INSTALLATION,
+ updateData.aInstalledPackage->getDisplayName(), exc.Message);
+ }
+ ++count;
+ }
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xStatusbar->set_percentage(100);
+ m_dialog.m_xFt_extension_name->set_label(OUString());
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sFinished);
+ }
+}
+
+void UpdateInstallDialog::Thread::removeTempDownloads()
+{
+ if (!m_sDownloadFolder.isEmpty())
+ {
+ dp_misc::erase_path(m_sDownloadFolder,
+ css::uno::Reference<css::ucb::XCommandEnvironment>(),false /* no throw: ignore errors */ );
+ //remove also the temp file which we have used to create the unique name
+ OUString tempFile = m_sDownloadFolder.copy(0, m_sDownloadFolder.getLength() - 1);
+ dp_misc::erase_path(tempFile, css::uno::Reference<css::ucb::XCommandEnvironment>(),false);
+ m_sDownloadFolder.clear();
+ }
+}
+
+bool UpdateInstallDialog::Thread::download(OUString const & sDownloadURL, UpdateData & aUpdateData)
+{
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ }
+
+ OSL_ASSERT(m_sDownloadFolder.getLength());
+ OUString destFolder, tempEntry;
+ if (::osl::File::createTempFile(
+ &m_sDownloadFolder,
+ nullptr, &tempEntry ) != ::osl::File::E_None)
+ {
+ //ToDo feedback in window that download of this component failed
+ throw css::uno::Exception("Could not create temporary file in folder " + destFolder + ".", nullptr);
+ }
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( m_sDownloadFolder, tempEntry ) + "_";
+
+ ::ucbhelper::Content destFolderContent;
+ dp_misc::create_folder( &destFolderContent, destFolder, m_updateCmdEnv.get() );
+
+ ::ucbhelper::Content sourceContent;
+ (void)dp_misc::create_ucb_content(&sourceContent, sDownloadURL, m_updateCmdEnv.get());
+
+ const OUString sTitle( StrTitle::getTitle( sourceContent ) );
+
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ sTitle, css::ucb::NameClash::OVERWRITE );
+
+ {
+ //the user may have cancelled the dialog because downloading took too long
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ //all errors should be handled by the command environment.
+ aUpdateData.sLocalURL = destFolder + "/" + sTitle;
+ }
+
+ return m_stop;
+}
+
+UpdateCommandEnv::UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > const & xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread>const & thread)
+ : m_installThread(thread),
+ m_xContext(xCtx)
+{
+}
+
+// XCommandEnvironment
+css::uno::Reference<css::task::XInteractionHandler> UpdateCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+css::uno::Reference<css::ucb::XProgressHandler> UpdateCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+void UpdateCommandEnv::handle(
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == css::uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE("[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n\n");
+
+ css::deployment::VersionException verExc;
+ bool approve = false;
+
+ if (request >>= verExc)
+ { //We must catch the version exception during the update,
+ //because otherwise the user would be confronted with the dialogs, asking
+ //them if they want to replace an already installed version of the same extension.
+ //During an update we assume that we always want to replace the old version with the
+ //new version.
+ approve = true;
+ }
+
+ if (!approve)
+ {
+ //forward to interaction handler for main dialog.
+ handleInteractionRequest( m_xContext, xRequest );
+ }
+ else
+ {
+ // select:
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ css::uno::Reference< css::task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ css::uno::Reference< css::task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], css::uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+}
+
+// XProgressHandler
+void UpdateCommandEnv::push( css::uno::Any const & /*Status*/ )
+{
+}
+
+void UpdateCommandEnv::update( css::uno::Any const & /*Status */)
+{
+}
+
+void UpdateCommandEnv::pop()
+{
+}
+
+
+} //end namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
new file mode 100644
index 000000000..714bb9698
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEINSTALLDIALOG_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_DP_GUI_UPDATEINSTALLDIALOG_HXX
+
+#include <sal/config.h>
+#include <vcl/weld.hxx>
+#include <rtl/ref.hxx>
+#include <vector>
+
+/// @HTML
+namespace com::sun::star::deployment {
+ class XExtensionManager;
+}
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace vcl { class Window; }
+
+namespace dp_gui {
+
+ struct UpdateData;
+ class UpdateCommandEnv;
+
+
+/**
+ The modal &ldquo;Download and Installation&rdquo; dialog.
+*/
+class UpdateInstallDialog : public weld::GenericDialogController
+{
+public:
+ /**
+ Create an instance.
+
+ @param parent
+ the parent window, may be null
+ */
+ UpdateInstallDialog(weld::Window* parent, std::vector<UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx);
+
+ virtual ~UpdateInstallDialog() override;
+
+ virtual short run() override;
+
+private:
+ UpdateInstallDialog(UpdateInstallDialog const &) = delete;
+ UpdateInstallDialog& operator =(UpdateInstallDialog const &) = delete;
+
+ class Thread;
+ friend class Thread;
+ friend class UpdateCommandEnv;
+
+ DECL_LINK(cancelHandler, weld::Button&, void);
+
+ //signals in the dialog that we have finished.
+ void updateDone();
+ //Writes a particular error into the info listbox.
+ enum INSTALL_ERROR
+ {
+ ERROR_DOWNLOAD,
+ ERROR_INSTALLATION,
+ ERROR_LICENSE_DECLINED
+ };
+ void setError(INSTALL_ERROR err, OUString const & sExtension, OUString const & exceptionMessage);
+ void setError(OUString const & exceptionMessage);
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const
+ { return m_xExtensionManager; }
+
+ rtl::Reference< Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ //Signals that an error occurred during download and installation
+ bool m_bError;
+ bool m_bNoEntry;
+
+ OUString m_sInstalling;
+ OUString m_sFinished;
+ OUString m_sNoErrors;
+ OUString m_sErrorDownload;
+ OUString m_sErrorInstallation;
+ OUString m_sErrorLicenseDeclined;
+ OUString m_sNoInstall;
+ OUString m_sThisErrorOccurred;
+
+ std::unique_ptr<weld::Label> m_xFt_action;
+ std::unique_ptr<weld::ProgressBar> m_xStatusbar;
+ std::unique_ptr<weld::Label> m_xFt_extension_name;
+ std::unique_ptr<weld::TextView> m_xMle_info;
+ std::unique_ptr<weld::Button> m_xHelp;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xCancel;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.cxx b/desktop/source/deployment/gui/license_dialog.cxx
new file mode 100644
index 000000000..3e92f8cf6
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.cxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <comphelper/unwrapargs.hxx>
+#include <vcl/event.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/threadex.hxx>
+#include <vcl/weld.hxx>
+
+#include "license_dialog.hxx"
+
+#include <functional>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_gui {
+
+namespace {
+
+struct LicenseDialogImpl : public weld::GenericDialogController
+{
+ bool m_bLicenseRead;
+ Idle m_aResized;
+ AutoTimer m_aRepeat;
+
+ std::unique_ptr<weld::Label> m_xFtHead;
+ std::unique_ptr<weld::Widget> m_xArrow1;
+ std::unique_ptr<weld::Widget> m_xArrow2;
+ std::unique_ptr<weld::TextView> m_xLicense;
+ std::unique_ptr<weld::Button> m_xDown;
+ std::unique_ptr<weld::Button> m_xAcceptButton;
+ std::unique_ptr<weld::Button> m_xDeclineButton;
+
+ void PageDown();
+ DECL_LINK(ScrollTimerHdl, Timer*, void);
+ DECL_LINK(ScrolledHdl, weld::TextView&, void);
+ DECL_LINK(ResizedHdl, Timer*, void);
+ DECL_LINK(CancelHdl, weld::Button&, void);
+ DECL_LINK(AcceptHdl, weld::Button&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_STATIC_LINK(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool);
+ DECL_LINK(MousePressHdl, const MouseEvent&, bool);
+ DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ LicenseDialogImpl(weld::Window * pParent,
+ const OUString & sExtensionName,
+ const OUString & sLicenseText);
+
+ bool IsEndReached() const;
+};
+
+}
+
+LicenseDialogImpl::LicenseDialogImpl(
+ weld::Window * pParent,
+ const OUString & sExtensionName,
+ const OUString & sLicenseText)
+ : GenericDialogController(pParent, "desktop/ui/licensedialog.ui", "LicenseDialog")
+ , m_bLicenseRead(false)
+ , m_xFtHead(m_xBuilder->weld_label("head"))
+ , m_xArrow1(m_xBuilder->weld_widget("arrow1"))
+ , m_xArrow2(m_xBuilder->weld_widget("arrow2"))
+ , m_xLicense(m_xBuilder->weld_text_view("textview"))
+ , m_xDown(m_xBuilder->weld_button("down"))
+ , m_xAcceptButton(m_xBuilder->weld_button("ok"))
+ , m_xDeclineButton(m_xBuilder->weld_button("cancel"))
+{
+ m_xArrow1->show();
+ m_xArrow2->hide();
+
+ m_xLicense->connect_size_allocate(LINK(this, LicenseDialogImpl, SizeAllocHdl));
+ m_xLicense->set_size_request(m_xLicense->get_approximate_digit_width() * 72,
+ m_xLicense->get_height_rows(21));
+
+ m_xLicense->set_text(sLicenseText);
+ m_xFtHead->set_label(m_xFtHead->get_label() + "\n" + sExtensionName);
+
+ m_xAcceptButton->connect_clicked( LINK(this, LicenseDialogImpl, AcceptHdl) );
+ m_xDeclineButton->connect_clicked( LINK(this, LicenseDialogImpl, CancelHdl) );
+
+ m_xLicense->connect_vadjustment_changed(LINK(this, LicenseDialogImpl, ScrolledHdl));
+ m_xDown->connect_mouse_press(LINK(this, LicenseDialogImpl, MousePressHdl));
+ m_xDown->connect_mouse_release(LINK(this, LicenseDialogImpl, MouseReleaseHdl));
+ m_xDown->connect_key_press(LINK(this, LicenseDialogImpl, KeyInputHdl));
+ m_xDown->connect_key_release(LINK(this, LicenseDialogImpl, KeyReleaseHdl));
+
+ m_aRepeat.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
+ m_aRepeat.SetInvokeHandler(LINK(this, LicenseDialogImpl, ScrollTimerHdl));
+
+ m_aResized.SetPriority(TaskPriority::LOWEST);
+ m_aResized.SetInvokeHandler(LINK(this, LicenseDialogImpl, ResizedHdl));
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, SizeAllocHdl, const Size&, void)
+{
+ m_aResized.Start();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, AcceptHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, CancelHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+bool LicenseDialogImpl::IsEndReached() const
+{
+ return m_xLicense->vadjustment_get_value() + m_xLicense->vadjustment_get_page_size() >= m_xLicense->vadjustment_get_upper();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrolledHdl, weld::TextView&, void)
+{
+ if (IsEndReached())
+ {
+ m_xDown->set_sensitive(false);
+ m_aRepeat.Stop();
+
+ if (!m_bLicenseRead)
+ {
+ m_xAcceptButton->set_sensitive(true);
+ m_xAcceptButton->grab_focus();
+ m_xArrow1->hide();
+ m_xArrow2->show();
+ m_bLicenseRead = true;
+ }
+ }
+ else
+ m_xDown->set_sensitive(true);
+}
+
+void LicenseDialogImpl::PageDown()
+{
+ m_xLicense->vadjustment_set_value(m_xLicense->vadjustment_get_value() +
+ m_xLicense->vadjustment_get_page_size());
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK(LicenseDialogImpl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ if (aKeyCode.GetCode() == KEY_RETURN || aKeyCode.GetCode() == KEY_SPACE)
+ PageDown();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ResizedHdl, Timer*, void)
+{
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrollTimerHdl, Timer*, void)
+{
+ PageDown();
+}
+
+IMPL_STATIC_LINK_NOARG(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool)
+{
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MousePressHdl, const MouseEvent&, bool)
+{
+ PageDown();
+ m_aRepeat.Start();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ m_aRepeat.Stop();
+ return false;
+}
+
+LicenseDialog::LicenseDialog( Sequence<Any> const& args,
+ Reference<XComponentContext> const& )
+{
+ comphelper::unwrapArgs( args, m_parent, m_sExtensionName, m_sLicenseText );
+}
+
+// XExecutableDialog
+
+void LicenseDialog::setTitle( OUString const & )
+{
+}
+
+sal_Int16 LicenseDialog::execute()
+{
+ return vcl::solarthread::syncExecute(
+ std::bind(&LicenseDialog::solar_execute, this));
+}
+
+sal_Int16 LicenseDialog::solar_execute()
+{
+ LicenseDialogImpl dlg(Application::GetFrameWeld(m_parent), m_sExtensionName, m_sLicenseText);
+ return dlg.run();
+}
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.hxx b/desktop/source/deployment/gui/license_dialog.hxx
new file mode 100644
index 000000000..4cb615ef8
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_LICENSE_DIALOG_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_GUI_LICENSE_DIALOG_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+
+
+namespace dp_gui {
+
+class LicenseDialog
+ : public ::cppu::WeakImplHelper<css::ui::dialogs::XExecutableDialog>
+{
+ css::uno::Reference<css::awt::XWindow> /* const */ m_parent;
+ OUString m_sExtensionName;
+ OUString /* const */ m_sLicenseText;
+
+ sal_Int16 solar_execute();
+
+public:
+ LicenseDialog( css::uno::Sequence<css::uno::Any> const & args,
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext );
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle( OUString const & title ) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_dependencies.hxx b/desktop/source/deployment/inc/dp_dependencies.hxx
new file mode 100644
index 000000000..24c8a86fd
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_dependencies.hxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_DEPENDENCIES_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_DEPENDENCIES_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "dp_misc_api.hxx"
+
+/// @HTML
+
+namespace com::sun::star::xml::dom {
+ class XElement;
+}
+namespace dp_misc { class DescriptionInfoset; }
+
+namespace dp_misc {
+
+/**
+ Dependency handling.
+*/
+namespace Dependencies {
+ /**
+ Check for unsatisfied dependencies.
+
+ @param infoset
+ the infoset containing the dependencies to check
+
+ @return
+ a list of the unsatisfied dependencies from <code>infoset</code> (in no
+ specific order)
+ */
+ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence<
+ css::uno::Reference< css::xml::dom::XElement > >
+ check(dp_misc::DescriptionInfoset const & infoset);
+
+ /**
+ Obtain the (human-readable) error message of a failed dependency.
+
+ @param dependency
+ a dependency represented as a non-null XML element
+
+ @return
+ the name of the dependency; will never be empty, as a localized
+ &ldquo;unknown&rdquo; is substituted for an empty/missing name
+ */
+ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getErrorText(
+ css::uno::Reference< css::xml::dom::XElement >
+ const & dependency);
+}
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_descriptioninfoset.hxx b/desktop/source/deployment/inc/dp_descriptioninfoset.hxx
new file mode 100644
index 000000000..b404751fc
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_descriptioninfoset.hxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_DESCRIPTIONINFOSET_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_DESCRIPTIONINFOSET_HXX
+
+#include <sal/config.h>
+
+#include <optional>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/types.h>
+#include "dp_misc_api.hxx"
+
+/// @HTML
+
+namespace com::sun::star {
+ namespace uno { class XComponentContext; }
+ namespace xml {
+ namespace dom {
+ class XNode;
+ class XNodeList;
+ }
+ namespace xpath { class XXPathAPI; }
+ }
+}
+
+namespace dp_misc {
+
+struct DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SimpleLicenseAttributes
+{
+ OUString acceptBy;
+ //Attribute suppress-on-update. Default is false.
+ bool suppressOnUpdate;
+ //Attribute suppress-if-required. Default is false.
+ bool suppressIfRequired;
+};
+
+
+/**
+ Access to the content of an XML <code>description</code> element.
+
+ <p>This works for <code>description</code> elements in both the
+ <code>description.xml</code> file and online update information formats.</p>
+*/
+class DESKTOP_DEPLOYMENTMISC_DLLPUBLIC DescriptionInfoset {
+public:
+ /**
+ Create an instance.
+
+ @param context
+ a non-null component context
+
+ @param element
+ a <code>description</code> element; may be null (equivalent to an element
+ with no content)
+ */
+ DescriptionInfoset(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::xml::dom::XNode > const & element);
+
+ ~DescriptionInfoset();
+
+ /**
+ Return the identifier.
+
+ @return
+ the identifier, or an empty <code>optional</code> if none is specified
+ */
+ ::std::optional< OUString > getIdentifier() const;
+
+ /**
+ Return the textual version representation.
+
+ @return
+ textual version representation
+ */
+ OUString getVersion() const;
+
+ /**
+ Returns a list of supported platforms.
+
+ If the extension does not specify a platform by leaving out the platform element
+ then we assume that the extension supports all platforms. In this case the returned
+ sequence will have one element, which is &quot;all&quot;.
+ If the platform element is present but does not specify a platform then an empty
+ sequence is returned. Examples for invalid platform elements:
+ <pre>
+ <platform />, <platform value="" />, <platform value=",">
+ </pre>
+
+ The value attribute can contain various platform tokens. They must be separated by
+ commas.Each token will be stripped from leading and trailing white space (trim()).
+ */
+ css::uno::Sequence< OUString > getSupportedPlatforms() const;
+
+ /**
+ Returns the localized publisher name and the corresponding URL.
+
+ In case there is no publisher element then a pair of two empty strings is returned.
+ */
+ std::pair< OUString, OUString > getLocalizedPublisherNameAndURL() const;
+
+ /**
+ Returns the URL for the release notes corresponding to the office's locale.
+
+ In case there is no release-notes element then an empty string is returned.
+ */
+ OUString getLocalizedReleaseNotesURL() const;
+
+ /** returns the relative path to the license file.
+
+ In case there is no simple-license element then an empty string is returned.
+ */
+ OUString getLocalizedLicenseURL() const;
+
+ /** returns the attributes of the simple-license element
+
+ As long as there is a simple-license element, the function will return
+ the structure. If it does not exist, then the optional object is uninitialized.
+ */
+ ::std::optional<SimpleLicenseAttributes> getSimpleLicenseAttributes() const;
+
+ /** returns the localized display name of the extensions.
+
+ In case there is no localized display-name then an empty string is returned.
+ */
+ OUString getLocalizedDisplayName() const;
+
+ /**
+ returns the download website URL from the update information.
+
+ There can be multiple URLs where each is assigned to a particular locale.
+ The function returns the URL which locale matches best the one used in the office.
+
+ The return value is an optional because it may be necessary to find out if there
+ was a value provided or not. This is necessary to flag the extension in the update dialog
+ properly as "browser based update". The return value will only then not be initialized
+ if there is no <code>&lt;update-website&gt;</code>. If the element exists, then it must
+ have at least one child element containing a URL.
+
+ The <code>&lt;update-website&gt;</code> and <code>&lt;update-download&gt;</code>
+ elements are mutually exclusive.
+
+ @return
+ the download website URL, or an empty <code>optional</code> if none is
+ specified
+ */
+ ::std::optional< OUString > getLocalizedUpdateWebsiteURL() const;
+
+ /** returns the relative URL to the description.
+
+ The URL is relative to the root directory of the extensions.
+ */
+ OUString getLocalizedDescriptionURL() const;
+ /**
+ Return the dependencies.
+
+ @return
+ dependencies; will never be null
+ */
+ css::uno::Reference< css::xml::dom::XNodeList >
+ getDependencies() const;
+
+ /**
+ Return the update information URLs.
+
+ @return
+ update information URLs
+ */
+ css::uno::Sequence< OUString > getUpdateInformationUrls() const;
+
+ /**
+ Return the download URLs from the update information.
+
+ Because the <code>&lt;update-download&gt;</code> and the <code>&lt;update-website&gt;</code>
+ elements are mutually exclusive one may need to determine exactly if the element
+ was provided.
+
+ @return
+ download URLs
+ */
+ css::uno::Sequence< OUString > getUpdateDownloadUrls() const;
+
+ /**
+ Returns the URL for the icon image.
+ */
+ OUString getIconURL( bool bHighContrast ) const;
+
+ bool hasDescription() const;
+
+private:
+ SAL_DLLPRIVATE ::std::optional< OUString > getOptionalValue(
+ OUString const & expression) const;
+
+ SAL_DLLPRIVATE css::uno::Sequence< OUString > getUrls(
+ OUString const & expression) const;
+
+ /** Retrieves a child element which as lang attribute which matches the office locale.
+
+ Only top-level children are taken into account. It is also assumed that they are all
+ of the same element type and have a lang attribute. The matching algorithm is according
+ to RFC 3066, with the exception that only one variant is allowed.
+ @param parent
+ the expression used to obtain the parent of the localized children. It can be null.
+ Then a null reference is returned.
+ */
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode >
+ getLocalizedChild( OUString const & sParent) const;
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode>
+ matchLanguageTag(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent,
+ OUString const & rTag) const;
+
+ /** If there is no child element with a locale matching the office locale, then we use
+ the first child. In the case of the simple-license we also use the former default locale, which
+ was determined by the default-license-id (/description/registration/simple-license/@default-license-id)
+ and the license-id attributes (/description/registration/simple-license/license-text/@license-id).
+ However, since OOo 2.4 we use also the first child as default for the license
+ unless the two attributes are present.
+ */
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode>
+ getChildWithDefaultLocale(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent) const;
+ /**
+ @param out_bParentExists
+ indicates if the element node specified in sXPathParent exists.
+ */
+ SAL_DLLPRIVATE OUString getLocalizedHREFAttrFromChild(
+ OUString const & sXPathParent, bool * out_bParentExists) const;
+
+ /** Gets the node value for a given expression. The expression is used in
+ m_xpath-selectSingleNode. The value of the returned node is return value
+ of this function.
+ */
+ SAL_DLLPRIVATE OUString
+ getNodeValueFromExpression(OUString const & expression) const;
+
+ /** Check the extensions blacklist if additional extension meta data (e.g. dependencies)
+ are defined for this extension and have to be taken into account.
+ */
+ SAL_DLLPRIVATE void
+ checkBlacklist() const;
+
+ /** Helper method to compare the versions with the current version
+ */
+ SAL_DLLPRIVATE static bool
+ checkBlacklistVersion(const OUString& currentversion,
+ css::uno::Sequence< OUString > const & versions);
+
+ css::uno::Reference< css::uno::XComponentContext > m_context;
+ css::uno::Reference< css::xml::dom::XNode > m_element;
+ css::uno::Reference< css::xml::xpath::XXPathAPI > m_xpath;
+};
+
+inline bool DescriptionInfoset::hasDescription() const
+{
+ return m_element.is();
+}
+
+/** creates a DescriptionInfoset object.
+
+ The argument sExtensionFolderURL is a file URL to extension folder containing
+ the description.xml.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+DescriptionInfoset getDescriptionInfoset(OUString const & sExtensionFolderURL);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_identifier.hxx b/desktop/source/deployment/inc/dp_identifier.hxx
new file mode 100644
index 000000000..5d766d5b2
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_identifier.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_IDENTIFIER_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_IDENTIFIER_HXX
+
+#include <sal/config.h>
+
+#include <optional>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include "dp_misc_api.hxx"
+
+namespace com::sun::star::deployment {
+ class XPackage;
+}
+
+namespace dp_misc {
+
+/**
+ Generates an identifier from an optional identifier.
+
+ @param optional
+ an optional identifier
+
+ @param fileName
+ a file name
+
+ @return
+ the given optional identifier if present, otherwise a legacy identifier based
+ on the given file name
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateIdentifier(
+ ::std::optional< OUString > const & optional,
+ OUString const & fileName);
+
+/**
+ Gets the identifier of a package.
+
+ @param package
+ a non-null package
+
+ @return
+ the explicit identifier of the given package if present, otherwise the
+ implicit legacy identifier of the given package
+
+ @throws css::uno::RuntimeException
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getIdentifier(
+ css::uno::Reference< css::deployment::XPackage >
+ const & package);
+
+/**
+ Generates a legacy identifier based on a file name.
+
+ @param fileName
+ a file name
+
+ @return
+ a legacy identifier based on the given file name
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateLegacyIdentifier(
+ OUString const & fileName);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_interact.h b/desktop/source/deployment/inc/dp_interact.h
new file mode 100644
index 000000000..a908af3b5
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_interact.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_INTERACT_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_INTERACT_H
+
+#include <config_options.h>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include "dp_misc_api.hxx"
+
+namespace dp_misc
+{
+
+inline void progressUpdate(
+ OUString const & status,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ if (xCmdEnv.is()) {
+ css::uno::Reference<css::ucb::XProgressHandler> xProgressHandler(
+ xCmdEnv->getProgressHandler() );
+ if (xProgressHandler.is()) {
+ xProgressHandler->update( css::uno::makeAny(status) );
+ }
+ }
+}
+
+
+class ProgressLevel
+{
+ css::uno::Reference<css::ucb::XProgressHandler> m_xProgressHandler;
+
+public:
+ inline ~ProgressLevel();
+ inline ProgressLevel(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ OUString const & status );
+
+ inline void update( OUString const & status ) const;
+ inline void update( css::uno::Any const & status ) const;
+};
+
+
+inline ProgressLevel::ProgressLevel(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
+ OUString const & status )
+{
+ if (xCmdEnv.is())
+ m_xProgressHandler = xCmdEnv->getProgressHandler();
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->push( css::uno::makeAny(status) );
+}
+
+
+inline ProgressLevel::~ProgressLevel()
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->pop();
+}
+
+
+inline void ProgressLevel::update( OUString const & status ) const
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->update( css::uno::makeAny(status) );
+}
+
+
+inline void ProgressLevel::update( css::uno::Any const & status ) const
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->update( status );
+}
+
+
+
+/** @return true if ia handler is present and any selection has been chosen
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool interactContinuation(
+ css::uno::Any const & request,
+ css::uno::Type const & continuation,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool * pcont, bool * pabort );
+
+
+
+
+class UNLESS_MERGELIBS(DESKTOP_DEPLOYMENTMISC_DLLPUBLIC) AbortChannel :
+ public ::cppu::WeakImplHelper<css::task::XAbortChannel>
+{
+ bool m_aborted;
+ css::uno::Reference<css::task::XAbortChannel> m_xNext;
+
+public:
+ AbortChannel() : m_aborted( false ) {}
+ static AbortChannel * get(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel )
+ { return static_cast<AbortChannel *>(xAbortChannel.get()); }
+
+ bool isAborted() const { return m_aborted; }
+
+ // XAbortChannel
+ virtual void SAL_CALL sendAbort() override;
+
+ class SAL_DLLPRIVATE Chain
+ {
+ const ::rtl::Reference<AbortChannel> m_abortChannel;
+ public:
+ Chain(
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ css::uno::Reference<css::task::XAbortChannel> const & xNext )
+ : m_abortChannel( abortChannel )
+ { if (m_abortChannel.is()) m_abortChannel->m_xNext = xNext; }
+ ~Chain()
+ { if (m_abortChannel.is()) m_abortChannel->m_xNext.clear(); }
+ };
+ friend class Chain;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_misc_api.hxx b/desktop/source/deployment/inc/dp_misc_api.hxx
new file mode 100644
index 000000000..0f4d1b469
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_misc_api.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_MISC_API_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_MISC_API_HXX
+
+#include <sal/config.h>
+#include <sal/types.h>
+
+#if defined DESKTOP_DEPLOYMENTMISC_DLLIMPLEMENTATION
+#define DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SAL_DLLPUBLIC_EXPORT
+#else
+#define DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SAL_DLLPUBLIC_IMPORT
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_package.hxx b/desktop/source/deployment/inc/dp_package.hxx
new file mode 100644
index 000000000..cf788c40b
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_package.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PACKAGE_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PACKAGE_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace deployment { class XPackageRegistry; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_registry::backend::bundle {
+
+css::uno::Reference<css::deployment::XPackageRegistry> create(
+ css::uno::Reference<css::deployment::XPackageRegistry> const &
+ xRootRegistry,
+ OUString const & context, OUString const & cachePath,
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_persmap.h b/desktop/source/deployment/inc/dp_persmap.h
new file mode 100644
index 000000000..c42841c83
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_persmap.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PERSMAP_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PERSMAP_H
+
+#include <rtl/ustring.hxx>
+#include <osl/file.hxx>
+#include <unordered_map>
+
+namespace dp_misc
+{
+
+typedef std::unordered_map<
+ OString, OString > t_string2string_map;
+
+// Class to read obsolete registered extensions
+// should be removed for LibreOffice 4.0
+class PersistentMap final
+{
+ ::osl::File m_MapFile;
+ t_string2string_map m_entries;
+ bool m_bIsOpen;
+ bool m_bToBeCreated;
+ bool m_bIsDirty;
+
+public:
+ ~PersistentMap();
+ PersistentMap( OUString const & url );
+ /** in mem db */
+ PersistentMap();
+
+ bool has( OString const & key ) const;
+ bool get( OString * value, OString const & key ) const;
+ const t_string2string_map& getEntries() const { return m_entries; }
+ void put( OString const & key, OString const & value );
+ bool erase( OString const & key );
+
+private:
+ void open();
+ void readAll();
+ void add( OString const & key, OString const & value );
+ void flush();
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_platform.hxx b/desktop/source/deployment/inc/dp_platform.hxx
new file mode 100644
index 000000000..43c7ad8c5
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_platform.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PLATFORM_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_PLATFORM_HXX
+
+
+#include "dp_misc_api.hxx"
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustring.hxx>
+
+namespace dp_misc
+{
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString const & getPlatformString();
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+ bool platform_fits( OUString const & platform_string );
+
+/** determines if the current platform corresponds to one of the platform strings.
+
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool hasValidPlatform( css::uno::Sequence< OUString > const & platformStrings);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_registry.hxx b/desktop/source/deployment/inc/dp_registry.hxx
new file mode 100644
index 000000000..d58ec9a59
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_registry.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_REGISTRY_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_REGISTRY_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace deployment { class XPackageRegistry; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_registry {
+
+css::uno::Reference<css::deployment::XPackageRegistry> create(
+ OUString const & context, OUString const & cachePath,
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_resource.h b/desktop/source/deployment/inc/dp_resource.h
new file mode 100644
index 000000000..f7840819c
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_resource.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_RESOURCE_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_RESOURCE_H
+
+#include <i18nlangtag/languagetag.hxx>
+#include "dp_misc_api.hxx"
+
+namespace dp_misc {
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC const LanguageTag & getOfficeLanguageTag();
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_services.hxx b/desktop/source/deployment/inc/dp_services.hxx
new file mode 100644
index 000000000..f40f908de
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_services.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_SERVICES_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_SERVICES_HXX
+
+#include <sal/config.h>
+
+namespace comphelper::service_decl { class ServiceDecl; }
+
+namespace dp_info {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace dp_log {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace dp_manager {
+
+namespace factory {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+
+}
+
+namespace dp_registry::backend {
+
+namespace component {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace configuration {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace executable {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace help {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace script {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace sfwk {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/desktop/source/deployment/inc/dp_ucb.h b/desktop/source/deployment/inc/dp_ucb.h
new file mode 100644
index 000000000..abfad2992
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_ucb.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_UCB_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_UCB_H
+
+#include <vector>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "dp_misc_api.hxx"
+#include <ucbhelper/content.hxx>
+
+namespace ucbhelper
+{
+class Content;
+}
+
+namespace dp_misc {
+
+struct DESKTOP_DEPLOYMENTMISC_DLLPUBLIC StrTitle
+{
+ static css::uno::Sequence< OUString > getTitleSequence()
+ {
+ css::uno::Sequence<OUString> aSeq { "Title" };
+ return aSeq;
+ }
+ static OUString getTitle( ::ucbhelper::Content &rContent )
+ {
+ return rContent.getPropertyValue("Title").get<OUString>();
+ }
+ // just return titles - the ucbhelper should have a simpler API for this [!]
+ static css::uno::Reference< css::sdbc::XResultSet >
+ createCursor( ::ucbhelper::Content &rContent,
+ ucbhelper::ResultSetInclude eInclude )
+ {
+ return rContent.createCursor( StrTitle::getTitleSequence(), eInclude );
+ }
+};
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_ucb_content(
+ ::ucbhelper::Content * ucb_content,
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+/** @return true if previously non-existing folder has been created
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_folder(
+ ::ucbhelper::Content * ucb_content,
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool erase_path(
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool readLine( OUString * res, OUString const & startingWith,
+ ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc );
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
+ ::ucbhelper::Content & ucb_content);
+
+
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_update.hxx b/desktop/source/deployment/inc/dp_update.hxx
new file mode 100644
index 000000000..c0e5bb2c0
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_update.hxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_UPDATE_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_UPDATE_HXX
+
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+
+#include "dp_misc_api.hxx"
+
+#include <map>
+#include <vector>
+
+namespace dp_misc {
+
+/** returns the default update URL (for the update information) which
+ is used when an extension does not provide its own URL.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString getExtensionDefaultUpdateURL();
+
+enum UPDATE_SOURCE
+{
+ UPDATE_SOURCE_NONE,
+ UPDATE_SOURCE_SHARED,
+ UPDATE_SOURCE_BUNDLED,
+ UPDATE_SOURCE_ONLINE
+};
+
+/* determine if an update is available which is installed in the
+ user repository.
+
+ If the return value is UPDATE_SOURCE_NONE, then no update is
+ available, otherwise the return value determine from which the
+ repository the update is used.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UPDATE_SOURCE isUpdateUserExtension(
+ bool bReadOnlyShared,
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+/* determine if an update is available which is installed in the
+ shared repository.
+
+ If the return value is UPDATE_SOURCE_NONE, then no update is
+ available, otherwise the return value determine from which the
+ repository the update is used.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UPDATE_SOURCE isUpdateSharedExtension(
+ bool bReadOnlyShared,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+/* determines the extension with the highest identifier and returns it
+
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+css::uno::Reference< css::deployment::XPackage>
+getExtensionWithHighestVersion(
+ css::uno::Sequence<
+ css::uno::Reference<
+ css::deployment::XPackage> > const & seqExtensionsWithSameId);
+
+
+struct UpdateInfo
+{
+ UpdateInfo( css::uno::Reference< css::deployment::XPackage> const & ext);
+
+ css::uno::Reference< css::deployment::XPackage> extension;
+ //version of the update
+ OUString version;
+ css::uno::Reference< css::xml::dom::XNode > info;
+};
+
+typedef std::map< OUString, UpdateInfo > UpdateInfoMap;
+
+/*
+ @param extensionList
+ List of extension for which online update information is to be obtained. If NULL, then
+ for update information is obtained for all installed extension. There may be only one extension
+ with a particular identifier contained in the list. If one extension is installed
+ in several repositories, then the one with the highest version must be used, because it contains
+ the more recent URLs for getting the update information (if at all).
+ @param out_errors
+ the first member of the pair is the extension and the second the exception that was produced
+ when processing the extension.
+
+ @return
+ A map of UpdateInfo instances. If the parameter extensionList was given, then the map contains
+ at only information for those extensions.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UpdateInfoMap getOnlineUpdateInfos(
+ css::uno::Reference< css::uno::XComponentContext> const &xContext,
+ css::uno::Reference< css::deployment::XExtensionManager> const & xExtMgr,
+ css::uno::Reference< css::deployment::XUpdateInformationProvider > const & updateInformation,
+ std::vector< css::uno::Reference< css::deployment::XPackage > > const * extensionList,
+ std::vector< std::pair< css::uno::Reference<
+ css::deployment::XPackage>, css::uno::Any> > & out_errors);
+
+/* returns the highest version from the provided arguments.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString getHighestVersion(
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_version.hxx b/desktop/source/deployment/inc/dp_version.hxx
new file mode 100644
index 000000000..a74f5fdca
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_version.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_VERSION_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_VERSION_HXX
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include "dp_misc_api.hxx"
+
+
+namespace dp_misc {
+
+enum Order { LESS, EQUAL, GREATER };
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC Order compareVersions(
+ OUString const & version1, OUString const & version2);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_xml.h b/desktop/source/deployment/inc/dp_xml.h
new file mode 100644
index 000000000..1831e2b32
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_xml.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_XML_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_DP_XML_H
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+
+namespace ucbhelper
+{
+class Content;
+}
+
+namespace dp_misc
+{
+
+
+void xml_parse(
+ css::uno::Reference< css::xml::sax::XDocumentHandler > const & xDocHandler,
+ ::ucbhelper::Content & ucb_content,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/lockfile.hxx b/desktop/source/deployment/inc/lockfile.hxx
new file mode 100644
index 000000000..18a0f1a62
--- /dev/null
+++ b/desktop/source/deployment/inc/lockfile.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/* Information:
+ * This class implements a mechanism to lock a users installation directory,
+ * which is necessary because instances of staroffice could be running on
+ * different hosts while using the same directory thus causing data
+ * inconsistency.
+ * When an existing lock is detected, the user will be asked whether he wants
+ * to continue anyway, thus removing the lock and replacing it with a new one
+ *
+ * ideas:
+ * - store information about user and host and time in the lockfile and display
+ * these when asking whether to remove the lockfile.
+ * - periodically check the lockfile and warn the user when it gets replaced
+ *
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_LOCKFILE_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_LOCKFILE_HXX
+
+#include <rtl/ustring.hxx>
+
+#include "dp_misc_api.hxx"
+
+#define LOCKFILE_GROUP "Lockdata"
+#define LOCKFILE_USERKEY "User"
+#define LOCKFILE_HOSTKEY "Host"
+#define LOCKFILE_STAMPKEY "Stamp"
+#define LOCKFILE_TIMEKEY "Time"
+#define LOCKFILE_IPCKEY "IPCServer"
+
+namespace desktop {
+
+ class Lockfile;
+ bool Lockfile_execWarning( Lockfile const * that );
+
+ class DESKTOP_DEPLOYMENTMISC_DLLPUBLIC Lockfile
+ {
+ public:
+
+ // constructs a new lockfile object
+ Lockfile( bool bIPCserver = true );
+
+ // separating GUI code:
+ typedef bool (* fpExecWarning)( Lockfile const * that );
+
+ // checks the lockfile, asks user when lockfile is
+ // found (iff gui) and returns false when we may not continue
+ bool check( fpExecWarning execWarning );
+
+ // removes the lockfile
+ ~Lockfile();
+
+ private:
+ bool m_bIPCserver;
+ // full qualified name (file://-url) of the lockfile
+ OUString m_aLockname;
+ // flag whether the d'tor should delete the lock
+ bool m_bRemove;
+ bool m_bIsLocked;
+ // ID
+ OUString m_aId;
+ OUString m_aDate;
+ // access to data in file
+ void syncToFile() const;
+ bool isStale() const;
+ friend bool Lockfile_execWarning( Lockfile const * that );
+
+ };
+
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_INC_LOCKFILE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_activepackages.cxx b/desktop/source/deployment/manager/dp_activepackages.cxx
new file mode 100644
index 000000000..b301bc5ad
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.cxx
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_extensions.h>
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <osl/diagnose.h>
+#include <rtl/string.hxx>
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+#include "dp_activepackages.hxx"
+
+// Old format of database entry:
+// key: UTF8(filename)
+// value: UTF8(tempname ";" mediatype)
+// New format of database entry:
+// key: 0xFF UTF8(identifier)
+// value: UTF8(tempname) 0xFF UTF8(filename) 0xFF UTF8(mediatype)
+
+#if HAVE_FEATURE_EXTENSIONS
+
+namespace {
+
+constexpr const char separator[] = "\xff";
+
+OString oldKey(OUString const & fileName) {
+ return OUStringToOString(fileName, RTL_TEXTENCODING_UTF8);
+}
+
+OString newKey(OUString const & id) {
+ return separator + OUStringToOString(id, RTL_TEXTENCODING_UTF8);
+}
+
+::dp_manager::ActivePackages::Data decodeOldData(
+ OUString const & fileName, OString const & value)
+{
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i = value.indexOf(';');
+ OSL_ASSERT(i >= 0);
+ d.temporaryName = OUString(value.getStr(), i, RTL_TEXTENCODING_UTF8);
+ d.fileName = fileName;
+ d.mediaType = OUString(
+ value.getStr() + i + 1, value.getLength() - i - 1,
+ RTL_TEXTENCODING_UTF8);
+ return d;
+}
+
+::dp_manager::ActivePackages::Data decodeNewData(OString const & value) {
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i1 = value.indexOf(separator);
+ OSL_ASSERT(i1 >= 0);
+ d.temporaryName = OUString(
+ value.getStr(), i1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i2 = value.indexOf(separator, i1 + 1);
+ OSL_ASSERT(i2 >= 0);
+ d.fileName = OUString(
+ value.getStr() + i1 + 1, i2 - i1 - 1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i3 = value.indexOf(separator, i2 + 1);
+
+ if (i3 < 0)
+ {
+ //Before ActivePackages::Data::version was added
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, value.getLength() - i2 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ sal_Int32 i4 = value.indexOf(separator, i3 + 1);
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, i3 - i2 -1, RTL_TEXTENCODING_UTF8);
+ d.version = OUString(
+ value.getStr() + i3 + 1, i4 - i3 - 1,
+ RTL_TEXTENCODING_UTF8);
+ d.failedPrerequisites = OUString(
+ value.getStr() + i4 + 1, value.getLength() - i4 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ return d;
+}
+
+}
+#endif
+
+namespace dp_manager {
+
+ActivePackages::ActivePackages() {}
+
+ActivePackages::ActivePackages(OUString const & url)
+#if HAVE_FEATURE_EXTENSIONS
+ : m_map(url)
+#endif
+{
+#if !HAVE_FEATURE_EXTENSIONS
+ (void) url;
+#endif
+}
+
+ActivePackages::~ActivePackages() {}
+
+bool ActivePackages::has(
+ OUString const & id, OUString const & fileName) const
+{
+ return get(nullptr, id, fileName);
+}
+
+bool ActivePackages::get(
+ Data * data, OUString const & id, OUString const & fileName)
+ const
+{
+#if HAVE_FEATURE_EXTENSIONS
+ OString v;
+ if (m_map.get(&v, newKey(id))) {
+ if (data != nullptr) {
+ *data = decodeNewData(v);
+ }
+ return true;
+ } else if (m_map.get(&v, oldKey(fileName))) {
+ if (data != nullptr) {
+ *data = decodeOldData(fileName, v);
+ }
+ return true;
+ } else {
+ return false;
+ }
+#else
+ (void) data;
+ (void) id;
+ (void) fileName;
+ (void) this;
+ return false;
+#endif
+}
+
+ActivePackages::Entries ActivePackages::getEntries() const {
+ Entries es;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::t_string2string_map m(m_map.getEntries());
+ for (auto const& elem : m)
+ {
+ if (!elem.first.isEmpty() && elem.first[0] == separator[0]) {
+ es.emplace_back(
+ OUString(
+ elem.first.getStr() + 1, elem.first.getLength() - 1,
+ RTL_TEXTENCODING_UTF8),
+ decodeNewData(elem.second));
+ } else {
+ OUString fn(
+ OStringToOUString(elem.first, RTL_TEXTENCODING_UTF8));
+ es.emplace_back(
+ ::dp_misc::generateLegacyIdentifier(fn),
+ decodeOldData(fn, elem.second));
+ }
+ }
+#else
+ (void) this;
+#endif
+ return es;
+}
+
+void ActivePackages::put(OUString const & id, Data const & data) {
+#if HAVE_FEATURE_EXTENSIONS
+ OString b =
+ OUStringToOString(data.temporaryName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.fileName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.mediaType, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.version, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.failedPrerequisites, RTL_TEXTENCODING_UTF8);
+ m_map.put(newKey(id), b);
+#else
+ (void) id;
+ (void) data;
+ (void) this;
+#endif
+}
+
+void ActivePackages::erase(
+ OUString const & id, OUString const & fileName)
+{
+#if HAVE_FEATURE_EXTENSIONS
+ m_map.erase(newKey(id)) || m_map.erase(oldKey(fileName));
+#else
+ (void) id;
+ (void) fileName;
+ (void) this;
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_activepackages.hxx b/desktop/source/deployment/manager/dp_activepackages.hxx
new file mode 100644
index 000000000..62c169e1a
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.hxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_ACTIVEPACKAGES_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_ACTIVEPACKAGES_HXX
+
+#include <config_extensions.h>
+
+#include <sal/config.h>
+
+#include <utility>
+#include <vector>
+
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+
+
+namespace dp_manager {
+
+class ActivePackages {
+public:
+ struct Data {
+ Data(): failedPrerequisites("0")
+ {}
+ /* name of the temporary file (shared, user extension) or the name of
+ the folder of the bundled extension.
+ It does not contain the trailing '_' of the folder.
+ UTF-8 encoded
+ */
+ OUString temporaryName;
+ /* The file name (shared, user) or the folder name (bundled)
+ If the key is the file name, then file name is not encoded.
+ If the key is the identifier then the file name is UTF-8 encoded.
+ */
+ OUString fileName;
+ OUString mediaType;
+ OUString version;
+ /* If this string contains the value according to
+ css::deployment::Prerequisites or "0". That is, if
+ the value is > 0 then
+ the call to XPackage::checkPrerequisites failed.
+ In this case the extension must not be registered.
+ */
+ OUString failedPrerequisites;
+ };
+
+ typedef std::vector< std::pair< OUString, Data > > Entries;
+
+ ActivePackages();
+
+ explicit ActivePackages(OUString const & url);
+
+ ~ActivePackages();
+
+ bool has(OUString const & id, OUString const & fileName)
+ const;
+
+ bool get(
+ Data * data, OUString const & id,
+ OUString const & fileName) const;
+
+ Entries getEntries() const;
+
+ void put(OUString const & id, Data const & value);
+
+ void erase(OUString const & id, OUString const & fileName);
+
+private:
+ ActivePackages(ActivePackages const &) = delete;
+ ActivePackages& operator =(ActivePackages const &) = delete;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::PersistentMap m_map;
+#endif
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.cxx b/desktop/source/deployment/manager/dp_commandenvironments.cxx
new file mode 100644
index 000000000..755657a9d
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "dp_commandenvironments.hxx"
+#include <osl/diagnose.h>
+
+namespace deployment = com::sun::star::deployment;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+using ::com::sun::star::uno::Reference;
+
+namespace dp_manager {
+
+BaseCommandEnv::BaseCommandEnv()
+{
+}
+
+BaseCommandEnv::BaseCommandEnv(
+ Reference< task::XInteractionHandler> const & handler)
+ : m_forwardHandler(handler)
+{
+}
+
+BaseCommandEnv::~BaseCommandEnv()
+{
+}
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler> BaseCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference<ucb::XProgressHandler> BaseCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+void BaseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & /*xRequest*/ )
+{
+}
+
+void BaseCommandEnv::handle_(bool approve,
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ if (!approve)
+ {
+ //not handled so far -> forwarding
+ if (m_forwardHandler.is())
+ m_forwardHandler->handle(xRequest);
+ else
+ return; //cannot handle
+ }
+ else
+ {
+ // select:
+ uno::Sequence< Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ Reference< task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ Reference< task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+
+}
+
+// XProgressHandler
+void BaseCommandEnv::push( uno::Any const & /*Status*/ )
+{
+}
+
+void BaseCommandEnv::update( uno::Any const & /*Status */)
+{
+}
+
+void BaseCommandEnv::pop()
+{
+}
+
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
+{
+}
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void TmpRepositoryCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::VersionException verExc;
+ deployment::LicenseException licExc;
+ deployment::InstallException instExc;
+
+ bool approve = false;
+
+ if ((request >>= verExc)
+ || (request >>= licExc)
+ || (request >>= instExc))
+ {
+ approve = true;
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+LicenseCommandEnv::LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString const & repository):
+ BaseCommandEnv(handler), m_repository(repository),
+ m_bSuppressLicense(bSuppressLicense)
+{
+}
+// XInteractionHandler
+void LicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ if (m_bSuppressLicense
+ || m_repository == "bundled"
+ || licExc.AcceptBy == "admin")
+ {
+ //always approve in bundled case, because we do not support
+ //showing licenses anyway.
+ //The "admin" already accepted the license when installing the
+ // shared extension
+ approve = true;
+ }
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+NoLicenseCommandEnv::NoLicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void NoLicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ approve = true;
+ }
+ handle_(approve, xRequest);
+}
+
+SilentCheckPrerequisitesCommandEnv::SilentCheckPrerequisitesCommandEnv()
+{
+}
+
+void SilentCheckPrerequisitesCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+ deployment::PlatformException platformExc;
+ deployment::DependencyException depExc;
+
+ if (request >>= licExc)
+ {
+ handle_(true, xRequest); // approve = true
+ }
+ else if ((request >>= platformExc)
+ || (request >>= depExc))
+ {
+ m_Exception = request;
+ }
+ else
+ {
+ m_UnknownException = request;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.hxx b/desktop/source/deployment/manager/dp_commandenvironments.hxx
new file mode 100644
index 000000000..680e1e891
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.hxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_COMMANDENVIRONMENTS_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_COMMANDENVIRONMENTS_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+
+namespace dp_manager {
+
+/**
+ This command environment is to be used when an extension is temporarily
+ stored in the "tmp" repository. It prevents all kind of user interaction.
+ */
+class BaseCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ css::uno::Reference< css::task::XInteractionHandler> m_forwardHandler;
+protected:
+ void handle_(bool approve,
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest );
+public:
+ virtual ~BaseCommandEnv() override;
+ BaseCommandEnv();
+ explicit BaseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+class TmpRepositoryCommandEnv : public BaseCommandEnv
+{
+public:
+ TmpRepositoryCommandEnv();
+ explicit TmpRepositoryCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::synchronize.
+
+ It handles particular license cases.
+ */
+class LicenseCommandEnv : public BaseCommandEnv
+{
+private:
+ OUString m_repository;
+ bool m_bSuppressLicense;
+public:
+ LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString const & repository);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::checkPrerequisites
+
+ It always prohibits a license interaction
+ */
+class NoLicenseCommandEnv : public BaseCommandEnv
+{
+
+public:
+ explicit NoLicenseCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/* For use in XExtensionManager::addExtension in the call to
+ XPackage::checkPrerequisites
+ It prevents all user interactions. The license is always accepted.
+ It remembers if there was a platform or a dependency exception in
+ the member m_bException. if there was any other exception then m_bUnknownException
+ is set.
+
+ */
+class SilentCheckPrerequisitesCommandEnv : public BaseCommandEnv
+{
+public:
+ SilentCheckPrerequisitesCommandEnv();
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // Set to true if a PlatformException or a DependencyException were handled.
+ css::uno::Any m_Exception;
+ // Set to true if an unknown exception was handled.
+ css::uno::Any m_UnknownException;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.cxx b/desktop/source/deployment/manager/dp_extensionmanager.cxx
new file mode 100644
index 000000000..39bdbf65c
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.cxx
@@ -0,0 +1,1405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/compbase.hxx>
+
+#include <comphelper/servicedecl.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/bootstrap.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/thePackageManagerFactory.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/beans/Ambiguous.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <comphelper/sequence.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <osl/diagnose.h>
+#include <dp_interact.h>
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include "dp_extensionmanager.hxx"
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+#include <set>
+
+namespace lang = com::sun::star::lang;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+namespace beans = com::sun::star::beans;
+namespace util = com::sun::star::util;
+
+using ::com::sun::star::uno::Reference;
+
+namespace {
+
+struct CompIdentifiers
+{
+ bool operator() (std::vector<Reference<css::deployment::XPackage> > const & a,
+ std::vector<Reference<css::deployment::XPackage> > const & b)
+ {
+ return getName(a).compareTo(getName(b)) < 0;
+ }
+
+ static OUString getName(std::vector<Reference<css::deployment::XPackage> > const & a);
+};
+
+OUString CompIdentifiers::getName(std::vector<Reference<css::deployment::XPackage> > const & a)
+{
+ OSL_ASSERT(a.size() == 3);
+ //get the first non-null reference
+ Reference<css::deployment::XPackage> extension;
+ for (auto const& elem : a)
+ {
+ if (elem.is())
+ {
+ extension = elem;
+ break;
+ }
+ }
+ OSL_ASSERT(extension.is());
+ return extension->getDisplayName();
+}
+
+void writeLastModified(OUString & url, Reference<ucb::XCommandEnvironment> const & xCmdEnv, Reference< uno::XComponentContext > const & xContext)
+{
+ //Write the lastmodified file
+ try {
+ ::rtl::Bootstrap::expandMacros(url);
+ ::ucbhelper::Content ucbStamp(url, xCmdEnv, xContext);
+ dp_misc::erase_path( url, xCmdEnv );
+ OString stamp("1" );
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ ucbStamp.writeStream( xData, true /* replace existing */ );
+ }
+ catch(...)
+ {
+ uno::Any exc(::cppu::getCaughtException());
+ throw css::deployment::DeploymentException("Failed to update" + url, nullptr, exc);
+ }
+}
+
+class ExtensionRemoveGuard
+{
+ css::uno::Reference<css::deployment::XPackage> m_extension;
+ css::uno::Reference<css::deployment::XPackageManager> m_xPackageManager;
+
+public:
+ ExtensionRemoveGuard(){};
+ ExtensionRemoveGuard(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager):
+ m_extension(extension), m_xPackageManager(xPackageManager) {}
+ ~ExtensionRemoveGuard();
+
+ void set(css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager) {
+ m_extension = extension;
+ m_xPackageManager = xPackageManager;
+ }
+};
+
+ExtensionRemoveGuard::~ExtensionRemoveGuard()
+{
+ try {
+ OSL_ASSERT(!(m_extension.is() && !m_xPackageManager.is()));
+ if (m_xPackageManager.is() && m_extension.is())
+ m_xPackageManager->removePackage(
+ dp_misc::getIdentifier(m_extension), OUString(),
+ css::uno::Reference<css::task::XAbortChannel>(),
+ css::uno::Reference<css::ucb::XCommandEnvironment>());
+ } catch (...) {
+ OSL_ASSERT(false);
+ }
+}
+
+}
+
+namespace dp_manager {
+
+//ToDo: bundled extension
+ExtensionManager::ExtensionManager( Reference< uno::XComponentContext > const& xContext) :
+ ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager >(getMutex())
+ , m_xContext(xContext)
+{
+ m_xPackageManagerFactory = css::deployment::thePackageManagerFactory::get(m_xContext);
+ OSL_ASSERT(m_xPackageManagerFactory.is());
+
+ m_repositoryNames.emplace_back("user");
+ m_repositoryNames.emplace_back("shared");
+ m_repositoryNames.emplace_back("bundled");
+}
+
+ExtensionManager::~ExtensionManager()
+{
+}
+
+Reference<css::deployment::XPackageManager> ExtensionManager::getUserRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("user");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getSharedRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("shared");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBundledRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bundled");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getTmpRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("tmp");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBakRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bak");
+}
+
+Reference<task::XAbortChannel> ExtensionManager::createAbortChannel()
+{
+ return new dp_misc::AbortChannel;
+}
+
+css::uno::Reference<css::deployment::XPackageManager>
+ExtensionManager::getPackageManager(OUString const & repository)
+{
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else if (repository == "bundled")
+ xPackageManager = getBundledRepository();
+ else if (repository == "tmp")
+ xPackageManager = getTmpRepository();
+ else if (repository == "bak")
+ xPackageManager = getBakRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ return xPackageManager;
+}
+
+/*
+ Enters the XPackage objects into a map. They must be all from the
+ same repository. The value type of the map is a vector, where each vector
+ represents an extension with a particular identifier. The first member
+ represents the user extension, the second the shared extension and the
+ third the bundled extension.
+ */
+void ExtensionManager::addExtensionsToMap(
+ id2extensions & mapExt,
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ OUString const & repository)
+{
+ //Determine the index in the vector where these extensions are to be
+ //added.
+ int index = 0;
+ for (auto const& repositoryName : m_repositoryNames)
+ {
+ if (repositoryName == repository)
+ break;
+ ++index;
+ }
+
+ for (const Reference<css::deployment::XPackage>& xExtension : seqExt)
+ {
+ OUString id = dp_misc::getIdentifier(xExtension);
+ id2extensions::iterator ivec = mapExt.find(id);
+ if (ivec == mapExt.end())
+ {
+ std::vector<Reference<css::deployment::XPackage> > vec(3);
+ vec[index] = xExtension;
+ mapExt[id] = vec;
+ }
+ else
+ {
+ ivec->second[index] = xExtension;
+ }
+ }
+}
+
+/*
+ returns a list containing extensions with the same identifier from
+ all repositories (user, shared, bundled). If one repository does not
+ have this extension, then the list contains an empty Reference. The list
+ is ordered according to the priority of the repositories:
+ 1. user
+ 2. shared
+ 3. bundled
+
+ The number of elements is always three, unless the number of repository
+ changes.
+ */
+std::vector<Reference<css::deployment::XPackage> >
+ ExtensionManager::getExtensionsWithSameId(
+ OUString const & identifier, OUString const & fileName)
+
+{
+ std::vector<Reference<css::deployment::XPackage> > extensionList;
+ Reference<css::deployment::XPackageManager> lRepos[] = {
+ getUserRepository(), getSharedRepository(), getBundledRepository() };
+ for (int i(0); i != SAL_N_ELEMENTS(lRepos); ++i)
+ {
+ Reference<css::deployment::XPackage> xPackage;
+ try
+ {
+ xPackage = lRepos[i]->getDeployedPackage(
+ identifier, fileName, Reference<ucb::XCommandEnvironment>());
+ }
+ catch(const lang::IllegalArgumentException &)
+ {
+ // thrown if the extension does not exist in this repository
+ }
+ extensionList.push_back(xPackage);
+ }
+ OSL_ASSERT(extensionList.size() == 3);
+ return extensionList;
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> >
+ExtensionManager::getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & fileName,
+ Reference< ucb::XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ try
+ {
+ std::vector<Reference<css::deployment::XPackage> > listExtensions =
+ getExtensionsWithSameId(identifier, fileName);
+ bool bHasExtension = false;
+
+ //throw an IllegalArgumentException if there is no extension at all.
+ for (auto const& extension : listExtensions)
+ bHasExtension |= extension.is();
+ if (!bHasExtension)
+ throw lang::IllegalArgumentException(
+ "Could not find extension: " + identifier + ", " + fileName,
+ static_cast<cppu::OWeakObject*>(this), -1);
+
+ return comphelper::containerToSequence(listExtensions);
+ }
+ catch ( const css::deployment::DeploymentException & )
+ {
+ throw;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ throw;
+ }
+ catch (css::uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during getExtensionsWithSameIdentifier",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+bool ExtensionManager::isUserDisabled(
+ OUString const & identifier, OUString const & fileName)
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch ( const lang::IllegalArgumentException & ) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ return isUserDisabled( ::comphelper::containerToSequence(listExtensions) );
+}
+
+bool ExtensionManager::isUserDisabled(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExtSameId)
+{
+ OSL_ASSERT(seqExtSameId.getLength() == 3);
+ Reference<css::deployment::XPackage> const & userExtension = seqExtSameId[0];
+ if (userExtension.is())
+ {
+ beans::Optional<beans::Ambiguous<sal_Bool> > reg =
+ userExtension->isRegistered(Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ //If the value is ambiguous, then we assume that the extension
+ //is enabled, but something went wrong during enabling. We do not
+ //automatically disable user extensions.
+ if (reg.IsPresent &&
+ ! reg.Value.IsAmbiguous && ! reg.Value.Value)
+ return true;
+ }
+ return false;
+}
+
+/*
+ This method determines the active extension (XPackage.registerPackage) with a
+ particular identifier.
+
+ The parameter bUserDisabled determines if the user extension is disabled.
+
+ When the user repository contains an extension with the given identifier and
+ it is not disabled by the user, then it is always registered. Otherwise an
+ extension is only registered when there is no registered extension in one of
+ the repositories with a higher priority. That is, if the extension is from
+ the shared repository and an active extension with the same identifier is in
+ the user repository, then the extension is not registered. Similarly a
+ bundled extension is not registered if there is an active extension with the
+ same identifier in the shared or user repository.
+*/
+void ExtensionManager::activateExtension(
+ OUString const & identifier, OUString const & fileName,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch (const lang::IllegalArgumentException &) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ activateExtension(
+ ::comphelper::containerToSequence(listExtensions),
+ bUserDisabled, bStartup, xAbortChannel, xCmdEnv);
+
+ fireModified();
+}
+
+void ExtensionManager::activateExtension(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ bool bActive = false;
+ sal_Int32 len = seqExt.getLength();
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ Reference<css::deployment::XPackage> const & aExt = seqExt[i];
+ if (aExt.is())
+ {
+ //get the registration value of the current iteration
+ beans::Optional<beans::Ambiguous<sal_Bool> > optReg =
+ aExt->isRegistered(xAbortChannel, xCmdEnv);
+ //If nothing can be registered then break
+ if (!optReg.IsPresent)
+ break;
+
+ //Check if this is a disabled user extension,
+ if (i == 0 && bUserDisabled)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ continue;
+ }
+
+ //If we have already determined an active extension then we must
+ //make sure to unregister all extensions with the same id in
+ //repositories with a lower priority
+ if (bActive)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ else
+ {
+ //This is the first extension in the ordered list, which becomes
+ //the active extension
+ bActive = true;
+ //Register if not already done.
+ //reregister if the value is ambiguous, which indicates that
+ //something went wrong during last registration.
+ aExt->registerPackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ }
+ }
+}
+
+Reference<css::deployment::XPackage> ExtensionManager::backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ Reference<css::deployment::XPackageManager> const & xPackageManager,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xBackup;
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ Reference<css::deployment::XPackage> xOldExtension = xPackageManager->getDeployedPackage(
+ identifier, fileName, tmpCmdEnv);
+
+ if (xOldExtension.is())
+ {
+ xBackup = getTmpRepository()->addPackage(
+ xOldExtension->getURL(), uno::Sequence<beans::NamedValue>(),
+ OUString(), Reference<task::XAbortChannel>(), tmpCmdEnv);
+
+ OSL_ENSURE(xBackup.is(), "Failed to backup extension");
+ }
+ return xBackup;
+}
+
+//The supported package types are actually determined by the registry. However
+//creating a registry
+//(desktop/source/deployment/registry/dp_registry.cxx:PackageRegistryImpl) will
+//create all the backends, so that the registry can obtain from them the package
+//types. Creating the registry will also set up the registry folder containing
+//all the subfolders for the respective backends.
+//Because all repositories support the same backends, we can just delegate this
+//call to one of the repositories.
+uno::Sequence< Reference<css::deployment::XPackageTypeInfo> >
+ExtensionManager::getSupportedPackageTypes()
+{
+ return getUserRepository()->getSupportedPackageTypes();
+}
+//Do some necessary checks and user interaction. This function does not
+//acquire the extension manager mutex and that mutex must not be acquired
+//when this function is called. doChecksForAddExtension does synchronous
+//user interactions which may require acquiring the solar mutex.
+//Returns true if the extension can be installed.
+bool ExtensionManager::doChecksForAddExtension(
+ Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ uno::Sequence<beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<css::deployment::XPackage> & out_existingExtension )
+{
+ try
+ {
+ Reference<css::deployment::XPackage> xOldExtension;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ const OUString sDisplayName = xTmpExtension->getDisplayName();
+ const OUString sVersion = xTmpExtension->getVersion();
+
+ try
+ {
+ xOldExtension = xPackageMgr->getDeployedPackage(
+ sIdentifier, sFileName, xCmdEnv);
+ out_existingExtension = xOldExtension;
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ }
+ bool bCanInstall = false;
+
+ //This part is not guarded against other threads removing, adding, disabling ...
+ //etc. the same extension.
+ //checkInstall is safe because it notifies the user if the extension is not yet
+ //installed in the same repository. Because addExtension has its own guard
+ //(m_addMutex), another thread cannot add the extension in the meantime.
+ //checkUpdate is called if the same extension exists in the same
+ //repository. The user is asked if they want to replace it. Another
+ //thread
+ //could already remove the extension. So asking the user was not
+ //necessary. No harm is done. The other thread may also ask the user
+ //if he wants to remove the extension. This depends on the
+ //XCommandEnvironment which it passes to removeExtension.
+ if (xOldExtension.is())
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkUpdate(sVersion, sDisplayName,xOldExtension, xCmdEnv);
+ }
+ else
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkInstall(sDisplayName, xCmdEnv);
+ }
+ //Prevent showing the license if requested.
+ Reference<ucb::XCommandEnvironment> _xCmdEnv(xCmdEnv);
+ ExtensionProperties props(OUString(), properties, Reference<ucb::XCommandEnvironment>(), m_xContext);
+
+ dp_misc::DescriptionInfoset info(dp_misc::getDescriptionInfoset(xTmpExtension->getURL()));
+ const ::std::optional<dp_misc::SimpleLicenseAttributes> licenseAttributes =
+ info.getSimpleLicenseAttributes();
+
+ if (licenseAttributes && licenseAttributes->suppressIfRequired
+ && props.isSuppressedLicense())
+ _xCmdEnv.set(new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler()));
+
+ bCanInstall = xTmpExtension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, xOldExtension.is() || props.isExtensionUpdate()) == 0;
+
+ return bCanInstall;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: unexpected exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this));
+ }
+}
+
+// Only add to shared and user repository
+Reference<css::deployment::XPackage> ExtensionManager::addExtension(
+ OUString const & url, uno::Sequence<beans::NamedValue> const & properties,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xNewExtension;
+ //Determine the repository to use
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ //We must make sure that the xTmpExtension is not create twice, because this
+ //would remove the first one.
+ ::osl::MutexGuard addGuard(m_addMutex);
+
+ Reference<css::deployment::XPackageManager> xTmpRepository(getTmpRepository());
+ // make sure xTmpRepository is alive as long as xTmpExtension is; as
+ // the "tmp" manager is only held weakly by m_xPackageManagerFactory, it
+ // could otherwise be disposed early, which would in turn dispose
+ // xTmpExtension's PackageRegistryBackend behind its back
+ Reference<css::deployment::XPackage> xTmpExtension(
+ xTmpRepository->addPackage(
+ url, uno::Sequence<beans::NamedValue>(), OUString(), xAbortChannel,
+ new TmpRepositoryCommandEnv()));
+ if (!xTmpExtension.is()) {
+ throw css::deployment::DeploymentException(
+ ("Extension Manager: Failed to create temporary XPackage for url: "
+ + url),
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+
+ //Make sure the extension is removed from the tmp repository in case
+ //of an exception
+ ExtensionRemoveGuard tmpExtensionRemoveGuard(xTmpExtension, getTmpRepository());
+ ExtensionRemoveGuard bakExtensionRemoveGuard;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ Reference<css::deployment::XPackage> xOldExtension;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+
+ uno::Any excOccurred2;
+ bool bCanInstall = doChecksForAddExtension(
+ xPackageManager,
+ properties,
+ xTmpExtension,
+ xAbortChannel,
+ xCmdEnv,
+ xOldExtension );
+
+ {
+ bool bUserDisabled = false;
+ // In this guarded section (getMutex) we must not use the argument xCmdEnv
+ // because it may bring up dialogs (XInteractionHandler::handle) this
+ // may potentially deadlock. See issue
+ // http://qa.openoffice.org/issues/show_bug.cgi?id=114933
+ // By not providing xCmdEnv the underlying APIs will throw an exception if
+ // the XInteractionRequest cannot be handled.
+ ::osl::MutexGuard guard(getMutex());
+
+ if (bCanInstall)
+ {
+ try
+ {
+ bUserDisabled = isUserDisabled(sIdentifier, sFileName);
+ if (xOldExtension.is())
+ {
+ try
+ {
+ xOldExtension->revokePackage(
+ false, xAbortChannel, Reference<ucb::XCommandEnvironment>());
+ //save the old user extension in case the user aborts
+ xExtensionBackup = getBakRepository()->importExtension(
+ xOldExtension, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ bakExtensionRemoveGuard.set(xExtensionBackup, getBakRepository());
+ }
+ catch (const lang::DisposedException &)
+ {
+ //Another thread might have removed the extension meanwhile
+ }
+ }
+ //check again dependencies but prevent user interaction,
+ //We can disregard the license, because the user must have already
+ //accepted it, when we called checkPrerequisites the first time
+ SilentCheckPrerequisitesCommandEnv * pSilentCommandEnv =
+ new SilentCheckPrerequisitesCommandEnv();
+ Reference<ucb::XCommandEnvironment> silentCommandEnv(pSilentCommandEnv);
+
+ sal_Int32 failedPrereq = xTmpExtension->checkPrerequisites(
+ xAbortChannel, silentCommandEnv, true);
+ if (failedPrereq == 0)
+ {
+ xNewExtension = xPackageManager->addPackage(
+ url, properties, OUString(), xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+ //If we add a user extension and there is already one which was
+ //disabled by a user, then the newly installed one is enabled. If we
+ //add to another repository then the user extension remains
+ //disabled.
+ bool bUserDisabled2 = bUserDisabled;
+ if (repository == "user")
+ bUserDisabled2 = false;
+
+ // pass the two values via variables to workaround gcc-4.3.4 specific bug (bnc#655912)
+ OUString sNewExtensionIdentifier = dp_misc::getIdentifier(xNewExtension);
+ OUString sNewExtensionFileName = xNewExtension->getName();
+
+ activateExtension(
+ sNewExtensionIdentifier, sNewExtensionFileName,
+ bUserDisabled2, false, xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+ }
+ else
+ {
+ if (pSilentCommandEnv->m_Exception.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_Exception);
+ else if ( pSilentCommandEnv->m_UnknownException.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_UnknownException);
+ else
+ throw css::deployment::DeploymentException (
+ "Extension Manager: exception during addExtension, ckeckPrerequisites failed",
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred2 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during addExtension, url: "
+ + url, static_cast<OWeakObject*>(this), excOccurred2);
+ excOccurred2 <<= exc;
+ }
+ }
+
+ if (excOccurred2.hasValue())
+ {
+ //It does not matter what exception is thrown. We try to
+ //recover the original status.
+ //If the user aborted installation then a ucb::CommandAbortedException
+ //is thrown.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ }
+ activateExtension(
+ sIdentifier, sFileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred2);
+ }
+ } // leaving the guarded section (getMutex())
+
+ try
+ {
+ fireModified();
+
+ }catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: unexpected exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this));
+ }
+
+ return xNewExtension;
+}
+
+void ExtensionManager::removeExtension(
+ OUString const & identifier, OUString const & fileName,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ uno::Any excOccurred1;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ bool bUserDisabled = false;
+ ::osl::MutexGuard guard(getMutex());
+ try
+ {
+//Determine the repository to use
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(identifier, fileName);
+ //Backup the extension, in case the user cancels the action
+ xExtensionBackup = backupExtension(
+ identifier, fileName, xPackageManager, xCmdEnv);
+
+ //revoke the extension if it is active
+ Reference<css::deployment::XPackage> xOldExtension =
+ xPackageManager->getDeployedPackage(
+ identifier, fileName, xCmdEnv);
+ xOldExtension->revokePackage(false, xAbortChannel, xCmdEnv);
+
+ xPackageManager->removePackage(
+ identifier, fileName, xAbortChannel, xCmdEnv);
+ activateExtension(identifier, fileName, bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred1 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during removeEtension",
+ static_cast<OWeakObject*>(this), excOccurred1);
+ excOccurred1 <<= exc;
+ }
+
+ if (excOccurred1.hasValue())
+ {
+ //User aborted installation, restore the previous situation.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+ activateExtension(
+ identifier, fileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred1);
+ }
+
+ if (xExtensionBackup.is())
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+}
+
+// Only enable extensions from shared and user repository
+void ExtensionManager::enableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ ::osl::MutexGuard guard(getMutex());
+ bool bUserDisabled = false;
+ uno::Any excOccurred;
+ try
+ {
+ if (!extension.is())
+ return;
+ OUString repository = extension->getRepositoryName();
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(dp_misc::getIdentifier(extension),
+ extension->getName());
+
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), false, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+sal_Int32 ExtensionManager::checkPrerequisitesAndEnable(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ ::osl::MutexGuard guard(getMutex());
+ sal_Int32 ret = 0;
+ Reference<css::deployment::XPackageManager> mgr =
+ getPackageManager(extension->getRepositoryName());
+ ret = mgr->checkPrerequisites(extension, xAbortChannel, xCmdEnv);
+ if (ret)
+ {
+ //There are some unfulfilled prerequisites, try to revoke
+ extension->revokePackage(false, xAbortChannel, xCmdEnv);
+ }
+ const OUString id(dp_misc::getIdentifier(extension));
+ activateExtension(id, extension->getName(),
+ isUserDisabled(id, extension->getName()), false,
+ xAbortChannel, xCmdEnv);
+ return ret;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+void ExtensionManager::disableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ ::osl::MutexGuard guard(getMutex());
+ uno::Any excOccurred;
+ bool bUserDisabled = false;
+ try
+ {
+ if (!extension.is())
+ return;
+ const OUString repository( extension->getRepositoryName());
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ const OUString id(dp_misc::getIdentifier(extension));
+ bUserDisabled = isUserDisabled(id, extension->getName());
+
+ activateExtension(id, extension->getName(), true, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+uno::Sequence< Reference<css::deployment::XPackage> >
+ ExtensionManager::getDeployedExtensions(
+ OUString const & repository,
+ Reference<task::XAbortChannel> const &xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackages(
+ xAbort, xCmdEnv);
+}
+
+Reference<css::deployment::XPackage>
+ ExtensionManager::getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackage(
+ identifier, filename, xCmdEnv);
+}
+
+uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > >
+ ExtensionManager::getAllExtensions(
+ Reference<task::XAbortChannel> const & xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ id2extensions mapExt;
+
+ uno::Sequence<Reference<css::deployment::XPackage> > userExt =
+ getUserRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, userExt, "user");
+ uno::Sequence<Reference<css::deployment::XPackage> > sharedExt =
+ getSharedRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, sharedExt, "shared");
+ uno::Sequence<Reference<css::deployment::XPackage> > bundledExt =
+ getBundledRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, bundledExt, "bundled");
+
+ // Create the tmp repository to trigger its clean up (deletion
+ // of old temporary data.)
+ getTmpRepository();
+
+ //copy the values of the map to a vector for sorting
+ std::vector< std::vector<Reference<css::deployment::XPackage> > >
+ vecExtensions;
+ for (auto const& elem : mapExt)
+ vecExtensions.push_back(elem.second);
+
+ //sort the element according to the identifier
+ std::sort(vecExtensions.begin(), vecExtensions.end(), CompIdentifiers());
+
+ sal_Int32 j = 0;
+ uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > > seqSeq(vecExtensions.size());
+ for (auto const& elem : vecExtensions)
+ {
+ seqSeq[j++] = comphelper::containerToSequence(elem);
+ }
+ return seqSeq;
+
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Only to be called from unopkg or soffice bootstrap (with force=true in the
+// latter case):
+void ExtensionManager::reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+
+ std::set< OUString > disabledExts;
+ {
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ beans::Optional< beans::Ambiguous< sal_Bool > > registered(
+ package->isRegistered(xAbortChannel, xCmdEnv));
+ if (registered.IsPresent &&
+ !(registered.Value.IsAmbiguous ||
+ registered.Value.Value))
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ OSL_ASSERT(!id.isEmpty());
+ disabledExts.insert(id);
+ }
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ }
+
+ ::osl::MutexGuard guard(getMutex());
+ xPackageManager->reinstallDeployedPackages(
+ force, xAbortChannel, xCmdEnv);
+ //We must sync here, otherwise we will get exceptions when extensions
+ //are removed.
+ dp_misc::syncRepositories(force, xCmdEnv);
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ const OUString fileName = package->getName();
+ OSL_ASSERT(!id.isEmpty());
+ activateExtension(
+ id, fileName, disabledExts.find(id) != disabledExts.end(),
+ true, xAbortChannel, xCmdEnv );
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+sal_Bool ExtensionManager::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ ::osl::MutexGuard guard(getMutex());
+ OUString sSynchronizingShared(StrSyncRepository());
+ sSynchronizingShared = sSynchronizingShared.replaceAll("%NAME", "shared");
+ dp_misc::ProgressLevel progressShared(xCmdEnv, sSynchronizingShared);
+ bool bModified = getSharedRepository()->synchronize(xAbortChannel, xCmdEnv);
+ progressShared.update("\n\n");
+
+ OUString sSynchronizingBundled(StrSyncRepository());
+ sSynchronizingBundled = sSynchronizingBundled.replaceAll("%NAME", "bundled");
+ dp_misc::ProgressLevel progressBundled(xCmdEnv, sSynchronizingBundled);
+ bModified |= static_cast<bool>(getBundledRepository()->synchronize(xAbortChannel, xCmdEnv));
+ progressBundled.update("\n\n");
+
+ //Always determine the active extension.
+ //TODO: Is this still necessary? (It used to be necessary for the
+ // first-start optimization: The setup created the registration data
+ // for the bundled extensions (share/prereg/bundled) which was copied to
+ // the user installation when a user started OOo for the first time
+ // after running setup. All bundled extensions were registered at that
+ // moment. However, extensions with the same identifier could be in the
+ // shared or user repository, in which case the respective bundled
+ // extensions had to be revoked.)
+ try
+ {
+ const uno::Sequence<uno::Sequence<Reference<css::deployment::XPackage> > >
+ seqSeqExt = getAllExtensions(xAbortChannel, xCmdEnv);
+ for (uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt : seqSeqExt)
+ {
+ activateExtension(seqExt, isUserDisabled(seqExt), true,
+ xAbortChannel, xCmdEnv);
+ }
+ }
+ catch (...)
+ {
+ //We catch the exception, so we can write the lastmodified file
+ //so we will no repeat this every time OOo starts.
+ OSL_FAIL("Extensions Manager: synchronize");
+ }
+ OUString lastSyncBundled("$BUNDLED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncBundled, xCmdEnv, m_xContext);
+ OUString lastSyncShared("$SHARED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncShared, xCmdEnv, m_xContext);
+ return bModified;
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception in synchronize",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Notify the user when a new extension is to be installed. This is only the
+// case when one uses the system integration to install an extension (double
+// clicking on .oxt file etc.)). The function must only be called if there is no
+// extension with the same identifier already deployed. Then the checkUpdate
+// function will inform the user that the extension is about to be installed In
+// case the user cancels the installation a CommandFailed exception is
+// thrown.
+void ExtensionManager::checkInstall(
+ OUString const & displayName,
+ Reference<ucb::XCommandEnvironment> const & cmdEnv)
+{
+ uno::Any request(
+ css::deployment::InstallException(
+ "Extension " + displayName +
+ " is about to be installed.",
+ static_cast<OWeakObject *>(this), displayName));
+ bool approve = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ cmdEnv, &approve, &abort ))
+ {
+ OSL_ASSERT( !approve && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !approve)
+ throw ucb::CommandFailedException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+/* The function will make the user interaction in case there is an extension
+installed with the same id. This function may only be called if there is already
+an extension.
+*/
+void ExtensionManager::checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ Reference<css::deployment::XPackage> const & oldExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ // package already deployed, interact --force:
+ uno::Any request(
+ (css::deployment::VersionException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED ) + newDisplayName,
+ static_cast<OWeakObject *>(this), newVersion, newDisplayName,
+ oldExtension ) ) );
+ bool replace = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ xCmdEnv, &replace, &abort )) {
+ OSL_ASSERT( !replace && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(
+ RID_STR_ERROR_WHILE_ADDING) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !replace)
+ throw ucb::CommandFailedException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> > SAL_CALL
+ExtensionManager::getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+ ::osl::MutexGuard guard(getMutex());
+ return xPackageManager->getExtensionsWithUnacceptedLicenses(xCmdEnv);
+}
+
+sal_Bool ExtensionManager::isReadOnlyRepository(OUString const & repository)
+{
+ return getPackageManager(repository)->isReadOnly();
+}
+
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<ExtensionManager> const servicePIP;
+sdecl::ServiceDecl const serviceDecl(
+ servicePIP,
+ // a private one:
+ "com.sun.star.comp.deployment.ExtensionManager",
+ "com.sun.star.comp.deployment.ExtensionManager");
+
+// XModifyBroadcaster
+
+void ExtensionManager::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void ExtensionManager::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void ExtensionManager::check()
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "ExtensionManager instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+void ExtensionManager::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+} // namespace dp_manager
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.hxx b/desktop/source/deployment/manager/dp_extensionmanager.hxx
new file mode 100644
index 000000000..2432279c3
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.hxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_EXTENSIONMANAGER_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_EXTENSIONMANAGER_HXX
+
+#include <strings.hrc>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <osl/mutex.hxx>
+#include <vector>
+#include <unordered_map>
+
+namespace dp_manager {
+
+typedef std::unordered_map<
+ OUString,
+ std::vector<css::uno::Reference<css::deployment::XPackage> > > id2extensions;
+
+class ExtensionManager : private ::dp_misc::MutexHolder,
+ public ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager >
+{
+public:
+ explicit ExtensionManager( css::uno::Reference< css::uno::XComponentContext >const& xContext);
+ virtual ~ExtensionManager() override;
+
+ void check();
+ void fireModified();
+
+public:
+
+// XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+//XExtensionManager
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addExtension(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removeExtension(
+ OUString const & identifier,
+ OUString const & filename,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL enableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisitesAndEnable(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedExtensions(
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference< css::deployment::XPackage>
+ SAL_CALL getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > >
+ SAL_CALL getAllExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ css::uno::Reference< css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Bool SAL_CALL isReadOnlyRepository(OUString const & repository) override;
+
+private:
+
+ static OUString StrSyncRepository() { return DpResId(RID_STR_SYNCHRONIZING_REPOSITORY); }
+
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ css::uno::Reference<css::deployment::XPackageManagerFactory> m_xPackageManagerFactory;
+
+ //only to be used within addExtension
+ ::osl::Mutex m_addMutex;
+ /* contains the names of all repositories (except tmp) in order of there
+ priority. That is, the first element is "user" followed by "shared" and
+ then "bundled"
+ */
+ std::vector< OUString > m_repositoryNames;
+
+ css::uno::Reference<css::deployment::XPackageManager> getUserRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getSharedRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBundledRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getTmpRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBakRepository();
+
+ bool isUserDisabled(OUString const & identifier,
+ OUString const & filename);
+
+ static bool isUserDisabled(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExtSameId);
+
+ void activateExtension(
+ OUString const & identifier,
+ OUString const & fileName,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ static void activateExtension(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+ std::vector<css::uno::Reference<css::deployment::XPackage> >
+ getExtensionsWithSameId(OUString const & identifier,
+ OUString const & fileName);
+
+ css::uno::Reference<css::deployment::XPackage> backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void checkInstall(
+ OUString const & displayName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & cmdEnv);
+
+ void checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ css::uno::Reference<css::deployment::XPackage> const & oldExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void addExtensionsToMap(
+ id2extensions & mapExt,
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ OUString const & repository);
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference<css::deployment::XPackageManager>
+ getPackageManager(OUString const & repository);
+
+ /// @throws css::deployment::DeploymentException
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::ucb::CommandAbortedException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool doChecksForAddExtension(
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::deployment::XPackage> & out_existingExtension );
+
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_informationprovider.cxx b/desktop/source/deployment/manager/dp_informationprovider.cxx
new file mode 100644
index 000000000..bde956937
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_informationprovider.cxx
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/servicedecl.hxx>
+
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/XPackageInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/content.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_services.hxx>
+#include <dp_version.hxx>
+#include <dp_update.hxx>
+
+namespace beans = com::sun::star::beans ;
+namespace deployment = com::sun::star::deployment ;
+namespace lang = com::sun::star::lang ;
+namespace task = com::sun::star::task ;
+namespace css_ucb = com::sun::star::ucb ;
+namespace uno = com::sun::star::uno ;
+namespace xml = com::sun::star::xml ;
+
+
+namespace dp_info {
+
+namespace {
+
+class PackageInformationProvider :
+ public ::cppu::WeakImplHelper< deployment::XPackageInformationProvider >
+
+{
+ public:
+ explicit PackageInformationProvider( uno::Reference< uno::XComponentContext >const& xContext);
+
+ // XPackageInformationProvider
+ virtual OUString SAL_CALL getPackageLocation( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL isUpdateAvailable( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL getExtensionList() override;
+
+private:
+
+ uno::Reference< uno::XComponentContext> mxContext;
+
+ OUString getPackageLocation( const OUString& repository,
+ const OUString& _sExtensionId );
+
+ uno::Reference< deployment::XUpdateInformationProvider > mxUpdateInformation;
+};
+
+}
+
+PackageInformationProvider::PackageInformationProvider( uno::Reference< uno::XComponentContext > const& xContext) :
+ mxContext( xContext ),
+ mxUpdateInformation( deployment::UpdateInformationProvider::create( xContext ) )
+{
+}
+
+OUString PackageInformationProvider::getPackageLocation(
+ const OUString & repository,
+ const OUString& _rExtensionId )
+{
+ OUString aLocationURL;
+ uno::Reference<deployment::XExtensionManager> xManager =
+ deployment::ExtensionManager::get(mxContext);
+
+ if ( xManager.is() )
+ {
+ const uno::Sequence< uno::Reference< deployment::XPackage > > packages(
+ xManager->getDeployedExtensions(
+ repository,
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () ) );
+
+ for ( int pos = packages.getLength(); pos--; )
+ {
+ try
+ {
+ const beans::Optional< OUString > aID = packages[ pos ]->getIdentifier();
+ if ( aID.IsPresent && (aID.Value == _rExtensionId ) )
+ {
+ aLocationURL = packages[ pos ]->getURL();
+ break;
+ }
+ }
+ catch ( uno::RuntimeException & ) {}
+ }
+ }
+
+ return aLocationURL;
+}
+
+
+OUString SAL_CALL
+PackageInformationProvider::getPackageLocation( const OUString& _sExtensionId )
+{
+ OUString aLocationURL = getPackageLocation( "user", _sExtensionId );
+
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "shared", _sExtensionId );
+ }
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "bundled", _sExtensionId );
+ }
+ if ( !aLocationURL.isEmpty() )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( aLocationURL, nullptr, mxContext );
+ aLocationURL = aContent.getURL();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ }
+ }
+ return aLocationURL;
+}
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL
+PackageInformationProvider::isUpdateAvailable( const OUString& _sExtensionId )
+{
+ uno::Sequence< uno::Sequence< OUString > > aList;
+
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!extMgr.is())
+ {
+ OSL_ASSERT(false);
+ return aList;
+ }
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+ dp_misc::UpdateInfoMap updateInfoMap;
+ if (!_sExtensionId.isEmpty())
+ {
+ std::vector<uno::Reference<deployment::XPackage> > vecExtensions;
+ uno::Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = dp_misc::getExtensionWithHighestVersion(
+ extMgr->getExtensionsWithSameIdentifier(
+ _sExtensionId, _sExtensionId, uno::Reference<css_ucb::XCommandEnvironment>()));
+ vecExtensions.push_back(extension);
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ OSL_ASSERT(false);
+ }
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, &vecExtensions, errors);
+ }
+ else
+ {
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, nullptr, errors);
+ }
+
+ int nCount = 0;
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+
+ OUString sOnlineVersion;
+ if (info.info.is())
+ {
+ // check, if there are unsatisfied dependencies and ignore this online update
+ dp_misc::DescriptionInfoset infoset(mxContext, info.info);
+ uno::Sequence< uno::Reference< xml::dom::XElement > >
+ ds( dp_misc::Dependencies::check( infoset ) );
+ if ( ! ds.hasElements() )
+ sOnlineVersion = info.version;
+ }
+
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<css_ucb::XCommandEnvironment>());
+ } catch (const lang::IllegalArgumentException&) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ OUString updateVersionUser;
+ OUString updateVersionShared;
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionUser = dp_misc::getHighestVersion(
+ sVersionShared, sVersionBundled, sOnlineVersion);
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionShared = dp_misc::getHighestVersion(
+ OUString(), sVersionBundled, sOnlineVersion);
+ OUString updateVersion;
+ if (dp_misc::compareVersions(updateVersionUser, updateVersionShared) == dp_misc::GREATER)
+ updateVersion = updateVersionUser;
+ else
+ updateVersion = updateVersionShared;
+ if (!updateVersion.isEmpty())
+ {
+
+ OUString aNewEntry[2];
+ aNewEntry[0] = updateInfo.first;
+ aNewEntry[1] = updateVersion;
+ aList.realloc( ++nCount );
+ aList[ nCount-1 ] = ::uno::Sequence< OUString >( aNewEntry, 2 );
+ }
+ }
+ return aList;
+}
+
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL PackageInformationProvider::getExtensionList()
+{
+ const uno::Reference<deployment::XExtensionManager> mgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!mgr.is())
+ return uno::Sequence< uno::Sequence< OUString > >();
+
+ const uno::Sequence< uno::Sequence< uno::Reference<deployment::XPackage > > >
+ allExt = mgr->getAllExtensions(
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () );
+
+ uno::Sequence< uno::Sequence< OUString > > retList;
+
+ sal_Int32 cAllIds = allExt.getLength();
+ retList.realloc(cAllIds);
+
+ for (sal_Int32 i = 0; i < cAllIds; i++)
+ {
+ //The inner sequence contains extensions with the same identifier from
+ //all the different repositories, that is user, share, bundled.
+ const uno::Sequence< uno::Reference< deployment::XPackage > > &
+ seqExtension = allExt[i];
+ sal_Int32 cExt = seqExtension.getLength();
+ OSL_ASSERT(cExt == 3);
+ for (sal_Int32 j = 0; j < cExt; j++)
+ {
+ //ToDo according to the old code the first found extension is used
+ //even if another one with the same id has a better version.
+ uno::Reference< deployment::XPackage > const & xExtension( seqExtension[j] );
+ if (xExtension.is())
+ {
+ OUString aNewEntry[2];
+ aNewEntry[0] = dp_misc::getIdentifier(xExtension);
+ aNewEntry[1] = xExtension->getVersion();
+ retList[i] = ::uno::Sequence< OUString >( aNewEntry, 2 );
+ break;
+ }
+ }
+ }
+ return retList;
+}
+
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<PackageInformationProvider> const servicePIP;
+sdecl::ServiceDecl const serviceDecl(
+ servicePIP,
+ // a private one:
+ "com.sun.star.comp.deployment.PackageInformationProvider",
+ "com.sun.star.comp.deployment.PackageInformationProvider" );
+
+} // namespace dp_info
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.cxx b/desktop/source/deployment/manager/dp_manager.cxx
new file mode 100644
index 000000000..33d8e6468
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.cxx
@@ -0,0 +1,1603 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <dp_interact.h>
+#include <dp_registry.hxx>
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include <dp_ucb.h>
+#include <dp_platform.hxx>
+#include "dp_manager.h"
+#include <dp_identifier.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/logging.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <comphelper/sequence.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/logging/FileHandler.hpp>
+#include <com/sun/star/logging/SimpleTextFormatter.hpp>
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <unotools/tempfile.hxx>
+
+#include <dp_descriptioninfoset.hxx>
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::logging;
+
+namespace dp_log {
+extern comphelper::service_decl::ServiceDecl const serviceDecl;
+}
+
+namespace dp_manager {
+
+namespace {
+
+struct MatchTempDir
+{
+ OUString m_str;
+ explicit MatchTempDir( OUString const & str ) : m_str( str ) {}
+ bool operator () ( ActivePackages::Entries::value_type const & v ) const {
+ return v.second.temporaryName.equalsIgnoreAsciiCase( m_str );
+ }
+};
+
+OUString getExtensionFolder(OUString const & parentFolder,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext)
+{
+ ::ucbhelper::Content tempFolder( parentFolder, xCmdEnv, xContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ OUString title;
+ if (xResultSet->next())
+ {
+ title = Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ;
+ }
+ return title;
+}
+}
+
+void PackageManagerImpl::initActivationLayer(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_activePackages.isEmpty())
+ {
+ OSL_ASSERT( m_registryCache.isEmpty() );
+ // documents temp activation:
+ m_activePackagesDB.reset( new ActivePackages );
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, m_context, xCmdEnv,
+ false /* no throw */ ))
+ {
+ // scan for all entries in m_packagesDir:
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (ucbContent, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
+
+ while (xResultSet->next())
+ {
+ Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ OUString title( xRow->getString( 1 /* Title */ ) );
+ // xxx todo: remove workaround for tdoc
+ if ( title == "this_is_a_dummy_stream_just_there_as_a_workaround_for_a_temporary_limitation_of_the_storage_api_implementation" )
+ continue;
+ if ( title == "META-INF" )
+ continue;
+
+ ::ucbhelper::Content sourceContent(
+ Reference<XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, m_xComponentContext );
+
+ OUString mediaType( detectMediaType( sourceContent,
+ false /* no throw */) );
+ if (!mediaType.isEmpty())
+ {
+ ActivePackages::Data dbData;
+ insertToActivationLayer(
+ Sequence<css::beans::NamedValue>(),mediaType, sourceContent,
+ title, &dbData );
+
+ insertToActivationLayerDB( title, dbData );
+ //TODO #i73136#: insertToActivationLayerDB needs id not
+ // title, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently
+ // does not work, anyway.
+ }
+ }
+ }
+ }
+ else
+ {
+ // user|share:
+ OSL_ASSERT( !m_activePackages.isEmpty() );
+ m_activePackages_expanded = expandUnoRcUrl( m_activePackages );
+ m_registrationData_expanded = expandUnoRcUrl(m_registrationData);
+ if (!m_readOnly)
+ create_folder( nullptr, m_activePackages_expanded, xCmdEnv);
+
+ OUString dbName;
+ if (m_context == "user")
+ dbName = m_activePackages_expanded + ".pmap";
+ else
+ {
+ // Create the extension data base in the user installation
+ create_folder( nullptr, m_registrationData_expanded, xCmdEnv);
+ dbName = m_registrationData_expanded + "/extensions.pmap";
+ }
+ // The data base can always be written because it is always in the user installation
+ m_activePackagesDB.reset( new ActivePackages( dbName ) );
+
+ if (! m_readOnly && m_context != "bundled")
+ {
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder,
+ ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
+
+ // get all temp directories:
+ std::vector<OUString> tempEntries;
+ std::vector<OUString> removedEntries;
+ while (xResultSet->next())
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ if (title.endsWith("removed", &title))
+ {
+ //save the file name without the "removed" part
+ removedEntries.push_back(::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ tempEntries.push_back( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ }
+
+ bool bShared = (m_context == "shared");
+ for (const OUString & tempEntry : tempEntries)
+ {
+ const MatchTempDir match( tempEntry );
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+ const OUString url(
+ makeURL(m_activePackages_expanded, tempEntry ) );
+
+ //In case of shared extensions, new entries are regarded as
+ //added extensions if there is no xxx.tmpremoved file.
+ if (bShared)
+ {
+ if (std::find(removedEntries.begin(), removedEntries.end(), tempEntry) ==
+ removedEntries.end())
+ {
+ continue;
+ }
+ else
+ {
+ //Make sure only the same user removes the extension, who
+ //previously unregistered it. This is avoid races if multiple instances
+ //of OOo are running which all have write access to the shared installation.
+ //For example, a user removes the extension, but keeps OOo
+ //running. Parts of the extension may still be loaded and used by OOo.
+ //Therefore the extension is only deleted the next time the extension manager is
+ //run after restarting OOo. While OOo is still running, another user starts OOo
+ //which would deleted the extension files. If the same user starts another
+ //instance of OOo then the lock file will prevent this.
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ ucbhelper::Content remFileContent(
+ url + "removed", Reference<XCommandEnvironment>(), m_xComponentContext);
+ std::vector<sal_Int8> data = dp_misc::readFile(remFileContent);
+ OString osData(reinterpret_cast<const char*>(data.data()),
+ data.size());
+ OUString sData = OStringToOUString(
+ osData, RTL_TEXTENCODING_UTF8);
+ if (sData != aUserName)
+ continue;
+ }
+ }
+ // temp entry not needed anymore:
+ erase_path( url + "_",
+ Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //delete the xxx.tmpremoved file
+ erase_path(url + "removed",
+ Reference<XCommandEnvironment>(), false);
+ }
+ }
+ }
+ }
+}
+
+
+void PackageManagerImpl::initRegistryBackends()
+{
+ if (!m_registryCache.isEmpty())
+ create_folder( nullptr, m_registryCache,
+ Reference<XCommandEnvironment>(), false);
+ m_xRegistry.set( ::dp_registry::create(
+ m_context, m_registryCache,
+ m_xComponentContext ) );
+}
+
+namespace {
+
+osl::FileBase::RC createDirectory(OUString const & url) {
+ auto e = osl::Directory::create(url);
+ if (e != osl::FileBase::E_NOENT) {
+ return e;
+ }
+ INetURLObject o(url);
+ if (!o.removeSegment()) {
+ return osl::FileBase::E_INVAL; // anything but E_None/E_EXIST
+ }
+ e = createDirectory(o.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
+ return e;
+ }
+ return osl::Directory::create(url);
+}
+
+bool isMacroURLReadOnly( const OUString &rMacro )
+{
+ OUString aDirURL( rMacro );
+ ::rtl::Bootstrap::expandMacros( aDirURL );
+
+ ::osl::FileBase::RC aErr = createDirectory( aDirURL );
+ if ( aErr == ::osl::FileBase::E_None )
+ return false; // it will be writeable
+ if ( aErr != ::osl::FileBase::E_EXIST )
+ return true; // some serious problem creating it
+
+ bool bError;
+ sal_uInt64 nWritten = 0;
+ OUString aFileURL( aDirURL + "/stamp.sys" );
+ ::osl::File aFile( aFileURL );
+
+ bError = aFile.open( osl_File_OpenFlag_Read |
+ osl_File_OpenFlag_Write |
+ osl_File_OpenFlag_Create ) != ::osl::FileBase::E_None;
+ if (!bError)
+ bError = aFile.write( "1", 1, nWritten ) != ::osl::FileBase::E_None;
+ if (aFile.close() != ::osl::FileBase::E_None)
+ bError = true;
+ if (osl::File::remove( aFileURL ) != ::osl::FileBase::E_None)
+ bError = true;
+
+ SAL_INFO(
+ "desktop.deployment",
+ "local url '" << rMacro << "' -> '" << aFileURL << "' "
+ << (bError ? "is" : "is not") << " readonly\n");
+ return bError;
+}
+
+}
+
+Reference<deployment::XPackageManager> PackageManagerImpl::create(
+ Reference<XComponentContext> const & xComponentContext,
+ OUString const & context )
+{
+ PackageManagerImpl * that = new PackageManagerImpl(
+ xComponentContext, context );
+ Reference<deployment::XPackageManager> xPackageManager( that );
+
+ OUString logFile, stamp;
+ if ( context == "user" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE";
+ that->m_registryCache = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry";
+ logFile = "$UNO_USER_PACKAGES_CACHE/log.txt";
+ //We use the extension .sys for the file because on Windows Vista a sys
+ //(as well as exe and dll) file
+ //will not be written in the VirtualStore. For example if the process has no
+ //admin right once cannot write to the %programfiles% folder. However, when
+ //virtualization is used, the file will be written into the VirtualStore and
+ //it appears as if one could write to %programfiles%. When we test for write
+ //access to the office/shared folder for shared extensions then this typically
+ //fails because a normal user typically cannot write to this folder. However,
+ //using virtualization it appears that he/she can. Then a shared extension can
+ //be installed but is only visible for the user (because the extension is in
+ //the virtual store).
+ stamp = "$UNO_USER_PACKAGES_CACHE";
+ }
+ else if ( context == "shared" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry";
+ logFile = "$SHARED_EXTENSIONS_USER/log.txt";
+#if !HAVE_FEATURE_READONLY_INSTALLSET
+ // The "shared" extensions are read-only when we have a
+ // read-only installset.
+ stamp = "$UNO_SHARED_PACKAGES_CACHE";
+#endif
+ }
+ else if ( context == "bundled" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS";
+ that->m_registrationData = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry";
+ logFile = "$BUNDLED_EXTENSIONS_USER/log.txt";
+ //No stamp file. We assume that bundled is always readonly. It must not be
+ //modified from ExtensionManager but only by the installer
+ }
+ else if ( context == "tmp" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$TMP_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$TMP_EXTENSIONS/registry";
+ stamp = "$TMP_EXTENSIONS";
+ }
+ else if (context == "bak") {
+ that->m_activePackages = "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$BAK_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$BAK_EXTENSIONS/registry";
+ stamp = "$BAK_EXTENSIONS";
+ }
+
+ else if (! context.match("vnd.sun.star.tdoc:/")) {
+ throw lang::IllegalArgumentException(
+ "invalid context given: " + context,
+ Reference<XInterface>(), static_cast<sal_Int16>(-1) );
+ }
+
+ Reference<XCommandEnvironment> xCmdEnv;
+
+ try {
+ // There is no stamp for the bundled folder:
+ if (!stamp.isEmpty())
+ that->m_readOnly = isMacroURLReadOnly( stamp );
+
+ if (!that->m_readOnly && !logFile.isEmpty())
+ {
+ // Initialize logger which will be used in ProgressLogImpl (created below)
+ rtl::Bootstrap::expandMacros(logFile);
+ comphelper::EventLogger logger(xComponentContext, "unopkg");
+ const Reference<XLogger> xLogger(logger.getLogger());
+ Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xComponentContext));
+ Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
+ Reference<XLogHandler> xFileHandler(css::logging::FileHandler::createWithSettings(xComponentContext, aSeq2));
+ xFileHandler->setLevel(LogLevel::WARNING);
+ xLogger->addLogHandler(xFileHandler);
+
+ that->m_xLogFile.set(
+ that->m_xComponentContext->getServiceManager()
+ ->createInstanceWithArgumentsAndContext(
+ dp_log::serviceDecl.getSupportedServiceNames()[0],
+ Sequence<Any>(),
+ that->m_xComponentContext ),
+ UNO_QUERY_THROW );
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) );
+ }
+
+ that->initRegistryBackends();
+ that->initActivationLayer( xCmdEnv );
+
+ return xPackageManager;
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ ("[context=\"" + context + "\"] caught unexpected "
+ + exc.getValueType().getTypeName() + ": " + e.Message),
+ Reference<XInterface>(), exc );
+ }
+}
+
+
+PackageManagerImpl::~PackageManagerImpl()
+{
+}
+
+
+void PackageManagerImpl::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+
+void PackageManagerImpl::disposing()
+{
+ try {
+// // xxx todo: guarding?
+// ::osl::MutexGuard guard( getMutex() );
+ try_dispose( m_xLogFile );
+ m_xLogFile.clear();
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ m_activePackagesDB.reset();
+ m_xComponentContext.clear();
+
+ t_pm_helper::disposing();
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing...",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+// XComponent
+
+void PackageManagerImpl::dispose()
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::dispose();
+}
+
+
+void PackageManagerImpl::addEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::addEventListener( xListener );
+}
+
+
+void PackageManagerImpl::removeEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::removeEventListener( xListener );
+}
+
+// XPackageManager
+
+OUString PackageManagerImpl::getContext()
+{
+ check();
+ return m_context;
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageManagerImpl::getSupportedPackageTypes()
+{
+ OSL_ASSERT( m_xRegistry.is() );
+ return m_xRegistry->getSupportedPackageTypes();
+}
+
+
+Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel()
+{
+ check();
+ return new AbortChannel;
+}
+
+// XModifyBroadcaster
+
+void PackageManagerImpl::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void PackageManagerImpl::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+OUString PackageManagerImpl::detectMediaType(
+ ::ucbhelper::Content const & ucbContent_, bool throw_exc )
+{
+ ::ucbhelper::Content ucbContent(ucbContent_);
+ OUString url( ucbContent.getURL() );
+ OUString mediaType;
+ if (url.match( "vnd.sun.star.tdoc:" ) || url.match( "vnd.sun.star.pkg:" ))
+ {
+ try {
+ ucbContent.getPropertyValue( "MediaType" ) >>= mediaType;
+ }
+ catch (const beans::UnknownPropertyException &) {
+ }
+ OSL_ENSURE( !mediaType.isEmpty(), "### no media-type?!" );
+ }
+ if (mediaType.isEmpty())
+ {
+ try {
+ Reference<deployment::XPackage> xPackage(
+ m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) );
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ }
+ catch (const lang::IllegalArgumentException &) {
+ if (throw_exc)
+ throw;
+ css::uno::Any ex( cppu::getCaughtException() );
+ SAL_WARN( "desktop", exceptionToString(ex) );
+ }
+ }
+ return mediaType;
+}
+
+
+OUString PackageManagerImpl::insertToActivationLayer(
+ Sequence<beans::NamedValue> const & properties,
+ OUString const & mediaType, ::ucbhelper::Content const & sourceContent_,
+ OUString const & title, ActivePackages::Data * dbData )
+{
+ ::ucbhelper::Content sourceContent(sourceContent_);
+ Reference<XCommandEnvironment> xCmdEnv(
+ sourceContent.getCommandEnvironment() );
+
+ OUString baseDir(m_activePackages_expanded);
+ ::utl::TempFile aTemp(&baseDir, false);
+ OUString tempEntry = aTemp.GetURL();
+ tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1);
+ OUString destFolder = makeURL( m_activePackages, tempEntry) + "_";
+
+ // prepare activation folder:
+ ::ucbhelper::Content destFolderContent;
+ create_folder( &destFolderContent, destFolder, xCmdEnv );
+
+ // copy content into activation temp dir:
+ if (mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.package-bundle") ||
+ // xxx todo: more sophisticated parsing
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.legacy-package-bundle"))
+ {
+ // inflate content:
+ OUStringBuffer buf;
+ if (!sourceContent.isFolder())
+ {
+ buf.append( "vnd.sun.star.zip://" );
+ buf.append( ::rtl::Uri::encode( sourceContent.getURL(),
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ //Folder. No need to unzip, just copy
+ buf.append(sourceContent.getURL());
+ }
+ buf.append( '/' );
+ sourceContent = ::ucbhelper::Content(
+ buf.makeStringAndClear(), xCmdEnv, m_xComponentContext );
+ }
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ title, NameClash::OVERWRITE );
+
+
+ // write to DB:
+ //bundled extensions should only be added by the synchronizeAddedExtensions
+ //functions. Moreover, there is no "temporary folder" for bundled extensions.
+ OSL_ASSERT(!(m_context == "bundled"));
+ OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title);
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(sFolderUrl);
+ dbData->temporaryName = tempEntry;
+ dbData->fileName = title;
+ dbData->mediaType = mediaType;
+ dbData->version = info.getVersion();
+
+ //No write the properties file next to the extension
+ ExtensionProperties props(sFolderUrl, properties, xCmdEnv, m_xComponentContext);
+ props.write();
+ return destFolder;
+}
+
+
+void PackageManagerImpl::insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData )
+{
+ //access to the database must be guarded. See removePackage
+ const ::osl::MutexGuard guard( getMutex() );
+ m_activePackagesDB->put( id, dbData );
+}
+
+
+/* The function returns true if there is an extension with the same id already
+ installed which needs to be uninstalled, before the new extension can be installed.
+*/
+bool PackageManagerImpl::isInstalled(
+ Reference<deployment::XPackage> const & package)
+{
+ OUString id(dp_misc::getIdentifier(package));
+ OUString fn(package->getName());
+ bool bInstalled = false;
+ if (m_activePackagesDB->has( id, fn ))
+ {
+ bInstalled = true;
+ }
+ return bInstalled;
+}
+
+// XPackageManager
+
+Reference<deployment::XPackage> PackageManagerImpl::importExtension(
+ Reference<deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ return addPackage(extension->getURL(), Sequence<beans::NamedValue>(),
+ OUString(), xAbortChannel, xCmdEnv_);
+}
+
+/* The function adds an extension but does not register it!!!
+ It may not do any user interaction. This is done in XExtensionManager::addExtension
+*/
+Reference<deployment::XPackage> PackageManagerImpl::addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType_,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (m_readOnly)
+ {
+ OUString message;
+ if (m_context == "shared")
+ message = "You need write permissions to install a shared extension!";
+ else
+ message = "You need write permissions to install this extension!";
+ throw deployment::DeploymentException(
+ message, static_cast<OWeakObject *>(this), Any() );
+ }
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ::ucbhelper::Content sourceContent;
+ (void)create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc
+ const OUString title( StrTitle::getTitle( sourceContent ) );
+ const OUString title_enc( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ OUString destFolder;
+
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ mediaType = detectMediaType( sourceContent );
+
+ Reference<deployment::XPackage> xPackage;
+ // copy file:
+ progressUpdate(
+ DpResId(RID_STR_COPYING_PACKAGE) + title, xCmdEnv );
+ if (m_activePackages.isEmpty())
+ {
+ ::ucbhelper::Content docFolderContent;
+ create_folder( &docFolderContent, m_context, xCmdEnv );
+ // copy into document, first:
+ docFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(),
+ NameClash::ASK /* xxx todo: ASK not needed? */);
+ // set media-type:
+ ::ucbhelper::Content docContent(
+ makeURL( m_context, title_enc ), xCmdEnv, m_xComponentContext );
+ //TODO #i73136#: using title instead of id can lead to
+ // clashes, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently does
+ // not work, anyway.
+ docContent.setPropertyValue("MediaType", Any(mediaType) );
+
+ // xxx todo: obsolete in the future
+ try {
+ docFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const UnsupportedCommandException &) {
+ }
+ }
+ ActivePackages::Data dbData;
+ destFolder = insertToActivationLayer(
+ properties, mediaType, sourceContent, title, &dbData );
+
+
+ // bind activation package:
+ //Because every shared/user extension will be unpacked in a folder,
+ //which was created with a unique name we will always have two different
+ //XPackage objects, even if the second extension is the same.
+ //Therefore bindPackage does not need a guard here.
+ xPackage = m_xRegistry->bindPackage(
+ makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv );
+
+ OSL_ASSERT( xPackage.is() );
+ if (xPackage.is())
+ {
+ bool install = false;
+ try
+ {
+ OUString const id = dp_misc::getIdentifier( xPackage );
+
+ ::osl::MutexGuard g(m_addMutex);
+ if (isInstalled(xPackage))
+ {
+ //Do not guard the complete function with the getMutex
+ removePackage(id, xPackage->getName(), xAbortChannel,
+ xCmdEnv);
+ }
+ install = true;
+ insertToActivationLayerDB(id, dbData);
+ }
+ catch (...)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ throw;
+ }
+ if (!install)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ }
+ //ToDo: We should notify only if the extension is registered
+ fireModified();
+ }
+ return xPackage;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + url,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+void PackageManagerImpl::deletePackageFromCache(
+ Reference<deployment::XPackage> const & xPackage,
+ OUString const & destFolder)
+{
+ try_dispose( xPackage );
+
+ //we remove the package from the uno cache
+ //no service from the package may be loaded at this time!!!
+ erase_path( destFolder, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //rm last character '_'
+ OUString url = destFolder.copy(0, destFolder.getLength() - 1);
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+
+}
+
+void PackageManagerImpl::removePackage(
+ OUString const & id, OUString const & fileName,
+ Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ Reference<deployment::XPackage> xPackage;
+ {
+ const ::osl::MutexGuard guard(getMutex());
+ //Check if this extension exist and throw an IllegalArgumentException
+ //if it does not
+ //If the files of the extension are already removed, or there is a
+ //different extension at the same place, for example after updating the
+ //extension, then the returned object is that which uses the database data.
+ xPackage = getDeployedPackage_(id, fileName, xCmdEnv );
+
+
+ //Because the extension is only removed the next time the extension
+ //manager runs after restarting OOo, we need to indicate that a
+ //shared extension was "deleted". When a user starts OOo, then it
+ //will check if something changed in the shared repository. Based on
+ //the flag file it will then recognize, that the extension was
+ //deleted and can then update the extension database of the shared
+ //extensions in the user installation.
+ if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && (m_context == "shared"))
+ {
+ ActivePackages::Data val;
+ m_activePackagesDB->get( & val, id, fileName);
+ OSL_ASSERT(!val.temporaryName.isEmpty());
+ OUString url(makeURL(m_activePackages_expanded,
+ val.temporaryName + "removed"));
+ ::ucbhelper::Content contentRemoved(url, xCmdEnv, m_xComponentContext);
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+
+ OString stamp = OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentRemoved.writeStream( xData, true /* replace existing */ );
+ }
+ m_activePackagesDB->erase( id, fileName ); // to be removed upon next start
+ //remove any cached data hold by the backend
+ m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType());
+ }
+ try_dispose( xPackage );
+
+ fireModified();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_REMOVING) + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data )
+{
+ OUStringBuffer buf;
+ buf.append( data.temporaryName );
+ //The bundled extensions are not contained in an additional folder
+ //with a unique name. data.temporaryName contains already the
+ //UTF8 encoded folder name. See PackageManagerImpl::synchronize
+ if (m_context != "bundled")
+ {
+ buf.append( "_/" );
+ buf.append( ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ return makeURL( m_activePackages, buf.makeStringAndClear() );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ ActivePackages::Data val;
+ if (m_activePackagesDB->get( &val, id, fileName ))
+ {
+ return getDeployedPackage_( id, val, xCmdEnv );
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ OUString const & id, ActivePackages::Data const & data,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms )
+{
+ if (ignoreAlienPlatforms)
+ {
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( data.mediaType, type, subType, &params ))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+ }
+ }
+ Reference<deployment::XPackage> xExtension;
+ try
+ {
+ //Ignore extensions where XPackage::checkPrerequisites failed.
+ //They must not be usable for this user.
+ if (data.failedPrerequisites == "0")
+ {
+ xExtension = m_xRegistry->bindPackage(
+ getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv );
+ }
+ }
+ catch (const deployment::InvalidRemovedParameterException& e)
+ {
+ xExtension = e.Extension;
+ }
+ return xExtension;
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages_(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector< Reference<deployment::XPackage> > packages;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ for (auto const& elem : id2temp)
+ {
+ if (elem.second.failedPrerequisites != "0")
+ continue;
+ try {
+ packages.push_back(
+ getDeployedPackage_(
+ elem.first, elem.second, xCmdEnv,
+ true /* xxx todo: think of GUI:
+ ignore other platforms than the current one */ ) );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const deployment::DeploymentException&) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ return comphelper::containerToSequence(packages);
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( getMutex() );
+ return getDeployedPackage_( id, fileName, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while accessing deployed package: " + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages(
+ Reference<task::XAbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( getMutex() );
+ return getDeployedPackages_( xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while getting all deployed packages: " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+//ToDo: the function must not call registerPackage, do this in
+//XExtensionManager.reinstallDeployedExtensions
+void PackageManagerImpl::reinstallDeployedPackages(
+ sal_Bool force, Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (!force && office_is_running())
+ throw RuntimeException(
+ "You must close any running Office process before reinstalling packages!",
+ static_cast<OWeakObject *>(this) );
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ProgressLevel progress(
+ xCmdEnv, "Reinstalling all deployed packages..." );
+
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ if (!m_registryCache.isEmpty())
+ erase_path( m_registryCache, xCmdEnv );
+ initRegistryBackends();
+ Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY );
+ if (xUpdatable.is())
+ xUpdatable->update();
+
+ //registering is done by the ExtensionManager service.
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ "Error while reinstalling all previously deployed packages of context " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+sal_Bool SAL_CALL PackageManagerImpl::isReadOnly( )
+{
+ return m_readOnly;
+}
+bool PackageManagerImpl::synchronizeRemovedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+
+ //find all which are in the extension data base but which
+ //are removed already.
+ OSL_ASSERT(!(m_context == "user"));
+ bool bModified = false;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ try
+ {
+ //Get the URL to the extensions folder, first make the url for the
+ //shared repository including the temporary name
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ bool bRemoved = false;
+ //Check if the URL to the extension is still the same
+ ::ucbhelper::Content contentExtension;
+
+ if (!create_ucb_content(
+ &contentExtension, url,
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+
+ //The folder is in the extension database, but it can still be deleted.
+ //look for the xxx.tmpremoved file
+ //There can also be the case that a different extension was installed
+ //in a "temp" folder with name that is already used.
+ if (!bRemoved && bShared)
+ {
+ ::ucbhelper::Content contentRemoved;
+
+ if (create_ucb_content(
+ &contentRemoved,
+ m_activePackages_expanded + "/" +
+ elem.second.temporaryName + "removed",
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+ }
+
+ if (!bRemoved)
+ {
+ //There may be another extensions at the same place
+ dp_misc::DescriptionInfoset infoset =
+ dp_misc::getDescriptionInfoset(url);
+ OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(),
+ "Extension Manager: bundled and shared extensions "
+ "must have an identifier and a version");
+ if (infoset.hasDescription() &&
+ infoset.getIdentifier() &&
+ ( elem.first != *(infoset.getIdentifier())
+ || elem.second.version != infoset.getVersion()))
+ {
+ bRemoved = true;
+ }
+
+ }
+ if (bRemoved)
+ {
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, elem.second.mediaType, true, elem.first, xCmdEnv );
+ OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object.
+ xPackage->revokePackage(true, xAbortChannel, xCmdEnv);
+ removePackage(xPackage->getIdentifier().Value, xPackage->getName(),
+ xAbortChannel, xCmdEnv);
+ bModified = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+
+bool PackageManagerImpl::synchronizeAddedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ bool bModified = false;
+ OSL_ASSERT(!(m_context == "user"));
+
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ //check if the folder exist at all. The shared extension folder
+ //may not exist for a normal user.
+ bool bOk=true;
+ try
+ {
+ bOk = create_ucb_content(
+ nullptr, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (!bOk)
+ return bModified;
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor( tempFolder,
+ ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ while (xResultSet->next())
+ {
+ try
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ //The temporary folders of user and shared have an '_' at then end.
+ //But the name in ActivePackages.temporaryName is saved without.
+ OUString title2 = title;
+ bool bShared = (m_context == "shared");
+ if (bShared)
+ {
+ OSL_ASSERT(title2.endsWith("_"));
+ title2 = title2.copy(0, title2.getLength() -1);
+ }
+ OUString titleEncoded = ::rtl::Uri::encode(
+ title2, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+
+ //It is sufficient to check for the folder name, because when the administrator
+ //installed the extension it was already checked if there is one with the
+ //same identifier.
+ const MatchTempDir match(titleEncoded);
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+
+ // The folder was not found in the data base, so it must be
+ // an added extension
+ OUString url(m_activePackages_expanded + "/" + titleEncoded);
+ OUString sExtFolder;
+ if (bShared) //that is, shared
+ {
+ //Check if the extension was not "deleted" already which is indicated
+ //by a xxx.tmpremoved file
+ ::ucbhelper::Content contentRemoved;
+ if (create_ucb_content(&contentRemoved, url + "removed",
+ Reference<XCommandEnvironment>(), false))
+ continue;
+ sExtFolder = getExtensionFolder(
+ m_activePackages_expanded + "/" + titleEncoded + "_",
+ xCmdEnv, m_xComponentContext);
+ url = makeURLAppendSysPathSegment(m_activePackages_expanded, title);
+ url = makeURLAppendSysPathSegment(url, sExtFolder);
+ }
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+ if (xPackage.is())
+ {
+ OUString id = dp_misc::getIdentifier( xPackage );
+
+ //Prepare the database entry
+ ActivePackages::Data dbData;
+
+ dbData.temporaryName = titleEncoded;
+ if (bShared)
+ dbData.fileName = sExtFolder;
+ else
+ dbData.fileName = title;
+ dbData.mediaType = xPackage->getPackageType()->getMediaType();
+ dbData.version = xPackage->getVersion();
+ SAL_WARN_IF(
+ dbData.version.isEmpty(), "desktop.deployment",
+ "bundled/shared extension " << id << " at <" << url
+ << "> has no explicit version");
+
+ //We provide a special command environment that will prevent
+ //showing a license if simple-license/@accept-by = "admin"
+ //It will also prevent showing the license for bundled extensions
+ //which is not supported.
+ OSL_ASSERT(!(m_context == "user"));
+
+ // shall the license be suppressed?
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(url);
+ ::std::optional<dp_misc::SimpleLicenseAttributes>
+ attr = info.getSimpleLicenseAttributes();
+ ExtensionProperties props(url, xCmdEnv, m_xComponentContext);
+ bool bNoLicense = false;
+ if (attr && attr->suppressIfRequired && props.isSuppressedLicense())
+ bNoLicense = true;
+
+ Reference<ucb::XCommandEnvironment> licCmdEnv(
+ new LicenseCommandEnv(xCmdEnv->getInteractionHandler(),
+ bNoLicense, m_context));
+ sal_Int32 failedPrereq = xPackage->checkPrerequisites(
+ xAbortChannel, licCmdEnv, false);
+ //Remember that this failed. For example, the user
+ //could have declined the license. Then the next time the
+ //extension folder is investigated we do not want to
+ //try to install the extension again.
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ bModified = true;
+ }
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ // Looks like exceptions being caught here is not an uncommon case.
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+sal_Bool PackageManagerImpl::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ check();
+ bool bModified = false;
+ if (m_context == "user")
+ return bModified;
+ bModified |=
+ synchronizeRemovedExtensions(xAbortChannel, xCmdEnv);
+ bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv);
+
+ return bModified;
+}
+
+Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > vec;
+
+ try
+ {
+ const ::osl::MutexGuard guard( getMutex() );
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ //Get the database entry
+ ActivePackages::Data const & dbData = elem.second;
+ sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32();
+ //If the installation failed for other reason then the license then we
+ //ignore it.
+ if (failedPrereq ^ deployment::Prerequisites::LICENSE)
+ continue;
+
+ //Prepare the URL to the extension
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ Reference<deployment::XPackage> p = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+
+ if (p.is())
+ vec.push_back(p);
+
+ }
+ return ::comphelper::containerToSequence(vec);
+ }
+ catch (const deployment::DeploymentException &)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ Any exc = ::cppu::getCaughtException();
+ deployment::DeploymentException de(
+ "PackageManagerImpl::getExtensionsWithUnacceptedLicenses",
+ static_cast<OWeakObject*>(this), exc);
+ exc <<= de;
+ ::cppu::throwException(exc);
+ }
+
+ return ::comphelper::containerToSequence(vec);
+}
+
+sal_Int32 PackageManagerImpl::checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ if (m_context != extension->getRepositoryName())
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: extension is not from this repository.",
+ nullptr, 0);
+
+ ActivePackages::Data dbData;
+ OUString id = dp_misc::getIdentifier(extension);
+ if (!m_activePackagesDB->get( &dbData, id, OUString()))
+ {
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: unknown extension",
+ nullptr, 0);
+
+ }
+ //If the license was already displayed, then do not show it again
+ Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv;
+ sal_Int32 prereq = dbData.failedPrerequisites.toInt32();
+ if ( !(prereq & deployment::Prerequisites::LICENSE))
+ _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler());
+
+ sal_Int32 failedPrereq = extension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, false);
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ return 0;
+ }
+ catch ( const deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ deployment::DeploymentException exc(
+ "PackageManagerImpl::checkPrerequisites: exception ",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl()
+{
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl(
+ Reference<XCommandEnvironment> const & xUserCmdEnv,
+ Reference<XProgressHandler> const & xLogFile )
+ : m_xLogFile( xLogFile )
+{
+ if (xUserCmdEnv.is()) {
+ m_xUserProgress.set( xUserCmdEnv->getProgressHandler() );
+ m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() );
+ }
+}
+
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler()
+{
+ return m_xUserInteractionHandler;
+}
+
+
+Reference<XProgressHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler()
+{
+ return this;
+}
+
+// XProgressHandler
+
+void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->push( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->push( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->update( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::pop()
+{
+ if (m_xLogFile.is())
+ m_xLogFile->pop();
+ if (m_xUserProgress.is())
+ m_xUserProgress->pop();
+}
+
+} // namespace dp_manager
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.h b/desktop/source/deployment/manager/dp_manager.h
new file mode 100644
index 000000000..6ec26c9ae
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.h
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_MANAGER_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_MANAGER_H
+
+#include <dp_misc.h>
+#include "dp_activepackages.hxx"
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <memory>
+
+
+namespace dp_manager {
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackageManager > t_pm_helper;
+
+
+class PackageManagerImpl final : private ::dp_misc::MutexHolder, public t_pm_helper
+{
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ OUString m_context;
+ OUString m_registrationData;
+ OUString m_registrationData_expanded;
+ OUString m_registryCache;
+ bool m_readOnly;
+
+ OUString m_activePackages;
+ OUString m_activePackages_expanded;
+ std::unique_ptr< ActivePackages > m_activePackagesDB;
+ //This mutex is only used for synchronization in addPackage
+ ::osl::Mutex m_addMutex;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ inline void logIntern( css::uno::Any const & status );
+ void fireModified();
+
+ css::uno::Reference<css::deployment::XPackageRegistry> m_xRegistry;
+
+ void initRegistryBackends();
+ void initActivationLayer(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ OUString detectMediaType(
+ ::ucbhelper::Content const & ucbContent, bool throw_exc = true );
+ OUString insertToActivationLayer(
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ ::ucbhelper::Content const & sourceContent,
+ OUString const & title, ActivePackages::Data * dbData );
+ void insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData );
+
+ static void deletePackageFromCache(
+ css::uno::Reference<css::deployment::XPackage> const & xPackage,
+ OUString const & destFolder );
+
+ bool isInstalled(
+ css::uno::Reference<css::deployment::XPackage> const & package);
+
+ bool synchronizeRemovedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ bool synchronizeAddedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ class CmdEnvWrapperImpl
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::ucb::XProgressHandler >
+ {
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xUserProgress;
+ css::uno::Reference<css::task::XInteractionHandler>
+ m_xUserInteractionHandler;
+
+ public:
+ virtual ~CmdEnvWrapperImpl() override;
+ CmdEnvWrapperImpl(
+ css::uno::Reference<css::ucb::XCommandEnvironment>
+ const & xUserCmdEnv,
+ css::uno::Reference<css::ucb::XProgressHandler> const & xLogFile );
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler> SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler> SAL_CALL
+ getProgressHandler() override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+ };
+
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageManagerImpl() override;
+ PackageManagerImpl(
+ css::uno::Reference<css::uno::XComponentContext>
+ const & xComponentContext, OUString const & context )
+ : t_pm_helper( getMutex() ),
+ m_xComponentContext( xComponentContext ),
+ m_context( context ),
+ m_readOnly( true )
+ {}
+
+public:
+ static css::uno::Reference<css::deployment::XPackageManager> create(
+ css::uno::Reference<css::uno::XComponentContext>
+ const & xComponentContext, OUString const & context );
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+ // XPackageManager
+ virtual OUString SAL_CALL getContext() override;
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL importExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removePackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ OUString getDeployPath( ActivePackages::Data const & data );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ OUString const & id, ActivePackages::Data const & data,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool ignoreAlienPlatforms = false );
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL
+ getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ getDeployedPackages_(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedPackages(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedPackages(
+ sal_Bool force,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Bool SAL_CALL isReadOnly( ) override;
+
+ virtual ::sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ };
+
+
+inline void PackageManagerImpl::check()
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ throw css::lang::DisposedException(
+ "PackageManager instance has already been disposed!",
+ static_cast< ::cppu::OWeakObject * >(this) );
+}
+
+
+inline void PackageManagerImpl::logIntern( css::uno::Any const & status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( status );
+}
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_managerfac.cxx b/desktop/source/deployment/manager/dp_managerfac.cxx
new file mode 100644
index 000000000..6263922d7
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_managerfac.cxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "dp_manager.h"
+#include <dp_services.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <unordered_map>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_manager::factory {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageManagerFactory > t_pmfac_helper;
+
+namespace {
+
+class PackageManagerFactoryImpl : private MutexHolder, public t_pmfac_helper
+{
+ Reference<XComponentContext> m_xComponentContext;
+
+ Reference<deployment::XPackageManager> m_xUserMgr;
+ Reference<deployment::XPackageManager> m_xSharedMgr;
+ Reference<deployment::XPackageManager> m_xBundledMgr;
+ Reference<deployment::XPackageManager> m_xTmpMgr;
+ Reference<deployment::XPackageManager> m_xBakMgr;
+ typedef std::unordered_map<
+ OUString, WeakReference<deployment::XPackageManager> > t_string2weakref;
+ t_string2weakref m_managers;
+
+protected:
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+public:
+ explicit PackageManagerFactoryImpl(
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageManagerFactory
+ virtual Reference<deployment::XPackageManager> SAL_CALL getPackageManager(
+ OUString const & context ) override;
+};
+
+}
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<PackageManagerFactoryImpl> const servicePMFI;
+sdecl::ServiceDecl const serviceDecl(
+ servicePMFI,
+ // a private one:
+ "com.sun.star.comp.deployment.PackageManagerFactory",
+ "com.sun.star.comp.deployment.PackageManagerFactory" );
+
+
+PackageManagerFactoryImpl::PackageManagerFactoryImpl(
+ Reference<XComponentContext> const & xComponentContext )
+ : t_pmfac_helper( getMutex() ),
+ m_xComponentContext( xComponentContext )
+{
+}
+
+inline void PackageManagerFactoryImpl::check()
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ {
+ throw lang::DisposedException(
+ "PackageManagerFactory instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageManagerFactoryImpl::disposing()
+{
+ // dispose all managers:
+ ::osl::MutexGuard guard( getMutex() );
+ for (auto const& elem : m_managers)
+ try_dispose( elem.second );
+ m_managers = t_string2weakref();
+ // the below are already disposed:
+ m_xUserMgr.clear();
+ m_xSharedMgr.clear();
+ m_xBundledMgr.clear();
+ m_xTmpMgr.clear();
+ m_xBakMgr.clear();
+}
+
+// XPackageManagerFactory
+
+Reference<deployment::XPackageManager>
+PackageManagerFactoryImpl::getPackageManager( OUString const & context )
+{
+ Reference< deployment::XPackageManager > xRet;
+ ::osl::ResettableMutexGuard guard( getMutex() );
+ check();
+ t_string2weakref::const_iterator const iFind( m_managers.find( context ) );
+ if (iFind != m_managers.end()) {
+ xRet = iFind->second;
+ if (xRet.is())
+ return xRet;
+ }
+
+ guard.clear();
+ xRet.set( PackageManagerImpl::create( m_xComponentContext, context ) );
+ guard.reset();
+ std::pair< t_string2weakref::iterator, bool > insertion(
+ m_managers.emplace( context, xRet ) );
+ if (insertion.second)
+ {
+ OSL_ASSERT( insertion.first->second.get() == xRet );
+ // hold user, shared mgrs for whole process: live deployment
+ if ( context == "user" )
+ m_xUserMgr = xRet;
+ else if ( context == "shared" )
+ m_xSharedMgr = xRet;
+ else if ( context == "bundled" )
+ m_xBundledMgr = xRet;
+ else if ( context == "tmp" )
+ m_xTmpMgr = xRet;
+ else if ( context == "bak" )
+ m_xBakMgr = xRet;
+ }
+ else
+ {
+ Reference< deployment::XPackageManager > xAlreadyIn(
+ insertion.first->second );
+ if (xAlreadyIn.is())
+ {
+ guard.clear();
+ try_dispose( xRet );
+ xRet = xAlreadyIn;
+ }
+ else
+ {
+ insertion.first->second = xRet;
+ }
+ }
+ return xRet;
+}
+
+} // namespace dp_manager::factory
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.cxx b/desktop/source/deployment/manager/dp_properties.cxx
new file mode 100644
index 000000000..ab5ccc7eb
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.cxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <xmlscript/xml_helper.hxx>
+#include <ucbhelper/content.hxx>
+
+#include <dp_ucb.h>
+#include <rtl/ustrbuf.hxx>
+#include "dp_properties.hxx"
+
+namespace lang = com::sun::star::lang;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+
+using ::com::sun::star::uno::Reference;
+
+#define PROP_SUPPRESS_LICENSE "SUPPRESS_LICENSE"
+#define PROP_EXTENSION_UPDATE "EXTENSION_UPDATE"
+
+namespace dp_manager {
+
+//Reading the file
+ExtensionProperties::ExtensionProperties(
+ OUString const & urlExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = urlExtension + "properties";
+
+ std::vector< std::pair< OUString, OUString> > props;
+ if (! dp_misc::create_ucb_content(nullptr, m_propFileUrl, nullptr, false))
+ return;
+
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ dp_misc::readProperties(props, contentProps);
+
+ for (auto const& prop : props)
+ {
+ if (prop.first == PROP_SUPPRESS_LICENSE)
+ m_prop_suppress_license = prop.second;
+ }
+}
+
+//Writing the file
+ExtensionProperties::ExtensionProperties(
+ OUString const & urlExtension,
+ uno::Sequence<css::beans::NamedValue> const & properties,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = urlExtension + "properties";
+
+ for (css::beans::NamedValue const & v : properties)
+ {
+ if (v.Name == PROP_SUPPRESS_LICENSE)
+ {
+ m_prop_suppress_license = getPropertyValue(v);
+ }
+ else if (v.Name == PROP_EXTENSION_UPDATE)
+ {
+ m_prop_extension_update = getPropertyValue(v);
+ }
+ else
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: unknown property", nullptr, -1);
+ }
+ }
+}
+
+OUString ExtensionProperties::getPropertyValue(css::beans::NamedValue const & v)
+{
+ OUString value("0");
+ if (! (v.Value >>= value) )
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: wrong property value", nullptr, -1);
+ }
+ return value;
+}
+void ExtensionProperties::write()
+{
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ OUStringBuffer buf;
+
+ if (m_prop_suppress_license)
+ {
+ buf.append(PROP_SUPPRESS_LICENSE);
+ buf.append("=");
+ buf.append(*m_prop_suppress_license);
+ }
+
+ OString stamp = OUStringToOString(
+ buf.makeStringAndClear(), RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentProps.writeStream( xData, true /* replace existing */ );
+}
+
+bool ExtensionProperties::isSuppressedLicense() const
+{
+ bool ret = false;
+ if (m_prop_suppress_license)
+ {
+ if (*m_prop_suppress_license == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+bool ExtensionProperties::isExtensionUpdate() const
+{
+ bool ret = false;
+ if (m_prop_extension_update)
+ {
+ if (*m_prop_extension_update == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+} // namespace dp_manager
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.hxx b/desktop/source/deployment/manager/dp_properties.hxx
new file mode 100644
index 000000000..2d72d8cd8
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_PROPERTIES_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_MANAGER_DP_PROPERTIES_HXX
+
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <optional>
+
+
+namespace dp_manager {
+
+
+class ExtensionProperties final
+{
+ OUString m_propFileUrl;
+ const css::uno::Reference<css::ucb::XCommandEnvironment> m_xCmdEnv;
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ ::std::optional< OUString> m_prop_suppress_license;
+ ::std::optional< OUString> m_prop_extension_update;
+
+ static OUString getPropertyValue(css::beans::NamedValue const & v);
+public:
+
+ ExtensionProperties(OUString const & urlExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext);
+
+ ExtensionProperties(OUString const & urlExtension,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext);
+
+ void write();
+
+ bool isSuppressedLicense() const;
+
+ bool isExtensionUpdate() const;
+};
+}
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_dependencies.cxx b/desktop/source/deployment/misc/dp_dependencies.cxx
new file mode 100644
index 000000000..081144af3
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_dependencies.cxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <unotools/configmgr.hxx>
+
+#include <strings.hrc>
+#include <dp_shared.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_version.hxx>
+
+namespace {
+
+static char const namespaceLibreOffice[] =
+ "http://libreoffice.org/extensions/description/2011";
+
+static char const namespaceOpenOfficeOrg[] =
+ "http://openoffice.org/extensions/description/2006";
+
+static char const minimalVersionLibreOffice[] = "LibreOffice-minimal-version";
+static char const maximalVersionLibreOffice[] = "LibreOffice-maximal-version";
+
+static char const minimalVersionOpenOfficeOrg[] =
+ "OpenOffice.org-minimal-version";
+
+static char const maximalVersionOpenOfficeOrg[] =
+ "OpenOffice.org-maximal-version";
+
+OUString getLibreOfficeMajorMinorMicro() {
+ return utl::ConfigManager::getAboutBoxProductVersion();
+}
+
+OUString getReferenceOpenOfficeOrgMajorMinor() {
+#ifdef ANDROID
+ // just hardcode the version
+ OUString v("4.1");
+#else
+ OUString v(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ReferenceOOoMajorMinor}");
+ rtl::Bootstrap::expandMacros(v); //TODO: check for failure
+#endif
+ return v;
+}
+
+bool satisfiesMinimalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::LESS;
+}
+
+bool satisfiesMaximalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::GREATER;
+}
+
+OUString produceErrorText(
+ OUString const & reason, OUString const & version)
+{
+ return reason.replaceFirst("%VERSION",
+ (version.isEmpty()
+ ? DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN)
+ : version));
+}
+
+}
+
+namespace dp_misc::Dependencies {
+
+css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+check(dp_misc::DescriptionInfoset const & infoset) {
+ css::uno::Reference< css::xml::dom::XNodeList > deps(
+ infoset.getDependencies());
+ sal_Int32 n = deps->getLength();
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(n);
+ sal_Int32 unsat = 0;
+ // check first if minimalVersionLibreOffice is specified -- in that case ignore the legacy OOo dependencies
+ bool bIgnoreOoo = false;
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ if ( e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice)
+ {
+ bIgnoreOoo = true;
+ break;
+ }
+ }
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ bool sat = false;
+ if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMaximalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice )
+ {
+ sat = satisfiesMinimalVersion(
+ getLibreOfficeMajorMinorMicro(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == maximalVersionLibreOffice )
+ {
+ sat = satisfiesMaximalVersion(getLibreOfficeMajorMinorMicro(), e->getAttribute("value"));
+ } else if (e->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ sat = satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ }
+ if (!sat) {
+ unsatisfied[unsat++] = e;
+ }
+ }
+ unsatisfied.realloc(unsat);
+ return unsatisfied;
+}
+
+OUString getErrorText(
+ css::uno::Reference< css::xml::dom::XElement > const & dependency)
+{
+ OSL_ASSERT(dependency.is());
+ if ( dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == minimalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == maximalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ } else {
+ return DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_descriptioninfoset.cxx b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
new file mode 100644
index 000000000..0caa69534
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
@@ -0,0 +1,809 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dp_descriptioninfoset.hxx>
+
+#include <dp_resource.h>
+#include <sal/config.h>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <optional>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/SequenceInputStream.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <ucbhelper/content.hxx>
+
+namespace {
+
+using css::uno::Reference;
+
+class EmptyNodeList:
+ public cppu::WeakImplHelper<css::xml::dom::XNodeList>
+{
+public:
+ EmptyNodeList();
+
+ EmptyNodeList(const EmptyNodeList&) = delete;
+ const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
+
+ virtual ::sal_Int32 SAL_CALL getLength() override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
+ item(::sal_Int32 index) override;
+};
+
+EmptyNodeList::EmptyNodeList() {}
+
+::sal_Int32 EmptyNodeList::getLength() {
+ return 0;
+}
+
+css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
+{
+ throw css::uno::RuntimeException("bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call",
+ static_cast< ::cppu::OWeakObject * >(this));
+}
+
+OUString getNodeValue(
+ css::uno::Reference< css::xml::dom::XNode > const & node)
+{
+ OSL_ASSERT(node.is());
+ try {
+ return node->getNodeValue();
+ } catch (const css::xml::dom::DOMException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.xml.dom.DOMException: " + e.Message,
+ nullptr, anyEx );
+ }
+}
+
+/**The class uses the UCB to access the description.xml file in an
+ extension. The UCB must have been initialized already. It also
+ requires that the extension has already be unzipped to a particular
+ location.
+ */
+class ExtensionDescription
+{
+public:
+ /**throws an exception if the description.xml is not
+ available, cannot be read, does not contain the expected data,
+ or any other error occurred. Therefore it should only be used with
+ new extensions.
+
+ Throws css::uno::RuntimeException,
+ css::deployment::DeploymentException,
+ dp_registry::backend::bundle::NoDescriptionException.
+ */
+ ExtensionDescription(
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const OUString& installDir,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
+ {
+ return m_xRoot;
+ }
+
+private:
+ css::uno::Reference<css::xml::dom::XNode> m_xRoot;
+};
+
+class NoDescriptionException
+{
+};
+
+class FileDoesNotExistFilter
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler >
+
+{
+ bool m_bExist;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
+
+public:
+ explicit FileDoesNotExistFilter(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ bool exist() { return m_bExist;}
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+};
+
+ExtensionDescription::ExtensionDescription(
+ const Reference<css::uno::XComponentContext>& xContext,
+ const OUString& installDir,
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
+{
+ try {
+ //may throw css::ucb::ContentCreationException
+ //If there is no description.xml then ucb will start an interaction which
+ //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
+ //and filter the respective exception out.
+ OUString sDescriptionUri(installDir + "/description.xml");
+ Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
+ ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
+
+ //throws a css::uno::Exception if the file is not available
+ Reference<css::io::XInputStream> xIn;
+ try
+ { //throws com.sun.star.ucb.InteractiveIOException
+ xIn = descContent.openStream();
+ }
+ catch ( const css::uno::Exception& )
+ {
+ if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
+ throw NoDescriptionException();
+ throw;
+ }
+ if (!xIn.is())
+ {
+ throw css::uno::Exception(
+ "Could not get XInputStream for description.xml of extension " +
+ sDescriptionUri, nullptr);
+ }
+
+ //get root node of description.xml
+ Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(xContext) );
+
+ if (!xDocBuilder->isNamespaceAware())
+ {
+ throw css::uno::Exception(
+ "Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware.", nullptr);
+ }
+
+ Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
+ if (!xDoc.is())
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
+ }
+
+ //check for proper root element and namespace
+ Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
+ if (!xRoot.is())
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " contains no root element.", nullptr);
+ }
+
+ if ( xRoot->getTagName() != "description")
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " does not contain the root element <description>.", nullptr);
+ }
+
+ m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
+ OUString nsDescription = xRoot->getNamespaceURI();
+
+ //check if this namespace is supported
+ if ( nsDescription != "http://openoffice.org/extensions/description/2006")
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
+ }
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::deployment::DeploymentException &) {
+ throw;
+ } catch (const css::uno::Exception & e) {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::deployment::DeploymentException(
+ e.Message, Reference< css::uno::XInterface >(), a);
+ }
+}
+
+FileDoesNotExistFilter::FileDoesNotExistFilter(
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
+ m_bExist(true), m_xCommandEnv(xCmdEnv)
+{}
+
+ // XCommandEnvironment
+Reference<css::task::XInteractionHandler >
+ FileDoesNotExistFilter::getInteractionHandler()
+{
+ return static_cast<css::task::XInteractionHandler*>(this);
+}
+
+Reference<css::ucb::XProgressHandler >
+ FileDoesNotExistFilter::getProgressHandler()
+{
+ return m_xCommandEnv.is()
+ ? m_xCommandEnv->getProgressHandler()
+ : Reference<css::ucb::XProgressHandler>();
+}
+
+// XInteractionHandler
+//If the interaction was caused by a non-existing file which is specified in the ctor
+//of FileDoesNotExistFilter, then we do nothing
+void FileDoesNotExistFilter::handle(
+ Reference<css::task::XInteractionRequest > const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+
+ css::ucb::InteractiveIOException ioexc;
+ if ((request>>= ioexc)
+ && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
+ || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
+ {
+ m_bExist = false;
+ return;
+ }
+ Reference<css::task::XInteractionHandler> xInteraction;
+ if (m_xCommandEnv.is()) {
+ xInteraction = m_xCommandEnv->getInteractionHandler();
+ }
+ if (xInteraction.is()) {
+ xInteraction->handle(xRequest);
+ }
+}
+
+}
+
+namespace dp_misc {
+
+DescriptionInfoset getDescriptionInfoset(OUString const & sExtensionFolderURL)
+{
+ Reference< css::xml::dom::XNode > root;
+ Reference<css::uno::XComponentContext> context(
+ comphelper::getProcessComponentContext());
+ try {
+ root =
+ ExtensionDescription(
+ context, sExtensionFolderURL,
+ Reference< css::ucb::XCommandEnvironment >()).
+ getRootElement();
+ } catch (const NoDescriptionException &) {
+ } catch (const css::deployment::DeploymentException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.deployment.DeploymentException: " + e.Message,
+ nullptr, anyEx );
+ }
+ return DescriptionInfoset(context, root);
+}
+
+DescriptionInfoset::DescriptionInfoset(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::xml::dom::XNode > const & element):
+ m_context(context),
+ m_element(element)
+{
+ if (m_element.is()) {
+ m_xpath = css::xml::xpath::XPathAPI::create(context);
+ m_xpath->registerNS("desc", element->getNamespaceURI());
+ m_xpath->registerNS("xlink", "http://www.w3.org/1999/xlink");
+ }
+}
+
+DescriptionInfoset::~DescriptionInfoset() {}
+
+::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
+ return getOptionalValue("desc:identifier/@value");
+}
+
+OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is() ? getNodeValue(n) : OUString();
+}
+
+void DescriptionInfoset::checkBlacklist() const
+{
+ if (!m_element.is())
+ return;
+
+ std::optional< OUString > id(getIdentifier());
+ if (!id)
+ return; // nothing to check
+ OUString currentversion(getVersion());
+ if (currentversion.getLength() == 0)
+ return; // nothing to check
+
+ css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", css::uno::Any(OUString("/org.openoffice.Office.ExtensionDependencies/Extensions"))}
+ }));
+ css::uno::Reference< css::container::XNameAccess > blacklist(
+ (css::configuration::theDefaultProvider::get(m_context)
+ ->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", args)),
+ css::uno::UNO_QUERY_THROW);
+
+ // check first if a blacklist entry is available
+ if (!(blacklist.is() && blacklist->hasByName(*id))) return;
+
+ css::uno::Reference< css::beans::XPropertySet > extProps(
+ blacklist->getByName(*id), css::uno::UNO_QUERY_THROW);
+
+ css::uno::Any anyValue = extProps->getPropertyValue("Versions");
+
+ css::uno::Sequence< OUString > blversions;
+ anyValue >>= blversions;
+
+ // check if the current version requires further dependency checks from the blacklist
+ if (!checkBlacklistVersion(currentversion, blversions)) return;
+
+ anyValue = extProps->getPropertyValue("Dependencies");
+ OUString udeps;
+ anyValue >>= udeps;
+
+ if (udeps.getLength() == 0)
+ return; // nothing todo
+
+ OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
+
+ css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
+ m_context->getServiceManager()->createInstanceWithContext("com.sun.star.xml.dom.DocumentBuilder", m_context),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
+
+ css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
+ css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
+ css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
+ sal_Int32 nLen = xDeps->getLength();
+
+ // get the parent xml document of current description info for the import
+ css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
+
+ // get dependency node of current description info to merge the new dependencies from the blacklist
+ css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
+ m_xpath->selectSingleNode(m_element, "desc:dependencies"));
+
+ // if no dependency node exists, create a new one in the current description info
+ if (!xCurrentDeps.is()) {
+ css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
+ xCurrentDescInfo->createElementNS(
+ "http://openoffice.org/extensions/description/2006",
+ "dependencies"), css::uno::UNO_QUERY_THROW);
+ m_element->appendChild(xNewDepNode);
+ xCurrentDeps = m_xpath->selectSingleNode(m_element, "desc:dependencies");
+ }
+
+ for (sal_Int32 i=0; i<nLen; i++) {
+ css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
+ css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
+ if (xDep.is()) {
+ // found valid blacklist dependency, import the node first and append it to the existing dependency node
+ css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
+ xCurrentDeps->appendChild(importedNode);
+ }
+ }
+}
+
+bool DescriptionInfoset::checkBlacklistVersion(
+ const OUString& currentversion,
+ css::uno::Sequence< OUString > const & versions)
+{
+ sal_Int32 nLen = versions.getLength();
+ for (sal_Int32 i=0; i<nLen; i++) {
+ if (currentversion == versions[i])
+ return true;
+ }
+
+ return false;
+}
+
+OUString DescriptionInfoset::getVersion() const
+{
+ return getNodeValueFromExpression( "desc:version/@value" );
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
+{
+ //When there is no description.xml then we assume that we support all platforms
+ if (! m_element.is())
+ {
+ return { OUString("all") };
+ }
+
+ //Check if the <platform> element was provided. If not the default is "all" platforms
+ css::uno::Reference< css::xml::dom::XNode > nodePlatform(
+ m_xpath->selectSingleNode(m_element, "desc:platform"));
+ if (!nodePlatform.is())
+ {
+ return { OUString("all") };
+ }
+
+ //There is a platform element.
+ const OUString value = getNodeValueFromExpression("desc:platform/@value");
+ //parse the string, it can contained multiple strings separated by commas
+ std::vector< OUString> vec;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const OUString aToken = value.getToken( 0, ',', nIndex ).trim();
+ if (!aToken.isEmpty())
+ vec.push_back(aToken);
+
+ }
+ while (nIndex >= 0);
+
+ return comphelper::containerToSequence(vec);
+}
+
+css::uno::Reference< css::xml::dom::XNodeList >
+DescriptionInfoset::getDependencies() const {
+ if (m_element.is()) {
+ try {
+ // check the extension blacklist first and expand the dependencies if applicable
+ checkBlacklist();
+
+ return m_xpath->selectNodeList(m_element, "desc:dependencies/*");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return new EmptyNodeList;
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateInformationUrls() const {
+ return getUrls("desc:update-information/desc:src/@xlink:href");
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateDownloadUrls() const
+{
+ return getUrls("desc:update-download/desc:src/@xlink:href");
+}
+
+OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
+{
+ css::uno::Sequence< OUString > aStrList = getUrls( "desc:icon/desc:default/@xlink:href" );
+ css::uno::Sequence< OUString > aStrListHC = getUrls( "desc:icon/desc:high-contrast/@xlink:href" );
+
+ if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
+ return aStrListHC[0];
+
+ if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
+ return aStrList[0];
+
+ return OUString();
+}
+
+::std::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
+ const
+{
+ bool bParentExists = false;
+ const OUString sURL (getLocalizedHREFAttrFromChild("/desc:description/desc:update-website", &bParentExists ));
+
+ if (!sURL.isEmpty())
+ return ::std::optional< OUString >(sURL);
+ else
+ return bParentExists ? ::std::optional< OUString >(OUString()) :
+ ::std::optional< OUString >();
+}
+
+::std::optional< OUString > DescriptionInfoset::getOptionalValue(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is()
+ ? ::std::optional< OUString >(getNodeValue(n))
+ : ::std::optional< OUString >();
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNodeList > ns;
+ if (m_element.is()) {
+ try {
+ ns = m_xpath->selectNodeList(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
+ for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
+ urls[i] = getNodeValue(ns->item(i));
+ }
+ return urls;
+}
+
+std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:publisher");
+
+ OUString sPublisherName;
+ OUString sURL;
+ if (node.is())
+ {
+ const OUString exp1("text()");
+ css::uno::Reference< css::xml::dom::XNode > xPathName;
+ try {
+ xPathName = m_xpath->selectSingleNode(node, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xPathName.is());
+ if (xPathName.is())
+ sPublisherName = xPathName->getNodeValue();
+
+ const OUString exp2("@xlink:href");
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, exp2);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ return std::make_pair(sPublisherName, sURL);
+}
+
+OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:release-notes", nullptr);
+}
+
+OUString DescriptionInfoset::getLocalizedDisplayName() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:display-name");
+ if (node.is())
+ {
+ const OUString exp("text()");
+ css::uno::Reference< css::xml::dom::XNode > xtext;
+ try {
+ xtext = m_xpath->selectSingleNode(node, exp);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (xtext.is())
+ return xtext->getNodeValue();
+ }
+ return OUString();
+}
+
+OUString DescriptionInfoset::getLocalizedLicenseURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:registration/desc:simple-license", nullptr);
+
+}
+
+::std::optional<SimpleLicenseAttributes>
+DescriptionInfoset::getSimpleLicenseAttributes() const
+{
+ //Check if the node exist
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, "/desc:description/desc:registration/desc:simple-license/@accept-by");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (n.is())
+ {
+ SimpleLicenseAttributes attributes;
+ attributes.acceptBy =
+ getNodeValueFromExpression("/desc:description/desc:registration/desc:simple-license/@accept-by");
+
+ ::std::optional< OUString > suppressOnUpdate = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-on-update");
+ if (suppressOnUpdate)
+ attributes.suppressOnUpdate = (*suppressOnUpdate).trim().equalsIgnoreAsciiCase("true");
+ else
+ attributes.suppressOnUpdate = false;
+
+ ::std::optional< OUString > suppressIfRequired = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-if-required");
+ if (suppressIfRequired)
+ attributes.suppressIfRequired = (*suppressIfRequired).trim().equalsIgnoreAsciiCase("true");
+ else
+ attributes.suppressIfRequired = false;
+
+ return ::std::optional<SimpleLicenseAttributes>(attributes);
+ }
+ }
+ return ::std::optional<SimpleLicenseAttributes>();
+}
+
+OUString DescriptionInfoset::getLocalizedDescriptionURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:extension-description", nullptr);
+}
+
+css::uno::Reference< css::xml::dom::XNode >
+DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
+{
+ if ( ! m_element.is() || sParent.isEmpty())
+ return css::uno::Reference< css::xml::dom::XNode > ();
+
+ css::uno::Reference< css::xml::dom::XNode > xParent;
+ try {
+ xParent = m_xpath->selectSingleNode(m_element, sParent);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+ if (xParent.is())
+ {
+ nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
+
+ //office: en-DE, en, en-DE-altmark
+ if (! nodeMatch.is())
+ {
+ // Already tried full tag, continue with first fallback.
+ const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
+ for (auto const& fallback : aFallbacks)
+ {
+ nodeMatch = matchLanguageTag(xParent, fallback);
+ if (nodeMatch.is())
+ break;
+ }
+ if (! nodeMatch.is())
+ nodeMatch = getChildWithDefaultLocale(xParent);
+ }
+ }
+
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::matchLanguageTag(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent, OUString const & rTag) const
+{
+ OSL_ASSERT(xParent.is());
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+
+ //first try exact match for lang
+ const OUString exp1("*[@lang=\"" + rTag + "\"]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ //try to match in strings that also have a country and/or variant, for
+ //example en matches in en-US-montana, en-US, en-montana
+ if (!nodeMatch.is())
+ {
+ const OUString exp2(
+ "*[starts-with(@lang,\"" + rTag + "-\")]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
+ const & xParent) const
+{
+ OSL_ASSERT(xParent.is());
+ if ( xParent->getNodeName() == "simple-license" )
+ {
+ css::uno::Reference<css::xml::dom::XNode> nodeDefault;
+ try {
+ nodeDefault = m_xpath->selectSingleNode(xParent, "@default-license-id");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (nodeDefault.is())
+ {
+ //The old way
+ const OUString exp1("desc:license-text[@license-id = \""
+ + nodeDefault->getNodeValue()
+ + "\"]");
+ try {
+ return m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ }
+
+ const OUString exp2("*[1]");
+ try {
+ return m_xpath->selectSingleNode(xParent, exp2);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ return nullptr;
+ }
+}
+
+OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
+ OUString const & sXPathParent, bool * out_bParentExists)
+ const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild(sXPathParent);
+
+ OUString sURL;
+ if (node.is())
+ {
+ if (out_bParentExists)
+ *out_bParentExists = true;
+ const OUString exp("@xlink:href");
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, exp);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ else
+ {
+ if (out_bParentExists)
+ *out_bParentExists = false;
+ }
+ return sURL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_identifier.cxx b/desktop/source/deployment/misc/dp_identifier.cxx
new file mode 100644
index 000000000..350f9a1f0
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_identifier.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <optional>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+
+namespace dp_misc {
+
+OUString generateIdentifier(
+ ::std::optional< OUString > const & optional,
+ OUString const & fileName)
+{
+ return optional ? *optional : generateLegacyIdentifier(fileName);
+}
+
+OUString getIdentifier(
+ css::uno::Reference< css::deployment::XPackage > const & package)
+{
+ OSL_ASSERT(package.is());
+ css::beans::Optional< OUString > id(package->getIdentifier());
+ return id.IsPresent
+ ? id.Value : generateLegacyIdentifier(package->getName());
+}
+
+OUString generateLegacyIdentifier(OUString const & fileName) {
+ return "org.openoffice.legacy." + fileName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_interact.cxx b/desktop/source/deployment/misc/dp_interact.cxx
new file mode 100644
index 000000000..6bb4e7e3c
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_interact.cxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_interact.h>
+
+#include <comphelper/interaction.hxx>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <osl/diagnose.h>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc {
+namespace {
+
+
+class InteractionContinuationImpl : public ::cppu::OWeakObject,
+ public task::XInteractionContinuation
+{
+ const Type m_type;
+ bool * m_pselect;
+
+public:
+ InteractionContinuationImpl( Type const & type, bool * pselect )
+ : m_type( type ),
+ m_pselect( pselect )
+ { OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(m_type) ); }
+
+ // XInterface
+ virtual void SAL_CALL acquire() throw () override;
+ virtual void SAL_CALL release() throw () override;
+ virtual Any SAL_CALL queryInterface( Type const & type ) override;
+
+ // XInteractionContinuation
+ virtual void SAL_CALL select() override;
+};
+
+// XInterface
+
+void InteractionContinuationImpl::acquire() throw ()
+{
+ OWeakObject::acquire();
+}
+
+
+void InteractionContinuationImpl::release() throw ()
+{
+ OWeakObject::release();
+}
+
+
+Any InteractionContinuationImpl::queryInterface( Type const & type )
+{
+ if (type.isAssignableFrom( m_type )) {
+ Reference<task::XInteractionContinuation> xThis(this);
+ return Any( &xThis, type );
+ }
+ else
+ return OWeakObject::queryInterface(type);
+}
+
+// XInteractionContinuation
+
+void InteractionContinuationImpl::select()
+{
+ *m_pselect = true;
+}
+
+} // anon namespace
+
+
+bool interactContinuation( Any const & request,
+ Type const & continuation,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool * pcont, bool * pabort )
+{
+ OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(
+ continuation ) );
+ if (xCmdEnv.is()) {
+ Reference<task::XInteractionHandler> xInteractionHandler(
+ xCmdEnv->getInteractionHandler() );
+ if (xInteractionHandler.is()) {
+ bool cont = false;
+ bool abort = false;
+ std::vector< Reference<task::XInteractionContinuation> > conts {
+ new InteractionContinuationImpl(continuation, &cont ),
+ new InteractionContinuationImpl( cppu::UnoType<task::XInteractionAbort>::get(), &abort ) };
+ xInteractionHandler->handle(
+ new ::comphelper::OInteractionRequest( request, conts ) );
+ if (cont || abort) {
+ if (pcont != nullptr)
+ *pcont = cont;
+ if (pabort != nullptr)
+ *pabort = abort;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// XAbortChannel
+
+void AbortChannel::sendAbort()
+{
+ m_aborted = true;
+ if (m_xNext.is())
+ m_xNext->sendAbort();
+}
+
+} // dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_misc.cxx b/desktop/source/deployment/misc/dp_misc.cxx
new file mode 100644
index 000000000..513294535
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_misc.cxx
@@ -0,0 +1,555 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+#include <config_features.h>
+#include <chrono>
+
+#include <dp_misc.h>
+#include <dp_interact.h>
+#include <rtl/uri.hxx>
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/bootstrap.hxx>
+#include <osl/file.hxx>
+#include <osl/pipe.hxx>
+#include <osl/security.hxx>
+#include <osl/thread.hxx>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/UnoUrlResolver.hpp>
+#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <memory>
+#include <string_view>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <salhelper/linkhelper.hxx>
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc {
+namespace {
+
+struct UnoRc : public rtl::StaticWithInit<
+ std::shared_ptr<rtl::Bootstrap>, UnoRc> {
+ std::shared_ptr<rtl::Bootstrap> operator () () {
+ OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
+ ::rtl::Bootstrap::expandMacros( unorc );
+ auto ret = std::make_shared<::rtl::Bootstrap>( unorc );
+ OSL_ASSERT( ret->getHandle() != nullptr );
+ return ret;
+ }
+};
+
+struct OfficePipeId : public rtl::StaticWithInit<OUString, OfficePipeId> {
+ OUString operator () ();
+};
+
+OUString OfficePipeId::operator () ()
+{
+ OUString userPath;
+ ::utl::Bootstrap::PathStatus aLocateResult =
+ ::utl::Bootstrap::locateUserInstallation( userPath );
+ if (!(aLocateResult == ::utl::Bootstrap::PATH_EXISTS ||
+ aLocateResult == ::utl::Bootstrap::PATH_VALID))
+ {
+ throw Exception("Extension Manager: Could not obtain path for UserInstallation.", nullptr);
+ }
+
+ rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
+ if (!digest) {
+ throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", nullptr );
+ }
+
+ sal_uInt8 const * data =
+ reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
+ std::size_t size = userPath.getLength() * sizeof (sal_Unicode);
+ sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
+ std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] );
+
+ rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_get( digest, md5_buf.get(), md5_key_len );
+ rtl_digest_destroy( digest );
+
+ // create hex-value string from the MD5 value to keep
+ // the string size minimal
+ OUStringBuffer buf;
+ buf.append( "SingleOfficeIPC_" );
+ for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
+ buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+bool existsOfficePipe()
+{
+ OUString const & pipeId = OfficePipeId::get();
+ if (pipeId.isEmpty())
+ return false;
+ ::osl::Security sec;
+ ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
+ return pipe.is();
+}
+
+//get modification time
+bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
+{
+ salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
+
+ if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
+ return false;
+
+ rTime = aResolver.m_aStatus.getModifyTime();
+
+ return true;
+}
+
+//Returns true if the Folder was more recently modified then
+//the lastsynchronized file. That is the repository needs to
+//be synchronized.
+bool compareExtensionFolderWithLastSynchronizedFile(
+ OUString const & folderURL, OUString const & fileURL)
+{
+ bool bNeedsSync = false;
+ ::osl::DirectoryItem itemExtFolder;
+ ::osl::File::RC err1 =
+ ::osl::DirectoryItem::get(folderURL, itemExtFolder);
+ //If it does not exist, then there is nothing to be done
+ if (err1 == ::osl::File::E_NOENT)
+ {
+ return false;
+ }
+ else if (err1 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access extension folder");
+ return true; //sync just in case
+ }
+
+ //If last synchronized does not exist, then OOo is started for the first time
+ ::osl::DirectoryItem itemFile;
+ ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
+ if (err2 == ::osl::File::E_NOENT)
+ {
+ return true;
+
+ }
+ else if (err2 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access file lastsynchronized");
+ return true; //sync just in case
+ }
+
+ //compare the modification time of the extension folder and the last
+ //modified file
+ TimeValue timeFolder;
+ if (getModifyTimeTargetFile(folderURL, timeFolder))
+ {
+ TimeValue timeFile;
+ if (getModifyTimeTargetFile(fileURL, timeFile))
+ {
+ if (timeFile.Seconds < timeFolder.Seconds)
+ bNeedsSync = true;
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+
+ return bNeedsSync;
+}
+
+bool needToSyncRepository(OUString const & name)
+{
+ OUString folder;
+ OUString file;
+ if ( name == "bundled" )
+ {
+ folder = "$BUNDLED_EXTENSIONS";
+ file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else if ( name == "shared" )
+ {
+ folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ return true;
+ }
+ ::rtl::Bootstrap::expandMacros(folder);
+ ::rtl::Bootstrap::expandMacros(file);
+ return compareExtensionFolderWithLastSynchronizedFile(
+ folder, file);
+}
+
+
+} // anon namespace
+
+
+namespace {
+OUString encodeForRcFile( OUString const & str )
+{
+ // escape $\{} (=> rtl bootstrap files)
+ OUStringBuffer buf(64);
+ sal_Int32 pos = 0;
+ const sal_Int32 len = str.getLength();
+ for ( ; pos < len; ++pos ) {
+ sal_Unicode c = str[ pos ];
+ switch (c) {
+ case '$':
+ case '\\':
+ case '{':
+ case '}':
+ buf.append( '\\' );
+ break;
+ }
+ buf.append( c );
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+
+OUString makeURL( OUString const & baseURL, OUString const & relPath_ )
+{
+ OUStringBuffer buf(128);
+ if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/')
+ buf.append( std::u16string_view(baseURL).substr(0, baseURL.getLength() - 1) );
+ else
+ buf.append( baseURL );
+ OUString relPath(relPath_);
+ if( relPath.startsWith("/") )
+ relPath = relPath.copy( 1 );
+ if (!relPath.isEmpty())
+ {
+ buf.append( '/' );
+ if (baseURL.match( "vnd.sun.star.expand:" )) {
+ // encode for macro expansion: relPath is supposed to have no
+ // macros, so encode $, {} \ (bootstrap mimic)
+ relPath = encodeForRcFile(relPath);
+
+ // encode once more for vnd.sun.star.expand schema:
+ // vnd.sun.star.expand:$UNO_...
+ // will expand to file-url
+ relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+ buf.append( relPath );
+ }
+ return buf.makeStringAndClear();
+}
+
+OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & segment )
+{
+ OSL_ASSERT(segment.indexOf(u'/') == -1);
+
+ ::rtl::Uri::encode(
+ segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+ return makeURL(baseURL, segment);
+}
+
+
+OUString expandUnoRcTerm( OUString const & term_ )
+{
+ OUString term(term_);
+ UnoRc::get()->expandMacrosFrom( term );
+ return term;
+}
+
+OUString makeRcTerm( OUString const & url )
+{
+ OSL_ASSERT( url.match( "vnd.sun.star.expand:" ));
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcterm = ::rtl::Uri::decode(
+ rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ return rcterm;
+ }
+ else
+ return url;
+}
+
+
+OUString expandUnoRcUrl( OUString const & url )
+{
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcurl = ::rtl::Uri::decode(
+ rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ // expand macro string:
+ UnoRc::get()->expandMacrosFrom( rcurl );
+ return rcurl;
+ }
+ else {
+ return url;
+ }
+}
+
+
+bool office_is_running()
+{
+ //We need to check if we run within the office process. Then we must not use the pipe, because
+ //this could cause a deadlock. This is actually a workaround for i82778
+ OUString sFile;
+ oslProcessError err = osl_getExecutableFile(& sFile.pData);
+ bool ret = false;
+ if (osl_Process_E_None == err)
+ {
+ sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
+ if (
+#if defined _WIN32
+ //osl_getExecutableFile should deliver "soffice.bin" on windows
+ //even if swriter.exe, scalc.exe etc. was started. This is a bug
+ //in osl_getExecutableFile
+ sFile == "soffice.bin" || sFile == "soffice.exe" || sFile == "soffice.com"
+ || sFile == "soffice" || sFile == "swriter.exe" || sFile == "swriter"
+ || sFile == "scalc.exe" || sFile == "scalc" || sFile == "simpress.exe"
+ || sFile == "simpress" || sFile == "sdraw.exe" || sFile == "sdraw"
+ || sFile == "sbase.exe" || sFile == "sbase"
+#elif defined MACOSX
+ sFile == "soffice"
+#elif defined UNIX
+ sFile == "soffice.bin"
+#else
+#error "Unsupported platform"
+#endif
+
+ )
+ ret = true;
+ else
+ ret = existsOfficePipe();
+ }
+ else
+ {
+ OSL_FAIL("NOT osl_Process_E_None ");
+ //if osl_getExecutable file then we take the risk of creating a pipe
+ ret = existsOfficePipe();
+ }
+ return ret;
+}
+
+
+oslProcess raiseProcess(
+ OUString const & appURL, Sequence<OUString> const & args )
+{
+ ::osl::Security sec;
+ oslProcess hProcess = nullptr;
+ oslProcessError rc = osl_executeProcess(
+ appURL.pData,
+ reinterpret_cast<rtl_uString **>(
+ const_cast<OUString *>(args.getConstArray()) ),
+ args.getLength(),
+ osl_Process_DETACHED,
+ sec.getHandle(),
+ nullptr, // => current working dir
+ nullptr, 0, // => no env vars
+ &hProcess );
+
+ switch (rc) {
+ case osl_Process_E_None:
+ break;
+ case osl_Process_E_NotFound:
+ throw RuntimeException( "image not found!", nullptr );
+ case osl_Process_E_TimedOut:
+ throw RuntimeException( "timeout occurred!", nullptr );
+ case osl_Process_E_NoPermission:
+ throw RuntimeException( "permission denied!", nullptr );
+ case osl_Process_E_Unknown:
+ throw RuntimeException( "unknown error!", nullptr );
+ case osl_Process_E_InvalidError:
+ default:
+ throw RuntimeException( "unmapped error!", nullptr );
+ }
+
+ return hProcess;
+}
+
+
+OUString generateRandomPipeId()
+{
+ // compute some good pipe id:
+ static rtlRandomPool s_hPool = rtl_random_createPool();
+ if (s_hPool == nullptr)
+ throw RuntimeException( "cannot create random pool!?", nullptr );
+ sal_uInt8 bytes[ 32 ];
+ if (rtl_random_getBytes(
+ s_hPool, bytes, SAL_N_ELEMENTS(bytes) ) != rtl_Random_E_None) {
+ throw RuntimeException( "random pool error!?", nullptr );
+ }
+ OUStringBuffer buf;
+ for (unsigned char byte : bytes) {
+ buf.append( static_cast<sal_Int32>(byte), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+Reference<XInterface> resolveUnoURL(
+ OUString const & connectString,
+ Reference<XComponentContext> const & xLocalContext,
+ AbortChannel const * abortChannel )
+{
+ Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
+ bridge::UnoUrlResolver::create( xLocalContext ) );
+
+ for (int i = 0; i <= 40; ++i) // 20 seconds
+ {
+ if (abortChannel != nullptr && abortChannel->isAborted()) {
+ throw ucb::CommandAbortedException( "abort!" );
+ }
+ try {
+ return xUnoUrlResolver->resolve( connectString );
+ }
+ catch (const connection::NoConnectException &) {
+ if (i < 40)
+ {
+ ::osl::Thread::wait( std::chrono::milliseconds(500) );
+ }
+ else throw;
+ }
+ }
+ return nullptr; // warning C4715
+}
+
+static void writeConsoleWithStream(OUString const & sText, FILE * stream)
+{
+ OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
+ fprintf(stream, "%s", s.getStr());
+ fflush(stream);
+}
+
+void writeConsole(OUString const & sText)
+{
+ writeConsoleWithStream(sText, stdout);
+}
+
+void writeConsoleError(OUString const & sText)
+{
+ writeConsoleWithStream(sText, stderr);
+}
+
+OUString readConsole()
+{
+ char buf[1024];
+ memset(buf, 0, 1024);
+ // read one char less so that the last char in buf is always zero
+ if (fgets(buf, 1024, stdin) != nullptr)
+ {
+ OUString value = OStringToOUString(OString(buf), osl_getThreadTextEncoding());
+ return value.trim();
+ }
+ throw css::uno::RuntimeException("reading from stdin failed");
+}
+
+void TRACE(OUString const & sText)
+{
+ SAL_INFO("desktop.deployment", sText);
+}
+
+void syncRepositories(
+ bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ OUString sDisable;
+ ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
+ if (!sDisable.isEmpty())
+ return;
+
+ Reference<deployment::XExtensionManager> xExtensionManager;
+ //synchronize shared before bundled otherwise there are
+ //more revoke and registration calls.
+ bool bModified = false;
+ if (force || needToSyncRepository("shared") || needToSyncRepository("bundled"))
+ {
+ xExtensionManager =
+ deployment::ExtensionManager::get(
+ comphelper::getProcessComponentContext());
+
+ if (xExtensionManager.is())
+ {
+ bModified = xExtensionManager->synchronize(
+ Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ }
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if (bModified && !comphelper::LibreOfficeKit::isActive())
+ {
+ Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
+ if (restarter.is())
+ {
+ restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
+ Reference<task::XInteractionHandler>());
+ }
+ }
+#endif
+}
+
+void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
+{
+ if (!ctx.is())
+ return;
+
+ Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(ctx) );
+
+ const Sequence< Reference<css::bridge::XBridge> >seqBridges = bridgeFac->getExistingBridges();
+ for (const Reference<css::bridge::XBridge>& bridge : seqBridges)
+ {
+ Reference<css::lang::XComponent> comp(bridge, UNO_QUERY);
+ if (comp.is())
+ {
+ try {
+ comp->dispose();
+ }
+ catch ( const css::lang::DisposedException& )
+ {
+ }
+ }
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_platform.cxx b/desktop/source/deployment/misc/dp_platform.cxx
new file mode 100644
index 000000000..9e4fd320b
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_platform.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_platform.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+
+#define PLATFORM_ALL "all"
+
+
+namespace dp_misc
+{
+namespace
+{
+ struct StrOperatingSystem :
+ public rtl::StaticWithInit<OUString, StrOperatingSystem> {
+ OUString operator () () {
+ OUString os( "$_OS" );
+ ::rtl::Bootstrap::expandMacros( os );
+ return os;
+ }
+ };
+
+ struct StrCPU :
+ public rtl::StaticWithInit<OUString, StrCPU> {
+ OUString operator () () {
+ OUString arch( "$_ARCH" );
+ ::rtl::Bootstrap::expandMacros( arch );
+ return arch;
+ }
+ };
+
+
+ struct StrPlatform : public rtl::StaticWithInit<
+ OUString, StrPlatform> {
+ OUString operator () () {
+ OUStringBuffer buf;
+ buf.append( StrOperatingSystem::get() );
+ buf.append( '_' );
+ buf.append( StrCPU::get() );
+ return buf.makeStringAndClear();
+ }
+ };
+
+ bool checkOSandCPU(OUString const & os, OUString const & cpu)
+ {
+ return (os == StrOperatingSystem::get())
+ && (cpu == StrCPU::get());
+ }
+
+ bool isPlatformSupported( OUString const & token )
+ {
+ bool ret = false;
+ if (token == PLATFORM_ALL)
+ ret = true;
+ else if (token == "windows_x86")
+ ret = checkOSandCPU("Windows", "x86");
+ else if (token == "windows_x86_64")
+ ret = checkOSandCPU("Windows", "X86_64");
+ else if (token == "solaris_sparc")
+ ret = checkOSandCPU("Solaris", "SPARC");
+ else if (token == "solaris_sparc64")
+ ret = checkOSandCPU("Solaris", "SPARC64");
+ else if (token == "solaris_x86")
+ ret = checkOSandCPU("Solaris", "x86");
+ else if (token == "aix_powerpc")
+ ret = checkOSandCPU("AIX", "PowerPC");
+ else if (token == "macosx_x86_64")
+ ret = checkOSandCPU("MacOSX", "X86_64");
+ else if (token == "linux_x86")
+ ret = checkOSandCPU("Linux", "x86");
+ else if (token == "linux_x86_64")
+ ret = checkOSandCPU("Linux", "X86_64");
+ else if (token == "linux_sparc")
+ ret = checkOSandCPU("Linux", "SPARC");
+ else if (token == "linux_sparc64")
+ ret = checkOSandCPU("Linux", "SPARC64");
+ else if (token == "linux_powerpc")
+ ret = checkOSandCPU("Linux", "PowerPC");
+ else if (token == "linux_powerpc64")
+ ret = checkOSandCPU("Linux", "PowerPC_64");
+ else if (token == "linux_powerpc64_le")
+ ret = checkOSandCPU("Linux", "PowerPC_64_LE");
+ else if (token == "linux_arm_eabi")
+ ret = checkOSandCPU("Linux", "ARM_EABI");
+ else if (token == "linux_arm_oabi")
+ ret = checkOSandCPU("Linux", "ARM_OABI");
+ else if (token == "linux_mips_el")
+ ret = checkOSandCPU("Linux", "MIPS_EL");
+ else if (token == "linux_mips64_el")
+ ret = checkOSandCPU("Linux", "MIPS64_EL");
+ else if (token == "linux_mips_eb")
+ ret = checkOSandCPU("Linux", "MIPS_EB");
+ else if (token == "linux_mips64_eb")
+ ret = checkOSandCPU("Linux", "MIPS64_EB");
+ else if (token == "linux_ia64")
+ ret = checkOSandCPU("Linux", "IA64");
+ else if (token == "linux_m68k")
+ ret = checkOSandCPU("Linux", "M68K");
+ else if (token == "linux_s390")
+ ret = checkOSandCPU("Linux", "S390");
+ else if (token == "linux_s390x")
+ ret = checkOSandCPU("Linux", "S390x");
+ else if (token == "linux_hppa")
+ ret = checkOSandCPU("Linux", "HPPA");
+ else if (token == "linux_alpha")
+ ret = checkOSandCPU("Linux", "ALPHA");
+ else if (token == "linux_aarch64")
+ ret = checkOSandCPU("Linux", "AARCH64");
+ else if (token == "freebsd_x86")
+ ret = checkOSandCPU("FreeBSD", "x86");
+ else if (token == "freebsd_x86_64")
+ ret = checkOSandCPU("FreeBSD", "X86_64");
+ else if (token == "freebsd_powerpc")
+ ret = checkOSandCPU("FreeBSD", "PowerPC");
+ else if (token == "kfreebsd_x86")
+ ret = checkOSandCPU("kFreeBSD", "x86");
+ else if (token == "kfreebsd_x86_64")
+ ret = checkOSandCPU("kFreeBSD", "X86_64");
+ else if (token == "netbsd_x86")
+ ret = checkOSandCPU("NetBSD", "x86");
+ else if (token == "netbsd_x86_64")
+ ret = checkOSandCPU("NetBSD", "X86_64");
+ else if (token == "openbsd_x86")
+ ret = checkOSandCPU("OpenBSD", "x86");
+ else if (token == "openbsd_x86_64")
+ ret = checkOSandCPU("OpenBSD", "X86_64");
+ else if (token == "dragonfly_x86")
+ ret = checkOSandCPU("DragonFly", "x86");
+ else if (token == "dragonfly_x86_64")
+ ret = checkOSandCPU("DragonFly", "X86_64");
+ else
+ {
+ OSL_FAIL("Extension Manager: The extension supports an unknown platform. "
+ "Check the platform in the description.xml");
+ ret = false;
+ }
+ return ret;
+ }
+
+} // anon namespace
+
+
+OUString const & getPlatformString()
+{
+ return StrPlatform::get();
+}
+
+bool platform_fits( OUString const & platform_string )
+{
+ sal_Int32 index = 0;
+ for (;;)
+ {
+ const OUString token(
+ platform_string.getToken( 0, ',', index ).trim() );
+ // check if this platform:
+ if (token.equalsIgnoreAsciiCase( StrPlatform::get() ) ||
+ (token.indexOf( '_' ) < 0 && /* check OS part only */
+ token.equalsIgnoreAsciiCase( StrOperatingSystem::get() )))
+ {
+ return true;
+ }
+ if (index < 0)
+ break;
+ }
+ return false;
+}
+
+bool hasValidPlatform( css::uno::Sequence<OUString> const & platformStrings)
+{
+ bool ret = false;
+ for (const OUString& s : platformStrings)
+ {
+ if ( isPlatformSupported( s ) )
+ {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_resource.cxx b/desktop/source/deployment/misc/dp_resource.cxx
new file mode 100644
index 000000000..320406176
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_resource.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_resource.h>
+#include <unotools/configmgr.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc {
+namespace {
+
+struct OfficeLocale :
+ public rtl::StaticWithInit<LanguageTag, OfficeLocale> {
+ LanguageTag operator () () {
+ OUString slang(utl::ConfigManager::getUILocale());
+ //fallback, the locale is currently only set when the user starts the
+ //office for the first time.
+ if (slang.isEmpty())
+ slang = "en-US";
+ return LanguageTag( slang);
+ }
+};
+
+} // anon namespace
+
+const LanguageTag & getOfficeLanguageTag()
+{
+ return OfficeLocale::get();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_ucb.cxx b/desktop/source/deployment/misc/dp_ucb.cxx
new file mode 100644
index 000000000..71e7d18a1
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_ucb.cxx
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <comphelper/processfactory.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc
+{
+
+
+bool create_ucb_content(
+ ::ucbhelper::Content * ret_ucbContent, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ try {
+ // Existence check...
+ // content ctor/isFolder() will throw exception in case the resource
+ // does not exist.
+
+ // dilemma: no chance to use the given handler here, because it would
+ // raise no such file dialogs, else no interaction for
+ // passwords, ...? xxx todo
+ ::ucbhelper::Content ucbContent(
+ url, Reference<XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ ucbContent.isFolder();
+
+ if (ret_ucbContent != nullptr)
+ {
+ ucbContent.setCommandEnvironment( xCmdEnv );
+ *ret_ucbContent = ucbContent;
+ }
+ return true;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ }
+ return false;
+}
+
+
+bool create_folder(
+ ::ucbhelper::Content * ret_ucb_content, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content, url_, xCmdEnv, false /* no throw */ ))
+ {
+ if (ucb_content.isFolder()) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+
+ OUString url( url_ );
+ // xxx todo: find parent
+ sal_Int32 slash = url.lastIndexOf( '/' );
+ if (slash < 0) {
+ // fallback:
+ url = expandUnoRcUrl( url );
+ slash = url.lastIndexOf( '/' );
+ }
+ if (slash < 0) {
+ // invalid: has to be at least "auth:/..."
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder (invalid path): '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+ }
+ ::ucbhelper::Content parentContent;
+ if (! create_folder(
+ &parentContent, url.copy( 0, slash ), xCmdEnv, throw_exc ))
+ return false;
+ const Any title( ::rtl::Uri::decode( url.copy( slash + 1 ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 ) );
+ const Sequence<ContentInfo> infos(
+ parentContent.queryCreatableContentsInfo() );
+ for ( ContentInfo const & info : infos )
+ {
+ // look KIND_FOLDER:
+ if ((info.Attributes & ContentInfoAttribute::KIND_FOLDER) != 0)
+ {
+ // make sure the only required bootstrap property is "Title":
+ Sequence<beans::Property> const & rProps = info.Properties;
+ if ( rProps.getLength() != 1 || rProps[ 0 ].Name != "Title" )
+ continue;
+
+ try {
+ if (parentContent.insertNewContent(
+ info.Type,
+ StrTitle::getTitleSequence(),
+ Sequence<Any>( &title, 1 ),
+ ucb_content )) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ // Interaction Handler already handled the error
+ // that has occurred...
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ }
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder: '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+}
+
+
+bool erase_path( OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content( &ucb_content, url, xCmdEnv, false /* no throw */ ))
+ {
+ try {
+ ucb_content.executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content )
+{
+ std::vector<sal_Int8> bytes;
+ Reference<io::XOutputStream> xStream(
+ ::xmlscript::createOutputStream( &bytes ) );
+ if (! ucb_content.openStream( xStream ))
+ throw RuntimeException(
+ "::ucbhelper::Content::openStream( XOutputStream ) failed!",
+ nullptr );
+ return bytes;
+}
+
+
+bool readLine( OUString * res, OUString const & startingWith,
+ ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), textenc );
+ sal_Int32 pos = 0;
+ for (;;)
+ {
+ if (file.match( startingWith, pos ))
+ {
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+ pos += startingWith.getLength();
+ for (;;)
+ {
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( std::u16string_view(file).substr(start) );
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ {
+ // consume extra CR
+ buf.append( std::u16string_view(file).substr(start, pos - start - 1) );
+ ++pos;
+ }
+ else
+ buf.append( std::u16string_view(file).substr(start, pos - start) );
+ ++pos; // consume LF
+ // check next line:
+ if (pos < file.getLength() &&
+ (file[ pos ] == ' ' || file[ pos ] == '\t'))
+ {
+ buf.append( ' ' );
+ ++pos;
+ start = pos;
+ continue;
+ }
+ }
+ break;
+ }
+ *res = buf.makeStringAndClear();
+ return true;
+ }
+ // next line:
+ sal_Int32 next_lf = file.indexOf( LF, pos );
+ if (next_lf < 0) // EOF
+ break;
+ pos = next_lf + 1;
+ }
+ return false;
+}
+
+bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
+ ::ucbhelper::Content & ucb_content )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), RTL_TEXTENCODING_UTF8);
+ sal_Int32 pos = 0;
+
+ for (;;)
+ {
+
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+
+ bool bEOF = false;
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( std::u16string_view(file).substr(start) );
+ bEOF = true;
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ // consume extra CR
+ buf.append( std::u16string_view(file).substr(start, pos - start - 1) );
+ else
+ buf.append( std::u16string_view(file).substr(start, pos - start) );
+ pos++;
+ }
+ OUString aLine = buf.makeStringAndClear();
+
+ sal_Int32 posEqual = aLine.indexOf('=');
+ if (posEqual > 0 && (posEqual + 1) < aLine.getLength())
+ {
+ OUString name = aLine.copy(0, posEqual);
+ OUString value = aLine.copy(posEqual + 1);
+ out_result.emplace_back(name, value);
+ }
+
+ if (bEOF)
+ break;
+ }
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_update.cxx b/desktop/source/deployment/misc/dp_update.cxx
new file mode 100644
index 000000000..7116be42b
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_update.cxx
@@ -0,0 +1,408 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <dp_update.hxx>
+#include <dp_version.hxx>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_misc {
+namespace {
+
+int determineHighestVersion(
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = 0;
+ OUString greatest = userVersion;
+ if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 1;
+ greatest = sharedVersion;
+ }
+ if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 2;
+ greatest = bundledVersion;
+ }
+ if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 3;
+ }
+ return index;
+}
+
+Sequence< Reference< xml::dom::XElement > >
+getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ Sequence< OUString > const & urls,
+ OUString const & identifier,
+ uno::Any & out_error)
+{
+ try {
+ return updateInformation->getUpdateInformation(urls, identifier);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const ucb::CommandFailedException & e) {
+ out_error = e.Reason;
+ } catch (const ucb::CommandAbortedException &) {
+ } catch (const uno::Exception & e) {
+ out_error <<= e;
+ }
+ return
+ Sequence<Reference< xml::dom::XElement > >();
+}
+
+void getOwnUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
+ bool & out_allFound)
+{
+ bool bAllHaveOwnUpdateInformation = true;
+ for (auto & inout : inout_map)
+ {
+ OSL_ASSERT(inout.second.extension.is());
+ Sequence<OUString> urls(inout.second.extension->getUpdateInformationURLs());
+ if (urls.hasElements())
+ {
+ const OUString search_id = dp_misc::getIdentifier(inout.second.extension);
+ SAL_INFO( "extensions.update", "Searching update for " << search_id );
+ uno::Any anyError;
+ //It is unclear from the idl if there can be a null reference returned.
+ //However all valid information should be the same
+ const Sequence<Reference< xml::dom::XElement > >
+ infos(getUpdateInformation(updateInformation, urls, search_id, anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(inout.second.extension, anyError);
+
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ dp_misc::DescriptionInfoset infoset(
+ xContext,
+ Reference< xml::dom::XNode >(element, UNO_QUERY_THROW));
+ if (!infoset.hasDescription())
+ continue;
+ std::optional< OUString > result_id(infoset.getIdentifier());
+ if (!result_id)
+ continue;
+ SAL_INFO( "extensions.update", " found version "
+ << infoset.getVersion() << " for " << *result_id );
+ if (*result_id != search_id)
+ continue;
+ inout.second.version = infoset.getVersion();
+ inout.second.info.set(element, UNO_QUERY_THROW);
+ break;
+ }
+ }
+ else
+ {
+ bAllHaveOwnUpdateInformation = false;
+ }
+ }
+ out_allFound = bAllHaveOwnUpdateInformation;
+}
+
+void getDefaultUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map,
+ std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ const OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
+ OSL_ASSERT(!sDefaultURL.isEmpty());
+
+ Any anyError;
+ const Sequence< Reference< xml::dom::XElement > >
+ infos(
+ getUpdateInformation(
+ updateInformation,
+ Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(Reference<deployment::XPackage>(), anyError);
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ Reference< xml::dom::XNode > node(element, UNO_QUERY_THROW);
+ dp_misc::DescriptionInfoset infoset(xContext, node);
+ std::optional< OUString > id(infoset.getIdentifier());
+ if (!id) {
+ continue;
+ }
+ UpdateInfoMap::iterator j = inout_map.find(*id);
+ if (j != inout_map.end())
+ {
+ //skip those extension which provide its own update urls
+ if (j->second.extension->getUpdateInformationURLs().getLength())
+ continue;
+ OUString v(infoset.getVersion());
+ //look for the highest version in the online repository
+ if (dp_misc::compareVersions(v, j->second.version) ==
+ dp_misc::GREATER)
+ {
+ j->second.version = v;
+ j->second.info = node;
+ }
+ }
+ }
+}
+
+bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
+{
+ OSL_ASSERT(sameIdExtensions.getLength() == 3);
+ return !sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is();
+}
+
+/** Returns true if the list of extensions are bundled extensions and there are no
+ other extensions with the same identifier in the shared or user repository.
+ If extensionList is NULL, then it is checked if there are only bundled extensions.
+*/
+bool onlyBundledExtensions(
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ std::vector< Reference<deployment::XPackage > > const * extensionList)
+{
+ OSL_ASSERT(xExtMgr.is());
+ bool bOnlyBundled = true;
+ if (extensionList)
+ {
+ for (auto const& elem : *extensionList)
+ {
+ Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(elem), elem->getName(), Reference<ucb::XCommandEnvironment>());
+
+ bOnlyBundled = containsBundledOnly(seqExt);
+ if (!bOnlyBundled)
+ break;
+ }
+ }
+ else
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
+ xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ for (int pos(0), nLen(seqAllExt.getLength()); bOnlyBundled && pos != nLen; ++pos)
+ {
+ bOnlyBundled = containsBundledOnly(seqAllExt[pos]);
+ }
+ }
+ return bOnlyBundled;
+}
+
+} // anon namespace
+
+
+OUString getExtensionDefaultUpdateURL()
+{
+ OUString sUrl(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ExtensionUpdateURL}");
+ ::rtl::Bootstrap::expandMacros(sUrl);
+ return sUrl;
+}
+
+/* returns the index of the greatest version, starting with 0
+
+ */
+UPDATE_SOURCE isUpdateUserExtension(
+ bool bReadOnlyShared,
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+ if (bReadOnlyShared)
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ else if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+
+ }
+ }
+ else
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ }
+
+ return retVal;
+}
+
+UPDATE_SOURCE isUpdateSharedExtension(
+ bool bReadOnlyShared,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ if (bReadOnlyShared)
+ return UPDATE_SOURCE_NONE;
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+
+ if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ return retVal;
+}
+
+Reference<deployment::XPackage>
+getExtensionWithHighestVersion(
+ Sequence<Reference<deployment::XPackage> > const & seqExt)
+{
+ if (!seqExt.hasElements())
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage> greatest;
+ sal_Int32 len = seqExt.getLength();
+
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ if (!greatest.is())
+ {
+ greatest = seqExt[i];
+ continue;
+ }
+ Reference<deployment::XPackage> const & current = seqExt[i];
+ //greatest has a value
+ if (! current.is())
+ continue;
+
+ if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
+ greatest = current;
+ }
+ return greatest;
+}
+
+UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
+extension(ext)
+{
+}
+
+
+UpdateInfoMap getOnlineUpdateInfos(
+ Reference<uno::XComponentContext> const &xContext,
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ std::vector<Reference<deployment::XPackage > > const * extensionList,
+ std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ OSL_ASSERT(xExtMgr.is());
+ UpdateInfoMap infoMap;
+ if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
+ return infoMap;
+
+ if (!extensionList)
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt = xExtMgr->getAllExtensions(
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
+ for (int pos = seqAllExt.getLength(); pos --; )
+ {
+ uno::Sequence<Reference<deployment::XPackage> > const & seqExt = seqAllExt[pos];
+
+ Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
+ OSL_ASSERT(extension.is());
+
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(extension), UpdateInfo(extension));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+ else
+ {
+ for (auto const& elem : *extensionList)
+ {
+ OSL_ASSERT(elem.is());
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(elem), UpdateInfo(elem));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+
+ //Now find the update information for the extensions which provide their own
+ //URLs to update information.
+ bool bAllInfosObtained = false;
+ getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, bAllInfosObtained);
+
+ if (!bAllInfosObtained)
+ getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
+ return infoMap;
+}
+OUString getHighestVersion(
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = determineHighestVersion(OUString(), sharedVersion, bundledVersion, onlineVersion);
+ switch (index)
+ {
+ case 1: return sharedVersion;
+ case 2: return bundledVersion;
+ case 3: return onlineVersion;
+ default: OSL_ASSERT(false);
+ }
+
+ return OUString();
+}
+} //namespace dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_version.cxx b/desktop/source/deployment/misc/dp_version.cxx
new file mode 100644
index 000000000..703045dde
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_version.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <dp_version.hxx>
+
+namespace {
+
+OUString getElement(OUString const & version, ::sal_Int32 * index)
+{
+ while (*index < version.getLength() && version[*index] == '0') {
+ ++*index;
+ }
+ return version.getToken(0, '.', *index);
+}
+
+}
+
+namespace dp_misc {
+
+::dp_misc::Order compareVersions(
+ OUString const & version1, OUString const & version2)
+{
+ for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
+ OUString e1(i1 >= 0 ? getElement(version1, &i1) : OUString());
+ OUString e2(i2 >= 0 ? getElement(version2, &i2) : OUString());
+ if (e1.getLength() < e2.getLength()) {
+ return ::dp_misc::LESS;
+ } else if (e1.getLength() > e2.getLength()) {
+ return ::dp_misc::GREATER;
+ } else if (e1 < e2) {
+ return ::dp_misc::LESS;
+ } else if (e1 > e2) {
+ return ::dp_misc::GREATER;
+ }
+ }
+ return ::dp_misc::EQUAL;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/lockfile.cxx b/desktop/source/deployment/misc/lockfile.cxx
new file mode 100644
index 000000000..50da67e97
--- /dev/null
+++ b/desktop/source/deployment/misc/lockfile.cxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#else
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+#include <comphelper/random.hxx>
+#include <sal/types.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <unotools/bootstrap.hxx>
+#include <tools/config.hxx>
+
+#include <lockfile.hxx>
+
+using namespace ::osl;
+using namespace ::utl;
+
+
+static OString impl_getHostname()
+{
+ OString aHost;
+#ifdef _WIN32
+ /*
+ prevent windows from connecting to the net to get its own
+ hostname by using the netbios name
+ */
+ DWORD sz = MAX_COMPUTERNAME_LENGTH + 1;
+ auto szHost = std::make_unique<char[]>(sz);
+ if (GetComputerNameA(szHost.get(), &sz))
+ aHost = OString(szHost.get());
+ else
+ aHost = OString("UNKNOWN");
+#else
+ /* Don't do dns lookup on Linux either */
+ char pHostName[1024];
+
+ if ( gethostname( pHostName, sizeof( pHostName ) - 1 ) == 0 )
+ {
+ pHostName[sizeof( pHostName ) - 1] = '\0';
+ aHost = OString( pHostName );
+ }
+ else
+ aHost = OString("UNKNOWN");
+#endif
+
+ return aHost;
+}
+
+namespace desktop {
+
+ Lockfile::Lockfile( bool bIPCserver )
+ :m_bIPCserver(bIPCserver)
+ ,m_bRemove(false)
+ ,m_bIsLocked(false)
+ {
+ // build the file-url to use for the lock
+ OUString aUserPath;
+ utl::Bootstrap::locateUserInstallation( aUserPath );
+ m_aLockname = aUserPath + "/.lock";
+
+ // generate ID
+ const int nIdBytes = 16;
+ char tmpId[nIdBytes*2+1];
+ time_t t = time(nullptr);
+ for (int i = 0; i<nIdBytes; i++) {
+ int tmpByte = comphelper::rng::uniform_int_distribution(0, 0xFF);
+ sprintf( tmpId+i*2, "%02X", tmpByte );
+ }
+ tmpId[nIdBytes*2]=0x00;
+ m_aId = OUString::createFromAscii( tmpId );
+
+ // generate date string
+ char *tmpTime = ctime( &t );
+ if (tmpTime != nullptr) {
+ m_aDate = OUString::createFromAscii( tmpTime );
+ sal_Int32 i = m_aDate.indexOf('\n');
+ if (i > 0)
+ m_aDate = m_aDate.copy(0, i);
+ }
+
+
+ // try to create file
+ File aFile(m_aLockname);
+ if (aFile.open( osl_File_OpenFlag_Create ) == File::E_EXIST) {
+ m_bIsLocked = true;
+ } else {
+ // new lock created
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ }
+ }
+
+ bool Lockfile::check( fpExecWarning execWarning )
+ {
+
+ if (m_bIsLocked) {
+ // lock existed, ask user what to do
+ if (isStale() ||
+ (execWarning != nullptr && (*execWarning)( this ))) {
+ // remove file and create new
+ File::remove( m_aLockname );
+ File aFile(m_aLockname);
+ (void)aFile.open( osl_File_OpenFlag_Create );
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ return true;
+ } else {
+ //leave alone and return false
+ m_bRemove = false;
+ return false;
+ }
+ } else {
+ // lock was created by us
+ return true;
+ }
+ }
+
+ bool Lockfile::isStale() const
+ {
+ // this checks whether the lockfile was created on the same
+ // host by the same user. Should this be the case it is safe
+ // to assume that it is a stale lockfile which can be overwritten
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+ OString aIPCserver = aConfig.ReadKey( LOCKFILE_IPCKEY );
+ if (!aIPCserver.equalsIgnoreAsciiCase("true"))
+ return false;
+
+ OString aHost = aConfig.ReadKey( LOCKFILE_HOSTKEY );
+ OString aUser = aConfig.ReadKey( LOCKFILE_USERKEY );
+
+ // lockfile from same host?
+ OString myHost( impl_getHostname() );
+ if (aHost == myHost) {
+ // lockfile by same UID
+ OUString myUserName;
+ Security aSecurity;
+ aSecurity.getUserName( myUserName );
+ OString myUser(OUStringToOString(myUserName, RTL_TEXTENCODING_ASCII_US));
+ if (aUser == myUser)
+ return true;
+ }
+ return false;
+ }
+
+ void Lockfile::syncToFile() const
+ {
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+
+ // get information
+ OString aHost( impl_getHostname() );
+ OUString aUserName;
+ Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ OString aUser = OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US );
+ OString aTime = OUStringToOString( m_aDate, RTL_TEXTENCODING_ASCII_US );
+ OString aStamp = OUStringToOString( m_aId, RTL_TEXTENCODING_ASCII_US );
+
+ // write information
+ aConfig.WriteKey( LOCKFILE_USERKEY, aUser );
+ aConfig.WriteKey( LOCKFILE_HOSTKEY, aHost );
+ aConfig.WriteKey( LOCKFILE_STAMPKEY, aStamp );
+ aConfig.WriteKey( LOCKFILE_TIMEKEY, aTime );
+ aConfig.WriteKey(
+ LOCKFILE_IPCKEY,
+ m_bIPCserver ? OString("true") : OString("false") );
+ aConfig.Flush( );
+ }
+
+ Lockfile::~Lockfile()
+ {
+ // unlock userdata by removing file
+ if ( m_bRemove )
+ File::remove( m_aLockname );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.cxx b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
new file mode 100644
index 000000000..05f265404
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_compbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/component-registry/2010"
+#define NS_PREFIX "comp"
+#define ROOT_ELEMENT_NAME "component-backend-db"
+#define KEY_ELEMENT_NAME "component"
+
+namespace dp_registry::backend::component {
+
+ComponentBackendDb::ComponentBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ComponentBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ComponentBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ComponentBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ComponentBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ComponentBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> componentNode = writeKeyElement(url);
+ writeSimpleElement("java-type-library",
+ OUString::boolean(data.javaTypeLibrary),
+ componentNode);
+
+ writeSimpleList(
+ data.implementationNames,
+ "implementation-names",
+ "name",
+ componentNode);
+
+ writeVectorOfPair(
+ data.singletons,
+ "singletons",
+ "item",
+ "key",
+ "value",
+ componentNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ComponentBackendDb::Data ComponentBackendDb::getEntry(OUString const & url)
+{
+ try
+ {
+ ComponentBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ bool bJava = readSimpleElement("java-type-library", aNode) == "true";
+ retData.javaTypeLibrary = bJava;
+
+ retData.implementationNames =
+ readList( aNode, "implementation-names", "name");
+
+ retData.singletons =
+ readVectorOfPair( aNode, "singletons", "item", "key", "value");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+} // namespace dp_registry::backend::component
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.hxx b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
new file mode 100644
index 000000000..b7bcdf50a
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_COMPONENT_DP_COMPBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_COMPONENT_DP_COMPBACKENDDB_HXX
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <deque>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace component {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+<component-backend-db xmlns="http://openoffice.org/extensionmanager/component-registry/2010">
+ <component url="vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages/5CD5.tmp_/leaves1.oxt/extensionoptions.jar">
+ <name>FileName</name>
+ <java-type-library>true</java-type-library>
+ <implementation-names>
+ <name>com.sun.star.comp.extensionoptions.OptionsEventHandler$_OptionsEventHandler</name>
+ ...
+ </implementation-names>
+ <singletons>
+ <item>
+ <key>com.sun.star.java.theJavaVirtualMachine</key>
+ <value>com.sun.star.java.JavaVirtualMachine</value>
+ </item>
+ ...
+ </singletons>
+ </component>
+
+ <component ...>
+ ...
+</component-backend-db>
+ */
+class ComponentBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ Data(): javaTypeLibrary(false) {};
+
+ std::deque< OUString> implementationNames;
+ std::vector< std::pair< OUString, OUString> >singletons;
+ // map from singleton names to implementation names
+ bool javaTypeLibrary;
+ };
+
+public:
+
+ ComponentBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ Data getEntry(OUString const & url);
+
+
+};
+
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_component.cxx b/desktop/source/deployment/registry/component/dp_component.cxx
new file mode 100644
index 000000000..8cb39ce47
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_component.cxx
@@ -0,0 +1,1706 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_shared.hxx>
+#include <dp_backend.h>
+#include <dp_platform.hxx>
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <comphelper/sequence.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/registry/XImplementationRegistration.hpp>
+#include <com/sun/star/loader/XImplementationLoader.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <algorithm>
+#include <deque>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include "dp_compbackenddb.hxx"
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::component {
+namespace {
+
+#define IMPLEMENTATION_NAME "com.sun.star.comp.deployment.component.PackageRegistryBackend"
+
+/** return a vector of bootstrap variables which have been provided
+ as command arguments.
+*/
+std::vector<OUString> getCmdBootstrapVariables()
+{
+ std::vector<OUString> ret;
+ sal_uInt32 count = osl_getCommandArgCount();
+ for (sal_uInt32 i = 0; i < count; i++)
+ {
+ OUString arg;
+ osl_getCommandArg(i, &arg.pData);
+ if (arg.startsWith("-env:"))
+ ret.push_back(arg);
+ }
+ return ret;
+}
+
+bool jarManifestHeaderPresent(
+ OUString const & url, OUString const & name,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUStringBuffer buf;
+ buf.append( "vnd.sun.star.zip://" );
+ buf.append(
+ ::rtl::Uri::encode(
+ url, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ buf.append( "/META-INF/MANIFEST.MF" );
+ ::ucbhelper::Content manifestContent;
+ OUString line;
+ return
+ create_ucb_content(
+ &manifestContent, buf.makeStringAndClear(), xCmdEnv,
+ false /* no throw */ )
+ && readLine( &line, name, manifestContent, RTL_TEXTENCODING_ASCII_US );
+}
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ComponentPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_loader;
+
+ enum class Reg { Uninit, Void, Registered, NotRegistered, MaybeRegistered };
+ Reg m_registered;
+
+ void getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > *
+ factories,
+ Reference<XComponentContext> const & xContext );
+
+ void componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories);
+
+ void componentLiveRemoval(ComponentBackendDb::Data const & data);
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ Reference<registry::XSimpleRegistry> getRDB() const;
+
+ public:
+ ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString const & loader, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class ComponentPackageImpl;
+
+ class ComponentsPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ public:
+ ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class ComponentsPackageImpl;
+
+ class TypelibraryPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const bool m_jarFile;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class TypelibraryPackageImpl;
+
+ /** Serves for unregistering packages that were registered on a
+ different platform. This can happen if one has remotely mounted
+ /home, for example.
+ */
+ class OtherPlatformPackageImpl : public ::dp_registry::backend::Package
+ {
+ public:
+ OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString const& rPlatform);
+
+ private:
+ BackendImpl * getMyBackend() const;
+
+ Reference<registry::XSimpleRegistry> impl_openRDB() const;
+ Reference<XInterface> impl_createInstance(OUString const& rService) const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ private:
+ OUString const m_aPlatform;
+ };
+ friend class OtherPlatformPackageImpl;
+
+ std::deque<OUString> m_jar_typelibs;
+ std::deque<OUString> m_rdb_typelibs;
+ std::deque<OUString> m_components;
+
+ enum RcItem { RCITEM_JAR_TYPELIB, RCITEM_RDB_TYPELIB, RCITEM_COMPONENTS };
+
+ std::deque<OUString> & getRcItemList( RcItem kind ) {
+ switch (kind)
+ {
+ case RCITEM_JAR_TYPELIB:
+ return m_jar_typelibs;
+ case RCITEM_RDB_TYPELIB:
+ return m_rdb_typelibs;
+ default: // case RCITEM_COMPONENTS
+ return m_components;
+ }
+ }
+
+ bool m_unorc_inited;
+ bool m_unorc_modified;
+ bool bSwitchedRdbFiles;
+
+ typedef std::unordered_map< OUString, Reference<XInterface> > t_string2object;
+ t_string2object m_backendObjects;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xDynComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xPythonComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xComponentsTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xRDBTypelibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaTypelibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ OUString m_commonRDB;
+ OUString m_nativeRDB;
+
+ //URLs of the original rdbs (before any switching):
+ OUString m_commonRDB_orig;
+ OUString m_nativeRDB_orig;
+
+ std::unique_ptr<ComponentBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ComponentBackendDb::Data const & data);
+ ComponentBackendDb::Data readDataFromDb(OUString const & url);
+ void revokeEntryFromDb(OUString const & url);
+
+ Reference<registry::XSimpleRegistry> m_xCommonRDB;
+ Reference<registry::XSimpleRegistry> m_xNativeRDB;
+
+ void unorc_verify_init( Reference<XCommandEnvironment> const & xCmdEnv );
+ void unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ Reference<XInterface> getObject( OUString const & id );
+ Reference<XInterface> insertObject(
+ OUString const & id, Reference<XInterface> const & xObject );
+ void releaseObject( OUString const & id );
+
+ void addToUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void removeFromUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ bool hasInUnoRc( RcItem kind, OUString const & url );
+
+ css::uno::Reference< css::uno::XComponentContext > getRootContext() const;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+
+ //Will be called from ComponentPackageImpl
+ void initServiceRdbFiles();
+};
+
+
+BackendImpl::ComponentPackageImpl::ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString const & loader, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_loader( loader ),
+ m_registered( Reg::Uninit )
+{}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::ComponentPackageImpl::getRDB() const
+{
+ BackendImpl * that = getMyBackend();
+
+ //Late "initialization" of the services rdb files
+ //This is to prevent problems when running several
+ //instances of OOo with root rights in parallel. This
+ //would otherwise cause problems when copying the rdbs.
+ //See http://qa.openoffice.org/issues/show_bug.cgi?id=99257
+ {
+ const ::osl::MutexGuard guard( getMutex() );
+ if (!that->bSwitchedRdbFiles)
+ {
+ that->bSwitchedRdbFiles = true;
+ that->initServiceRdbFiles();
+ }
+ }
+ if ( m_loader == "com.sun.star.loader.SharedLibrary" )
+ return that->m_xNativeRDB;
+ else
+ return that->m_xCommonRDB;
+}
+
+BackendImpl * BackendImpl::ComponentPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+
+void BackendImpl::disposing()
+{
+ try {
+ m_backendObjects = t_string2object();
+ if (m_xNativeRDB.is()) {
+ m_xNativeRDB->close();
+ m_xNativeRDB.clear();
+ }
+ if (m_xCommonRDB.is()) {
+ m_xCommonRDB->close();
+ m_xCommonRDB.clear();
+ }
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing...",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+void BackendImpl::initServiceRdbFiles()
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ ::ucbhelper::Content cacheDir( getCachePath(), xCmdEnv, m_xComponentContext );
+ ::ucbhelper::Content oldRDB;
+ // switch common rdb:
+ if (!m_commonRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL( getCachePath(), m_commonRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ m_commonRDB = m_commonRDB_orig == "common.rdb" ? OUStringLiteral("common_.rdb") : OUStringLiteral("common.rdb");
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_commonRDB, NameClash::OVERWRITE );
+ oldRDB = ::ucbhelper::Content();
+ }
+ // switch native rdb:
+ if (!m_nativeRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL(getCachePath(), m_nativeRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ const OUString plt_rdb( getPlatformString() + ".rdb" );
+ const OUString plt_rdb_( getPlatformString() + "_.rdb" );
+ m_nativeRDB = (m_nativeRDB_orig == plt_rdb ) ? plt_rdb_ : plt_rdb;
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_nativeRDB, NameClash::OVERWRITE );
+ }
+
+ // UNO is bootstrapped, flush for next process start:
+ m_unorc_modified = true;
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+
+ // common rdb for java, native rdb for shared lib components
+ if (!m_commonRDB.isEmpty()) {
+ m_xCommonRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_commonRDB ),
+ false, true);
+ }
+ if (!m_nativeRDB.isEmpty()) {
+ m_xNativeRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_nativeRDB ),
+ false, true);
+ }
+}
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_unorc_inited( false ),
+ m_unorc_modified( false ),
+ bSwitchedRdbFiles(false),
+ m_xDynComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString(),
+ "*" SAL_DLLEXTENSION,
+ DpResId(RID_STR_DYN_COMPONENT)
+ ) ),
+ m_xJavaComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_COMPONENT)
+ ) ),
+ m_xPythonComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Python",
+ "*.py",
+ DpResId(
+ RID_STR_PYTHON_COMPONENT)
+ ) ),
+ m_xComponentsTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-components",
+ "*.components",
+ DpResId(RID_STR_COMPONENTS)
+ ) ),
+ m_xRDBTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=RDB",
+ "*.rdb",
+ DpResId(RID_STR_RDB_TYPELIB)
+ ) ),
+ m_xJavaTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_TYPELIB)
+ ) ),
+ m_typeInfos( 6 )
+{
+ m_typeInfos[ 0 ] = m_xDynComponentTypeInfo;
+ m_typeInfos[ 1 ] = m_xJavaComponentTypeInfo;
+ m_typeInfos[ 2 ] = m_xPythonComponentTypeInfo;
+ m_typeInfos[ 3 ] = m_xComponentsTypeInfo;
+ m_typeInfos[ 4 ] = m_xRDBTypelibTypeInfo;
+ m_typeInfos[ 5 ] = m_xJavaTypelibTypeInfo;
+
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // in-mem rdbs:
+ // common rdb for java, native rdb for shared lib components
+ m_xCommonRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ m_xNativeRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ }
+ else
+ {
+ unorc_verify_init( xCmdEnv );
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ComponentBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ComponentBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ComponentBackendDb::Data BackendImpl::readDataFromDb(OUString const & url)
+{
+ ComponentBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType(mediaType_);
+ if ( mediaType.isEmpty() || mediaType == "application/vnd.sun.star.uno-component" || mediaType == "application/vnd.sun.star.uno-typelibrary" )
+ {
+ // detect exact media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv )) {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(SAL_DLLEXTENSION))
+ {
+ mediaType = "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString();
+ }
+ else if (title.endsWithIgnoreAsciiCase(".jar"))
+ {
+ if (jarManifestHeaderPresent(
+ url, "RegistrationClassName", xCmdEnv ))
+ mediaType = "application/vnd.sun.star.uno-component;type=Java";
+ if (mediaType.isEmpty())
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=Java";
+ }
+ else if (title.endsWithIgnoreAsciiCase(".py"))
+ mediaType = "application/vnd.sun.star.uno-component;type=Python";
+ else if (title.endsWithIgnoreAsciiCase(".rdb"))
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=RDB";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-component"))
+ {
+ // xxx todo: probe and evaluate component xml description
+
+ auto const iter = params.find(OString("platform"));
+ bool bPlatformFits(iter == params.end());
+ OUString aPlatform;
+ if (!bPlatformFits) // platform is specified, we have to check
+ {
+ aPlatform = iter->second.m_sValue;
+ bPlatformFits = platform_fits(aPlatform);
+ }
+ // If the package is being removed, do not care whether
+ // platform fits. We won't be using it anyway.
+ if (bPlatformFits || bRemoved) {
+ auto const iterType = params.find(OString("type"));
+ if (iterType != params.end())
+ {
+ OUString const & value = iterType->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("native")) {
+ if (bPlatformFits)
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ "com.sun.star.loader.SharedLibrary",
+ bRemoved, identifier);
+ else
+ return new BackendImpl::OtherPlatformPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ bRemoved, identifier, aPlatform);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xJavaComponentTypeInfo,
+ "com.sun.star.loader.Java2",
+ bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Python")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xPythonComponentTypeInfo,
+ "com.sun.star.loader.Python",
+ bRemoved, identifier);
+ }
+ }
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-components"))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter == params.end() || platform_fits(iter->second.m_sValue)) {
+ return new BackendImpl::ComponentsPackageImpl(
+ this, url, name, m_xComponentsTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-typelibrary"))
+ {
+ auto const iter = params.find(OString("type"));
+ if (iter != params.end()) {
+ OUString const & value = iter->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("RDB"))
+ {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xRDBTypelibTypeInfo,
+ false /* rdb */, bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xJavaTypelibTypeInfo,
+ true /* jar */, bRemoved, identifier);
+ }
+ }
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::unorc_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( getMutex() );
+ if ( m_unorc_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "unorc" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "UNO_JAVA_CLASSPATH=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = sizeof ("UNO_JAVA_CLASSPATH=") - 1;
+ do {
+ OUString token( line.getToken( 0, ' ', index ).trim() );
+ if (!token.isEmpty())
+ {
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The jar file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_jar_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_TYPES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = sizeof ("UNO_TYPES=") - 1;
+ do {
+ OUString token( line.getToken( 0, ' ', index ).trim() );
+ if (!token.isEmpty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.copy( 1 );
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The RDB file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc.
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_rdb_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ // The UNO_SERVICES line always has the BNF form
+ // "UNO_SERVICES="
+ // ("?$ORIGIN/" <common-rdb>)? -- first
+ // "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}"? -- second
+ // ("?" ("BUNDLED_EXTENSIONS" | -- third
+ // "UNO_SHARED_PACKAGES_CACHE" | "UNO_USER_PACKAGES_CACHE")
+ // ...)*
+ // so can unambiguously be split into its three parts:
+ int state = 1;
+ for (sal_Int32 i = RTL_CONSTASCII_LENGTH("UNO_SERVICES=");
+ i >= 0;)
+ {
+ OUString token(line.getToken(0, ' ', i));
+ if (!token.isEmpty())
+ {
+ if (state == 1 && token.match("?$ORIGIN/"))
+ {
+ m_commonRDB_orig = token.copy(
+ RTL_CONSTASCII_LENGTH("?$ORIGIN/"));
+ state = 2;
+ }
+ else if ( state <= 2 && token == "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" )
+ {
+ state = 3;
+ }
+ else
+ {
+ if (token[0] == '?')
+ {
+ token = token.copy(1);
+ }
+ m_components.push_back(token);
+ state = 3;
+ }
+ }
+ }
+ }
+
+ // native rc:
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), getPlatformString() + "rc"),
+ xCmdEnv, false /* no throw */ )) {
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ m_nativeRDB_orig = line.copy(
+ sizeof ("UNO_SERVICES=?$ORIGIN/") - 1 );
+ }
+ }
+ }
+ m_unorc_modified = false;
+ m_unorc_inited = true;
+}
+
+
+void BackendImpl::unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_unorc_inited || !m_unorc_modified)
+ return;
+
+ OStringBuffer buf;
+
+ buf.append("ORIGIN=");
+ OUString sOrigin = dp_misc::makeRcTerm(m_cachePath);
+ OString osOrigin = OUStringToOString(sOrigin, RTL_TEXTENCODING_UTF8);
+ buf.append(osOrigin);
+ buf.append(LF);
+
+ if (! m_jar_typelibs.empty())
+ {
+ auto iPos( m_jar_typelibs.cbegin() );
+ auto const iEnd( m_jar_typelibs.cend() );
+ buf.append( "UNO_JAVA_CLASSPATH=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_rdb_typelibs.empty())
+ {
+ auto iPos( m_rdb_typelibs.cbegin() );
+ auto const iEnd( m_rdb_typelibs.cend() );
+ buf.append( "UNO_TYPES=" );
+ while (iPos != iEnd) {
+ buf.append( '?' );
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // If we duplicated the common or native rdb then we must use those urls
+ //otherwise we use those of the original files. That is, m_commonRDB_orig
+ //and m_nativeRDB_orig;
+ OUString sCommonRDB(m_commonRDB.isEmpty() ? m_commonRDB_orig : m_commonRDB );
+ OUString sNativeRDB(m_nativeRDB.isEmpty() ? m_nativeRDB_orig : m_nativeRDB );
+
+ if (!sCommonRDB.isEmpty() || !sNativeRDB.isEmpty() ||
+ !m_components.empty())
+ {
+ buf.append( "UNO_SERVICES=" );
+ bool space = false;
+ if (!sCommonRDB.isEmpty())
+ {
+ buf.append( "?$ORIGIN/" );
+ buf.append( OUStringToOString(
+ sCommonRDB, RTL_TEXTENCODING_ASCII_US ) );
+ space = true;
+ }
+ if (!sNativeRDB.isEmpty())
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append( "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" );
+ space = true;
+
+ // write native rc:
+ OString buf2 =
+ "ORIGIN=" +
+ osOrigin +
+ OStringChar(LF) +
+ "UNO_SERVICES=?$ORIGIN/" +
+ OUStringToOString( sNativeRDB, RTL_TEXTENCODING_ASCII_US ) +
+ OStringChar(LF);
+
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf2.getStr()),
+ buf2.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), getPlatformString() + "rc" ),
+ xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+ }
+ for (auto const& component : m_components)
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append('?');
+ buf.append(OUStringToOString(component, RTL_TEXTENCODING_UTF8));
+ space = true;
+ }
+ buf.append(LF);
+ }
+
+ // write unorc:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "unorc" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_unorc_modified = false;
+}
+
+
+void BackendImpl::addToUnoRc( RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( getMutex() );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getRcItemList(kind);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+ }
+}
+
+
+void BackendImpl::removeFromUnoRc(
+ RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( getMutex() );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & aRcItemList = getRcItemList(kind);
+ aRcItemList.erase(std::remove(aRcItemList.begin(), aRcItemList.end(), rcterm), aRcItemList.end());
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+}
+
+
+bool BackendImpl::hasInUnoRc(
+ RcItem kind, OUString const & url_ )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( getMutex() );
+ std::deque<OUString> const & rSet = getRcItemList(kind);
+ return std::find( rSet.begin(), rSet.end(), rcterm ) != rSet.end();
+}
+
+css::uno::Reference< css::uno::XComponentContext > BackendImpl::getRootContext()
+ const
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getComponentContext()->getValueByName("_root"),
+ css::uno::UNO_QUERY);
+ return rootContext.is() ? rootContext : getComponentContext();
+}
+
+
+void BackendImpl::releaseObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( getMutex() );
+ m_backendObjects.erase( id );
+}
+
+
+Reference<XInterface> BackendImpl::getObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( getMutex() );
+ const t_string2object::const_iterator iFind( m_backendObjects.find( id ) );
+ if (iFind == m_backendObjects.end())
+ return Reference<XInterface>();
+ else
+ return iFind->second;
+}
+
+
+Reference<XInterface> BackendImpl::insertObject(
+ OUString const & id, Reference<XInterface> const & xObject )
+{
+ const ::osl::MutexGuard guard( getMutex() );
+ const std::pair<t_string2object::iterator, bool> insertion(
+ m_backendObjects.emplace( id, xObject ) );
+ return insertion.first->second;
+}
+
+
+Reference<XComponentContext> raise_uno_process(
+ Reference<XComponentContext> const & xContext,
+ ::rtl::Reference<AbortChannel> const & abortChannel )
+{
+ OSL_ASSERT( xContext.is() );
+
+ OUString url( util::theMacroExpander::get(xContext)->expandMacros( "$URE_BIN_DIR/uno" ) );
+
+ const OUString connectStr = "uno:pipe,name=" + generateRandomPipeId() + ";urp;uno.ComponentContext";
+
+ // raise core UNO process to register/run a component,
+ // javavm service uses unorc next to executable to retrieve deployed
+ // jar typelibs
+
+ std::vector<OUString> args{
+#if OSL_DEBUG_LEVEL == 0
+ "--quiet",
+#endif
+ "--singleaccept",
+ "-u",
+ connectStr,
+ // don't inherit from unorc:
+ "-env:INIFILENAME=" };
+
+ //now add the bootstrap variables which were supplied on the command line
+ std::vector<OUString> bootvars = getCmdBootstrapVariables();
+ args.insert(args.end(), bootvars.begin(), bootvars.end());
+
+ oslProcess hProcess;
+ try {
+ hProcess = raiseProcess(
+ url, comphelper::containerToSequence(args) );
+ }
+ catch (...) {
+ OUStringBuffer sMsg = "error starting process: " + url;
+ for(const auto& arg : args)
+ sMsg.append(" ").append(arg);
+ throw uno::RuntimeException(sMsg.makeStringAndClear());
+ }
+ try {
+ return Reference<XComponentContext>(
+ resolveUnoURL( connectStr, xContext, abortChannel.get() ),
+ UNO_QUERY_THROW );
+ }
+ catch (...) {
+ // try to terminate process:
+ if ( osl_terminateProcess( hProcess ) != osl_Process_E_None )
+ {
+ OSL_ASSERT( false );
+ }
+ throw;
+ }
+}
+
+void extractComponentData(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::registry::XRegistryKey > const & registry,
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ css::uno::Reference< css::loader::XImplementationLoader > const &
+ componentLoader,
+ OUString const & componentUrl)
+{
+ OSL_ASSERT(
+ context.is() && registry.is() && data != nullptr && componentLoader.is());
+ OUString registryName(registry->getKeyName());
+ sal_Int32 prefix = registryName.getLength();
+ if (!registryName.endsWith("/")) {
+ prefix += RTL_CONSTASCII_LENGTH("/");
+ }
+ const css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > >
+ keys(registry->openKeys());
+ css::uno::Reference< css::lang::XMultiComponentFactory > smgr(
+ context->getServiceManager(), css::uno::UNO_SET_THROW);
+ for (css::uno::Reference< css::registry::XRegistryKey > const & key : keys) {
+ OUString name(key->getKeyName().copy(prefix));
+ data->implementationNames.push_back(name);
+ css::uno::Reference< css::registry::XRegistryKey > singletons(
+ key->openKey("UNO/SINGLETONS"));
+ if (singletons.is()) {
+ sal_Int32 prefix2 = key->getKeyName().getLength() +
+ RTL_CONSTASCII_LENGTH("/UNO/SINGLETONS/");
+ const css::uno::Sequence<
+ css::uno::Reference< css::registry::XRegistryKey > >
+ singletonKeys(singletons->openKeys());
+ for (css::uno::Reference< css::registry::XRegistryKey > const & singletonKey : singletonKeys) {
+ data->singletons.emplace_back(
+ singletonKey->getKeyName().copy(prefix2), name);
+ }
+ }
+ if (factories != nullptr) {
+ factories->push_back(
+ componentLoader->activate(
+ name, OUString(), componentUrl, key));
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ Reference<XComponentContext> const & xContext )
+{
+ const Reference<loader::XImplementationLoader> xLoader(
+ xContext->getServiceManager()->createInstanceWithContext(
+ m_loader, xContext ), UNO_QUERY );
+ if (! xLoader.is())
+ {
+ throw css::deployment::DeploymentException(
+ "cannot instantiate loader " + m_loader,
+ static_cast< OWeakObject * >(this), Any());
+ }
+
+ // HACK: highly dependent on stoc/source/servicemanager
+ // and stoc/source/implreg implementation which rely on the same
+ // services.rdb format!
+ // .../UNO/LOCATION and .../UNO/ACTIVATOR appear not to be written by
+ // writeRegistryInfo, however, but are known, fixed values here, so
+ // can be passed into extractComponentData
+ OUString url(getURL());
+ const Reference<registry::XSimpleRegistry> xMemReg(
+ xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry", xContext ),
+ UNO_QUERY_THROW );
+ xMemReg->open( OUString() /* in mem */, false, true );
+ xLoader->writeRegistryInfo( xMemReg->getRootKey(), OUString(), url );
+ extractComponentData(
+ xContext, xMemReg->getRootKey(), data, factories, xLoader, url);
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ std::vector< css::uno::Reference< css::uno::XInterface > >::const_iterator
+ factory(factories.begin());
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->insert(css::uno::Any(*factory++));
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "implementation already registered " << implementationName);
+ }
+ }
+ if (data.singletons.empty()) return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Update should be atomic:
+ try {
+ cont->removeByName( name + "/arguments");
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->insertByName( name + "/service", css::uno::Any(singleton.second));
+ } catch (const container::ElementExistException &) {
+ cont->replaceByName( name + "/service", css::uno::Any(singleton.second));
+ }
+ try {
+ cont->insertByName(name, css::uno::Any());
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "singleton already registered " << singleton.first);
+ cont->replaceByName(name, css::uno::Any());
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveRemoval(
+ ComponentBackendDb::Data const & data)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->remove(css::uno::Any(implementationName));
+ } catch (const css::container::NoSuchElementException &) {
+ // ignore if factory has not been live deployed
+ }
+ }
+ if (data.singletons.empty())
+ return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Removal should be atomic:
+ try {
+ cont->removeByName(name);
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/service" );
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/arguments" );
+ } catch (const container::NoSuchElementException &) {}
+ }
+}
+
+// Package
+
+//We could use here BackendImpl::hasActiveEntry. However, this check is just as well.
+//And it also shows the problem if another extension has overwritten an implementation
+//entry, because it contains the same service implementation
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & )
+{
+ if (m_registered == Reg::Uninit)
+ {
+ m_registered = Reg::NotRegistered;
+ const Reference<registry::XSimpleRegistry> xRDB( getRDB() );
+ if (xRDB.is())
+ {
+ bool bAmbiguousComponentName = false;
+ // lookup rdb for location URL:
+ const Reference<registry::XRegistryKey> xRootKey(
+ xRDB->getRootKey() );
+ const Reference<registry::XRegistryKey> xImplKey(
+ xRootKey->openKey( "IMPLEMENTATIONS" ) );
+ Sequence<OUString> implNames;
+ if (xImplKey.is() && xImplKey->isValid())
+ implNames = xImplKey->getKeyNames();
+ OUString const * pImplNames = implNames.getConstArray();
+ sal_Int32 pos = implNames.getLength();
+ for ( ; pos--; )
+ {
+ checkAborted( abortChannel );
+ const OUString key(
+ pImplNames[ pos ] + "/UNO/LOCATION" );
+ const Reference<registry::XRegistryKey> xKey(
+ xRootKey->openKey(key) );
+ if (xKey.is() && xKey->isValid())
+ {
+ const OUString location( xKey->getAsciiValue() );
+ if (location.equalsIgnoreAsciiCase( getURL() ))
+ {
+ break;
+ }
+ else
+ {
+ //try to match only the file name
+ OUString thisUrl(getURL());
+ OUString thisFileName(thisUrl.copy(thisUrl.lastIndexOf('/')));
+
+ OUString locationFileName(location.copy(location.lastIndexOf('/')));
+ if (locationFileName.equalsIgnoreAsciiCase(thisFileName))
+ bAmbiguousComponentName = true;
+ }
+ }
+ }
+ if (pos >= 0)
+ m_registered = Reg::Registered;
+ else if (bAmbiguousComponentName)
+ m_registered = Reg::MaybeRegistered;
+ }
+ }
+
+ //Different extensions can use the same service implementations. Then the extensions
+ //which was installed last will overwrite the one from the other extension. That is
+ //the registry will contain the path (the location) of the library or jar of the
+ //second extension. In this case isRegistered called for the lib of the first extension
+ //would return "not registered". That would mean that during uninstallation
+ //XPackage::registerPackage is not called, because it just was not registered. This is,
+ //however, necessary for jar files. Registering and unregistering update
+ //uno_packages/cache/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc
+ //Therefore, we will return always "is ambiguous" if the path of this component cannot
+ //be found in the registry and if there is another path and both have the same file name (but
+ //the rest of the path is different).
+ //If the caller cannot precisely determine that this package was registered, then it must
+ //call registerPackage.
+ bool bAmbiguous = m_registered == Reg::Void // Reg::Void == we are in the progress of unregistration
+ || m_registered == Reg::MaybeRegistered;
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_registered == Reg::Registered, bAmbiguous) );
+}
+
+
+void BackendImpl::ComponentPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ ComponentBackendDb::Data data;
+ css::uno::Reference< css::uno::XComponentContext > context;
+ if (startup) {
+ context = that->getComponentContext();
+ } else {
+ context.set(that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration> impreg(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::registry::XSimpleRegistry > rdb(getRDB());
+ impreg->registerImplementation(m_loader, url, rdb);
+ // Only write to unorc after successful registration; it may fail if
+ // there is no suitable java
+ if (m_loader == "com.sun.star.loader.Java2" && !jarManifestHeaderPresent(url, "UNO-Type-Path", xCmdEnv))
+ {
+ that->addToUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ data.javaTypeLibrary = true;
+ }
+ std::vector< css::uno::Reference< css::uno::XInterface > > factories;
+ getComponentInfo(&data, startup ? nullptr : &factories, context);
+ if (!startup) {
+ try {
+ componentLiveInsertion(data, factories);
+ } catch (css::uno::Exception &) {
+ TOOLS_INFO_EXCEPTION("desktop.deployment", "caught");
+ try {
+ impreg->revokeImplementation(url, rdb);
+ } catch (css::uno::RuntimeException &) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignored");
+ }
+ throw;
+ }
+ }
+ m_registered = Reg::Registered;
+ that->addDataToDb(url, data);
+ } else { // revoke
+ m_registered = Reg::Void;
+ ComponentBackendDb::Data data(that->readDataFromDb(url));
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ bool remoteContext = context.is();
+ if (!remoteContext) {
+ context = that->getComponentContext();
+ }
+ if (!startup) {
+ componentLiveRemoval(data);
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration >(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW)->revokeImplementation(url, getRDB());
+ if (data.javaTypeLibrary) {
+ that->removeFromUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ }
+ if (remoteContext) {
+ that->releaseObject(url);
+ }
+ m_registered = Reg::NotRegistered;
+ getMyBackend()->revokeEntryFromDb(url);
+ }
+}
+
+BackendImpl::TypelibraryPackageImpl::TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_jarFile( jarFile )
+{
+}
+
+// Package
+BackendImpl * BackendImpl::TypelibraryPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException( "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<TypelibraryPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::TypelibraryPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ that->hasInUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, getURL() ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::TypelibraryPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ const OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ // live insertion:
+ if (m_jarFile) {
+ // xxx todo add to classpath at runtime: ???
+ //SB: It is probably not worth it to add the live inserted type
+ // library JAR to the UnoClassLoader in the soffice process. Any
+ // live inserted component JAR that might reference this type
+ // library JAR runs in its own uno process, so there is probably no
+ // Java code in the soffice process that would see any UNO types
+ // introduced by this type library JAR.
+ }
+ else // RDB:
+ {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->insert(
+ css::uno::makeAny(expandUnoRcUrl(url)));
+ }
+
+ that->addToUnoRc( m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB,
+ url, xCmdEnv );
+ }
+ else // revokePackage()
+ {
+ that->removeFromUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, url, xCmdEnv );
+
+ // revoking types at runtime, possible, sensible?
+ if (!m_jarFile) {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->remove(
+ css::uno::makeAny(expandUnoRcUrl(url)));
+ }
+ }
+}
+
+BackendImpl::OtherPlatformPackageImpl::OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString const& rPlatform)
+ : Package(myBackend, url, name, name, xPackageType, bRemoved, identifier)
+ , m_aPlatform(rPlatform)
+{
+ OSL_PRECOND(bRemoved, "this class can only be used for removing packages!");
+}
+
+BackendImpl *
+BackendImpl::OtherPlatformPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<OtherPlatformPackageImpl*>(this)));
+ }
+ return pBackend;
+}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::OtherPlatformPackageImpl::impl_openRDB() const
+{
+ OUString const aRDB(m_aPlatform + ".rdb");
+ OUString const aRDBPath(makeURL(getMyBackend()->getCachePath(), aRDB));
+
+ Reference<registry::XSimpleRegistry> xRegistry;
+
+ try
+ {
+ xRegistry.set(
+ impl_createInstance("com.sun.star.registry.SimpleRegistry"),
+ UNO_QUERY)
+ ;
+ if (xRegistry.is())
+ xRegistry->open(expandUnoRcUrl(aRDBPath), false, false);
+ }
+ catch (registry::InvalidRegistryException const&)
+ {
+ // If the registry does not exist, we do not need to bother at all
+ xRegistry.set(nullptr);
+ }
+
+ SAL_WARN_IF( !xRegistry.is(), "desktop.deployment", "could not create registry for the package's platform");
+ return xRegistry;
+}
+
+Reference<XInterface>
+BackendImpl::OtherPlatformPackageImpl::impl_createInstance(OUString const& rService)
+const
+{
+ Reference<XComponentContext> const xContext(getMyBackend()->getComponentContext());
+ OSL_ASSERT(xContext.is());
+ Reference<XInterface> xService;
+ if (xContext.is())
+ xService.set(xContext->getServiceManager()->createInstanceWithContext(rService, xContext));
+ return xService;
+}
+
+beans::Optional<beans::Ambiguous<sal_Bool> >
+BackendImpl::OtherPlatformPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */ )
+{
+ return beans::Optional<beans::Ambiguous<sal_Bool> >(true,
+ beans::Ambiguous<sal_Bool>(true, false));
+}
+
+void
+BackendImpl::OtherPlatformPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ bool bRegisterPackage,
+ bool /* bStartup */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */)
+{
+ OSL_PRECOND(!bRegisterPackage, "this class can only be used for removing packages!");
+
+ OUString const aURL(getURL());
+
+ Reference<registry::XSimpleRegistry> const xServicesRDB(impl_openRDB());
+ Reference<registry::XImplementationRegistration> const xImplReg(
+ impl_createInstance("com.sun.star.registry.ImplementationRegistration"),
+ UNO_QUERY)
+ ;
+ if (xImplReg.is() && xServicesRDB.is())
+ xImplReg->revokeImplementation(aURL, xServicesRDB);
+ if (xServicesRDB.is())
+ xServicesRDB->close();
+
+ getMyBackend()->revokeEntryFromDb(aURL);
+}
+
+BackendImpl * BackendImpl::ComponentsPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentsPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentsPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true,
+ beans::Ambiguous<sal_Bool>(
+ getMyBackend()->hasInUnoRc(RCITEM_COMPONENTS, getURL()), false));
+}
+
+void BackendImpl::ComponentsPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ if (!startup) {
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args
+ {
+ { "uri", css::uno::makeAny(expandUnoRcUrl(url)) },
+ { "component-context", css::uno::makeAny(context) }
+ };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->insert(css::uno::makeAny(args));
+ }
+ that->addToUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ } else { // revoke
+ that->removeFromUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ if (!startup) {
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args { { "uri", css::uno::makeAny(expandUnoRcUrl(url)) } };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->remove(css::uno::makeAny(args));
+ }
+ that->releaseObject(url);
+ that->revokeEntryFromDb(url); // in case it got added with old code
+ }
+}
+
+BackendImpl::ComponentsPackageImpl::ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+{}
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ IMPLEMENTATION_NAME,
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configuration.cxx b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
new file mode 100644
index 000000000..394cd0069
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
@@ -0,0 +1,798 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+//TODO: Large parts of this file were copied from dp_component.cxx; those parts
+// should be consolidated.
+
+#include <config_extensions.h>
+
+#include <dp_backend.h>
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <comphelper/lok.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/configuration/Update.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <deque>
+#include <memory>
+#include <utility>
+
+#include "dp_configurationbackenddb.hxx"
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::configuration {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const ;
+
+ const bool m_isSchema;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool isSchema, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_isSchema( isSchema )
+ {}
+ };
+ friend class PackageImpl;
+
+ std::deque<OUString> m_xcs_files;
+ std::deque<OUString> m_xcu_files;
+ std::deque<OUString> & getFiles( bool xcs ) {
+ return xcs ? m_xcs_files : m_xcu_files;
+ }
+
+ bool m_configmgrini_inited;
+ bool m_configmgrini_modified;
+ std::unique_ptr<ConfigurationBackendDb> m_backendDb;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+#if HAVE_FEATURE_EXTENSIONS
+ // for backwards compatibility - nil if no (compatible) back-compat db present
+ std::unique_ptr<PersistentMap> m_registeredPackages;
+#endif
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xConfDataTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xConfSchemaTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ void configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void configmgrini_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ /* The parameter isURL is false in the case of adding the conf:ini-entry
+ value from the backend db. This entry already contains the path as it
+ is used in the configmgr.ini.
+ */
+ void addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#if HAVE_FEATURE_EXTENSIONS
+ bool removeFromConfigmgrIni( bool isSchema, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#endif
+ void addDataToDb(OUString const & url, ConfigurationBackendDb::Data const & data);
+ ::std::optional<ConfigurationBackendDb::Data> readDataFromDb(OUString const & url);
+ void revokeEntryFromDb(OUString const & url);
+ bool hasActiveEntry(OUString const & url);
+ bool activateEntry(OUString const & url);
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+};
+
+
+void BackendImpl::disposing()
+{
+ try {
+ configmgrini_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing...",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_configmgrini_inited( false ),
+ m_configmgrini_modified( false ),
+ m_xConfDataTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-data",
+ "*.xcu",
+ DpResId(RID_STR_CONF_DATA)
+ ) ),
+ m_xConfSchemaTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-schema",
+ "*.xcs",
+ DpResId(RID_STR_CONF_SCHEMA)
+ ) ),
+ m_typeInfos( 2 )
+{
+ m_typeInfos[ 0 ] = m_xConfDataTypeInfo;
+ m_typeInfos[ 1 ] = m_xConfSchemaTypeInfo;
+
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // TODO
+ }
+ else
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ConfigurationBackendDb(getComponentContext(), dbFile));
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+
+ configmgrini_verify_init( xCmdEnv );
+
+#if HAVE_FEATURE_EXTENSIONS
+ std::unique_ptr<PersistentMap> pMap;
+ OUString aCompatURL( makeURL( getCachePath(), "registered_packages.pmap" ) );
+
+ // Don't create it if it doesn't exist already
+ if ( ::utl::UCBContentHelper::Exists( expandUnoRcUrl( aCompatURL ) ) )
+ {
+ try
+ {
+ pMap.reset( new PersistentMap( aCompatURL ) );
+ }
+ catch (const Exception &e)
+ {
+ OUString aStr = "Exception loading legacy package database: '" +
+ e.Message +
+ "' - ignoring file, please remove it.\n";
+ dp_misc::writeConsole( aStr.getStr() );
+ }
+ }
+ m_registeredPackages = std::move(pMap);
+#endif
+ }
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ConfigurationBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+::std::optional<ConfigurationBackendDb::Data> BackendImpl::readDataFromDb(
+ OUString const & url)
+{
+ ::std::optional<ConfigurationBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".xcu" )) {
+ mediaType = "application/vnd.sun.star.configuration-data";
+ }
+ if (title.endsWithIgnoreAsciiCase( ".xcs" )) {
+ mediaType = "application/vnd.sun.star.configuration-schema";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xConfDataTypeInfo, false /* data file */,
+ bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-schema")) {
+ return new PackageImpl(
+ this, url, name, m_xConfSchemaTypeInfo, true /* schema file */,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( getMutex() );
+ if ( m_configmgrini_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "configmgr.ini" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "SCHEMA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("SCHEMA=");
+ do {
+ OUString token( line.getToken( 0, ' ', index ).trim() );
+ if (!token.isEmpty()) {
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcs_files.push_back( token );
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "DATA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("DATA=");
+ do {
+ OUString token( line.getToken( 0, ' ', index ).trim() );
+ if (!token.isEmpty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.copy( 1 );
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcu_files.push_back( token );
+ }
+ }
+ while (index >= 0);
+ }
+ }
+ m_configmgrini_modified = false;
+ m_configmgrini_inited = true;
+}
+
+
+void BackendImpl::configmgrini_flush(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_configmgrini_inited || !m_configmgrini_modified)
+ return;
+
+ OStringBuffer buf;
+ if (! m_xcs_files.empty())
+ {
+ auto iPos( m_xcs_files.cbegin() );
+ auto const iEnd( m_xcs_files.cend() );
+ buf.append( "SCHEMA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_xcu_files.empty())
+ {
+ auto iPos( m_xcu_files.cbegin() );
+ auto const iEnd( m_xcu_files.cend() );
+ buf.append( "DATA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // write configmgr.ini:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "configmgr.ini" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_configmgrini_modified = false;
+}
+
+
+void BackendImpl::addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( isURL ? dp_misc::makeRcTerm(url_) : url_ );
+ const ::osl::MutexGuard guard( getMutex() );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ }
+}
+
+#if HAVE_FEATURE_EXTENSIONS
+bool BackendImpl::removeFromConfigmgrIni(
+ bool isSchema, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( getMutex() );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ auto i(std::find(rSet.begin(), rSet.end(), rcterm));
+ if (i == rSet.end() && !isSchema)
+ {
+ //in case the xcu contained %origin% then the configmr.ini contains the
+ //url to the file in the user installation (e.g. $BUNDLED_EXTENSIONS_USER)
+ //However, m_url (getURL()) contains the URL for the file in the actual
+ //extension installation.
+ ::std::optional<ConfigurationBackendDb::Data> data = readDataFromDb(url_);
+ if (data)
+ i = std::find(rSet.begin(), rSet.end(), data->iniEntry);
+ }
+ if (i == rSet.end()) {
+ return false;
+ }
+ rSet.erase(i);
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ return true;
+}
+#endif
+
+// Package
+
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+#if HAVE_FEATURE_EXTENSIONS
+ const OUString url(getURL());
+ if (!bReg && that->m_registeredPackages)
+ {
+ // fallback for user extension registered in berkeley DB
+ bReg = that->m_registeredPackages->has(
+ OUStringToOString( url, RTL_TEXTENCODING_UTF8 ));
+ }
+#endif
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+OUString encodeForXml( OUString const & text )
+{
+ // encode conforming xml:
+ sal_Int32 len = text.getLength();
+ OUStringBuffer buf;
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ sal_Unicode c = text[ pos ];
+ switch (c) {
+ case '<':
+ buf.append( "&lt;" );
+ break;
+ case '>':
+ buf.append( "&gt;" );
+ break;
+ case '&':
+ buf.append( "&amp;" );
+ break;
+ case '\'':
+ buf.append( "&apos;" );
+ break;
+ case '\"':
+ buf.append( "&quot;" );
+ break;
+ default:
+ buf.append( c );
+ break;
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+
+OUString replaceOrigin(
+ OUString const & url, OUString const & destFolder, Reference< XCommandEnvironment > const & xCmdEnv, Reference< XComponentContext > const & xContext, bool & out_replaced)
+{
+ // looking for %origin%:
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ std::vector<sal_Int8> filtered( bytes.size() * 2 );
+ bool use_filtered = false;
+ OString origin;
+ char const * pBytes = reinterpret_cast<char const *>(
+ bytes.data());
+ std::size_t nBytes = bytes.size();
+ size_t write_pos = 0;
+ while (nBytes > 0)
+ {
+ sal_Int32 index = rtl_str_indexOfChar_WithLength( pBytes, nBytes, '%' );
+ if (index < 0) {
+ if (! use_filtered) // opt
+ break;
+ index = nBytes;
+ }
+
+ if ((write_pos + index) > filtered.size())
+ filtered.resize( (filtered.size() + index) * 2 );
+ memcpy( filtered.data() + write_pos, pBytes, index );
+ write_pos += index;
+ pBytes += index;
+ nBytes -= index;
+ if (nBytes == 0)
+ break;
+
+ // consume %:
+ ++pBytes;
+ --nBytes;
+ char const * pAdd = "%";
+ sal_Int32 nAdd = 1;
+ if (nBytes > 1 && pBytes[ 0 ] == '%')
+ {
+ // %% => %
+ ++pBytes;
+ --nBytes;
+ use_filtered = true;
+ }
+ else if (rtl_str_shortenedCompare_WithLength(
+ pBytes, nBytes,
+ "origin%",
+ RTL_CONSTASCII_LENGTH("origin%"),
+ RTL_CONSTASCII_LENGTH("origin%")) == 0)
+ {
+ if (origin.isEmpty()) {
+ // encode only once
+ origin = OUStringToOString(
+ encodeForXml( url.copy( 0, url.lastIndexOf( '/' ) ) ),
+ // xxx todo: encode always for UTF-8? => lookup doc-header?
+ RTL_TEXTENCODING_UTF8 );
+ }
+ pAdd = origin.getStr();
+ nAdd = origin.getLength();
+ pBytes += RTL_CONSTASCII_LENGTH("origin%");
+ nBytes -= RTL_CONSTASCII_LENGTH("origin%");
+ use_filtered = true;
+ }
+ if ((write_pos + nAdd) > filtered.size())
+ filtered.resize( (filtered.size() + nAdd) * 2 );
+ memcpy( filtered.data() + write_pos, pAdd, nAdd );
+ write_pos += nAdd;
+ }
+ if (!use_filtered)
+ return url;
+ if (write_pos < filtered.size())
+ filtered.resize( write_pos );
+ OUString newUrl(url);
+ if (!destFolder.isEmpty())
+ {
+ //get the file name of the xcu and add it to the url of the temporary folder
+ sal_Int32 i = url.lastIndexOf('/');
+ newUrl = destFolder + url.copy(i);
+ }
+
+ ucbhelper::Content(newUrl, xCmdEnv, xContext).writeStream(
+ xmlscript::createInputStream(filtered), true);
+ out_replaced = true;
+ return newUrl;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ if (getMyBackend()->activateEntry(getURL()))
+ {
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ OSL_ASSERT(data);
+ that->addToConfigmgrIni( m_isSchema, false, data->iniEntry, xCmdEnv );
+ }
+ else
+ {
+ ConfigurationBackendDb::Data data;
+ if (!m_isSchema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url = replaceOrigin(url, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ //No need for live-deployment for bundled extension, because OOo
+ //restarts after installation
+ if ((that->m_eContext != Context::Bundled && !startup)
+ || comphelper::LibreOfficeKit::isActive())
+ {
+ if (m_isSchema)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcsFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ else
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcuFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ }
+ that->addToConfigmgrIni( m_isSchema, true, url, xCmdEnv );
+ data.iniEntry = dp_misc::makeRcTerm(url);
+ that->addDataToDb(getURL(), data);
+ }
+ }
+ else // revoke
+ {
+#if HAVE_FEATURE_EXTENSIONS
+ if (!that->removeFromConfigmgrIni(m_isSchema, url, xCmdEnv) &&
+ that->m_registeredPackages) {
+ // Obsolete package database handling - should be removed for LibreOffice 4.0
+ t_string2string_map entries(
+ that->m_registeredPackages->getEntries());
+ for (auto const& entry : entries)
+ {
+ //If the xcu file was installed before the configmgr was changed
+ //to use the configmgr.ini, one needed to rebuild to whole directory
+ //structure containing the xcu, xcs files from all extensions. Now,
+ //we just add all other xcu/xcs files to the configmgr.ini instead of
+ //rebuilding the directory structure.
+ OUString url2(
+ OStringToOUString(entry.first, RTL_TEXTENCODING_UTF8));
+ if (url2 != url) {
+ bool schema = entry.second.equalsIgnoreAsciiCase(
+ "vnd.sun.star.configuration-schema");
+ OUString url_replaced(url2);
+ ConfigurationBackendDb::Data data;
+ if (!schema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url_replaced = replaceOrigin(
+ url2, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ that->addToConfigmgrIni(schema, true, url_replaced, xCmdEnv);
+ data.iniEntry = dp_misc::makeRcTerm(url_replaced);
+ that->addDataToDb(url2, data);
+ }
+ that->m_registeredPackages->erase(entry.first);
+ }
+ try
+ {
+ ::ucbhelper::Content(
+ makeURL( that->getCachePath(), "registry" ),
+ xCmdEnv, that->getComponentContext() ).executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch(const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+#endif
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ //If an xcu file was life deployed then always a data entry is written.
+ //If the xcu file was already in the configmr.ini then there is also
+ //a data entry
+ if (!m_isSchema && data)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->removeExtensionXcuFile(expandUnoRcTerm(data->iniEntry));
+ }
+ that->revokeEntryFromDb(url);
+ }
+}
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ "com.sun.star.comp.deployment.configuration.PackageRegistryBackend",
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
new file mode 100644
index 000000000..fd5d3b3c4
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/xpath/XXPathAPI.hpp>
+
+#include "dp_configurationbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/configuration-registry/2010"
+#define NS_PREFIX "conf"
+#define ROOT_ELEMENT_NAME "configuration-backend-db"
+#define KEY_ELEMENT_NAME "configuration"
+
+namespace dp_registry::backend::configuration {
+
+ConfigurationBackendDb::ConfigurationBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ConfigurationBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ConfigurationBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ConfigurationBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ConfigurationBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void ConfigurationBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement("data-url", data.dataUrl, helpNode);
+ writeSimpleElement("ini-entry", data.iniEntry, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<ConfigurationBackendDb::Data>
+ConfigurationBackendDb::getEntry(OUString const & url)
+{
+ try
+ {
+ ConfigurationBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement("data-url", aNode);
+ retData.iniEntry = readSimpleElement("ini-entry", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> ConfigurationBackendDb::getAllDataUrls()
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression(
+ sPrefix + ":configuration/" + sPrefix + ":data-url/text()");
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sExpression);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::configuration
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
new file mode 100644
index 000000000..0b345cb3f
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_CONFIGURATION_DP_CONFIGURATIONBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_CONFIGURATION_DP_CONFIGURATIONBACKENDDB_HXX
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <optional>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace configuration {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ConfigurationBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the xcu or xcs files which contained
+ %origin%
+ */
+ OUString dataUrl;
+ /* the URL of the xcu or xcs file which is written in to the configmgr.ini
+ */
+ OUString iniEntry;
+ };
+
+public:
+
+ ConfigurationBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ ::std::optional<Data> getEntry(OUString const & url);
+ std::vector< OUString> getAllDataUrls();
+};
+
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_backend.cxx b/desktop/source/deployment/registry/dp_backend.cxx
new file mode 100644
index 000000000..42bbc65cd
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_backend.cxx
@@ -0,0 +1,767 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include <rtl/ustring.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/unwrapargs.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <tools/diagnose_ex.h>
+#include <unotools/tempfile.hxx>
+#include <optional>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend {
+
+
+PackageRegistryBackend::~PackageRegistryBackend()
+{
+}
+
+
+void PackageRegistryBackend::disposing( lang::EventObject const & event )
+{
+ Reference<deployment::XPackage> xPackage(
+ event.Source, UNO_QUERY_THROW );
+ OUString url( xPackage->getURL() );
+ ::osl::MutexGuard guard( getMutex() );
+ if ( m_bound.erase( url ) != 1 )
+ {
+ SAL_WARN("desktop.deployment", "erase(" << url << ") != 1");
+ }
+}
+
+
+PackageRegistryBackend::PackageRegistryBackend(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext )
+ : t_BackendBase( getMutex() ),
+ m_xComponentContext( xContext ),
+ m_eContext( Context::Unknown )
+{
+ assert(xContext.is());
+ std::optional<OUString> cachePath;
+ std::optional<bool> readOnly;
+ comphelper::unwrapArgs( args, m_context, cachePath, readOnly );
+ if (cachePath)
+ m_cachePath = *cachePath;
+
+ if ( m_context == "user" )
+ m_eContext = Context::User;
+ else if ( m_context == "shared" )
+ m_eContext = Context::Shared;
+ else if ( m_context == "bundled" )
+ m_eContext = Context::Bundled;
+ else if ( m_context == "tmp" )
+ m_eContext = Context::Tmp;
+ else if (m_context.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/"))
+ m_eContext = Context::Document;
+ else
+ m_eContext = Context::Unknown;
+}
+
+
+void PackageRegistryBackend::check()
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "PackageRegistryBackend instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageRegistryBackend::disposing()
+{
+ try {
+ for (auto const& elem : m_bound)
+ elem.second->removeEventListener(this);
+ m_bound.clear();
+ m_xComponentContext.clear();
+ WeakComponentImplHelperBase::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing!",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+// XPackageRegistry
+
+Reference<deployment::XPackage> PackageRegistryBackend::bindPackage(
+ OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ ::osl::ResettableMutexGuard guard( getMutex() );
+ check();
+
+ t_string2ref::const_iterator const iFind( m_bound.find( url ) );
+ if (iFind != m_bound.end())
+ {
+ Reference<deployment::XPackage> xPackage( iFind->second );
+ if (xPackage.is())
+ {
+ if (!mediaType.isEmpty() &&
+ mediaType != xPackage->getPackageType()->getMediaType())
+ throw lang::IllegalArgumentException
+ ("XPackageRegistry::bindPackage: media type does not match",
+ static_cast<OWeakObject*>(this), 1);
+ if (xPackage->isRemoved() != bRemoved)
+ throw deployment::InvalidRemovedParameterException(
+ "XPackageRegistry::bindPackage: bRemoved parameter does not match",
+ static_cast<OWeakObject*>(this), xPackage->isRemoved(), xPackage);
+ return xPackage;
+ }
+ }
+
+ guard.clear();
+
+ Reference<deployment::XPackage> xNewPackage;
+ try {
+ xNewPackage = bindPackage_( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "Error binding package: " + url,
+ static_cast<OWeakObject *>(this), exc );
+ }
+
+ guard.reset();
+
+ std::pair< t_string2ref::iterator, bool > insertion(
+ m_bound.emplace( url, xNewPackage ) );
+ if (insertion.second)
+ { // first insertion
+ SAL_WARN_IF(
+ Reference<XInterface>(insertion.first->second) != xNewPackage,
+ "desktop.deployment", "mismatch");
+ }
+ else
+ { // found existing entry
+ Reference<deployment::XPackage> xPackage( insertion.first->second );
+ if (xPackage.is())
+ return xPackage;
+ insertion.first->second = xNewPackage;
+ }
+
+ guard.clear();
+ xNewPackage->addEventListener( this ); // listen for disposing events
+ return xNewPackage;
+}
+
+OUString PackageRegistryBackend::createFolder(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ const OUString sDataFolder = makeURL(getCachePath(), OUString());
+ //make sure the folder exist
+ ucbhelper::Content dataContent;
+ ::dp_misc::create_folder(&dataContent, sDataFolder, xCmdEnv);
+
+ const OUString baseDir(sDataFolder);
+ ::utl::TempFile aTemp(&baseDir, true);
+ const OUString& url = aTemp.GetURL();
+ return sDataFolder + url.copy(url.lastIndexOf('/'));
+}
+
+//folderURL can have the extension .tmp or .tmp_
+//Before OOo 3.4 the created a tmp file with osl_createTempFile and
+//then created a Folder with a same name and a trailing '_'
+//If the folderURL has no '_' then there is no corresponding tmp file.
+void PackageRegistryBackend::deleteTempFolder(
+ OUString const & folderUrl)
+{
+ if (!folderUrl.isEmpty())
+ {
+ erase_path( folderUrl, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+
+ if (folderUrl.endsWith("_"))
+ {
+ const OUString tempFile = folderUrl.copy(0, folderUrl.getLength() - 1);
+ erase_path( tempFile, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ }
+ }
+}
+
+//usedFolders can contain folder names which have the extension .tmp or .tmp_
+//Before OOo 3.4 we created a tmp file with osl_createTempFile and
+//then created a Folder with a same name and a trailing '_'
+//If the folderURL has no '_' then there is no corresponding tmp file.
+void PackageRegistryBackend::deleteUnusedFolders(
+ std::vector< OUString> const & usedFolders)
+{
+ try
+ {
+ const OUString sDataFolder = makeURL(getCachePath(), OUString());
+ ::ucbhelper::Content tempFolder(
+ sDataFolder, Reference<ucb::XCommandEnvironment>(), m_xComponentContext);
+
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor( tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ // get all temp directories:
+ std::vector<OUString> tempEntries;
+
+ while (xResultSet->next())
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+
+ if (title.endsWith(".tmp"))
+ tempEntries.push_back(
+ makeURLAppendSysPathSegment(sDataFolder, title));
+ }
+
+ for (const OUString & tempEntrie : tempEntries)
+ {
+ if (std::find( usedFolders.begin(), usedFolders.end(), tempEntrie ) ==
+ usedFolders.end())
+ {
+ deleteTempFolder(tempEntrie);
+ }
+ }
+ }
+ catch (const ucb::InteractiveAugmentedIOException& e)
+ {
+ //In case the folder containing all the data folder does not
+ //exist yet, we ignore the exception
+ if (e.Code != ucb::IOErrorCode_NOT_EXISTING)
+ throw;
+ }
+
+}
+
+
+Package::~Package()
+{
+}
+
+
+Package::Package( ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & rName,
+ OUString const & displayName,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved,
+ OUString const & identifier)
+ : t_PackageBase( getMutex() ),
+ m_myBackend( myBackend ),
+ m_url( url ),
+ m_name( rName ),
+ m_displayName( displayName ),
+ m_xPackageType( xPackageType ),
+ m_bRemoved(bRemoved),
+ m_identifier(identifier)
+{
+ if (m_bRemoved)
+ {
+ //We use the last segment of the URL
+ SAL_WARN_IF(
+ !m_name.isEmpty(), "desktop.deployment", "non-empty m_name");
+ OUString name = m_url;
+ rtl::Bootstrap::expandMacros(name);
+ sal_Int32 index = name.lastIndexOf('/');
+ if (index != -1 && index < name.getLength())
+ m_name = name.copy(index + 1);
+ }
+}
+
+
+void Package::disposing()
+{
+ m_myBackend.clear();
+ WeakComponentImplHelperBase::disposing();
+}
+
+
+void Package::check() const
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "Package instance has already been disposed!",
+ static_cast<OWeakObject *>(const_cast<Package *>(this)));
+ }
+}
+
+// XComponent
+
+void Package::dispose()
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::dispose();
+}
+
+
+void Package::addEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::addEventListener( xListener );
+}
+
+
+void Package::removeEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::removeEventListener( xListener );
+}
+
+// XModifyBroadcaster
+
+void Package::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void Package::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void Package::checkAborted(
+ ::rtl::Reference<AbortChannel> const & abortChannel )
+{
+ if (abortChannel.is() && abortChannel->isAborted()) {
+ throw CommandAbortedException(
+ "abort!", static_cast<OWeakObject *>(this) );
+ }
+}
+
+// XPackage
+
+Reference<task::XAbortChannel> Package::createAbortChannel()
+{
+ check();
+ return new AbortChannel;
+}
+
+
+sal_Bool Package::isBundle()
+{
+ return false; // default
+}
+
+
+::sal_Int32 Package::checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >&,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >&,
+ sal_Bool)
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return 0;
+}
+
+
+sal_Bool Package::checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return true;
+}
+
+
+Sequence< Reference<deployment::XPackage> > Package::getBundle(
+ Reference<task::XAbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return Sequence< Reference<deployment::XPackage> >();
+}
+
+
+OUString Package::getName()
+{
+ return m_name;
+}
+
+beans::Optional<OUString> Package::getIdentifier()
+{
+ if (m_bRemoved)
+ return beans::Optional<OUString>(true, m_identifier);
+
+ return beans::Optional<OUString>();
+}
+
+
+OUString Package::getVersion()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+OUString Package::getURL()
+{
+ return m_url;
+}
+
+
+OUString Package::getDisplayName()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return m_displayName;
+}
+
+
+OUString Package::getDescription()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+OUString Package::getLicenseText()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+Sequence<OUString> Package::getUpdateInformationURLs()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return Sequence<OUString>();
+}
+
+
+css::beans::StringPair Package::getPublisherInfo()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ css::beans::StringPair aEmptyPair;
+ return aEmptyPair;
+}
+
+
+uno::Reference< css::graphic::XGraphic > Package::getIcon( sal_Bool /*bHighContrast*/ )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ uno::Reference< css::graphic::XGraphic > aEmpty;
+ return aEmpty;
+}
+
+
+Reference<deployment::XPackageTypeInfo> Package::getPackageType()
+{
+ return m_xPackageType;
+}
+
+void Package::exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::ucbhelper::Content destFolder( destFolderURL, xCmdEnv, getMyBackend()->getComponentContext() );
+ ::ucbhelper::Content sourceContent( getURL(), xCmdEnv, getMyBackend()->getComponentContext() );
+ bool bOk=true;
+ try
+ {
+ destFolder.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ newTitle, nameClashAction);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (!bOk)
+ throw RuntimeException( "UCB transferContent() failed!", nullptr );
+}
+
+void Package::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * container = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (container == nullptr)
+ return;
+
+ const Sequence< Reference<XInterface> > elements(
+ container->getElements() );
+ lang::EventObject evt( static_cast<OWeakObject *>(this) );
+ for ( const Reference<XInterface>& x : elements )
+ {
+ Reference<util::XModifyListener> xListener( x, UNO_QUERY );
+ if (xListener.is())
+ xListener->modified( evt );
+ }
+}
+
+// XPackage
+
+beans::Optional< beans::Ambiguous<sal_Bool> > Package::isRegistered(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ try {
+ ::osl::ResettableMutexGuard guard( getMutex() );
+ return isRegistered_( guard,
+ AbortChannel::get(xAbortChannel),
+ xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "unexpected " + exc.getValueTypeName() + ": " + e.Message,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+void Package::processPackage_impl(
+ bool doRegisterPackage,
+ bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ check();
+ bool action = false;
+
+ try {
+ try {
+ ::osl::ResettableMutexGuard guard( getMutex() );
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ isRegistered_( guard, AbortChannel::get(xAbortChannel),
+ xCmdEnv ) );
+ action = (option.IsPresent &&
+ (option.Value.IsAmbiguous ||
+ (doRegisterPackage ? !option.Value.Value
+ : option.Value.Value)));
+ if (action) {
+
+ OUString displayName = isRemoved() ? getName() : getDisplayName();
+ ProgressLevel progress(
+ xCmdEnv,
+ (doRegisterPackage
+ ? PackageRegistryBackend::StrRegisteringPackage()
+ : PackageRegistryBackend::StrRevokingPackage())
+ + displayName );
+ processPackage_( guard,
+ doRegisterPackage,
+ startup,
+ AbortChannel::get(xAbortChannel),
+ xCmdEnv );
+ }
+ }
+ catch (lang::IllegalArgumentException &) {
+ Any e(cppu::getCaughtException());
+ throw deployment::DeploymentException(
+ ((doRegisterPackage
+ ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
+ : DpResId(RID_STR_ERROR_WHILE_REVOKING))
+ + getDisplayName()),
+ static_cast< OWeakObject * >(this), e);
+ }
+ catch (const RuntimeException &) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected");
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ (doRegisterPackage
+ ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
+ : DpResId(RID_STR_ERROR_WHILE_REVOKING))
+ + getDisplayName(), static_cast<OWeakObject *>(this), exc );
+ }
+ }
+ catch (...) {
+ if (action)
+ fireModified();
+ throw;
+ }
+ if (action)
+ fireModified();
+}
+
+
+void Package::registerPackage(
+ sal_Bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ processPackage_impl( true /* register */, startup, xAbortChannel, xCmdEnv );
+}
+
+
+void Package::revokePackage(
+ sal_Bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ processPackage_impl( false /* revoke */, startup, xAbortChannel, xCmdEnv );
+
+}
+
+PackageRegistryBackend * Package::getMyBackend() const
+{
+ PackageRegistryBackend * pBackend = m_myBackend.get();
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<Package *>(this)));
+ }
+ return pBackend;
+}
+
+OUString Package::getRepositoryName()
+{
+ PackageRegistryBackend * backEnd = getMyBackend();
+ return backEnd->getContext();
+}
+
+beans::Optional< OUString > Package::getRegistrationDataURL()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return beans::Optional<OUString>();
+}
+
+sal_Bool Package::isRemoved()
+{
+ return m_bRemoved;
+}
+
+Package::TypeInfo::~TypeInfo()
+{
+}
+
+// XPackageTypeInfo
+
+OUString Package::TypeInfo::getMediaType()
+{
+ return m_mediaType;
+}
+
+
+OUString Package::TypeInfo::getDescription()
+{
+ return getShortDescription();
+}
+
+
+OUString Package::TypeInfo::getShortDescription()
+{
+ return m_shortDescr;
+}
+
+OUString Package::TypeInfo::getFileFilter()
+{
+ return m_fileFilter;
+}
+
+Any Package::TypeInfo::getIcon( sal_Bool /*highContrast*/, sal_Bool /*smallIcon*/ )
+{
+ return Any();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_backenddb.cxx b/desktop/source/deployment/registry/dp_backenddb.cxx
new file mode 100644
index 000000000..edc64b199
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_backenddb.cxx
@@ -0,0 +1,655 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/file.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XActiveDataControl.hpp>
+#include <dp_misc.h>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <dp_backenddb.hxx>
+
+
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend {
+
+BackendDb::BackendDb(
+ Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url):
+ m_xContext(xContext)
+{
+ m_urlDb = dp_misc::expandUnoRcUrl(url);
+}
+
+void BackendDb::save()
+{
+ const Reference<css::io::XActiveDataSource> xDataSource(m_doc,css::uno::UNO_QUERY_THROW);
+ std::vector<sal_Int8> bytes;
+ xDataSource->setOutputStream(::xmlscript::createOutputStream(&bytes));
+ const Reference<css::io::XActiveDataControl> xDataControl(m_doc,css::uno::UNO_QUERY_THROW);
+ xDataControl->start();
+
+ const Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(bytes));
+ ::ucbhelper::Content ucbDb(m_urlDb, nullptr, m_xContext);
+ ucbDb.writeStream(xData, true /*replace existing*/);
+}
+
+css::uno::Reference<css::xml::dom::XDocument> const & BackendDb::getDocument()
+{
+ if (!m_doc.is())
+ {
+ const Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(m_xContext) );
+
+ ::osl::DirectoryItem item;
+ ::osl::File::RC err = ::osl::DirectoryItem::get(m_urlDb, item);
+ if (err == ::osl::File::E_None)
+ {
+ ::ucbhelper::Content descContent(
+ m_urlDb, css::uno::Reference<css::ucb::XCommandEnvironment>(),
+ m_xContext);
+ Reference<css::io::XInputStream> xIn = descContent.openStream();
+ m_doc = xDocBuilder->parse(xIn);
+ }
+ else if (err == ::osl::File::E_NOENT)
+ {
+ //Create a new document and insert some basic stuff
+ m_doc = xDocBuilder->newDocument();
+ const Reference<css::xml::dom::XElement> rootNode =
+ m_doc->createElementNS(getDbNSName(), getNSPrefix() +
+ ":" + getRootElementName());
+
+ m_doc->appendChild(Reference<css::xml::dom::XNode>(
+ rootNode, UNO_QUERY_THROW));
+ save();
+ }
+ else
+ throw css::uno::RuntimeException(
+ "Extension manager could not access database file:"
+ + m_urlDb, nullptr);
+
+ if (!m_doc.is())
+ throw css::uno::RuntimeException(
+ "Extension manager could not get root node of data base file: "
+ + m_urlDb, nullptr);
+ }
+
+ return m_doc;
+}
+
+Reference<css::xml::xpath::XXPathAPI> const & BackendDb::getXPathAPI()
+{
+ if (!m_xpathApi.is())
+ {
+ m_xpathApi = css::xml::xpath::XPathAPI::create( m_xContext );
+
+ m_xpathApi->registerNS( getNSPrefix(), getDbNSName() );
+ }
+
+ return m_xpathApi;
+}
+
+void BackendDb::removeElement(OUString const & sXPathExpression)
+{
+ try
+ {
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ //find the extension element that is to be removed
+ const Reference<css::xml::dom::XNode> aNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+
+ if (aNode.is())
+ {
+ root->removeChild(aNode);
+ save();
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be any other entry with the same url
+ const Reference<css::xml::dom::XNode> nextNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+ OSL_ASSERT(! nextNode.is());
+#endif
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+void BackendDb::removeEntry(OUString const & url)
+{
+ const OUString sKeyElement = getKeyElementName();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ removeElement(sExpression);
+}
+
+void BackendDb::revokeEntry(OUString const & url)
+{
+ try
+ {
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ entry->setAttribute("revoked", "true");
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::activateEntry(OUString const & url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ //no attribute "active" means it is active, that is, registered.
+ entry->removeAttribute("revoked");
+ save();
+ ret = true;
+ }
+ return ret;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::hasActiveEntry(OUString const & url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ OUString sActive = entry->getAttribute("revoked");
+ if (!(sActive == "true"))
+ ret = true;
+ }
+ return ret;
+
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to determine an active entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+Reference<css::xml::dom::XNode> BackendDb::getKeyElement(
+ OUString const & url)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ return xpathApi->selectSingleNode(root, sExpression);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ OUString const & sVectorTagName,
+ OUString const & sPairTagName,
+ OUString const & sFirstTagName,
+ OUString const & sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent)
+{
+ try{
+ if (vecPairs.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ OSL_ASSERT(!sNameSpace.isEmpty());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> vectorNode(
+ doc->createElementNS(sNameSpace, sPrefix + sVectorTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ vectorNode, css::uno::UNO_QUERY_THROW));
+ for (auto const& vecPair : vecPairs)
+ {
+ const Reference<css::xml::dom::XElement> pairNode(
+ doc->createElementNS(sNameSpace, sPrefix + sPairTagName));
+
+ vectorNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ pairNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> firstNode(
+ doc->createElementNS(sNameSpace, sPrefix + sFirstTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> firstTextNode(
+ doc->createTextNode( vecPair.first));
+
+ firstNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstTextNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> secondNode(
+ doc->createElementNS(sNameSpace, sPrefix + sSecondTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> secondTextNode(
+ doc->createTextNode( vecPair.second));
+
+ secondNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondTextNode, css::uno::UNO_QUERY_THROW));
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector< std::pair< OUString, OUString > >
+BackendDb::readVectorOfPair(
+ Reference<css::xml::dom::XNode> const & parent,
+ OUString const & sListTagName,
+ OUString const & sPairTagName,
+ OUString const & sFirstTagName,
+ OUString const & sSecondTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprPairs(
+ sPrefix + sListTagName + "/" + sPrefix + sPairTagName);
+ const Reference<css::xml::dom::XNodeList> listPairs =
+ xpathApi->selectNodeList(parent, sExprPairs);
+
+ std::vector< std::pair< OUString, OUString > > retVector;
+ sal_Int32 length = listPairs->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> aPair = listPairs->item(i);
+ const OUString sExprFirst(sPrefix + sFirstTagName + "/text()");
+ const Reference<css::xml::dom::XNode> first =
+ xpathApi->selectSingleNode(aPair, sExprFirst);
+
+ const OUString sExprSecond(sPrefix + sSecondTagName + "/text()");
+ const Reference<css::xml::dom::XNode> second =
+ xpathApi->selectSingleNode(aPair, sExprSecond);
+ OSL_ASSERT(first.is() && second.is());
+
+ retVector.emplace_back(
+ first->getNodeValue(), second->getNodeValue());
+ }
+ return retVector;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeSimpleList(
+ std::deque< OUString> const & list,
+ OUString const & sListTagName,
+ OUString const & sMemberTagName,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (list.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> listNode(
+ doc->createElementNS(sNameSpace, sPrefix + sListTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ listNode, css::uno::UNO_QUERY_THROW));
+
+ for (auto const& elem : list)
+ {
+ const Reference<css::xml::dom::XNode> memberNode(
+ doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW);
+
+ listNode->appendChild(memberNode);
+
+ const Reference<css::xml::dom::XNode> textNode(
+ doc->createTextNode(elem), css::uno::UNO_QUERY_THROW);
+
+ memberNode->appendChild(textNode);
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Writes only the element if is has a value.
+//The prefix is automatically added to the element name
+void BackendDb::writeSimpleElement(
+ OUString const & sElementName, OUString const & value,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (value.isEmpty())
+ return;
+ const OUString sPrefix = getNSPrefix();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const OUString sNameSpace = getDbNSName();
+ const Reference<css::xml::dom::XNode> dataNode(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName),
+ UNO_QUERY_THROW);
+ xParent->appendChild(dataNode);
+
+ const Reference<css::xml::dom::XNode> dataValue(
+ doc->createTextNode(value), UNO_QUERY_THROW);
+ dataNode->appendChild(dataValue);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+
+}
+
+/// The key elements have a url attribute and are always children of the root element.
+Reference<css::xml::dom::XNode> BackendDb::writeKeyElement(
+ OUString const & url)
+{
+ try
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sElementName = getKeyElementName();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ //Check if there are an entry with the same url. This can be the case if the
+ //status of an XPackage is ambiguous. In this case a call to activateExtension
+ //(dp_extensionmanager.cxx), will register the package again. See also
+ //Package::processPackage_impl in dp_backend.cxx.
+ //A package can become
+ //invalid after its successful registration, for example if a second extension with
+ //the same service is installed.
+ const OUString sExpression(
+ sPrefix + ":" + sElementName + "[@url = \"" + url + "\"]");
+ const Reference<css::xml::dom::XNode> existingNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ if (existingNode.is())
+ {
+ OSL_ASSERT(false);
+ //replace the existing entry.
+ removeEntry(url);
+ }
+
+ const Reference<css::xml::dom::XElement> keyElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName));
+
+ keyElement->setAttribute("url", url);
+
+ const Reference<css::xml::dom::XNode> keyNode(
+ keyElement, UNO_QUERY_THROW);
+ root->appendChild(keyNode);
+ return keyNode;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+OUString BackendDb::readSimpleElement(
+ OUString const & sElementName, Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sExpr(sPrefix + ":" + sElementName + "/text()");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const Reference<css::xml::dom::XNode> val =
+ xpathApi->selectSingleNode(xParent, sExpr);
+ if (val.is())
+ return val->getNodeValue();
+ return OUString();
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data (readSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+std::deque< OUString> BackendDb::readList(
+ Reference<css::xml::dom::XNode> const & parent,
+ OUString const & sListTagName,
+ OUString const & sMemberTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprList(
+ sPrefix + sListTagName + "/" + sPrefix + sMemberTagName + "/text()");
+ const Reference<css::xml::dom::XNodeList> list =
+ xpathApi->selectNodeList(parent, sExprList);
+
+ std::deque<OUString > retList;
+ sal_Int32 length = list->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> member = list->item(i);
+ retList.push_back(member->getNodeValue());
+ }
+ return retList;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> BackendDb::getOneChildFromAllEntries(
+ OUString const & name)
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sNodeSelectExpr =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "/" +
+ sPrefix +
+ ":" +
+ name +
+ "/text()";
+
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sNodeSelectExpr);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+RegisteredDb::RegisteredDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+}
+
+void RegisteredDb::addEntry(OUString const & url)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sEntry = getKeyElementName();
+
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be yet an entry with the same url
+ OUString sExpression(
+ sPrefix + ":" + sEntry + "[@url = \"" + url + "\"]");
+ Reference<css::xml::dom::XNode> _extensionNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ OSL_ASSERT(! _extensionNode.is());
+#endif
+ Reference<css::xml::dom::XElement> helpElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sEntry));
+
+ helpElement->setAttribute("url", url);
+
+ Reference<css::xml::dom::XNode> helpNode(
+ helpElement, UNO_QUERY_THROW);
+ root->appendChild(helpNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_registry.cxx b/desktop/source/deployment/registry/dp_registry.cxx
new file mode 100644
index 000000000..50d0c0c4d
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_registry.cxx
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <dp_shared.hxx>
+#include <dp_package.hxx>
+#include <strings.hrc>
+#include <dp_registry.hxx>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/deployment/XPackageTypeInfo.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+
+namespace dp_registry {
+
+namespace {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageRegistry, util::XUpdatable > t_helper;
+
+
+class PackageRegistryImpl : private MutexHolder, public t_helper
+{
+ struct ci_string_hash {
+ std::size_t operator () ( OUString const & str ) const {
+ return str.toAsciiLowerCase().hashCode();
+ }
+ };
+ struct ci_string_equals {
+ bool operator () ( OUString const & str1, OUString const & str2 ) const{
+ return str1.equalsIgnoreAsciiCase( str2 );
+ }
+ };
+ typedef std::unordered_map<
+ OUString, Reference<deployment::XPackageRegistry>,
+ ci_string_hash, ci_string_equals > t_string2registry;
+ typedef std::unordered_map<
+ OUString, OUString,
+ ci_string_hash, ci_string_equals > t_string2string;
+ typedef std::set<
+ Reference<deployment::XPackageRegistry> > t_registryset;
+
+ t_string2registry m_mediaType2backend;
+ t_string2string m_filter2mediaType;
+ t_registryset m_ambiguousBackends;
+ t_registryset m_allBackends;
+ std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
+
+ void insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend );
+
+protected:
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryImpl() override;
+ PackageRegistryImpl() : t_helper( getMutex() ) {}
+
+
+public:
+ static Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+void PackageRegistryImpl::check()
+{
+ ::osl::MutexGuard guard( getMutex() );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "PackageRegistry instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageRegistryImpl::disposing()
+{
+ // dispose all backends:
+ for (auto const& backend : m_allBackends)
+ {
+ try_dispose(backend);
+ }
+ m_mediaType2backend = t_string2registry();
+ m_ambiguousBackends = t_registryset();
+ m_allBackends = t_registryset();
+
+ t_helper::disposing();
+}
+
+
+PackageRegistryImpl::~PackageRegistryImpl()
+{
+}
+
+
+OUString normalizeMediaType( OUString const & mediaType )
+{
+ OUStringBuffer buf;
+ sal_Int32 index = 0;
+ for (;;) {
+ buf.append( mediaType.getToken( 0, '/', index ).trim() );
+ if (index < 0)
+ break;
+ buf.append( '/' );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+void PackageRegistryImpl::packageRemoved(
+ OUString const & url, OUString const & mediaType)
+{
+ const t_string2registry::const_iterator i =
+ m_mediaType2backend.find(mediaType);
+
+ if (i != m_mediaType2backend.end())
+ {
+ i->second->packageRemoved(url, mediaType);
+ }
+}
+
+void PackageRegistryImpl::insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend )
+{
+ m_allBackends.insert( xBackend );
+ std::unordered_set<OUString> ambiguousFilters;
+
+ const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
+ xBackend->getSupportedPackageTypes() );
+ for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
+ {
+ m_typesInfos.push_back( xPackageType );
+
+ const OUString mediaType( normalizeMediaType(
+ xPackageType->getMediaType() ) );
+ std::pair<t_string2registry::iterator, bool> a_insertion(
+ m_mediaType2backend.emplace( mediaType, xBackend ) );
+ if (a_insertion.second)
+ {
+ // add parameterless media-type, too:
+ sal_Int32 semi = mediaType.indexOf( ';' );
+ if (semi >= 0) {
+ m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
+ }
+ const OUString fileFilter( xPackageType->getFileFilter() );
+ //The package backend shall also be called to determine the mediatype
+ //(XPackageRegistry.bindPackage) when the URL points to a directory.
+ const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
+ if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
+ {
+ m_ambiguousBackends.insert( xBackend );
+ }
+ else
+ {
+ sal_Int32 nIndex = 0;
+ do {
+ OUString token( fileFilter.getToken( 0, ';', nIndex ) );
+ if (token.match( "*." ))
+ token = token.copy( 1 );
+ if (token.isEmpty())
+ continue;
+ // mark any further wildcards ambig:
+ bool ambig = (token.indexOf('*') >= 0 ||
+ token.indexOf('?') >= 0);
+ if (! ambig) {
+ std::pair<t_string2string::iterator, bool> ins(
+ m_filter2mediaType.emplace(
+ token, mediaType ) );
+ ambig = !ins.second;
+ if (ambig) {
+ // filter has already been in: add previously
+ // added backend to ambig set
+ const t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find(
+ /* media-type of pr. added backend */
+ ins.first->second ) );
+ OSL_ASSERT(
+ iFind != m_mediaType2backend.end() );
+ if (iFind != m_mediaType2backend.end())
+ m_ambiguousBackends.insert( iFind->second );
+ }
+ }
+ if (ambig) {
+ m_ambiguousBackends.insert( xBackend );
+ // mark filter to be removed later from filters map:
+ ambiguousFilters.insert( token );
+ }
+ }
+ while (nIndex >= 0);
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
+ << mediaType
+ << "\" => "
+ << Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )->getImplementationName()
+ << "\"!" );
+ }
+#endif
+ }
+
+ // cut out ambiguous filters:
+ for (auto const& ambiguousFilter : ambiguousFilters)
+ {
+ m_filter2mediaType.erase(ambiguousFilter);
+ }
+}
+
+
+Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ PackageRegistryImpl * that = new PackageRegistryImpl;
+ Reference<deployment::XPackageRegistry> xRet(that);
+
+ // auto-detect all registered package registries:
+ Reference<container::XEnumeration> xEnum(
+ Reference<container::XContentEnumerationAccess>(
+ xComponentContext->getServiceManager(),
+ UNO_QUERY_THROW )->createContentEnumeration(
+ "com.sun.star.deployment.PackageRegistryBackend" ) );
+ if (xEnum.is())
+ {
+ while (xEnum->hasMoreElements())
+ {
+ Any element( xEnum->nextElement() );
+ Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
+ registryArgs[ 0 ] <<= context;
+ if (!cachePath.isEmpty())
+ {
+ Reference<lang::XServiceInfo> xServiceInfo(
+ element, UNO_QUERY_THROW );
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ registryArgs[ 1 ] <<= registryCachePath;
+ registryArgs[ 2 ] <<= false; // readOnly;
+ create_folder( nullptr, registryCachePath,
+ Reference<XCommandEnvironment>() );
+ }
+
+ Reference<deployment::XPackageRegistry> xBackend;
+ Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
+ if (xFac.is()) {
+ xBackend.set(
+ xFac->createInstanceWithArgumentsAndContext(
+ registryArgs, xComponentContext ), UNO_QUERY );
+ }
+ else {
+ Reference<lang::XSingleServiceFactory> xSingleServiceFac(
+ element, UNO_QUERY_THROW );
+ xBackend.set(
+ xSingleServiceFac->createInstanceWithArguments(
+ registryArgs ), UNO_QUERY );
+ }
+ if (! xBackend.is()) {
+ throw DeploymentException(
+ "cannot instantiate PackageRegistryBackend service: "
+ + Reference<lang::XServiceInfo>(
+ element, UNO_QUERY_THROW )->getImplementationName(),
+ static_cast<OWeakObject *>(that) );
+ }
+
+ that->insertBackend( xBackend );
+ }
+ }
+
+ // Insert bundle back-end.
+ // Always register as last, because we want to add extensions also as folders
+ // and as a default we accept every folder, which was not recognized by the other
+ // backends.
+ Reference<deployment::XPackageRegistry> extensionBackend =
+ ::dp_registry::backend::bundle::create(
+ that, context, cachePath, xComponentContext);
+ that->insertBackend(extensionBackend);
+
+ Reference<lang::XServiceInfo> xServiceInfo(
+ extensionBackend, UNO_QUERY_THROW );
+
+ OSL_ASSERT(xServiceInfo.is());
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
+
+
+#if OSL_DEBUG_LEVEL > 0
+ // dump tables:
+ {
+ t_registryset allBackends;
+ dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
+ for (auto const& elem : that->m_filter2mediaType)
+ {
+ OUStringBuffer buf;
+ buf.append( "extension \"" );
+ buf.append( elem.first );
+ buf.append( "\" maps to media-type \"" );
+ buf.append( elem.second );
+ buf.append( "\" maps to backend " );
+ const Reference<deployment::XPackageRegistry> xBackend(
+ that->m_mediaType2backend.find( elem.second )->second );
+ allBackends.insert( xBackend );
+ buf.append( Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )
+ ->getImplementationName() );
+ dp_misc::TRACE( buf.makeStringAndClear() + "\n");
+ }
+ dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
+ for (auto const& ambiguousBackend : that->m_ambiguousBackends)
+ {
+ OUStringBuffer buf;
+ buf.append(
+ Reference<lang::XServiceInfo>(
+ ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() );
+ buf.append( ": " );
+ const Sequence< Reference<deployment::XPackageTypeInfo> > types(
+ ambiguousBackend->getSupportedPackageTypes() );
+ for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
+ Reference<deployment::XPackageTypeInfo> const & xInfo =
+ types[ pos ];
+ buf.append( xInfo->getMediaType() );
+ const OUString filter( xInfo->getFileFilter() );
+ if (!filter.isEmpty()) {
+ buf.append( " (" );
+ buf.append( filter );
+ buf.append( ")" );
+ }
+ if (pos < (types.getLength() - 1))
+ buf.append( ", " );
+ }
+ dp_misc::TRACE(buf.makeStringAndClear() + "\n\n");
+ }
+ allBackends.insert( that->m_ambiguousBackends.begin(),
+ that->m_ambiguousBackends.end() );
+ OSL_ASSERT( allBackends == that->m_allBackends );
+ }
+#endif
+
+ return xRet;
+}
+
+// XUpdatable: broadcast to backends
+
+void PackageRegistryImpl::update()
+{
+ check();
+ for (auto const& backend : m_allBackends)
+ {
+ const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
+ if (xUpdatable.is())
+ xUpdatable->update();
+ }
+}
+
+// XPackageRegistry
+
+Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
+ OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ check();
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ {
+ ::ucbhelper::Content ucbContent;
+ bool bOk=true;
+
+ try
+ {
+ bOk = create_ucb_content(
+ &ucbContent, url, xCmdEnv, false /* no throw */ )
+ && !ucbContent.isFolder();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (bOk)
+ {
+ OUString title( StrTitle::getTitle( ucbContent ) );
+ for (;;)
+ {
+ const t_string2string::const_iterator iFind(
+ m_filter2mediaType.find(title) );
+ if (iFind != m_filter2mediaType.end()) {
+ mediaType = iFind->second;
+ break;
+ }
+ sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
+ if (point < 0)
+ break;
+ title = title.copy(point);
+ }
+ }
+ }
+ if (mediaType.isEmpty())
+ {
+ // try ambiguous backends:
+ for (auto const& ambiguousBackend : m_ambiguousBackends)
+ {
+ try {
+ return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ }
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ else
+ {
+ // get backend by media-type:
+ t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
+ if (iFind == m_mediaType2backend.end()) {
+ // xxx todo: more sophisticated media-type argument parsing...
+ sal_Int32 q = mediaType.indexOf( ';' );
+ if (q >= 0) {
+ iFind = m_mediaType2backend.find(
+ normalizeMediaType(
+ // cut parameters:
+ mediaType.copy( 0, q ) ) );
+ }
+ }
+ if (iFind == m_mediaType2backend.end()) {
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ return iFind->second->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageRegistryImpl::getSupportedPackageTypes()
+{
+ return comphelper::containerToSequence(m_typesInfos);
+}
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ return PackageRegistryImpl::create(
+ context, cachePath, xComponentContext );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executable.cxx b/desktop/source/deployment/registry/executable/dp_executable.cxx
new file mode 100644
index 000000000..9769051af
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executable.cxx
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <dp_misc.h>
+#include <dp_backend.h>
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <svl/inettype.hxx>
+#include "dp_executablebackenddb.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace dp_misc;
+
+namespace dp_registry::backend::executable {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ExecutablePackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ bool getFileAttributes(sal_uInt64& out_Attributes);
+ bool isUrlTargetInExtension() const;
+
+ public:
+ ExecutablePackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+ {}
+ };
+ friend class ExecutablePackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(OUString const & url);
+ void revokeEntryFromDb(OUString const & url);
+
+ Reference<deployment::XPackageTypeInfo> m_xExecutableTypeInfo;
+ std::unique_ptr<ExecutableBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xExecutableTypeInfo(new Package::TypeInfo(
+ "application/vnd.sun.star.executable",
+ "", "Executable" ) )
+{
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ExecutableBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+void BackendImpl::revokeEntryFromDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence<Reference<deployment::XPackageTypeInfo> >(
+ & m_xExecutableTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (mediaType.isEmpty())
+ {
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.executable"))
+ {
+ return new BackendImpl::ExecutablePackageImpl(
+ this, url, name, m_xExecutableTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ return Reference<deployment::XPackage>();
+}
+
+
+// Package
+BackendImpl * BackendImpl::ExecutablePackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException( "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ExecutablePackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ExecutablePackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<dp_misc::AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ bool registered = getMyBackend()->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ registered, false /* IsAmbiguous */ ) );
+}
+
+void BackendImpl::ExecutablePackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ checkAborted(abortChannel);
+ if (doRegisterPackage)
+ {
+ if (!isUrlTargetInExtension())
+ {
+ OSL_ASSERT(false);
+ return;
+ }
+ sal_uInt64 attributes = 0;
+ //Setting the executable attribute does not affect executables on Windows
+ if (getFileAttributes(attributes))
+ {
+ if(getMyBackend()->m_context == "user")
+ attributes |= osl_File_Attribute_OwnExe;
+ else if (getMyBackend()->m_context == "shared")
+ attributes |= (osl_File_Attribute_OwnExe | osl_File_Attribute_GrpExe
+ | osl_File_Attribute_OthExe);
+ else if (getMyBackend()->m_context != "bundled")
+ //Bundled extensions are required to be in the properly
+ //installed. That is an executable must have the right flags
+ OSL_ASSERT(false);
+
+ //This won't have effect on Windows
+ osl::File::setAttributes(
+ dp_misc::expandUnoRcUrl(m_url), attributes);
+ }
+ getMyBackend()->addDataToDb(getURL());
+ }
+ else
+ {
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+//We currently cannot check if this XPackage represents a content of a particular extension
+//But we can check if we are within $UNO_USER_PACKAGES_CACHE etc.
+//Done for security reasons. For example an extension manifest could contain a path to
+//an executable outside the extension.
+bool BackendImpl::ExecutablePackageImpl::isUrlTargetInExtension() const
+{
+ bool bSuccess = false;
+ OUString sExtensionDir;
+ if(getMyBackend()->m_context == "user")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_USER_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "shared")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_SHARED_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "bundled")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$BUNDLED_EXTENSIONS");
+ else
+ OSL_ASSERT(false);
+ //remove file ellipses
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(OUString(), sExtensionDir, sExtensionDir))
+ {
+ OUString sFile;
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(
+ OUString(), dp_misc::expandUnoRcUrl(m_url), sFile))
+ {
+ if (sFile.match(sExtensionDir))
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+bool BackendImpl::ExecutablePackageImpl::getFileAttributes(sal_uInt64& out_Attributes)
+{
+ bool bSuccess = false;
+ const OUString url(dp_misc::expandUnoRcUrl(m_url));
+ osl::DirectoryItem item;
+ if (osl::FileBase::E_None == osl::DirectoryItem::get(url, item))
+ {
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ if( osl::FileBase::E_None == item.getFileStatus(aStatus))
+ {
+ out_Attributes = aStatus.getAttributes();
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ "com.sun.star.comp.deployment.executable.PackageRegistryBackend",
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry::backend::executable
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
new file mode 100644
index 000000000..19790e9bc
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_executablebackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/executable-registry/2010"
+#define NS_PREFIX "exe"
+#define ROOT_ELEMENT_NAME "executable-backend-db"
+#define ENTRY_NAME "executable"
+
+namespace dp_registry::backend::executable {
+
+ExecutableBackendDb::ExecutableBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ExecutableBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExecutableBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExecutableBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExecutableBackendDb::getKeyElementName()
+{
+ return ENTRY_NAME;
+}
+
+
+} // namespace dp_registry::backend::executable
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
new file mode 100644
index 000000000..d992b0248
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_EXECUTABLE_DP_EXECUTABLEBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_EXECUTABLE_DP_EXECUTABLEBACKENDDB_HXX
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace executable {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+ */
+class ExecutableBackendDb: public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+
+ ExecutableBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+};
+
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_help.cxx b/desktop/source/deployment/registry/help/dp_help.cxx
new file mode 100644
index 000000000..eddb42b28
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_help.cxx
@@ -0,0 +1,601 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <config_feature_desktop.h>
+
+#include <strings.hrc>
+#include <dp_backend.h>
+#include "dp_helpbackenddb.hxx"
+#include <dp_services.hxx>
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <svl/inettype.hxx>
+#include <unotools/pathoptions.hxx>
+
+#if HAVE_FEATURE_DESKTOP
+#include <helpcompiler/compilehelp.hxx>
+#include <helpcompiler/HelpIndexer.hxx>
+#endif
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/util/XMacroExpander.hpp>
+#include <optional>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::help {
+namespace {
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+
+ bool extensionContainsCompiledHelp();
+
+ //XPackage
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void implProcessHelp( PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ void implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector );
+
+ ::std::optional<HelpBackendDb::Data> readDataFromDb(OUString const & url);
+ bool hasActiveEntry(OUString const & url);
+ bool activateEntry(OUString const & url);
+
+ Reference< ucb::XSimpleFileAccess3 > const & getFileAccess();
+ Reference< ucb::XSimpleFileAccess3 > m_xSFA;
+
+ const Reference<deployment::XPackageTypeInfo> m_xHelpTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<HelpBackendDb> m_backendDb;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xHelpTypeInfo( new Package::TypeInfo("application/vnd.sun.star.help",
+ OUString(),
+ DpResId(RID_STR_HELP)
+ ) ),
+ m_typeInfos( 1 )
+{
+ m_typeInfos[ 0 ] = m_xHelpTypeInfo;
+ if (transientMode())
+ return;
+
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new HelpBackendDb(getComponentContext(), dbFile));
+
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ // we don't support auto detection:
+ if (mediaType_.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType_, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.help"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xHelpTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType_,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+::std::optional<HelpBackendDb::Data> BackendImpl::readDataFromDb(
+ OUString const & url)
+{
+ ::std::optional<HelpBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+bool BackendImpl::hasActiveEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name, xPackageType, bRemoved,
+ identifier)
+{
+}
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+bool BackendImpl::PackageImpl::extensionContainsCompiledHelp()
+{
+ bool bCompiled = true;
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl(getURL());
+
+ ::osl::Directory helpFolder(aExpandedHelpURL);
+ if ( helpFolder.open() == ::osl::File::E_None)
+ {
+ //iterate over the contents of the help folder
+ //We assume that all folders within the help folder contain language specific
+ //help files. If just one of them does not contain compiled help then this
+ //function returns false.
+ ::osl::DirectoryItem item;
+ ::osl::File::RC errorNext = ::osl::File::E_None;
+ while ((errorNext = helpFolder.getNextItem(item)) == ::osl::File::E_None)
+ {
+ //No find the language folders
+ ::osl::FileStatus stat(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |osl_FileStatus_Mask_FileURL);
+ if (item.getFileStatus(stat) == ::osl::File::E_None)
+ {
+ if (stat.getFileType() != ::osl::FileStatus::Directory)
+ continue;
+
+ //look if there is the folder help.idxl in the language folder
+ OUString compUrl(stat.getFileURL() + "/help.idxl");
+ ::osl::Directory compiledFolder(compUrl);
+ if (compiledFolder.open() != ::osl::File::E_None)
+ {
+ bCompiled = false;
+ break;
+ }
+ }
+ else
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ break;
+ }
+ }
+ if (errorNext != ::osl::File::E_NOENT
+ && errorNext != ::osl::File::E_None)
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ }
+ }
+ return bCompiled;
+}
+
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+ return beans::Optional< beans::Ambiguous<sal_Bool> >( true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl* that = getMyBackend();
+ that->implProcessHelp( this, doRegisterPackage, xCmdEnv);
+}
+
+beans::Optional< OUString > BackendImpl::PackageImpl::getRegistrationDataURL()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::std::optional<HelpBackendDb::Data> data =
+ getMyBackend()->readDataFromDb(getURL());
+
+ if (data && getMyBackend()->hasActiveEntry(getURL()))
+ return beans::Optional<OUString>(true, data->dataUrl);
+
+ return beans::Optional<OUString>(true, OUString());
+}
+
+void BackendImpl::implProcessHelp(
+ PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference< deployment::XPackage > xPackage(package);
+ OSL_ASSERT(xPackage.is());
+ if (doRegisterPackage)
+ {
+ //revive already processed help if possible
+ if ( !activateEntry(xPackage->getURL()))
+ {
+ HelpBackendDb::Data data;
+ data.dataUrl = xPackage->getURL();
+ if (!package->extensionContainsCompiledHelp())
+ {
+#if HAVE_FEATURE_DESKTOP
+ const OUString sHelpFolder = createFolder(xCmdEnv);
+ data.dataUrl = sHelpFolder;
+
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+ OUString aHelpURL = xPackage->getURL();
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl( aHelpURL );
+ if( !xSFA->isFolder( aExpandedHelpURL ) )
+ {
+ OUString aErrStr = DpResId( RID_STR_HELPPROCESSING_GENERAL_ERROR ) +
+ "No help folder";
+ OWeakObject* oWeakThis = static_cast<OWeakObject *>(this);
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ makeAny( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+
+ // Scan languages
+ Sequence< OUString > aLanguageFolderSeq = xSFA->getFolderContents( aExpandedHelpURL, true );
+ sal_Int32 nLangCount = aLanguageFolderSeq.getLength();
+ const OUString* pSeq = aLanguageFolderSeq.getConstArray();
+ for( sal_Int32 iLang = 0 ; iLang < nLangCount ; ++iLang )
+ {
+ OUString aLangURL = pSeq[iLang];
+ if( xSFA->isFolder( aLangURL ) )
+ {
+ std::vector< OUString > aXhpFileVector;
+
+ // calculate jar file URL
+ sal_Int32 indexStartSegment = aLangURL.lastIndexOf('/');
+ // for example "/en"
+ OUString langFolderURLSegment(
+ aLangURL.copy(
+ indexStartSegment + 1, aLangURL.getLength() - indexStartSegment - 1));
+
+ //create the folder in the "temporary folder"
+ ::ucbhelper::Content langFolderContent;
+ const OUString langFolderDest = makeURL(sHelpFolder, langFolderURLSegment);
+ const OUString langFolderDestExpanded = ::dp_misc::expandUnoRcUrl(langFolderDest);
+ ::dp_misc::create_folder(
+ &langFolderContent,
+ langFolderDest, xCmdEnv);
+
+ const OUString aHelpStr("help");
+ const OUString aSlash("/");
+
+ OUString aJarFile(
+ makeURL(sHelpFolder, langFolderURLSegment + aSlash + aHelpStr + ".jar"));
+ aJarFile = ::dp_misc::expandUnoRcUrl(aJarFile);
+
+ OUString aEncodedJarFilePath = rtl::Uri::encode(
+ aJarFile, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ OUString aDestBasePath = "vnd.sun.star.zip://" +
+ aEncodedJarFilePath + "/" ;
+
+ sal_Int32 nLenLangFolderURL = aLangURL.getLength() + 1;
+
+ Sequence< OUString > aSubLangSeq = xSFA->getFolderContents( aLangURL, true );
+ sal_Int32 nSubLangCount = aSubLangSeq.getLength();
+ const OUString* pSubLangSeq = aSubLangSeq.getConstArray();
+ for( sal_Int32 iSubLang = 0 ; iSubLang < nSubLangCount ; ++iSubLang )
+ {
+ OUString aSubFolderURL = pSubLangSeq[iSubLang];
+ if( !xSFA->isFolder( aSubFolderURL ) )
+ continue;
+
+ implCollectXhpFiles( aSubFolderURL, aXhpFileVector );
+
+ // Copy to package (later: move?)
+ OUString aDestPath = aDestBasePath;
+ OUString aPureFolderName = aSubFolderURL.copy( nLenLangFolderURL );
+ aDestPath += aPureFolderName;
+ xSFA->copy( aSubFolderURL, aDestPath );
+ }
+
+ // Call compiler
+ sal_Int32 nXhpFileCount = aXhpFileVector.size();
+ std::unique_ptr<OUString[]> pXhpFiles(new OUString[nXhpFileCount]);
+ for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
+ {
+ OUString aXhpFile = aXhpFileVector[iXhp];
+ OUString aXhpRelFile = aXhpFile.copy( nLenLangFolderURL );
+ pXhpFiles[iXhp] = aXhpRelFile;
+ }
+
+ OUString aOfficeHelpPath( SvtPathOptions().GetHelpPath() );
+ OUString aOfficeHelpPathFileURL;
+ ::osl::File::getFileURLFromSystemPath( aOfficeHelpPath, aOfficeHelpPathFileURL );
+
+ HelpProcessingErrorInfo aErrorInfo;
+ bool bSuccess = compileExtensionHelp(
+ aOfficeHelpPathFileURL, aHelpStr, aLangURL,
+ nXhpFileCount, pXhpFiles.get(),
+ langFolderDestExpanded, aErrorInfo );
+
+ pXhpFiles.reset();
+
+ if( bSuccess )
+ {
+ OUString aLang;
+ sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ aLang = aLangURL.copy( nLastSlash + 1 );
+ else
+ aLang = "en";
+
+ HelpIndexer aIndexer(aLang, "help", langFolderDestExpanded, langFolderDestExpanded);
+ aIndexer.indexDocuments();
+ }
+
+ if( !bSuccess )
+ {
+ const char* pErrStrId = nullptr;
+ switch( aErrorInfo.m_eErrorClass )
+ {
+ case HelpProcessingErrorClass::General: pErrStrId = RID_STR_HELPPROCESSING_GENERAL_ERROR; break;
+ case HelpProcessingErrorClass::XmlParsing: pErrStrId = RID_STR_HELPPROCESSING_XMLPARSING_ERROR; break;
+ default: ;
+ };
+
+ OUString aErrStr;
+ if (pErrStrId)
+ {
+ aErrStr = DpResId(pErrStrId);
+
+ // Remove CR/LF
+ OUString aErrMsg( aErrorInfo.m_aErrorMsg );
+ sal_Unicode const nCR = 13, nLF = 10;
+ sal_Int32 nSearchCR = aErrMsg.indexOf( nCR );
+ sal_Int32 nSearchLF = aErrMsg.indexOf( nLF );
+ sal_Int32 nCopy;
+ if( nSearchCR != -1 || nSearchLF != -1 )
+ {
+ if( nSearchCR == -1 )
+ nCopy = nSearchLF;
+ else if( nSearchLF == -1 )
+ nCopy = nSearchCR;
+ else
+ nCopy = ( nSearchCR < nSearchLF ) ? nSearchCR : nSearchLF;
+
+ aErrMsg = aErrMsg.copy( 0, nCopy );
+ }
+ aErrStr += aErrMsg;
+ if (!strcmp(pErrStrId, RID_STR_HELPPROCESSING_XMLPARSING_ERROR) && !aErrorInfo.m_aXMLParsingFile.isEmpty() )
+ {
+ aErrStr += " in ";
+
+ OUString aDecodedFile = rtl::Uri::decode( aErrorInfo.m_aXMLParsingFile,
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ aErrStr += aDecodedFile;
+ if( aErrorInfo.m_nXMLParsingLine != -1 )
+ {
+ aErrStr += ", line " +
+ OUString::number( aErrorInfo.m_nXMLParsingLine );
+ }
+ }
+ }
+
+ OWeakObject* oWeakThis = static_cast<OWeakObject *>(this);
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ makeAny( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+ }
+ }
+#else
+ (void) xCmdEnv;
+#endif
+ }
+ // Writing the data entry replaces writing the flag file. If we got to this
+ // point the registration was successful.
+ if (m_backendDb)
+ m_backendDb->addEntry(xPackage->getURL(), data);
+ }
+ } //if (doRegisterPackage)
+ else
+ {
+ if (m_backendDb)
+ m_backendDb->revokeEntry(xPackage->getURL());
+ }
+}
+
+void BackendImpl::implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector )
+{
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+
+ // Scan xhp files recursively
+ Sequence< OUString > aSeq = xSFA->getFolderContents( aDir, true );
+ sal_Int32 nCount = aSeq.getLength();
+ const OUString* pSeq = aSeq.getConstArray();
+ for( sal_Int32 i = 0 ; i < nCount ; ++i )
+ {
+ OUString aURL = pSeq[i];
+ if( xSFA->isFolder( aURL ) )
+ {
+ implCollectXhpFiles( aURL, o_rXhpFileVector );
+ }
+ else
+ {
+ sal_Int32 nLastDot = aURL.lastIndexOf( '.' );
+ if( nLastDot != -1 )
+ {
+ OUString aExt = aURL.copy( nLastDot + 1 );
+ if( aExt.equalsIgnoreAsciiCase( "xhp" ) )
+ o_rXhpFileVector.push_back( aURL );
+ }
+ }
+ }
+}
+
+Reference< ucb::XSimpleFileAccess3 > const & BackendImpl::getFileAccess()
+{
+ if( !m_xSFA.is() )
+ {
+ Reference<XComponentContext> const & xContext = getComponentContext();
+ if( xContext.is() )
+ {
+ m_xSFA = ucb::SimpleFileAccess::create(xContext);
+ }
+ if( !m_xSFA.is() )
+ {
+ throw RuntimeException(
+ "dp_registry::backend::help::BackendImpl::getFileAccess(), "
+ "could not instantiate SimpleFileAccess." );
+ }
+ }
+ return m_xSFA;
+}
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ "com.sun.star.comp.deployment.help.PackageRegistryBackend",
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
new file mode 100644
index 000000000..73b7279d1
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_helpbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/help-registry/2010"
+#define NS_PREFIX "help"
+#define ROOT_ELEMENT_NAME "help-backend-db"
+#define KEY_ELEMENT_NAME "help"
+
+namespace dp_registry::backend::help {
+
+HelpBackendDb::HelpBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString HelpBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString HelpBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString HelpBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString HelpBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void HelpBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement("data-url", data.dataUrl, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<HelpBackendDb::Data>
+HelpBackendDb::getEntry(OUString const & url)
+{
+ try
+ {
+ HelpBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement("data-url", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> HelpBackendDb::getAllDataUrls()
+{
+ return getOneChildFromAllEntries("data-url");
+}
+
+} // namespace dp_registry::backend::help
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
new file mode 100644
index 000000000..18e0aa4a0
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_HELP_DP_HELPBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_HELP_DP_HELPBACKENDDB_HXX
+
+#include <rtl/ustring.hxx>
+#include <optional>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace help {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class HelpBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the compiled help files, etc.
+ */
+ OUString dataUrl;
+
+ };
+
+public:
+
+ HelpBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ ::std::optional<Data> getEntry(OUString const & url);
+ //must also return the data urls for entries with @active="false". That is,
+ //those are currently revoked.
+ std::vector< OUString> getAllDataUrls();
+
+};
+
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backend.h b/desktop/source/deployment/registry/inc/dp_backend.h
new file mode 100644
index 000000000..ea54e4185
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backend.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_INC_DP_BACKEND_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_INC_DP_BACKEND_H
+
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <dp_interact.h>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <unordered_map>
+#include <strings.hrc>
+
+namespace dp_registry
+{
+namespace backend
+{
+
+class PackageRegistryBackend;
+
+#define BACKEND_SERVICE_NAME "com.sun.star.deployment.PackageRegistryBackend"
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackage > t_PackageBase;
+
+
+class Package : protected ::dp_misc::MutexHolder, public t_PackageBase
+{
+ PackageRegistryBackend * getMyBackend() const;
+ void processPackage_impl(
+ bool registerPackage,
+ bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+protected:
+ ::rtl::Reference<PackageRegistryBackend> m_myBackend;
+ const OUString m_url;
+ OUString m_name;
+ OUString m_displayName;
+ const css::uno::Reference<css::deployment::XPackageTypeInfo> m_xPackageType;
+ const bool m_bRemoved;
+ //Only set if m_bRemoved = true;
+ const OUString m_identifier;
+
+ void check() const;
+ void fireModified();
+ virtual void SAL_CALL disposing() override;
+
+ void checkAborted(
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel );
+
+ // @@@ to be implemented by specific backend:
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ virtual ~Package() override;
+ Package( ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ OUString const & displayName,
+ css::uno::Reference<css::deployment::XPackageTypeInfo> const &
+ xPackageType,
+ bool bRemoved,
+ OUString const & identifier);
+
+public:
+
+ class TypeInfo :
+ public ::cppu::WeakImplHelper<css::deployment::XPackageTypeInfo>
+ {
+ const OUString m_mediaType;
+ const OUString m_fileFilter;
+ const OUString m_shortDescr;
+ public:
+ virtual ~TypeInfo() override;
+ TypeInfo( OUString const & mediaType,
+ OUString const & fileFilter,
+ OUString const & shortDescr )
+ : m_mediaType(mediaType), m_fileFilter(fileFilter),
+ m_shortDescr(shortDescr)
+ {}
+ // XPackageTypeInfo
+ virtual OUString SAL_CALL getMediaType() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getShortDescription() override;
+ virtual OUString SAL_CALL getFileFilter() override;
+ virtual css::uno::Any SAL_CALL getIcon( sal_Bool highContrast,
+ sal_Bool smallIcon ) override;
+ };
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+ // XPackage
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ SAL_CALL isRegistered(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >& xAbortChannel,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual ::sal_Bool SAL_CALL checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual void SAL_CALL registerPackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void SAL_CALL revokePackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual sal_Bool SAL_CALL isBundle() override;
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getBundle(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getIdentifier() override;
+ virtual OUString SAL_CALL getVersion() override;
+ virtual OUString SAL_CALL getURL() override;
+ virtual OUString SAL_CALL getDisplayName() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getUpdateInformationURLs() override;
+ virtual css::beans::StringPair SAL_CALL getPublisherInfo() override;
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ virtual css::uno::Reference<css::deployment::XPackageTypeInfo> SAL_CALL
+ getPackageType() override;
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL,
+ OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getRepositoryName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ virtual sal_Bool SAL_CALL isRemoved() override;
+
+};
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XEventListener,
+ css::deployment::XPackageRegistry > t_BackendBase;
+
+
+class PackageRegistryBackend
+ : protected ::dp_misc::MutexHolder, public t_BackendBase
+{
+ //The map held originally WeakReferences. The map entries are removed in the disposing
+ //function, which is called when the XPackages are destructed or they are
+ //explicitly disposed. The latter happens, for example, when an extension is
+ //removed (see dp_manager.cxx). However, because of how the help systems work, now
+ // XPackageManager::getDeployedPackages is called often. This results in a lot
+ //of bindPackage calls which are costly. Therefore we keep hard references in
+ //the map now.
+ typedef std::unordered_map<
+ OUString, css::uno::Reference<css::deployment::XPackage> > t_string2ref;
+ t_string2ref m_bound;
+
+protected:
+ OUString m_cachePath;
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+
+ OUString m_context;
+ // currently only for library containers:
+ enum class Context {
+ Unknown, User, Shared, Bundled, Tmp, Document
+ } m_eContext;
+
+ static OUString StrCannotDetectMediaType() { return DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE); }
+ static OUString StrUnsupportedMediaType() { return DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE); }
+
+ // @@@ to be implemented by specific backend:
+ virtual css::uno::Reference<css::deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryBackend() override;
+ PackageRegistryBackend(
+ css::uno::Sequence<css::uno::Any> const & args,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+
+ /* creates a folder with a unique name.
+ If url is empty then it is created in the backend folder, otherwise
+ at a location relative to that folder specified by url.
+ */
+ OUString createFolder(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+ /* deletes folders and files.
+
+ All folder all files which end with ".tmp" or ".tmp_" and which are
+ not used are deleted.
+ */
+ void deleteUnusedFolders(
+ std::vector< OUString> const & usedFolders);
+ /* deletes one folder with a "temporary" name and the corresponding
+ tmp file, which was used to derive the folder name.
+ */
+ static void deleteTempFolder(
+ OUString const & folderUrl);
+
+public:
+ static OUString StrRegisteringPackage() { return DpResId(RID_STR_REGISTERING_PACKAGE); }
+ static OUString StrRevokingPackage() { return DpResId(RID_STR_REVOKING_PACKAGE); }
+
+ css::uno::Reference<css::uno::XComponentContext> const &
+ getComponentContext() const { return m_xComponentContext; }
+
+ OUString const & getCachePath() const { return m_cachePath; }
+ bool transientMode() const { return m_cachePath.isEmpty(); }
+
+ const OUString& getContext() const {return m_context; }
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XPackageRegistry
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType,
+ sal_Bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+// virtual void SAL_CALL packageRemoved(
+// OUString const & url, OUString const & mediaType)
+// throw (css::deployment::DeploymentException,
+// css::uno::RuntimeException);
+
+};
+
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backenddb.hxx b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
new file mode 100644
index 000000000..8fd0cd0e8
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_INC_DP_BACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_INC_DP_BACKENDDB_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+#include <deque>
+#include <vector>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace xml::dom {
+ class XDocument;
+ class XNode;
+ }
+ namespace xml::xpath {
+ class XXPathAPI;
+ }
+}
+
+namespace dp_registry {
+namespace backend {
+
+class BackendDb
+{
+private:
+
+ css::uno::Reference<css::xml::dom::XDocument> m_doc;
+ css::uno::Reference<css::xml::xpath::XXPathAPI> m_xpathApi;
+
+ BackendDb(BackendDb const &) = delete;
+ BackendDb & operator = (BackendDb const &) = delete;
+
+protected:
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ OUString m_urlDb;
+
+protected:
+
+ /* caller must make sure that only one thread accesses the function
+ */
+ css::uno::Reference<css::xml::dom::XDocument> const & getDocument();
+
+ /* the namespace prefix is "reg" (without quotes)
+ */
+ css::uno::Reference<css::xml::xpath::XXPathAPI> const & getXPathAPI();
+ void save();
+ void removeElement(OUString const & sXPathExpression);
+
+ css::uno::Reference<css::xml::dom::XNode> getKeyElement(
+ OUString const & url);
+
+ void writeSimpleList(
+ std::deque< OUString> const & list,
+ OUString const & sListTagName,
+ OUString const & sMemberTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ OUString const & sVectorTagName,
+ OUString const & sPairTagName,
+ OUString const & sFirstTagName,
+ OUString const & sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeSimpleElement(
+ OUString const & sElementName, OUString const & value,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ css::uno::Reference<css::xml::dom::XNode> writeKeyElement(
+ OUString const & url);
+
+ OUString readSimpleElement(
+ OUString const & sElementName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ std::vector< std::pair< OUString, OUString > >
+ readVectorOfPair(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ OUString const & sListTagName,
+ OUString const & sPairTagName,
+ OUString const & sFirstTagName,
+ OUString const & sSecondTagName);
+
+ std::deque< OUString> readList(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ OUString const & sListTagName,
+ OUString const & sMemberTagName);
+
+ /* returns the values of one particularly child element of all key elements.
+ */
+ std::vector< OUString> getOneChildFromAllEntries(
+ OUString const & sElementName);
+
+
+ /* returns the namespace which is to be written as xmlns attribute
+ into the root element.
+ */
+ virtual OUString getDbNSName()=0;
+ /* return the namespace prefix which is to be registered with the XPath API.
+
+ The prefix can then be used in XPath expressions.
+ */
+ virtual OUString getNSPrefix()=0;
+ /* returns the name of the root element without any namespace prefix.
+ */
+ virtual OUString getRootElementName()=0;
+ /* returns the name of xml element for each entry
+ */
+ virtual OUString getKeyElementName()=0;
+
+public:
+ BackendDb(css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+ virtual ~BackendDb() {};
+
+ void removeEntry(OUString const & url);
+
+ /* This is called to write the "revoked" attribute to the entry.
+ This is done when XPackage::revokePackage is called.
+ */
+ void revokeEntry(OUString const & url);
+
+ /* returns false if the entry does not exist yet.
+ */
+ bool activateEntry(OUString const & url);
+
+ bool hasActiveEntry(OUString const & url);
+
+};
+
+class RegisteredDb: public BackendDb
+{
+
+public:
+ RegisteredDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+
+ void addEntry(OUString const & url);
+};
+
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.cxx b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
new file mode 100644
index 000000000..c96d1466b
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_extbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/extension-registry/2010"
+#define NS_PREFIX "ext"
+#define ROOT_ELEMENT_NAME "extension-backend-db"
+#define KEY_ELEMENT_NAME "extension"
+
+namespace dp_registry::backend::bundle {
+
+ExtensionBackendDb::ExtensionBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ExtensionBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExtensionBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExtensionBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExtensionBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ExtensionBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ //reactive revoked entry if possible.
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> extensionNodeNode = writeKeyElement(url);
+ writeVectorOfPair( data.items, "extension-items", "item",
+ "url", "media-type", extensionNodeNode);
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ExtensionBackendDb::Data ExtensionBackendDb::getEntry(OUString const & url)
+{
+ try
+ {
+ ExtensionBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+
+ if (aNode.is())
+ {
+ retData.items =
+ readVectorOfPair( aNode, "extension-items", "item",
+ "url", "media-type");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::bundle
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.hxx b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
new file mode 100644
index 000000000..51427e73b
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_PACKAGE_DP_EXTBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_PACKAGE_DP_EXTBACKENDDB_HXX
+
+#include <utility>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace bundle {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ExtensionBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* every element consists of a pair of the url to the item (jar,rdb, etc)
+ and the media type
+ */
+ std::vector< std::pair< OUString, OUString> > items;
+ };
+
+public:
+ ExtensionBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ Data getEntry(OUString const & url);
+
+};
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_package.cxx b/desktop/source/deployment/registry/package/dp_package.cxx
new file mode 100644
index 000000000..ce8fe18a4
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_package.cxx
@@ -0,0 +1,1596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_package.hxx>
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <dp_dependencies.hxx>
+#include <dp_platform.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_resource.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XInteractionReplaceExistingData.hpp>
+#include <com/sun/star/ucb/NameClashResolveRequest.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <optional>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "dp_extbackenddb.hxx"
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::bundle {
+namespace {
+
+typedef cppu::ImplInheritanceHelper<PackageRegistryBackend,
+ lang::XServiceInfo> ImplBaseT;
+
+
+class BackendImpl : public ImplBaseT
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+ /** contains the old tooltip description for the Extension Manager GUI in OOo v.2.x
+ We keep it for backward compatibility.
+ */
+ OUString m_oldDescription;
+ OUString m_url_expanded;
+ const bool m_legacyBundle;
+ Sequence< Reference<deployment::XPackage> > m_bundle;
+ Sequence< Reference<deployment::XPackage> > * m_pBundle;
+
+ ExtensionBackendDb::Data m_dbData;
+
+ Reference<deployment::XPackage> bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, //that is, using data base information
+ OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError = true );
+
+ typedef std::vector< Reference<deployment::XPackage> > t_packagevec;
+ void scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv );
+ void scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration = false );
+ std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ bool checkPlatform(
+ Reference<ucb::XCommandEnvironment > const & environment);
+
+ bool checkDependencies(
+ Reference<ucb::XCommandEnvironment > const &
+ environment,
+ DescriptionInfoset const & description);
+ // throws css::uno::RuntimeException,
+ // css::deployment::DeploymentException
+
+ /// @throws deployment::DeploymentException
+ /// @throws ucb::CommandFailedException
+ /// @throws ucb::CommandAbortedException
+ /// @throws RuntimeException
+ bool checkLicense(
+ Reference< ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & description, bool bNoLicenseChecking);
+ // @throws DeploymentException
+ OUString getTextFromURL(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl);
+
+ DescriptionInfoset getDescriptionInfoset() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle,
+ bool bRemoved,
+ OUString const & identifier);
+
+ // XPackage
+ virtual sal_Bool SAL_CALL isBundle() override;
+
+ virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getDescription() override;
+
+ virtual OUString SAL_CALL getLicenseText() override;
+
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const Reference< task::XAbortChannel >& xAbortChannel,
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual sal_Bool SAL_CALL checkDependencies(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
+
+ virtual OUString SAL_CALL getVersion() override;
+
+ virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
+
+ virtual beans::StringPair SAL_CALL getPublisherInfo() override;
+
+ virtual OUString SAL_CALL getDisplayName() override;
+
+ virtual Reference< graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ };
+ friend class PackageImpl;
+
+ Reference<deployment::XPackageRegistry> m_xRootRegistry;
+ const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ std::unique_ptr<ExtensionBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
+ ExtensionBackendDb::Data readDataFromDb(OUString const & url);
+ void revokeEntryFromDb(OUString const & url);
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using ImplBaseT::disposing;
+};
+
+//Used to find a XPackage with a particular URL
+class XPackage_eq
+{
+ OUString m_URL;
+public:
+ explicit XPackage_eq(const OUString & s) : m_URL(s) {}
+ bool operator() (const Reference<deployment::XPackage> & p) const
+ {
+ return m_URL == p->getURL();
+ }
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry )
+ : ImplBaseT( args, xComponentContext ),
+ m_xRootRegistry( xRootRegistry ),
+ m_xBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.package-bundle",
+ "*.oxt;*.uno.pkg",
+ DpResId(RID_STR_PACKAGE_BUNDLE)
+ ) ),
+ m_xLegacyBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.legacy-package-bundle",
+ "*.zip",
+ m_xBundleTypeInfo->getShortDescription()
+ ) ),
+ m_typeInfos(2)
+{
+ m_typeInfos[ 0 ] = m_xBundleTypeInfo;
+ m_typeInfos[ 1 ] = m_xLegacyBundleTypeInfo;
+
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), getImplementationName());
+ dbFile = makeURL(dbFile, "backenddb.xml");
+ m_backendDb.reset(
+ new ExtensionBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+
+void BackendImpl::disposing()
+{
+ m_xRootRegistry.clear();
+ PackageRegistryBackend::disposing();
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.bundle.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence<OUString> BackendImpl::getSupportedServiceNames()
+{
+ return { OUString(BACKEND_SERVICE_NAME) };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ //Notify the backend responsible for processing the different media
+ //types that this extension was removed.
+ ExtensionBackendDb::Data data = readDataFromDb(url);
+ for (auto const& item : data.items)
+ {
+ m_xRootRegistry->packageRemoved(item.first, item.second);
+ }
+
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ if (ucbContent.isFolder())
+ {
+ //Every .oxt, uno.pkg file must contain a META-INF folder
+ ::ucbhelper::Content metaInfContent;
+ if (create_ucb_content(
+ &metaInfContent, makeURL( url, "META-INF" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.package-bundle";
+ }
+ //No support of legacy bundles, because every folder could be one.
+ }
+ else
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(".oxt") ||
+ title.endsWithIgnoreAsciiCase(".uno.pkg"))
+ mediaType = "application/vnd.sun.star.package-bundle";
+ else if (title.endsWithIgnoreAsciiCase(".zip"))
+ mediaType = "application/vnd.sun.star.legacy-package-bundle";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+
+ //In case a XPackage is created for a removed extension, we cannot
+ //obtain the name
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xBundleTypeInfo, false, bRemoved,
+ identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ExtensionBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ExtensionBackendDb::Data BackendImpl::readDataFromDb(
+ OUString const & url)
+{
+ ExtensionBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_url_expanded( expandUnoRcUrl( url ) ),
+ m_legacyBundle( legacyBundle ),
+ m_pBundle( nullptr )
+{
+ if (bRemoved)
+ m_dbData = getMyBackend()->readDataFromDb(url);
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+void BackendImpl::PackageImpl::disposing()
+{
+ sal_Int32 len = m_bundle.getLength();
+ Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ try_dispose( p[ pos ] );
+ m_bundle.realloc( 0 );
+
+ Package::disposing();
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ //In case the object was created for a removed extension (m_bRemoved = true)
+ //but the extension is not registered, then bundle will be empty. Then
+ //the return value will be Optional<...>.IsPresent= false. Although this is
+ //not true, this does not matter. Then registerPackage or revokePackage
+ //would never be called for the items. But since the extension is removed
+ //and not registered anyway, this does not matter.
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel.get(), xCmdEnv ) );
+
+ bool reg = false;
+ bool present = false;
+ bool ambig = false;
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
+
+ //present = true if at least one bundle item has this value.
+ //reg = true if all bundle items have an option value (option.IsPresent == 1)
+ //and all have value of true (option.Value.Value == true)
+ //If not, then the bundle has the status of not registered and ambiguous.
+ if (option.IsPresent)
+ {
+ beans::Ambiguous<sal_Bool> const & status = option.Value;
+ if (present)
+ {
+ //we never come here in the first iteration
+ if (reg != bool(status.Value)) {
+
+ ambig = true;
+ reg = false;
+ break;
+ }
+ }
+ else
+ {
+ //we always come here in the first iteration
+ reg = status.Value;
+ present = true;
+ }
+ }
+ }
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ present, beans::Ambiguous<sal_Bool>(reg, ambig) );
+}
+
+OUString BackendImpl::PackageImpl::getTextFromURL(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl)
+{
+ try
+ {
+ ::ucbhelper::Content descContent(
+ licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
+ std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
+ return OUString( reinterpret_cast<char const *>(
+ seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
+ }
+ catch (const css::uno::Exception&)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Could not read file " + licenseUrl, nullptr, exc);
+ }
+
+}
+
+DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() const
+{
+ return dp_misc::getDescriptionInfoset(m_url_expanded);
+}
+
+bool BackendImpl::PackageImpl::checkPlatform(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
+{
+ bool ret = false;
+ DescriptionInfoset info(getDescriptionInfoset());
+ Sequence<OUString> platforms(info.getSupportedPlatforms());
+ if (hasValidPlatform(platforms))
+ {
+ ret = true;
+ }
+ else
+ {
+ ret = false;
+ OUString msg(
+ "unsupported platform");
+ Any e(
+ css::deployment::PlatformException(
+ msg, static_cast<OWeakObject *>(this), this));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ }
+ return ret;
+}
+
+
+bool BackendImpl::PackageImpl::checkDependencies(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
+ DescriptionInfoset const & description)
+{
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(dp_misc::Dependencies::check(description));
+
+ if (!unsatisfied.hasElements()) {
+ return true;
+ } else {
+ OUString msg(
+ "unsatisfied dependencies");
+ Any e(
+ css::deployment::DependencyException(
+ msg, static_cast<OWeakObject *>(this), unsatisfied));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ return false;
+ }
+}
+
+bool BackendImpl::PackageImpl::checkLicense(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & info, bool alreadyInstalled)
+{
+ try
+ {
+ ::std::optional<SimpleLicenseAttributes> simplLicAttr
+ = info.getSimpleLicenseAttributes();
+ if (! simplLicAttr)
+ return true;
+ OUString sLic = info.getLocalizedLicenseURL();
+ //If we do not get a localized licence then there is an error in the description.xml
+ //This should be handled by using a validating parser. Therefore we assume that no
+ //license is available.
+ if (sLic.isEmpty())
+ throw css::deployment::DeploymentException(
+ "Could not obtain path to license. Possible error in description.xml", nullptr, Any());
+ OUString sHref = m_url_expanded + "/" + sLic;
+ OUString sLicense = getTextFromURL(xCmdEnv, sHref);
+ ////determine who has to agree to the license
+ //check correct value for attribute
+ if ( ! (simplLicAttr->acceptBy == "user" || simplLicAttr->acceptBy == "admin"))
+ throw css::deployment::DeploymentException(
+ "Could not obtain attribute simple-license@accept-by or it has no valid value", nullptr, Any());
+
+
+ //Only use interaction if there is no version of this extension already installed
+ //and the suppress-on-update flag is not set for the new extension
+ // alreadyInstalled | bSuppressOnUpdate | show license
+
+ // 0 | 0 | 1
+ // 0 | 1 | 1
+ // 1 | 0 | 1
+ // 1 | 1 | 0
+
+ if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
+ {
+ css::deployment::LicenseException licExc(
+ OUString(), nullptr, getDisplayName(), sLicense,
+ simplLicAttr->acceptBy);
+ bool approve = false;
+ bool abort = false;
+ if (! interactContinuation(
+ Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
+ throw css::deployment::DeploymentException(
+ "Could not interact with user.", nullptr, Any());
+
+ return approve;
+ }
+ return true;
+ } catch (const css::ucb::CommandFailedException&) {
+ throw;
+ } catch (const css::ucb::CommandAbortedException&) {
+ throw;
+ } catch (const css::deployment::DeploymentException&) {
+ throw;
+ } catch (const css::uno::RuntimeException&) {
+ throw;
+ } catch (const css::uno::Exception&) {
+ Any anyExc = cppu::getCaughtException();
+ throw css::deployment::DeploymentException("Unexpected exception", nullptr, anyExc);
+ }
+}
+
+::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >&,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool alreadyInstalled)
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return 0;
+
+ //always return LICENSE as long as the user did not accept the license
+ //so that XExtensonManager::checkPrerequisitesAndEnable will again
+ //check the license
+ if (!checkPlatform(xCmdEnv))
+ return deployment::Prerequisites::PLATFORM |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkDependencies(xCmdEnv, info))
+ return deployment::Prerequisites::DEPENDENCIES |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
+ return deployment::Prerequisites::LICENSE;
+ else
+ return 0;
+}
+
+sal_Bool BackendImpl::PackageImpl::checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return true;
+
+ return checkDependencies(xCmdEnv, info);
+}
+
+beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
+{
+ OUString identifier;
+ if (m_bRemoved)
+ identifier = m_identifier;
+ else
+ identifier = dp_misc::generateIdentifier(
+ getDescriptionInfoset().getIdentifier(), m_name);
+
+ return beans::Optional<OUString>(
+ true, identifier);
+}
+
+OUString BackendImpl::PackageImpl::getVersion()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getVersion();
+}
+
+Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getUpdateInformationUrls();
+}
+
+beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
+ beans::StringPair aStrPair( aInfo.first, aInfo.second );
+ return aStrPair;
+}
+
+
+uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ uno::Reference< graphic::XGraphic > xGraphic;
+
+ OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
+ if ( !aIconURL.isEmpty() )
+ {
+ OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
+
+ uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
+ uno::Reference< graphic::XGraphicProvider > xGraphProvider( graphic::GraphicProvider::create(xContext) );
+
+ uno::Sequence< beans::PropertyValue > aMediaProps( 1 );
+ aMediaProps[0].Name = "URL";
+ aMediaProps[0].Value <<= aFullIconURL;
+
+ xGraphic = xGraphProvider->queryGraphic( aMediaProps );
+ }
+
+ return xGraphic;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel.get(), xCmdEnv ) );
+
+ if (doRegisterPackage)
+ {
+ ExtensionBackendDb::Data data;
+ const sal_Int32 len = bundle.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ //We even try a rollback if the user cancelled the action (CommandAbortedException)
+ //in order to prevent invalid database entries.
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item registration error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow;
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ if (approve && !abort) // ignore error, just continue
+ continue;
+
+ {
+ ProgressLevel progress( xCmdEnv, "rollback..." );
+ // try rollback
+ for ( ; pos--; )
+ {
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ // ignore any errors of rollback
+ }
+ }
+ progress.update( "rollback finished." );
+ }
+
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc) {
+ throw ucb::CommandFailedException(
+ dpExc.Message, dpExc.Context, dpExc.Cause );
+ }
+ else {
+ // rethrow CommandFailedException
+ ::cppu::throwException(exc);
+ }
+ }
+ data.items.emplace_back(xPackage->getURL(),
+ xPackage->getPackageType()->getMediaType());
+ }
+ getMyBackend()->addDataToDb(getURL(), data);
+ }
+ else
+ {
+ // revoke in reverse order:
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ // CommandFailedException, DeploymentException:
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item revocation error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ // ignore errors when revoking, although abort may have been
+ // selected
+ }
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
+ OUString sDescription;
+ if (!sRelativeURL.isEmpty())
+ {
+ OUString sURL = m_url_expanded + "/" + sRelativeURL;
+
+ try
+ {
+ sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+
+ if (!sDescription.isEmpty())
+ return sDescription;
+ return m_oldDescription;
+}
+
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sLicense;
+ DescriptionInfoset aInfo = getDescriptionInfoset();
+
+ ::std::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
+ if ( aSimplLicAttr )
+ {
+ OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
+
+ if ( !aLicenseURL.isEmpty() )
+ {
+ OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
+ sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
+ }
+ }
+
+ return sLicense;
+}
+
+
+void BackendImpl::PackageImpl::exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::ucbhelper::Content sourceContent(
+ m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
+ OUString title(newTitle);
+ if (title.isEmpty())
+ sourceContent.getPropertyValue( "Title" ) >>= title;
+ OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+
+ if (nameClashAction == ucb::NameClash::ASK)
+ {
+ if (create_ucb_content(
+ nullptr, destURL, xCmdEnv, false /* no throw */ )) {
+ bool replace = false, abort = false;
+ if (! interactContinuation(
+ Any( ucb::NameClashResolveRequest(
+ "file already exists: " + title,
+ static_cast<OWeakObject *>(this),
+ task::InteractionClassification_QUERY,
+ destFolderURL, title, OUString() ) ),
+ cppu::UnoType<ucb::XInteractionReplaceExistingData>::get(), xCmdEnv,
+ &replace, &abort ) || !replace) {
+ return;
+ }
+ }
+ }
+ else if (nameClashAction != ucb::NameClash::OVERWRITE) {
+ throw ucb::CommandFailedException("unsupported nameClashAction!",
+ static_cast<OWeakObject *>(this), Any() );
+ }
+ erase_path( destURL, xCmdEnv );
+
+ OUStringBuffer buf;
+ buf.append( "vnd.sun.star.zip://" );
+ buf.append( ::rtl::Uri::encode( destURL,
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ buf.append( '/' );
+ OUString destFolder( buf.makeStringAndClear() );
+
+ ::ucbhelper::Content destFolderContent(
+ destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
+ {
+ // transfer every item of folder into zip:
+ Reference<sdbc::XResultSet> xResultSet(
+ sourceContent.createCursor( Sequence<OUString>() ) );
+ ProgressLevel progress( xCmdEnv, OUString() );
+ while (xResultSet->next())
+ {
+ ::ucbhelper::Content subContent(
+ Reference<ucb::XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ destFolderContent.transferContent(
+ subContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ progress.update( Any() ); // animating progress bar
+ }
+ }
+
+ // assure META-INF folder:
+ ::ucbhelper::Content metainfFolderContent;
+ create_folder( &metainfFolderContent,
+ makeURL( destFolderContent.getURL(), "META-INF" ),
+ xCmdEnv );
+
+ if (m_legacyBundle)
+ {
+ // easy to migrate legacy bundles to new format:
+ // just export them once using a .oxt name!
+ // set detected media-types of any bundle item:
+
+ // collect all manifest entries:
+ Sequence< Reference<deployment::XPackage> > bundle;
+ try {
+ bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ // xxx todo: think about exception specs:
+ catch (const deployment::DeploymentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+
+ std::vector< Sequence<beans::PropertyValue> > manifest;
+ manifest.reserve( bundle.getLength() );
+ sal_Int32 baseURLlen = m_url_expanded.getLength();
+ Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
+ const OUString strMediaType( "MediaType" );
+ const OUString strFullPath( "FullPath" );
+ const OUString strIsFolder( "IsFolder" );
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
+ OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
+ OSL_ASSERT( url_.getLength() >= baseURLlen );
+ OUString fullPath;
+ if (url_.getLength() > baseURLlen)
+ fullPath = url_.copy( baseURLlen + 1 );
+ ::ucbhelper::Content ucbContent(
+ url_, xCmdEnv, getMyBackend()->getComponentContext() );
+ if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
+ fullPath += "/";
+ Sequence<beans::PropertyValue> attribs( 2 );
+ beans::PropertyValue * pattribs = attribs.getArray();
+ pattribs[ 0 ].Name = strFullPath;
+ pattribs[ 0 ].Value <<= fullPath;
+ pattribs[ 1 ].Name = strMediaType;
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OUString mediaType;
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ else
+ mediaType = "unknown";
+ pattribs[ 1 ].Value <<= mediaType;
+ manifest.push_back( attribs );
+ }
+
+ // write into pipe:
+ Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestWriter> xManifestWriter =
+ packages::manifest::ManifestWriter::create( xContext );
+ Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
+ xManifestWriter->writeManifestSequence(
+ xPipe, comphelper::containerToSequence(manifest) );
+
+ // write buffered pipe data to content:
+ ::ucbhelper::Content manifestContent(
+ makeURL( metainfFolderContent.getURL(), "manifest.xml" ),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ manifestContent.writeStream(
+ Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
+ true /* replace existing */ );
+ }
+ else
+ {
+ bool bSuccess = false;
+ try
+ {
+ // overwrite manifest.xml:
+ ::ucbhelper::Content manifestContent;
+ if ( ! create_ucb_content(
+ &manifestContent,
+ makeURL( m_url_expanded, "META-INF/manifest.xml" ),
+ xCmdEnv, false ) )
+ {
+ OSL_FAIL( "### missing META-INF/manifest.xml file!" );
+ return;
+ }
+
+ metainfFolderContent.transferContent(
+ manifestContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ bSuccess = true;
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
+ }
+
+ if (!bSuccess)
+ throw RuntimeException( "UCB transferContent() failed!",
+ static_cast<OWeakObject *>(this) );
+ }
+
+ // xxx todo: maybe obsolete in the future
+ try {
+ destFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const ucb::UnsupportedCommandException &) {
+ }
+}
+
+
+sal_Bool BackendImpl::PackageImpl::isBundle()
+{
+ return true;
+}
+
+
+Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
+ if (pBundle == nullptr)
+ {
+ t_packagevec bundle;
+ if (m_bRemoved)
+ {
+ bundle = getPackagesFromDb(xCmdEnv);
+ }
+ else
+ {
+ try {
+ if (m_legacyBundle)
+ {
+ // .zip legacy packages allow script.xlb, dialog.xlb in bundle
+ // root folder:
+ OUString mediaType;
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "script.xlb" ),
+ xCmdEnv, false /* no throw */ )) {
+ mediaType = "application/vnd.sun.star.basic-library";
+ }
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+
+ if (!mediaType.isEmpty()) {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( getURL(), mediaType, false, OUString(),
+ xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ // continue scanning:
+ }
+ scanLegacyBundle( bundle, getURL(),
+ AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+ else
+ {
+ // .oxt:
+ scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "error scanning bundle: " + getURL(),
+ static_cast<OWeakObject *>(this), exc );
+ }
+ }
+
+ // sort: schema before config data, typelibs before components:
+ Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
+ Reference<deployment::XPackage> * pret = ret.getArray();
+ sal_Int32 lower_end = 0;
+ sal_Int32 upper_end = ret.getLength();
+ for (auto const& elem : bundle)
+ {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ elem->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ const OUString mediaType( xPackageType->getMediaType() );
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
+ type.equalsIgnoreAsciiCase("application") &&
+ (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
+ {
+ --upper_end;
+ pret[ upper_end ] = elem;
+ continue;
+ }
+ }
+ pret[ lower_end ] = elem;
+ ++lower_end;
+ }
+ OSL_ASSERT( lower_end == upper_end );
+
+ const ::osl::MutexGuard guard( getMutex() );
+ pBundle = m_pBundle;
+ if (pBundle == nullptr) {
+ m_bundle = ret;
+ pBundle = &m_bundle;
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_pBundle = pBundle;
+ }
+ }
+ else {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *pBundle;
+}
+
+bool isBundle_( OUString const & mediaType )
+{
+ // xxx todo: additional parsing?
+ return !mediaType.isEmpty() &&
+ (mediaType.matchIgnoreAsciiCase( "application/vnd.sun.star.package-bundle") ||
+ mediaType.matchIgnoreAsciiCase( "application/vnd.sun.star.legacy-package-bundle"));
+}
+
+
+Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError )
+{
+ // ignore any nested bundles:
+ if (isBundle_(mediaType))
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage>xPackage;
+ try {
+ try {
+ xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
+ url, mediaType, bRemoved, identifier, xCmdEnv ) );
+ OSL_ASSERT( xPackage.is() );
+ } catch (css::lang::IllegalArgumentException & e) {
+ css::uno::Any exc(cppu::getCaughtException());
+ throw css::lang::WrappedTargetException(
+ "wrapped: " + e.Message, e.Context, exc);
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ // ignore already handled error
+ }
+ catch (const Exception &) {
+ const Any exc( ::cppu::getCaughtException() );
+ if (notifyDetectionError ||
+ !exc.isExtractableTo( cppu::UnoType<lang::IllegalArgumentException>::get()) )
+ {
+ (void)interactContinuation(
+ Any( lang::WrappedTargetException("bundle item error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
+ }
+ }
+
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ // ignore any nested bundles:
+ if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
+ xPackage.clear();
+ }
+ return xPackage;
+}
+
+
+void BackendImpl::PackageImpl::scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OSL_ASSERT( !m_legacyBundle );
+
+ OUString mfUrl( makeURL( m_url_expanded, "META-INF/manifest.xml" ) );
+ ::ucbhelper::Content manifestContent;
+ if (! create_ucb_content(
+ &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
+ {
+ SAL_WARN(
+ "desktop.deployment",
+ "cannot create UCB Content for <" << mfUrl << ">" );
+ return;
+ }
+
+
+ const LanguageTag& officeLocale = getOfficeLanguageTag();
+ const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
+ const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
+ size_t descrPenalty = nPenaltyMax;
+ OUString descrFile;
+
+ const Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestReader> xManifestReader =
+ packages::manifest::ManifestReader::create( xContext );
+ const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
+ xManifestReader->readManifestSequence( manifestContent.openStream() ) );
+ const OUString packageRootURL( getURL() );
+ for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
+ {
+ OUString fullPath, mediaType;
+ Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
+ for ( sal_Int32 i = attribs.getLength(); i--; )
+ {
+ if (!(fullPath.isEmpty() || mediaType.isEmpty()))
+ break;
+ if ( attribs[i].Name == "FullPath" )
+ attribs[i].Value >>= fullPath;
+ else if ( attribs[i].Name == "MediaType" )
+ attribs[i].Value >>= mediaType;
+ }
+
+ if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
+ continue;
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (! INetContentTypes::parse( mediaType, type, subType, &params ))
+ continue;
+
+ {
+ auto const iter = params.find("platform");
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ continue;
+ }
+ const OUString url( makeURL( packageRootURL, fullPath ) );
+
+ // check for bundle description:
+ if (type.equalsIgnoreAsciiCase("application") &&
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
+ {
+ // check locale:
+ auto const iter = params.find("locale");
+ if (iter == params.end())
+ {
+ if (descrFile.isEmpty())
+ descrFile = url;
+ }
+ else {
+ // match best locale:
+ LanguageTag descrTag(iter->second.m_sValue);
+ if (officeLocale.getLanguage() == descrTag.getLanguage())
+ {
+ size_t nPenalty = nPenaltyMax;
+ const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
+ for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
+ {
+ for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
+ {
+ if (officeFallbacks[o] == descrFallbacks[d])
+ {
+ // The last fallbacks are always language-only
+ // fallbacks, so we _will_ have _some_ match if
+ // we ever entered the overall if() condition.
+ nPenalty = o * 1000 + d;
+ if (descrPenalty > nPenalty)
+ {
+ descrPenalty = nPenalty;
+ descrFile = url;
+ }
+ }
+ }
+ }
+ }
+ // TODO: we could break here if descrPenalty==0 for an exact
+ // match of officeLocale, but the previous code didn't; are
+ // there side effects?
+ }
+ continue;
+ }
+
+ checkAborted( abortChannel );
+
+ //We make sure that we only create one XPackage for a particular URL.
+ //Sometime programmers insert the same URL several times in the manifest
+ //which may lead to DisposedExceptions.
+ if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
+ {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ }
+ else
+ {
+ SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
+ }
+ }
+
+ if (descrFile.isEmpty())
+ return;
+
+ ::ucbhelper::Content descrFileContent;
+ if (!create_ucb_content( &descrFileContent, descrFile,
+ xCmdEnv, false /* no throw */ ))
+ return;
+
+ // patch description:
+ std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
+ OUStringBuffer buf;
+ if ( !bytes.empty() )
+ {
+ buf.append( OUString( reinterpret_cast<char const *>(
+ bytes.data() ),
+ bytes.size(), RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ buf.append( Package::getDescription() );
+ }
+ m_oldDescription = buf.makeStringAndClear();
+}
+
+
+void BackendImpl::PackageImpl::scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration )
+{
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getMyBackend()->getComponentContext() );
+
+ // check for platform paths:
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
+ !platform_fits( title.copy( 0, title.getLength() - 4 ) )) {
+ return;
+ }
+ if (title.endsWithIgnoreAsciiCase("skip_registration") )
+ skip_registration = true;
+
+ Sequence<OUString> ar { OUString("Title"), OUString("IsFolder") };
+ Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
+ while (xResultSet->next())
+ {
+ checkAborted( abortChannel );
+
+ const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ const OUString title_enc( ::rtl::Uri::encode(
+ xRow->getString( 1 /* Title */ ),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ const OUString path( makeURL( url, title_enc ) );
+
+ OUString mediaType;
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( path, OUString() /* detect */, false, OUString(),
+ xCmdEnv, false /* ignore detection errors */ ) );
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+
+ if (skip_registration &&
+ // xxx todo: additional parsing?
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
+ continue;
+
+ bundle.push_back( xPackage );
+ }
+
+ if (mediaType.isEmpty() ||
+ // script.xlb, dialog.xlb can be met everywhere:
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
+ {
+ if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
+ scanLegacyBundle(
+ bundle, path, abortChannel, xCmdEnv, skip_registration );
+ }
+ }
+ }
+}
+
+OUString BackendImpl::PackageImpl::getDisplayName()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
+ if (sName.isEmpty())
+ return m_displayName;
+ else
+ return sName;
+}
+
+std::vector<Reference<deployment::XPackage> >
+BackendImpl::PackageImpl::getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > retVector;
+
+ for (auto const& item : m_dbData.items)
+ {
+ Reference<deployment::XPackage> xExtension =
+ bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
+ OSL_ASSERT(xExtension.is());
+ if (xExtension.is())
+ retVector.push_back(xExtension);
+ }
+
+ return retVector;
+}
+
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ Reference<deployment::XPackageRegistry> const & xRootRegistry,
+ OUString const & context, OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
+ args[ 0 ] <<= context;
+ if (!cachePath.isEmpty()) {
+ args[ 1 ] <<= cachePath;
+ args[ 2 ] <<= false; // readOnly
+ }
+ return new BackendImpl( args, xComponentContext, xRootRegistry );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.cxx b/desktop/source/deployment/registry/script/dp_lib_container.cxx
new file mode 100644
index 000000000..3a6f30253
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <strings.hrc>
+#include <dp_resource.h>
+#include <dp_shared.hxx>
+#include <dp_xml.h>
+#include "dp_lib_container.h"
+
+#include <rtl/ustring.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xmllib_imexp.hxx>
+
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::script {
+
+namespace {
+ OUString StrCannotDetermineLibName() { return DpResId(RID_STR_CANNOT_DETERMINE_LIBNAME); }
+}
+
+OUString LibraryContainer::get_libname(
+ OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ Reference<XComponentContext> const & xContext )
+{
+ ::xmlscript::LibDescriptor import;
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ xml_parse( ::xmlscript::importLibrary( import ), ucb_content, xContext );
+
+ if (import.aName.isEmpty()) {
+ throw Exception( StrCannotDetermineLibName(),
+ Reference<XInterface>() );
+ }
+ return import.aName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.h b/desktop/source/deployment/registry/script/dp_lib_container.h
new file mode 100644
index 000000000..00e9ab348
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SCRIPT_DP_LIB_CONTAINER_H
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SCRIPT_DP_LIB_CONTAINER_H
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace ucb {
+ class XCommandEnvironment;
+ }
+}
+
+
+namespace dp_registry {
+namespace backend {
+namespace script {
+
+
+class LibraryContainer
+{
+public:
+ static OUString get_libname(
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+};
+
+}
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_script.cxx b/desktop/source/deployment/registry/script/dp_script.cxx
new file mode 100644
index 000000000..39de39f2b
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_script.cxx
@@ -0,0 +1,459 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_services.hxx>
+#include "dp_lib_container.h"
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/script/XLibraryContainer3.hpp>
+#include <memory>
+#include "dp_scriptbackenddb.hxx"
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::script {
+namespace {
+
+typedef ::cppu::ImplInheritanceHelper<
+ ::dp_registry::backend::PackageRegistryBackend, util::XUpdatable > t_helper;
+
+class BackendImpl : public t_helper
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_scriptURL;
+ const OUString m_dialogURL;
+ OUString m_dialogName;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(OUString const & url);
+ void revokeEntryFromDb(OUString const & url);
+
+ const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<ScriptBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend.get(), url,
+ OUString(), OUString(), // will be late-initialized
+ !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
+ : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
+ m_scriptURL( scriptURL ),
+ m_dialogURL( dialogURL )
+{
+ // name, displayName:
+ if (!dialogURL.isEmpty()) {
+ m_dialogName = LibraryContainer::get_libname(
+ dialogURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ if (!scriptURL.isEmpty()) {
+ assert(m_name.pData);
+ m_name = LibraryContainer::get_libname(
+ scriptURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ else
+ m_name = m_dialogName;
+ m_displayName = m_name;
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : t_helper( args, xComponentContext ),
+ m_xBasicLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.basic-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_BASIC_LIB)
+ ) ),
+ m_xDialogLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.dialog-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_DIALOG_LIB)
+ ) ),
+ m_typeInfos( 2 )
+{
+ m_typeInfos[ 0 ] = m_xBasicLibTypeInfo;
+ m_typeInfos[ 1 ] = m_xDialogLibTypeInfo;
+
+ OSL_ASSERT( ! transientMode() );
+
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ScriptBackendDb(getComponentContext(), dbFile));
+ }
+
+}
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(OUString const & url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+// XUpdatable
+
+void BackendImpl::update()
+{
+ // Nothing to do here after fixing i70283!?
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::revokeEntryFromDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "script.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.basic-library";
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( url, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString dialogURL( makeURL( url, "dialog.xlb" ) );
+ if (! create_ucb_content(
+ nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
+ dialogURL.clear();
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
+ {
+ OUString scriptURL( makeURL( url, "script.xlb"));
+ if (! create_ucb_content(
+ nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
+ scriptURL.clear();
+ }
+
+ return new PackageImpl(
+ this, url, xCmdEnv, scriptURL,
+ dialogURL, bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase(
+ "vnd.sun.star.dialog-library")) {
+ return new PackageImpl(
+ this, url, xCmdEnv,
+ OUString() /* no script lib */,
+ dialogURL,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+ Reference< deployment::XPackage > xThisPackage( this );
+
+ bool registered = that->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
+}
+
+void
+lcl_maybeRemoveScript(
+ bool const bExists,
+ OUString const& rName,
+ OUString const& rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
+ {
+ const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ if (sScriptUrl == rScriptURL)
+ xScriptLibs->removeLibrary(rName);
+ }
+}
+
+bool
+lcl_maybeAddScript(
+ bool const bExists,
+ OUString const& rName,
+ OUString const& rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (bExists && xScriptLibs.is())
+ {
+ bool bCanAdd = true;
+ if (xScriptLibs->hasByName(rName))
+ {
+ const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ //We assume here that library names in extensions are unique, which may not be the case
+ //ToDo: If the script exist in another extension, then both extensions must have the
+ //same id
+ if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
+ || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
+ {
+ xScriptLibs->removeLibrary(rName);
+ bCanAdd = true;
+ }
+ else
+ {
+ bCanAdd = false;
+ }
+ }
+
+ if (bCanAdd)
+ {
+ xScriptLibs->createLibraryLink(rName, rScriptURL, false);
+ return xScriptLibs->hasByName(rName);
+ }
+ }
+
+ return false;
+}
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+
+ Reference< deployment::XPackage > xThisPackage( this );
+ Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
+
+ bool bScript = !m_scriptURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xScriptLibs;
+
+ bool bDialog = !m_dialogURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xDialogLibs;
+
+ bool bRunning = !startup && office_is_running();
+ if( bRunning )
+ {
+ if( bScript )
+ {
+ xScriptLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationScriptLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+
+ if( bDialog )
+ {
+ xDialogLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationDialogLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+ }
+ bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
+ if( !doRegisterPackage )
+ {
+ //We cannot just call removeLibrary(name) because this could remove a
+ //script which was added by an extension in a different repository. For
+ //example, extension foo is contained in the bundled repository and then
+ //the user adds it to the user repository. The extension manager will
+ //then register the new script and revoke the script from the bundled
+ //extension. removeLibrary(name) would now remove the script from the
+ //user repository. That is, the script of the newly added user extension does
+ //not work anymore. Therefore we must check if the currently active
+ //script comes in fact from the currently processed extension.
+
+ if (bRegistered)
+ {
+ //we also prevent and live deployment at startup
+ if (!isRemoved() && !startup)
+ {
+ lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ return;
+ }
+ }
+ if (bRegistered)
+ return; // Already registered
+
+ // Update LibraryContainer
+ bool bScriptSuccess = false;
+ bool bDialogSuccess = false;
+ if (!startup)
+ {
+ //If there is a bundled extension, and the user installs the same extension
+ //then the script from the bundled extension must be removed. If this does not work
+ //then live deployment does not work for scripts.
+ bScriptSuccess = lcl_maybeAddScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ bDialogSuccess = lcl_maybeAddScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ bool bSuccess = bScript || bDialog; // Something must have happened
+ if( bRunning )
+ if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
+ bSuccess = false;
+
+ if (bSuccess)
+ getMyBackend()->addDataToDb(getURL());
+}
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ "com.sun.star.comp.deployment.script.PackageRegistryBackend",
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry::backend::script
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
new file mode 100644
index 000000000..a669f1b58
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_scriptbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+#define EXTENSION_REG_NS "http://openoffice.org/extensionmanager/script-registry/2010"
+#define NS_PREFIX "script"
+#define ROOT_ELEMENT_NAME "script-backend-db"
+#define KEY_ELEMENT_NAME "script"
+
+namespace dp_registry::backend::script {
+
+ScriptBackendDb::ScriptBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ScriptBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ScriptBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ScriptBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ScriptBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+} // namespace dp_registry::backend::script
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
new file mode 100644
index 000000000..61e993c90
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SCRIPT_DP_SCRIPTBACKENDDB_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SCRIPT_DP_SCRIPTBACKENDDB_HXX
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+#include <optional>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry {
+namespace backend {
+namespace script {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ScriptBackendDb: public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+
+public:
+
+ ScriptBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+};
+
+
+}
+}
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
new file mode 100644
index 000000000..530924a07
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dp_misc.h>
+#include "dp_parceldesc.hxx"
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+
+// XDocumentHandler
+void SAL_CALL
+ParcelDescDocHandler::startDocument()
+{
+ m_bIsParsed = false;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::endDocument()
+{
+ m_bIsParsed = true;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::characters( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::ignorableWhitespace( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::processingInstruction(
+ const OUString &, const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::setDocumentLocator(
+ const Reference< xml::sax::XLocator >& )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::startElement( const OUString& aName,
+ const Reference< xml::sax::XAttributeList > & xAttribs )
+{
+
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() for " +
+ aName + "\n");
+ if ( !skipIndex )
+ {
+ if ( aName == "parcel" )
+ {
+ m_sLang = xAttribs->getValueByName( "language" );
+ }
+ ++skipIndex;
+ }
+ else
+ {
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() skipping for "
+ + aName + "\n");
+ }
+
+}
+
+void SAL_CALL ParcelDescDocHandler::endElement( const OUString & aName )
+{
+ if ( skipIndex )
+ {
+ --skipIndex;
+ dp_misc::TRACE("ParcelDescDocHandler::endElement() skipping for "
+ + aName + "\n");
+ }
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
new file mode 100644
index 000000000..0d09f6288
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SFWK_DP_PARCELDESC_HXX
+#define INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SFWK_DP_PARCELDESC_HXX
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/xml/sax/XAttributeList.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+namespace dp_registry
+{
+namespace backend
+{
+namespace sfwk
+{
+
+class ParcelDescDocHandler : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
+{
+private:
+ bool m_bIsParsed;
+ OUString m_sLang;
+ sal_Int32 skipIndex;
+public:
+ ParcelDescDocHandler():m_bIsParsed( false ), skipIndex( 0 ){}
+ const OUString& getParcelLanguage() const { return m_sLang; }
+ bool isParsed() const { return m_bIsParsed; }
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void SAL_CALL startElement( const OUString& aName,
+ const css::uno::Reference< css::xml::sax::XAttributeList > & xAttribs ) override;
+
+ virtual void SAL_CALL endElement( const OUString & aName ) override;
+
+ virtual void SAL_CALL characters( const OUString & aChars ) override;
+
+ virtual void SAL_CALL ignorableWhitespace( const OUString & aWhitespaces ) override;
+
+ virtual void SAL_CALL processingInstruction(
+ const OUString & aTarget, const OUString & aData ) override;
+
+ virtual void SAL_CALL setDocumentLocator(
+ const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+};
+}
+}
+}
+
+#endif // INCLUDED_DESKTOP_SOURCE_DEPLOYMENT_REGISTRY_SFWK_DP_PARCELDESC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
new file mode 100644
index 000000000..58c72b6ca
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <dp_services.hxx>
+#include <strings.hrc>
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include "dp_parceldesc.hxx"
+#include <rtl/uri.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::script;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ Reference< container::XNameContainer > m_xNameCntrPkgHandler;
+ OUString m_descr;
+
+ void initPackageHandler();
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString const & libType, bool bRemoved,
+ OUString const & identifier);
+ // XPackage
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xTypeInfo;
+
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+};
+
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_descr.isEmpty())
+ return Package::getDescription();
+ else
+ return m_descr;
+}
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ return Package::getDescription();
+}
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString const & libType, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend.get(), url, OUString(), OUString(),
+ myBackend->m_xTypeInfo, bRemoved, identifier),
+ m_descr(libType)
+{
+ initPackageHandler();
+
+ sal_Int32 segmEnd = url.getLength();
+ if ( url.endsWith("/") )
+ --segmEnd;
+ sal_Int32 segmStart = url.lastIndexOf( '/', segmEnd ) + 1;
+ if (segmStart < 0)
+ segmStart = 0;
+ // name and display name default the same:
+ m_displayName = ::rtl::Uri::decode(
+ url.copy( segmStart, segmEnd - segmStart ),
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ m_name = m_displayName;
+
+ dp_misc::TRACE("PackageImpl displayName is " + m_displayName);
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.framework-script",
+ OUString() /* no file filter */,
+ "Scripting Framework Script Library"
+ ) )
+{
+}
+
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence< Reference<deployment::XPackageTypeInfo> >(&m_xTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & /*url*/, OUString const & /*mediaType*/)
+{
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for parcel-descriptor.xml:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "parcel-descriptor.xml" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.framework-script";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.framework-script"))
+ {
+ OUString lang = "Script";
+ OUString sParcelDescURL = makeURL(
+ url, "parcel-descriptor.xml" );
+
+ ::ucbhelper::Content ucb_content;
+
+ if (create_ucb_content( &ucb_content, sParcelDescURL,
+ xCmdEnv, false /* no throw */ ))
+ {
+ ParcelDescDocHandler* pHandler =
+ new ParcelDescDocHandler();
+ Reference< xml::sax::XDocumentHandler >
+ xDocHandler = pHandler;
+
+ Reference<XComponentContext>
+ xContext( getComponentContext() );
+
+ Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
+
+ xParser->setDocumentHandler( xDocHandler );
+ xml::sax::InputSource source;
+ source.aInputStream = ucb_content.openStream();
+ source.sSystemId = ucb_content.getURL();
+ xParser->parseStream( source );
+
+ if ( pHandler->isParsed() )
+ {
+ lang = pHandler->getParcelLanguage();
+ }
+ }
+
+ OUString sfwkLibType = DpResId( RID_STR_SFWK_LIB );
+ // replace %MACRONAME placeholder with language name
+ OUString MACRONAME( "%MACROLANG" );
+ sal_Int32 startOfReplace = sfwkLibType.indexOf( MACRONAME );
+ sal_Int32 charsToReplace = MACRONAME.getLength();
+ sfwkLibType = sfwkLibType.replaceAt( startOfReplace, charsToReplace, lang );
+ dp_misc::TRACE("******************************\n");
+ dp_misc::TRACE(" BackEnd detected lang = " + lang + "\n");
+ dp_misc::TRACE(" for url " + sParcelDescURL + "\n");
+ dp_misc::TRACE("******************************\n");
+ return new PackageImpl( this, url, sfwkLibType, bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::PackageImpl:: initPackageHandler()
+{
+ if (m_xNameCntrPkgHandler.is())
+ return;
+
+ BackendImpl * that = getMyBackend();
+ Any aContext;
+
+ if ( that->m_eContext == Context::User )
+ {
+ aContext <<= OUString("user");
+ }
+ else if ( that->m_eContext == Context::Shared )
+ {
+ aContext <<= OUString("share");
+ }
+ else if ( that->m_eContext == Context::Bundled )
+ {
+ aContext <<= OUString("bundled");
+ }
+ else
+ {
+ OSL_ASSERT( false );
+ // NOT supported at the moment // TODO
+ }
+
+ Reference< provider::XScriptProviderFactory > xFac =
+ provider::theMasterScriptProviderFactory::get( that->getComponentContext() );
+
+ Reference< container::XNameContainer > xName( xFac->createScriptProvider( aContext ), UNO_QUERY );
+ if ( xName.is() )
+ {
+ m_xNameCntrPkgHandler.set( xName );
+ }
+ // TODO what happens if above fails??
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_xNameCntrPkgHandler.is() && m_xNameCntrPkgHandler->hasByName(
+ m_url ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ if ( !m_xNameCntrPkgHandler.is() )
+ {
+ dp_misc::TRACE("no package handler!!!!\n");
+ throw RuntimeException( "No package Handler " );
+ }
+
+ if (doRegisterPackage)
+ {
+ // will throw if it fails
+ m_xNameCntrPkgHandler->insertByName( m_url, makeAny( Reference< XPackage >(this) ) );
+
+ }
+ else // revokePackage()
+ {
+ m_xNameCntrPkgHandler->removeByName( m_url );
+ }
+}
+
+namespace sdecl = comphelper::service_decl;
+sdecl::class_<BackendImpl, sdecl::with_args<true> > const serviceBI;
+sdecl::ServiceDecl const serviceDecl(
+ serviceBI,
+ "com.sun.star.comp.deployment.sfwk.PackageRegistryBackend",
+ BACKEND_SERVICE_NAME );
+
+} // namespace dp_registry::backend::sfwk
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/inc/helpids.h b/desktop/source/inc/helpids.h
new file mode 100644
index 000000000..6f6003812
--- /dev/null
+++ b/desktop/source/inc/helpids.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_HELPIDS_H
+#define INCLUDED_DESKTOP_HELPIDS_H
+
+#define HID_EXTENSION_MANAGER_LISTBOX_ENABLE "DESKTOP_HID_EXTENSION_MANAGER_LISTBOX_ENABLE"
+#define HID_EXTENSION_MANAGER_LISTBOX_DISABLE "DESKTOP_HID_EXTENSION_MANAGER_LISTBOX_DISABLE"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
new file mode 100644
index 000000000..717a542a9
--- /dev/null
+++ b/desktop/source/lib/init.cxx
@@ -0,0 +1,6189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config_features.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef IOS
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unicode/udata.h>
+#include <unicode/ucnv.h>
+#include <premac.h>
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#include <postmac.h>
+#endif
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#include <algorithm>
+#include <memory>
+#include <iostream>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <LibreOfficeKit/LibreOfficeKit.h>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <sal/log.hxx>
+#include <vcl/errinf.hxx>
+#include <vcl/lok.hxx>
+#include <o3tl/any.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/uri.hxx>
+#include <svl/zforlist.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/threadpool.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultEvent.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/reflection/XIdlClass.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/datatransfer/XTransferable2.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/document/XRedlinesSupplier.hpp>
+#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker.hpp>
+#include <com/sun/star/i18n/Calendar2.hpp>
+#include <com/sun/star/i18n/LocaleCalendar2.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <editeng/flstitem.hxx>
+#ifdef IOS
+#include <sfx2/app.hxx>
+#endif
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokcharthelper.hxx>
+#include <sfx2/DocumentSigner.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdview.hxx>
+#include <svx/svxids.hrc>
+#include <svx/ucsubset.hxx>
+#include <vcl/vclevent.hxx>
+#include <vcl/GestureEvent.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/resmgr.hxx>
+#include <tools/fract.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svtools/langtab.hxx>
+#include <vcl/floatwin.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/graphicfilter.hxx>
+#ifdef IOS
+#include <vcl/sysdata.hxx>
+#endif
+#include <vcl/virdev.hxx>
+#include <vcl/ImageTree.hxx>
+#include <vcl/ITiledRenderable.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <unicode/uchar.h>
+#include <unotools/syslocaleoptions.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/streamwrap.hxx>
+#include <osl/module.hxx>
+#include <comphelper/sequence.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <svl/undo.hxx>
+#include <unotools/datetime.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/abstdlg.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/uitest/uiobject.hxx>
+
+// Needed for getUndoManager()
+#include <com/sun/star/document/XUndoManager.hpp>
+#include <com/sun/star/document/XUndoManagerSupplier.hpp>
+#include <editeng/sizeitem.hxx>
+#include <svx/rulritem.hxx>
+#include <svx/pageitem.hxx>
+
+#include <app.hxx>
+
+#include "../app/cmdlineargs.hxx"
+// We also need to hackily be able to start the main libreoffice thread:
+#include "../app/sofficemain.h"
+#include "../app/officeipcthread.hxx"
+#include <lib/init.hxx>
+
+#include "lokinteractionhandler.hxx"
+#include "lokclipboard.hxx"
+#include <officecfg/Office/Impress.hxx>
+
+using namespace css;
+using namespace vcl;
+using namespace desktop;
+using namespace utl;
+
+static LibLibreOffice_Impl *gImpl = nullptr;
+static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
+static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
+
+static void SetLastExceptionMsg(const OUString& s = OUString())
+{
+ SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
+ if (gImpl)
+ gImpl->maLastExceptionMsg = s;
+}
+
+namespace {
+
+struct ExtensionMap
+{
+ const char *extn;
+ const char *filterName;
+};
+
+}
+
+static const ExtensionMap aWriterExtensionMap[] =
+{
+ { "doc", "MS Word 97" },
+ { "docm", "MS Word 2007 XML VBA" },
+ { "docx", "MS Word 2007 XML" },
+ { "fodt", "OpenDocument Text Flat XML" },
+ { "html", "HTML (StarWriter)" },
+ { "odt", "writer8" },
+ { "ott", "writer8_template" },
+ { "pdf", "writer_pdf_Export" },
+ { "epub", "EPUB" },
+ { "rtf", "Rich Text Format" },
+ { "txt", "Text" },
+ { "xhtml", "XHTML Writer File" },
+ { "png", "writer_png_Export" },
+ { nullptr, nullptr }
+};
+
+static const ExtensionMap aCalcExtensionMap[] =
+{
+ { "csv", "Text - txt - csv (StarCalc)" },
+ { "fods", "OpenDocument Spreadsheet Flat XML" },
+ { "html", "HTML (StarCalc)" },
+ { "ods", "calc8" },
+ { "ots", "calc8_template" },
+ { "pdf", "calc_pdf_Export" },
+ { "xhtml", "XHTML Calc File" },
+ { "xls", "MS Excel 97" },
+ { "xlsm", "Calc MS Excel 2007 VBA XML" },
+ { "xlsx", "Calc MS Excel 2007 XML" },
+ { "png", "calc_png_Export" },
+ { nullptr, nullptr }
+};
+
+static const ExtensionMap aImpressExtensionMap[] =
+{
+ { "fodp", "OpenDocument Presentation Flat XML" },
+ { "html", "impress_html_Export" },
+ { "odg", "impress8_draw" },
+ { "odp", "impress8" },
+ { "otp", "impress8_template" },
+ { "pdf", "impress_pdf_Export" },
+ { "potm", "Impress MS PowerPoint 2007 XML Template" },
+ { "pot", "MS PowerPoint 97 Vorlage" },
+ { "pptm", "Impress MS PowerPoint 2007 XML VBA" },
+ { "pptx", "Impress MS PowerPoint 2007 XML" },
+ { "pps", "MS PowerPoint 97 Autoplay" },
+ { "ppt", "MS PowerPoint 97" },
+ { "svg", "impress_svg_Export" },
+ { "xhtml", "XHTML Impress File" },
+ { "png", "impress_png_Export"},
+ { nullptr, nullptr }
+};
+
+static const ExtensionMap aDrawExtensionMap[] =
+{
+ { "fodg", "draw_ODG_FlatXML" },
+ { "html", "draw_html_Export" },
+ { "odg", "draw8" },
+ { "pdf", "draw_pdf_Export" },
+ { "svg", "draw_svg_Export" },
+ { "xhtml", "XHTML Draw File" },
+ { "png", "draw_png_Export"},
+ { nullptr, nullptr }
+};
+
+static OUString getUString(const char* pString)
+{
+ if (pString == nullptr)
+ return OUString();
+
+ OString sString(pString, strlen(pString));
+ return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
+}
+
+// Tolerate embedded \0s etc.
+static char *convertOString(const OString &rStr)
+{
+ char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1));
+ assert(pMemory); // don't tolerate failed allocations.
+ memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1);
+ return pMemory;
+}
+
+static char *convertOUString(const OUString &aStr)
+{
+ return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
+}
+
+/// Try to convert a relative URL to an absolute one, unless it already looks like a URL.
+static OUString getAbsoluteURL(const char* pURL)
+{
+ OUString aURL(getUString(pURL));
+ if (aURL.isEmpty())
+ return aURL;
+
+ // convert relative paths to absolute ones
+ OUString aWorkingDir;
+ osl_getProcessWorkingDir(&aWorkingDir.pData);
+ if (!aWorkingDir.endsWith("/"))
+ aWorkingDir += "/";
+
+ try
+ {
+ return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
+ }
+ catch (const rtl::MalformedUriException &)
+ {
+ }
+
+ return OUString();
+}
+
+static uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree)
+{
+ uno::Any aAny;
+ uno::Any aValue;
+ sal_Int32 nFields;
+ uno::TypeClass aTypeClass;
+ uno::Reference< reflection::XIdlField > aField;
+ boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField;
+ const std::string& rType = aTree.get<std::string>("type", "");
+ const std::string& rValue = aTree.get<std::string>("value", "");
+ uno::Sequence< uno::Reference< reflection::XIdlField > > aFields;
+ uno::Reference< reflection:: XIdlClass > xIdlClass =
+ css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())->forName(OUString::fromUtf8(rType.c_str()));
+ if (xIdlClass.is())
+ {
+ aTypeClass = xIdlClass->getTypeClass();
+ xIdlClass->createObject(aAny);
+ aFields = xIdlClass->getFields();
+ nFields = aFields.getLength();
+ aNodeValue = aTree.get_child("value", aNodeNull);
+ if (nFields > 0 && aNodeValue != aNodeNull)
+ {
+ for (sal_Int32 itField = 0; itField < nFields; ++itField)
+ {
+ aField = aFields[itField];
+ aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull);
+ if (aNodeField != aNodeNull)
+ {
+ aValue = jsonToUnoAny(aNodeField);
+ aField->set(aAny, aValue);
+ }
+ }
+ }
+ else if (!rValue.empty())
+ {
+ if (aTypeClass == uno::TypeClass_VOID)
+ aAny.clear();
+ else if (aTypeClass == uno::TypeClass_BYTE)
+ aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32());
+ else if (aTypeClass == uno::TypeClass_BOOLEAN)
+ aAny <<= OString(rValue.c_str()).toBoolean();
+ else if (aTypeClass == uno::TypeClass_SHORT)
+ aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32());
+ else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT)
+ aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32());
+ else if (aTypeClass == uno::TypeClass_LONG)
+ aAny <<= OString(rValue.c_str()).toInt32();
+ else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG)
+ aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32());
+ else if (aTypeClass == uno::TypeClass_FLOAT)
+ aAny <<= OString(rValue.c_str()).toFloat();
+ else if (aTypeClass == uno::TypeClass_DOUBLE)
+ aAny <<= OString(rValue.c_str()).toDouble();
+ else if (aTypeClass == uno::TypeClass_STRING)
+ aAny <<= OUString::fromUtf8(rValue.c_str());
+ }
+ }
+ return aAny;
+}
+
+std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
+{
+ std::vector<beans::PropertyValue> aArguments;
+ if (pJSON && pJSON[0] != '\0')
+ {
+ boost::property_tree::ptree aTree, aNodeNull, aNodeValue;
+ std::stringstream aStream(pJSON);
+ boost::property_tree::read_json(aStream, aTree);
+
+ for (const auto& rPair : aTree)
+ {
+ const std::string& rType = rPair.second.get<std::string>("type", "");
+ const std::string& rValue = rPair.second.get<std::string>("value", "");
+
+ beans::PropertyValue aValue;
+ aValue.Name = OUString::fromUtf8(rPair.first.c_str());
+ if (rType == "string")
+ aValue.Value <<= OUString::fromUtf8(rValue.c_str());
+ else if (rType == "boolean")
+ aValue.Value <<= OString(rValue.c_str()).toBoolean();
+ else if (rType == "float")
+ aValue.Value <<= OString(rValue.c_str()).toFloat();
+ else if (rType == "long")
+ aValue.Value <<= OString(rValue.c_str()).toInt32();
+ else if (rType == "short")
+ aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
+ else if (rType == "unsigned short")
+ aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
+ else if (rType == "int64")
+ aValue.Value <<= OString(rValue.c_str()).toInt64();
+ else if (rType == "int32")
+ aValue.Value <<= OString(rValue.c_str()).toInt32();
+ else if (rType == "int16")
+ aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
+ else if (rType == "uint64")
+ aValue.Value <<= OString(rValue.c_str()).toUInt64();
+ else if (rType == "uint32")
+ aValue.Value <<= OString(rValue.c_str()).toUInt32();
+ else if (rType == "uint16")
+ aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
+ else if (rType == "[]byte")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ if (aNodeValue != aNodeNull && aNodeValue.size() == 0)
+ {
+ uno::Sequence< sal_Int8 > aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), rValue.size());
+ aValue.Value <<= aSeqByte;
+ }
+ }
+ else if (rType == "[]any")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ if (aNodeValue != aNodeNull && !aNodeValue.empty())
+ {
+ sal_Int32 itSeq = 0;
+ uno::Sequence< uno::Any > aSeq(aNodeValue.size());
+ for (const auto& rSeqPair : aNodeValue)
+ aSeq[itSeq++] = jsonToUnoAny(rSeqPair.second);
+ aValue.Value <<= aSeq;
+ }
+ }
+ else
+ SAL_WARN("desktop.lib", "jsonToPropertyValuesVector: unhandled type '"<<rType<<"'");
+ aArguments.push_back(aValue);
+ }
+ }
+ return aArguments;
+}
+
+
+static StringMap jsonToStringMap(const char* pJSON)
+{
+ StringMap aArgs;
+ if (pJSON && pJSON[0] != '\0')
+ {
+ std::stringstream aStream(pJSON);
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ for (const auto& rPair : aTree)
+ {
+ aArgs[OUString::fromUtf8(rPair.first.c_str())] = OUString::fromUtf8(rPair.second.get_value<std::string>(".").c_str());
+ }
+ }
+ return aArgs;
+}
+
+
+static boost::property_tree::ptree unoAnyToPropertyTree(const uno::Any& anyItem)
+{
+ boost::property_tree::ptree aTree;
+ OUString aType = anyItem.getValueTypeName();
+ aTree.put("type", aType.toUtf8().getStr());
+
+ if (aType == "string")
+ aTree.put("value", anyItem.get<OUString>().toUtf8().getStr());
+ else if (aType == "unsigned long")
+ aTree.put("value", OString::number(anyItem.get<sal_uInt32>()).getStr());
+ else if (aType == "long")
+ aTree.put("value", OString::number(anyItem.get<sal_Int32>()).getStr());
+ else if (aType == "[]any")
+ {
+ uno::Sequence<uno::Any> aSeq;
+ if (anyItem >>= aSeq)
+ {
+ boost::property_tree::ptree aSubTree;
+
+ for (auto i = 0; i < aSeq.getLength(); ++i)
+ {
+ aSubTree.add_child(OString::number(i).getStr(), unoAnyToPropertyTree(aSeq[i]));
+ }
+ aTree.add_child("value", aSubTree);
+ }
+ }
+
+ // TODO: Add more as required
+
+ return aTree;
+}
+
+namespace desktop {
+
+RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
+{
+ RectangleAndPart aRet;
+ if (rPayload.compare(0, 5, "EMPTY") == 0) // payload starts with "EMPTY"
+ {
+ aRet.m_aRectangle = tools::Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips);
+ if (comphelper::LibreOfficeKit::isPartInInvalidation())
+ aRet.m_nPart = std::stol(rPayload.substr(6));
+
+ return aRet;
+ }
+
+ std::istringstream aStream(rPayload);
+ long nLeft, nTop, nWidth, nHeight;
+ long nPart = INT_MIN;
+ char nComma;
+ if (comphelper::LibreOfficeKit::isPartInInvalidation())
+ {
+ aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight >> nComma >> nPart;
+ }
+ else
+ {
+ aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
+ }
+
+ if (nWidth > 0 && nHeight > 0)
+ {
+ // The top-left corner starts at (0, 0).
+ // Anything negative is invalid.
+ if (nLeft < 0)
+ {
+ nWidth += nLeft;
+ nLeft = 0;
+ }
+
+ if (nTop < 0)
+ {
+ nHeight += nTop;
+ nTop = 0;
+ }
+
+ if (nWidth > 0 && nHeight > 0)
+ {
+ aRet.m_aRectangle = tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
+ }
+ }
+ // else leave empty rect.
+
+ aRet.m_nPart = nPart;
+ return aRet;
+}
+
+RectangleAndPart& CallbackFlushHandler::CallbackData::setRectangleAndPart(const std::string& payload)
+{
+ setRectangleAndPart(RectangleAndPart::Create(payload));
+
+ // Return reference to the cached object.
+ return boost::get<RectangleAndPart>(PayloadObject);
+}
+
+void CallbackFlushHandler::CallbackData::setRectangleAndPart(const RectangleAndPart& rRectAndPart)
+{
+ PayloadString = rRectAndPart.toString().getStr();
+ PayloadObject = rRectAndPart;
+}
+
+const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const
+{
+ assert(PayloadObject.which() == 1);
+ return boost::get<RectangleAndPart>(PayloadObject);
+}
+
+boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
+{
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(payload);
+ boost::property_tree::read_json(aStream, aTree);
+
+ // Let boost normalize the payload so it always matches the cache.
+ setJson(aTree);
+
+ // Return reference to the cached object.
+ return boost::get<boost::property_tree::ptree>(PayloadObject);
+}
+
+void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
+{
+ std::stringstream aJSONStream;
+ constexpr bool bPretty = false; // Don't waste time and bloat logs.
+ boost::property_tree::write_json(aJSONStream, rTree, bPretty);
+ PayloadString = boost::trim_copy(aJSONStream.str());
+
+ PayloadObject = rTree;
+}
+
+const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
+{
+ assert(PayloadObject.which() == 2);
+ return boost::get<boost::property_tree::ptree>(PayloadObject);
+}
+
+bool CallbackFlushHandler::CallbackData::validate() const
+{
+ switch (PayloadObject.which())
+ {
+ // Not cached.
+ case 0:
+ return true;
+
+ // RectangleAndPart.
+ case 1:
+ return getRectangleAndPart().toString().getStr() == PayloadString;
+
+ // Json.
+ case 2:
+ {
+ std::stringstream aJSONStream;
+ boost::property_tree::write_json(aJSONStream, getJson(), false);
+ const std::string aExpected = boost::trim_copy(aJSONStream.str());
+ return aExpected == PayloadString;
+ }
+
+ default:
+ assert(!"Unknown variant type; please add an entry to validate.");
+ }
+
+ return false;
+}
+
+}
+
+namespace {
+
+bool lcl_isViewCallbackType(const int type)
+{
+ switch (type)
+ {
+ case LOK_CALLBACK_CELL_VIEW_CURSOR:
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int lcl_getViewId(const std::string& payload)
+{
+ // this is a cheap way how to get the viewId from a JSON message; proper
+ // parsing is terribly expensive, and we just need the viewId here
+ size_t viewIdPos = payload.find("viewId");
+ if (viewIdPos == std::string::npos)
+ return 0;
+
+ size_t numberPos = payload.find(":", viewIdPos + 6);
+ if (numberPos == std::string::npos)
+ return 0;
+
+ for (++numberPos; numberPos < payload.length(); ++numberPos)
+ {
+ if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
+ break;
+ }
+
+ if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
+ return strtol(payload.substr(numberPos).c_str(), nullptr, 10);
+
+ return 0;
+}
+
+int lcl_getViewId(const desktop::CallbackFlushHandler::CallbackData& rCallbackData)
+{
+ if (rCallbackData.isCached())
+ return rCallbackData.getJson().get<int>("viewId");
+ return lcl_getViewId(rCallbackData.PayloadString);
+}
+
+std::string extractCertificate(const std::string & certificate)
+{
+ const std::string header("-----BEGIN CERTIFICATE-----");
+ const std::string footer("-----END CERTIFICATE-----");
+
+ std::string result;
+
+ size_t pos1 = certificate.find(header);
+ if (pos1 == std::string::npos)
+ return result;
+
+ size_t pos2 = certificate.find(footer, pos1 + 1);
+ if (pos2 == std::string::npos)
+ return result;
+
+ pos1 = pos1 + header.length();
+ pos2 = pos2 - pos1;
+
+ return certificate.substr(pos1, pos2);
+}
+
+std::string extractPrivateKey(const std::string & privateKey)
+{
+ const std::string header("-----BEGIN PRIVATE KEY-----");
+ const std::string footer("-----END PRIVATE KEY-----");
+
+ std::string result;
+
+ size_t pos1 = privateKey.find(header);
+ if (pos1 == std::string::npos)
+ return result;
+
+ size_t pos2 = privateKey.find(footer, pos1 + 1);
+ if (pos2 == std::string::npos)
+ return result;
+
+ pos1 = pos1 + header.length();
+ pos2 = pos2 - pos1;
+
+ return privateKey.substr(pos1, pos2);
+}
+
+// Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation
+css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame )
+{
+ const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController();
+ if ( xController.is() )
+ {
+ const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW );
+ return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW );
+ }
+ }
+
+ return css::uno::Reference< css::document::XUndoManager > ();
+}
+
+// Adjusts page margins for Writer doc. Needed by ToggleOrientation
+void ExecuteMarginLRChange(
+ const long nPageLeftMargin,
+ const long nPageRightMargin,
+ SvxLongLRSpaceItem* pPageLRMarginItem)
+{
+ pPageLRMarginItem->SetLeft( nPageLeftMargin );
+ pPageLRMarginItem->SetRight( nPageRightMargin );
+ SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE,
+ SfxCallMode::RECORD, { pPageLRMarginItem });
+}
+
+// Adjusts page margins for Writer doc. Needed by ToggleOrientation
+void ExecuteMarginULChange(
+ const long nPageTopMargin,
+ const long nPageBottomMargin,
+ SvxLongULSpaceItem* pPageULMarginItem)
+{
+ pPageULMarginItem->SetUpper( nPageTopMargin );
+ pPageULMarginItem->SetLower( nPageBottomMargin );
+ SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE,
+ SfxCallMode::RECORD, { pPageULMarginItem });
+}
+
+// Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation
+void ExecuteOrientationChange()
+{
+ std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE));
+ std::unique_ptr<SvxSizeItem> pPageSizeItem(new SvxSizeItem(SID_ATTR_PAGE_SIZE));
+ std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ));
+ std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ));
+ // 1mm in twips rounded
+ // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx
+ const long MINBODY = 56;
+ bool bIsLandscape = false;
+
+ css::uno::Reference< css::document::XUndoManager > mxUndoManager(
+ getUndoManager( SfxViewFrame::Current()->GetFrame().GetFrameInterface() ) );
+
+ if ( mxUndoManager.is() )
+ mxUndoManager->enterUndoContext( "" );
+
+
+ const SfxPoolItem* pItem;
+
+
+ SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pItem);
+ pPageSizeItem.reset( static_cast<SvxSizeItem*>(pItem->Clone()) );
+
+
+
+ SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pItem);
+ pPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pItem->Clone()) );
+
+
+
+ SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pItem);
+ pPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pItem->Clone()) );
+
+
+ {
+ if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height())
+ bIsLandscape = true;
+
+ // toggle page orientation
+ pPageItem->SetLandscape(!bIsLandscape);
+
+
+ // swap the width and height of the page size
+ const long nRotatedWidth = pPageSizeItem->GetSize().Height();
+ const long nRotatedHeight = pPageSizeItem->GetSize().Width();
+ pPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight));
+
+
+ // apply changed attributes
+ if (SfxViewShell::Current())
+ {
+ SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE,
+ SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() });
+ }
+ }
+
+
+ // check, if margin values still fit to the changed page size.
+ // if not, adjust margin values
+ {
+ const long nML = pPageLRMarginItem->GetLeft();
+ const long nMR = pPageLRMarginItem->GetRight();
+ const long nTmpPW = nML + nMR + MINBODY;
+
+ const long nPW = pPageSizeItem->GetSize().Width();
+
+ if ( nTmpPW > nPW )
+ {
+ if ( nML <= nMR )
+ {
+ ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() );
+ }
+ else
+ {
+ ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() );
+ }
+ }
+
+ const long nMT = pPageULMarginItem->GetUpper();
+ const long nMB = pPageULMarginItem->GetLower();
+ const long nTmpPH = nMT + nMB + MINBODY;
+
+ const long nPH = pPageSizeItem->GetSize().Height();
+
+ if ( nTmpPH > nPH )
+ {
+ if ( nMT <= nMB )
+ {
+ ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() );
+ }
+ else
+ {
+ ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() );
+ }
+ }
+ }
+
+ if ( mxUndoManager.is() )
+ mxUndoManager->leaveUndoContext();
+}
+
+void setupSidebar(bool bShow)
+{
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
+ if (pViewFrame)
+ {
+ if (bShow && !pViewFrame->GetChildWindow(SID_SIDEBAR))
+ pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */);
+
+ pViewFrame->ShowChildWindow(SID_SIDEBAR, bShow);
+
+ if (!bShow)
+ return;
+
+ // Force synchronous population of panels
+ SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
+ if (!pChild)
+ return;
+
+ auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow());
+ if (!pDockingWin)
+ return;
+ pDockingWin->SyncUpdate();
+ }
+ else
+ SetLastExceptionMsg("No view shell or sidebar");
+}
+
+VclPtr<Window> getSidebarWindow()
+{
+ VclPtr<Window> xRet;
+
+ setupSidebar(true);
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
+ if (!pViewFrame)
+ return xRet;
+
+ // really a SidebarChildWindow
+ SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
+ if (!pChild)
+ return xRet;
+
+ // really a SidebarDockingWindow
+ vcl::Window *pWin = pChild->GetWindow();
+ if (!pWin)
+ return xRet;
+ xRet = pWin;
+ return xRet;
+}
+
+} // end anonymous namespace
+
+// Could be anonymous in principle, but for the unit testing purposes, we
+// declare it in init.hxx.
+OUString desktop::extractParameter(OUString& rOptions, const OUString& rName)
+{
+ OUString aValue;
+
+ OUString aNameEquals(rName + "=");
+ OUString aCommaNameEquals("," + rName + "=");
+
+ int nIndex = -1;
+ if (rOptions.startsWith(aNameEquals))
+ {
+ size_t nLen = aNameEquals.getLength();
+ int nComma = rOptions.indexOf(",", nLen);
+ if (nComma >= 0)
+ {
+ aValue = rOptions.copy(nLen, nComma - nLen);
+ rOptions = rOptions.copy(nComma + 1);
+ }
+ else
+ {
+ aValue = rOptions.copy(nLen);
+ rOptions.clear();
+ }
+ }
+ else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
+ {
+ size_t nLen = aCommaNameEquals.getLength();
+ int nComma = rOptions.indexOf(",", nIndex + nLen);
+ if (nComma >= 0)
+ {
+ aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
+ rOptions = rOptions.copy(0, nIndex) + rOptions.copy(nComma);
+ }
+ else
+ {
+ aValue = rOptions.copy(nIndex + nLen);
+ rOptions = rOptions.copy(0, nIndex);
+ }
+ }
+
+ return aValue;
+}
+
+extern "C"
+{
+
+static void doc_destroy(LibreOfficeKitDocument* pThis);
+static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
+static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
+static int doc_getParts(LibreOfficeKitDocument* pThis);
+static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
+static int doc_getPart(LibreOfficeKitDocument* pThis);
+static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
+static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
+static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
+static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
+static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
+static void doc_paintTile(LibreOfficeKitDocument* pThis,
+ unsigned char* pBuffer,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight);
+#ifdef IOS
+static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
+ void* rCGContext,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight);
+#endif
+static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
+ unsigned char* pBuffer,
+ const int nPart,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight);
+static int doc_getTileMode(LibreOfficeKitDocument* pThis);
+static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
+ long* pWidth,
+ long* pHeight);
+static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
+ const char* pArguments);
+
+static void doc_registerCallback(LibreOfficeKitDocument* pThis,
+ LibreOfficeKitCallback pCallback,
+ void* pData);
+static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
+ int nType,
+ int nCharCode,
+ int nKeyCode);
+static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
+ unsigned nWindowId,
+ int nType,
+ const char* pText);
+static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ int nCharBefore,
+ int nCharAfter);
+static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ const char* pArguments);
+static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ int nType,
+ int nCharCode,
+ int nKeyCode);
+static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
+ int nType,
+ int nX,
+ int nY,
+ int nCount,
+ int nButtons,
+ int nModifier);
+static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ int nType,
+ int nX,
+ int nY,
+ int nCount,
+ int nButtons,
+ int nModifier);
+static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ const char* pType,
+ int nX,
+ int nY,
+ int nOffset);
+static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
+ const char* pCommand,
+ const char* pArguments,
+ bool bNotifyWhenFinished);
+static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis,
+ unsigned nLOKWindowId,
+ bool swap,
+ int nX,
+ int nY);
+static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
+ int nType,
+ int nX,
+ int nY);
+static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
+ const char* pMimeType,
+ char** pUsedMimeType);
+static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
+static int doc_getClipboard (LibreOfficeKitDocument* pThis,
+ const char **pMimeTypes,
+ size_t *pOutCount,
+ char ***pOutMimeTypes,
+ size_t **pOutSizes,
+ char ***pOutStreams);
+static int doc_setClipboard (LibreOfficeKitDocument* pThis,
+ const size_t nInCount,
+ const char **pInMimeTypes,
+ const size_t *pInSizes,
+ const char **pInStreams);
+static bool doc_paste(LibreOfficeKitDocument* pThis,
+ const char* pMimeType,
+ const char* pData,
+ size_t nSize);
+static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
+ int nType,
+ int nX,
+ int nY);
+static void doc_resetSelection (LibreOfficeKitDocument* pThis);
+static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
+static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
+ int nTilePixelWidth,
+ int nTilePixelHeight,
+ int nTileTwipWidth,
+ int nTileTwipHeight);
+static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
+static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
+static int doc_createView(LibreOfficeKitDocument* pThis);
+static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
+static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
+static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
+static int doc_getView(LibreOfficeKitDocument* pThis);
+static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
+static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
+static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
+static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
+ const char *pFontName,
+ const char *pChar,
+ int* pFontWidth,
+ int* pFontHeight,
+ int pOrientation);
+static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
+ const char *pFontName,
+ const char *pChar,
+ int* pFontWidth,
+ int* pFontHeight);
+static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
+
+static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
+ const int nX, const int nY,
+ const int nWidth, const int nHeight);
+
+static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
+ const int nX, const int nY,
+ const int nWidth, const int nHeight,
+ const double fDPIScale);
+
+static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
+ const int nX, const int nY,
+ const int nWidth, const int nHeight,
+ const double fDPIScale, int viewId);
+
+static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
+ nLOKWindowId, int nAction, const char* pData);
+
+static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
+
+static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize);
+
+static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize);
+
+static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
+
+static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
+
+static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
+ const int nWidth, const int nHeight);
+
+static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char*);
+
+
+static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis,
+ const char* pArguments);
+} // extern "C"
+
+namespace {
+ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
+{
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+ return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
+}
+
+#ifndef IOS
+
+/*
+ * Unfortunately clipboard creation using UNO is insanely baroque.
+ * we also need to ensure that this works for the first view which
+ * has no clear 'createView' called for it (unfortunately).
+ */
+rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
+{
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
+
+ SAL_INFO("lok", "Set to clipboard for view " << xClip.get());
+ // FIXME: using a hammer here - should not be necessary if all tests used createView.
+ pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY));
+
+ return xClip;
+}
+
+#endif
+
+} // anonymous namespace
+
+LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent)
+ : mxComponent(xComponent)
+{
+ m_pDocumentClass = gDocumentClass.lock();
+ if (!m_pDocumentClass)
+ {
+ m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>();
+
+ m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
+
+ m_pDocumentClass->destroy = doc_destroy;
+ m_pDocumentClass->saveAs = doc_saveAs;
+ m_pDocumentClass->getDocumentType = doc_getDocumentType;
+ m_pDocumentClass->getParts = doc_getParts;
+ m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles;
+ m_pDocumentClass->getPart = doc_getPart;
+ m_pDocumentClass->setPart = doc_setPart;
+ m_pDocumentClass->selectPart = doc_selectPart;
+ m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
+ m_pDocumentClass->getPartName = doc_getPartName;
+ m_pDocumentClass->setPartMode = doc_setPartMode;
+ m_pDocumentClass->paintTile = doc_paintTile;
+#ifdef IOS
+ m_pDocumentClass->paintTileToCGContext = doc_paintTileToCGContext;
+#endif
+ m_pDocumentClass->paintPartTile = doc_paintPartTile;
+ m_pDocumentClass->getTileMode = doc_getTileMode;
+ m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
+ m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
+ m_pDocumentClass->registerCallback = doc_registerCallback;
+ m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
+ m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent;
+ m_pDocumentClass->removeTextContext = doc_removeTextContext;
+ m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent;
+ m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
+ m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent;
+ m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent;
+ m_pDocumentClass->postUnoCommand = doc_postUnoCommand;
+ m_pDocumentClass->setTextSelection = doc_setTextSelection;
+ m_pDocumentClass->setWindowTextSelection = doc_setWindowTextSelection;
+ m_pDocumentClass->getTextSelection = doc_getTextSelection;
+ m_pDocumentClass->getSelectionType = doc_getSelectionType;
+ m_pDocumentClass->getClipboard = doc_getClipboard;
+ m_pDocumentClass->setClipboard = doc_setClipboard;
+ m_pDocumentClass->paste = doc_paste;
+ m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection;
+ m_pDocumentClass->resetSelection = doc_resetSelection;
+ m_pDocumentClass->getCommandValues = doc_getCommandValues;
+ m_pDocumentClass->setClientZoom = doc_setClientZoom;
+ m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea;
+ m_pDocumentClass->setOutlineState = doc_setOutlineState;
+
+ m_pDocumentClass->createView = doc_createView;
+ m_pDocumentClass->destroyView = doc_destroyView;
+ m_pDocumentClass->setView = doc_setView;
+ m_pDocumentClass->getView = doc_getView;
+ m_pDocumentClass->getViewsCount = doc_getViewsCount;
+ m_pDocumentClass->getViewIds = doc_getViewIds;
+
+ m_pDocumentClass->renderFont = doc_renderFont;
+ m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
+ m_pDocumentClass->getPartHash = doc_getPartHash;
+
+ m_pDocumentClass->paintWindow = doc_paintWindow;
+ m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI;
+ m_pDocumentClass->paintWindowForView = doc_paintWindowForView;
+ m_pDocumentClass->postWindow = doc_postWindow;
+ m_pDocumentClass->resizeWindow = doc_resizeWindow;
+
+ m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
+
+ m_pDocumentClass->getPartInfo = doc_getPartInfo;
+
+ m_pDocumentClass->insertCertificate = doc_insertCertificate;
+ m_pDocumentClass->addCertificate = doc_addCertificate;
+ m_pDocumentClass->getSignatureState = doc_getSignatureState;
+
+ m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
+ m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
+
+ m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
+ m_pDocumentClass->completeFunction = doc_completeFunction;
+
+ m_pDocumentClass->sendFormFieldEvent = doc_sendFormFieldEvent;
+
+ gDocumentClass = m_pDocumentClass;
+ }
+ pClass = m_pDocumentClass.get();
+
+#ifndef IOS
+ forceSetClipboardForCurrentView(this);
+#endif
+}
+
+LibLODocument_Impl::~LibLODocument_Impl()
+{
+ try
+ {
+ mxComponent->dispose();
+ }
+ catch (const css::lang::DisposedException& rException)
+ {
+ SAL_WARN("lok", "failed to dispose document:" << rException.Message);
+ }
+}
+
+static OUString getGenerator()
+{
+ OUString sGenerator(
+ Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"));
+ OUString os("$_OS");
+ ::rtl::Bootstrap::expandMacros(os);
+ return sGenerator.replaceFirst("%1", os);
+}
+
+extern "C" {
+
+CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
+ : Idle( "lokit timer callback" ),
+ m_pDocument(pDocument),
+ m_pCallback(pCallback),
+ m_pData(pData),
+ m_nDisableCallbacks(0)
+{
+ SetPriority(TaskPriority::POST_PAINT);
+
+ // Add the states that are safe to skip duplicates on, even when
+ // not consequent (i.e. do no emit them if unchanged from last).
+ m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL");
+ m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL");
+ m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL");
+ m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL");
+ m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL");
+ m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL");
+ m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL");
+ m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL");
+ m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL");
+ m_states.emplace(LOK_CALLBACK_SET_PART, "NIL");
+
+ Start();
+}
+
+CallbackFlushHandler::~CallbackFlushHandler()
+{
+ Stop();
+}
+
+void CallbackFlushHandler::callback(const int type, const char* payload, void* data)
+{
+ CallbackFlushHandler* self = static_cast<CallbackFlushHandler*>(data);
+ if (self)
+ {
+ self->queue(type, payload);
+ }
+}
+
+void CallbackFlushHandler::queue(const int type, const char* data)
+{
+ comphelper::ProfileZone aZone("CallbackFlushHander::queue");
+
+ CallbackData aCallbackData(type, (data ? data : "(nil)"));
+ const std::string& payload = aCallbackData.PayloadString;
+ SAL_INFO("lok", "Queue: [" << type << "]: [" << payload << "] on " << m_queue.size() << " entries.");
+
+ bool bIsChartActive = false;
+ if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
+ {
+ LokChartHelper aChartHelper(SfxViewShell::Current());
+ bIsChartActive = aChartHelper.GetWindow() != nullptr;
+ }
+
+ if (callbacksDisabled() && !bIsChartActive)
+ {
+ // We drop notifications when this is set, except for important ones.
+ // When we issue a complex command (such as .uno:InsertAnnotation)
+ // there will be multiple notifications. On the first invalidation
+ // we will start painting, but other events will get fired
+ // while the complex command in question executes.
+ // We don't want to suppress everything here on the wrong assumption
+ // that no new events are fired during painting.
+ if (type != LOK_CALLBACK_STATE_CHANGED &&
+ type != LOK_CALLBACK_INVALIDATE_TILES &&
+ type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
+ type != LOK_CALLBACK_CURSOR_VISIBLE &&
+ type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
+ type != LOK_CALLBACK_TEXT_SELECTION &&
+ type != LOK_CALLBACK_REFERENCE_MARKS)
+ {
+ SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << payload << "].");
+ return;
+ }
+
+ // In Writer we drop all notifications during painting.
+ if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
+ return;
+ }
+
+ // Suppress invalid payloads.
+ if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
+ payload.find(", 0, 0, ") != std::string::npos)
+ {
+ // The cursor position is often the relative coordinates of the widget
+ // issuing it, instead of the absolute one that we expect.
+ // This is temporary however, and, once the control is created and initialized
+ // correctly, it eventually emits the correct absolute coordinates.
+ SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ // drop duplicate callbacks for the listed types
+ switch (type)
+ {
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ case LOK_CALLBACK_STATE_CHANGED:
+ case LOK_CALLBACK_MOUSE_POINTER:
+ case LOK_CALLBACK_CELL_CURSOR:
+ case LOK_CALLBACK_CELL_VIEW_CURSOR:
+ case LOK_CALLBACK_CELL_FORMULA:
+ case LOK_CALLBACK_CELL_ADDRESS:
+ case LOK_CALLBACK_CURSOR_VISIBLE:
+ case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
+ case LOK_CALLBACK_SET_PART:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ case LOK_CALLBACK_INVALIDATE_HEADER:
+ case LOK_CALLBACK_WINDOW:
+ case LOK_CALLBACK_CALC_FUNCTION_LIST:
+ {
+ const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
+ [type] (const queue_type::value_type& elem) { return (elem.Type == type); });
+
+ if (pos != m_queue.rend() && pos->PayloadString == payload)
+ {
+ SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << payload << "].");
+ return;
+ }
+ }
+ break;
+ }
+
+ if (type == LOK_CALLBACK_TEXT_SELECTION && payload.empty())
+ {
+ const auto& posStart = std::find_if(m_queue.rbegin(), m_queue.rend(),
+ [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_START); });
+ if (posStart != m_queue.rend())
+ posStart->PayloadString.clear();
+
+ const auto& posEnd = std::find_if(m_queue.rbegin(), m_queue.rend(),
+ [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_END); });
+ if (posEnd != m_queue.rend())
+ posEnd->PayloadString.clear();
+ }
+
+ // When payload is empty discards any previous state.
+ if (payload.empty())
+ {
+ switch (type)
+ {
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ if (removeAll(
+ [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
+ SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
+ break;
+ }
+ }
+ else
+ {
+ switch (type)
+ {
+ // These are safe to use the latest state and ignore previous
+ // ones (if any) since the last overrides previous ones.
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_MOUSE_POINTER:
+ case LOK_CALLBACK_CELL_CURSOR:
+ case LOK_CALLBACK_CELL_FORMULA:
+ case LOK_CALLBACK_CELL_ADDRESS:
+ case LOK_CALLBACK_CURSOR_VISIBLE:
+ case LOK_CALLBACK_SET_PART:
+ case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
+ case LOK_CALLBACK_RULER_UPDATE:
+ {
+ if (removeAll(
+ [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
+ SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
+ }
+ break;
+
+ // These are safe to use the latest state and ignore previous
+ // ones (if any) since the last overrides previous ones,
+ // but only if the view is the same.
+ case LOK_CALLBACK_CELL_VIEW_CURSOR:
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
+ case LOK_CALLBACK_CALC_FUNCTION_LIST:
+ {
+ const int nViewId = lcl_getViewId(payload);
+ removeAll(
+ [type, nViewId] (const queue_type::value_type& elem) {
+ return (elem.Type == type && nViewId == lcl_getViewId(elem));
+ }
+ );
+ }
+ break;
+
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ if (processInvalidateTilesEvent(aCallbackData))
+ return;
+ break;
+
+ // State changes with same name override previous ones with a different value.
+ // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
+ case LOK_CALLBACK_STATE_CHANGED:
+ {
+ // Compare the state name=value and overwrite earlier entries with same name.
+ const auto pos = payload.find('=');
+ if (pos != std::string::npos)
+ {
+ const std::string name = payload.substr(0, pos + 1);
+ removeAll(
+ [type, &name] (const queue_type::value_type& elem) {
+ return (elem.Type == type) && (elem.PayloadString.compare(0, name.size(), name) == 0);
+ }
+ );
+ }
+ }
+ break;
+
+ case LOK_CALLBACK_WINDOW:
+ if (processWindowEvent(aCallbackData))
+ return;
+ break;
+
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ {
+ // remove only selection ranges and 'EMPTY' messages
+ // always send 'INPLACE' and 'INPLACE EXIT' messages
+ removeAll([type, payload] (const queue_type::value_type& elem)
+ { return (elem.Type == type && elem.PayloadString[0] != 'I'); });
+ }
+ break;
+ }
+ }
+
+ // Validate that the cached data and the payload string are identical.
+ assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
+ m_queue.emplace_back(aCallbackData);
+ SAL_INFO("lok", "Queued #" << (m_queue.size() - 1) <<
+ " [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
+
+#ifdef DBG_UTIL
+ {
+ // Dump the queue state and validate cached data.
+ int i = 1;
+ std::ostringstream oss;
+ if (m_queue.empty())
+ oss << "Empty";
+ else
+ oss << m_queue.size() << " items\n";
+ for (const CallbackData& c : m_queue)
+ oss << i++ << ": [" << c.Type << "] [" << c.PayloadString << "].\n";
+ SAL_INFO("lok", "Current Queue: " << oss.str());
+ assert(
+ std::all_of(
+ m_queue.begin(), m_queue.end(),
+ [](const CallbackData& c) { return c.validate(); }));
+ }
+#endif
+
+ lock.unlock();
+ if (!IsActive())
+ {
+ Start();
+ }
+}
+
+bool CallbackFlushHandler::processInvalidateTilesEvent(CallbackData& aCallbackData)
+{
+ const std::string& payload = aCallbackData.PayloadString;
+ const int type = aCallbackData.Type;
+
+ RectangleAndPart& rcNew = aCallbackData.setRectangleAndPart(payload);
+ if (rcNew.isEmpty())
+ {
+ SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
+ return true;
+ }
+
+ // If we have to invalidate all tiles, we can skip any new tile invalidation.
+ // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
+ const auto& pos
+ = std::find_if(m_queue.rbegin(), m_queue.rend(), [](const queue_type::value_type& elem) {
+ return (elem.Type == LOK_CALLBACK_INVALIDATE_TILES);
+ });
+ if (pos != m_queue.rend())
+ {
+ const RectangleAndPart& rcOld = pos->getRectangleAndPart();
+ if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
+ {
+ SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
+ << "] since all tiles need to be invalidated.");
+ return true;
+ }
+
+ if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
+ {
+ // If fully overlapping.
+ if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
+ {
+ SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
+ << "] since overlaps existing all-parts.");
+ return true;
+ }
+ }
+ }
+
+ if (rcNew.isInfinite())
+ {
+ SAL_INFO("lok", "Have Empty [" << type << "]: [" << payload
+ << "] so removing all with part " << rcNew.m_nPart << ".");
+ removeAll([&rcNew](const queue_type::value_type& elem) {
+ if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
+ {
+ // Remove exiting if new is all-encompassing, or if of the same part.
+ const RectangleAndPart rcOld = RectangleAndPart::Create(elem.PayloadString);
+ return (rcNew.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart);
+ }
+
+ // Keep others.
+ return false;
+ });
+ }
+ else
+ {
+ const auto rcOrig = rcNew;
+
+ SAL_INFO("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
+ removeAll([&rcNew](const queue_type::value_type& elem) {
+ if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
+ {
+ const RectangleAndPart& rcOld = elem.getRectangleAndPart();
+ if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
+ {
+ SAL_INFO("lok", "Nothing to merge between new: "
+ << rcNew.toString() << ", and old: " << rcOld.toString());
+ return false;
+ }
+
+ if (rcNew.m_nPart == -1)
+ {
+ // Don't merge unless fully overlapped.
+ SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
+ << "?");
+ if (rcNew.m_aRectangle.IsInside(rcOld.m_aRectangle))
+ {
+ SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
+ << rcOld.toString() << ".");
+ return true;
+ }
+ }
+ else if (rcOld.m_nPart == -1)
+ {
+ // Don't merge unless fully overlapped.
+ SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
+ << "?");
+ if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
+ {
+ SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
+ << rcOld.toString() << ".");
+ return true;
+ }
+ }
+ else
+ {
+ const tools::Rectangle rcOverlap
+ = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
+ const bool bOverlap = !rcOverlap.IsEmpty();
+ SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
+ << " => " << rcOverlap.toString()
+ << " Overlap: " << bOverlap);
+ if (bOverlap)
+ {
+ rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
+ SAL_INFO("lok", "Merged: " << rcNew.toString());
+ return true;
+ }
+ }
+ }
+
+ // Keep others.
+ return false;
+ });
+
+ if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
+ {
+ SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
+ if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
+ || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
+ {
+ SAL_WARN("lok", "Error: merged rect smaller.");
+ }
+ }
+ }
+
+ aCallbackData.setRectangleAndPart(rcNew);
+ // Queue this one.
+ return false;
+}
+
+bool CallbackFlushHandler::processWindowEvent(CallbackData& aCallbackData)
+{
+ const std::string& payload = aCallbackData.PayloadString;
+ const int type = aCallbackData.Type;
+
+ boost::property_tree::ptree& aTree = aCallbackData.setJson(payload);
+ const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
+ const std::string aAction = aTree.get<std::string>("action", "");
+ if (aAction == "invalidate")
+ {
+ std::string aRectStr = aTree.get<std::string>("rectangle", "");
+ // no 'rectangle' field => invalidate all of the window =>
+ // remove all previous window part invalidations
+ if (aRectStr.empty())
+ {
+ removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
+ if (elem.Type == LOK_CALLBACK_WINDOW)
+ {
+ const boost::property_tree::ptree& aOldTree = elem.getJson();
+ if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
+ && aOldTree.get<std::string>("action", "") == "invalidate")
+ {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+ else
+ {
+ // if we have to invalidate all of the window, ignore
+ // any part invalidation message
+ const auto invAllExist = std::any_of(m_queue.rbegin(), m_queue.rend(),
+ [&nLOKWindowId] (const queue_type::value_type& elem)
+ {
+ if (elem.Type != LOK_CALLBACK_WINDOW)
+ return false;
+
+ const boost::property_tree::ptree& aOldTree = elem.getJson();
+ return nLOKWindowId == aOldTree.get<unsigned>("id", 0)
+ && aOldTree.get<std::string>("action", "") == "invalidate"
+ && aOldTree.get<std::string>("rectangle", "").empty();
+ });
+
+ // we found a invalidate-all window callback
+ if (invAllExist)
+ {
+ SAL_INFO("lok.dialog", "Skipping queue ["
+ << type << "]: [" << payload
+ << "] since whole window needs to be invalidated.");
+ return true;
+ }
+
+ std::istringstream aRectStream(aRectStr);
+ long nLeft, nTop, nWidth, nHeight;
+ char nComma;
+ aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
+ tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
+ bool currentIsRedundant = false;
+ removeAll([&aNewRect, &nLOKWindowId,
+ &currentIsRedundant](const queue_type::value_type& elem) {
+ if (elem.Type != LOK_CALLBACK_WINDOW)
+ return false;
+
+ const boost::property_tree::ptree& aOldTree = elem.getJson();
+ if (aOldTree.get<std::string>("action", "") == "invalidate")
+ {
+ // Not possible that we encounter an empty rectangle here; we already handled this case above.
+ std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
+ long nOldLeft, nOldTop, nOldWidth, nOldHeight;
+ char nOldComma;
+ aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
+ >> nOldComma >> nOldHeight;
+ const tools::Rectangle aOldRect = tools::Rectangle(
+ nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
+
+ if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
+ {
+ if (aNewRect == aOldRect)
+ {
+ SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
+ << "]. Skipping new.");
+ // We have a rectangle in the queue already that makes the current Callback useless.
+ currentIsRedundant = true;
+ return false;
+ }
+ // new one engulfs the old one?
+ else if (aNewRect.IsInside(aOldRect))
+ {
+ SAL_INFO("lok.dialog",
+ "New rect [" << aNewRect.toString() << "] engulfs old ["
+ << aOldRect.toString() << "]. Replacing old.");
+ return true;
+ }
+ // old one engulfs the new one?
+ else if (aOldRect.IsInside(aNewRect))
+ {
+ SAL_INFO("lok.dialog",
+ "Old rect [" << aOldRect.toString() << "] engulfs new ["
+ << aNewRect.toString() << "]. Skipping new.");
+ // We have a rectangle in the queue already that makes the current Callback useless.
+ currentIsRedundant = true;
+ return false;
+ }
+ else
+ {
+ // Overlapping rects.
+ const tools::Rectangle aPreMergeRect = aNewRect;
+ aNewRect.Union(aOldRect);
+ SAL_INFO("lok.dialog", "Merging rects ["
+ << aPreMergeRect.toString() << "] & ["
+ << aOldRect.toString() << "] = ["
+ << aNewRect.toString()
+ << "]. Replacing old.");
+ return true;
+ }
+ }
+ }
+
+ // keep rest
+ return false;
+ });
+
+ // Do not enqueue if redundant.
+ if (currentIsRedundant)
+ return true;
+
+ aTree.put("rectangle", aNewRect.toString().getStr());
+ aCallbackData.setJson(aTree);
+ assert(aCallbackData.validate() && "Validation after setJson failed!");
+ }
+ }
+ else if (aAction == "created")
+ {
+ // Remove all previous actions on same dialog, if we are creating it anew.
+ removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
+ if (elem.Type == LOK_CALLBACK_WINDOW)
+ {
+ const boost::property_tree::ptree& aOldTree = elem.getJson();
+ if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
+ return true;
+ }
+ return false;
+ });
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
+ return false;
+ }
+
+#ifndef IOS
+ auto xClip = forceSetClipboardForCurrentView(m_pDocument);
+
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip.get());
+ pWindow->SetClipboard(xClipboard);
+#endif
+ }
+ else if (aAction == "size_changed")
+ {
+ // A size change is practically re-creation of the window.
+ // But at a minimum it's a full invalidation.
+ removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
+ if (elem.Type == LOK_CALLBACK_WINDOW)
+ {
+ const boost::property_tree::ptree& aOldTree = elem.getJson();
+ if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
+ {
+ const std::string aOldAction = aOldTree.get<std::string>("action", "");
+ if (aOldAction == "invalidate")
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ // Queue this one.
+ return false;
+}
+
+void CallbackFlushHandler::Invoke()
+{
+ comphelper::ProfileZone aZone("CallbackFlushHander::Invoke");
+
+ if (!m_pCallback)
+ return;
+
+ std::scoped_lock<std::mutex> lock(m_mutex);
+
+ SAL_INFO("lok", "Flushing " << m_queue.size() << " elements.");
+ for (const auto& rCallbackData : m_queue)
+ {
+ const int type = rCallbackData.Type;
+ const auto& payload = rCallbackData.PayloadString;
+ const int viewId = lcl_isViewCallbackType(type) ? lcl_getViewId(rCallbackData) : -1;
+
+ if (viewId == -1)
+ {
+ const auto stateIt = m_states.find(type);
+ if (stateIt != m_states.end())
+ {
+ // If the state didn't change, it's safe to ignore.
+ if (stateIt->second == payload)
+ {
+ SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
+ continue;
+ }
+
+ stateIt->second = payload;
+ }
+ }
+ else
+ {
+ const auto statesIt = m_viewStates.find(viewId);
+ if (statesIt != m_viewStates.end())
+ {
+ auto& states = statesIt->second;
+ const auto stateIt = states.find(type);
+ if (stateIt != states.end())
+ {
+ // If the state didn't change, it's safe to ignore.
+ if (stateIt->second == payload)
+ {
+ SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
+ continue;
+ }
+
+ SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
+ stateIt->second = payload;
+ }
+ else
+ {
+ SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
+ states.emplace(type, payload);
+
+ }
+ }
+ }
+
+ m_pCallback(type, payload.c_str(), m_pData);
+ }
+
+ m_queue.clear();
+}
+
+bool CallbackFlushHandler::removeAll(const std::function<bool (const CallbackFlushHandler::queue_type::value_type&)>& rTestFunc)
+{
+ auto newEnd = std::remove_if(m_queue.begin(), m_queue.end(), rTestFunc);
+ if (newEnd != m_queue.end())
+ {
+ m_queue.erase(newEnd, m_queue.end());
+ return true;
+ }
+
+ return false;
+}
+
+void CallbackFlushHandler::addViewStates(int viewId)
+{
+ const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
+ if (!result.second && result.first != m_viewStates.end())
+ {
+ result.first->second.clear();
+ }
+}
+
+void CallbackFlushHandler::removeViewStates(int viewId)
+{
+ m_viewStates.erase(viewId);
+}
+
+
+static void doc_destroy(LibreOfficeKitDocument *pThis)
+{
+ comphelper::ProfileZone aZone("doc_destroy");
+
+ SolarMutexGuard aGuard;
+
+ LOKClipboardFactory::releaseClipboardForView(-1);
+
+ LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
+ delete pDocument;
+}
+
+static void lo_destroy (LibreOfficeKit* pThis);
+static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
+static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL);
+static char * lo_getError (LibreOfficeKit* pThis);
+static void lo_freeError (char* pFree);
+static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis,
+ const char* pURL,
+ const char* pOptions);
+static void lo_registerCallback (LibreOfficeKit* pThis,
+ LibreOfficeKitCallback pCallback,
+ void* pData);
+static char* lo_getFilterTypes(LibreOfficeKit* pThis);
+static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
+static void lo_setDocumentPassword(LibreOfficeKit* pThis,
+ const char* pURL,
+ const char* pPassword);
+static char* lo_getVersionInfo(LibreOfficeKit* pThis);
+static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
+
+static bool lo_signDocument(LibreOfficeKit* pThis,
+ const char* pUrl,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize);
+
+static void lo_runLoop(LibreOfficeKit* pThis,
+ LibreOfficeKitPollCallback pPollCallback,
+ LibreOfficeKitWakeCallback pWakeCallback,
+ void* pData);
+
+LibLibreOffice_Impl::LibLibreOffice_Impl()
+ : m_pOfficeClass( gOfficeClass.lock() )
+ , maThread(nullptr)
+ , mpCallback(nullptr)
+ , mpCallbackData(nullptr)
+ , mOptionalFeatures(0)
+{
+ if(!m_pOfficeClass) {
+ m_pOfficeClass = std::make_shared<LibreOfficeKitClass>();
+ m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
+
+ m_pOfficeClass->destroy = lo_destroy;
+ m_pOfficeClass->documentLoad = lo_documentLoad;
+ m_pOfficeClass->getError = lo_getError;
+ m_pOfficeClass->freeError = lo_freeError;
+ m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
+ m_pOfficeClass->registerCallback = lo_registerCallback;
+ m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
+ m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
+ m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
+ m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
+ m_pOfficeClass->runMacro = lo_runMacro;
+ m_pOfficeClass->signDocument = lo_signDocument;
+ m_pOfficeClass->runLoop = lo_runLoop;
+
+ gOfficeClass = m_pOfficeClass;
+ }
+
+ pClass = m_pOfficeClass.get();
+}
+
+LibLibreOffice_Impl::~LibLibreOffice_Impl()
+{
+}
+
+namespace
+{
+
+#ifdef IOS
+void paintTileToCGContext(ITiledRenderable* pDocument,
+ void* rCGContext, const Size nCanvasSize,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight)
+{
+ SystemGraphicsData aData;
+ aData.rCGContext = reinterpret_cast<CGContextRef>(rCGContext);
+
+ ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ pDevice->SetOutputSizePixel(nCanvasSize);
+ pDocument->paintTile(*pDevice, nCanvasSize.Width(), nCanvasSize.Height(),
+ nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+}
+
+void paintTileIOS(LibreOfficeKitDocument* pThis,
+ unsigned char* pBuffer,
+ const int nCanvasWidth, const int nCanvasHeight, const double fDPIScale,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight)
+{
+ CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
+ nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
+ kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
+
+ CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
+ CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
+
+ doc_paintTileToCGContext(pThis, (void*) pCGContext, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+
+ CGContextRelease(pCGContext);
+}
+#endif
+
+void setLanguageAndLocale(OUString const & aLangISO)
+{
+ SvtSysLocaleOptions aLocalOptions;
+ aLocalOptions.SetLocaleConfigString(aLangISO);
+ aLocalOptions.SetUILocaleConfigString(aLangISO);
+ aLocalOptions.Commit();
+}
+
+} // anonymous namespace
+
+// Wonder global state ...
+static uno::Reference<css::uno::XComponentContext> xContext;
+static uno::Reference<css::lang::XMultiServiceFactory> xSFactory;
+static uno::Reference<css::lang::XMultiComponentFactory> xFactory;
+
+static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
+{
+ return lo_documentLoadWithOptions(pThis, pURL, nullptr);
+}
+
+static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
+{
+ comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
+
+ SolarMutexGuard aGuard;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ pLib->maLastExceptionMsg.clear();
+
+ OUString aURL(getAbsoluteURL(pURL));
+ if (aURL.isEmpty())
+ {
+ pLib->maLastExceptionMsg = "Filename to load was not provided.";
+ SAL_INFO("lok", "URL for load is empty");
+ return nullptr;
+ }
+
+ pLib->maLastExceptionMsg.clear();
+
+ if (!xContext.is())
+ {
+ pLib->maLastExceptionMsg = "ComponentContext is not available";
+ SAL_INFO("lok", "ComponentContext is not available");
+ return nullptr;
+ }
+
+ uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
+
+ if (!xComponentLoader.is())
+ {
+ pLib->maLastExceptionMsg = "ComponentLoader is not available";
+ SAL_INFO("lok", "ComponentLoader is not available");
+ return nullptr;
+ }
+
+ try
+ {
+ // 'Language=...' is an option that LOK consumes by itself, and does
+ // not pass it as a parameter to the filter
+ OUString aOptions = getUString(pOptions);
+ const OUString aLanguage = extractParameter(aOptions, "Language");
+
+ if (!aLanguage.isEmpty())
+ {
+ SfxLokHelper::setDefaultLanguage(aLanguage);
+ // Set the LOK language tag, used for dialog tunneling.
+ comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
+ comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage));
+
+ SAL_INFO("lok", "Set document language to " << aLanguage);
+ // use with care - it sets it for the entire core, not just the
+ // document
+ setLanguageAndLocale(aLanguage);
+ // Need to reset the static initialized values
+ SvNumberFormatter::resetTheCurrencyTable();
+ }
+
+ const OUString aDeviceFormFactor = extractParameter(aOptions, "DeviceFormFactor");
+ SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
+
+ uno::Sequence<css::beans::PropertyValue> aFilterOptions(2);
+ aFilterOptions[0] = css::beans::PropertyValue( "FilterOptions",
+ 0,
+ uno::makeAny(aOptions),
+ beans::PropertyState_DIRECT_VALUE);
+
+ rtl::Reference<LOKInteractionHandler> const pInteraction(
+ new LOKInteractionHandler("load", pLib));
+ auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
+ comphelper::ScopeGuard const g([&] () {
+ if (pair.second)
+ {
+ pLib->mInteractionMap.erase(aURL.toUtf8());
+ }
+ });
+ uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
+ aFilterOptions[1].Name = "InteractionHandler";
+ aFilterOptions[1].Value <<= xInteraction;
+
+ /* TODO
+ sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
+ aFilterOptions[2].Name = "MacroExecutionMode";
+ aFilterOptions[2].Value <<= nMacroExecMode;
+
+ sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
+ aFilterOptions[3].Name = "UpdateDocMode";
+ aFilterOptions[3].Value <<= nUpdateDoc;
+ */
+
+ uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
+ aURL, "_blank", 0,
+ aFilterOptions);
+
+ assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
+
+ if (!xComponent.is())
+ {
+ pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
+ SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
+ return nullptr;
+ }
+
+ LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent);
+ if (pLib->mpCallback)
+ {
+ int nState = doc_getSignatureState(pDocument);
+ pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
+ }
+ return pDocument;
+ }
+ catch (const uno::Exception& exception)
+ {
+ pLib->maLastExceptionMsg = exception.Message;
+ TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
+ }
+
+ return nullptr;
+}
+
+static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
+{
+ comphelper::ProfileZone aZone("lo_runMacro");
+
+ SolarMutexGuard aGuard;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ pLib->maLastExceptionMsg.clear();
+
+ OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
+ if (sURL.isEmpty())
+ {
+ pLib->maLastExceptionMsg = "Macro to run was not provided.";
+ SAL_INFO("lok", "Macro URL is empty");
+ return false;
+ }
+
+ if (!sURL.startsWith("macro://"))
+ {
+ pLib->maLastExceptionMsg = "This doesn't look like macro URL";
+ SAL_INFO("lok", "Macro URL is invalid");
+ return false;
+ }
+
+ pLib->maLastExceptionMsg.clear();
+
+ if (!xContext.is())
+ {
+ pLib->maLastExceptionMsg = "ComponentContext is not available";
+ SAL_INFO("lok", "ComponentContext is not available");
+ return false;
+ }
+
+ util::URL aURL;
+ aURL.Complete = sURL;
+
+ uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) );
+
+ if( xParser.is() )
+ xParser->parseStrict( aURL );
+
+ uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
+
+ if (!xComponentLoader.is())
+ {
+ pLib->maLastExceptionMsg = "ComponentLoader is not available";
+ SAL_INFO("lok", "ComponentLoader is not available");
+ return false;
+ }
+
+ xFactory = xContext->getServiceManager();
+
+ if (xFactory.is())
+ {
+ uno::Reference<frame::XDispatchProvider> xDP;
+ xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
+ xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
+ uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
+
+ if (!xD.is())
+ {
+ pLib->maLastExceptionMsg = "Macro loader is not available";
+ SAL_INFO("lok", "Macro loader is not available");
+ return false;
+ }
+
+ uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
+ uno::Sequence<css::beans::PropertyValue> aEmpty;
+ css::beans::PropertyValue aErr;
+ uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
+ aRet >>= aErr;
+
+ if (aErr.Name == "ErrorCode")
+ {
+ sal_uInt32 nErrCode = 0; // ERRCODE_NONE
+ aErr.Value >>= nErrCode;
+
+ pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
+ SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
+ const char* pURL,
+ const unsigned char* pCertificateBinary,
+ const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary,
+ const int nPrivateKeyBinarySize)
+{
+ comphelper::ProfileZone aZone("lo_signDocument");
+
+ OUString aURL(getAbsoluteURL(pURL));
+ if (aURL.isEmpty())
+ return false;
+
+ if (!xContext.is())
+ return false;
+
+ uno::Sequence<sal_Int8> aCertificateSequence;
+
+ std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
+ std::string aCertificateBase64String = extractCertificate(aCertificateString);
+ if (!aCertificateBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
+ comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
+ }
+ else
+ {
+ aCertificateSequence.realloc(nCertificateBinarySize);
+ std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
+ }
+
+ uno::Sequence<sal_Int8> aPrivateKeySequence;
+ std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
+ std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
+ if (!aPrivateKeyBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
+ comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
+ }
+ else
+ {
+ aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
+ std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.begin());
+ }
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+ uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
+
+ if (!xCertificateCreator.is())
+ return false;
+
+ uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
+
+ if (!xCertificate.is())
+ return false;
+
+ sfx2::DocumentSigner aDocumentSigner(aURL);
+ if (!aDocumentSigner.signDocument(xCertificate))
+ return false;
+
+ return true;
+}
+
+static void lo_registerCallback (LibreOfficeKit* pThis,
+ LibreOfficeKitCallback pCallback,
+ void* pData)
+{
+ SolarMutexGuard aGuard;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ pLib->maLastExceptionMsg.clear();
+
+ pLib->mpCallback = pCallback;
+ pLib->mpCallbackData = pData;
+}
+
+static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
+{
+ comphelper::ProfileZone aZone("doc_saveAs");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ OUString sFormat = getUString(pFormat);
+ OUString aURL(getAbsoluteURL(sUrl));
+ if (aURL.isEmpty())
+ {
+ SetLastExceptionMsg("Filename to save to was not provided.");
+ SAL_INFO("lok", "URL for save is empty");
+ return false;
+ }
+
+ try
+ {
+ const ExtensionMap* pMap;
+
+ switch (doc_getDocumentType(pThis))
+ {
+ case LOK_DOCTYPE_SPREADSHEET:
+ pMap = aCalcExtensionMap;
+ break;
+ case LOK_DOCTYPE_PRESENTATION:
+ pMap = aImpressExtensionMap;
+ break;
+ case LOK_DOCTYPE_DRAWING:
+ pMap = aDrawExtensionMap;
+ break;
+ case LOK_DOCTYPE_TEXT:
+ pMap = aWriterExtensionMap;
+ break;
+ case LOK_DOCTYPE_OTHER:
+ default:
+ SAL_INFO("lok", "Can't save document - unsupported document type.");
+ return false;
+ }
+
+ if (pFormat == nullptr)
+ {
+ // sniff from the extension
+ sal_Int32 idx = aURL.lastIndexOf(".");
+ if( idx > 0 )
+ {
+ sFormat = aURL.copy( idx + 1 );
+ }
+ else
+ {
+ SetLastExceptionMsg("input filename without a suffix");
+ return false;
+ }
+ }
+
+ OUString aFilterName;
+ for (sal_Int32 i = 0; pMap[i].extn; ++i)
+ {
+ if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
+ {
+ aFilterName = getUString(pMap[i].filterName);
+ break;
+ }
+ }
+ if (aFilterName.isEmpty())
+ {
+ SetLastExceptionMsg("no output filter found for provided suffix");
+ return false;
+ }
+
+ OUString aFilterOptions = getUString(pFilterOptions);
+
+ // Check if watermark for pdf is passed by filteroptions...
+ // It is not a real filter option so it must be filtered out.
+ OUString watermarkText, sFullSheetPreview;
+ int aIndex = -1;
+ if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
+ {
+ int bIndex = aFilterOptions.indexOf("WATERMARKEND");
+ watermarkText = aFilterOptions.copy(aIndex+11, bIndex-(aIndex+11));
+
+ OUString temp = aFilterOptions.copy(0, aIndex);
+ aFilterOptions = temp + aFilterOptions.copy(bIndex+12);
+ }
+
+ if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
+ {
+ int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
+ sFullSheetPreview = aFilterOptions.copy(aIndex+18, bIndex-(aIndex+18));
+
+ OUString temp = aFilterOptions.copy(0, aIndex);
+ aFilterOptions = temp + aFilterOptions.copy(bIndex+16);
+ }
+
+ bool bFullSheetPreview = sFullSheetPreview == "true";
+
+ // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
+ // gets a new name). When this is not provided, the meaning of
+ // saveAs() is more like save-a-copy, which allows saving to any
+ // random format like PDF or PNG.
+ // It is not a real filter option, so we have to filter it out.
+ const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
+ std::vector<OUString> aFilteredOptionVec;
+ bool bTakeOwnership = false;
+ MediaDescriptor aSaveMediaDescriptor;
+ for (const auto& rOption : aOptionSeq)
+ {
+ if (rOption == "TakeOwnership")
+ bTakeOwnership = true;
+ else if (rOption == "NoFileSync")
+ aSaveMediaDescriptor["NoFileSync"] <<= true;
+ else
+ aFilteredOptionVec.push_back(rOption);
+ }
+
+ aSaveMediaDescriptor["Overwrite"] <<= true;
+ aSaveMediaDescriptor["FilterName"] <<= aFilterName;
+
+ auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
+ aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
+ aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS()] <<= aFilterOptions;
+
+ if(!watermarkText.isEmpty() || bFullSheetPreview)
+ {
+ uno::Sequence< beans::PropertyValue > aFilterData( static_cast<int>(bFullSheetPreview) + static_cast<int>(!watermarkText.isEmpty()) );
+
+ if (!watermarkText.isEmpty())
+ {
+ aFilterData[ 0 ].Name = "TiledWatermark";
+ aFilterData[ 0 ].Value <<= watermarkText;
+ }
+
+ if (bFullSheetPreview)
+ {
+ int nOptIndex = static_cast<int>(!watermarkText.isEmpty());
+
+ aFilterData[ nOptIndex ].Name = "SinglePageSheets";
+ aFilterData[ nOptIndex ].Value <<= true;
+ }
+
+ aSaveMediaDescriptor["FilterData"] <<= aFilterData;
+ }
+
+ // add interaction handler too
+ if (gImpl)
+ {
+ // gImpl does not have to exist when running from a unit test
+ rtl::Reference<LOKInteractionHandler> const pInteraction(
+ new LOKInteractionHandler("saveas", gImpl, pDocument));
+ uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
+
+ aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER()] <<= xInteraction;
+ }
+
+ uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
+
+ if (bTakeOwnership)
+ xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
+ else
+ xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
+
+ return true;
+ }
+ catch (const uno::Exception& exception)
+ {
+ SetLastExceptionMsg("exception: " + exception.Message);
+ }
+ return false;
+}
+
+/**
+ * Initialize UNO commands, in the sense that from now on, the LOK client gets updates for status
+ * changes of these commands. This is necessary, because (unlike in the desktop case) there are no
+ * toolbars hosting widgets these UNO commands, so no such status updates would be sent to the
+ * headless LOK clients out of the box.
+ */
+static void doc_iniUnoCommands ()
+{
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ OUString sUnoCommands[] =
+ {
+ OUString(".uno:AlignLeft"),
+ OUString(".uno:AlignHorizontalCenter"),
+ OUString(".uno:AlignRight"),
+ OUString(".uno:BackColor"),
+ OUString(".uno:BackgroundColor"),
+ OUString(".uno:TableCellBackgroundColor"),
+ OUString(".uno:Bold"),
+ OUString(".uno:CenterPara"),
+ OUString(".uno:CharBackColor"),
+ OUString(".uno:CharBackgroundExt"),
+ OUString(".uno:CharFontName"),
+ OUString(".uno:Color"),
+ OUString(".uno:ControlCodes"),
+ OUString(".uno:DecrementIndent"),
+ OUString(".uno:DefaultBullet"),
+ OUString(".uno:DefaultNumbering"),
+ OUString(".uno:FontColor"),
+ OUString(".uno:FontHeight"),
+ OUString(".uno:IncrementIndent"),
+ OUString(".uno:Italic"),
+ OUString(".uno:JustifyPara"),
+ OUString(".uno:OutlineFont"),
+ OUString(".uno:LeftPara"),
+ OUString(".uno:LanguageStatus"),
+ OUString(".uno:RightPara"),
+ OUString(".uno:Shadowed"),
+ OUString(".uno:SubScript"),
+ OUString(".uno:SuperScript"),
+ OUString(".uno:Strikeout"),
+ OUString(".uno:StyleApply"),
+ OUString(".uno:Underline"),
+ OUString(".uno:ModifiedStatus"),
+ OUString(".uno:Undo"),
+ OUString(".uno:Redo"),
+ OUString(".uno:InsertPage"),
+ OUString(".uno:DeletePage"),
+ OUString(".uno:DuplicatePage"),
+ OUString(".uno:Cut"),
+ OUString(".uno:Copy"),
+ OUString(".uno:Paste"),
+ OUString(".uno:SelectAll"),
+ OUString(".uno:InsertAnnotation"),
+ OUString(".uno:DeleteAnnotation"),
+ OUString(".uno:ReplyComment"),
+ OUString(".uno:ResolveComment"),
+ OUString(".uno:InsertRowsBefore"),
+ OUString(".uno:InsertRowsAfter"),
+ OUString(".uno:InsertColumnsBefore"),
+ OUString(".uno:InsertColumnsAfter"),
+ OUString(".uno:DeleteRows"),
+ OUString(".uno:DeleteColumns"),
+ OUString(".uno:DeleteTable"),
+ OUString(".uno:SelectTable"),
+ OUString(".uno:EntireRow"),
+ OUString(".uno:EntireColumn"),
+ OUString(".uno:EntireCell"),
+ OUString(".uno:AssignLayout"),
+ OUString(".uno:StatusDocPos"),
+ OUString(".uno:RowColSelCount"),
+ OUString(".uno:StatusPageStyle"),
+ OUString(".uno:InsertMode"),
+ OUString(".uno:SpellOnline"),
+ OUString(".uno:StatusSelectionMode"),
+ OUString(".uno:StateTableCell"),
+ OUString(".uno:StatusBarFunc"),
+ OUString(".uno:StatePageNumber"),
+ OUString(".uno:StateWordCount"),
+ OUString(".uno:SelectionMode"),
+ OUString(".uno:PageStatus"),
+ OUString(".uno:LayoutStatus"),
+ OUString(".uno:Context"),
+ OUString(".uno:WrapText"),
+ OUString(".uno:ToggleMergeCells"),
+ OUString(".uno:NumberFormatCurrency"),
+ OUString(".uno:NumberFormatPercent"),
+ OUString(".uno:NumberFormatDecimal"),
+ OUString(".uno:NumberFormatDate"),
+ OUString(".uno:FrameLineColor"),
+ OUString(".uno:SortAscending"),
+ OUString(".uno:SortDescending"),
+ OUString(".uno:TrackChanges"),
+ OUString(".uno:ShowTrackedChanges"),
+ OUString(".uno:NextTrackedChange"),
+ OUString(".uno:PreviousTrackedChange"),
+ OUString(".uno:AcceptAllTrackedChanges"),
+ OUString(".uno:RejectAllTrackedChanges"),
+ OUString(".uno:TableDialog"),
+ OUString(".uno:FormatCellDialog"),
+ OUString(".uno:FontDialog"),
+ OUString(".uno:ParagraphDialog"),
+ OUString(".uno:OutlineBullet"),
+ OUString(".uno:InsertIndexesEntry"),
+ OUString(".uno:DocumentRepair"),
+ OUString(".uno:TransformDialog"),
+ OUString(".uno:InsertPageHeader"),
+ OUString(".uno:InsertPageFooter"),
+ OUString(".uno:OnlineAutoFormat"),
+ OUString(".uno:InsertSymbol"),
+ OUString(".uno:EditRegion"),
+ OUString(".uno:ThesaurusDialog"),
+ OUString(".uno:Orientation"),
+ OUString(".uno:ObjectAlignLeft"),
+ OUString(".uno:ObjectAlignRight"),
+ OUString(".uno:AlignCenter"),
+ OUString(".uno:TransformPosX"),
+ OUString(".uno:TransformPosY"),
+ OUString(".uno:TransformWidth"),
+ OUString(".uno:TransformHeight")
+ };
+
+ util::URL aCommandURL;
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
+
+ // check if Frame-Controller were created.
+ if (!pViewFrame)
+ {
+ SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
+ return;
+ }
+
+ if (!xContext.is())
+ xContext = comphelper::getProcessComponentContext();
+ if (!xContext.is())
+ {
+ SAL_WARN("lok", "iniUnoCommands: Component context is not available");
+ return;
+ }
+
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
+ uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
+
+ for (const auto & sUnoCommand : sUnoCommands)
+ {
+ aCommandURL.Complete = sUnoCommand;
+ xParser->parseStrict(aCommandURL);
+
+ // when null, this command is not supported by the given component
+ // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
+ if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
+ {
+ // Initialize slot to dispatch .uno: Command.
+ pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
+ }
+ }
+}
+
+static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getDocumentType");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ try
+ {
+ uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
+
+ if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ {
+ return LOK_DOCTYPE_SPREADSHEET;
+ }
+ else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
+ {
+ return LOK_DOCTYPE_PRESENTATION;
+ }
+ else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
+ {
+ return LOK_DOCTYPE_DRAWING;
+ }
+ else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
+ {
+ return LOK_DOCTYPE_TEXT;
+ }
+ else
+ {
+ SetLastExceptionMsg("unknown document type");
+ }
+ }
+ catch (const uno::Exception& exception)
+ {
+ SetLastExceptionMsg("exception: " + exception.Message);
+ }
+ return LOK_DOCTYPE_OTHER;
+}
+
+static int doc_getParts (LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getParts");
+
+ SolarMutexGuard aGuard;
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return 0;
+ }
+
+ return pDoc->getParts();
+}
+
+static int doc_getPart (LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getPart");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return 0;
+ }
+
+ return pDoc->getPart();
+}
+
+static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
+{
+ comphelper::ProfileZone aZone("doc_setPart");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->setPart( nPart );
+}
+
+static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
+{
+ comphelper::ProfileZone aZone("doc_getPartInfo");
+
+ SolarMutexGuard aGuard;
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ return convertOUString(pDoc->getPartInfo(nPart));
+}
+
+static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
+{
+ SolarMutexGuard aGuard;
+ if (gImpl)
+ gImpl->maLastExceptionMsg.clear();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
+ return;
+ }
+
+ pDoc->selectPart( nPart, nSelect );
+}
+
+static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
+{
+ SolarMutexGuard aGuard;
+ if (gImpl)
+ gImpl->maLastExceptionMsg.clear();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
+ return;
+ }
+
+ pDoc->moveSelectedParts(nPosition, bDuplicate);
+}
+
+static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getPartPageRectangles");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ return convertOUString(pDoc->getPartPageRectangles());
+}
+
+static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
+{
+ comphelper::ProfileZone aZone("doc_getPartName");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ return convertOUString(pDoc->getPartName(nPart));
+}
+
+static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
+{
+ comphelper::ProfileZone aZone("doc_getPartHash");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ return convertOUString(pDoc->getPartHash(nPart));
+}
+
+static void doc_setPartMode(LibreOfficeKitDocument* pThis,
+ int nPartMode)
+{
+ comphelper::ProfileZone aZone("doc_setPartMode");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+
+ int nCurrentPart = pDoc->getPart();
+
+ pDoc->setPartMode(nPartMode);
+
+ // We need to make sure the internal state is updated, just changing the mode
+ // might not update the relevant shells (i.e. impress will keep rendering the
+ // previous mode unless we do this).
+ // TODO: we might want to do this within the relevant components rather than
+ // here, but that's also dependent on how we implement embedded object
+ // rendering I guess?
+ // TODO: we could be clever and e.g. set to 0 when we change to/from
+ // embedded object mode, and not when changing between slide/notes/combined
+ // modes?
+ if ( nCurrentPart < pDoc->getParts() )
+ {
+ pDoc->setPart( nCurrentPart );
+ }
+ else
+ {
+ pDoc->setPart( 0 );
+ }
+}
+
+static void doc_paintTile(LibreOfficeKitDocument* pThis,
+ unsigned char* pBuffer,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight)
+{
+ comphelper::ProfileZone aZone("doc_paintTile");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
+ "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
+ nCanvasWidth << "x" << nCanvasHeight << "]px" );
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+#if defined(UNX) && !defined(MACOSX) && !defined(ENABLE_HEADLESS)
+
+ // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
+ // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
+ // everything is painted bigger or smaller. This is different to what Calc's internal scaling
+ // would do - because that one is trying to fit the lines between cells to integer multiples of
+ // pixels.
+ comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
+
+#if defined(IOS)
+ double fDPIScaleX = 1.0;
+ paintTileIOS(pThis, pBuffer, nCanvasWidth, nCanvasHeight, fDPIScaleX, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+#else
+ ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::DEFAULT);
+
+#if !defined(ANDROID) || HAVE_FEATURE_ANDROID_LOK
+ // Don't set the transparent background in the 'old' (JNI-based) Android
+ // app - no idea why it needs avoiding this.
+ // Set background to transparent by default.
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+#endif
+
+ pDevice->SetOutputSizePixelScaleOffsetAndBuffer(
+ Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
+ pBuffer);
+
+ pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
+ nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+
+ static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
+ if (bDebug)
+ {
+ // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
+ tools::Rectangle aRect(0, 0, 5, 5);
+ aRect = pDevice->PixelToLogic(aRect);
+ pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
+ pDevice->SetFillColor(COL_LIGHTRED);
+ pDevice->SetLineColor();
+ pDevice->DrawRect(aRect);
+ pDevice->Pop();
+ }
+#endif
+
+#else
+ (void) pBuffer;
+#endif
+}
+
+#ifdef IOS
+
+// This function is separate only to be used by LibreOfficeLight. If that app can be retired, this
+// function's code can be inlined.
+static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
+ void* rCGContext,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight)
+{
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ SAL_INFO( "lok.tiledrendering", "paintTileToCGContext: painting [" << nTileWidth << "x" << nTileHeight <<
+ "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
+ nCanvasWidth << "x" << nCanvasHeight << "]px" );
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ Size aCanvasSize(nCanvasWidth, nCanvasHeight);
+ paintTileToCGContext(pDoc, rCGContext, aCanvasSize, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+}
+
+#endif
+
+static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
+ unsigned char* pBuffer,
+ const int nPart,
+ const int nCanvasWidth, const int nCanvasHeight,
+ const int nTilePosX, const int nTilePosY,
+ const int nTileWidth, const int nTileHeight)
+{
+ comphelper::ProfileZone aZone("doc_paintPartTile");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " ["
+ << nTileWidth << "x" << nTileHeight << "]@("
+ << nTilePosX << ", " << nTilePosY << ") to ["
+ << nCanvasWidth << "x" << nCanvasHeight << "]px" );
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+ int nOrigViewId = doc_getView(pThis);
+
+ if (nOrigViewId < 0)
+ {
+ // tile painting always needs a SfxViewShell::Current(), but actually
+ // it does not really matter which one - all of them should paint the
+ // same thing.
+ int viewCount = doc_getViewsCount(pThis);
+ if (viewCount == 0)
+ return;
+
+ std::vector<int> viewIds(viewCount);
+ doc_getViewIds(pThis, viewIds.data(), viewCount);
+
+ nOrigViewId = viewIds[0];
+ doc_setView(pThis, nOrigViewId);
+ }
+
+ // Disable callbacks while we are painting.
+ if (nOrigViewId >= 0)
+ {
+ const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
+ if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
+ handlerIt->second->disableCallbacks();
+ }
+
+ try
+ {
+ // Text documents have a single coordinate system; don't change part.
+ int nOrigPart = 0;
+ const bool isText = (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT);
+ int nViewId = nOrigViewId;
+ if (!isText)
+ {
+ // Check if just switching to another view is enough, that has
+ // less side-effects.
+ if (nPart != doc_getPart(pThis))
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->getPart() == nPart)
+ {
+ nViewId = static_cast<sal_Int32>(pViewShell->GetViewShellId());
+ doc_setView(pThis, nViewId);
+ break;
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+
+ nOrigPart = doc_getPart(pThis);
+ if (nPart != nOrigPart)
+ {
+ doc_setPart(pThis, nPart);
+ }
+ }
+
+ doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+
+ if (!isText && nPart != nOrigPart)
+ {
+ doc_setPart(pThis, nOrigPart);
+ }
+ if (!isText && nViewId != nOrigViewId)
+ {
+ doc_setView(pThis, nOrigViewId);
+ }
+ }
+ catch (const std::exception&)
+ {
+ // Nothing to do but restore the PartTilePainting flag.
+ }
+
+ if (nOrigViewId >= 0)
+ {
+ const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
+ if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
+ handlerIt->second->enableCallbacks();
+ }
+}
+
+static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
+{
+ SetLastExceptionMsg();
+ return LOK_TILEMODE_BGRA;
+}
+
+static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
+ long* pWidth,
+ long* pHeight)
+{
+ comphelper::ProfileZone aZone("doc_getDocumentSize");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (pDoc)
+ {
+ Size aDocumentSize = pDoc->getDocumentSize();
+ *pWidth = aDocumentSize.Width();
+ *pHeight = aDocumentSize.Height();
+ }
+ else
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ }
+}
+
+static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
+ const char* pArguments)
+{
+ comphelper::ProfileZone aZone("doc_initializeForRendering");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (pDoc)
+ {
+ doc_iniUnoCommands();
+ pDoc->initializeForTiledRendering(
+ comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments)));
+ }
+}
+
+static void doc_registerCallback(LibreOfficeKitDocument* pThis,
+ LibreOfficeKitCallback pCallback,
+ void* pData)
+{
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ int nView = SfxLokHelper::getView();
+ if (nView < 0)
+ return;
+
+ if (pCallback != nullptr)
+ {
+ size_t nId = nView;
+ for (auto& pair : pDocument->mpCallbackFlushHandlers)
+ {
+ if (pair.first == nId)
+ continue;
+
+ pair.second->addViewStates(nView);
+ }
+ }
+ else
+ {
+ size_t nId = nView;
+ for (auto& pair : pDocument->mpCallbackFlushHandlers)
+ {
+ if (pair.first == nId)
+ continue;
+
+ pair.second->removeViewStates(nView);
+ }
+ }
+
+ pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData);
+
+ if (pCallback != nullptr)
+ {
+ size_t nId = nView;
+ for (const auto& pair : pDocument->mpCallbackFlushHandlers)
+ {
+ if (pair.first == nId)
+ continue;
+
+ pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
+ }
+ }
+
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->registerLibreOfficeKitViewCallback(CallbackFlushHandler::callback, pDocument->mpCallbackFlushHandlers[nView].get());
+ }
+}
+
+/// Returns the JSON representation of all the comments in the document
+static char* getPostIts(LibreOfficeKitDocument* pThis)
+{
+ SetLastExceptionMsg();
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ OUString aComments = pDoc->getPostIts();
+ return strdup(aComments.toUtf8().getStr());
+}
+
+/// Returns the JSON representation of the positions of all the comments in the document
+static char* getPostItsPos(LibreOfficeKitDocument* pThis)
+{
+ SetLastExceptionMsg();
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ OUString aComments = pDoc->getPostItsPos();
+ return strdup(aComments.toUtf8().getStr());
+}
+
+static char* getRulerState(LibreOfficeKitDocument* pThis)
+{
+ SetLastExceptionMsg();
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ OUString state = pDoc->getRulerState();
+ return strdup(state.toUtf8().getStr());
+}
+
+static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
+{
+ comphelper::ProfileZone aZone("doc_postKeyEvent");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ try
+ {
+ pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
+ }
+ catch (const uno::Exception& exception)
+ {
+ SetLastExceptionMsg(exception.Message);
+ SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message);
+ }
+}
+
+static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
+{
+ comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow;
+ if (nWindowId == 0)
+ {
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+ pWindow = pDoc->getDocWindow();
+ }
+ else
+ {
+ pWindow = vcl::Window::FindLOKWindow(nWindowId);
+ }
+
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
+ return;
+ }
+
+ SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(OString(pText, strlen(pText))));
+}
+
+static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
+{
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow;
+ if (nLOKWindowId == 0)
+ {
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
+ return;
+ }
+ pWindow = pDoc->getDocWindow();
+ }
+ else
+ {
+ pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ }
+
+ if (!pWindow)
+ {
+ gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
+ return;
+ }
+
+ // Annoyingly - backspace and delete are handled in the apps via an accelerator
+ // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
+ // order we do this synchronously here, unless we're in a dialog.
+ if (nCharBefore > 0)
+ {
+ // backspace
+ if (nLOKWindowId == 0)
+ {
+ KeyEvent aEvt(8, 1283);
+ for (int i = 0; i < nCharBefore; ++i)
+ pWindow->KeyInput(aEvt);
+ }
+ else
+ SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
+ }
+
+ if (nCharAfter > 0)
+ {
+ // delete (forward)
+ if (nLOKWindowId == 0)
+ {
+ KeyEvent aEvt(46, 1286);
+ for (int i = 0; i < nCharAfter; ++i)
+ pWindow->KeyInput(aEvt);
+ }
+ else
+ SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
+ }
+}
+
+static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
+{
+ comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+ KeyEvent aEvent(nCharCode, nKeyCode, 0);
+
+ switch (nType)
+ {
+ case LOK_KEYEVENT_KEYINPUT:
+ Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
+ break;
+ case LOK_KEYEVENT_KEYUP:
+ Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
+{
+ comphelper::ProfileZone aZone("doc_renderShapeSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LokChartHelper aChartHelper(SfxViewShell::Current());
+
+ if (aChartHelper.GetWindow())
+ return 0;
+
+ try
+ {
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
+
+ SvMemoryStream aOutStream;
+ uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ switch (doc_getDocumentType(pThis))
+ {
+ case LOK_DOCTYPE_PRESENTATION:
+ aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+ break;
+ case LOK_DOCTYPE_TEXT:
+ aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
+ break;
+ case LOK_DOCTYPE_SPREADSHEET:
+ aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
+ break;
+ default:
+ SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
+ }
+ aMediaDescriptor["SelectionOnly"] <<= true;
+ aMediaDescriptor["OutputStream"] <<= xOut;
+
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+
+ if (pOutput)
+ {
+ const size_t nOutputSize = aOutStream.GetEndOfData();
+ *pOutput = static_cast<char*>(malloc(nOutputSize));
+ if (*pOutput)
+ {
+ std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
+ return nOutputSize;
+ }
+ }
+ }
+ catch (const uno::Exception& exception)
+ {
+ css::uno::Any exAny( cppu::getCaughtException() );
+ SetLastExceptionMsg(exception.Message);
+ SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
+ }
+
+ return 0;
+}
+
+namespace {
+
+/** Class to react on finishing of a dispatched command.
+
+ This will call a LOK_COMMAND_FINISHED callback when postUnoCommand was
+ called with the parameter requesting the notification.
+
+ @see LibreOfficeKitCallbackType::LOK_CALLBACK_UNO_COMMAND_RESULT.
+*/
+class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
+{
+ OString maCommand; ///< Command for which this is the result.
+ std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call.
+
+public:
+ DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> const & pCallback)
+ : maCommand(pCommand)
+ , mpCallback(pCallback)
+ {
+ assert(mpCallback);
+ }
+
+ virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
+ {
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", maCommand.getStr());
+
+ if (rEvent.State != frame::DispatchResultState::DONTKNOW)
+ {
+ bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
+ aTree.put("success", bSuccess);
+ }
+
+ aTree.add_child("result", unoAnyToPropertyTree(rEvent.Result));
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ OString aPayload = aStream.str().c_str();
+ mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
+ }
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
+};
+
+} // anonymous namespace
+
+static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nWindowId, const char* pArguments)
+{
+ SolarMutexGuard aGuard;
+
+ StringMap aMap(jsonToStringMap(pArguments));
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nWindowId);
+
+ if (!pWindow && nWindowId >= 1000000000 /* why unsigned? */)
+ pWindow = getSidebarWindow();
+
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+ else if (aMap.find("id") != aMap.end())
+ {
+ const OUString sClickAction("CLICK");
+ const OUString sSelectAction("SELECT");
+ const OUString sClearAction("CLEAR");
+ const OUString sTypeAction("TYPE");
+ const OUString sUpAction("UP");
+ const OUString sDownAction("DOWN");
+ const OUString sValue("VALUE");
+
+ try
+ {
+ WindowUIObject aUIObject(pWindow);
+ std::unique_ptr<UIObject> pUIWindow(aUIObject.get_child(aMap["id"]));
+ if (pUIWindow) {
+ bool bIsClickAction = false;
+
+ if (aMap.find("cmd") != aMap.end()) {
+ if (aMap["cmd"] == "selected")
+ {
+ aMap["POS"] = aMap["data"];
+ aMap["TEXT"] = aMap["data"];
+
+ pUIWindow->execute(sSelectAction, aMap);
+ }
+ else if (aMap["cmd"] == "plus")
+ {
+ pUIWindow->execute(sUpAction, aMap);
+ }
+ else if (aMap["cmd"] == "minus")
+ {
+ pUIWindow->execute(sDownAction, aMap);
+ }
+ else if (aMap["cmd"] == "set")
+ {
+ aMap["TEXT"] = aMap["data"];
+
+ pUIWindow->execute(sClearAction, aMap);
+ pUIWindow->execute(sTypeAction, aMap);
+ }
+ else if (aMap["cmd"] == "value")
+ {
+ aMap["VALUE"] = aMap["data"];
+ pUIWindow->execute(sValue, aMap);
+ }
+ else if (aMap["cmd"] == "selecttab")
+ {
+ aMap["POS"] = aMap["data"];
+
+ pUIWindow->execute(sSelectAction, aMap);
+ }
+ else
+ bIsClickAction = true;
+ }
+ else
+ bIsClickAction = true;
+
+ if (bIsClickAction)
+ pUIWindow->execute(sClickAction, aMap);
+ }
+ } catch(...) {}
+
+ // force resend
+ pWindow->Resize();
+ }
+}
+
+static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
+{
+ comphelper::ProfileZone aZone("doc_postUnoCommand");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
+
+ if (!vcl::lok::isUnipoll())
+ {
+ beans::PropertyValue aSynchronMode;
+ aSynchronMode.Name = "SynchronMode";
+ aSynchronMode.Value <<= false;
+ aPropertyValuesVector.push_back(aSynchronMode);
+ }
+
+ int nView = SfxLokHelper::getView();
+ if (nView < 0)
+ return;
+
+ if (gImpl && aCommand == ".uno:ToggleOrientation")
+ {
+ ExecuteOrientationChange();
+ return;
+ }
+
+ // handle potential interaction
+ if (gImpl && aCommand == ".uno:Save")
+ {
+ rtl::Reference<LOKInteractionHandler> const pInteraction(
+ new LOKInteractionHandler("save", gImpl, pDocument));
+ uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
+
+ beans::PropertyValue aValue;
+ aValue.Name = "InteractionHandler";
+ aValue.Value <<= xInteraction;
+ aPropertyValuesVector.push_back(aValue);
+
+ bool bDontSaveIfUnmodified = false;
+ aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
+ aPropertyValuesVector.end(),
+ [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
+ if (aItem.Name == "DontSaveIfUnmodified")
+ {
+ bDontSaveIfUnmodified = aItem.Value.get<bool>();
+ return true;
+ }
+ return false;
+ }), aPropertyValuesVector.end());
+
+ // skip saving and tell the result via UNO_COMMAND_RESULT
+ if (bDontSaveIfUnmodified && !pDocSh->IsModified())
+ {
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", pCommand);
+ aTree.put("success", false);
+
+ // Add the reason for not saving
+ const uno::Any aResultValue = uno::makeAny(OUString("unmodified"));
+ aTree.add_child("result", unoAnyToPropertyTree(aResultValue));
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ OString aPayload = aStream.str().c_str();
+ pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
+ return;
+ }
+ }
+ else if (gImpl && aCommand == ".uno:TransformDialog")
+ {
+ bool bNeedConversion = false;
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ LokChartHelper aChartHelper(pViewShell);
+
+ if (aChartHelper.GetWindow() )
+ {
+ bNeedConversion = true;
+ }
+ else if (const SdrView* pView = pViewShell->GetDrawView())
+ {
+ if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
+ {
+ bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
+ }
+ }
+
+ if (bNeedConversion)
+ {
+ sal_Int32 value;
+ for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
+ {
+ if (rPropValue.Name == "TransformPosX"
+ || rPropValue.Name == "TransformPosY"
+ || rPropValue.Name == "TransformWidth"
+ || rPropValue.Name == "TransformHeight"
+ || rPropValue.Name == "TransformRotationX"
+ || rPropValue.Name == "TransformRotationY")
+ {
+ rPropValue.Value >>= value;
+ value = OutputDevice::LogicToLogic(value, MapUnit::MapTwip, MapUnit::Map100thMM);
+ rPropValue.Value <<= value;
+ }
+ }
+ }
+
+ if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0)
+ {
+ if (aPropertyValuesVector[0].Name != "Action")
+ {
+ tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
+ int nLeft = OutputDevice::LogicToLogic(aChartBB.Left(), MapUnit::MapTwip, MapUnit::Map100thMM);
+ int nTop = OutputDevice::LogicToLogic(aChartBB.Top(), MapUnit::MapTwip, MapUnit::Map100thMM);
+
+ for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
+ {
+ if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
+ {
+ auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
+ rPropValue.Value <<= value - nLeft;
+ }
+ else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
+ {
+ auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
+ rPropValue.Value <<= value - nTop;
+ }
+ }
+ }
+ util::URL aCommandURL;
+ aCommandURL.Path = "LOKTransform";
+ css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
+ aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
+ return;
+ }
+ }
+ else if (gImpl && aCommand == ".uno:SidebarShow")
+ {
+ setupSidebar(true);
+ return;
+ }
+ else if (gImpl && aCommand == ".uno:SidebarHide")
+ {
+ setupSidebar(false);
+ return;
+ }
+
+ bool bResult = false;
+ LokChartHelper aChartHelper(SfxViewShell::Current());
+
+ if (aChartHelper.GetWindow() )
+ {
+ util::URL aCommandURL;
+ aCommandURL.Path = aCommand.copy(5);
+ css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
+ aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
+ return;
+ }
+ else if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
+ {
+ bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
+ new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
+ }
+ else
+ bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
+
+ if (!bResult)
+ {
+ SetLastExceptionMsg("Failed to dispatch " + aCommand);
+ }
+}
+
+static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
+{
+ comphelper::ProfileZone aZone("doc_postMouseEvent");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+ try
+ {
+ pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
+ }
+ catch (const uno::Exception& exception)
+ {
+ SetLastExceptionMsg(exception.Message);
+ SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message);
+ }
+}
+
+static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
+{
+ comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+ const Point aPos(nX, nY);
+
+ MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
+
+ vcl::EnableDialogInput(pWindow);
+
+ switch (nType)
+ {
+ case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
+ Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
+ break;
+ case LOK_MOUSEEVENT_MOUSEBUTTONUP:
+ Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
+ break;
+ case LOK_MOUSEEVENT_MOUSEMOVE:
+ Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
+{
+ comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+ OString aType(pType);
+ GestureEventType eEventType = GestureEventType::PanningUpdate;
+
+ if (aType == "panBegin")
+ eEventType = GestureEventType::PanningBegin;
+ else if (aType == "panEnd")
+ eEventType = GestureEventType::PanningEnd;
+
+ GestureEvent aEvent {
+ sal_Int32(nX),
+ sal_Int32(nY),
+ eEventType,
+ sal_Int32(nOffset),
+ PanningOrientation::Vertical,
+ };
+
+ vcl::EnableDialogInput(pWindow);
+
+ Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
+}
+
+static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
+{
+ comphelper::ProfileZone aZone("doc_setTextSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->setTextSelection(nType, nX, nY);
+}
+
+static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY)
+{
+ comphelper::ProfileZone aZone("doc_setWindowTextSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+
+ Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
+ Point aCursorPos(nX, nY);
+ aCursorPos.Move(aOffset);
+ sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT;
+
+ MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier);
+ Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent);
+ Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent);
+}
+
+static bool getFromTransferrable(
+ const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
+ const OString &aInMimeType, OString &aRet);
+
+static bool encodeImageAsHTML(
+ const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
+ const OString &aMimeType, OString &aRet)
+{
+ if (!getFromTransferrable(xTransferable, aMimeType, aRet))
+ return false;
+
+ // Encode in base64.
+ auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
+ aRet.getLength());
+ OUStringBuffer aBase64Data;
+ comphelper::Base64::encode(aBase64Data, aSeq);
+
+ // Embed in HTML.
+ aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+ "<html><head>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
+ "name=\"generator\" content=\""
+ + getGenerator().toUtf8()
+ + "\"/>"
+ "</head><body><img src=\"data:" + aMimeType + ";base64,"
+ + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>";
+
+ return true;
+}
+
+static bool encodeTextAsHTML(
+ const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
+ const OString &aMimeType, OString &aRet)
+{
+ if (!getFromTransferrable(xTransferable, aMimeType, aRet))
+ return false;
+
+ // Embed in HTML - FIXME: needs some escaping.
+ aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+ "<html><head>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
+ "name=\"generator\" content=\""
+ + getGenerator().toUtf8()
+ + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
+
+ return true;
+}
+
+static bool getFromTransferrable(
+ const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
+ const OString &aInMimeType, OString &aRet)
+{
+ OString aMimeType(aInMimeType);
+
+ // Take care of UTF-8 text here.
+ bool bConvert = false;
+ sal_Int32 nIndex = 0;
+ if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
+ {
+ if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
+ {
+ aMimeType = "text/plain;charset=utf-16";
+ bConvert = true;
+ }
+ }
+
+ datatransfer::DataFlavor aFlavor;
+ aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
+ if (aMimeType == "text/plain;charset=utf-16")
+ aFlavor.DataType = cppu::UnoType<OUString>::get();
+ else
+ aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
+
+ if (!xTransferable->isDataFlavorSupported(aFlavor))
+ {
+ // Try harder for HTML it is our copy/paste meta-file format
+ if (aInMimeType == "text/html")
+ {
+ // Desperate measures - convert text to HTML instead.
+ if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
+ return true;
+ // If html is not supported, might be a graphic-selection,
+ if (encodeImageAsHTML(xTransferable, "image/png", aRet))
+ return true;
+ }
+
+ SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
+ return false;
+ }
+
+ uno::Any aAny;
+ try
+ {
+ aAny = xTransferable->getTransferData(aFlavor);
+ }
+ catch (const css::datatransfer::UnsupportedFlavorException& e)
+ {
+ SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
+ return false;
+ }
+ catch (const css::uno::Exception& e)
+ {
+ SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
+ return false;
+ }
+
+ if (aFlavor.DataType == cppu::UnoType<OUString>::get())
+ {
+ OUString aString;
+ aAny >>= aString;
+ if (bConvert)
+ aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
+ else
+ aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
+ }
+ else
+ {
+ uno::Sequence<sal_Int8> aSequence;
+ aAny >>= aSequence;
+ aRet = OString(reinterpret_cast<char*>(aSequence.getArray()), aSequence.getLength());
+ }
+
+ return true;
+}
+
+static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
+{
+ comphelper::ProfileZone aZone("doc_getTextSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
+ if (!xTransferable)
+ {
+ SetLastExceptionMsg("No selection available");
+ return nullptr;
+ }
+
+ const char *pType = pMimeType;
+ if (!pType || pType[0] == '\0')
+ pType = "text/plain;charset=utf-8";
+
+ OString aRet;
+ bool bSuccess = getFromTransferrable(xTransferable, OString(pType), aRet);
+ if (!bSuccess)
+ return nullptr;
+
+ if (pUsedMimeType) // legacy
+ {
+ if (pMimeType)
+ *pUsedMimeType = strdup(pMimeType);
+ else
+ *pUsedMimeType = nullptr;
+ }
+
+ return convertOString(aRet);
+}
+
+static int doc_getSelectionType(LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getSelectionType");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return LOK_SELTYPE_NONE;
+ }
+
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(pDoc->getSelection(), css::uno::UNO_QUERY);
+ if (!xTransferable)
+ {
+ SetLastExceptionMsg("No selection available");
+ return LOK_SELTYPE_NONE;
+ }
+
+ if (xTransferable->isComplex())
+ return LOK_SELTYPE_COMPLEX;
+
+ OString aRet;
+ bool bSuccess = getFromTransferrable(xTransferable, "text/plain;charset=utf-8", aRet);
+ if (!bSuccess)
+ return LOK_SELTYPE_NONE;
+
+ if (aRet.getLength() > 10000)
+ return LOK_SELTYPE_COMPLEX;
+
+ return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE;
+}
+
+static int doc_getClipboard(LibreOfficeKitDocument* pThis,
+ const char **pMimeTypes,
+ size_t *pOutCount,
+ char ***pOutMimeTypes,
+ size_t **pOutSizes,
+ char ***pOutStreams)
+{
+ comphelper::ProfileZone aZone("doc_getClipboard");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ assert (pOutCount);
+ assert (pOutMimeTypes);
+ assert (pOutSizes);
+ assert (pOutStreams);
+
+ *pOutCount = 0;
+ *pOutMimeTypes = nullptr;
+ *pOutSizes = nullptr;
+ *pOutStreams = nullptr;
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return 0;
+ }
+
+ rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
+
+ css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents();
+ SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferrable: " << xTransferable);
+ if (!xTransferable)
+ {
+ SetLastExceptionMsg("No clipboard content available");
+ return 0;
+ }
+
+ std::vector<OString> aMimeTypes;
+ if (!pMimeTypes) // everything
+ {
+ const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors();
+ if (!flavors.getLength())
+ {
+ SetLastExceptionMsg("Flavourless selection");
+ return 0;
+ }
+ for (const auto &it : flavors)
+ aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8));
+ }
+ else
+ {
+ for (size_t i = 0; pMimeTypes[i]; ++i)
+ aMimeTypes.push_back(OString(pMimeTypes[i]));
+ }
+
+ *pOutCount = aMimeTypes.size();
+ *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t)));
+ *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
+ *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
+ for (size_t i = 0; i < aMimeTypes.size(); ++i)
+ {
+ if (aMimeTypes[i] == "text/plain;charset=utf-16")
+ (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8");
+ else
+ (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr());
+
+ OString aRet;
+ bool bSuccess = getFromTransferrable(xTransferable, (*pOutMimeTypes)[i], aRet);
+ if (!bSuccess || aRet.getLength() < 1)
+ {
+ (*pOutSizes)[i] = 0;
+ (*pOutStreams)[i] = nullptr;
+ }
+ else
+ {
+ (*pOutSizes)[i] = aRet.getLength();
+ (*pOutStreams)[i] = convertOString(aRet);
+ }
+ }
+
+ return 1;
+}
+
+static int doc_setClipboard(LibreOfficeKitDocument* pThis,
+ const size_t nInCount,
+ const char **pInMimeTypes,
+ const size_t *pInSizes,
+ const char **pInStreams)
+{
+#ifdef IOS
+ (void) pThis;
+ (void) nInCount;
+ (void) pInMimeTypes;
+ (void) pInSizes;
+ (void) pInStreams;
+#else
+ comphelper::ProfileZone aZone("doc_setClipboard");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return false;
+ }
+
+ uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams));
+
+ auto xClip = forceSetClipboardForCurrentView(pThis);
+ xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
+
+ SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable);
+
+ if (!pDoc->isMimeTypeSupported())
+ {
+ SetLastExceptionMsg("Document doesn't support this mime type");
+ return false;
+ }
+#endif
+ return true;
+}
+
+static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize)
+{
+ comphelper::ProfileZone aZone("doc_paste");
+
+ SolarMutexGuard aGuard;
+
+ const char *pInMimeTypes[1];
+ const char *pInStreams[1];
+ size_t pInSizes[1];
+ pInMimeTypes[0] = pMimeType;
+ pInSizes[0] = nSize;
+ pInStreams[0] = pData;
+
+ if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams))
+ return false;
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AS_CHARACTER))},
+ {"IgnoreComments", uno::makeAny(true)},
+ }));
+ if (!comphelper::dispatchCommand(".uno:Paste", aPropertyValues))
+ {
+ SetLastExceptionMsg("Failed to dispatch the .uno: command");
+ return false;
+ }
+
+ return true;
+}
+
+static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
+{
+ comphelper::ProfileZone aZone("doc_setGraphicSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->setGraphicSelection(nType, nX, nY);
+}
+
+static void doc_resetSelection(LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_resetSelection");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->resetSelection();
+}
+
+static char* getLanguages(const char* pCommand)
+{
+ css::uno::Sequence< css::lang::Locale > aLocales;
+
+ if (xContext.is())
+ {
+ css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
+ if (xLangSrv.is())
+ {
+ css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
+ if (xSpell.is())
+ aLocales = xSpell->getLocales();
+ }
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", pCommand);
+ boost::property_tree::ptree aValues;
+ boost::property_tree::ptree aChild;
+ OUString sLanguage;
+ for ( css::lang::Locale const & locale : std::as_const(aLocales) )
+ {
+ const LanguageTag aLanguageTag( locale );
+ sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
+ if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
+ continue;
+
+ sLanguage += ";" + aLanguageTag.getBcp47(false);
+ aChild.put("", sLanguage.toUtf8());
+ aValues.push_back(std::make_pair("", aChild));
+ }
+ aTree.add_child("commandValues", aValues);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
+ assert(pJson); // Don't handle OOM conditions
+ strcpy(pJson, aStream.str().c_str());
+ pJson[aStream.str().size()] = '\0';
+ return pJson;
+}
+
+static char* getFonts (const char* pCommand)
+{
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
+ pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
+ const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", pCommand);
+ boost::property_tree::ptree aValues;
+ if ( pList )
+ {
+ sal_uInt16 nFontCount = pList->GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nFontCount; ++i)
+ {
+ boost::property_tree::ptree aChildren;
+ const FontMetric& rFontMetric = pList->GetFontName(i);
+ const sal_IntPtr* pAry = pList->GetSizeAry(rFontMetric);
+ sal_uInt16 nSizeCount = 0;
+ while (pAry[nSizeCount])
+ {
+ boost::property_tree::ptree aChild;
+ aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10);
+ aChildren.push_back(std::make_pair("", aChild));
+ nSizeCount++;
+ }
+ aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren);
+ }
+ }
+ aTree.add_child("commandValues", aValues);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
+ assert(pJson); // Don't handle OOM conditions
+ strcpy(pJson, aStream.str().c_str());
+ pJson[aStream.str().size()] = '\0';
+ return pJson;
+}
+
+static char* getFontSubset (const OString& aFontName)
+{
+ OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8));
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
+ pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
+ const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", ".uno:FontSubset");
+ boost::property_tree::ptree aValues;
+
+ if ( pList && !aFoundFont.isEmpty() )
+ {
+ sal_uInt16 nFontCount = pList->GetFontNameCount();
+ sal_uInt16 nItFont = 0;
+ for (; nItFont < nFontCount; ++nItFont)
+ {
+ if (aFoundFont == pList->GetFontName(nItFont).GetFamilyName())
+ {
+ break;
+ }
+ }
+
+ if ( nItFont < nFontCount )
+ {
+ FontCharMapRef xFontCharMap (new FontCharMap());
+ auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
+ const vcl::Font& aFont(pList->GetFontName(nItFont));
+
+ aDevice->SetFont(aFont);
+ aDevice->GetFontCharMap(xFontCharMap);
+ SubsetMap aSubMap(xFontCharMap);
+
+ for (auto const& subset : aSubMap.GetSubsetMap())
+ {
+ boost::property_tree::ptree aChild;
+ aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin())));
+ aValues.push_back(std::make_pair("", aChild));
+ }
+ }
+ }
+
+ aTree.add_child("commandValues", aValues);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
+ assert(pJson); // Don't handle OOM conditions
+ strcpy(pJson, aStream.str().c_str());
+ pJson[aStream.str().size()] = '\0';
+ return pJson;
+}
+
+static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand)
+{
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", pCommand);
+ uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
+ const uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ const uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames();
+
+ static const std::vector<OUString> aWriterStyles =
+ {
+ "Text body",
+ "Quotations",
+ "Title",
+ "Subtitle",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3"
+ };
+
+ // We need to keep a list of the default style names
+ // in order to filter these out later when processing
+ // the full list of styles.
+ std::set<OUString> aDefaultStyleNames;
+
+ boost::property_tree::ptree aValues;
+ for (OUString const & sStyleFam : aStyleFamilies)
+ {
+ boost::property_tree::ptree aChildren;
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY);
+
+ // Writer provides a huge number of styles, we have a list of 7 "default" styles which
+ // should be shown in the normal dropdown, which we should add to the start of the list
+ // to simplify their selection.
+ if (sStyleFam == "ParagraphStyles"
+ && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT)
+ {
+ for (const OUString& rStyle: aWriterStyles)
+ {
+ aDefaultStyleNames.insert( rStyle );
+
+ boost::property_tree::ptree aChild;
+ aChild.put("", rStyle.toUtf8());
+ aChildren.push_back(std::make_pair("", aChild));
+ }
+ }
+
+ const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
+ for (const OUString& rStyle: aStyles )
+ {
+ // Filter out the default styles - they are already at the top
+ // of the list
+ if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() ||
+ (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) )
+ {
+ boost::property_tree::ptree aChild;
+ aChild.put("", rStyle.toUtf8());
+ aChildren.push_back(std::make_pair("", aChild));
+ }
+ }
+ aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren);
+ }
+
+ // Header & Footer Styles
+ {
+ boost::property_tree::ptree aChild;
+ boost::property_tree::ptree aChildren;
+ const OUString sPageStyles("PageStyles");
+ uno::Reference<beans::XPropertySet> xProperty;
+ uno::Reference<container::XNameContainer> xContainer;
+
+ if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer))
+ {
+ const uno::Sequence<OUString> aSeqNames = xContainer->getElementNames();
+ for (OUString const & sName : aSeqNames)
+ {
+ bool bIsPhysical;
+ xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY);
+ if (xProperty.is() && (xProperty->getPropertyValue("IsPhysical") >>= bIsPhysical) && bIsPhysical)
+ {
+ OUString displayName;
+ xProperty->getPropertyValue("DisplayName") >>= displayName;
+ aChild.put("", displayName.toUtf8());
+ aChildren.push_back(std::make_pair("", aChild));
+ }
+ }
+ aValues.add_child("HeaderFooter", aChildren);
+ }
+ }
+
+ {
+ boost::property_tree::ptree aCommandList;
+
+ {
+ boost::property_tree::ptree aChild;
+
+ OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM);
+
+ boost::property_tree::ptree aName;
+ aName.put("", sClearFormat.toUtf8());
+ aChild.push_back(std::make_pair("text", aName));
+
+ boost::property_tree::ptree aCommand;
+ aCommand.put("", ".uno:ResetAttributes");
+ aChild.push_back(std::make_pair("id", aCommand));
+
+ aCommandList.push_back(std::make_pair("", aChild));
+ }
+
+ aValues.add_child("Commands", aCommandList);
+ }
+
+ aTree.add_child("commandValues", aValues);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
+ assert(pJson); // Don't handle OOM conditions
+ strcpy(pJson, aStream.str().c_str());
+ pJson[aStream.str().size()] = '\0';
+ return pJson;
+}
+
+namespace {
+
+enum class UndoOrRedo
+{
+ UNDO,
+ REDO
+};
+
+}
+
+/// Returns the JSON representation of either an undo or a redo stack.
+static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
+{
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
+ if (!pBaseModel)
+ return nullptr;
+
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+ if (!pObjectShell)
+ return nullptr;
+
+ SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager();
+ if (!pUndoManager)
+ return nullptr;
+
+ OUString aString;
+ if (eCommand == UndoOrRedo::UNDO)
+ aString = pUndoManager->GetUndoActionsInfo();
+ else
+ aString = pUndoManager->GetRedoActionsInfo();
+ char* pJson = strdup(aString.toUtf8().getStr());
+ return pJson;
+}
+
+/// Returns the JSON representation of the redline stack.
+static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
+{
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
+ std::stringstream aStream;
+ // We want positions of the track changes also which is not possible from
+ // UNO. Enable positioning information for text documents only for now, so
+ // construct the tracked changes JSON from inside the sw/, not here using UNO
+ if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
+ {
+ uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
+ boost::property_tree::ptree aRedlines;
+ for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
+ {
+ uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
+ boost::property_tree::ptree aRedline;
+ aRedline.put("index", nIndex);
+
+ OUString sAuthor;
+ xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
+ aRedline.put("author", sAuthor.toUtf8().getStr());
+
+ OUString sType;
+ xRedline->getPropertyValue("RedlineType") >>= sType;
+ aRedline.put("type", sType.toUtf8().getStr());
+
+ OUString sComment;
+ xRedline->getPropertyValue("RedlineComment") >>= sComment;
+ aRedline.put("comment", sComment.toUtf8().getStr());
+
+ OUString sDescription;
+ xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
+ aRedline.put("description", sDescription.toUtf8().getStr());
+
+ util::DateTime aDateTime;
+ xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
+ OUString sDateTime = utl::toISO8601(aDateTime);
+ aRedline.put("dateTime", sDateTime.toUtf8().getStr());
+
+ aRedlines.push_back(std::make_pair("", aRedline));
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.add_child("redlines", aRedlines);
+ boost::property_tree::write_json(aStream, aTree);
+ }
+ else
+ {
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ OUString aTrackedChanges = pDoc->getTrackedChanges();
+ aStream << aTrackedChanges.toUtf8();
+ }
+
+ char* pJson = strdup(aStream.str().c_str());
+ return pJson;
+}
+
+
+/// Returns the JSON representation of the redline author table.
+static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
+{
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ OUString aAuthors = pDoc->getTrackedChangeAuthors();
+ return strdup(aAuthors.toUtf8().getStr());
+}
+
+static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
+{
+ comphelper::ProfileZone aZone("doc_getCommandValues");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ OString aCommand(pCommand);
+ static const OString aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
+ static const OString aCellCursor(".uno:CellCursor");
+ static const OString aFontSubset(".uno:FontSubset&name=");
+
+ if (!strcmp(pCommand, ".uno:LanguageStatus"))
+ {
+ return getLanguages(pCommand);
+ }
+ else if (!strcmp(pCommand, ".uno:CharFontName"))
+ {
+ return getFonts(pCommand);
+ }
+ else if (!strcmp(pCommand, ".uno:StyleApply"))
+ {
+ return getStyles(pThis, pCommand);
+ }
+ else if (aCommand == ".uno:Undo")
+ {
+ return getUndoOrRedo(pThis, UndoOrRedo::UNDO);
+ }
+ else if (aCommand == ".uno:Redo")
+ {
+ return getUndoOrRedo(pThis, UndoOrRedo::REDO);
+ }
+ else if (aCommand == ".uno:AcceptTrackedChanges")
+ {
+ return getTrackedChanges(pThis);
+ }
+ else if (aCommand == ".uno:TrackedChangeAuthors")
+ {
+ return getTrackedChangeAuthors(pThis);
+ }
+ else if (aCommand == ".uno:ViewAnnotations")
+ {
+ return getPostIts(pThis);
+ }
+ else if (aCommand == ".uno:ViewAnnotationsPosition")
+ {
+ return getPostItsPos(pThis);
+ }
+ else if (aCommand == ".uno:RulerState")
+ {
+ return getRulerState(pThis);
+ }
+ else if (aCommand.startsWith(aViewRowColumnHeaders))
+ {
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
+ tools::Rectangle aRectangle;
+ if (aCommand.getLength() > aViewRowColumnHeaders.getLength())
+ {
+ // Command has parameters.
+ int nX = 0;
+ int nY = 0;
+ int nWidth = 0;
+ int nHeight = 0;
+ OString aArguments = aCommand.copy(aViewRowColumnHeaders.getLength() + 1);
+ sal_Int32 nParamIndex = 0;
+ do
+ {
+ OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
+ sal_Int32 nIndex = 0;
+ OString aKey;
+ OString aValue;
+ do
+ {
+ OString aToken = aParamToken.getToken(0, '=', nIndex);
+ if (!aKey.getLength())
+ aKey = aToken;
+ else
+ aValue = aToken;
+ }
+ while (nIndex >= 0);
+ if (aKey == "x")
+ nX = aValue.toInt32();
+ else if (aKey == "y")
+ nY = aValue.toInt32();
+ else if (aKey == "width")
+ nWidth = aValue.toInt32();
+ else if (aKey == "height")
+ nHeight = aValue.toInt32();
+ }
+ while (nParamIndex >= 0);
+
+ aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
+ }
+
+ OUString aHeaders = pDoc->getRowColumnHeaders(aRectangle);
+ if (aHeaders.isEmpty())
+ return nullptr;
+ else
+ return convertOUString(aHeaders);
+ }
+ else if (aCommand.startsWith(aCellCursor))
+ {
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+ // Ignore command's deprecated parameters.
+ return convertOString(pDoc->getCellCursor());
+ }
+ else if (aCommand.startsWith(aFontSubset))
+ {
+ return getFontSubset(OString(pCommand + aFontSubset.getLength()));
+ }
+ else
+ {
+ SetLastExceptionMsg("Unknown command, no values returned");
+ return nullptr;
+ }
+}
+
+static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight,
+ int nTileTwipWidth, int nTileTwipHeight)
+{
+ comphelper::ProfileZone aZone("doc_setClientZoom");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
+}
+
+static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight)
+{
+ comphelper::ProfileZone aZone("doc_setClientVisibleArea");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
+ pDoc->setClientVisibleArea(aRectangle);
+}
+
+static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden)
+{
+ comphelper::ProfileZone aZone("doc_setOutlineState");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden);
+}
+
+static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis,
+ const char* pOptions)
+{
+ comphelper::ProfileZone aZone("doc_createView");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ OUString aOptions = getUString(pOptions);
+ const OUString aLanguage = extractParameter(aOptions, "Language");
+
+ if (!aLanguage.isEmpty())
+ {
+ // Set the LOK language tag, used for dialog tunneling.
+ comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
+ comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage));
+ }
+
+ const OUString aDeviceFormFactor = extractParameter(aOptions, "DeviceFormFactor");
+ SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
+
+ int nId = SfxLokHelper::createView();
+
+#ifdef IOS
+ (void) pThis;
+#else
+ forceSetClipboardForCurrentView(pThis);
+#endif
+
+ return nId;
+}
+
+static int doc_createView(LibreOfficeKitDocument* pThis)
+{
+ return doc_createViewWithOptions(pThis, nullptr); // No options.
+}
+
+static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
+{
+ comphelper::ProfileZone aZone("doc_destroyView");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LOKClipboardFactory::releaseClipboardForView(nId);
+
+ SfxLokHelper::destroyView(nId);
+}
+
+static void doc_setView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
+{
+ comphelper::ProfileZone aZone("doc_setView");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ SfxLokHelper::setView(nId);
+}
+
+static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
+{
+ comphelper::ProfileZone aZone("doc_getView");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ return SfxLokHelper::getView();
+}
+
+static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
+{
+ comphelper::ProfileZone aZone("doc_getViewsCount");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ return SfxLokHelper::getViewsCount();
+}
+
+static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int* pArray, size_t nSize)
+{
+ comphelper::ProfileZone aZone("doc_getViewsIds");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ return SfxLokHelper::getViewIds(pArray, nSize);
+}
+
+static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language)
+{
+ comphelper::ProfileZone aZone("doc_setViewLanguage");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ OUString sLanguage = OStringToOUString(language, RTL_TEXTENCODING_UTF8);
+ SfxLokHelper::setViewLanguage(nId, sLanguage);
+ SfxLokHelper::setViewLocale(nId, sLanguage);
+}
+
+
+
+unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
+ const char* pFontName,
+ const char* pChar,
+ int* pFontWidth,
+ int* pFontHeight)
+{
+ return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0);
+}
+
+unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/,
+ const char* pFontName,
+ const char* pChar,
+ int* pFontWidth,
+ int* pFontHeight,
+ int pOrientation)
+{
+ comphelper::ProfileZone aZone("doc_renderFont");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ OString aSearchedFontName(pFontName);
+ OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8));
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
+ pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
+ const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
+
+ const int nDefaultFontSize = 25;
+
+ if ( pList )
+ {
+ sal_uInt16 nFontCount = pList->GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nFontCount; ++i)
+ {
+ const FontMetric& rFontMetric = pList->GetFontName(i);
+ const OUString& aFontName = rFontMetric.GetFamilyName();
+ if (aSearchedFontName != aFontName.toUtf8())
+ continue;
+
+ if (aText.isEmpty())
+ aText = rFontMetric.GetFamilyName();
+
+ auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
+ ::tools::Rectangle aRect;
+ vcl::Font aFont(rFontMetric);
+ aFont.SetFontSize(Size(0, nDefaultFontSize));
+ aFont.SetOrientation(pOrientation);
+ aDevice->SetFont(aFont);
+ aDevice->GetTextBoundRect(aRect, aText);
+ if (aRect.IsEmpty())
+ break;
+
+ int nFontWidth = aRect.BottomRight().X() + 1;
+ int nFontHeight = aRect.BottomRight().Y() + 1;
+
+ if (!(nFontWidth > 0 && nFontHeight > 0))
+ break;
+
+ if (*pFontWidth > 0 && *pFontHeight > 0)
+ {
+ double fScaleX = *pFontWidth / static_cast<double>(nFontWidth) / 1.5;
+ double fScaleY = *pFontHeight / static_cast<double>(nFontHeight) / 1.5;
+
+ double fScale = std::min(fScaleX, fScaleY);
+
+ if (fScale >= 1.0)
+ {
+ int nFontSize = fScale * nDefaultFontSize;
+ aFont.SetFontSize(Size(0, nFontSize));
+ aDevice->SetFont(aFont);
+ }
+
+ aRect = tools::Rectangle(0, 0, *pFontWidth, *pFontHeight);
+
+ nFontWidth = *pFontWidth;
+ nFontHeight = *pFontHeight;
+
+ }
+
+ unsigned char* pBuffer = static_cast<unsigned char*>(malloc(4 * nFontWidth * nFontHeight));
+ if (!pBuffer)
+ break;
+
+ memset(pBuffer, 0, nFontWidth * nFontHeight * 4);
+ aDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+ aDevice->SetOutputSizePixelScaleOffsetAndBuffer(
+ Size(nFontWidth, nFontHeight), Fraction(1.0), Point(),
+ pBuffer);
+
+ if (*pFontWidth > 0 && *pFontHeight > 0)
+ {
+ DrawTextFlags const nStyle =
+ DrawTextFlags::Center
+ | DrawTextFlags::VCenter
+ | DrawTextFlags::Bottom
+ | DrawTextFlags::MultiLine
+ | DrawTextFlags::WordBreak;// | DrawTextFlags::WordBreakHyphenation ;
+
+ aDevice->DrawText(aRect, aText, nStyle);
+ }
+ else
+ {
+ *pFontWidth = nFontWidth;
+ *pFontHeight = nFontHeight;
+
+ aDevice->DrawText(Point(0,0), aText);
+ }
+
+
+ return pBuffer;
+ }
+ }
+ return nullptr;
+}
+
+
+static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
+ unsigned char* pBuffer,
+ const int nX, const int nY,
+ const int nWidth, const int nHeight)
+{
+ doc_paintWindowDPI(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, 1.0);
+}
+
+static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
+ unsigned char* pBuffer,
+ const int nX, const int nY,
+ const int nWidth, const int nHeight,
+ const double fDPIScale)
+{
+ doc_paintWindowForView(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, fDPIScale, -1);
+}
+
+static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
+ unsigned char* pBuffer, const int nX, const int nY,
+ const int nWidth, const int nHeight,
+ const double fDPIScale, int viewId)
+{
+ comphelper::ProfileZone aZone("doc_paintWindowDPI");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+ // Used to avoid work in setView if set.
+ comphelper::LibreOfficeKit::setDialogPainting(true);
+
+ if (viewId >= 0)
+ doc_setView(pThis, viewId);
+
+ // Setup cairo (or CoreGraphics, in the iOS case) to draw with the changed DPI scale (and return
+ // back to 1.0 when the painting finishes)
+ comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
+ comphelper::LibreOfficeKit::setDPIScale(fDPIScale);
+
+#if defined(IOS)
+
+ CGContextRef cgc = CGBitmapContextCreate(pBuffer, nWidth, nHeight, 8, nWidth*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little);
+
+ CGContextTranslateCTM(cgc, 0, nHeight);
+ CGContextScaleCTM(cgc, fDPIScale, -fDPIScale);
+
+ SystemGraphicsData aData;
+ aData.rCGContext = cgc;
+
+ ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+
+ pDevice->SetOutputSizePixel(Size(nWidth, nHeight));
+
+ MapMode aMapMode(pDevice->GetMapMode());
+ aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
+ pDevice->SetMapMode(aMapMode);
+
+ pWindow->PaintToDevice(pDevice.get(), Point(0, 0));
+
+ CGContextRelease(cgc);
+
+#else
+
+ ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
+ pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
+
+ pDevice->SetOutputSizePixelScaleOffsetAndBuffer(Size(nWidth, nHeight), Fraction(1.0), Point(), pBuffer);
+
+ MapMode aMapMode(pDevice->GetMapMode());
+ aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
+ pDevice->SetMapMode(aMapMode);
+
+ pWindow->PaintToDevice(pDevice.get(), Point(0, 0));
+#endif
+
+ comphelper::LibreOfficeKit::setDialogPainting(false);
+}
+
+static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nAction, const char* pData)
+{
+ comphelper::ProfileZone aZone("doc_postWindow");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
+ return;
+ }
+
+ if (nAction == LOK_WINDOW_CLOSE)
+ {
+ bool bWasDialog = vcl::CloseDialog(pWindow);
+ if (!bWasDialog)
+ {
+ if (FloatingWindow* pFloatWin = dynamic_cast<FloatingWindow*>(pWindow.get()))
+ pFloatWin->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
+ }
+ }
+ else if (nAction == LOK_WINDOW_PASTE)
+ {
+ OUString aMimeType;
+ css::uno::Sequence<sal_Int8> aData;
+ std::vector<beans::PropertyValue> aArgs(jsonToPropertyValuesVector(pData));
+ {
+ aArgs.size() == 2 &&
+ aArgs[0].Name == "MimeType" && (aArgs[0].Value >>= aMimeType) &&
+ aArgs[1].Name == "Data" && (aArgs[1].Value >>= aData);
+ }
+
+ if (!aMimeType.isEmpty() && aData.hasElements())
+ {
+ uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(aMimeType, aData));
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard);
+ xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
+ pWindow->SetClipboard(xClipboard);
+
+ KeyEvent aEvent(0, KEY_PASTE, 0);
+ Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
+ }
+ else
+ SetLastExceptionMsg("Window command 'paste': wrong parameters.");
+ }
+}
+
+// CERTIFICATE AND DOCUMENT SIGNING
+static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary, const int nCertificateBinarySize,
+ const unsigned char* pPrivateKeyBinary, const int nPrivateKeySize)
+{
+ comphelper::ProfileZone aZone("doc_insertCertificate");
+
+ if (!xContext.is())
+ return false;
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ if (!pDocument->mxComponent.is())
+ return false;
+
+ SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
+ if (!pBaseModel)
+ return false;
+
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+
+ if (!pObjectShell)
+ return false;
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+ uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
+
+ if (!xCertificateCreator.is())
+ return false;
+
+ uno::Sequence<sal_Int8> aCertificateSequence;
+
+ std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
+ std::string aCertificateBase64String = extractCertificate(aCertificateString);
+ if (!aCertificateBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
+ comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
+ }
+ else
+ {
+ aCertificateSequence.realloc(nCertificateBinarySize);
+ std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
+ }
+
+ uno::Sequence<sal_Int8> aPrivateKeySequence;
+ std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeySize);
+ std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
+ if (!aPrivateKeyBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
+ comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
+ }
+ else
+ {
+ aPrivateKeySequence.realloc(nPrivateKeySize);
+ std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeySize, aPrivateKeySequence.begin());
+ }
+
+ uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
+
+ if (!xCertificate.is())
+ return false;
+
+ SolarMutexGuard aGuard;
+
+ return pObjectShell->SignDocumentContentUsingCertificate(xCertificate);
+}
+
+static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary, const int nCertificateBinarySize)
+{
+ comphelper::ProfileZone aZone("doc_addCertificate");
+
+ if (!xContext.is())
+ return false;
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ if (!pDocument->mxComponent.is())
+ return false;
+
+ SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
+ if (!pBaseModel)
+ return false;
+
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+
+ if (!pObjectShell)
+ return false;
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+ uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
+
+ if (!xCertificateCreator.is())
+ return false;
+
+ uno::Sequence<sal_Int8> aCertificateSequence;
+
+ std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
+ std::string aCertificateBase64String = extractCertificate(aCertificateString);
+ if (!aCertificateBase64String.empty())
+ {
+ OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
+ comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
+ }
+ else
+ {
+ aCertificateSequence.realloc(nCertificateBinarySize);
+ std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
+ }
+
+ uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, "TCu,Cu,Tu");
+
+ if (!xCertificate.is())
+ return false;
+
+ SAL_INFO("lok", "Certificate Added = IssuerName: " << xCertificate->getIssuerName() << " SubjectName: " << xCertificate->getSubjectName());
+
+ return true;
+}
+
+static int doc_getSignatureState(LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getSignatureState");
+
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ if (!pDocument->mxComponent.is())
+ return int(SignatureState::UNKNOWN);
+
+ SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
+ if (!pBaseModel)
+ return int(SignatureState::UNKNOWN);
+
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+ if (!pObjectShell)
+ return int(SignatureState::UNKNOWN);
+
+ SolarMutexGuard aGuard;
+
+ pObjectShell->RecheckSignature(false);
+
+ return int(pObjectShell->GetDocumentSignatureState());
+}
+
+static void doc_resizeWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId,
+ const int nWidth, const int nHeight)
+{
+ SolarMutexGuard aGuard;
+ if (gImpl)
+ gImpl->maLastExceptionMsg.clear();
+
+ VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
+ if (!pWindow)
+ {
+ gImpl->maLastExceptionMsg = "Document doesn't support dialog resizing, or window not found.";
+ return;
+ }
+
+ pWindow->SetSizePixel(Size(nWidth, nHeight));
+}
+
+static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char* pFunctionName)
+{
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
+ pDoc->completeFunction(OUString::fromUtf8(pFunctionName));
+}
+
+
+static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis, const char* pArguments)
+{
+ SolarMutexGuard aGuard;
+
+ // Supported in Writer only
+ if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT)
+ return;
+
+ StringMap aMap(jsonToStringMap(pArguments));
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering!");
+ return;
+ }
+
+ // Sanity check
+ if (aMap.find("type") == aMap.end() || aMap.find("cmd") == aMap.end())
+ {
+ SetLastExceptionMsg("Wrong arguments for sendFormFieldEvent!");
+ return;
+ }
+
+ pDoc->executeFromFieldEvent(aMap);
+}
+
+static char* lo_getError (LibreOfficeKit *pThis)
+{
+ comphelper::ProfileZone aZone("lo_getError");
+
+ SolarMutexGuard aGuard;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ return convertOUString(pLib->maLastExceptionMsg);
+}
+
+static void lo_freeError(char* pFree)
+{
+ free(pFree);
+}
+
+static char* lo_getFilterTypes(LibreOfficeKit* pThis)
+{
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LibLibreOffice_Impl* pImpl = static_cast<LibLibreOffice_Impl*>(pThis);
+
+ if (!xSFactory.is())
+ xSFactory = comphelper::getProcessServiceFactory();
+
+ if (!xSFactory.is())
+ {
+ pImpl->maLastExceptionMsg = "Service factory is not available";
+ return nullptr;
+ }
+
+ uno::Reference<container::XNameAccess> xTypeDetection(xSFactory->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
+ const uno::Sequence<OUString> aTypes = xTypeDetection->getElementNames();
+ boost::property_tree::ptree aTree;
+ for (const OUString& rType : aTypes)
+ {
+ uno::Sequence<beans::PropertyValue> aValues;
+ if (xTypeDetection->getByName(rType) >>= aValues)
+ {
+ auto it = std::find_if(aValues.begin(), aValues.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "MediaType"; });
+ OUString aValue;
+ if (it != aValues.end() && (it->Value >>= aValue) && !aValue.isEmpty())
+ {
+ boost::property_tree::ptree aChild;
+ aChild.put("MediaType", aValue.toUtf8());
+ aTree.add_child(rType.toUtf8().getStr(), aChild);
+ }
+ }
+ }
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ return strdup(aStream.str().c_str());
+}
+
+static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long const features)
+{
+ comphelper::ProfileZone aZone("lo_setOptionalFeatures");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ pLib->mOptionalFeatures = features;
+ if (features & LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK)
+ comphelper::LibreOfficeKit::setPartInInvalidation(true);
+ if (features & LOK_FEATURE_NO_TILED_ANNOTATIONS)
+ comphelper::LibreOfficeKit::setTiledAnnotations(false);
+ if (features & LOK_FEATURE_RANGE_HEADERS)
+ comphelper::LibreOfficeKit::setRangeHeaders(true);
+ if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK)
+ comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
+}
+
+static void lo_setDocumentPassword(LibreOfficeKit* pThis,
+ const char* pURL, const char* pPassword)
+{
+ comphelper::ProfileZone aZone("lo_setDocumentPassword");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ assert(pThis);
+ assert(pURL);
+ LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ assert(pLib->mInteractionMap.find(OString(pURL)) != pLib->mInteractionMap.end());
+ pLib->mInteractionMap.find(OString(pURL))->second->SetPassword(pPassword);
+}
+
+static char* lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit* /*pThis*/)
+{
+ SetLastExceptionMsg();
+ const OUString sVersionStrTemplate(
+ "{ "
+ "\"ProductName\": \"%PRODUCTNAME\", "
+ "\"ProductVersion\": \"%PRODUCTVERSION\", "
+ "\"ProductExtension\": \"%PRODUCTEXTENSION\", "
+ "\"BuildId\": \"%BUILDID\" "
+ "}"
+ );
+ return convertOUString(ReplaceStringHookProc(sVersionStrTemplate));
+}
+
+static void aBasicErrorFunc(const OUString& rError, const OUString& rAction)
+{
+ OString aBuffer = "Unexpected dialog: " +
+ OUStringToOString(rAction, RTL_TEXTENCODING_ASCII_US) +
+ " Error: " +
+ OUStringToOString(rError, RTL_TEXTENCODING_ASCII_US);
+
+ fprintf(stderr, "Unexpected basic error dialog '%s'\n", aBuffer.getStr());
+}
+
+static bool initialize_uno(const OUString& aAppProgramURL)
+{
+#ifdef IOS
+ // For iOS we already hardcode the inifile as "rc" in the .app directory.
+ rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("fundamental"));
+ xContext = cppu::defaultBootstrap_InitialComponentContext(aAppProgramURL + "/rc");
+#elif defined MACOSX
+ rtl::Bootstrap::setIniFilename(aAppProgramURL + "/../Resources/" SAL_CONFIGFILE("soffice"));
+ xContext = cppu::defaultBootstrap_InitialComponentContext();
+#else
+ rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("soffice"));
+ xContext = cppu::defaultBootstrap_InitialComponentContext();
+#endif
+
+ if (!xContext.is())
+ {
+ SetLastExceptionMsg("XComponentContext could not be created");
+ SAL_INFO("lok", "XComponentContext could not be created");
+ return false;
+ }
+
+ xFactory = xContext->getServiceManager();
+ if (!xFactory.is())
+ {
+ SetLastExceptionMsg("XMultiComponentFactory could not be created");
+ SAL_INFO("lok", "XMultiComponentFactory could not be created");
+ return false;
+ }
+
+ xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
+ comphelper::setProcessServiceFactory(xSFactory);
+
+ SAL_INFO("lok", "Uno initialized - " << xContext.is());
+
+ // set UserInstallation to user profile dir in test/user-template
+// rtl::Bootstrap aDefaultVars;
+// aDefaultVars.set(OUString("UserInstallation"), aAppProgramURL + "../registry" );
+ // configmgr setup ?
+
+ return true;
+}
+
+// pre-unipoll version.
+static void lo_startmain(void*)
+{
+ osl_setThreadName("lo_startmain");
+
+ if (comphelper::SolarMutex::get())
+ Application::GetSolarMutex().tryToAcquire();
+
+ Application::UpdateMainThread();
+
+ soffice_main();
+
+ Application::ReleaseSolarMutex();
+}
+
+// unipoll version.
+static void lo_runLoop(LibreOfficeKit* /*pThis*/,
+ LibreOfficeKitPollCallback pPollCallback,
+ LibreOfficeKitWakeCallback pWakeCallback,
+ void* pData)
+{
+#if defined(IOS) || defined(ANDROID)
+ Application::GetSolarMutex().acquire();
+#endif
+
+ {
+ SolarMutexGuard aGuard;
+
+ vcl::lok::registerPollCallbacks(pPollCallback, pWakeCallback, pData);
+ Application::UpdateMainThread();
+ soffice_main();
+ }
+#if defined(IOS) || defined(ANDROID)
+ vcl::lok::unregisterPollCallbacks();
+ Application::ReleaseSolarMutex();
+#endif
+}
+
+static bool bInitialized = false;
+
+static void lo_status_indicator_callback(void *data, comphelper::LibreOfficeKit::statusIndicatorCallbackType type, int percent)
+{
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(data);
+
+ if (!pLib->mpCallback)
+ return;
+
+ switch (type)
+ {
+ case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Start:
+ pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_START, nullptr, pLib->mpCallbackData);
+ break;
+ case comphelper::LibreOfficeKit::statusIndicatorCallbackType::SetValue:
+ pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE,
+ OUString(OUString::number(percent)).toUtf8().getStr(), pLib->mpCallbackData);
+ break;
+ case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Finish:
+ pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_FINISH, nullptr, pLib->mpCallbackData);
+ break;
+ }
+}
+
+/// Used only by LibreOfficeKit when used by Online to pre-initialize
+static void preloadData()
+{
+ comphelper::ProfileZone aZone("preload data");
+
+ // Create user profile in the temp directory for loading the dictionaries
+ OUString sUserPath;
+ rtl::Bootstrap::get("UserInstallation", sUserPath);
+ utl::TempFile aTempDir(nullptr, true);
+ aTempDir.EnableKillingFile();
+ rtl::Bootstrap::set("UserInstallation", aTempDir.GetURL());
+
+ // Register the bundled extensions
+ desktop::Desktop::SynchronizeExtensionRepositories(true);
+ bool bAbort = desktop::Desktop::CheckExtensionDependencies();
+ if(bAbort)
+ std::cerr << "CheckExtensionDependencies failed" << std::endl;
+
+ // preload all available dictionaries
+ css::uno::Reference<css::linguistic2::XLinguServiceManager> xLngSvcMgr =
+ css::linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext());
+ css::uno::Reference<linguistic2::XSpellChecker> xSpellChecker(xLngSvcMgr->getSpellChecker());
+
+ std::cerr << "Preloading dictionaries: ";
+ css::uno::Reference<linguistic2::XSupportedLocales> xSpellLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
+ uno::Sequence< css::lang::Locale > aLocales = xSpellLocales->getLocales();
+ for (auto &it : aLocales)
+ {
+ std::cerr << it.Language << "_" << it.Country << " ";
+ css::beans::PropertyValues aNone;
+ xSpellChecker->isValid("forcefed", it, aNone);
+ }
+ std::cerr << "\n";
+
+ // Hack to load and cache the module liblocaledata_others.so which is not loaded normally
+ // (when loading dictionaries of just non-Asian locales). Creating a XCalendar4 of one Asian locale
+ // will cheaply load this missing "others" locale library. Appending an Asian locale in
+ // LOK_WHITELIST_LANGUAGES env-var also works but at the cost of loading that dictionary.
+ css::uno::Reference< css::i18n::XCalendar4 > xCal = css::i18n::LocaleCalendar2::create(comphelper::getProcessComponentContext());
+ css::lang::Locale aAsianLocale = {"hi", "IN", ""};
+ xCal->loadDefaultCalendar(aAsianLocale);
+
+ // preload all available thesauri
+ css::uno::Reference<linguistic2::XThesaurus> xThesaurus(xLngSvcMgr->getThesaurus());
+ css::uno::Reference<linguistic2::XSupportedLocales> xThesLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
+ aLocales = xThesLocales->getLocales();
+ std::cerr << "Preloading thesauri: ";
+ for (auto &it : aLocales)
+ {
+ std::cerr << it.Language << "_" << it.Country << " ";
+ css::beans::PropertyValues aNone;
+ xThesaurus->queryMeanings("forcefed", it, aNone);
+ }
+ std::cerr << "\n";
+
+ css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create(
+ comphelper::getProcessComponentContext());
+ xGlobalCfg->getAllKeyEvents();
+
+ std::cerr << "Preload icons\n";
+ ImageTree &images = ImageTree::get();
+ images.getImageUrl("forcefed.png", "style", "FO_oo");
+
+ std::cerr << "Preload languages\n";
+
+ // force load language singleton
+ SvtLanguageTable::HasLanguageType(LANGUAGE_SYSTEM);
+ (void)LanguageTag::isValidBcp47("foo", nullptr);
+
+ std::cerr << "Preload fonts\n";
+
+ // Initialize fonts.
+ css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
+ if (xLangSrv.is())
+ {
+ css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
+ if (xSpell.is())
+ aLocales = xSpell->getLocales();
+ }
+
+ for (const auto& aLocale : std::as_const(aLocales))
+ {
+ //TODO: Add more types and cache more aggressively. For now this initializes the fontcache.
+ using namespace ::com::sun::star::i18n::ScriptType;
+ LanguageType nLang;
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), LATIN);
+ OutputDevice::GetDefaultFont(DefaultFontType::LATIN_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), ASIAN);
+ OutputDevice::GetDefaultFont(DefaultFontType::CJK_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), COMPLEX);
+ OutputDevice::GetDefaultFont(DefaultFontType::CTL_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
+ }
+
+ // Set user profile's path back to the original one
+ rtl::Bootstrap::set("UserInstallation", sUserPath);
+}
+
+namespace {
+
+class ProfileZoneDumper : public AutoTimer
+{
+ static const int dumpTimeoutMS = 5000;
+public:
+ ProfileZoneDumper() : AutoTimer( "zone dumper" )
+ {
+ SetTimeout(dumpTimeoutMS);
+ Start();
+ }
+ virtual void Invoke() override
+ {
+ const css::uno::Sequence<OUString> aEvents =
+ comphelper::ProfileRecording::getRecordingAndClear();
+ OStringBuffer aOutput;
+ for (const auto &s : aEvents)
+ {
+ aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8));
+ aOutput.append("\n");
+ }
+ OString aChunk = aOutput.makeStringAndClear();
+ if (gImpl && gImpl->mpCallback)
+ gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData);
+ }
+};
+
+}
+
+static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char* pUserProfileUrl)
+{
+ enum {
+ PRE_INIT, // setup shared data in master process
+ SECOND_INIT, // complete init. after fork
+ FULL_INIT // do a standard complete init.
+ } eStage;
+
+ // Did we do a pre-initialize
+ static bool bPreInited = false;
+ static bool bUnipoll = false;
+ static bool bProfileZones = false;
+
+ { // cf. string lifetime for preinit
+ std::vector<OUString> aOpts;
+
+ // ':' delimited options - avoiding ABI change for new parameters
+ const char *pOptions = getenv("SAL_LOK_OPTIONS");
+ if (pOptions)
+ aOpts = comphelper::string::split(OUString(pOptions, strlen(pOptions), RTL_TEXTENCODING_UTF8), ':');
+ for (const auto &it : aOpts)
+ {
+ if (it == "unipoll")
+ bUnipoll = true;
+ else if (it == "profile_events")
+ bProfileZones = true;
+ else if (it == "sc_no_grid_bg")
+ comphelper::LibreOfficeKit::setCompatFlag(
+ comphelper::LibreOfficeKit::Compat::scNoGridBackground);
+ }
+ }
+
+ // What stage are we at ?
+ if (pThis == nullptr)
+ eStage = PRE_INIT;
+ else if (bPreInited)
+ eStage = SECOND_INIT;
+ else
+ eStage = FULL_INIT;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+
+ if (bInitialized)
+ return 1;
+
+ // Turn profile zones on early
+ if (bProfileZones && eStage == SECOND_INIT)
+ {
+ comphelper::ProfileRecording::startRecording(true);
+ new ProfileZoneDumper();
+ }
+
+ comphelper::ProfileZone aZone("lok-init");
+
+ if (eStage == PRE_INIT)
+ rtl_alloc_preInit(true);
+ else if (eStage == SECOND_INIT)
+ rtl_alloc_preInit(false);
+
+ if (eStage != SECOND_INIT)
+ comphelper::LibreOfficeKit::setActive();
+
+ if (eStage != PRE_INIT)
+ comphelper::LibreOfficeKit::setStatusIndicatorCallback(lo_status_indicator_callback, pLib);
+
+ if (pUserProfileUrl && eStage != PRE_INIT)
+ {
+ OUString url(
+ pUserProfileUrl, strlen(pUserProfileUrl), RTL_TEXTENCODING_UTF8);
+ OUString path;
+ if (url.startsWithIgnoreAsciiCase("vnd.sun.star.pathname:", &path))
+ {
+ OUString url2;
+ osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath(
+ path, url2);
+ if (e == osl::FileBase::E_None)
+ url = url2;
+ else
+ SAL_WARN("lok", "resolving <" << url << "> failed with " << +e);
+ }
+ rtl::Bootstrap::set("UserInstallation", url);
+ if (eStage == SECOND_INIT)
+ utl::Bootstrap::reloadData();
+ }
+
+ OUString aAppPath;
+ if (pAppPath)
+ {
+ aAppPath = OUString(pAppPath, strlen(pAppPath), RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+#ifdef ANDROID
+ aAppPath = OUString::fromUtf8(lo_get_app_data_dir()) + "/program";
+#else
+ // Fun conversion dance back and forth between URLs and system paths...
+ OUString aAppURL;
+ ::osl::Module::getUrlFromAddress( reinterpret_cast< oslGenericFunction >(lo_initialize),
+ aAppURL);
+ osl::FileBase::getSystemPathFromFileURL( aAppURL, aAppPath );
+#endif
+
+#ifdef IOS
+ // The above gives something like
+ // "/private/var/containers/Bundle/Application/953AA851-CC15-4C60-A2CB-C2C6F24E6F71/Foo.app/Foo",
+ // and we want to drop the final component (the binary name).
+ sal_Int32 lastSlash = aAppPath.lastIndexOf('/');
+ assert(lastSlash > 0);
+ aAppPath = aAppPath.copy(0, lastSlash);
+#endif
+ }
+
+ OUString aAppURL;
+ if (osl::FileBase::getFileURLFromSystemPath(aAppPath, aAppURL) != osl::FileBase::E_None)
+ return 0;
+
+#ifdef IOS
+ // A LibreOffice-using iOS app should have the ICU data file in the app bundle. Initialize ICU
+ // to use that.
+ NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
+
+ int fd = open([[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String], O_RDONLY);
+ if (fd == -1)
+ NSLog(@"Could not open ICU data file %s", [[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String]);
+ else
+ {
+ struct stat st;
+ if (fstat(fd, &st) == -1)
+ NSLog(@"fstat on ICU data file failed: %s", strerror(errno));
+ else
+ {
+ void *icudata = mmap(0, (size_t) st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
+ if (icudata == MAP_FAILED)
+ NSLog(@"mmap failed: %s", strerror(errno));
+ else
+ {
+ UErrorCode icuStatus = U_ZERO_ERROR;
+ udata_setCommonData(icudata, &icuStatus);
+ if (U_FAILURE(icuStatus))
+ NSLog(@"udata_setCommonData failed");
+ else
+ {
+ // Quick test that ICU works...
+ UConverter *cnv = ucnv_open("iso-8859-3", &icuStatus);
+ if (U_SUCCESS(icuStatus))
+ ucnv_close(cnv);
+ else
+ NSLog(@"ucnv_open() failed: %s", u_errorName(icuStatus));
+ }
+ }
+ }
+ close(fd);
+ }
+#endif
+
+ try
+ {
+ if (eStage != SECOND_INIT)
+ {
+ SAL_INFO("lok", "Attempting to initialize UNO");
+
+ if (!initialize_uno(aAppURL))
+ return false;
+
+ // Force headless -- this is only for bitmap rendering.
+ rtl::Bootstrap::set("SAL_USE_VCLPLUGIN", "svp");
+
+ // We specifically need to make sure we have the "headless"
+ // command arg set (various code specifically checks via
+ // CommandLineArgs):
+ desktop::Desktop::GetCommandLineArgs().setHeadless();
+
+#ifdef IOS
+ if (InitVCL() && [NSThread isMainThread])
+ {
+ static bool bFirstTime = true;
+ if (bFirstTime)
+ {
+ Application::GetSolarMutex().release();
+ bFirstTime = false;
+ }
+ }
+ SfxApplication::GetOrCreate();
+#endif
+
+#if HAVE_FEATURE_ANDROID_LOK
+ // Register the bundled extensions - so that the dictionaries work
+ desktop::Desktop::SynchronizeExtensionRepositories(false);
+ bool bFailed = desktop::Desktop::CheckExtensionDependencies();
+ if (bFailed)
+ SAL_INFO("lok", "CheckExtensionDependencies failed");
+#endif
+
+ if (eStage == PRE_INIT)
+ {
+ {
+ comphelper::ProfileZone aInit("Init vcl");
+ std::cerr << "Init vcl\n";
+ InitVCL();
+ }
+
+ // pre-load all graphic libraries.
+ GraphicFilter::GetGraphicFilter().preload();
+
+ // pre-load all component libraries.
+ if (!xContext.is())
+ throw css::uno::DeploymentException("preInit: XComponentContext is not created");
+
+ css::uno::Reference< css::uno::XInterface > xService;
+ xContext->getValueByName("/singletons/com.sun.star.lang.theServiceManager") >>= xService;
+ if (!xService.is())
+ throw css::uno::DeploymentException("preInit: XMultiComponentFactory is not created");
+
+ css::uno::Reference<css::lang::XInitialization> aService(
+ xService, css::uno::UNO_QUERY_THROW);
+
+ // pre-requisites:
+ // In order to load implementations and invoke
+ // component factory it is required:
+ // 1) defaultBootstrap_InitialComponentContext()
+ // 2) comphelper::setProcessServiceFactory(xSFactory);
+ // 3) InitVCL()
+ {
+ comphelper::ProfileZone aInit("preload");
+ aService->initialize({css::uno::makeAny<OUString>("preload")});
+ }
+ { // Force load some modules
+ comphelper::ProfileZone aInit("preload modules");
+ VclBuilder::preload();
+ VclAbstractDialogFactory::Create();
+ }
+
+ preloadData();
+
+ // Release Solar Mutex, lo_startmain thread should acquire it.
+ Application::ReleaseSolarMutex();
+ }
+
+ setLanguageAndLocale("en-US");
+ }
+
+ if (eStage != PRE_INIT)
+ {
+ SAL_INFO("lok", "Re-initialize temp paths");
+ SvtPathOptions aOptions;
+ OUString aNewTemp;
+ osl::FileBase::getTempDirURL(aNewTemp);
+ aOptions.SetTempPath(aNewTemp);
+ desktop::Desktop::CreateTemporaryDirectory();
+
+ // The RequestHandler is specifically set to be ready when all the other
+ // init in Desktop::Main (run from soffice_main) is done. We can enable
+ // the RequestHandler here (without starting any IPC thread;
+ // shortcutting the invocation in Desktop::Main that would start the IPC
+ // thread), and can then use it to wait until we're definitely ready to
+ // continue.
+
+ SAL_INFO("lok", "Enabling RequestHandler");
+ RequestHandler::Enable(false);
+ SAL_INFO("lok", "Starting soffice_main");
+ RequestHandler::SetReady(false);
+ if (!bUnipoll)
+ {
+ // Start the main thread only in non-unipoll mode (i.e. multithreaded).
+ pLib->maThread = osl_createThread(lo_startmain, nullptr);
+ SAL_INFO("lok", "Waiting for RequestHandler");
+ RequestHandler::WaitForReady();
+ SAL_INFO("lok", "RequestHandler ready -- continuing");
+ }
+ else
+ InitVCL();
+ }
+
+ if (eStage != SECOND_INIT)
+ ErrorRegistry::RegisterDisplay(aBasicErrorFunc);
+
+ SAL_INFO("lok", "LOK Initialized");
+ if (eStage == PRE_INIT)
+ bPreInited = true;
+ else
+ bInitialized = true;
+ }
+ catch (css::uno::Exception& exception)
+ {
+ fprintf(stderr, "Bootstrapping exception '%s'\n",
+ OUStringToOString(exception.Message, RTL_TEXTENCODING_UTF8).getStr());
+ }
+
+ if (eStage == PRE_INIT)
+ {
+ comphelper::ThreadPool::getSharedOptimalPool().shutdown();
+ }
+
+// Turn off quick editing on IOS and ANDROID
+#if defined IOS || defined ANDROID
+ if (officecfg::Office::Impress::Misc::TextObject::QuickEditing::get())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Impress::Misc::TextObject::QuickEditing::set(false, batch);
+ batch->commit();
+ }
+#endif
+
+ return bInitialized;
+}
+
+SAL_JNI_EXPORT
+LibreOfficeKit *libreofficekit_hook_2(const char* install_path, const char* user_profile_url)
+{
+ if (!gImpl)
+ {
+ SAL_INFO("lok", "Create libreoffice object");
+
+ gImpl = new LibLibreOffice_Impl();
+ if (!lo_initialize(gImpl, install_path, user_profile_url))
+ {
+ lo_destroy(gImpl);
+ }
+ }
+ return static_cast<LibreOfficeKit*>(gImpl);
+}
+
+SAL_JNI_EXPORT
+LibreOfficeKit *libreofficekit_hook(const char* install_path)
+{
+ return libreofficekit_hook_2(install_path, nullptr);
+}
+
+SAL_JNI_EXPORT
+int lok_preinit(const char* install_path, const char* user_profile_url)
+{
+ return lo_initialize(nullptr, install_path, user_profile_url);
+}
+
+static void lo_destroy(LibreOfficeKit* pThis)
+{
+ SolarMutexClearableGuard aGuard;
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+ gImpl = nullptr;
+
+ SAL_INFO("lok", "LO Destroy");
+
+ comphelper::LibreOfficeKit::setStatusIndicatorCallback(nullptr, nullptr);
+ uno::Reference <frame::XDesktop2> xDesktop = frame::Desktop::create ( ::comphelper::getProcessComponentContext() );
+ // FIXME: the terminate() call here is a no-op because it detects
+ // that LibreOfficeKit::isActive() and then returns early!
+ bool bSuccess = xDesktop.is() && xDesktop->terminate();
+
+ if (!bSuccess)
+ {
+ bSuccess = GetpApp() && GetpApp()->QueryExit();
+ }
+
+ if (!bSuccess)
+ {
+ Application::Quit();
+ }
+
+ aGuard.clear();
+
+ osl_joinWithThread(pLib->maThread);
+ osl_destroyThread(pLib->maThread);
+
+ delete pLib;
+ bInitialized = false;
+ SAL_INFO("lok", "LO Destroy Done");
+}
+
+#ifdef IOS
+
+// Used by the unmaintained LibreOfficeLight app. Once that has been retired, get rid of this, too.
+
+__attribute__((visibility("default")))
+void temporaryHackToInvokeCallbackHandlers(LibreOfficeKitDocument* pThis)
+{
+ SolarMutexGuard aGuard;
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ int nOrigViewId = doc_getView(pThis);
+
+ if (nOrigViewId >= 0 && pDocument->mpCallbackFlushHandlers[nOrigViewId])
+ {
+ pDocument->mpCallbackFlushHandlers[nOrigViewId]->Invoke();
+ }
+}
+
+#endif
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/lokandroid.cxx b/desktop/source/lib/lokandroid.cxx
new file mode 100644
index 000000000..e800c82b0
--- /dev/null
+++ b/desktop/source/lib/lokandroid.cxx
@@ -0,0 +1,422 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <unistd.h>
+#include <jni.h>
+
+#include <sal/types.h>
+#include <vcl/event.hxx>
+#include <android/log.h>
+
+#include <osl/detail/android-bootstrap.h>
+
+#include <LibreOfficeKit/LibreOfficeKit.h>
+
+/* LibreOfficeKit */
+
+namespace
+{
+
+jfieldID getHandleField(JNIEnv* pEnv, jobject aObject)
+{
+ jclass clazz = pEnv->GetObjectClass(aObject);
+ return pEnv->GetFieldID(clazz, "handle", "Ljava/nio/ByteBuffer;");
+}
+
+template <typename T>
+T* getHandle(JNIEnv* pEnv, jobject aObject)
+{
+ jobject aHandle = pEnv->GetObjectField(aObject, getHandleField(pEnv, aObject));
+ return reinterpret_cast<T*>(pEnv->GetDirectBufferAddress(aHandle));
+}
+
+const char* copyJavaString(JNIEnv* pEnv, jstring aJavaString)
+{
+ const char* pTemp = pEnv->GetStringUTFChars(aJavaString, NULL);
+ const char* pClone = strdup(pTemp);
+ pEnv->ReleaseStringUTFChars(aJavaString, pTemp);
+
+ return pClone;
+}
+
+} // anonymous namespace
+
+extern "C" SAL_JNI_EXPORT jstring JNICALL Java_org_libreoffice_kit_Office_getError
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+ char* pError = pLibreOfficeKit->pClass->getError(pLibreOfficeKit);
+ return pEnv->NewStringUTF(pError);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_destroy
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+ pLibreOfficeKit->pClass->destroy(pLibreOfficeKit);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_destroyAndExit(JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+ pLibreOfficeKit->pClass->destroy(pLibreOfficeKit);
+ // Stopgap fix: _exit() to force the OS to restart the LO activity.
+ // Better than to hang.
+ _exit(0);
+}
+
+namespace
+{
+
+struct CallbackData
+{
+ jmethodID aJavaCallbackMethod;
+ jclass aClass;
+ jobject aObject;
+};
+
+static CallbackData gCallbackData;
+static CallbackData gCallbackDataLOKit;
+
+/**
+ * Handle retrieved callback
+ */
+void messageCallback(int nType, const char* pPayload, void* pData)
+{
+ CallbackData* pCallbackData = (CallbackData*) pData;
+
+ JavaVM* pJavaVM = lo_get_javavm();
+ JNIEnv* pEnv;
+ bool bIsAttached = false;
+
+ int status = pJavaVM->GetEnv((void **) &pEnv, JNI_VERSION_1_6);
+
+ if(status < 0)
+ {
+ status = pJavaVM->AttachCurrentThread(&pEnv, NULL);
+ if(status < 0)
+ {
+ return;
+ }
+ bIsAttached = true;
+ }
+
+ jstring sPayload = pEnv->NewStringUTF(pPayload);
+
+ jvalue aParameter[2];
+ aParameter[0].i = nType;
+ aParameter[1].l = sPayload;
+
+ pEnv->CallVoidMethodA(pCallbackData->aObject, pCallbackData->aJavaCallbackMethod, aParameter);
+
+ pEnv->DeleteLocalRef(sPayload);
+
+ if (bIsAttached)
+ {
+ pJavaVM->DetachCurrentThread();
+ }
+}
+
+} // anonymous namespace
+
+extern "C" SAL_JNI_EXPORT jobject JNICALL Java_org_libreoffice_kit_Office_documentLoadNative
+ (JNIEnv* pEnv, jobject aObject, jstring documentPath)
+{
+ const char* aCloneDocumentPath = copyJavaString(pEnv, documentPath);
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+
+ LibreOfficeKitDocument* pDocument = pLibreOfficeKit->pClass->documentLoad(pLibreOfficeKit, aCloneDocumentPath);
+
+ if (pDocument == NULL)
+ return NULL;
+
+ jobject aHandle = pEnv->NewDirectByteBuffer((void*) pDocument, sizeof(LibreOfficeKitDocument));
+
+ return aHandle;
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_setDocumentPassword
+ (JNIEnv* pEnv, jobject aObject, jstring sUrl, jstring sPassword)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+
+ char const* pUrl = copyJavaString(pEnv, sUrl);
+ if (sPassword == NULL) {
+ pLibreOfficeKit->pClass->setDocumentPassword(pLibreOfficeKit, pUrl, nullptr);
+ } else {
+ char const* pPassword = copyJavaString(pEnv, sPassword);
+ pLibreOfficeKit->pClass->setDocumentPassword(pLibreOfficeKit, pUrl, pPassword);
+ }
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_setOptionalFeatures
+ (JNIEnv* pEnv, jobject aObject, jlong options)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+
+ unsigned long long pOptions = (unsigned long long)options;
+
+ pLibreOfficeKit->pClass->setOptionalFeatures(pLibreOfficeKit, pOptions);
+}
+
+/** Implementation of org.libreoffice.kit.Office.bindMessageCallback method */
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_bindMessageCallback
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
+
+ gCallbackDataLOKit.aObject = (jobject) pEnv->NewGlobalRef(aObject);
+ jclass aClass = pEnv->GetObjectClass(aObject);
+ gCallbackDataLOKit.aClass = (jclass) pEnv->NewGlobalRef(aClass);
+
+ gCallbackDataLOKit.aJavaCallbackMethod = pEnv->GetMethodID(aClass, "messageRetrievedLOKit", "(ILjava/lang/String;)V");
+
+ pLibreOfficeKit->pClass->registerCallback(pLibreOfficeKit, messageCallback, (void*) &gCallbackDataLOKit);
+}
+
+/* Document */
+
+/** Implementation of org.libreoffice.kit.Document.bindMessageCallback method */
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_bindMessageCallback
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ gCallbackData.aObject = (jobject) pEnv->NewGlobalRef(aObject);
+ jclass aClass = pEnv->GetObjectClass(aObject);
+ gCallbackData.aClass = (jclass) pEnv->NewGlobalRef(aClass);
+
+ gCallbackData.aJavaCallbackMethod = pEnv->GetMethodID(aClass, "messageRetrieved", "(ILjava/lang/String;)V");
+
+ pDocument->pClass->registerCallback(pDocument, messageCallback, (void*) &gCallbackData);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_destroy
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->destroy(pDocument);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_setPart
+ (JNIEnv* pEnv, jobject aObject, jint aPart)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->setPart(pDocument, aPart);
+}
+
+extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Document_getPart
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ return (jint) pDocument->pClass->getPart(pDocument);
+}
+
+extern "C" SAL_JNI_EXPORT jstring JNICALL Java_org_libreoffice_kit_Document_getPartPageRectangles
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ char* pRectangles = pDocument->pClass->getPartPageRectangles(pDocument);
+ return pEnv->NewStringUTF(pRectangles);
+}
+
+extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Document_getParts
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ return (jint) pDocument->pClass->getParts(pDocument);
+}
+
+extern "C" SAL_JNI_EXPORT jstring JNICALL Java_org_libreoffice_kit_Document_getPartName
+ (JNIEnv* pEnv, jobject aObject, jint nPartIndex)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ char* pPartName = pDocument->pClass->getPartName(pDocument, nPartIndex);
+ return pEnv->NewStringUTF(pPartName);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_setPartMode
+ (JNIEnv* pEnv, jobject aObject, jint nPartMode)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ pDocument->pClass->setPartMode(pDocument, nPartMode);
+}
+
+extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Document_getDocumentTypeNative
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ return (jint) pDocument->pClass->getDocumentType(pDocument);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_paintTileNative
+ (JNIEnv* pEnv, jobject aObject, jobject aByteBuffer,
+ jint nCanvasWidth, jint nCanvasHeight, jint nTilePosX, jint nTilePosY,
+ jint nTileWidth, jint nTileHeight)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ unsigned char* buffer = (unsigned char*) pEnv->GetDirectBufferAddress(aByteBuffer);
+ pDocument->pClass->paintTile(pDocument, buffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
+}
+
+extern "C" SAL_JNI_EXPORT jlong JNICALL Java_org_libreoffice_kit_Document_getDocumentHeight
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ long nWidth;
+ long nHeight;
+ pDocument->pClass->getDocumentSize(pDocument, &nWidth, &nHeight);
+ return nHeight;
+}
+
+extern "C" SAL_JNI_EXPORT jlong JNICALL Java_org_libreoffice_kit_Document_getDocumentWidth
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ long nWidth;
+ long nHeight;
+ pDocument->pClass->getDocumentSize(pDocument, &nWidth, &nHeight);
+ return nWidth;
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_initializeForRendering
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->initializeForRendering(pDocument, NULL);
+}
+
+extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Document_saveAs
+ (JNIEnv* pEnv, jobject aObject, jstring sUrl, jstring sFormat, jstring sOptions)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ const char* pUrl = pEnv->GetStringUTFChars(sUrl, NULL);
+ const char* pFormat = pEnv->GetStringUTFChars(sFormat, NULL);
+ const char* pOptions = pEnv->GetStringUTFChars(sOptions, NULL);
+
+ int result = pDocument->pClass->saveAs(pDocument, pUrl, pFormat, pOptions);
+
+ pEnv->ReleaseStringUTFChars(sUrl, pUrl);
+ pEnv->ReleaseStringUTFChars(sFormat, pFormat);
+ pEnv->ReleaseStringUTFChars(sOptions, pOptions);
+
+ return result;
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_postKeyEvent
+ (JNIEnv* pEnv, jobject aObject, jint nType, jint nCharCode, jint nKeyCode)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->postKeyEvent(pDocument, nType, nCharCode, nKeyCode);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_postMouseEvent
+ (JNIEnv* pEnv, jobject aObject, jint type, jint x, jint y, jint count, jint button, jint modifier)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->postMouseEvent(pDocument, type, x, y, count, button, modifier);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_postUnoCommand
+ (JNIEnv* pEnv, jobject aObject, jstring command, jstring arguments, jboolean bNotifyWhenFinished)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ const char* pCommand = pEnv->GetStringUTFChars(command, NULL);
+ const char* pArguments = nullptr;
+ if (arguments != NULL)
+ pArguments = pEnv->GetStringUTFChars(arguments, NULL);
+
+ pDocument->pClass->postUnoCommand(pDocument, pCommand, pArguments, bNotifyWhenFinished);
+
+ pEnv->ReleaseStringUTFChars(command, pCommand);
+ if (arguments != NULL)
+ pEnv->ReleaseStringUTFChars(arguments, pArguments);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_setTextSelection
+ (JNIEnv* pEnv, jobject aObject, jint type, jint x, jint y)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->setTextSelection(pDocument, type, x, y);
+}
+
+extern "C" SAL_JNI_EXPORT jstring JNICALL Java_org_libreoffice_kit_Document_getTextSelection
+ (JNIEnv* pEnv, jobject aObject, jstring mimeType)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ const char* pMimeType = pEnv->GetStringUTFChars(mimeType, NULL);
+
+ char* pUsedMimeType = 0;
+ LibreOfficeKitDocumentClass* pcls = pDocument->pClass;
+ char* pSelection = pcls->getTextSelection(pDocument, pMimeType, &pUsedMimeType);
+ free(pUsedMimeType);
+
+ pEnv->ReleaseStringUTFChars(mimeType, pMimeType);
+
+ return pEnv->NewStringUTF(pSelection);
+}
+
+extern "C" SAL_JNI_EXPORT jboolean JNICALL Java_org_libreoffice_kit_Document_paste
+ (JNIEnv* pEnv, jobject aObject, jstring mimeType, jstring data)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ const char* pMimeType = pEnv->GetStringUTFChars(mimeType, NULL);
+ const char* pData = pEnv->GetStringUTFChars(data, NULL);
+ const size_t nSize = pEnv->GetStringLength(data);
+
+ LibreOfficeKitDocumentClass* pcls = pDocument->pClass;
+ bool result = pcls->paste(pDocument, pMimeType, pData, nSize);
+ pEnv->ReleaseStringUTFChars(mimeType, pMimeType);
+ pEnv->ReleaseStringUTFChars(data, pData);
+
+ return result;
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_setGraphicSelection
+ (JNIEnv* pEnv, jobject aObject, jint type, jint x, jint y)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->setGraphicSelection(pDocument, type, x, y);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_resetSelection
+ (JNIEnv* pEnv, jobject aObject)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->resetSelection(pDocument);
+}
+
+extern "C" SAL_JNI_EXPORT jstring JNICALL Java_org_libreoffice_kit_Document_getCommandValues
+ (JNIEnv* pEnv, jobject aObject, jstring command)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+
+ const char* pCommand = pEnv->GetStringUTFChars(command, NULL);
+
+ char* pValue = pDocument->pClass->getCommandValues(pDocument, pCommand);
+
+ pEnv->ReleaseStringUTFChars(command, pCommand);
+
+ return pEnv->NewStringUTF(pValue);
+}
+
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_setClientZoom
+ (JNIEnv* pEnv, jobject aObject, jint nTilePixelWidth, jint nTilePixelHeight, jint nTileTwipWidth, jint nTileTwipHeight)
+{
+ LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+ pDocument->pClass->setClientZoom(pDocument, nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx
new file mode 100644
index 000000000..7efaca9ab
--- /dev/null
+++ b/desktop/source/lib/lokclipboard.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "lokclipboard.hxx"
+#include <unordered_map>
+#include <vcl/lazydelete.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+/* static */ osl::Mutex LOKClipboardFactory::gMutex;
+static vcl::DeleteOnDeinit<std::unordered_map<int, rtl::Reference<LOKClipboard>>>
+gClipboards(new std::unordered_map<int, rtl::Reference<LOKClipboard>>);
+
+rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView()
+{
+ int nViewId = SfxLokHelper::getView(); // currently active.
+
+ osl::MutexGuard aGuard(gMutex);
+
+ auto it = gClipboards.get()->find(nViewId);
+ if (it != gClipboards.get()->end())
+ {
+ SAL_INFO("lok", "Got clip: " << it->second.get() << " from " << nViewId);
+ return it->second;
+ }
+ rtl::Reference<LOKClipboard> xClip(new LOKClipboard());
+ (*gClipboards.get())[nViewId] = xClip;
+ SAL_INFO("lok", "Created clip: " << xClip.get() << " for viewId " << nViewId);
+ return xClip;
+}
+
+void LOKClipboardFactory::releaseClipboardForView(int nViewId)
+{
+ osl::MutexGuard aGuard(gMutex);
+
+ if (nViewId < 0) // clear all
+ {
+ gClipboards.get()->clear();
+ SAL_INFO("lok", "Released all clipboards on doc destroy\n");
+ }
+ else if (gClipboards.get())
+ {
+ auto it = gClipboards.get()->find(nViewId);
+ if (it != gClipboards.get()->end())
+ {
+ SAL_INFO("lok", "Releasing clip: " << it->second.get() << " for destroyed " << nViewId);
+ gClipboards.get()->erase(it);
+ }
+ }
+}
+
+uno::Reference<uno::XInterface>
+ SAL_CALL LOKClipboardFactory::createInstanceWithArguments(const Sequence<Any>& /* rArgs */)
+{
+ return uno::Reference<uno::XInterface>(
+ static_cast<cppu::OWeakObject*>(getClipboardForCurView().get()));
+}
+
+LOKClipboard::LOKClipboard()
+ : cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard,
+ css::lang::XServiceInfo>(m_aMutex)
+{
+ // Encourage 'paste' menu items to always show up.
+ uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable());
+ setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
+}
+
+Sequence<OUString> LOKClipboard::getSupportedServiceNames_static()
+{
+ Sequence<OUString> aRet{ "com.sun.star.datatransfer.clipboard.SystemClipboard" };
+ return aRet;
+}
+
+OUString LOKClipboard::getImplementationName() { return "com.sun.star.datatransfer.LOKClipboard"; }
+
+Sequence<OUString> LOKClipboard::getSupportedServiceNames()
+{
+ return getSupportedServiceNames_static();
+}
+
+sal_Bool LOKClipboard::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Reference<css::datatransfer::XTransferable> LOKClipboard::getContents() { return m_xTransferable; }
+
+void LOKClipboard::setContents(
+ const Reference<css::datatransfer::XTransferable>& xTrans,
+ const Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ Reference<datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner);
+ Reference<datatransfer::XTransferable> xOldContents(m_xTransferable);
+ m_xTransferable = xTrans;
+ m_aOwner = xClipboardOwner;
+
+ std::vector<Reference<datatransfer::clipboard::XClipboardListener>> aListeners(m_aListeners);
+ datatransfer::clipboard::ClipboardEvent aEv;
+ aEv.Contents = m_xTransferable;
+ SAL_INFO("lok", "Clip: " << this << " set contents to " << m_xTransferable);
+
+ aGuard.clear();
+
+ if (xOldOwner.is() && xOldOwner != xClipboardOwner)
+ xOldOwner->lostOwnership(this, xOldContents);
+ for (auto const& listener : aListeners)
+ {
+ listener->changedContents(aEv);
+ }
+}
+
+void LOKClipboard::addClipboardListener(
+ const Reference<datatransfer::clipboard::XClipboardListener>& listener)
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ m_aListeners.push_back(listener);
+}
+
+void LOKClipboard::removeClipboardListener(
+ const Reference<datatransfer::clipboard::XClipboardListener>& listener)
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), listener),
+ m_aListeners.end());
+}
+LOKTransferable::LOKTransferable(const OUString& sMimeType,
+ const css::uno::Sequence<sal_Int8>& aSequence)
+{
+ m_aContent.reserve(1);
+ m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1);
+ initFlavourFromMime(m_aFlavors[0], sMimeType);
+
+ uno::Any aContent;
+ if (m_aFlavors[0].DataType == cppu::UnoType<OUString>::get())
+ {
+ auto pText = reinterpret_cast<const char*>(aSequence.getConstArray());
+ aContent <<= OUString(pText, aSequence.getLength(), RTL_TEXTENCODING_UTF8);
+ }
+ else
+ aContent <<= aSequence;
+ m_aContent.push_back(aContent);
+}
+
+/// Use to ensure we have some dummy content on the clipboard to allow a 1st 'paste'
+LOKTransferable::LOKTransferable()
+{
+ m_aContent.reserve(1);
+ m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1);
+ initFlavourFromMime(m_aFlavors[0], "text/plain");
+ uno::Any aContent;
+ aContent <<= OUString();
+ m_aContent.push_back(aContent);
+}
+
+// cf. sot/source/base/exchange.cxx for these two exceptional types.
+void LOKTransferable::initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor,
+ OUString aMimeType)
+{
+ if (aMimeType.startsWith("text/plain"))
+ {
+ aMimeType = "text/plain;charset=utf-16";
+ rFlavor.DataType = cppu::UnoType<OUString>::get();
+ }
+ else if (aMimeType == "application/x-libreoffice-tsvc")
+ rFlavor.DataType = cppu::UnoType<OUString>::get();
+ else
+ rFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get();
+ rFlavor.MimeType = aMimeType;
+ rFlavor.HumanPresentableName = aMimeType;
+}
+
+LOKTransferable::LOKTransferable(const size_t nInCount, const char** pInMimeTypes,
+ const size_t* pInSizes, const char** pInStreams)
+{
+ m_aContent.reserve(nInCount);
+ m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(nInCount);
+ for (size_t i = 0; i < nInCount; ++i)
+ {
+ initFlavourFromMime(m_aFlavors[i], OUString::fromUtf8(pInMimeTypes[i]));
+
+ uno::Any aContent;
+ if (m_aFlavors[i].DataType == cppu::UnoType<OUString>::get())
+ aContent <<= OUString(pInStreams[i], pInSizes[i], RTL_TEXTENCODING_UTF8);
+ else
+ aContent <<= css::uno::Sequence<sal_Int8>(
+ reinterpret_cast<const sal_Int8*>(pInStreams[i]), pInSizes[i]);
+ m_aContent.push_back(aContent);
+ }
+}
+
+uno::Any SAL_CALL LOKTransferable::getTransferData(const datatransfer::DataFlavor& rFlavor)
+{
+ assert(m_aContent.size() == static_cast<size_t>(m_aFlavors.getLength()));
+ for (size_t i = 0; i < m_aContent.size(); ++i)
+ {
+ if (m_aFlavors[i].MimeType == rFlavor.MimeType)
+ {
+ if (m_aFlavors[i].DataType != rFlavor.DataType)
+ SAL_WARN("lok", "Horror type mismatch!");
+ return m_aContent[i];
+ }
+ }
+ return uno::Any();
+}
+
+uno::Sequence<datatransfer::DataFlavor> SAL_CALL LOKTransferable::getTransferDataFlavors()
+{
+ return m_aFlavors;
+}
+
+sal_Bool SAL_CALL LOKTransferable::isDataFlavorSupported(const datatransfer::DataFlavor& rFlavor)
+{
+ return std::find_if(m_aFlavors.begin(), m_aFlavors.end(),
+ [&rFlavor](const datatransfer::DataFlavor& i) {
+ return i.MimeType == rFlavor.MimeType && i.DataType == rFlavor.DataType;
+ })
+ != m_aFlavors.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/lokclipboard.hxx b/desktop/source/lib/lokclipboard.hxx
new file mode 100644
index 000000000..9688956c0
--- /dev/null
+++ b/desktop/source/lib/lokclipboard.hxx
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_LIB_LOKCLIPBOARD_HXX
+#define INCLUDED_DESKTOP_SOURCE_LIB_LOKCLIPBOARD_HXX
+
+#include <vector>
+
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp>
+
+using namespace css::uno;
+
+/// A clipboard implementation for LibreOfficeKit.
+class LOKClipboard final
+ : public cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard,
+ css::lang::XServiceInfo>
+{
+ osl::Mutex m_aMutex;
+ css::uno::Reference<css::datatransfer::XTransferable> m_xTransferable;
+ css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner;
+ std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> m_aListeners;
+
+public:
+ LOKClipboard();
+
+ /// get an XInterface easily.
+ css::uno::Reference<css::uno::XInterface> getXI()
+ {
+ return css::uno::Reference<css::uno::XInterface>(static_cast<cppu::OWeakObject*>(this));
+ }
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ static Sequence<OUString> getSupportedServiceNames_static();
+
+ // XClipboard
+ css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override;
+ void SAL_CALL setContents(
+ const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable,
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
+ override;
+ OUString SAL_CALL getName() override { return "CLIPBOARD"; }
+
+ // XClipboardEx
+ sal_Int8 SAL_CALL getRenderingCapabilities() override { return 0; }
+
+ // XClipboardNotifier
+ void SAL_CALL addClipboardListener(
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
+ override;
+ void SAL_CALL removeClipboardListener(
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
+ override;
+};
+
+/// Represents the contents of LOKClipboard.
+class LOKTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransferable>
+{
+ css::uno::Sequence<css::datatransfer::DataFlavor> m_aFlavors;
+ std::vector<css::uno::Any> m_aContent;
+
+ static void initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor, OUString aMimeType);
+
+public:
+ LOKTransferable();
+ LOKTransferable(size_t nInCount, const char** pInMimeTypes, const size_t* pInSizes,
+ const char** pInStreams);
+ LOKTransferable(const OUString& sMimeType, const css::uno::Sequence<sal_Int8>& aSequence);
+
+ css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override;
+
+ css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() override;
+
+ sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) override;
+};
+
+/// Theoretically to hook into the (horrible) vcl dtranscomp.cxx code.
+class LOKClipboardFactory : public ::cppu::WeakComponentImplHelper<css::lang::XSingleServiceFactory>
+{
+ static osl::Mutex gMutex;
+
+public:
+ LOKClipboardFactory()
+ : cppu::WeakComponentImplHelper<css::lang::XSingleServiceFactory>(gMutex)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> SAL_CALL createInstance() override
+ {
+ return createInstanceWithArguments(css::uno::Sequence<css::uno::Any>());
+ }
+ css::uno::Reference<css::uno::XInterface> SAL_CALL
+ createInstanceWithArguments(const css::uno::Sequence<css::uno::Any>& /* rArgs */) override;
+
+ /// Fetch clipboard from the global pool.
+ static rtl::Reference<LOKClipboard> getClipboardForCurView();
+
+ /// Release a clipboard before its document dies, nViewId of -1 clears all.
+ static void releaseClipboardForView(int nViewId);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/lokinteractionhandler.cxx b/desktop/source/lib/lokinteractionhandler.cxx
new file mode 100644
index 000000000..0933e4c23
--- /dev/null
+++ b/desktop/source/lib/lokinteractionhandler.cxx
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "lokinteractionhandler.hxx"
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionPassword2.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkOffLineException.hpp>
+
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
+
+#include <com/sun/star/task/DocumentPasswordRequest2.hpp>
+#include <com/sun/star/task/DocumentMSPasswordRequest2.hpp>
+
+#include "../../inc/lib/init.hxx"
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewsh.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+
+LOKInteractionHandler::LOKInteractionHandler(
+ const OString& rCommand,
+ desktop::LibLibreOffice_Impl *const pLOKit,
+ desktop::LibLODocument_Impl *const pLOKDocument)
+ : m_pLOKit(pLOKit)
+ , m_pLOKDocument(pLOKDocument)
+ , m_command(rCommand)
+ , m_usePassword(false)
+{
+ assert(m_pLOKit);
+}
+
+LOKInteractionHandler::~LOKInteractionHandler()
+{
+}
+
+OUString SAL_CALL LOKInteractionHandler::getImplementationName()
+{
+ return "com.sun.star.comp.uui.LOKInteractionHandler";
+}
+
+sal_Bool SAL_CALL LOKInteractionHandler::supportsService(OUString const & rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL LOKInteractionHandler::getSupportedServiceNames()
+{
+ return { "com.sun.star.task.InteractionHandler",
+ // added to indicate support for configuration.backend.MergeRecoveryRequest
+ "com.sun.star.configuration.backend.InteractionHandler",
+ // for backwards compatibility
+ "com.sun.star.uui.InteractionHandler" };
+}
+
+void SAL_CALL LOKInteractionHandler::initialize(uno::Sequence<uno::Any> const & /*rArguments*/)
+{
+}
+
+void SAL_CALL LOKInteractionHandler::handle(
+ uno::Reference<task::XInteractionRequest> const & xRequest)
+{
+ // just do the same thing in both cases
+ handleInteractionRequest(xRequest);
+}
+
+void LOKInteractionHandler::postError(css::task::InteractionClassification classif, const char* kind, ErrCode code, const OUString &message)
+{
+ const char *classification = "error";
+ switch (classif)
+ {
+ case task::InteractionClassification_ERROR: break;
+ case task::InteractionClassification_WARNING: classification = "warning"; break;
+ case task::InteractionClassification_INFO: classification = "info"; break;
+ case task::InteractionClassification_QUERY: classification = "query"; break;
+ default: assert(false); break;
+ }
+
+ // create the JSON representation
+ boost::property_tree::ptree aTree;
+ aTree.put("classification", classification);
+ aTree.put("cmd", m_command.getStr());
+ aTree.put("kind", kind);
+ aTree.put("code", code);
+ aTree.put("message", message.toUtf8());
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+
+ std::size_t nView = SfxViewShell::Current() ? SfxLokHelper::getView() : 0;
+ if (m_pLOKDocument && m_pLOKDocument->mpCallbackFlushHandlers.count(nView))
+ m_pLOKDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_ERROR, aStream.str().c_str());
+ else if (m_pLOKit->mpCallback)
+ m_pLOKit->mpCallback(LOK_CALLBACK_ERROR, aStream.str().c_str(), m_pLOKit->mpCallbackData);
+}
+
+namespace {
+
+/// Just approve the interaction.
+void selectApproved(uno::Sequence<uno::Reference<task::XInteractionContinuation>> const &rContinuations)
+{
+ for (auto const & c : rContinuations)
+ {
+ uno::Reference<task::XInteractionApprove> xApprove(c, uno::UNO_QUERY);
+ if (xApprove.is())
+ xApprove->select();
+ }
+}
+
+}
+
+bool LOKInteractionHandler::handleIOException(const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> &rContinuations, const css::uno::Any& rRequest)
+{
+ ucb::InteractiveIOException aIoException;
+ if (!(rRequest >>= aIoException))
+ return false;
+
+ static ErrCode const aErrorCode[int(ucb::IOErrorCode_WRONG_VERSION) + 1] =
+ {
+ ERRCODE_IO_ABORT,
+ ERRCODE_IO_ACCESSDENIED,
+ ERRCODE_IO_ALREADYEXISTS,
+ ERRCODE_IO_BADCRC,
+ ERRCODE_IO_CANTCREATE,
+ ERRCODE_IO_CANTREAD,
+ ERRCODE_IO_CANTSEEK,
+ ERRCODE_IO_CANTTELL,
+ ERRCODE_IO_CANTWRITE,
+ ERRCODE_IO_CURRENTDIR,
+ ERRCODE_IO_DEVICENOTREADY,
+ ERRCODE_IO_NOTSAMEDEVICE,
+ ERRCODE_IO_GENERAL,
+ ERRCODE_IO_INVALIDACCESS,
+ ERRCODE_IO_INVALIDCHAR,
+ ERRCODE_IO_INVALIDDEVICE,
+ ERRCODE_IO_INVALIDLENGTH,
+ ERRCODE_IO_INVALIDPARAMETER,
+ ERRCODE_IO_ISWILDCARD,
+ ERRCODE_IO_LOCKVIOLATION,
+ ERRCODE_IO_MISPLACEDCHAR,
+ ERRCODE_IO_NAMETOOLONG,
+ ERRCODE_IO_NOTEXISTS,
+ ERRCODE_IO_NOTEXISTSPATH,
+ ERRCODE_IO_NOTSUPPORTED,
+ ERRCODE_IO_NOTADIRECTORY,
+ ERRCODE_IO_NOTAFILE,
+ ERRCODE_IO_OUTOFSPACE,
+ ERRCODE_IO_TOOMANYOPENFILES,
+ ERRCODE_IO_OUTOFMEMORY,
+ ERRCODE_IO_PENDING,
+ ERRCODE_IO_RECURSIVE,
+ ERRCODE_IO_UNKNOWN,
+ ERRCODE_IO_WRITEPROTECTED,
+ ERRCODE_IO_WRONGFORMAT,
+ ERRCODE_IO_WRONGVERSION,
+ };
+
+ postError(aIoException.Classification, "io", aErrorCode[static_cast<int>(aIoException.Code)], "");
+ selectApproved(rContinuations);
+
+ return true;
+}
+
+bool LOKInteractionHandler::handleNetworkException(const uno::Sequence<uno::Reference<task::XInteractionContinuation>> &rContinuations, const uno::Any &rRequest)
+{
+ ucb::InteractiveNetworkException aNetworkException;
+ if (!(rRequest >>= aNetworkException))
+ return false;
+
+ ErrCode nErrorCode;
+ OUString aMessage;
+
+ ucb::InteractiveNetworkOffLineException aOffLineException;
+ ucb::InteractiveNetworkResolveNameException aResolveNameException;
+ ucb::InteractiveNetworkConnectException aConnectException;
+ ucb::InteractiveNetworkReadException aReadException;
+ ucb::InteractiveNetworkWriteException aWriteException;
+ if (rRequest >>= aOffLineException)
+ {
+ nErrorCode = ERRCODE_INET_OFFLINE;
+ }
+ else if (rRequest >>= aResolveNameException)
+ {
+ nErrorCode = ERRCODE_INET_NAME_RESOLVE;
+ aMessage = aResolveNameException.Server;
+ }
+ else if (rRequest >>= aConnectException)
+ {
+ nErrorCode = ERRCODE_INET_CONNECT;
+ aMessage = aConnectException.Server;
+ }
+ else if (rRequest >>= aReadException)
+ {
+ nErrorCode = ERRCODE_INET_READ;
+ aMessage = aReadException.Diagnostic;
+ }
+ else if (rRequest >>= aWriteException)
+ {
+ nErrorCode = ERRCODE_INET_WRITE;
+ aMessage = aWriteException.Diagnostic;
+ }
+ else
+ {
+ nErrorCode = ERRCODE_INET_GENERAL;
+ }
+
+ postError(aNetworkException.Classification, "network", nErrorCode, aMessage);
+ selectApproved(rContinuations);
+
+ return true;
+}
+
+bool LOKInteractionHandler::handlePasswordRequest(const uno::Sequence<uno::Reference<task::XInteractionContinuation>> &rContinuations, const uno::Any &rRequest)
+{
+ bool bPasswordRequestFound = false;
+ bool bIsRequestPasswordToModify = false;
+
+ OString sUrl;
+
+ task::DocumentPasswordRequest passwordRequest;
+ if (rRequest >>= passwordRequest)
+ {
+ bIsRequestPasswordToModify = false;
+ sUrl = passwordRequest.Name.toUtf8();
+ bPasswordRequestFound = true;
+ }
+
+ task::DocumentPasswordRequest2 passwordRequest2;
+ if (rRequest >>= passwordRequest2)
+ {
+ bIsRequestPasswordToModify = passwordRequest2.IsRequestPasswordToModify;
+ sUrl = passwordRequest2.Name.toUtf8();
+ bPasswordRequestFound = true;
+ }
+
+ task::DocumentMSPasswordRequest2 passwordMSRequest;
+ if (rRequest >>= passwordMSRequest)
+ {
+ bIsRequestPasswordToModify = passwordMSRequest.IsRequestPasswordToModify;
+ sUrl = passwordMSRequest.Name.toUtf8();
+ bPasswordRequestFound = true;
+ }
+
+ if (!bPasswordRequestFound)
+ return false;
+
+ if (m_pLOKit->mpCallback &&
+ m_pLOKit->hasOptionalFeature(bIsRequestPasswordToModify ? LOK_FEATURE_DOCUMENT_PASSWORD_TO_MODIFY
+ : LOK_FEATURE_DOCUMENT_PASSWORD))
+ {
+ // release SolarMutex, so the callback handler, which may run in another thread,
+ // can acquire it in 'lo_setDocumentPassword'
+ SolarMutexReleaser aReleaser;
+ m_pLOKit->mpCallback(bIsRequestPasswordToModify ? LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY
+ : LOK_CALLBACK_DOCUMENT_PASSWORD,
+ sUrl.getStr(),
+ m_pLOKit->mpCallbackData);
+
+ // block until SetPassword is called
+ m_havePassword.wait();
+ m_havePassword.reset();
+ }
+
+ for (auto const & cont : rContinuations)
+ {
+ if (m_usePassword)
+ {
+ if (bIsRequestPasswordToModify)
+ {
+ uno::Reference<task::XInteractionPassword2> const xIPW2(cont, uno::UNO_QUERY);
+ xIPW2->setPasswordToModify(m_Password);
+ xIPW2->select();
+ }
+ else
+ {
+ uno::Reference<task::XInteractionPassword> const xIPW(cont, uno::UNO_QUERY);
+ if (xIPW.is())
+ {
+ xIPW->setPassword(m_Password);
+ xIPW->select();
+ }
+ }
+ }
+ else
+ {
+ if (bIsRequestPasswordToModify)
+ {
+ uno::Reference<task::XInteractionPassword2> const xIPW2(cont, uno::UNO_QUERY);
+ xIPW2->setRecommendReadOnly(true);
+ xIPW2->select();
+ }
+ else
+ {
+ uno::Reference<task::XInteractionAbort> const xAbort(cont, uno::UNO_QUERY);
+ if (xAbort.is())
+ {
+ xAbort->select();
+ }
+ }
+ }
+ }
+ return true;
+}
+
+sal_Bool SAL_CALL LOKInteractionHandler::handleInteractionRequest(
+ const uno::Reference<task::XInteractionRequest>& xRequest)
+{
+ uno::Sequence<uno::Reference<task::XInteractionContinuation>> const &rContinuations = xRequest->getContinuations();
+ uno::Any const request(xRequest->getRequest());
+
+ if (handleIOException(rContinuations, request))
+ return true;
+
+ if (handleNetworkException(rContinuations, request))
+ return true;
+
+ if (handlePasswordRequest(rContinuations, request))
+ return true;
+
+ // TODO: perform more interactions 'for real' like the above
+ selectApproved(rContinuations);
+
+ return true;
+}
+
+void LOKInteractionHandler::SetPassword(char const*const pPassword)
+{
+ if (pPassword)
+ {
+ m_Password = OUString(pPassword, strlen(pPassword), RTL_TEXTENCODING_UTF8);
+ m_usePassword = true;
+ }
+ else
+ {
+ m_usePassword = false;
+ }
+ m_havePassword.set();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/lib/lokinteractionhandler.hxx b/desktop/source/lib/lokinteractionhandler.hxx
new file mode 100644
index 000000000..8c11fd6b3
--- /dev/null
+++ b/desktop/source/lib/lokinteractionhandler.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_LIB_LOKINTERACTIONHANDLER_HXX
+#define INCLUDED_DESKTOP_SOURCE_LIB_LOKINTERACTIONHANDLER_HXX
+
+#include <osl/conditn.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/errcode.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/task/XInteractionHandler2.hpp>
+
+namespace desktop {
+ struct LibLibreOffice_Impl;
+ struct LibLODocument_Impl;
+}
+
+/** InteractionHandler is an interface that provides the user with various dialogs / error messages.
+
+We need an own implementation for the LibreOfficeKit so that we can route the
+information easily via callbacks.
+
+TODO: the callbacks are not implemented yet, we just approve any interaction
+that we get.
+*/
+class LOKInteractionHandler: public cppu::WeakImplHelper<com::sun::star::lang::XServiceInfo,
+ com::sun::star::lang::XInitialization,
+ com::sun::star::task::XInteractionHandler2>
+{
+private:
+ desktop::LibLibreOffice_Impl * m_pLOKit;
+ desktop::LibLODocument_Impl * m_pLOKDocument;
+
+ /// Command for which we use this interaction handler (like "load", "save", "saveas", ...)
+ OString m_command;
+
+ OUString m_Password;
+ bool m_usePassword;
+ osl::Condition m_havePassword;
+
+ LOKInteractionHandler(const LOKInteractionHandler&) = delete;
+ LOKInteractionHandler& operator=(const LOKInteractionHandler&) = delete;
+
+ /** Call the LOK_CALLBACK_ERROR on the LOK document (if available) or LOK lib.
+
+ The error itself is a JSON message, like:
+ {
+ "classification": "error" | "warning" | "info"
+ "kind": "network" etc.
+ "code": 403 | 404 | ...
+ "message": freeform description
+ }
+ */
+ void postError(css::task::InteractionClassification classif, const char* kind, ErrCode code, const OUString &message);
+
+ bool handleIOException(const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> &rContinuations, const css::uno::Any& rRequest);
+ bool handleNetworkException(const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> &rContinuations, const css::uno::Any& rRequest);
+ bool handlePasswordRequest(const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> &rContinuations, const css::uno::Any& rRequest);
+
+public:
+ void SetPassword(char const* pPassword);
+
+ explicit LOKInteractionHandler(
+ const OString& rCommand,
+ desktop::LibLibreOffice_Impl *,
+ desktop::LibLODocument_Impl *pLOKDocumt = nullptr);
+
+ virtual ~LOKInteractionHandler() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override;
+
+ virtual com::sun::star::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ virtual void SAL_CALL initialize(com::sun::star::uno::Sequence<com::sun::star::uno::Any > const & rArguments) override;
+
+ virtual void SAL_CALL handle(com::sun::star::uno::Reference<com::sun::star::task::XInteractionRequest> const & rRequest) override;
+
+ virtual sal_Bool SAL_CALL handleInteractionRequest(const ::com::sun::star::uno::Reference<::com::sun::star::task::XInteractionRequest>& Request) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/migration.cxx b/desktop/source/migration/migration.cxx
new file mode 100644
index 000000000..25964de3c
--- /dev/null
+++ b/desktop/source/migration/migration.cxx
@@ -0,0 +1,1114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <set>
+
+#include <migration.hxx>
+#include "migration_impl.hxx"
+
+#include <sal/log.hxx>
+#include <unotools/textsearch.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <unotools/bootstrap.hxx>
+#include <rtl/uri.hxx>
+#include <i18nlangtag/lang.h>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <com/sun/star/configuration/Update.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/FileSystemStorageFactory.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/ui/UIConfigurationManager.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <vcl/commandinfoprovider.hxx>
+
+using namespace osl;
+using namespace std;
+using namespace com::sun::star::task;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::container;
+using com::sun::star::uno::Exception;
+using namespace com::sun::star;
+
+
+namespace desktop
+{
+
+static const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL";
+static const char ITEM_DESCRIPTOR_CONTAINER[] = "ItemDescriptorContainer";
+static const char ITEM_DESCRIPTOR_LABEL[] = "Label";
+
+static OUString mapModuleShortNameToIdentifier(const OUString& sShortName)
+{
+ OUString sIdentifier;
+
+ if ( sShortName == "StartModule" )
+ sIdentifier = "com.sun.star.frame.StartModule";
+
+ else if ( sShortName == "swriter" )
+ sIdentifier = "com.sun.star.text.TextDocument";
+
+ else if ( sShortName == "scalc" )
+ sIdentifier = "com.sun.star.sheet.SpreadsheetDocument";
+
+ else if ( sShortName == "sdraw" )
+ sIdentifier = "com.sun.star.drawing.DrawingDocument";
+
+ else if ( sShortName == "simpress" )
+ sIdentifier = "com.sun.star.presentation.PresentationDocument";
+
+ else if ( sShortName == "smath" )
+ sIdentifier = "com.sun.star.formula.FormulaProperties";
+
+ else if ( sShortName == "schart" )
+ sIdentifier = "com.sun.star.chart2.ChartDocument";
+
+ else if ( sShortName == "BasicIDE" )
+ sIdentifier = "com.sun.star.script.BasicIDE";
+
+ else if ( sShortName == "dbapp" )
+ sIdentifier = "com.sun.star.sdb.OfficeDatabaseDocument";
+
+ else if ( sShortName == "sglobal" )
+ sIdentifier = "com.sun.star.text.GlobalDocument";
+
+ else if ( sShortName == "sweb" )
+ sIdentifier = "com.sun.star.text.WebDocument";
+
+ else if ( sShortName == "swxform" )
+ sIdentifier = "com.sun.star.xforms.XMLFormDocument";
+
+ else if ( sShortName == "sbibliography" )
+ sIdentifier = "com.sun.star.frame.Bibliography";
+
+ return sIdentifier;
+}
+
+bool MigrationImpl::alreadyMigrated()
+{
+ OUString const MIGRATION_STAMP_NAME("/MIGRATED4");
+ OUString aStr = m_aInfo.userdata + MIGRATION_STAMP_NAME;
+ File aFile(aStr);
+ // create migration stamp, and/or check its existence
+ bool bRet = aFile.open (osl_File_OpenFlag_Write | osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock) == FileBase::E_EXIST;
+ SAL_INFO( "desktop.migration", "File '" << aStr << "' exists? " << bRet );
+ return bRet;
+}
+
+bool MigrationImpl::initializeMigration()
+{
+ bool bRet = false;
+
+ if (!checkMigrationCompleted()) {
+ readAvailableMigrations(m_vMigrationsAvailable);
+ sal_Int32 nIndex = findPreferredMigrationProcess(m_vMigrationsAvailable);
+ // m_aInfo is now set to the preferred migration source
+ if ( nIndex >= 0 ) {
+ if (alreadyMigrated())
+ return false;
+ m_vrMigrations = readMigrationSteps(m_vMigrationsAvailable[nIndex].name);
+ }
+
+ bRet = !m_aInfo.userdata.isEmpty();
+ }
+
+ SAL_INFO( "desktop.migration", "Migration " << ( bRet ? "needed" : "not required" ) );
+
+ return bRet;
+}
+
+void Migration::migrateSettingsIfNecessary()
+{
+ MigrationImpl aImpl;
+
+ if (! aImpl.initializeMigration() )
+ return;
+
+ bool bResult = false;
+ try {
+ bResult = aImpl.doMigration();
+ } catch (const Exception&) {
+ TOOLS_WARN_EXCEPTION( "desktop", "doMigration()");
+ }
+ OSL_ENSURE(bResult, "Migration has not been successful");
+}
+
+MigrationImpl::MigrationImpl()
+{
+}
+
+MigrationImpl::~MigrationImpl()
+{
+}
+
+// The main entry point for migrating settings
+bool MigrationImpl::doMigration()
+{
+ // compile file list for migration
+ m_vrFileList = compileFileList();
+
+ bool result = false;
+ try {
+ NewVersionUIInfo aNewVersionUIInfo;
+ std::vector< MigrationModuleInfo > vModulesInfo = dectectUIChangesForAllModules();
+ aNewVersionUIInfo.init(vModulesInfo);
+
+ copyFiles();
+
+ const OUString sMenubarResourceURL("private:resource/menubar/menubar");
+ const OUString sToolbarResourcePre("private:resource/toolbar/");
+ for (MigrationModuleInfo & i : vModulesInfo) {
+ OUString sModuleIdentifier = mapModuleShortNameToIdentifier(i.sModuleShortName);
+ if (sModuleIdentifier.isEmpty())
+ continue;
+
+
+ OUString aOldCfgDataPath = m_aInfo.userdata + "/user/config/soffice.cfg/modules/" + i.sModuleShortName;
+ uno::Sequence< uno::Any > lArgs {uno::makeAny(aOldCfgDataPath), uno::makeAny(embed::ElementModes::READ)};
+
+ uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext());
+ uno::Reference< lang::XSingleServiceFactory > xStorageFactory(embed::FileSystemStorageFactory::create(xContext));
+ uno::Reference< embed::XStorage > xModules(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY);
+ uno::Reference< ui::XUIConfigurationManager2 > xOldCfgManager = ui::UIConfigurationManager::create(xContext);
+
+ if ( xModules.is() ) {
+ xOldCfgManager->setStorage( xModules );
+ xOldCfgManager->reload();
+ }
+
+ uno::Reference< ui::XUIConfigurationManager > xCfgManager = aNewVersionUIInfo.getConfigManager(i.sModuleShortName);
+
+ if (i.bHasMenubar) {
+ uno::Reference< container::XIndexContainer > xOldVersionMenuSettings(xOldCfgManager->getSettings(sMenubarResourceURL, true), uno::UNO_QUERY);
+ uno::Reference< container::XIndexContainer > xNewVersionMenuSettings = aNewVersionUIInfo.getNewMenubarSettings(i.sModuleShortName);
+ compareOldAndNewConfig(OUString(), xOldVersionMenuSettings, xNewVersionMenuSettings, sMenubarResourceURL);
+ mergeOldToNewVersion(xCfgManager, xNewVersionMenuSettings, sModuleIdentifier, sMenubarResourceURL);
+ }
+
+ sal_Int32 nToolbars = i.m_vToolbars.size();
+ if (nToolbars >0) {
+ for (sal_Int32 j=0; j<nToolbars; ++j) {
+ OUString sToolbarName = i.m_vToolbars[j];
+ OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName;
+
+ uno::Reference< container::XIndexContainer > xOldVersionToolbarSettings(xOldCfgManager->getSettings(sToolbarResourceURL, true), uno::UNO_QUERY);
+ uno::Reference< container::XIndexContainer > xNewVersionToolbarSettings = aNewVersionUIInfo.getNewToolbarSettings(i.sModuleShortName, sToolbarName);
+ compareOldAndNewConfig(OUString(), xOldVersionToolbarSettings, xNewVersionToolbarSettings, sToolbarResourceURL);
+ mergeOldToNewVersion(xCfgManager, xNewVersionToolbarSettings, sModuleIdentifier, sToolbarResourceURL);
+ }
+ }
+
+ m_aOldVersionItemsHashMap.clear();
+ }
+
+ // execute the migration items from Setup.xcu
+ copyConfig();
+
+ // execute custom migration services from Setup.xcu
+ // and refresh the cache
+ runServices();
+ uno::Reference< XRefreshable >(
+ configuration::theDefaultProvider::get(comphelper::getProcessComponentContext()),
+ uno::UNO_QUERY_THROW)->refresh();
+
+ result = true;
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.migration",
+ "ignored Exception while migrating from version \"" << m_aInfo.productname
+ << "\" data \"" << m_aInfo.userdata << "\"");
+ }
+
+ // prevent running the migration multiple times
+ setMigrationCompleted();
+ return result;
+}
+
+void MigrationImpl::setMigrationCompleted()
+{
+ try {
+ uno::Reference< XPropertySet > aPropertySet(getConfigAccess("org.openoffice.Setup/Office", true), uno::UNO_QUERY_THROW);
+ aPropertySet->setPropertyValue("MigrationCompleted", uno::makeAny(true));
+ uno::Reference< XChangesBatch >(aPropertySet, uno::UNO_QUERY_THROW)->commitChanges();
+ } catch (...) {
+ // fail silently
+ }
+}
+
+bool MigrationImpl::checkMigrationCompleted()
+{
+ bool bMigrationCompleted = false;
+ try {
+ uno::Reference< XPropertySet > aPropertySet(
+ getConfigAccess("org.openoffice.Setup/Office"), uno::UNO_QUERY_THROW);
+ aPropertySet->getPropertyValue("MigrationCompleted") >>= bMigrationCompleted;
+
+ if( !bMigrationCompleted && getenv("SAL_DISABLE_USERMIGRATION" ) ) {
+ // migration prevented - fake its success
+ setMigrationCompleted();
+ bMigrationCompleted = true;
+ }
+ } catch (const Exception&) {
+ // just return false...
+ }
+ SAL_INFO( "desktop.migration", "Migration " << ( bMigrationCompleted ? "already completed" : "not done" ) );
+
+ return bMigrationCompleted;
+}
+
+static void insertSorted(migrations_available& rAvailableMigrations, supported_migration const & aSupportedMigration)
+{
+ migrations_available::iterator pIter = std::find_if(rAvailableMigrations.begin(), rAvailableMigrations.end(),
+ [&aSupportedMigration](const supported_migration& rMigration) { return rMigration.nPriority < aSupportedMigration.nPriority; });
+ if (pIter != rAvailableMigrations.end())
+ rAvailableMigrations.insert(pIter, aSupportedMigration );
+ else
+ rAvailableMigrations.push_back( aSupportedMigration );
+}
+
+void MigrationImpl::readAvailableMigrations(migrations_available& rAvailableMigrations)
+{
+ // get supported version names
+ uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_SET_THROW);
+ const uno::Sequence< OUString > seqSupportedVersions = aMigrationAccess->getElementNames();
+
+ const OUString aVersionIdentifiers( "VersionIdentifiers" );
+ const OUString aPriorityIdentifier( "Priority" );
+
+ for (OUString const & supportedVersion :seqSupportedVersions) {
+ sal_Int32 nPriority( 0 );
+ uno::Sequence< OUString > seqVersions;
+ uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(supportedVersion), uno::UNO_QUERY_THROW );
+ xMigrationData->getByName( aVersionIdentifiers ) >>= seqVersions;
+ xMigrationData->getByName( aPriorityIdentifier ) >>= nPriority;
+
+ supported_migration aSupportedMigration;
+ aSupportedMigration.name = supportedVersion;
+ aSupportedMigration.nPriority = nPriority;
+ for (OUString const & s : std::as_const(seqVersions))
+ aSupportedMigration.supported_versions.push_back(s.trim());
+ insertSorted( rAvailableMigrations, aSupportedMigration );
+ SAL_INFO( "desktop.migration", " available migration '" << aSupportedMigration.name << "'" );
+ }
+}
+
+migrations_vr MigrationImpl::readMigrationSteps(const OUString& rMigrationName)
+{
+ // get migration access
+ uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_SET_THROW);
+ uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(rMigrationName), uno::UNO_QUERY_THROW );
+
+ // get migration description from org.openoffice.Setup/Migration
+ // and build vector of migration steps
+ uno::Reference< XNameAccess > theNameAccess(xMigrationData->getByName("MigrationSteps"), uno::UNO_QUERY_THROW);
+ uno::Reference< XNameAccess > tmpAccess;
+ uno::Sequence< OUString > tmpSeq;
+ migrations_vr vrMigrations(new migrations_v);
+ const css::uno::Sequence<OUString> aMigrationSteps = theNameAccess->getElementNames();
+ for (const OUString& rMigrationStep : aMigrationSteps) {
+ // get current migration step
+ theNameAccess->getByName(rMigrationStep) >>= tmpAccess;
+ migration_step tmpStep;
+
+ // read included files from current step description
+ if (tmpAccess->getByName("IncludedFiles") >>= tmpSeq) {
+ for (const OUString& rSeqEntry : std::as_const(tmpSeq))
+ tmpStep.includeFiles.push_back(rSeqEntry);
+ }
+
+ // excluded files...
+ if (tmpAccess->getByName("ExcludedFiles") >>= tmpSeq) {
+ for (const OUString& rSeqEntry : std::as_const(tmpSeq))
+ tmpStep.excludeFiles.push_back(rSeqEntry);
+ }
+
+ // included nodes...
+ if (tmpAccess->getByName("IncludedNodes") >>= tmpSeq) {
+ for (const OUString& rSeqEntry : std::as_const(tmpSeq))
+ tmpStep.includeConfig.push_back(rSeqEntry);
+ }
+
+ // excluded nodes...
+ if (tmpAccess->getByName("ExcludedNodes") >>= tmpSeq) {
+ for (const OUString& rSeqEntry : std::as_const(tmpSeq))
+ tmpStep.excludeConfig.push_back(rSeqEntry);
+ }
+
+ // excluded extensions...
+ if (tmpAccess->getByName("ExcludedExtensions") >>= tmpSeq) {
+ for (const OUString& rSeqEntry : std::as_const(tmpSeq))
+ tmpStep.excludeExtensions.push_back(rSeqEntry);
+ }
+
+ // generic service
+ tmpAccess->getByName("MigrationService") >>= tmpStep.service;
+
+ vrMigrations->push_back(tmpStep);
+ }
+ return vrMigrations;
+}
+
+static FileBase::RC _checkAndCreateDirectory(INetURLObject const & dirURL)
+{
+ FileBase::RC result = Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri));
+ if (result == FileBase::E_NOENT) {
+ INetURLObject baseURL(dirURL);
+ baseURL.removeSegment();
+ _checkAndCreateDirectory(baseURL);
+ return Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri));
+ } else
+ return result;
+}
+
+#if defined UNX && ! defined MACOSX
+
+static const char XDG_CONFIG_PART[] = "/.config/";
+
+OUString MigrationImpl::preXDGConfigDir(const OUString& rConfigDir)
+{
+ OUString aPreXDGConfigPath;
+ const char* pXDGCfgHome = getenv("XDG_CONFIG_HOME");
+
+ // cater for XDG_CONFIG_HOME change
+ // If XDG_CONFIG_HOME is set then we;
+ // assume the user knows what they are doing ( room for improvement here, we could
+ // of course search the default config dir etc. also - but this is more complex,
+ // we would need to weigh results from the current config dir against matches in
+ // the 'old' config dir etc. ) - currently we just use the returned config dir.
+ // If XDG_CONFIG_HOME is NOT set;
+ // assume then we should now using the default $HOME/.config config location for
+ // our user profiles, however *all* previous libreoffice and openoffice.org
+ // configurations will be in the 'old' config directory and that's where we need
+ // to search - we convert the returned config dir to the 'old' dir
+ if ( !pXDGCfgHome && rConfigDir.endsWith( XDG_CONFIG_PART ) )
+ // remove trailing '.config/' but leave the terminating '/'
+ aPreXDGConfigPath = rConfigDir.copy( 0, rConfigDir.getLength() - sizeof( XDG_CONFIG_PART ) + 2 );
+ else
+ aPreXDGConfigPath = rConfigDir;
+
+ // the application-specific config dir is no longer prefixed by '.' because it is hidden under ".config"
+ // we have to add the '.' for the pre-XDG directory names
+ aPreXDGConfigPath += ".";
+
+ return aPreXDGConfigPath;
+}
+#endif
+
+void MigrationImpl::setInstallInfoIfExist(
+ install_info& aInfo,
+ const OUString& rConfigDir,
+ const OUString& rVersion)
+{
+ OUString url(INetURLObject(rConfigDir).GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ osl::DirectoryItem item;
+ osl::FileStatus stat(osl_FileStatus_Mask_Type);
+
+ if (osl::DirectoryItem::get(url, item) == osl::FileBase::E_None
+ && item.getFileStatus(stat) == osl::FileBase::E_None
+ && stat.getFileType() == osl::FileStatus::Directory) {
+ aInfo.userdata = url;
+ aInfo.productname = rVersion;
+ }
+}
+
+install_info MigrationImpl::findInstallation(const strings_v& rVersions)
+{
+
+ OUString aTopConfigDir;
+ osl::Security().getConfigDir( aTopConfigDir );
+ if ( !aTopConfigDir.isEmpty() && aTopConfigDir[ aTopConfigDir.getLength()-1 ] != '/' )
+ aTopConfigDir += "/";
+
+#if defined UNX && ! defined MACOSX
+ OUString aPreXDGTopConfigDir = preXDGConfigDir(aTopConfigDir);
+#endif
+
+ install_info aInfo;
+ for (auto const& elem : rVersions)
+ {
+ OUString aVersion, aProfileName;
+ sal_Int32 nSeparatorIndex = elem.indexOf('=');
+ if ( nSeparatorIndex != -1 ) {
+ aVersion = elem.copy( 0, nSeparatorIndex );
+ aProfileName = elem.copy( nSeparatorIndex+1 );
+ }
+
+ if ( !aVersion.isEmpty() && !aProfileName.isEmpty() &&
+ ( aInfo.userdata.isEmpty() ||
+ aProfileName.equalsIgnoreAsciiCase(
+ utl::ConfigManager::getProductName() ) ) ) {
+ setInstallInfoIfExist(aInfo, aTopConfigDir + aProfileName, aVersion);
+#if defined UNX && ! defined MACOSX
+ //try preXDG path if the new one does not exist
+ if ( aInfo.userdata.isEmpty())
+ setInstallInfoIfExist(aInfo, aPreXDGTopConfigDir + aProfileName, aVersion);
+#endif
+ }
+ }
+
+ return aInfo;
+}
+
+sal_Int32 MigrationImpl::findPreferredMigrationProcess(const migrations_available& rAvailableMigrations)
+{
+ sal_Int32 nIndex( -1 );
+ sal_Int32 i( 0 );
+
+ for (auto const& availableMigration : rAvailableMigrations)
+ {
+ install_info aInstallInfo = findInstallation(availableMigration.supported_versions);
+ if (!aInstallInfo.productname.isEmpty() ) {
+ m_aInfo = aInstallInfo;
+ nIndex = i;
+ break;
+ }
+ ++i;
+ }
+
+ SAL_INFO( "desktop.migration", " preferred migration is from product '" << m_aInfo.productname << "'");
+ SAL_INFO( "desktop.migration", " and settings directory '" << m_aInfo.userdata << "'");
+
+ return nIndex;
+}
+
+strings_vr MigrationImpl::applyPatterns(const strings_v& vSet, const strings_v& vPatterns)
+{
+ using namespace utl;
+ strings_vr vrResult(new strings_v);
+ for (auto const& pattern : vPatterns)
+ {
+ // find matches for this pattern in input set
+ // and copy them to the result
+ SearchParam param(pattern, SearchParam::SearchType::Regexp);
+ TextSearch ts(param, LANGUAGE_DONTKNOW);
+ sal_Int32 start = 0;
+ sal_Int32 end = 0;
+ for (auto const& elem : vSet)
+ {
+ end = elem.getLength();
+ if (ts.SearchForward(elem, &start, &end))
+ vrResult->push_back(elem);
+ }
+ }
+ return vrResult;
+}
+
+strings_vr MigrationImpl::getAllFiles(const OUString& baseURL) const
+{
+ strings_vr vrResult(new strings_v);
+
+ // get sub dirs
+ Directory dir(baseURL);
+ if (dir.open() == FileBase::E_None) {
+ strings_v vSubDirs;
+ strings_vr vrSubResult;
+
+ // work through directory contents...
+ DirectoryItem item;
+ FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
+ while (dir.getNextItem(item) == FileBase::E_None) {
+ if (item.getFileStatus(fs) == FileBase::E_None) {
+ if (fs.getFileType() == FileStatus::Directory)
+ vSubDirs.push_back(fs.getFileURL());
+ else
+ vrResult->push_back(fs.getFileURL());
+ }
+ }
+
+ // recurse subfolders
+ for (auto const& subDir : vSubDirs)
+ {
+ vrSubResult = getAllFiles(subDir);
+ vrResult->insert(vrResult->end(), vrSubResult->begin(), vrSubResult->end());
+ }
+ }
+ return vrResult;
+}
+
+namespace
+{
+
+// removes elements of vector 2 in vector 1
+strings_v subtract(strings_v const & va, strings_v const & vb)
+{
+ strings_v a(va);
+ std::sort(a.begin(), a.end());
+ strings_v::iterator ae(std::unique(a.begin(), a.end()));
+ strings_v b(vb);
+ std::sort(b.begin(), b.end());
+ strings_v::iterator be(std::unique(b.begin(), b.end()));
+ strings_v c;
+ std::set_difference(a.begin(), ae, b.begin(), be, std::back_inserter(c));
+ return c;
+}
+
+}
+
+strings_vr MigrationImpl::compileFileList()
+{
+
+ strings_vr vrResult(new strings_v);
+ strings_vr vrInclude;
+ strings_vr vrExclude;
+
+ // get a list of all files:
+ strings_vr vrFiles = getAllFiles(m_aInfo.userdata);
+
+ // get a file list result for each migration step
+ for (auto const& rMigration : *m_vrMigrations)
+ {
+ vrInclude = applyPatterns(*vrFiles, rMigration.includeFiles);
+ vrExclude = applyPatterns(*vrFiles, rMigration.excludeFiles);
+ strings_v sub(subtract(*vrInclude, *vrExclude));
+ vrResult->insert(vrResult->end(), sub.begin(), sub.end());
+ }
+ return vrResult;
+}
+
+namespace
+{
+
+struct componentParts {
+ std::set< OUString > includedPaths;
+ std::set< OUString > excludedPaths;
+};
+
+typedef std::map< OUString, componentParts > Components;
+
+bool getComponent(OUString const & path, OUString * component)
+{
+ OSL_ASSERT(component != nullptr);
+ if (path.isEmpty() || path[0] != '/') {
+ SAL_INFO( "desktop.migration", "configuration migration in/exclude path " << path << " ignored (does not start with slash)" );
+ return false;
+ }
+ sal_Int32 i = path.indexOf('/', 1);
+ *component = i < 0 ? path.copy(1) : path.copy(1, i - 1);
+ return true;
+}
+
+}
+
+void MigrationImpl::copyConfig()
+{
+ Components comps;
+ for (auto const& rMigrationStep : *m_vrMigrations) {
+ for (const OUString& rIncludePath : rMigrationStep.includeConfig) {
+ OUString comp;
+ if (getComponent(rIncludePath, &comp)) {
+ comps[comp].includedPaths.insert(rIncludePath);
+ }
+ }
+ for (const OUString& rExcludePath : rMigrationStep.excludeConfig) {
+ OUString comp;
+ if (getComponent(rExcludePath, &comp)) {
+ comps[comp].excludedPaths.insert(rExcludePath);
+ }
+ }
+ }
+
+ // check if the shared registrymodifications.xcu file exists
+ bool bRegistryModificationsXcuExists = false;
+ OUString regFilePath = m_aInfo.userdata + "/user/registrymodifications.xcu";
+ File regFile(regFilePath);
+ ::osl::FileBase::RC nError = regFile.open(osl_File_OpenFlag_Read);
+ if ( nError == ::osl::FileBase::E_None ) {
+ bRegistryModificationsXcuExists = true;
+ regFile.close();
+ }
+
+ for (auto const& comp : comps)
+ {
+ if (!comp.second.includedPaths.empty()) {
+ if (!bRegistryModificationsXcuExists) {
+ // shared registrymodifications.xcu does not exists
+ // the configuration is split in many registry files
+ // determine the file names from the first element in included paths
+ OUStringBuffer buf(m_aInfo.userdata);
+ buf.append("/user/registry/data");
+ sal_Int32 n = 0;
+ do {
+ OUString seg(comp.first.getToken(0, '.', n));
+ OUString enc(
+ rtl::Uri::encode(
+ seg, rtl_UriCharClassPchar, rtl_UriEncodeStrict,
+ RTL_TEXTENCODING_UTF8));
+ if (enc.isEmpty() && !seg.isEmpty()) {
+ SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (cannot be encoded as file path)" );
+ goto next;
+ }
+ buf.append('/');
+ buf.append(enc);
+ } while (n >= 0);
+ buf.append(".xcu");
+ regFilePath = buf.toString();
+ }
+ configuration::Update::get(
+ comphelper::getProcessComponentContext())->
+ insertModificationXcuFile(
+ regFilePath,
+ comphelper::containerToSequence(comp.second.includedPaths),
+ comphelper::containerToSequence(comp.second.excludedPaths));
+ } else {
+ SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (only excludes, no includes)" );
+ }
+next:
+ ;
+ }
+}
+
+uno::Reference< XNameAccess > MigrationImpl::getConfigAccess(const char* pPath, bool bUpdate)
+{
+ uno::Reference< XNameAccess > xNameAccess;
+ try {
+ OUString sAccessSrvc;
+ if (bUpdate)
+ sAccessSrvc = "com.sun.star.configuration.ConfigurationUpdateAccess";
+ else
+ sAccessSrvc = "com.sun.star.configuration.ConfigurationAccess";
+
+ OUString sConfigURL = OUString::createFromAscii(pPath);
+
+ uno::Reference< XMultiServiceFactory > theConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()));
+
+ // access the provider
+ uno::Sequence< uno::Any > theArgs {uno::makeAny(sConfigURL)};
+ xNameAccess.set(
+ theConfigProvider->createInstanceWithArguments(
+ sAccessSrvc, theArgs ), uno::UNO_QUERY_THROW );
+ } catch (const css::uno::Exception&) {
+ TOOLS_WARN_EXCEPTION("desktop.migration", "ignoring");
+ }
+ return xNameAccess;
+}
+
+void MigrationImpl::copyFiles()
+{
+ OUString localName;
+ OUString destName;
+ OUString userInstall;
+ utl::Bootstrap::PathStatus aStatus;
+ aStatus = utl::Bootstrap::locateUserInstallation(userInstall);
+ if (aStatus == utl::Bootstrap::PATH_EXISTS) {
+ for (auto const& rFile : *m_vrFileList)
+ {
+ // remove installation prefix from file
+ localName = rFile.copy(m_aInfo.userdata.getLength());
+ if (localName.endsWith( "/autocorr/acor_.dat")) {
+ // Previous versions used an empty language tag for
+ // LANGUAGE_DONTKNOW with the "[All]" autocorrection entry.
+ // As of LibreOffice 4.0 it is 'und' for LANGUAGE_UNDETERMINED
+ // so the file name is "acor_und.dat".
+ localName = localName.copy( 0, localName.getLength() - 4) + "und.dat";
+ }
+ destName = userInstall + localName;
+ INetURLObject aURL(destName);
+ // check whether destination directory exists
+ aURL.removeSegment();
+ _checkAndCreateDirectory(aURL);
+ FileBase::RC copyResult = File::copy(rFile, destName);
+ if (copyResult != FileBase::E_None) {
+ SAL_WARN( "desktop", "Cannot copy " << rFile << " to " << destName);
+ }
+ }
+ } else {
+ OSL_FAIL("copyFiles: UserInstall does not exist");
+ }
+}
+
+void MigrationImpl::runServices()
+{
+ // Build argument array
+ uno::Sequence< uno::Any > seqArguments(3);
+ seqArguments[0] <<= NamedValue("Productname",
+ uno::makeAny(m_aInfo.productname));
+ seqArguments[1] <<= NamedValue("UserData",
+ uno::makeAny(m_aInfo.userdata));
+
+
+ // create an instance of every migration service
+ // and execute the migration job
+ uno::Reference< XJob > xMigrationJob;
+
+ uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext());
+ for (auto const& rMigration : *m_vrMigrations)
+ {
+ if( !rMigration.service.isEmpty()) {
+
+ try {
+ // set black list for extension migration
+ uno::Sequence< OUString > seqExtBlackList;
+ sal_uInt32 nSize = rMigration.excludeExtensions.size();
+ if ( nSize > 0 )
+ seqExtBlackList = comphelper::arrayToSequence< OUString >(
+ rMigration.excludeExtensions.data(), nSize );
+ seqArguments[2] <<= NamedValue("ExtensionBlackList",
+ uno::makeAny( seqExtBlackList ));
+
+ xMigrationJob.set(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(rMigration.service, seqArguments, xContext),
+ uno::UNO_QUERY_THROW);
+
+ xMigrationJob->execute(uno::Sequence< NamedValue >());
+
+
+ } catch (const Exception&) {
+ TOOLS_WARN_EXCEPTION( "desktop", "Execution of migration service failed. Service: "
+ << rMigration.service);
+ } catch (...) {
+ SAL_WARN( "desktop", "Execution of migration service failed (Exception caught).\nService: "
+ << rMigration.service << "\nNo message available");
+ }
+
+ }
+ }
+}
+
+std::vector< MigrationModuleInfo > MigrationImpl::dectectUIChangesForAllModules() const
+{
+ std::vector< MigrationModuleInfo > vModulesInfo;
+ const OUString MENUBAR("menubar");
+ const OUString TOOLBAR("toolbar");
+
+ uno::Sequence< uno::Any > lArgs {uno::makeAny(m_aInfo.userdata + "/user/config/soffice.cfg/modules"),
+ uno::makeAny(embed::ElementModes::READ)};
+
+ uno::Reference< lang::XSingleServiceFactory > xStorageFactory(
+ embed::FileSystemStorageFactory::create(comphelper::getProcessComponentContext()));
+ uno::Reference< embed::XStorage > xModules;
+
+ xModules.set(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY);
+ if (!xModules.is())
+ return vModulesInfo;
+
+ uno::Sequence< OUString > lNames = xModules->getElementNames();
+ sal_Int32 nLength = lNames.getLength();
+ for (sal_Int32 i=0; i<nLength; ++i) {
+ OUString sModuleShortName = lNames[i];
+ uno::Reference< embed::XStorage > xModule = xModules->openStorageElement(sModuleShortName, embed::ElementModes::READ);
+ if (xModule.is()) {
+ MigrationModuleInfo aModuleInfo;
+
+ uno::Reference< embed::XStorage > xMenubar = xModule->openStorageElement(MENUBAR, embed::ElementModes::READ);
+ if (xMenubar.is()) {
+ if (xMenubar->getElementNames().hasElements()) {
+ aModuleInfo.sModuleShortName = sModuleShortName;
+ aModuleInfo.bHasMenubar = true;
+ }
+ }
+
+ uno::Reference< embed::XStorage > xToolbar = xModule->openStorageElement(TOOLBAR, embed::ElementModes::READ);
+ if (xToolbar.is()) {
+ const ::uno::Sequence< OUString > lToolbars = xToolbar->getElementNames();
+ for (OUString const & sToolbarName : lToolbars) {
+ if (sToolbarName.startsWith("custom_"))
+ continue;
+
+ aModuleInfo.sModuleShortName = sModuleShortName;
+ sal_Int32 nIndex = sToolbarName.lastIndexOf('.');
+ if (nIndex > 0) {
+ OUString sExtension(sToolbarName.copy(nIndex));
+ OUString sToolbarResourceName(sToolbarName.copy(0, nIndex));
+ if (!sToolbarResourceName.isEmpty() && sExtension == ".xml")
+ aModuleInfo.m_vToolbars.push_back(sToolbarResourceName);
+ }
+ }
+ }
+
+ if (!aModuleInfo.sModuleShortName.isEmpty())
+ vModulesInfo.push_back(aModuleInfo);
+ }
+ }
+
+ return vModulesInfo;
+}
+
+void MigrationImpl::compareOldAndNewConfig(const OUString& sParent,
+ const uno::Reference< container::XIndexContainer >& xIndexOld,
+ const uno::Reference< container::XIndexContainer >& xIndexNew,
+ const OUString& sResourceURL)
+{
+ const OUString MENU_SEPARATOR(" | ");
+
+ std::vector< MigrationItem > vOldItems;
+ std::vector< MigrationItem > vNewItems;
+ uno::Sequence< beans::PropertyValue > aProps;
+ sal_Int32 nOldCount = xIndexOld->getCount();
+ sal_Int32 nNewCount = xIndexNew->getCount();
+
+ for (int n=0; n<nOldCount; ++n) {
+ MigrationItem aMigrationItem;
+ if (xIndexOld->getByIndex(n) >>= aProps) {
+ for(beans::PropertyValue const & prop : std::as_const(aProps)) {
+ if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ prop.Value >>= aMigrationItem.m_sCommandURL;
+ else if ( prop.Name == ITEM_DESCRIPTOR_CONTAINER )
+ prop.Value >>= aMigrationItem.m_xPopupMenu;
+ }
+
+ if (!aMigrationItem.m_sCommandURL.isEmpty())
+ vOldItems.push_back(aMigrationItem);
+ }
+ }
+
+ for (int n=0; n<nNewCount; ++n) {
+ MigrationItem aMigrationItem;
+ if (xIndexNew->getByIndex(n) >>= aProps) {
+ for(beans::PropertyValue const & prop : std::as_const(aProps)) {
+ if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ prop.Value >>= aMigrationItem.m_sCommandURL;
+ else if ( prop.Name == ITEM_DESCRIPTOR_CONTAINER )
+ prop.Value >>= aMigrationItem.m_xPopupMenu;
+ }
+
+ if (!aMigrationItem.m_sCommandURL.isEmpty())
+ vNewItems.push_back(aMigrationItem);
+ }
+ }
+
+ OUString sSibling;
+ for (auto const& oldItem : vOldItems)
+ {
+ std::vector< MigrationItem >::iterator pFound = std::find(vNewItems.begin(), vNewItems.end(), oldItem);
+ if (pFound != vNewItems.end() && oldItem.m_xPopupMenu.is()) {
+ OUString sName;
+ if (!sParent.isEmpty())
+ sName = sParent + MENU_SEPARATOR + oldItem.m_sCommandURL;
+ else
+ sName = oldItem.m_sCommandURL;
+ compareOldAndNewConfig(sName, oldItem.m_xPopupMenu, pFound->m_xPopupMenu, sResourceURL);
+ } else if (pFound == vNewItems.end()) {
+ MigrationItem aMigrationItem(sParent, sSibling, oldItem.m_sCommandURL, oldItem.m_xPopupMenu);
+ if (m_aOldVersionItemsHashMap.find(sResourceURL)==m_aOldVersionItemsHashMap.end()) {
+ std::vector< MigrationItem > vMigrationItems;
+ m_aOldVersionItemsHashMap.emplace(sResourceURL, vMigrationItems);
+ m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem);
+ } else {
+ if (std::find(m_aOldVersionItemsHashMap[sResourceURL].begin(), m_aOldVersionItemsHashMap[sResourceURL].end(), aMigrationItem)==m_aOldVersionItemsHashMap[sResourceURL].end())
+ m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem);
+ }
+ }
+
+ sSibling = oldItem.m_sCommandURL;
+ }
+}
+
+void MigrationImpl::mergeOldToNewVersion(const uno::Reference< ui::XUIConfigurationManager >& xCfgManager,
+ const uno::Reference< container::XIndexContainer>& xIndexContainer,
+ const OUString& sModuleIdentifier,
+ const OUString& sResourceURL)
+{
+ MigrationHashMap::iterator pFound = m_aOldVersionItemsHashMap.find(sResourceURL);
+ if (pFound==m_aOldVersionItemsHashMap.end())
+ return;
+
+ for (auto const& elem : pFound->second)
+ {
+ uno::Reference< container::XIndexContainer > xTemp = xIndexContainer;
+
+ OUString sParentNodeName = elem.m_sParentNodeName;
+ sal_Int32 nIndex = 0;
+ do {
+ OUString sToken = sParentNodeName.getToken(0, '|', nIndex).trim();
+ if (sToken.isEmpty())
+ break;
+
+ sal_Int32 nCount = xTemp->getCount();
+ for (sal_Int32 i=0; i<nCount; ++i) {
+ OUString sCommandURL;
+ OUString sLabel;
+ uno::Reference< container::XIndexContainer > xChild;
+
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ xTemp->getByIndex(i) >>= aPropSeq;
+ for (beans::PropertyValue const & prop : std::as_const(aPropSeq)) {
+ OUString sPropName = prop.Name;
+ if ( sPropName == ITEM_DESCRIPTOR_COMMANDURL )
+ prop.Value >>= sCommandURL;
+ else if ( sPropName == ITEM_DESCRIPTOR_LABEL )
+ prop.Value >>= sLabel;
+ else if ( sPropName == ITEM_DESCRIPTOR_CONTAINER )
+ prop.Value >>= xChild;
+ }
+
+ if (sCommandURL == sToken) {
+ xTemp = xChild;
+ break;
+ }
+ }
+
+ } while (nIndex >= 0);
+
+ if (nIndex == -1) {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(elem.m_sCommandURL, sModuleIdentifier);
+ uno::Sequence< beans::PropertyValue > aPropSeq {
+ beans::PropertyValue(ITEM_DESCRIPTOR_COMMANDURL, 0, uno::makeAny(elem.m_sCommandURL), beans::PropertyState_DIRECT_VALUE),
+ beans::PropertyValue(ITEM_DESCRIPTOR_LABEL, 0, uno::makeAny(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)), beans::PropertyState_DIRECT_VALUE),
+ beans::PropertyValue(ITEM_DESCRIPTOR_CONTAINER, 0, uno::makeAny(elem.m_xPopupMenu), beans::PropertyState_DIRECT_VALUE)
+ };
+
+ if (elem.m_sPrevSibling.isEmpty())
+ xTemp->insertByIndex(0, uno::makeAny(aPropSeq));
+ else {
+ sal_Int32 nCount = xTemp->getCount();
+ sal_Int32 i = 0;
+ for (; i<nCount; ++i) {
+ OUString sCmd;
+ uno::Sequence< beans::PropertyValue > aTempPropSeq;
+ xTemp->getByIndex(i) >>= aTempPropSeq;
+ for (beans::PropertyValue const & prop : std::as_const(aTempPropSeq)) {
+ if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) {
+ prop.Value >>= sCmd;
+ break;
+ }
+ }
+
+ if (sCmd == elem.m_sPrevSibling)
+ break;
+ }
+
+ xTemp->insertByIndex(i+1, uno::makeAny(aPropSeq));
+ }
+ }
+ }
+
+ if (xIndexContainer.is())
+ xCfgManager->replaceSettings(sResourceURL, xIndexContainer);
+
+ uno::Reference< ui::XUIConfigurationPersistence > xUIConfigurationPersistence(xCfgManager, uno::UNO_QUERY);
+ if (xUIConfigurationPersistence.is())
+ xUIConfigurationPersistence->store();
+}
+
+uno::Reference< ui::XUIConfigurationManager > NewVersionUIInfo::getConfigManager(const OUString& sModuleShortName) const
+{
+ uno::Reference< ui::XUIConfigurationManager > xCfgManager;
+
+ for ( const css::beans::PropertyValue& rProp : m_lCfgManagerSeq) {
+ if (rProp.Name == sModuleShortName) {
+ rProp.Value >>= xCfgManager;
+ break;
+ }
+ }
+
+ return xCfgManager;
+}
+
+uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewMenubarSettings(const OUString& sModuleShortName) const
+{
+ uno::Reference< container::XIndexContainer > xNewMenuSettings;
+
+ for (auto const & prop : m_lNewVersionMenubarSettingsSeq) {
+ if (prop.Name == sModuleShortName) {
+ prop.Value >>= xNewMenuSettings;
+ break;
+ }
+ }
+
+ return xNewMenuSettings;
+}
+
+uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewToolbarSettings(const OUString& sModuleShortName, const OUString& sToolbarName) const
+{
+ uno::Reference< container::XIndexContainer > xNewToolbarSettings;
+
+ for (auto const & newProp : m_lNewVersionToolbarSettingsSeq) {
+ if (newProp.Name == sModuleShortName) {
+ uno::Sequence< beans::PropertyValue > lToolbarSettingsSeq;
+ newProp.Value >>= lToolbarSettingsSeq;
+ for (auto const & prop : std::as_const(lToolbarSettingsSeq)) {
+ if (prop.Name == sToolbarName) {
+ prop.Value >>= xNewToolbarSettings;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return xNewToolbarSettings;
+}
+
+void NewVersionUIInfo::init(const std::vector< MigrationModuleInfo >& vModulesInfo)
+{
+ m_lCfgManagerSeq.resize(vModulesInfo.size());
+ m_lNewVersionMenubarSettingsSeq.realloc(vModulesInfo.size());
+ m_lNewVersionToolbarSettingsSeq.realloc(vModulesInfo.size());
+
+ const OUString sMenubarResourceURL("private:resource/menubar/menubar");
+ const OUString sToolbarResourcePre("private:resource/toolbar/");
+
+ uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = ui::theModuleUIConfigurationManagerSupplier::get( ::comphelper::getProcessComponentContext() );
+
+ for (size_t i=0; i<vModulesInfo.size(); ++i) {
+ OUString sModuleIdentifier = mapModuleShortNameToIdentifier(vModulesInfo[i].sModuleShortName);
+ if (!sModuleIdentifier.isEmpty()) {
+ uno::Reference< ui::XUIConfigurationManager > xCfgManager = xModuleCfgSupplier->getUIConfigurationManager(sModuleIdentifier);
+ m_lCfgManagerSeq[i].Name = vModulesInfo[i].sModuleShortName;
+ m_lCfgManagerSeq[i].Value <<= xCfgManager;
+
+ if (vModulesInfo[i].bHasMenubar) {
+ m_lNewVersionMenubarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName;
+ m_lNewVersionMenubarSettingsSeq[i].Value <<= xCfgManager->getSettings(sMenubarResourceURL, true);
+ }
+
+ sal_Int32 nToolbars = vModulesInfo[i].m_vToolbars.size();
+ if (nToolbars > 0) {
+ uno::Sequence< beans::PropertyValue > lPropSeq(nToolbars);
+ for (sal_Int32 j=0; j<nToolbars; ++j) {
+ OUString sToolbarName = vModulesInfo[i].m_vToolbars[j];
+ OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName;
+
+ lPropSeq[j].Name = sToolbarName;
+ lPropSeq[j].Value <<= xCfgManager->getSettings(sToolbarResourceURL, true);
+ }
+
+ m_lNewVersionToolbarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName;
+ m_lNewVersionToolbarSettingsSeq[i].Value <<= lPropSeq;
+ }
+ }
+ }
+}
+
+} // namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/migration_impl.hxx b/desktop/source/migration/migration_impl.hxx
new file mode 100644
index 000000000..a66c2ea0f
--- /dev/null
+++ b/desktop/source/migration/migration_impl.hxx
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_MIGRATION_IMPL_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_MIGRATION_IMPL_HXX
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager.hpp>
+
+namespace desktop
+{
+
+struct install_info
+{
+ OUString productname; // human readable product name
+ OUString userdata; // file: url for user installation
+};
+
+typedef std::vector< OUString > strings_v;
+typedef std::unique_ptr< strings_v > strings_vr;
+
+struct migration_step
+{
+ strings_v includeFiles;
+ strings_v excludeFiles;
+ strings_v includeConfig;
+ strings_v excludeConfig;
+ strings_v excludeExtensions;
+ OUString service;
+};
+
+struct supported_migration
+{
+ OUString name;
+ sal_Int32 nPriority;
+ strings_v supported_versions;
+};
+
+typedef std::vector< migration_step > migrations_v;
+typedef std::unique_ptr< migrations_v > migrations_vr;
+typedef std::vector< supported_migration > migrations_available;
+
+inline bool areBothOpenFrom(OUString const & cmd1, OUString const & cmd2)
+{
+ return cmd1 == ".uno:Open" && cmd2.startsWith(".uno:OpenFrom");
+}
+
+/**
+ define the item, e.g.:menuitem, toolbaritem, to be migrated. we keep the information
+ of the command URL, the previous sibling node and the parent node of an item
+*/
+struct MigrationItem
+{
+ OUString m_sParentNodeName;
+ OUString m_sPrevSibling;
+ OUString m_sCommandURL;
+ css::uno::Reference< css::container::XIndexContainer > m_xPopupMenu;
+
+ MigrationItem()
+ {
+ }
+
+ MigrationItem(const OUString& sParentNodeName,
+ const OUString& sPrevSibling,
+ const OUString& sCommandURL,
+ const css::uno::Reference< css::container::XIndexContainer > & xPopupMenu)
+ : m_sParentNodeName(sParentNodeName), m_sPrevSibling(sPrevSibling),
+ m_sCommandURL(sCommandURL), m_xPopupMenu(xPopupMenu)
+ {
+ }
+
+ bool operator==(const MigrationItem& aMigrationItem) const
+ {
+ return
+ (aMigrationItem.m_sCommandURL == m_sCommandURL
+ || areBothOpenFrom(aMigrationItem.m_sCommandURL, m_sCommandURL)
+ || areBothOpenFrom(m_sCommandURL, aMigrationItem.m_sCommandURL))
+ && aMigrationItem.m_sParentNodeName == m_sParentNodeName
+ && aMigrationItem.m_sPrevSibling == m_sPrevSibling
+ && aMigrationItem.m_xPopupMenu.is() == m_xPopupMenu.is();
+ }
+};
+
+typedef std::unordered_map< OUString, std::vector< MigrationItem > > MigrationHashMap;
+
+/**
+ information for the UI elements to be migrated for one module
+*/
+struct MigrationModuleInfo
+{
+ OUString sModuleShortName;
+ bool bHasMenubar;
+ std::vector< OUString > m_vToolbars;
+
+ MigrationModuleInfo() : bHasMenubar(false) {};
+};
+
+
+/**
+ get the information before copying the ui configuration files of old version to new version
+*/
+class NewVersionUIInfo
+{
+public:
+
+ css::uno::Reference< css::ui::XUIConfigurationManager > getConfigManager(const OUString& sModuleShortName) const;
+ css::uno::Reference< css::container::XIndexContainer > getNewMenubarSettings(const OUString& sModuleShortName) const;
+ css::uno::Reference< css::container::XIndexContainer > getNewToolbarSettings(const OUString& sModuleShortName, const OUString& sToolbarName) const;
+ void init(const std::vector< MigrationModuleInfo >& vModulesInfo);
+
+private:
+
+ std::vector< css::beans::PropertyValue > m_lCfgManagerSeq;
+ css::uno::Sequence< css::beans::PropertyValue > m_lNewVersionMenubarSettingsSeq;
+ css::uno::Sequence< css::beans::PropertyValue > m_lNewVersionToolbarSettingsSeq;
+};
+
+class MigrationImpl
+{
+
+private:
+ migrations_available m_vMigrationsAvailable; // list of all available migrations
+ migrations_vr m_vrMigrations; // list of all migration specs from config
+ install_info m_aInfo; // info about the version being migrated
+ strings_vr m_vrFileList; // final list of files to be copied
+ MigrationHashMap m_aOldVersionItemsHashMap;
+
+ // functions to control the migration process
+ static void readAvailableMigrations(migrations_available&);
+ bool alreadyMigrated();
+ static migrations_vr readMigrationSteps(const OUString& rMigrationName);
+ sal_Int32 findPreferredMigrationProcess(const migrations_available&);
+#if defined UNX && ! defined MACOSX
+ static OUString preXDGConfigDir(const OUString& rConfigDir);
+#endif
+ static void setInstallInfoIfExist(install_info& aInfo, const OUString& rConfigDir, const OUString& rVersion);
+ static install_info findInstallation(const strings_v& rVersions);
+ strings_vr compileFileList();
+
+ // helpers
+ strings_vr getAllFiles(const OUString& baseURL) const;
+ static strings_vr applyPatterns(const strings_v& vSet, const strings_v& vPatterns);
+ static css::uno::Reference< css::container::XNameAccess > getConfigAccess(const char* path, bool rw=false);
+
+ std::vector< MigrationModuleInfo > dectectUIChangesForAllModules() const;
+ void compareOldAndNewConfig(const OUString& sParentNodeName,
+ const css::uno::Reference< css::container::XIndexContainer >& xOldIndexContainer,
+ const css::uno::Reference< css::container::XIndexContainer >& xNewIndexContainer,
+ const OUString& sToolbarName);
+ void mergeOldToNewVersion(const css::uno::Reference< css::ui::XUIConfigurationManager >& xCfgManager,
+ const css::uno::Reference< css::container::XIndexContainer>& xIndexContainer,
+ const OUString& sModuleIdentifier,
+ const OUString& sResourceURL);
+
+ // actual processing function that perform the migration steps
+ void copyFiles();
+ void copyConfig();
+ void runServices();
+
+ static void setMigrationCompleted();
+ static bool checkMigrationCompleted();
+
+public:
+ MigrationImpl();
+ ~MigrationImpl();
+ bool initializeMigration();
+ bool doMigration();
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/basicmigration.cxx b/desktop/source/migration/services/basicmigration.cxx
new file mode 100644
index 000000000..bec200e21
--- /dev/null
+++ b/desktop/source/migration/services/basicmigration.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "basicmigration.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace migration
+{
+
+
+ #define sSourceUserBasic "/user/basic"
+ #define sTargetUserBasic "/user/__basic_80"
+
+
+ // component operations
+
+
+ OUString BasicMigration_getImplementationName()
+ {
+ return "com.sun.star.comp.desktop.migration.Basic";
+ }
+
+
+ Sequence< OUString > BasicMigration_getSupportedServiceNames()
+ {
+ Sequence< OUString > aNames { "com.sun.star.migration.Basic" };
+ return aNames;
+ }
+
+
+ // BasicMigration
+
+
+ BasicMigration::BasicMigration()
+ {
+ }
+
+
+ BasicMigration::~BasicMigration()
+ {
+ }
+
+
+ TStringVectorPtr BasicMigration::getFiles( const OUString& rBaseURL ) const
+ {
+ TStringVectorPtr aResult( new TStringVector );
+ ::osl::Directory aDir( rBaseURL);
+
+ if ( aDir.open() == ::osl::FileBase::E_None )
+ {
+ // iterate over directory content
+ TStringVector aSubDirs;
+ ::osl::DirectoryItem aItem;
+ while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None )
+ {
+ ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL );
+ if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None )
+ {
+ if ( aFileStatus.getFileType() == ::osl::FileStatus::Directory )
+ aSubDirs.push_back( aFileStatus.getFileURL() );
+ else
+ aResult->push_back( aFileStatus.getFileURL() );
+ }
+ }
+
+ // iterate recursive over subfolders
+ for (auto const& subDir : aSubDirs)
+ {
+ TStringVectorPtr aSubResult = getFiles(subDir);
+ aResult->insert( aResult->end(), aSubResult->begin(), aSubResult->end() );
+ }
+ }
+
+ return aResult;
+ }
+
+
+ void BasicMigration::checkAndCreateDirectory( INetURLObject const & rDirURL )
+ {
+ ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ if ( aResult == ::osl::FileBase::E_NOENT )
+ {
+ INetURLObject aBaseURL( rDirURL );
+ aBaseURL.removeSegment();
+ checkAndCreateDirectory( aBaseURL );
+ ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ }
+ }
+
+
+ void BasicMigration::copyFiles()
+ {
+ OUString sTargetDir;
+ ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( sTargetDir );
+ if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
+ {
+ sTargetDir += sTargetUserBasic;
+ TStringVectorPtr aFileList = getFiles( m_sSourceDir );
+ for (auto const& elem : *aFileList)
+ {
+ OUString sLocalName = elem.copy( m_sSourceDir.getLength() );
+ OUString sTargetName = sTargetDir + sLocalName;
+ INetURLObject aURL( sTargetName );
+ aURL.removeSegment();
+ checkAndCreateDirectory( aURL );
+ ::osl::FileBase::RC aResult = ::osl::File::copy( elem, sTargetName );
+ if ( aResult != ::osl::FileBase::E_None )
+ {
+ SAL_WARN( "desktop", "BasicMigration::copyFiles: cannot copy "
+ << elem << " to " << sTargetName );
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "BasicMigration::copyFiles: no user installation!" );
+ }
+ }
+
+
+ // XServiceInfo
+
+
+ OUString BasicMigration::getImplementationName()
+ {
+ return BasicMigration_getImplementationName();
+ }
+
+
+ sal_Bool BasicMigration::supportsService(OUString const & ServiceName)
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+
+ Sequence< OUString > BasicMigration::getSupportedServiceNames()
+ {
+ return BasicMigration_getSupportedServiceNames();
+ }
+
+
+ // XInitialization
+
+
+ void BasicMigration::initialize( const Sequence< Any >& aArguments )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const Any* pIter = aArguments.getConstArray();
+ const Any* pEnd = pIter + aArguments.getLength();
+ for ( ; pIter != pEnd ; ++pIter )
+ {
+ beans::NamedValue aValue;
+ *pIter >>= aValue;
+ if ( aValue.Name == "UserData" )
+ {
+ if ( !(aValue.Value >>= m_sSourceDir) )
+ {
+ OSL_FAIL( "BasicMigration::initialize: argument UserData has wrong type!" );
+ }
+ m_sSourceDir += sSourceUserBasic;
+ break;
+ }
+ }
+ }
+
+
+ // XJob
+
+
+ Any BasicMigration::execute( const Sequence< beans::NamedValue >& )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ copyFiles();
+
+ return Any();
+ }
+
+
+ // component operations
+
+
+ Reference< XInterface > BasicMigration_create(
+ Reference< XComponentContext > const & )
+ {
+ return static_cast< lang::XTypeProvider * >( new BasicMigration() );
+ }
+
+
+} // namespace migration
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/basicmigration.hxx b/desktop/source/migration/services/basicmigration.hxx
new file mode 100644
index 000000000..88f668581
--- /dev/null
+++ b/desktop/source/migration/services/basicmigration.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_BASICMIGRATION_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_BASICMIGRATION_HXX
+
+#include "misc.hxx"
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+
+
+class INetURLObject;
+
+
+namespace migration
+{
+
+
+ OUString BasicMigration_getImplementationName();
+ css::uno::Sequence< OUString > BasicMigration_getSupportedServiceNames();
+ css::uno::Reference< css::uno::XInterface > BasicMigration_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::task::XJob > BasicMigration_BASE;
+
+ class BasicMigration : public BasicMigration_BASE
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ OUString m_sSourceDir;
+
+ TStringVectorPtr getFiles( const OUString& rBaseURL ) const;
+ void checkAndCreateDirectory( INetURLObject const & rDirURL );
+ void copyFiles();
+
+ public:
+ BasicMigration();
+ virtual ~BasicMigration() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XJob
+ virtual css::uno::Any SAL_CALL execute(
+ const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override;
+ };
+
+
+} // namespace migration
+
+
+#endif // INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_BASICMIGRATION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/cexports.cxx b/desktop/source/migration/services/cexports.cxx
new file mode 100644
index 000000000..6f0e50d9a
--- /dev/null
+++ b/desktop/source/migration/services/cexports.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/implementationentry.hxx>
+#include "basicmigration.hxx"
+#include "wordbookmigration.hxx"
+
+
+extern "C"
+{
+
+::cppu::ImplementationEntry const oo2_entries [] =
+{
+ {
+ migration::BasicMigration_create, migration::BasicMigration_getImplementationName,
+ migration::BasicMigration_getSupportedServiceNames, ::cppu::createSingleComponentFactory,
+ nullptr, 0
+ },
+ {
+ migration::WordbookMigration_create, migration::WordbookMigration_getImplementationName,
+ migration::WordbookMigration_getSupportedServiceNames, ::cppu::createSingleComponentFactory,
+ nullptr, 0
+ },
+ // Extension migration was disabled by Oracle / OpenOffice.org
+#if 0
+ {
+ migration::ExtensionMigration_create, migration::ExtensionMigration_getImplementationName,
+ migration::ExtensionMigration_getSupportedServiceNames, ::cppu::createSingleComponentFactory,
+ 0, 0
+ },
+#endif
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
+};
+
+
+SAL_DLLPUBLIC_EXPORT void * migrationoo2_component_getFactory(
+ const char * pImplName, void * pServiceManager, void * pRegistryKey )
+{
+ return ::cppu::component_getFactoryHelper(
+ pImplName, pServiceManager, pRegistryKey, oo2_entries );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/cexportsoo3.cxx b/desktop/source/migration/services/cexportsoo3.cxx
new file mode 100644
index 000000000..d2a82c1a8
--- /dev/null
+++ b/desktop/source/migration/services/cexportsoo3.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/implementationentry.hxx>
+#include "oo3extensionmigration.hxx"
+
+extern "C"
+{
+
+::cppu::ImplementationEntry const oo3_entries [] =
+{
+ {
+ migration::OO3ExtensionMigration_create, migration::OO3ExtensionMigration_getImplementationName,
+ migration::OO3ExtensionMigration_getSupportedServiceNames, ::cppu::createSingleComponentFactory,
+ nullptr, 0
+ },
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
+};
+
+
+SAL_DLLPUBLIC_EXPORT void * migrationoo3_component_getFactory(
+ const char * pImplName, void * pServiceManager, void * pRegistryKey )
+{
+ return ::cppu::component_getFactoryHelper(
+ pImplName, pServiceManager, pRegistryKey, oo3_entries );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/cppumaker.mk b/desktop/source/migration/services/cppumaker.mk
new file mode 100644
index 000000000..57e070f80
--- /dev/null
+++ b/desktop/source/migration/services/cppumaker.mk
@@ -0,0 +1,27 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+.IF "$(debug)" != ""
+
+# MSVC++: no inlining
+.IF "$(COM)" == "MSC"
+CFLAGS += -Ob0
+.ENDIF
+
+.ENDIF
+
diff --git a/desktop/source/migration/services/jvmfwk.cxx b/desktop/source/migration/services/jvmfwk.cxx
new file mode 100644
index 000000000..20a7c2ff2
--- /dev/null
+++ b/desktop/source/migration/services/jvmfwk.cxx
@@ -0,0 +1,395 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/types.h>
+#include <sal/config.h>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/configuration/backend/XLayer.hpp>
+#include <com/sun/star/configuration/backend/XLayerHandler.hpp>
+#include <com/sun/star/configuration/backend/MalformedDataException.hpp>
+#include <com/sun/star/configuration/backend/TemplateIdentifier.hpp>
+#include <jvmfwk/framework.hxx>
+#include "jvmfwk.hxx"
+#include <memory>
+#include <stack>
+#include <stdio.h>
+
+#include <osl/diagnose.h>
+
+#define SERVICE_NAME "com.sun.star.migration.Java"
+#define IMPL_NAME "com.sun.star.comp.desktop.migration.Java"
+
+#define ENABLE_JAVA 1
+#define USER_CLASS_PATH 2
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::configuration::backend;
+
+namespace migration
+{
+
+namespace {
+
+class JavaMigration : public ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::task::XJob,
+ css::configuration::backend::XLayerHandler>
+{
+public:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ //XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ //XJob
+ virtual css::uno::Any SAL_CALL execute(
+ const css::uno::Sequence<css::beans::NamedValue >& Arguments ) override;
+
+ // XLayerHandler
+ virtual void SAL_CALL startLayer() override;
+
+ virtual void SAL_CALL endLayer() override;
+
+ virtual void SAL_CALL overrideNode(
+ const OUString& aName,
+ sal_Int16 aAttributes,
+ sal_Bool bClear) override;
+
+ virtual void SAL_CALL addOrReplaceNode(
+ const OUString& aName,
+ sal_Int16 aAttributes) override;
+
+ virtual void SAL_CALL addOrReplaceNodeFromTemplate(
+ const OUString& aName,
+ const css::configuration::backend::TemplateIdentifier& aTemplate,
+ sal_Int16 aAttributes ) override;
+
+ virtual void SAL_CALL endNode() override;
+
+ virtual void SAL_CALL dropNode(
+ const OUString& aName ) override;
+
+ virtual void SAL_CALL overrideProperty(
+ const OUString& aName,
+ sal_Int16 aAttributes,
+ const css::uno::Type& aType,
+ sal_Bool bClear ) override;
+
+ virtual void SAL_CALL setPropertyValue(
+ const css::uno::Any& aValue ) override;
+
+ virtual void SAL_CALL setPropertyValueForLocale(
+ const css::uno::Any& aValue,
+ const OUString& aLocale ) override;
+
+ virtual void SAL_CALL endProperty() override;
+
+ virtual void SAL_CALL addProperty(
+ const OUString& aName,
+ sal_Int16 aAttributes,
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL addPropertyWithValue(
+ const OUString& aName,
+ sal_Int16 aAttributes,
+ const css::uno::Any& aValue ) override;
+
+
+ virtual ~JavaMigration() override;
+
+private:
+ OUString m_sUserDir;
+ css::uno::Reference< css::configuration::backend::XLayer> m_xLayer;
+
+ void migrateJavarc();
+ typedef std::pair< OUString, sal_Int16> TElementType;
+ typedef std::stack< TElementType > TElementStack;
+ TElementStack m_aStack;
+
+};
+
+}
+
+JavaMigration::~JavaMigration()
+{
+ OSL_ASSERT(m_aStack.empty());
+}
+
+OUString jvmfwk_getImplementationName()
+{
+ return IMPL_NAME;
+}
+
+css::uno::Sequence< OUString > jvmfwk_getSupportedServiceNames()
+{
+ return { SERVICE_NAME };
+}
+
+// XServiceInfo
+OUString SAL_CALL JavaMigration::getImplementationName()
+{
+ return jvmfwk_getImplementationName();
+}
+
+sal_Bool JavaMigration::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL JavaMigration::getSupportedServiceNames()
+{
+ return jvmfwk_getSupportedServiceNames();
+}
+
+//XInitialization ----------------------------------------------------------------------
+void SAL_CALL JavaMigration::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ const css::uno::Any* pIter = aArguments.getConstArray();
+ const css::uno::Any* pEnd = pIter + aArguments.getLength();
+ css::uno::Sequence<css::beans::NamedValue> aOldConfigValues;
+ css::beans::NamedValue aValue;
+ for(;pIter != pEnd;++pIter)
+ {
+ *pIter >>= aValue;
+ if ( aValue.Name == "OldConfiguration" )
+ {
+ bool bSuccess = aValue.Value >>= aOldConfigValues;
+ OSL_ENSURE(bSuccess, "[Service implementation " IMPL_NAME
+ "] XInitialization::initialize: Argument OldConfiguration has wrong type.");
+ if (bSuccess)
+ {
+ const css::beans::NamedValue* pIter2 = aOldConfigValues.getConstArray();
+ const css::beans::NamedValue* pEnd2 = pIter2 + aOldConfigValues.getLength();
+ for(;pIter2 != pEnd2;++pIter2)
+ {
+ if ( pIter2->Name == "org.openoffice.Office.Java" )
+ {
+ pIter2->Value >>= m_xLayer;
+ break;
+ }
+ }
+ }
+ }
+ else if ( aValue.Name == "UserData" )
+ {
+ if ( !(aValue.Value >>= m_sUserDir) )
+ {
+ OSL_FAIL(
+ "[Service implementation " IMPL_NAME
+ "] XInitialization::initialize: Argument UserData has wrong type.");
+ }
+ }
+ }
+
+}
+
+//XJob
+css::uno::Any SAL_CALL JavaMigration::execute(
+ const css::uno::Sequence<css::beans::NamedValue >& )
+{
+ migrateJavarc();
+ if (m_xLayer.is())
+ m_xLayer->readData(this);
+
+ return css::uno::Any();
+}
+
+void JavaMigration::migrateJavarc()
+{
+ if (m_sUserDir.isEmpty())
+ return;
+
+ OUString sValue;
+ rtl::Bootstrap javaini(m_sUserDir + "/user/config/" SAL_CONFIGFILE("java"));
+ bool bSuccess = javaini.getFrom("Home", sValue);
+ OSL_ENSURE(bSuccess, "[Service implementation " IMPL_NAME
+ "] XJob::execute: Could not get Home entry from java.ini/javarc.");
+ if (!(bSuccess && !sValue.isEmpty()))
+ return;
+
+ //get the directory
+ std::unique_ptr<JavaInfo> aInfo;
+ javaFrameworkError err = jfw_getJavaInfoByPath(sValue, &aInfo);
+
+ if (err == JFW_E_NONE)
+ {
+ if (jfw_setSelectedJRE(aInfo.get()) != JFW_E_NONE)
+ {
+ OSL_FAIL("[Service implementation " IMPL_NAME
+ "] XJob::execute: jfw_setSelectedJRE failed.");
+ fprintf(stderr, "\nCannot migrate Java. An error occurred.\n");
+ }
+ }
+ else if (err == JFW_E_FAILED_VERSION)
+ {
+ fprintf(stderr, "\nCannot migrate Java settings because the version of the Java "
+ "is not supported anymore.\n");
+ }
+}
+
+
+// XLayerHandler
+void SAL_CALL JavaMigration::startLayer()
+{
+}
+
+
+void SAL_CALL JavaMigration::endLayer()
+{
+}
+
+
+void SAL_CALL JavaMigration::overrideNode(
+ const OUString&,
+ sal_Int16,
+ sal_Bool)
+
+{
+
+}
+
+
+void SAL_CALL JavaMigration::addOrReplaceNode(
+ const OUString&,
+ sal_Int16)
+{
+
+}
+void SAL_CALL JavaMigration::endNode()
+{
+}
+
+
+void SAL_CALL JavaMigration::dropNode(
+ const OUString& )
+{
+}
+
+
+void SAL_CALL JavaMigration::overrideProperty(
+ const OUString& aName,
+ sal_Int16,
+ const Type&,
+ sal_Bool )
+{
+ if ( aName == "Enable" )
+ m_aStack.push(TElementStack::value_type(aName,ENABLE_JAVA));
+ else if ( aName == "UserClassPath" )
+ m_aStack.push(TElementStack::value_type(aName, USER_CLASS_PATH));
+}
+
+
+void SAL_CALL JavaMigration::setPropertyValue(
+ const Any& aValue )
+{
+ if ( m_aStack.empty())
+ return;
+
+ switch (m_aStack.top().second)
+ {
+ case ENABLE_JAVA:
+ {
+ bool val;
+ if (!(aValue >>= val))
+ throw MalformedDataException(
+ "[Service implementation " IMPL_NAME
+ "] XLayerHandler::setPropertyValue received wrong type for Enable property", nullptr, Any());
+ if (jfw_setEnabled(val) != JFW_E_NONE)
+ throw WrappedTargetException(
+ "[Service implementation " IMPL_NAME
+ "] XLayerHandler::setPropertyValue: jfw_setEnabled failed.", nullptr, Any());
+
+ break;
+ }
+ case USER_CLASS_PATH:
+ {
+ OUString cp;
+ if (!(aValue >>= cp))
+ throw MalformedDataException(
+ "[Service implementation " IMPL_NAME
+ "] XLayerHandler::setPropertyValue received wrong type for UserClassPath property", nullptr, Any());
+
+ if (jfw_setUserClassPath(cp) != JFW_E_NONE)
+ throw WrappedTargetException(
+ "[Service implementation " IMPL_NAME
+ "] XLayerHandler::setPropertyValue: jfw_setUserClassPath failed.", nullptr, Any());
+ break;
+ }
+ default:
+ OSL_ASSERT(false);
+ }
+}
+
+
+void SAL_CALL JavaMigration::setPropertyValueForLocale(
+ const Any&,
+ const OUString& )
+{
+}
+
+
+void SAL_CALL JavaMigration::endProperty()
+{
+ if (!m_aStack.empty())
+ m_aStack.pop();
+}
+
+
+void SAL_CALL JavaMigration::addProperty(
+ const OUString&,
+ sal_Int16,
+ const Type& )
+{
+}
+
+
+void SAL_CALL JavaMigration::addPropertyWithValue(
+ const OUString&,
+ sal_Int16,
+ const Any& )
+{
+}
+
+void SAL_CALL JavaMigration::addOrReplaceNodeFromTemplate(
+ const OUString&,
+ const TemplateIdentifier&,
+ sal_Int16 )
+{
+}
+
+
+//ToDo enable java, user class path
+
+} //end namespace jfw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/jvmfwk.hxx b/desktop/source/migration/services/jvmfwk.hxx
new file mode 100644
index 000000000..31c35feb4
--- /dev/null
+++ b/desktop/source/migration/services/jvmfwk.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_JVMFWK_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_JVMFWK_HXX
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+
+namespace migration
+{
+
+OUString jvmfwk_getImplementationName();
+
+css::uno::Sequence< OUString > jvmfwk_getSupportedServiceNames();
+
+} //end blind namespace
+
+#endif // INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_JVMFWK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/migrationoo2.component b/desktop/source/migration/services/migrationoo2.component
new file mode 100644
index 000000000..cadfb6a34
--- /dev/null
+++ b/desktop/source/migration/services/migrationoo2.component
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="migrationoo2" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.desktop.migration.Basic">
+ <service name="com.sun.star.migration.Basic"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.desktop.migration.Wordbooks">
+ <service name="com.sun.star.migration.Wordbooks"/>
+ </implementation>
+</component>
diff --git a/desktop/source/migration/services/migrationoo3.component b/desktop/source/migration/services/migrationoo3.component
new file mode 100644
index 000000000..1ff56cd68
--- /dev/null
+++ b/desktop/source/migration/services/migrationoo3.component
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="migrationoo3" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.desktop.migration.OOo3Extensions">
+ <service name="com.sun.star.migration.Extensions"/>
+ </implementation>
+</component>
diff --git a/desktop/source/migration/services/misc.hxx b/desktop/source/migration/services/misc.hxx
new file mode 100644
index 000000000..ca1320a2a
--- /dev/null
+++ b/desktop/source/migration/services/misc.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_MISC_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_MISC_HXX
+
+#include <rtl/ustring.hxx>
+
+#include <vector>
+#include <memory>
+
+
+namespace migration
+{
+
+
+ typedef std::vector< OUString > TStringVector;
+ typedef std::unique_ptr< TStringVector > TStringVectorPtr;
+
+
+} // namespace migration
+
+
+#endif // INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_MISC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/oo3extensionmigration.cxx b/desktop/source/migration/services/oo3extensionmigration.cxx
new file mode 100644
index 000000000..aec9fe051
--- /dev/null
+++ b/desktop/source/migration/services/oo3extensionmigration.cxx
@@ -0,0 +1,428 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "oo3extensionmigration.hxx"
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/bootstrap.hxx>
+#include <unotools/textsearch.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace migration
+{
+
+// component operations
+
+
+OUString OO3ExtensionMigration_getImplementationName()
+{
+ return "com.sun.star.comp.desktop.migration.OOo3Extensions";
+}
+
+
+Sequence< OUString > OO3ExtensionMigration_getSupportedServiceNames()
+{
+ return { "com.sun.star.migration.Extensions" };
+}
+
+
+// ExtensionMigration
+
+
+OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) :
+m_ctx(ctx)
+{
+}
+
+
+OO3ExtensionMigration::~OO3ExtensionMigration()
+{
+}
+
+void OO3ExtensionMigration::scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions )
+{
+ osl::Directory aScanRootDir( sSourceDir );
+ osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
+ osl::FileBase::RC nRetCode = aScanRootDir.open();
+ if ( nRetCode != osl::Directory::E_None )
+ return;
+
+ sal_uInt32 nHint( 0 );
+ osl::DirectoryItem aItem;
+ while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None )
+ {
+ if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) &&
+ ( fs.getFileType() == osl::FileStatus::Directory ))
+ {
+ //Check next folder as the "real" extension folder is below a temp folder!
+ OUString sExtensionFolderURL = fs.getFileURL();
+
+ osl::Directory aExtensionRootDir( sExtensionFolderURL );
+
+ nRetCode = aExtensionRootDir.open();
+ if ( nRetCode == osl::Directory::E_None )
+ {
+ osl::DirectoryItem aExtDirItem;
+ while ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None )
+ {
+ bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None;
+ bool bIsDir = fs.getFileType() == osl::FileStatus::Directory;
+
+ if ( bFileStatus && bIsDir )
+ {
+ sExtensionFolderURL = fs.getFileURL();
+ ScanResult eResult = scanExtensionFolder( sExtensionFolderURL );
+ if ( eResult == SCANRESULT_MIGRATE_EXTENSION )
+ aMigrateExtensions.push_back( sExtensionFolderURL );
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const OUString& sExtFolder )
+{
+ ScanResult aResult = SCANRESULT_NOTFOUND;
+ osl::Directory aDir(sExtFolder);
+
+ // get sub dirs
+ if (aDir.open() == osl::FileBase::E_None)
+ {
+ // work through directory contents...
+ osl::DirectoryItem item;
+ osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
+ TStringVector aDirectories;
+ while ((aDir.getNextItem(item) == osl::FileBase::E_None ) &&
+ ( aResult == SCANRESULT_NOTFOUND ))
+ {
+ if (item.getFileStatus(fs) == osl::FileBase::E_None)
+ {
+ OUString aDirEntryURL;
+ if (fs.getFileType() == osl::FileStatus::Directory)
+ aDirectories.push_back( fs.getFileURL() );
+ else
+ {
+ aDirEntryURL = fs.getFileURL();
+ if ( aDirEntryURL.indexOf( "/description.xml" ) > 0 )
+ aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION;
+ }
+ }
+ }
+
+ for (auto const& directory : aDirectories)
+ {
+ aResult = scanExtensionFolder(directory);
+ if (aResult != SCANRESULT_NOTFOUND)
+ break;
+ }
+ }
+ return aResult;
+}
+
+bool OO3ExtensionMigration::scanDescriptionXml( const OUString& sDescriptionXmlURL )
+{
+ if ( !m_xDocBuilder.is() )
+ {
+ m_xDocBuilder.set( xml::dom::DocumentBuilder::create(m_ctx) );
+ }
+
+ if ( !m_xSimpleFileAccess.is() )
+ {
+ m_xSimpleFileAccess = ucb::SimpleFileAccess::create(m_ctx);
+ }
+
+ OUString aExtIdentifier;
+ try
+ {
+ uno::Reference< io::XInputStream > xIn =
+ m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL );
+
+ if ( xIn.is() )
+ {
+ uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn );
+ if ( xDoc.is() )
+ {
+ uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement();
+ if ( xRoot.is() && xRoot->getTagName() == "description" )
+ {
+ uno::Reference< xml::xpath::XXPathAPI > xPath = xml::xpath::XPathAPI::create(m_ctx);
+
+ xPath->registerNS("desc", xRoot->getNamespaceURI());
+ xPath->registerNS("xlink", "http://www.w3.org/1999/xlink");
+
+ try
+ {
+ uno::Reference< xml::dom::XNode > xNode(
+ xPath->selectSingleNode(
+ xRoot, "desc:identifier/@value" ));
+ if ( xNode.is() )
+ aExtIdentifier = xNode->getNodeValue();
+ }
+ catch ( const xml::xpath::XPathException& )
+ {
+ }
+ catch ( const xml::dom::DOMException& )
+ {
+ }
+ }
+ }
+ }
+
+ if ( !aExtIdentifier.isEmpty() )
+ {
+ // scan extension identifier and try to match with our black list entries
+ for (const OUString & i : m_aBlackList)
+ {
+ utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
+ utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
+
+ sal_Int32 start = 0;
+ sal_Int32 end = aExtIdentifier.getLength();
+ if (ts.SearchForward(aExtIdentifier, &start, &end))
+ return false;
+ }
+ }
+ }
+ catch ( const ucb::CommandAbortedException& )
+ {
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+
+ if ( aExtIdentifier.isEmpty() )
+ {
+ // Fallback:
+ // Try to use the folder name to match our black list
+ // as some extensions don't provide an identifier in the
+ // description.xml!
+ for (const OUString & i : m_aBlackList)
+ {
+ utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp);
+ utl::TextSearch ts(param, LANGUAGE_DONTKNOW);
+
+ sal_Int32 start = 0;
+ sal_Int32 end = sDescriptionXmlURL.getLength();
+ if (ts.SearchForward(sDescriptionXmlURL, &start, &end))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void OO3ExtensionMigration::migrateExtension( const OUString& sSourceDir )
+{
+ css::uno::Reference< css::deployment::XExtensionManager > extMgr(
+ deployment::ExtensionManager::get( m_ctx ) );
+ try
+ {
+ TmpRepositoryCommandEnv* pCmdEnv = new TmpRepositoryCommandEnv();
+
+ uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
+ static_cast< cppu::OWeakObject* >( pCmdEnv ), uno::UNO_QUERY );
+ uno::Reference< task::XAbortChannel > xAbortChannel;
+ extMgr->addExtension(
+ sSourceDir, uno::Sequence<beans::NamedValue>(), "user",
+ xAbortChannel, xCmdEnv );
+ }
+ catch ( css::uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.migration",
+ "Ignoring UNO Exception while migrating extension from <" << sSourceDir << ">");
+ }
+}
+
+
+// XServiceInfo
+
+
+OUString OO3ExtensionMigration::getImplementationName()
+{
+ return OO3ExtensionMigration_getImplementationName();
+}
+
+
+sal_Bool OO3ExtensionMigration::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+Sequence< OUString > OO3ExtensionMigration::getSupportedServiceNames()
+{
+ return OO3ExtensionMigration_getSupportedServiceNames();
+}
+
+
+// XInitialization
+
+
+void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const Any* pIter = aArguments.getConstArray();
+ const Any* pEnd = pIter + aArguments.getLength();
+ for ( ; pIter != pEnd ; ++pIter )
+ {
+ beans::NamedValue aValue;
+ *pIter >>= aValue;
+ if ( aValue.Name == "UserData" )
+ {
+ if ( !(aValue.Value >>= m_sSourceDir) )
+ {
+ OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" );
+ }
+ }
+ else if ( aValue.Name == "ExtensionBlackList" )
+ {
+ Sequence< OUString > aBlackList;
+ if ( (aValue.Value >>= aBlackList ) && aBlackList.hasElements())
+ {
+ m_aBlackList.resize( aBlackList.getLength() );
+ ::comphelper::sequenceToArray< OUString >( m_aBlackList.data(), aBlackList );
+ }
+ }
+ }
+}
+
+Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir );
+ if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
+ {
+ // copy all extensions
+ OUString sSourceDir = m_sSourceDir +
+ "/user/uno_packages/cache/uno_packages";
+ TStringVector aExtensionToMigrate;
+ scanUserExtensions( sSourceDir, aExtensionToMigrate );
+ for (auto const& extensionToMigrate : aExtensionToMigrate)
+ {
+ migrateExtension(extensionToMigrate);
+ }
+ }
+
+ return Any();
+}
+
+
+// TmpRepositoryCommandEnv
+
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
+{
+}
+
+TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv()
+{
+}
+// XCommandEnvironment
+
+uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+void TmpRepositoryCommandEnv::handle(
+ uno::Reference< task::XInteractionRequest> const & xRequest )
+{
+ OSL_ASSERT( xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ bool approve = true;
+
+ // select:
+ uno::Sequence< Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ Reference< task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ uno::Reference< task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+}
+
+// XProgressHandler
+void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ )
+{
+}
+
+
+void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */)
+{
+}
+
+void TmpRepositoryCommandEnv::pop()
+{
+}
+
+
+// component operations
+
+
+Reference< XInterface > OO3ExtensionMigration_create(
+ Reference< XComponentContext > const & ctx )
+{
+ return static_cast< lang::XTypeProvider * >( new OO3ExtensionMigration(
+ ctx) );
+}
+
+
+} // namespace migration
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/oo3extensionmigration.hxx b/desktop/source/migration/services/oo3extensionmigration.hxx
new file mode 100644
index 000000000..089c343aa
--- /dev/null
+++ b/desktop/source/migration/services/oo3extensionmigration.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_OO3EXTENSIONMIGRATION_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_OO3EXTENSIONMIGRATION_HXX
+
+#include "misc.hxx"
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+
+#include <osl/mutex.hxx>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+class INetURLObject;
+
+
+namespace migration
+{
+
+ OUString OO3ExtensionMigration_getImplementationName();
+ css::uno::Sequence< OUString > OO3ExtensionMigration_getSupportedServiceNames();
+ css::uno::Reference< css::uno::XInterface > OO3ExtensionMigration_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::task::XJob > ExtensionMigration_BASE;
+
+ class OO3ExtensionMigration : public ExtensionMigration_BASE
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext > m_ctx;
+ css::uno::Reference< css::xml::dom::XDocumentBuilder > m_xDocBuilder;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSimpleFileAccess;
+ ::osl::Mutex m_aMutex;
+ OUString m_sSourceDir;
+ OUString m_sTargetDir;
+ TStringVector m_aBlackList;
+
+ enum ScanResult
+ {
+ SCANRESULT_NOTFOUND,
+ SCANRESULT_MIGRATE_EXTENSION,
+ SCANRESULT_DONTMIGRATE_EXTENSION
+ };
+
+ ScanResult scanExtensionFolder( const OUString& sExtFolder );
+ void scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions );
+ bool scanDescriptionXml( const OUString& sDescriptionXmlFilePath );
+ void migrateExtension( const OUString& sSourceDir );
+
+ public:
+ explicit OO3ExtensionMigration(css::uno::Reference<
+ css::uno::XComponentContext > const & ctx);
+ virtual ~OO3ExtensionMigration() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XJob
+ virtual css::uno::Any SAL_CALL execute(
+ const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override;
+ };
+
+ class TmpRepositoryCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+ {
+ public:
+ virtual ~TmpRepositoryCommandEnv() override;
+ TmpRepositoryCommandEnv();
+
+ // XCommandEnvironment
+ virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference< css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference< css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+ };
+
+
+} // namespace migration
+
+
+#endif // INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_OO3EXTENSIONMIGRATION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/wordbookmigration.cxx b/desktop/source/migration/services/wordbookmigration.cxx
new file mode 100644
index 000000000..354643e8e
--- /dev/null
+++ b/desktop/source/migration/services/wordbookmigration.cxx
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "wordbookmigration.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace migration
+{
+ // component operations
+
+
+ OUString WordbookMigration_getImplementationName()
+ {
+ return "com.sun.star.comp.desktop.migration.Wordbooks";
+ }
+
+
+ Sequence< OUString > WordbookMigration_getSupportedServiceNames()
+ {
+ return { "com.sun.star.migration.Wordbooks" };
+ }
+
+
+ // WordbookMigration
+
+
+ WordbookMigration::WordbookMigration()
+ {
+ }
+
+
+ WordbookMigration::~WordbookMigration()
+ {
+ }
+
+
+ TStringVectorPtr WordbookMigration::getFiles( const OUString& rBaseURL ) const
+ {
+ TStringVectorPtr aResult( new TStringVector );
+ ::osl::Directory aDir( rBaseURL);
+
+ if ( aDir.open() == ::osl::FileBase::E_None )
+ {
+ // iterate over directory content
+ TStringVector aSubDirs;
+ ::osl::DirectoryItem aItem;
+ while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None )
+ {
+ ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL );
+ if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None )
+ {
+ if ( aFileStatus.getFileType() == ::osl::FileStatus::Directory )
+ aSubDirs.push_back( aFileStatus.getFileURL() );
+ else
+ aResult->push_back( aFileStatus.getFileURL() );
+ }
+ }
+
+ // iterate recursive over subfolders
+ for (auto const& subDir : aSubDirs)
+ {
+ TStringVectorPtr aSubResult = getFiles(subDir);
+ aResult->insert( aResult->end(), aSubResult->begin(), aSubResult->end() );
+ }
+ }
+
+ return aResult;
+ }
+
+
+ void WordbookMigration::checkAndCreateDirectory( INetURLObject const & rDirURL )
+ {
+ ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ if ( aResult == ::osl::FileBase::E_NOENT )
+ {
+ INetURLObject aBaseURL( rDirURL );
+ aBaseURL.removeSegment();
+ checkAndCreateDirectory( aBaseURL );
+ ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ }
+ }
+
+#define MAX_HEADER_LENGTH 16
+static bool IsUserWordbook( const OUString& rFile )
+{
+ bool bRet = false;
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( rFile, StreamMode::STD_READ );
+ if ( pStream && !pStream->GetError() )
+ {
+ static const char* const pVerOOo7 = "OOoUserDict1";
+ sal_uInt64 const nSniffPos = pStream->Tell();
+ static std::size_t nVerOOo7Len = sal::static_int_cast< std::size_t >(strlen( pVerOOo7 ));
+ char pMagicHeader[MAX_HEADER_LENGTH];
+ pMagicHeader[ nVerOOo7Len ] = '\0';
+ if (pStream->ReadBytes(static_cast<void *>(pMagicHeader), nVerOOo7Len) == nVerOOo7Len)
+ {
+ if ( !strcmp(pMagicHeader, pVerOOo7) )
+ bRet = true;
+ else
+ {
+ sal_uInt16 nLen;
+ pStream->Seek (nSniffPos);
+ pStream->ReadUInt16( nLen );
+ if ( nLen < MAX_HEADER_LENGTH )
+ {
+ pStream->ReadBytes(pMagicHeader, nLen);
+ pMagicHeader[nLen] = '\0';
+ if ( !strcmp(pMagicHeader, "WBSWG2")
+ || !strcmp(pMagicHeader, "WBSWG5")
+ || !strcmp(pMagicHeader, "WBSWG6") )
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+
+ void WordbookMigration::copyFiles()
+ {
+ OUString sTargetDir;
+ ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( sTargetDir );
+ if ( aStatus == ::utl::Bootstrap::PATH_EXISTS )
+ {
+ sTargetDir += "/user/wordbook";
+ TStringVectorPtr aFileList = getFiles( m_sSourceDir );
+ for (auto const& elem : *aFileList)
+ {
+ if (IsUserWordbook(elem) )
+ {
+ OUString sSourceLocalName = elem.copy( m_sSourceDir.getLength() );
+ OUString sTargetName = sTargetDir + sSourceLocalName;
+ INetURLObject aURL( sTargetName );
+ aURL.removeSegment();
+ checkAndCreateDirectory( aURL );
+ ::osl::FileBase::RC aResult = ::osl::File::copy( elem, sTargetName );
+ if ( aResult != ::osl::FileBase::E_None )
+ {
+ SAL_WARN( "desktop", "WordbookMigration::copyFiles: cannot copy "
+ << elem << " to " << sTargetName);
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "WordbookMigration::copyFiles: no user installation!" );
+ }
+ }
+
+
+ // XServiceInfo
+
+
+ OUString WordbookMigration::getImplementationName()
+ {
+ return WordbookMigration_getImplementationName();
+ }
+
+
+ sal_Bool WordbookMigration::supportsService(OUString const & ServiceName)
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+
+ Sequence< OUString > WordbookMigration::getSupportedServiceNames()
+ {
+ return WordbookMigration_getSupportedServiceNames();
+ }
+
+
+ // XInitialization
+
+
+ void WordbookMigration::initialize( const Sequence< Any >& aArguments )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const Any* pIter = aArguments.getConstArray();
+ const Any* pEnd = pIter + aArguments.getLength();
+ for ( ; pIter != pEnd ; ++pIter )
+ {
+ beans::NamedValue aValue;
+ *pIter >>= aValue;
+ if ( aValue.Name == "UserData" )
+ {
+ if ( !(aValue.Value >>= m_sSourceDir) )
+ {
+ OSL_FAIL( "WordbookMigration::initialize: argument UserData has wrong type!" );
+ }
+ m_sSourceDir += "/user/wordbook";
+ break;
+ }
+ }
+ }
+
+
+ // XJob
+
+
+ Any WordbookMigration::execute( const Sequence< beans::NamedValue >& )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ copyFiles();
+
+ return Any();
+ }
+
+
+ // component operations
+
+
+ Reference< XInterface > WordbookMigration_create(
+ Reference< XComponentContext > const & )
+ {
+ return static_cast< lang::XTypeProvider * >( new WordbookMigration() );
+ }
+
+
+} // namespace migration
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/migration/services/wordbookmigration.hxx b/desktop/source/migration/services/wordbookmigration.hxx
new file mode 100644
index 000000000..6da5526d8
--- /dev/null
+++ b/desktop/source/migration/services/wordbookmigration.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_WORDBOOKMIGRATION_HXX
+#define INCLUDED_DESKTOP_SOURCE_MIGRATION_SERVICES_WORDBOOKMIGRATION_HXX
+
+#include "misc.hxx"
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+
+
+class INetURLObject;
+
+
+namespace migration
+{
+
+
+ OUString WordbookMigration_getImplementationName();
+ css::uno::Sequence< OUString > WordbookMigration_getSupportedServiceNames();
+ css::uno::Reference< css::uno::XInterface > WordbookMigration_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::task::XJob > WordbookMigration_BASE;
+
+ class WordbookMigration : public WordbookMigration_BASE
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ OUString m_sSourceDir;
+
+ TStringVectorPtr getFiles( const OUString& rBaseURL ) const;
+ void checkAndCreateDirectory( INetURLObject const & rDirURL );
+ void copyFiles();
+
+ public:
+ WordbookMigration();
+ virtual ~WordbookMigration() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XJob
+ virtual css::uno::Any SAL_CALL execute(
+ const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override;
+ };
+
+
+} // namespace migration
+
+
+#endif // _DESKTOP_AUTOCORRMIGRATION_HXX_
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/minidump/minidump.cxx b/desktop/source/minidump/minidump.cxx
new file mode 100644
index 000000000..cdf7bf049
--- /dev/null
+++ b/desktop/source/minidump/minidump.cxx
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <desktop/minidump.hxx>
+
+#include <map>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <curl/curl.h>
+
+static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
+
+static std::map<std::string, std::string> readStrings(std::istream& file)
+{
+ std::map<std::string, std::string> parameters;
+
+ // when file is not readable, the status eof would not be set
+ // better test of state is okay
+ while (file)
+ {
+ std::string line;
+ std::getline(file, line);
+ int sep = line.find('=');
+ if (sep >= 0)
+ {
+ std::string key = line.substr(0, sep);
+ std::string value = line.substr(sep + 1);
+ parameters[key] = value;
+ }
+ }
+
+ return parameters;
+}
+
+// Callback to get the response data from server.
+static size_t WriteCallback(void const *ptr, size_t size,
+ size_t nmemb, void *userp)
+{
+ if (!userp)
+ return 0;
+
+ std::string* response = static_cast<std::string *>(userp);
+ size_t real_size = size * nmemb;
+ response->append(static_cast<char const *>(ptr), real_size);
+ return real_size;
+}
+
+static void getProperty(const std::string& key, std::string& value,
+ std::map<std::string, std::string>& parameters)
+{
+ auto itr = parameters.find(key);
+ if (itr != parameters.end())
+ {
+ value = itr->second;
+ parameters.erase(itr);
+ }
+}
+
+static std::string generate_json(const std::map<std::string, std::string>& parameters)
+{
+ std::ostringstream stream;
+ stream << "{\n";
+ bool first = true;
+ for (auto itr = parameters.begin(), itrEnd = parameters.end(); itr != itrEnd; ++itr)
+ {
+ if (!first)
+ {
+ stream << ",\n";
+ }
+ first = false;
+ stream << "\"" << itr->first << "\": \"" << itr->second << "\"";
+ }
+ stream << "\n}";
+
+ return stream.str();
+}
+
+static bool uploadContent(std::map<std::string, std::string>& parameters, std::string& response)
+{
+ CURL* curl = curl_easy_init();
+ if (!curl)
+ return false;
+
+ std::string proxy, proxy_user_pwd, ca_certificate_file, file, url, version;
+
+ getProperty("Proxy", proxy, parameters);
+ getProperty("ProxyUserPW", proxy_user_pwd, parameters);
+ getProperty("CAFile", ca_certificate_file, parameters);
+
+ getProperty("DumpFile", file, parameters);
+ getProperty("URL", url, parameters);
+ getProperty("Version", version, parameters);
+ if (url.empty())
+ return false;
+
+ if (file.empty())
+ return false;
+
+ if (version.empty())
+ return false;
+
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
+ // Set proxy information if necessary.
+ if (!proxy.empty())
+ {
+ curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
+
+ curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE);
+
+ if (!proxy_user_pwd.empty())
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
+ else
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, ":");
+ }
+
+ if (!ca_certificate_file.empty())
+ curl_easy_setopt(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());
+
+ curl_httppost* formpost = nullptr;
+ curl_httppost* lastptr = nullptr;
+ std::string additional_data = generate_json(parameters);
+ curl_formadd(&formpost, &lastptr,
+ CURLFORM_COPYNAME, "AdditionalData",
+ CURLFORM_COPYCONTENTS, additional_data.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost, &lastptr,
+ CURLFORM_COPYNAME, "Version",
+ CURLFORM_COPYCONTENTS, version.c_str(),
+ CURLFORM_END);
+
+ std::string response_body;
+ long response_code;
+ curl_formadd(&formpost, &lastptr,
+ CURLFORM_COPYNAME, "upload_file_minidump",
+ CURLFORM_FILE, file.c_str(),
+ CURLFORM_END);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+
+
+ // Disable 100-continue header.
+ char buf[] = "Expect:";
+ curl_slist* headerlist = nullptr;
+ headerlist = curl_slist_append(headerlist, buf);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA,
+ static_cast<void *>(&response_body));
+
+ // Fail if 400+ is returned from the web server.
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+
+ CURLcode cc = curl_easy_perform(curl);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+#ifndef NDEBUG
+ if (cc != CURLE_OK)
+ fprintf(stderr, "Failed to send http request to %s, error: %s\n",
+ url.c_str(),
+ curl_easy_strerror(cc));
+#endif
+
+ if (formpost != nullptr)
+ {
+ curl_formfree(formpost);
+ }
+ if (headerlist != nullptr)
+ {
+ curl_slist_free_all(headerlist);
+ }
+
+ response = response_body;
+
+ if( CURLE_OK != cc )
+ return false;
+
+ return true;
+}
+
+namespace crashreport {
+
+bool readConfig(const std::string& iniPath, std::string * response)
+{
+ std::ifstream file(iniPath);
+ std::map<std::string, std::string> parameters = readStrings(file);
+
+ // make sure that at least the mandatory parameters are in there
+ if (parameters.find("DumpFile") == parameters.end())
+ {
+ if(response != nullptr)
+ *response = "ini file needs to contain a key DumpFile!";
+ return false;
+ }
+
+ if (parameters.find("Version") == parameters.end())
+ {
+ if (response != nullptr)
+ *response = "ini file needs to contain a key Version!";
+ return false;
+ }
+
+ if (parameters.find("URL") == parameters.end())
+ {
+ if (response != nullptr)
+ *response = "ini file needs to contain a key URL!";
+ return false;
+ }
+
+ if (response != nullptr)
+ return uploadContent(parameters, *response);
+
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/minidump/minidump_upload.cxx b/desktop/source/minidump/minidump_upload.cxx
new file mode 100644
index 000000000..15af26430
--- /dev/null
+++ b/desktop/source/minidump/minidump_upload.cxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <desktop/minidump.hxx>
+
+#include <iostream>
+#include <string>
+
+int main(int argc, char** argv)
+{
+ if (argc < 2)
+ {
+ std::cerr << "minidump_upload path_to_ini_file" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::string iniPath(argv[1]);
+ std::string response;
+ if (!crashreport::readConfig(iniPath, &response))
+ return EXIT_FAILURE;
+
+ std::cout << "Response: " << response << std::endl;
+ return EXIT_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/offacc/acceptor.cxx b/desktop/source/offacc/acceptor.cxx
new file mode 100644
index 000000000..a417d6a5d
--- /dev/null
+++ b/desktop/source/offacc/acceptor.cxx
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "acceptor.hxx"
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/connection/Acceptor.hpp>
+#include <com/sun/star/uno/XNamingService.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace css::bridge;
+using namespace css::connection;
+using namespace css::lang;
+using namespace css::uno;
+
+namespace desktop
+{
+
+extern "C" {
+
+static void offacc_workerfunc (void * acc)
+{
+ osl_setThreadName("URP Acceptor");
+
+ static_cast<Acceptor*>(acc)->run();
+}
+
+}
+
+Acceptor::Acceptor( const Reference< XComponentContext >& rxContext )
+ : m_thread(nullptr)
+ , m_rContext(rxContext)
+ , m_bInit(false)
+ , m_bDying(false)
+{
+ m_rAcceptor = css::connection::Acceptor::create(m_rContext);
+ m_rBridgeFactory = BridgeFactory::create(m_rContext);
+}
+
+
+Acceptor::~Acceptor()
+{
+ m_rAcceptor->stopAccepting();
+ oslThread t;
+ {
+ osl::MutexGuard g(m_aMutex);
+ t = m_thread;
+ }
+ //prevent locking if the thread is still waiting
+ m_bDying = true;
+ m_cEnable.set();
+ osl_joinWithThread(t);
+ osl_destroyThread(t);
+ {
+ // Make the final state of m_bridges visible to this thread (since
+ // m_thread is joined, the code that follows is the only one left
+ // accessing m_bridges):
+ osl::MutexGuard g(m_aMutex);
+ }
+ for (;;) {
+ css::uno::Reference< css::bridge::XBridge > b(m_bridges.remove());
+ if (!b.is()) {
+ break;
+ }
+ css::uno::Reference< css::lang::XComponent >(
+ b, css::uno::UNO_QUERY_THROW)->dispose();
+ }
+}
+
+void Acceptor::run()
+{
+ SAL_INFO( "desktop.offacc", "Acceptor::run" );
+ for (;;)
+ {
+ try
+ {
+ // wait until we get enabled
+ SAL_INFO( "desktop.offacc",
+ "Acceptor::run waiting for office to come up");
+ m_cEnable.wait();
+ if (m_bDying) //see destructor
+ break;
+ SAL_INFO( "desktop.offacc",
+ "Acceptor::run now enabled and continuing");
+
+ // accept connection
+ Reference< XConnection > rConnection = m_rAcceptor->accept( m_aConnectString );
+ // if we return without a valid connection we must assume that the acceptor
+ // is destructed so we break out of the run method terminating the thread
+ if (! rConnection.is()) break;
+ OUString aDescription = rConnection->getDescription();
+ SAL_INFO( "desktop.offacc", "Acceptor::run connection " << aDescription );
+
+ // create instanceprovider for this connection
+ Reference< XInstanceProvider > rInstanceProvider(new AccInstanceProvider(m_rContext));
+ // create the bridge. The remote end will have a reference to this bridge
+ // thus preventing the bridge from being disposed. When the remote end releases
+ // the bridge, it will be destructed.
+ Reference< XBridge > rBridge = m_rBridgeFactory->createBridge(
+ "", m_aProtocol, rConnection, rInstanceProvider);
+ osl::MutexGuard g(m_aMutex);
+ m_bridges.add(rBridge);
+ } catch (const Exception&) {
+ TOOLS_WARN_EXCEPTION("desktop.offacc", "");
+ // connection failed...
+ // something went wrong during connection setup.
+ // just wait for a new connection to accept
+ }
+ }
+}
+
+// XInitialize
+void Acceptor::initialize( const Sequence<Any>& aArguments )
+{
+ // prevent multiple initialization
+ osl::MutexGuard aGuard( m_aMutex );
+ SAL_INFO( "desktop.offacc", "Acceptor::initialize()" );
+
+ bool bOk = false;
+
+ // arg count
+ int nArgs = aArguments.getLength();
+
+ // not yet initialized and accept-string
+ if (!m_bInit && nArgs > 0 && (aArguments[0] >>= m_aAcceptString))
+ {
+ SAL_INFO( "desktop.offacc", "Acceptor::initialize string=" << m_aAcceptString );
+
+ // get connect string and protocol from accept string
+ // "<connectString>;<protocol>"
+ sal_Int32 nIndex1 = m_aAcceptString.indexOf( ';' );
+ if (nIndex1 < 0)
+ throw IllegalArgumentException(
+ "Invalid accept-string format", m_rContext, 1);
+ m_aConnectString = m_aAcceptString.copy( 0 , nIndex1 ).trim();
+ nIndex1++;
+ sal_Int32 nIndex2 = m_aAcceptString.indexOf( ';' , nIndex1 );
+ if (nIndex2 < 0) nIndex2 = m_aAcceptString.getLength();
+ m_aProtocol = m_aAcceptString.copy( nIndex1, nIndex2 - nIndex1 );
+
+ // start accepting in new thread...
+ m_thread = osl_createThread(offacc_workerfunc, this);
+ m_bInit = true;
+ bOk = true;
+ }
+
+ // do we want to enable accepting?
+ bool bEnable = false;
+ if (((nArgs == 1 && (aArguments[0] >>= bEnable)) ||
+ (nArgs == 2 && (aArguments[1] >>= bEnable))) &&
+ bEnable )
+ {
+ m_cEnable.set();
+ bOk = true;
+ }
+
+ if (!bOk)
+ {
+ throw IllegalArgumentException( "invalid initialization", m_rContext, 1);
+ }
+}
+
+// XServiceInfo
+OUString Acceptor::impl_getImplementationName()
+{
+ return "com.sun.star.office.comp.Acceptor";
+}
+OUString Acceptor::getImplementationName()
+{
+ return Acceptor::impl_getImplementationName();
+}
+Sequence<OUString> Acceptor::impl_getSupportedServiceNames()
+{
+ return { "com.sun.star.office.Acceptor" };
+}
+Sequence<OUString> Acceptor::getSupportedServiceNames()
+{
+ return Acceptor::impl_getSupportedServiceNames();
+}
+
+sal_Bool Acceptor::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// Factory
+Reference< XInterface > Acceptor::impl_getInstance( const Reference< XMultiServiceFactory >& aFactory )
+{
+ try {
+ return static_cast<cppu::OWeakObject *>(
+ new Acceptor(comphelper::getComponentContext(aFactory)));
+ } catch ( const Exception& ) {
+ return css::uno::Reference<css::uno::XInterface>();
+ }
+}
+
+// InstanceProvider
+AccInstanceProvider::AccInstanceProvider(const Reference<XComponentContext>& rxContext)
+ : m_rContext(rxContext)
+{
+}
+
+AccInstanceProvider::~AccInstanceProvider()
+{
+}
+
+Reference<XInterface> AccInstanceProvider::getInstance (const OUString& aName )
+{
+
+ Reference<XInterface> rInstance;
+
+ if ( aName == "StarOffice.ServiceManager" )
+ {
+ rInstance.set( m_rContext->getServiceManager() );
+ }
+ else if ( aName == "StarOffice.ComponentContext" )
+ {
+ rInstance = m_rContext;
+ }
+ else if ( aName == "StarOffice.NamingService" )
+ {
+ Reference< XNamingService > rNamingService(
+ m_rContext->getServiceManager()->createInstanceWithContext("com.sun.star.uno.NamingService", m_rContext),
+ UNO_QUERY );
+ if ( rNamingService.is() )
+ {
+ rNamingService->registerObject( "StarOffice.ServiceManager", m_rContext->getServiceManager() );
+ rNamingService->registerObject( "StarOffice.ComponentContext", m_rContext );
+ rInstance = rNamingService;
+ }
+ }
+ return rInstance;
+}
+
+}
+
+// component management stuff...
+
+extern "C"
+{
+using namespace desktop;
+
+SAL_DLLPUBLIC_EXPORT void * offacc_component_getFactory(char const *pImplementationName, void *pServiceManager, void *)
+{
+ void* pReturn = nullptr ;
+ if ( pImplementationName && pServiceManager )
+ {
+ // Define variables which are used in following macros.
+ Reference< XSingleServiceFactory > xFactory;
+ Reference< XMultiServiceFactory > xServiceManager(
+ static_cast< XMultiServiceFactory* >(pServiceManager));
+
+ if (desktop::Acceptor::impl_getImplementationName().equalsAscii( pImplementationName ) )
+ {
+ xFactory.set( cppu::createSingleFactory(
+ xServiceManager, desktop::Acceptor::impl_getImplementationName(),
+ desktop::Acceptor::impl_getInstance, desktop::Acceptor::impl_getSupportedServiceNames()) );
+ }
+
+ // Factory is valid - service was found.
+ if ( xFactory.is() )
+ {
+ xFactory->acquire();
+ pReturn = xFactory.get();
+ }
+ }
+
+ // Return with result of this operation.
+ return pReturn ;
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/offacc/acceptor.hxx b/desktop/source/offacc/acceptor.hxx
new file mode 100644
index 000000000..a7cc9c4e9
--- /dev/null
+++ b/desktop/source/offacc/acceptor.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_OFFACC_ACCEPTOR_HXX
+#define INCLUDED_DESKTOP_SOURCE_OFFACC_ACCEPTOR_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/connection/XAcceptor.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/bridge/XInstanceProvider.hpp>
+#include <com/sun/star/bridge/XBridgeFactory2.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <comphelper/weakbag.hxx>
+#include <osl/mutex.hxx>
+#include <osl/conditn.hxx>
+#include <osl/thread.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace desktop {
+
+class Acceptor
+ : public ::cppu::WeakImplHelper<css::lang::XServiceInfo, css::lang::XInitialization>
+{
+private:
+ osl::Mutex m_aMutex;
+
+ oslThread m_thread;
+ comphelper::WeakBag< css::bridge::XBridge > m_bridges;
+
+ ::osl::Condition m_cEnable;
+
+ css::uno::Reference< css::uno::XComponentContext > m_rContext;
+ css::uno::Reference< css::connection::XAcceptor > m_rAcceptor;
+ css::uno::Reference< css::bridge::XBridgeFactory2 > m_rBridgeFactory;
+
+ OUString m_aAcceptString;
+ OUString m_aConnectString;
+ OUString m_aProtocol;
+
+ bool m_bInit;
+ bool m_bDying;
+
+public:
+ explicit Acceptor( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~Acceptor() override;
+
+ void run();
+
+ // XService info
+ static OUString impl_getImplementationName();
+ virtual OUString SAL_CALL getImplementationName() override;
+ static css::uno::Sequence<OUString> impl_getSupportedServiceNames();
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& aName ) override;
+
+ // XInitialize
+ virtual void SAL_CALL initialize( const css::uno::Sequence<css::uno::Any>& aArguments ) override;
+
+ static css::uno::Reference<css::uno::XInterface> impl_getInstance( const css::uno::Reference< css::lang::XMultiServiceFactory >& aFactory );
+};
+
+class AccInstanceProvider : public ::cppu::WeakImplHelper<css::bridge::XInstanceProvider>
+{
+private:
+ css::uno::Reference<css::uno::XComponentContext> m_rContext;
+
+public:
+ AccInstanceProvider(const css::uno::Reference< css::uno::XComponentContext >& rxContext);
+ virtual ~AccInstanceProvider() override;
+
+ // XInstanceProvider
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getInstance (const OUString& aName ) override;
+};
+
+
+} //namespace desktop
+
+#endif // INCLUDED_DESKTOP_SOURCE_OFFACC_ACCEPTOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/offacc/offacc.component b/desktop/source/offacc/offacc.component
new file mode 100644
index 000000000..66ba2d8e0
--- /dev/null
+++ b/desktop/source/offacc/offacc.component
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="offacc" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.office.comp.Acceptor">
+ <service name="com.sun.star.office.Acceptor"/>
+ </implementation>
+</component>
diff --git a/desktop/source/pkgchk/unopkg/unopkg_app.cxx b/desktop/source/pkgchk/unopkg/unopkg_app.cxx
new file mode 100644
index 000000000..6c9f8ce00
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_app.cxx
@@ -0,0 +1,635 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_misc.h>
+#include "unopkg_main.h"
+#include "unopkg_shared.h"
+#include <dp_identifier.hxx>
+#include <tools/extendapplicationenvironment.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+#include <osl/conditn.hxx>
+#include <unotools/tempfile.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/logging.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+
+#include <com/sun/star/deployment/ui/PackageManagerDialog.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/logging/ConsoleHandler.hpp>
+#include <com/sun/star/logging/FileHandler.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/logging/SimpleTextFormatter.hpp>
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp>
+#if defined(UNX)
+ #include <unistd.h>
+#endif
+#include <vector>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::logging;
+using namespace ::com::sun::star::uno;
+using namespace ::unopkg;
+
+namespace {
+
+struct ExtensionName
+{
+ OUString m_str;
+ explicit ExtensionName( OUString const & str ) : m_str( str ) {}
+ bool operator () ( Reference<deployment::XPackage> const & e ) const
+ {
+ return m_str == dp_misc::getIdentifier(e)
+ || m_str == e->getName();
+ }
+};
+
+
+const char s_usingText [] =
+"\n"
+"using: " APP_NAME " add <options> extension-path...\n"
+" " APP_NAME " validate <options> extension-identifier...\n"
+" " APP_NAME " remove <options> extension-identifier...\n"
+" " APP_NAME " list <options> extension-identifier...\n"
+" " APP_NAME " reinstall <options>\n"
+" " APP_NAME " gui\n"
+" " APP_NAME " -V\n"
+" " APP_NAME " -h\n"
+"\n"
+"sub-commands:\n"
+" add add extension\n"
+" validate checks the prerequisites of an installed extension and\n"
+" registers it if possible\n"
+" remove remove extensions by identifier\n"
+" reinstall expert feature: reinstall all deployed extensions\n"
+" list list information about deployed extensions\n"
+" gui raise Extension Manager Graphical User Interface (GUI)\n"
+"\n"
+"options:\n"
+" -h, --help this help\n"
+" -V, --version version information\n"
+" -v, --verbose verbose output\n"
+" -f, --force force overwriting existing extensions\n"
+" -s, --suppress-license prevents showing the license\n"
+" --log-file <file> custom log file; default: <cache-dir>/log.txt\n"
+" --shared expert feature: operate on shared installation\n"
+" deployment context;\n"
+" run only when no concurrent Office\n"
+" process(es) are running!\n"
+" --bundled expert feature: operate on bundled extensions. Only\n"
+" works with list, validate, reinstall;\n"
+" --deployment-context expert feature: explicit deployment context\n"
+" <context>\n"
+"\n"
+"To learn more about the Extension Manager and extensions, see:\n"
+"http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extensions\n\n";
+
+
+const OptionInfo s_option_infos [] = {
+ { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false },
+ { RTL_CONSTASCII_STRINGPARAM("version"), 'V', false },
+ { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
+ { RTL_CONSTASCII_STRINGPARAM("force"), 'f', false },
+ { RTL_CONSTASCII_STRINGPARAM("log-file"), '\0', true },
+ { RTL_CONSTASCII_STRINGPARAM("shared"), '\0', false },
+ { RTL_CONSTASCII_STRINGPARAM("deployment-context"), '\0', true },
+ { RTL_CONSTASCII_STRINGPARAM("bundled"), '\0', false},
+ { RTL_CONSTASCII_STRINGPARAM("suppress-license"), 's', false},
+
+ { nullptr, 0, '\0', false }
+};
+
+class DialogClosedListenerImpl :
+ public ::cppu::WeakImplHelper< ui::dialogs::XDialogClosedListener >
+{
+ osl::Condition & m_rDialogClosedCondition;
+
+public:
+ explicit DialogClosedListenerImpl( osl::Condition & rDialogClosedCondition )
+ : m_rDialogClosedCondition( rDialogClosedCondition ) {}
+
+ // XEventListener (base of XDialogClosedListener)
+ virtual void SAL_CALL disposing( lang::EventObject const & Source ) override;
+
+ // XDialogClosedListener
+ virtual void SAL_CALL dialogClosed(
+ ui::dialogs::DialogClosedEvent const & aEvent ) override;
+};
+
+// XEventListener (base of XDialogClosedListener)
+void DialogClosedListenerImpl::disposing( lang::EventObject const & )
+{
+ // nothing to do
+}
+
+// XDialogClosedListener
+void DialogClosedListenerImpl::dialogClosed(
+ ui::dialogs::DialogClosedEvent const & )
+{
+ m_rDialogClosedCondition.set();
+}
+
+// If a package had been installed with a pre OOo 2.2, it could not normally be
+// found via its identifier; similarly (and for ease of use), a package
+// installed with OOo 2.2 or later could not normally be found via its file
+// name.
+Reference<deployment::XPackage> findPackage(
+ OUString const & repository,
+ Reference<deployment::XExtensionManager> const & manager,
+ Reference<ucb::XCommandEnvironment > const & environment,
+ OUString const & idOrFileName )
+{
+ const Sequence< Reference<deployment::XPackage> > ps(
+ manager->getDeployedExtensions(repository,
+ Reference<task::XAbortChannel>(), environment ) );
+ for ( auto const & package : ps )
+ if ( dp_misc::getIdentifier( package ) == idOrFileName )
+ return package;
+ for ( auto const & package : ps )
+ if ( package->getName() == idOrFileName )
+ return package;
+ return Reference<deployment::XPackage>();
+}
+
+} // anon namespace
+
+extern "C" int unopkg_main()
+{
+ tools::extendApplicationEnvironment();
+ bool bShowFailedMsg = true;
+ OUString subCommand;
+ bool option_shared = false;
+ bool option_force = false;
+ bool option_verbose = false;
+ bool option_bundled = false;
+ bool option_suppressLicense = false;
+ bool option_help = false;
+ bool subcmd_gui = false;
+ OUString logFile;
+ OUString repository;
+ OUString cmdArg;
+ std::vector<OUString> cmdPackages;
+ Reference<XLogHandler> xFileHandler;
+ Reference<XLogHandler> xConsoleHandler;
+ std::unique_ptr<comphelper::EventLogger> logger;
+ std::unique_ptr<utl::TempFile> pUserProfileTempDir;
+
+ OptionInfo const * info_shared = getOptionInfo(
+ s_option_infos, "shared" );
+ OptionInfo const * info_force = getOptionInfo(
+ s_option_infos, "force" );
+ OptionInfo const * info_verbose = getOptionInfo(
+ s_option_infos, "verbose" );
+ OptionInfo const * info_log = getOptionInfo(
+ s_option_infos, "log-file" );
+ OptionInfo const * info_context = getOptionInfo(
+ s_option_infos, "deployment-context" );
+ OptionInfo const * info_help = getOptionInfo(
+ s_option_infos, "help" );
+ OptionInfo const * info_version = getOptionInfo(
+ s_option_infos, "version" );
+ OptionInfo const * info_bundled = getOptionInfo(
+ s_option_infos, "bundled" );
+ OptionInfo const * info_suppressLicense = getOptionInfo(
+ s_option_infos, "suppress-license" );
+
+
+ Reference<XComponentContext> xComponentContext;
+ Reference<XComponentContext> xLocalComponentContext;
+
+ try {
+ sal_uInt32 nPos = 0;
+ sal_uInt32 nCount = osl_getCommandArgCount();
+ if (nCount == 0 || isOption( info_help, &nPos ))
+ {
+ dp_misc::writeConsole(s_usingText);
+ return 0;
+ }
+ else if (isOption( info_version, &nPos )) {
+ dp_misc::writeConsole("\n" APP_NAME " Version 3.3\n");
+ return 0;
+ }
+ //consume all bootstrap variables which may occur before the sub-command
+ while(isBootstrapVariable(&nPos))
+ ;
+
+ if(nPos >= nCount)
+ return 0;
+ //get the sub-command
+ osl_getCommandArg( nPos, &subCommand.pData );
+ ++nPos;
+ subCommand = subCommand.trim();
+ bool subcmd_add = subCommand == "add";
+ subcmd_gui = subCommand == "gui";
+
+ // sub-command options and packages:
+ while (nPos < nCount)
+ {
+ if (readArgument( &cmdArg, info_log, &nPos )) {
+ logFile = makeAbsoluteFileUrl(
+ cmdArg.trim(), getProcessWorkingDir() );
+ }
+ else if (!readOption( &option_verbose, info_verbose, &nPos ) &&
+ !readOption( &option_shared, info_shared, &nPos ) &&
+ !readOption( &option_force, info_force, &nPos ) &&
+ !readOption( &option_bundled, info_bundled, &nPos ) &&
+ !readOption( &option_suppressLicense, info_suppressLicense, &nPos ) &&
+ !readOption( &option_help, info_help, &nPos ) &&
+ !readArgument( &repository, info_context, &nPos ) &&
+ !isBootstrapVariable(&nPos))
+ {
+ osl_getCommandArg( nPos, &cmdArg.pData );
+ ++nPos;
+ cmdArg = cmdArg.trim();
+ if (!cmdArg.isEmpty())
+ {
+ if (cmdArg[ 0 ] == '-')
+ {
+ // is option:
+ dp_misc::writeConsoleError(
+ "\nERROR: unexpected option " +
+ cmdArg +
+ "!\n Use " APP_NAME " " +
+ toString(info_help) +
+ " to print all options.\n");
+ return 1;
+ }
+ else
+ {
+ // is package:
+ cmdPackages.push_back(
+ subcmd_add || subcmd_gui
+ ? makeAbsoluteFileUrl(
+ cmdArg, getProcessWorkingDir() )
+ : cmdArg );
+ }
+ }
+ }
+ }
+
+ // tdf#129917 Use temp user profile when installing shared extensions
+ if (option_shared)
+ {
+ pUserProfileTempDir.reset(new utl::TempFile(nullptr, true));
+ pUserProfileTempDir->EnableKillingFile();
+ }
+
+ xComponentContext = getUNO(option_verbose, subcmd_gui,
+ pUserProfileTempDir ? pUserProfileTempDir->GetURL() : "",
+ xLocalComponentContext);
+
+ // Initialize logging. This will log errors to the console and
+ // also to file if the --log-file parameter was provided.
+ logger.reset(new comphelper::EventLogger(xLocalComponentContext, "unopkg"));
+ const Reference<XLogger> xLogger(logger->getLogger());
+ xLogger->setLevel(LogLevel::WARNING);
+ Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xLocalComponentContext));
+ Sequence < beans::NamedValue > aSeq { { "Formatter", Any(xLogFormatter) } };
+
+ xConsoleHandler.set(ConsoleHandler::createWithSettings(xLocalComponentContext, aSeq));
+ xLogger->addLogHandler(xConsoleHandler);
+ xConsoleHandler->setLevel(LogLevel::WARNING);
+ xLogger->setLevel(LogLevel::WARNING);
+
+
+ if (!logFile.isEmpty())
+ {
+ Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
+ xFileHandler.set(css::logging::FileHandler::createWithSettings(xLocalComponentContext, aSeq2));
+ xFileHandler->setLevel(LogLevel::WARNING);
+ xLogger->addLogHandler(xFileHandler);
+ }
+
+ if (option_verbose)
+ {
+ xLogger->setLevel(LogLevel::INFO);
+ xConsoleHandler->setLevel(LogLevel::INFO);
+ if (xFileHandler.is())
+ xFileHandler->setLevel(LogLevel::INFO);
+ }
+
+ if (repository.isEmpty())
+ {
+ if (option_shared)
+ repository = "shared";
+ else if (option_bundled)
+ repository = "bundled";
+ else
+ repository = "user";
+ }
+ else
+ {
+ if ( repository == "shared" ) {
+ option_shared = true;
+ }
+ else if (option_shared)
+ {
+ logger->log(LogLevel::WARNING, "Explicit context given! Ignoring option '$1$'", toString(info_shared));
+ }
+ }
+#if defined(UNX)
+ if ( geteuid() == 0 )
+ {
+ if ( !(option_shared || option_bundled || option_help) )
+ {
+ logger->log(LogLevel::SEVERE, "Cannot run $1$ as root without $2$ or $3$ option.",
+ APP_NAME, toString(info_shared), toString(info_bundled));
+ return 1;
+ }
+ }
+#endif
+
+ if (subCommand == "reinstall")
+ {
+ //We must prevent that services and types are loaded by UNO,
+ //otherwise we cannot delete the registry data folder.
+ OUString extensionUnorc;
+ if (repository == "user")
+ extensionUnorc = "$UNO_USER_PACKAGES_CACHE/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else if (repository == "shared")
+ extensionUnorc = "$SHARED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else if (repository == "bundled")
+ extensionUnorc = "$BUNDLED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else
+ OSL_ASSERT(false);
+
+ ::rtl::Bootstrap::expandMacros(extensionUnorc);
+ oslFileError e = osl_removeFile(extensionUnorc.pData);
+ if (e != osl_File_E_None && e != osl_File_E_NOENT)
+ throw Exception("Could not delete " + extensionUnorc, nullptr);
+ }
+
+ Reference<deployment::XExtensionManager> xExtensionManager(
+ deployment::ExtensionManager::get( xComponentContext ) );
+
+ Reference<css::ucb::XCommandEnvironment> xCmdEnv(
+ createCmdEnv(xComponentContext, option_force, option_verbose, option_suppressLicense));
+
+ //synchronize bundled/shared extensions
+ //Do not synchronize when command is "reinstall". This could add types and services to UNO and
+ //prevent the deletion of the registry data folder
+ //syncing is done in XExtensionManager.reinstall
+ if (!subcmd_gui && subCommand != "reinstall"
+ && ! dp_misc::office_is_running())
+ dp_misc::syncRepositories(false, xCmdEnv);
+
+ if ( subcmd_add || subCommand == "remove" )
+ {
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ if (subcmd_add)
+ {
+ beans::NamedValue nvSuppress(
+ "SUPPRESS_LICENSE", option_suppressLicense ?
+ makeAny(OUString("1")):makeAny(OUString("0")));
+ xExtensionManager->addExtension(
+ cmdPackage, Sequence<beans::NamedValue>(&nvSuppress, 1),
+ repository, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ else
+ {
+ try
+ {
+ xExtensionManager->removeExtension(
+ cmdPackage, cmdPackage, repository,
+ Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ Reference<deployment::XPackage> p(
+ findPackage(repository,
+ xExtensionManager, xCmdEnv, cmdPackage ) );
+ if ( !p.is())
+ throw;
+ else if (p.is())
+ xExtensionManager->removeExtension(
+ ::dp_misc::getIdentifier(p), p->getName(),
+ repository,
+ Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ }
+ }
+ }
+ else if ( subCommand == "reinstall" )
+ {
+ xExtensionManager->reinstallDeployedExtensions(
+ false, repository, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ else if ( subCommand == "list" )
+ {
+ std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
+ ::comphelper::sequenceToContainer(vecExtUnaccepted,
+ xExtensionManager->getExtensionsWithUnacceptedLicenses(
+ repository, xCmdEnv));
+
+ //This vector tells what XPackage in allExtensions has an
+ //unaccepted license.
+ std::vector<bool> vecUnaccepted;
+ std::vector<Reference<deployment::XPackage> > allExtensions;
+ if (cmdPackages.empty())
+ {
+ Sequence< Reference<deployment::XPackage> >
+ packages = xExtensionManager->getDeployedExtensions(
+ repository, Reference<task::XAbortChannel>(), xCmdEnv );
+
+ std::vector<Reference<deployment::XPackage> > vec_packages;
+ ::comphelper::sequenceToContainer(vec_packages, packages);
+
+ //First copy the extensions with the unaccepted license
+ //to vector allExtensions.
+ allExtensions.resize(vecExtUnaccepted.size() + vec_packages.size());
+
+ std::vector<Reference<deployment::XPackage> >::iterator i_all_ext =
+ std::copy(vecExtUnaccepted.begin(), vecExtUnaccepted.end(),
+ allExtensions.begin());
+ //Now copy those we got from getDeployedExtensions
+ std::copy(vec_packages.begin(), vec_packages.end(), i_all_ext);
+
+ //Now prepare the vector which tells what extension has an
+ //unaccepted license
+ vecUnaccepted.resize(vecExtUnaccepted.size() + vec_packages.size());
+ std::fill_n(vecUnaccepted.begin(), vecExtUnaccepted.size(), true);
+ std::fill_n(vecUnaccepted.begin() + vecExtUnaccepted.size(),
+ vec_packages.size(), false);
+
+ dp_misc::writeConsole(
+ "All deployed " + repository + " extensions:\n\n");
+ }
+ else
+ {
+ //The user provided the names (ids or file names) of the extensions
+ //which shall be listed
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = xExtensionManager->getDeployedExtension(
+ repository, cmdPackage, cmdPackage, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ extension = findPackage(repository,
+ xExtensionManager, xCmdEnv, cmdPackage );
+ }
+
+ //Now look if the requested extension has an unaccepted license
+ bool bUnacceptedLic = false;
+ if (!extension.is())
+ {
+ std::vector<Reference<deployment::XPackage> >::const_iterator
+ i = std::find_if(
+ vecExtUnaccepted.begin(),
+ vecExtUnaccepted.end(), ExtensionName(cmdPackage));
+ if (i != vecExtUnaccepted.end())
+ {
+ extension = *i;
+ bUnacceptedLic = true;
+ }
+ }
+
+ if (!extension.is())
+ throw lang::IllegalArgumentException(
+ "There is no such extension deployed: " +
+ cmdPackage,nullptr,-1);
+ allExtensions.push_back(extension);
+ vecUnaccepted.push_back(bUnacceptedLic);
+ }
+
+ }
+
+ printf_packages(allExtensions, vecUnaccepted, xCmdEnv );
+ }
+ else if ( subCommand == "validate" )
+ {
+ std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
+ ::comphelper::sequenceToContainer(
+ vecExtUnaccepted, xExtensionManager->getExtensionsWithUnacceptedLicenses(
+ repository, xCmdEnv));
+
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = xExtensionManager->getDeployedExtension(
+ repository, cmdPackage, cmdPackage, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ extension = findPackage(
+ repository, xExtensionManager, xCmdEnv, cmdPackage );
+ }
+
+ if (!extension.is())
+ {
+ std::vector<Reference<deployment::XPackage> >::const_iterator
+ i = std::find_if(
+ vecExtUnaccepted.begin(),
+ vecExtUnaccepted.end(), ExtensionName(cmdPackage));
+ if (i != vecExtUnaccepted.end())
+ {
+ extension = *i;
+ }
+ }
+
+ if (extension.is())
+ xExtensionManager->checkPrerequisitesAndEnable(
+ extension, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ }
+ else if ( subCommand == "gui" )
+ {
+ Reference<ui::dialogs::XAsynchronousExecutableDialog> xDialog(
+ deployment::ui::PackageManagerDialog::createAndInstall(
+ xComponentContext,
+ !cmdPackages.empty() ? cmdPackages[0] : OUString() ));
+
+ osl::Condition dialogEnded;
+ dialogEnded.reset();
+
+ Reference< ui::dialogs::XDialogClosedListener > xListener(
+ new DialogClosedListenerImpl( dialogEnded ) );
+
+ xDialog->startExecuteModal(xListener);
+ dialogEnded.wait();
+ return 0;
+ }
+ else
+ {
+ logger->log(LogLevel::SEVERE,
+ "Unknown sub-command: '$1$'. Use $2$ $3$ to print all options.",
+ subCommand, APP_NAME, toString(info_help));
+ return 1;
+ }
+
+ logger->log(LogLevel::INFO, "$1$ done.", APP_NAME);
+ //Force to release all bridges which connect us to the child processes
+ dp_misc::disposeBridges(xLocalComponentContext);
+ css::uno::Reference<css::lang::XComponent>(
+ xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
+ return 0;
+ }
+ catch (const ucb::CommandFailedException &e)
+ {
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ logger->log(LogLevel::SEVERE, "$1$ aborted.", APP_NAME);
+ bShowFailedMsg = false;
+ }
+ catch (const deployment::DeploymentException & exc)
+ {
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", exc.Message);
+ logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc.Cause));
+ }
+ catch (const LockFileException & e)
+ {
+ // No logger since it requires UNO which we don't have here
+ dp_misc::writeConsoleError(e.Message + "\n");
+ bShowFailedMsg = false;
+ }
+ catch (const css::uno::Exception & e ) {
+ Any exc( ::cppu::getCaughtException() );
+
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
+ logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc));
+ }
+ if (bShowFailedMsg)
+ logger->log(LogLevel::SEVERE, "$1$ failed.", APP_NAME);
+ dp_misc::disposeBridges(xLocalComponentContext);
+ if (xLocalComponentContext.is()) {
+ css::uno::Reference<css::lang::XComponent>(
+ xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
+ }
+ return 1;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx b/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
new file mode 100644
index 000000000..060d2918f
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include "unopkg_shared.h"
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/i18n/Collator.hpp>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+
+#include <dp_version.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::unopkg;
+
+
+namespace {
+
+
+class CommandEnvironmentImpl
+ : public ::cppu::WeakImplHelper< XCommandEnvironment,
+ task::XInteractionHandler,
+ XProgressHandler >
+{
+ sal_Int32 m_logLevel;
+ bool m_option_force_overwrite;
+ bool m_option_verbose;
+ bool m_option_suppress_license;
+ Reference< XComponentContext > m_xComponentContext;
+ Reference< XProgressHandler > m_xLogFile;
+
+ /// @throws RuntimeException
+ void update_( Any const & Status );
+ void printLicense(const OUString & sName,const OUString& sLicense,
+ bool & accept, bool & decline);
+
+public:
+ virtual ~CommandEnvironmentImpl() override;
+ CommandEnvironmentImpl(
+ Reference<XComponentContext> const & xComponentContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppress_license);
+
+ // XCommandEnvironment
+ virtual Reference< task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual Reference< XProgressHandler > SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ Reference< task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( Any const & Status ) override;
+ virtual void SAL_CALL update( Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+CommandEnvironmentImpl::CommandEnvironmentImpl(
+ Reference<XComponentContext> const & xComponentContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppressLicense)
+ : m_logLevel(0),
+ m_option_force_overwrite( option_force_overwrite ),
+ m_option_verbose( option_verbose ),
+ m_option_suppress_license( option_suppressLicense ),
+ m_xComponentContext(xComponentContext)
+{
+ m_xLogFile.set(
+ xComponentContext->getServiceManager()
+ ->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.deployment.ProgressLog",
+ Sequence<Any>(), xComponentContext ),
+ UNO_QUERY_THROW );
+}
+
+
+CommandEnvironmentImpl::~CommandEnvironmentImpl()
+{
+ try {
+ Reference< lang::XComponent > xComp( m_xLogFile, UNO_QUERY );
+ if (xComp.is())
+ xComp->dispose();
+ }
+ catch (const RuntimeException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+}
+
+//May throw exceptions
+void CommandEnvironmentImpl::printLicense(
+ const OUString & sName, const OUString& sLicense, bool & accept, bool &decline)
+{
+ OUString s1tmp(DpResId(RID_STR_UNOPKG_ACCEPT_LIC_1));
+ OUString s1(s1tmp.replaceAll("$NAME", sName));
+ OUString s2 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_2);
+ OUString s3 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_3);
+ OUString s4 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_4);
+ OUString sYES = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_YES);
+ OUString sY = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_Y);
+ OUString sNO = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_NO);
+ OUString sN = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_N);
+
+ OUString sNewLine("\n");
+
+ dp_misc::writeConsole(sNewLine + sNewLine + s1 + sNewLine + sNewLine);
+ dp_misc::writeConsole(sLicense + sNewLine + sNewLine);
+ dp_misc::writeConsole(s2 + sNewLine);
+ dp_misc::writeConsole(s3);
+
+ //the user may enter "yes" or "no", we compare in a case insensitive way
+ Reference< css::i18n::XCollator > xCollator =
+ css::i18n::Collator::create( m_xComponentContext );
+ xCollator->loadDefaultCollator(
+ LanguageTag(utl::ConfigManager::getUILocale()).getLocale(),
+ css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE);
+
+ do
+ {
+ OUString sAnswer = dp_misc::readConsole();
+ if (xCollator->compareString(sAnswer, sYES) == 0
+ || xCollator->compareString(sAnswer, sY) == 0)
+ {
+ accept = true;
+ break;
+ }
+ else if(xCollator->compareString(sAnswer, sNO) == 0
+ || xCollator->compareString(sAnswer, sN) == 0)
+ {
+ decline = true;
+ break;
+ }
+ else
+ {
+ dp_misc::writeConsole(sNewLine + sNewLine + s4 + sNewLine);
+ }
+ }
+ while(true);
+}
+
+// XCommandEnvironment
+
+Reference< task::XInteractionHandler >
+CommandEnvironmentImpl::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference< XProgressHandler > CommandEnvironmentImpl::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+
+void CommandEnvironmentImpl::handle(
+ Reference<task::XInteractionRequest> const & xRequest )
+{
+ Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == TypeClass_EXCEPTION );
+ dp_misc::TRACE("[unopkg_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n\n");
+
+ // selections:
+ bool approve = false;
+ bool abort = false;
+
+ lang::WrappedTargetException wtExc;
+ deployment::LicenseException licExc;
+ deployment::InstallException instExc;
+ deployment::PlatformException platExc;
+
+ if (request >>= wtExc) {
+ // ignore intermediate errors of legacy packages, i.e.
+ // former pkgchk behaviour:
+ const Reference<deployment::XPackage> xPackage(
+ wtExc.Context, UNO_QUERY );
+ OSL_ASSERT( xPackage.is() );
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is()) {
+ approve = (xPackage->isBundle() &&
+ xPackageType->getMediaType().match(
+ "application/vnd.sun.star.legacy-package-bundle") );
+ }
+ }
+ abort = !approve;
+ if (abort) {
+ // notify cause as error:
+ request = wtExc.TargetException;
+ }
+ else {
+ // handable deployment error signalled, e.g.
+ // bundle item registration failed, notify as warning:
+ update_( wtExc.TargetException );
+ }
+ }
+ else if (request >>= licExc)
+ {
+ if ( !m_option_suppress_license )
+ printLicense(licExc.ExtensionName, licExc.Text, approve, abort);
+ else
+ {
+ approve = true;
+ abort = false;
+ }
+ }
+ else if (request >>= instExc)
+ {
+ //Only if the unopgk was started with gui + extension then the user is asked.
+ //In console mode there is no asking.
+ approve = true;
+ }
+ else if (request >>= platExc)
+ {
+ OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
+ sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
+ dp_misc::writeConsole("\n" + sMsg + "\n\n");
+ approve = true;
+ }
+ else {
+ deployment::VersionException nc_exc;
+ if (request >>= nc_exc) {
+ approve = m_option_force_overwrite ||
+ (::dp_misc::compareVersions(nc_exc.NewVersion, nc_exc.Deployed->getVersion())
+ == ::dp_misc::GREATER);
+ abort = !approve;
+ }
+ else
+ return; // unknown request => no selection at all
+ }
+
+ if (abort && m_option_verbose)
+ {
+ OUString msg = ::comphelper::anyToString(request);
+ dp_misc::writeConsoleError("\nERROR: " + msg + "\n");
+ }
+
+ // select:
+ const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> xIC = xRequest->getContinuations();
+ for ( auto const& rCont : xIC )
+ {
+ if (approve) {
+ Reference<task::XInteractionApprove> xInteractionApprove(
+ rCont, UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ break;
+ }
+ }
+ else if (abort) {
+ Reference<task::XInteractionAbort> xInteractionAbort(
+ rCont, UNO_QUERY );
+ if (xInteractionAbort.is()) {
+ xInteractionAbort->select();
+ break;
+ }
+ }
+ }
+}
+
+// XProgressHandler
+
+void CommandEnvironmentImpl::push( Any const & Status )
+{
+ update_( Status );
+ OSL_ASSERT( m_logLevel >= 0 );
+ ++m_logLevel;
+ if (m_xLogFile.is())
+ m_xLogFile->push( Status );
+}
+
+
+void CommandEnvironmentImpl::update_( Any const & Status )
+{
+ if (! Status.hasValue())
+ return;
+ bool bUseErr = false;
+ OUString msg;
+ if (Status >>= msg) {
+ if (! m_option_verbose)
+ return;
+ }
+ else {
+ OUStringBuffer buf;
+ buf.append( "WARNING: " );
+ deployment::DeploymentException dp_exc;
+ if (Status >>= dp_exc) {
+ buf.append( dp_exc.Message );
+ buf.append( ", Cause: " );
+ buf.append( ::comphelper::anyToString(dp_exc.Cause) );
+ }
+ else {
+ buf.append( ::comphelper::anyToString(Status) );
+ }
+ msg = buf.makeStringAndClear();
+ bUseErr = true;
+ }
+ OSL_ASSERT( m_logLevel >= 0 );
+ for ( sal_Int32 n = 0; n < m_logLevel; ++n )
+ {
+ if (bUseErr)
+ dp_misc::writeConsoleError(" ");
+ else
+ dp_misc::writeConsole(" ");
+ }
+
+ if (bUseErr)
+ dp_misc::writeConsoleError(msg + "\n");
+ else
+ dp_misc::writeConsole(msg + "\n");
+}
+
+
+void CommandEnvironmentImpl::update( Any const & Status )
+{
+ update_( Status );
+ if (m_xLogFile.is())
+ m_xLogFile->update( Status );
+}
+
+
+void CommandEnvironmentImpl::pop()
+{
+ OSL_ASSERT( m_logLevel > 0 );
+ --m_logLevel;
+ if (m_xLogFile.is())
+ m_xLogFile->pop();
+}
+
+
+} // anon namespace
+
+namespace unopkg {
+
+
+Reference< XCommandEnvironment > createCmdEnv(
+ Reference< XComponentContext > const & xContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppress_license)
+{
+ return new CommandEnvironmentImpl(
+ xContext, option_force_overwrite, option_verbose, option_suppress_license);
+}
+} // unopkg
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_main.c b/desktop/source/pkgchk/unopkg/unopkg_main.c
new file mode 100644
index 000000000..9905cb6e7
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_main.c
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/main.h>
+
+#include "unopkg_main.h"
+
+SAL_IMPLEMENT_MAIN() {
+ return unopkg_main();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_main.h b/desktop/source/pkgchk/unopkg/unopkg_main.h
new file mode 100644
index 000000000..bfd9ee69b
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_main.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_PKGCHK_UNOPKG_UNOPKG_MAIN_H
+#define INCLUDED_DESKTOP_SOURCE_PKGCHK_UNOPKG_UNOPKG_MAIN_H
+
+#include <desktop/dllapi.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+DESKTOP_DLLPUBLIC int unopkg_main(void);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_misc.cxx b/desktop/source/pkgchk/unopkg/unopkg_misc.cxx
new file mode 100644
index 000000000..ecbb122f8
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_misc.cxx
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/bootstrap.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+
+#include <strings.hrc>
+#include "unopkg_shared.h"
+#include <dp_identifier.hxx>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <lockfile.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace unopkg {
+
+OUString toString( OptionInfo const * info )
+{
+ assert(info != nullptr);
+ OUStringBuffer buf;
+ buf.append("--");
+ buf.appendAscii(info->m_name);
+ if (info->m_short_option != '\0')
+ {
+ buf.append(" (short -" );
+ buf.append(info->m_short_option );
+ buf.append(")");
+ }
+ if (info->m_has_argument)
+ buf.append(" <argument>" );
+ return buf.makeStringAndClear();
+}
+
+
+OptionInfo const * getOptionInfo(
+ OptionInfo const * list,
+ OUString const & opt )
+{
+ for ( ; list->m_name != nullptr; ++list )
+ {
+ OptionInfo const & option_info = *list;
+ if (!opt.isEmpty())
+ {
+ if (opt.equalsAsciiL(
+ option_info.m_name, option_info.m_name_length ))
+ {
+ return &option_info;
+ }
+ }
+ }
+ SAL_WARN( "desktop", opt );
+ return nullptr;
+}
+
+
+bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ assert(option_info != nullptr);
+ if (osl_getCommandArgCount() <= *pIndex)
+ return false;
+
+ OUString arg;
+ osl_getCommandArg( *pIndex, &arg.pData );
+ sal_Int32 len = arg.getLength();
+
+ if (len < 2 || arg[ 0 ] != '-')
+ return false;
+
+ if (len == 2 && arg[ 1 ] == option_info->m_short_option)
+ {
+ ++(*pIndex);
+ dp_misc::TRACE(__FILE__ ": identified option \'\'"
+ + OUStringChar( option_info->m_short_option ) + "\n");
+ return true;
+ }
+ if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
+ arg.pData->buffer + 2, option_info->m_name ) == 0)
+ {
+ ++(*pIndex);
+ dp_misc::TRACE(__FILE__ ": identified option \'"
+ + OUString::createFromAscii(option_info->m_name) + "\'\n");
+ return true;
+ }
+ return false;
+}
+
+
+bool isBootstrapVariable(sal_uInt32 * pIndex)
+{
+ OSL_ASSERT(osl_getCommandArgCount() >= *pIndex);
+
+ OUString arg;
+ osl_getCommandArg(*pIndex, &arg.pData);
+ if (arg.match("-env:"))
+ {
+ ++(*pIndex);
+ return true;
+ }
+ return false;
+}
+
+
+bool readArgument(
+ OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ if (isOption( option_info, pIndex ))
+ {
+ if (*pIndex < osl_getCommandArgCount())
+ {
+ OSL_ASSERT( pValue != nullptr );
+ osl_getCommandArg( *pIndex, &pValue->pData );
+ dp_misc::TRACE(__FILE__ ": argument value: "
+ + *pValue + "\n");
+ ++(*pIndex);
+ return true;
+ }
+ --(*pIndex);
+ }
+ return false;
+}
+
+
+namespace {
+struct ExecutableDir : public rtl::StaticWithInit<
+ OUString, ExecutableDir> {
+ OUString operator () () {
+ OUString path;
+ if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
+ throw RuntimeException("cannot locate executable directory!",nullptr);
+ }
+ return path.copy( 0, path.lastIndexOf( '/' ) );
+ }
+};
+struct ProcessWorkingDir : public rtl::StaticWithInit<
+ OUString, ProcessWorkingDir> {
+ OUString operator () () {
+ OUString workingDir;
+ utl::Bootstrap::getProcessWorkingDir(workingDir);
+ return workingDir;
+ }
+};
+} // anon namespace
+
+
+OUString const & getExecutableDir()
+{
+ return ExecutableDir::get();
+}
+
+
+OUString const & getProcessWorkingDir()
+{
+ return ProcessWorkingDir::get();
+}
+
+
+OUString makeAbsoluteFileUrl(
+ OUString const & sys_path, OUString const & base_url )
+{
+ // system path to file url
+ OUString file_url;
+ oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
+ if ( rc != osl_File_E_None) {
+ OUString tempPath;
+ if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) != osl_File_E_None )
+ {
+ throw RuntimeException("cannot get file url from system path: " +
+ sys_path );
+ }
+ file_url = sys_path;
+ }
+
+ OUString abs;
+ if (osl_getAbsoluteFileURL(
+ base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
+ {
+ throw RuntimeException(
+ "making absolute file url failed: \"" + base_url
+ + "\" (base-url) and \"" + file_url + "\" (file-url)!" );
+ }
+ return abs[ abs.getLength() -1 ] == '/'
+ ? abs.copy( 0, abs.getLength() -1 ) : abs;
+}
+
+
+namespace {
+
+
+void printf_space( sal_Int32 space )
+{
+ while (space--)
+ dp_misc::writeConsole(" ");
+}
+
+
+void printf_line(
+ OUString const & name, OUString const & value, sal_Int32 level )
+{
+ printf_space( level );
+ dp_misc::writeConsole(name + ": " + value + "\n");
+}
+
+
+void printf_package(
+ Reference<deployment::XPackage> const & xPackage,
+ Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
+{
+ beans::Optional< OUString > id(
+ level == 0
+ ? beans::Optional< OUString >(
+ true, dp_misc::getIdentifier( xPackage ) )
+ : xPackage->getIdentifier() );
+ if (id.IsPresent)
+ printf_line( "Identifier", id.Value, level );
+ OUString version(xPackage->getVersion());
+ if (!version.isEmpty())
+ printf_line( "Version", version, level + 1 );
+ printf_line( "URL", xPackage->getURL(), level + 1 );
+
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
+ OUString value;
+ if (option.IsPresent) {
+ beans::Ambiguous<sal_Bool> const & reg = option.Value;
+ if (reg.IsAmbiguous)
+ value = "unknown";
+ else
+ value = reg.Value ? OUStringLiteral("yes") : OUStringLiteral("no");
+ }
+ else
+ value = "n/a";
+ printf_line( "is registered", value, level + 1 );
+
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is()) {
+ printf_line( "Media-Type", xPackageType->getMediaType(), level + 1 );
+ }
+ printf_line( "Description", xPackage->getDescription(), level + 1 );
+ if (!xPackage->isBundle())
+ return;
+
+ Sequence< Reference<deployment::XPackage> > seq(
+ xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
+ printf_space( level + 1 );
+ dp_misc::writeConsole("bundled Packages: {\n");
+ std::vector<Reference<deployment::XPackage> >vec_bundle;
+ ::comphelper::sequenceToContainer(vec_bundle, seq);
+ printf_packages( vec_bundle, std::vector<bool>(vec_bundle.size()),
+ xCmdEnv, level + 2 );
+ printf_space( level + 1 );
+ dp_misc::writeConsole("}\n");
+}
+
+} // anon namespace
+
+static void printf_unaccepted_licenses(
+ Reference<deployment::XPackage> const & ext)
+{
+ OUString id(
+ dp_misc::getIdentifier(ext) );
+ printf_line( "Identifier", id, 0 );
+ printf_space(1);
+ dp_misc::writeConsole("License not accepted\n\n");
+}
+
+
+void printf_packages(
+ std::vector< Reference<deployment::XPackage> > const & allExtensions,
+ std::vector<bool> const & vecUnaccepted,
+ Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
+{
+ OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
+
+ if (allExtensions.empty())
+ {
+ printf_space( level );
+ dp_misc::writeConsole("<none>\n");
+ }
+ else
+ {
+ int index = 0;
+ for (auto const& extension : allExtensions)
+ {
+ if (vecUnaccepted[index])
+ printf_unaccepted_licenses(extension);
+ else
+ printf_package( extension, xCmdEnv, level );
+ dp_misc::writeConsole("\n");
+ ++index;
+ }
+ }
+}
+
+
+namespace {
+
+
+Reference<XComponentContext> bootstrapStandAlone()
+{
+ Reference<XComponentContext> xContext =
+ ::cppu::defaultBootstrap_InitialComponentContext();
+
+ Reference<lang::XMultiServiceFactory> xServiceManager(
+ xContext->getServiceManager(), UNO_QUERY_THROW );
+ // set global process service factory used by unotools config helpers
+ ::comphelper::setProcessServiceFactory( xServiceManager );
+
+ // Initialize the UCB (for backwards compatibility, in case some code still
+ // uses plain createInstance w/o args directly to obtain an instance):
+ UniversalContentBroker::create( xContext );
+
+ return xContext;
+}
+
+
+Reference<XComponentContext> connectToOffice(
+ Reference<XComponentContext> const & xLocalComponentContext,
+ bool verbose )
+{
+ OUString pipeId( ::dp_misc::generateRandomPipeId() );
+ OUString acceptArg = "--accept=pipe,name=" + pipeId + ";urp;";
+
+ Sequence<OUString> args { "--nologo", "--nodefault", acceptArg };
+ OUString appURL( getExecutableDir() + "/soffice" );
+
+ if (verbose)
+ {
+ dp_misc::writeConsole(
+ "Raising process: " + appURL +
+ "\nArguments: --nologo --nodefault " + args[2] +
+ "\n");
+ }
+
+ ::dp_misc::raiseProcess( appURL, args );
+
+ if (verbose)
+ dp_misc::writeConsole("OK. Connecting...");
+
+ OUString sUnoUrl = "uno:pipe,name=" + pipeId + ";urp;StarOffice.ComponentContext";
+ Reference<XComponentContext> xRet(
+ ::dp_misc::resolveUnoURL(
+ sUnoUrl, xLocalComponentContext ),
+ UNO_QUERY_THROW );
+ if (verbose)
+ dp_misc::writeConsole("OK.\n");
+
+ return xRet;
+}
+
+} // anon namespace
+
+/** returns the path to the lock file used by unopkg.
+ @return the path. An empty string signifies an error.
+*/
+static OUString getLockFilePath()
+{
+ OUString ret;
+ OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}");
+ rtl::Bootstrap::expandMacros(sBootstrap);
+ OUString sAbs;
+ if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
+ sBootstrap, ".lock", sAbs))
+ {
+ if (::osl::File::E_None ==
+ ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
+ {
+ ret = sBootstrap;
+ }
+ }
+
+ return ret;
+}
+
+Reference<XComponentContext> getUNO(
+ bool verbose, bool bGui, const OUString& sTempDir,
+ Reference<XComponentContext> & out_localContext)
+{
+ // do not create any user data (for the root user) in --shared mode:
+ if (!sTempDir.isEmpty())
+ rtl::Bootstrap::set("UserInstallation", sTempDir);
+
+ // hold lock during process runtime:
+ static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
+ Reference<XComponentContext> xComponentContext( bootstrapStandAlone() );
+ out_localContext = xComponentContext;
+ if (::dp_misc::office_is_running()) {
+ xComponentContext.set(
+ connectToOffice( xComponentContext, verbose ) );
+ }
+ else
+ {
+ if (! s_lockfile.check( nullptr ))
+ {
+ OUString sMsg(DpResId(RID_STR_CONCURRENTINSTANCE));
+ OUString sError(DpResId(RID_STR_UNOPKG_ERROR));
+
+ sMsg += "\n" + getLockFilePath();
+
+ if (bGui)
+ {
+ //We show a message box or print to the console that there
+ //is another instance already running
+ if ( ! InitVCL() )
+ throw RuntimeException( "Cannot initialize VCL!" );
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMsg));
+ xWarn->set_title(utl::ConfigManager::getProductName());
+ xWarn->run();
+ }
+ DeInitVCL();
+ }
+
+ throw LockFileException(sError + sMsg);
+ }
+ }
+
+ return xComponentContext;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_shared.h b/desktop/source/pkgchk/unopkg/unopkg_shared.h
new file mode 100644
index 000000000..f86b3248c
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_shared.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+#define APP_NAME "unopkg"
+
+namespace unopkg {
+
+struct OptionInfo
+{
+ char const * m_name;
+ sal_uInt32 m_name_length;
+ sal_Unicode m_short_option;
+ bool m_has_argument;
+};
+
+struct LockFileException : public css::uno::Exception
+{
+ explicit LockFileException(OUString const & sMessage) :
+ css::uno::Exception(sMessage, css::uno::Reference< css::uno::XInterface > ()) {}
+};
+
+
+OUString toString( OptionInfo const * info );
+
+
+OptionInfo const * getOptionInfo(
+ OptionInfo const * list,
+ OUString const & opt );
+
+
+bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex );
+
+
+bool readArgument(
+ OUString * pValue, OptionInfo const * option_info,
+ sal_uInt32 * pIndex );
+
+
+inline bool readOption(
+ bool * flag, OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ if (isOption( option_info, pIndex )) {
+ OSL_ASSERT( flag != nullptr );
+ *flag = true;
+ return true;
+ }
+ return false;
+}
+
+
+/** checks if an argument is a bootstrap variable. These start with -env:. For example
+ -env:UNO_JAVA_JFW_USER_DATA=file:///d:/user
+*/
+bool isBootstrapVariable(sal_uInt32 * pIndex);
+
+OUString const & getExecutableDir();
+
+
+OUString const & getProcessWorkingDir();
+
+
+OUString makeAbsoluteFileUrl(
+ OUString const & sys_path, OUString const & base_url );
+
+
+
+
+css::uno::Reference<css::ucb::XCommandEnvironment> createCmdEnv(
+ css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppressLicense);
+
+void printf_packages(
+ std::vector<
+ css::uno::Reference<css::deployment::XPackage> > const & allExtensions,
+ std::vector<bool> const & vecUnaccepted,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ sal_Int32 level = 0 );
+
+
+
+
+css::uno::Reference<css::uno::XComponentContext> getUNO(
+ bool verbose, bool bGui, const OUString& sTempDir,
+ css::uno::Reference<css::uno::XComponentContext> & out_LocalComponentContext);
+
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/splash/services_spl.cxx b/desktop/source/splash/services_spl.cxx
new file mode 100644
index 000000000..dfa8728fd
--- /dev/null
+++ b/desktop/source/splash/services_spl.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implementationentry.hxx>
+#include <sal/types.h>
+
+#include "splash.hxx"
+#include "unxsplash.hxx"
+
+namespace {
+
+static cppu::ImplementationEntry const services[] = {
+ {
+ &desktop::splash::create,
+ &desktop::splash::getImplementationName,
+ &desktop::splash::getSupportedServiceNames,
+ &cppu::createSingleComponentFactory, nullptr, 0
+ },
+ {
+ UnxSplash_createInstance,
+ UnxSplash_getImplementationName,
+ UnxSplash_getSupportedServiceNames,
+ ::cppu::createSingleComponentFactory, nullptr, 0
+ },
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * spl_component_getFactory(
+ char const * pImplName, void * pServiceManager, void * pRegistryKey)
+{
+ return cppu::component_getFactoryHelper(
+ pImplName, pServiceManager, pRegistryKey, services);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/splash/spl.component b/desktop/source/splash/spl.component
new file mode 100644
index 000000000..bc0ff98fc
--- /dev/null
+++ b/desktop/source/splash/spl.component
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="spl" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.office.comp.SplashScreen">
+ <service name="com.sun.star.office.SplashScreen"/>
+ </implementation>
+ <implementation name="com.sun.star.office.comp.PipeSplashScreen">
+ <service name="com.sun.star.office.PipeSplashScreen"/>
+ </implementation>
+</component>
diff --git a/desktop/source/splash/splash.cxx b/desktop/source/splash/splash.cxx
new file mode 100644
index 000000000..b42ffe11b
--- /dev/null
+++ b/desktop/source/splash/splash.cxx
@@ -0,0 +1,644 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "splash.hxx"
+#include <sal/log.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/salnativewidgets.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/math.hxx>
+#include <vcl/introwin.hxx>
+#include <vcl/virdev.hxx>
+
+#define NOT_LOADED (long(-1))
+#define NOT_LOADED_COLOR (Color(0xffffffff))
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class SplashScreen;
+
+class SplashScreenWindow : public IntroWindow
+{
+public:
+ SplashScreen *pSpl;
+ ScopedVclPtr<VirtualDevice> _vdev;
+ explicit SplashScreenWindow(SplashScreen *);
+ virtual ~SplashScreenWindow() override { disposeOnce(); }
+ virtual void dispose() override;
+ // workwindow
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override;
+ void Redraw();
+
+};
+
+class SplashScreen
+ : public ::cppu::WeakImplHelper< XStatusIndicator, XInitialization, XServiceInfo >
+{
+ friend class SplashScreenWindow;
+private:
+ VclPtr<SplashScreenWindow> pWindow;
+
+ DECL_LINK( AppEventListenerHdl, VclSimpleEvent&, void );
+ virtual ~SplashScreen() override;
+ void loadConfig();
+ void updateStatus();
+ void SetScreenBitmap(BitmapEx &rBitmap);
+ static void determineProgressRatioValues( double& rXRelPos, double& rYRelPos, double& rRelWidth, double& rRelHeight );
+
+ static osl::Mutex _aMutex;
+
+ BitmapEx _aIntroBmp;
+ Color _cProgressFrameColor;
+ Color _cProgressBarColor;
+ Color _cProgressTextColor;
+ bool _bNativeProgress;
+ OUString _sAppName;
+ OUString _sProgressText;
+
+ sal_Int32 _iMax;
+ sal_Int32 _iProgress;
+ bool _bPaintProgress;
+ bool _bVisible;
+ bool _bShowLogo;
+ bool _bFullScreenSplash;
+ bool _bProgressEnd;
+ long _height, _width, _tlx, _tly, _barwidth;
+ long _barheight, _barspace, _textBaseline;
+ double _fXPos, _fYPos;
+ double _fWidth, _fHeight;
+ static constexpr long _xoffset = 12, _yoffset = 18;
+
+public:
+ SplashScreen();
+
+ // XStatusIndicator
+ virtual void SAL_CALL end() override;
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL setText(const OUString& aText) override;
+ virtual void SAL_CALL setValue(sal_Int32 nValue) override;
+ virtual void SAL_CALL start(const OUString& aText, sal_Int32 nRange) override;
+
+ // XInitialize
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any>& aArguments ) override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ { return desktop::splash::getImplementationName(); }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ { return desktop::splash::getSupportedServiceNames(); }
+};
+
+SplashScreenWindow::SplashScreenWindow(SplashScreen *pSplash)
+ : IntroWindow()
+ , pSpl( pSplash )
+ , _vdev(VclPtr<VirtualDevice>::Create(*this))
+{
+ _vdev->EnableRTL(IsRTLEnabled());
+}
+
+void SplashScreenWindow::dispose()
+{
+ pSpl = nullptr;
+ IntroWindow::dispose();
+}
+
+void SplashScreenWindow::Redraw()
+{
+ Invalidate();
+ // Trigger direct painting too - otherwise the splash screen won't be
+ // shown in some cases (when the idle timer won't be hit).
+ Paint(*this, tools::Rectangle());
+ Flush();
+}
+
+SplashScreen::SplashScreen()
+ : pWindow( VclPtr<SplashScreenWindow>::Create(this) )
+ , _cProgressFrameColor(NOT_LOADED_COLOR)
+ , _cProgressBarColor(NOT_LOADED_COLOR)
+ , _cProgressTextColor(NOT_LOADED_COLOR)
+ , _bNativeProgress(true)
+ , _iMax(100)
+ , _iProgress(0)
+ , _bPaintProgress(false)
+ , _bVisible(true)
+ , _bShowLogo(true)
+ , _bFullScreenSplash(false)
+ , _bProgressEnd(false)
+ , _height(0)
+ , _width(0)
+ , _tlx(NOT_LOADED)
+ , _tly(NOT_LOADED)
+ , _barwidth(NOT_LOADED)
+ , _barheight(NOT_LOADED)
+ , _barspace(2)
+ , _textBaseline(NOT_LOADED)
+ , _fXPos(-1.0)
+ , _fYPos(-1.0)
+ , _fWidth(-1.0)
+ , _fHeight(-1.0)
+{
+ loadConfig();
+}
+
+SplashScreen::~SplashScreen()
+{
+ Application::RemoveEventListener(
+ LINK( this, SplashScreen, AppEventListenerHdl ) );
+ pWindow->Hide();
+ pWindow.disposeAndClear();
+}
+
+void SAL_CALL SplashScreen::start(const OUString&, sal_Int32 nRange)
+{
+ _iMax = nRange;
+ if (_bVisible) {
+ _bProgressEnd = false;
+ SolarMutexGuard aSolarGuard;
+ pWindow->Show();
+ pWindow->Redraw();
+ }
+}
+
+void SAL_CALL SplashScreen::end()
+{
+ _iProgress = _iMax;
+ if (_bVisible )
+ {
+ pWindow->Hide();
+ }
+ _bProgressEnd = true;
+}
+
+void SAL_CALL SplashScreen::reset()
+{
+ _iProgress = 0;
+ if (_bVisible && !_bProgressEnd )
+ {
+ pWindow->Show();
+ updateStatus();
+ }
+}
+
+void SAL_CALL SplashScreen::setText(const OUString& rText)
+{
+ SolarMutexGuard aSolarGuard;
+ if ( _sProgressText != rText )
+ {
+ _sProgressText = rText;
+
+ if (_bVisible && !_bProgressEnd)
+ {
+ pWindow->Show();
+ updateStatus();
+ }
+ }
+}
+
+void SAL_CALL SplashScreen::setValue(sal_Int32 nValue)
+{
+ SAL_INFO( "desktop.splash", "setValue: " << nValue );
+
+ SolarMutexGuard aSolarGuard;
+ if (_bVisible && !_bProgressEnd) {
+ pWindow->Show();
+ if (nValue >= _iMax)
+ _iProgress = _iMax;
+ else
+ _iProgress = nValue;
+ updateStatus();
+ }
+}
+
+// XInitialize
+void SAL_CALL
+SplashScreen::initialize( const css::uno::Sequence< css::uno::Any>& aArguments )
+{
+ osl::MutexGuard aGuard( _aMutex );
+ if (!aArguments.hasElements())
+ return;
+
+ aArguments[0] >>= _bVisible;
+ if (aArguments.getLength() > 1 )
+ aArguments[1] >>= _sAppName;
+
+ // start to determine bitmap and all other required value
+ if ( _bShowLogo )
+ SetScreenBitmap (_aIntroBmp);
+ Size aSize = _aIntroBmp.GetSizePixel();
+ pWindow->SetOutputSizePixel( aSize );
+ pWindow->_vdev->SetOutputSizePixel( aSize );
+ _height = aSize.Height();
+ _width = aSize.Width();
+ if (_width > 500)
+ {
+ Point xtopleft(212,216);
+ if ( NOT_LOADED == _tlx || NOT_LOADED == _tly )
+ {
+ _tlx = xtopleft.X(); // top-left x
+ _tly = xtopleft.Y(); // top-left y
+ }
+ if ( NOT_LOADED == _barwidth )
+ _barwidth = 263;
+ if ( NOT_LOADED == _barheight )
+ _barheight = 8;
+ }
+ else
+ {
+ if ( NOT_LOADED == _barwidth )
+ _barwidth = _width - (2 * _xoffset);
+ if ( NOT_LOADED == _barheight )
+ _barheight = 6;
+ if ( NOT_LOADED == _tlx || NOT_LOADED == _tly )
+ {
+ _tlx = _xoffset; // top-left x
+ _tly = _height - _yoffset; // top-left y
+ }
+ }
+
+ if ( NOT_LOADED == _textBaseline )
+ _textBaseline = _height;
+
+ if ( NOT_LOADED_COLOR == _cProgressFrameColor )
+ _cProgressFrameColor = COL_LIGHTGRAY;
+
+ if ( NOT_LOADED_COLOR == _cProgressBarColor )
+ {
+ // progress bar: new color only for big bitmap format
+ if ( _width > 500 )
+ _cProgressBarColor = Color( 157, 202, 18 );
+ else
+ _cProgressBarColor = COL_BLUE;
+ }
+
+ if ( NOT_LOADED_COLOR == _cProgressTextColor )
+ _cProgressTextColor = COL_BLACK;
+
+ Application::AddEventListener(
+ LINK( this, SplashScreen, AppEventListenerHdl ) );
+}
+
+void SplashScreen::updateStatus()
+{
+ if (!_bVisible || _bProgressEnd)
+ return;
+ if (!_bPaintProgress)
+ _bPaintProgress = true;
+ pWindow->Redraw();
+}
+
+// internal private methods
+IMPL_LINK( SplashScreen, AppEventListenerHdl, VclSimpleEvent&, inEvent, void )
+{
+ if (static_cast<VclWindowEvent&>(inEvent).GetWindow() == pWindow)
+ {
+ switch ( inEvent.GetId() )
+ {
+ case VclEventId::WindowShow:
+ pWindow->Redraw();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Read keys from soffice{.ini|rc}:
+OUString implReadBootstrapKey( const OUString& _rKey )
+{
+ OUString sValue;
+ rtl::Bootstrap::get(_rKey, sValue);
+ return sValue;
+}
+
+void SplashScreen::loadConfig()
+{
+ _bShowLogo = implReadBootstrapKey( "Logo" ) != "0";
+
+ OUString sProgressFrameColor = implReadBootstrapKey( "ProgressFrameColor" );
+ OUString sProgressBarColor = implReadBootstrapKey( "ProgressBarColor" );
+ OUString sProgressTextColor = implReadBootstrapKey( "ProgressTextColor" );
+ OUString sProgressTextBaseline = implReadBootstrapKey( "ProgressTextBaseline" );
+ OUString sSize = implReadBootstrapKey( "ProgressSize" );
+ OUString sPosition = implReadBootstrapKey( "ProgressPosition" );
+ OUString sFullScreenSplash = implReadBootstrapKey( "FullScreenSplash" );
+ OUString sNativeProgress = implReadBootstrapKey( "NativeProgress" );
+
+
+ // Determine full screen splash mode
+ _bFullScreenSplash = (( !sFullScreenSplash.isEmpty() ) &&
+ ( sFullScreenSplash != "0" ));
+
+ // Try to retrieve the relative values for the progress bar. The current
+ // schema uses the screen ratio to retrieve the associated values.
+ if ( _bFullScreenSplash )
+ determineProgressRatioValues( _fXPos, _fYPos, _fWidth, _fHeight );
+
+ if ( !sProgressFrameColor.isEmpty() )
+ {
+ sal_uInt8 nRed = 0;
+ sal_Int32 idx = 0;
+ sal_Int32 temp = sProgressFrameColor.getToken( 0, ',', idx ).toInt32();
+ if ( idx != -1 )
+ {
+ nRed = static_cast< sal_uInt8 >( temp );
+ temp = sProgressFrameColor.getToken( 0, ',', idx ).toInt32();
+ }
+ if ( idx != -1 )
+ {
+ sal_uInt8 nGreen = static_cast< sal_uInt8 >( temp );
+ sal_uInt8 nBlue = static_cast< sal_uInt8 >( sProgressFrameColor.getToken( 0, ',', idx ).toInt32() );
+ _cProgressFrameColor = Color( nRed, nGreen, nBlue );
+ }
+ }
+
+ if ( !sProgressBarColor.isEmpty() )
+ {
+ sal_uInt8 nRed = 0;
+ sal_Int32 idx = 0;
+ sal_Int32 temp = sProgressBarColor.getToken( 0, ',', idx ).toInt32();
+ if ( idx != -1 )
+ {
+ nRed = static_cast< sal_uInt8 >( temp );
+ temp = sProgressBarColor.getToken( 0, ',', idx ).toInt32();
+ }
+ if ( idx != -1 )
+ {
+ sal_uInt8 nGreen = static_cast< sal_uInt8 >( temp );
+ sal_uInt8 nBlue = static_cast< sal_uInt8 >( sProgressBarColor.getToken( 0, ',', idx ).toInt32() );
+ _cProgressBarColor = Color( nRed, nGreen, nBlue );
+ }
+ }
+
+ if ( !sProgressTextColor.isEmpty() )
+ {
+ sal_uInt8 nRed = 0;
+ sal_Int32 idx = 0;
+ sal_Int32 temp = sProgressTextColor.getToken( 0, ',', idx ).toInt32();
+ if ( idx != -1 )
+ {
+ nRed = static_cast< sal_uInt8 >( temp );
+ temp = sProgressTextColor.getToken( 0, ',', idx ).toInt32();
+ }
+ if ( idx != -1 )
+ {
+ sal_uInt8 nGreen = static_cast< sal_uInt8 >( temp );
+ sal_uInt8 nBlue = static_cast< sal_uInt8 >( sProgressTextColor.getToken( 0, ',', idx ).toInt32() );
+ _cProgressTextColor = Color( nRed, nGreen, nBlue );
+ }
+ }
+
+ if ( !sProgressTextBaseline.isEmpty() )
+ {
+ _textBaseline = sProgressTextBaseline.toInt32();
+ }
+
+ if( !sNativeProgress.isEmpty() )
+ {
+ _bNativeProgress = sNativeProgress.toBoolean();
+ }
+
+ if ( !sSize.isEmpty() )
+ {
+ sal_Int32 idx = 0;
+ sal_Int32 temp = sSize.getToken( 0, ',', idx ).toInt32();
+ if ( idx != -1 )
+ {
+ _barwidth = temp;
+ _barheight = sSize.getToken( 0, ',', idx ).toInt32();
+ }
+ }
+
+ if ( _barheight >= 10 )
+ _barspace = 3; // more space between frame and bar
+
+ if ( !sPosition.isEmpty() )
+ {
+ sal_Int32 idx = 0;
+ sal_Int32 temp = sPosition.getToken( 0, ',', idx ).toInt32();
+ if ( idx != -1 )
+ {
+ _tlx = temp;
+ _tly = sPosition.getToken( 0, ',', idx ).toInt32();
+ }
+ }
+}
+
+void SplashScreen::SetScreenBitmap(BitmapEx &rBitmap)
+{
+ sal_Int32 nWidth( 0 );
+ sal_Int32 nHeight( 0 );
+
+ // determine desktop resolution
+ sal_uInt32 nCount = Application::GetScreenCount();
+ if ( nCount > 0 )
+ {
+ // retrieve size from first screen
+ tools::Rectangle aScreenArea = Application::GetScreenPosSizePixel(static_cast<unsigned int>(0));
+ nWidth = aScreenArea.GetWidth();
+ nHeight = aScreenArea.GetHeight();
+ }
+
+ // create file name from screen resolution information
+ OStringBuffer aStrBuf( 128 );
+ aStrBuf.append( "intro_" );
+ if ( !_sAppName.isEmpty() )
+ {
+ aStrBuf.append( OUStringToOString(_sAppName, RTL_TEXTENCODING_UTF8) );
+ aStrBuf.append( "_" );
+ }
+ OString aResBuf = OString::number( nWidth ) + "x" + OString::number( nHeight );
+
+ aStrBuf.append( aResBuf.getStr() );
+ if (Application::LoadBrandBitmap (aStrBuf.makeStringAndClear().getStr(), rBitmap))
+ return;
+
+ aStrBuf.append( "intro_" );
+ aStrBuf.append( aResBuf.getStr() );
+ if (Application::LoadBrandBitmap (aResBuf.getStr(), rBitmap))
+ return;
+
+ (void)Application::LoadBrandBitmap ("intro", rBitmap);
+}
+
+void SplashScreen::determineProgressRatioValues(
+ double& rXRelPos, double& rYRelPos,
+ double& rRelWidth, double& rRelHeight )
+{
+ sal_Int32 nWidth( 0 );
+ sal_Int32 nHeight( 0 );
+ sal_Int32 nScreenRatio( 0 );
+
+ // determine desktop resolution
+ sal_uInt32 nCount = Application::GetScreenCount();
+ if ( nCount > 0 )
+ {
+ // retrieve size from first screen
+ tools::Rectangle aScreenArea = Application::GetScreenPosSizePixel(static_cast<unsigned int>(0));
+ nWidth = aScreenArea.GetWidth();
+ nHeight = aScreenArea.GetHeight();
+ nScreenRatio = nHeight ? sal_Int32( rtl::math::round( double( nWidth ) / double( nHeight ), 2 ) * 100 ) : 0;
+ }
+
+ char szFullScreenProgressRatio[] = "FullScreenProgressRatio0";
+ char szFullScreenProgressPos[] = "FullScreenProgressPos0";
+ char szFullScreenProgressSize[] = "FullScreenProgressSize0";
+ for ( sal_Int32 i = 0; i <= 9; i++ )
+ {
+ char cNum = '0' + char( i );
+ szFullScreenProgressRatio[23] = cNum;
+ szFullScreenProgressPos[21] = cNum;
+ szFullScreenProgressSize[22] = cNum;
+
+ OUString sFullScreenProgressRatio = implReadBootstrapKey(
+ OUString::createFromAscii( szFullScreenProgressRatio ) );
+
+ if ( !sFullScreenProgressRatio.isEmpty() )
+ {
+ double fRatio = sFullScreenProgressRatio.toDouble();
+ sal_Int32 nRatio = sal_Int32( rtl::math::round( fRatio, 2 ) * 100 );
+ if ( nRatio == nScreenRatio )
+ {
+ OUString sFullScreenProgressPos = implReadBootstrapKey(
+ OUString::createFromAscii( szFullScreenProgressPos ) );
+ OUString sFullScreenProgressSize = implReadBootstrapKey(
+ OUString::createFromAscii( szFullScreenProgressSize ) );
+
+ if ( !sFullScreenProgressPos.isEmpty() )
+ {
+ sal_Int32 idx = 0;
+ double temp = sFullScreenProgressPos.getToken( 0, ',', idx ).toDouble();
+ if ( idx != -1 )
+ {
+ rXRelPos = temp;
+ rYRelPos = sFullScreenProgressPos.getToken( 0, ',', idx ).toDouble();
+ }
+ }
+
+ if ( !sFullScreenProgressSize.isEmpty() )
+ {
+ sal_Int32 idx = 0;
+ double temp = sFullScreenProgressSize.getToken( 0, ',', idx ).toDouble();
+ if ( idx != -1 )
+ {
+ rRelWidth = temp;
+ rRelHeight = sFullScreenProgressSize.getToken( 0, ',', idx ).toDouble();
+ }
+ }
+ }
+ }
+ else
+ break;
+ }
+}
+
+void SplashScreenWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ if (!pSpl || !pSpl->_bVisible)
+ return;
+
+ //native drawing
+ // in case of native controls we need to draw directly to the window
+ if (pSpl->_bNativeProgress && rRenderContext.IsNativeControlSupported(ControlType::IntroProgress, ControlPart::Entire))
+ {
+ rRenderContext.DrawBitmapEx(Point(), pSpl->_aIntroBmp);
+
+ ImplControlValue aValue( pSpl->_iProgress * pSpl->_barwidth / pSpl->_iMax);
+ tools::Rectangle aDrawRect( Point(pSpl->_tlx, pSpl->_tly), Size( pSpl->_barwidth, pSpl->_barheight));
+ tools::Rectangle aNativeControlRegion, aNativeContentRegion;
+
+ if (rRenderContext.GetNativeControlRegion(ControlType::IntroProgress, ControlPart::Entire, aDrawRect,
+ ControlState::ENABLED, aValue,
+ aNativeControlRegion, aNativeContentRegion))
+ {
+ long nProgressHeight = aNativeControlRegion.GetHeight();
+ aDrawRect.AdjustTop( -((nProgressHeight - pSpl->_barheight)/2) );
+ aDrawRect.AdjustBottom((nProgressHeight - pSpl->_barheight)/2 );
+ }
+
+ if (rRenderContext.DrawNativeControl(ControlType::IntroProgress, ControlPart::Entire, aDrawRect,
+ ControlState::ENABLED, aValue, pSpl->_sProgressText))
+ {
+ return;
+ }
+ }
+
+ // non native drawing
+ // draw bitmap
+ _vdev->DrawBitmapEx(Point(), pSpl->_aIntroBmp);
+
+ if (pSpl->_bPaintProgress) {
+ // draw progress...
+ long length = (pSpl->_iProgress * pSpl->_barwidth / pSpl->_iMax) - (2 * pSpl->_barspace);
+ if (length < 0) length = 0;
+
+ // border
+ _vdev->SetFillColor();
+ _vdev->SetLineColor( pSpl->_cProgressFrameColor );
+ _vdev->DrawRect(tools::Rectangle(pSpl->_tlx, pSpl->_tly, pSpl->_tlx+pSpl->_barwidth, pSpl->_tly+pSpl->_barheight));
+ _vdev->SetFillColor( pSpl->_cProgressBarColor );
+ _vdev->SetLineColor();
+ _vdev->DrawRect(tools::Rectangle(pSpl->_tlx+pSpl->_barspace, pSpl->_tly+pSpl->_barspace, pSpl->_tlx+pSpl->_barspace+length, pSpl->_tly+pSpl->_barheight-pSpl->_barspace));
+ vcl::Font aFont;
+ aFont.SetFontSize(Size(0, 12));
+ aFont.SetAlignment(ALIGN_BASELINE);
+ _vdev->SetFont(aFont);
+ _vdev->SetTextColor(pSpl->_cProgressTextColor);
+ _vdev->DrawText(Point(pSpl->_tlx, pSpl->_textBaseline), pSpl->_sProgressText);
+ }
+ rRenderContext.DrawOutDev(Point(), GetOutputSizePixel(), Point(), _vdev->GetOutputSizePixel(), *_vdev);
+}
+
+
+// get service instance...
+osl::Mutex SplashScreen::_aMutex;
+
+}
+
+css::uno::Reference< css::uno::XInterface > desktop::splash::create(
+ css::uno::Reference< css::uno::XComponentContext > const &)
+{
+ return static_cast< cppu::OWeakObject * >(new SplashScreen);
+}
+
+OUString desktop::splash::getImplementationName() {
+ return "com.sun.star.office.comp.SplashScreen";
+}
+
+css::uno::Sequence< OUString > desktop::splash::getSupportedServiceNames()
+{
+ return { "com.sun.star.office.SplashScreen" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/splash/splash.hxx b/desktop/source/splash/splash.hxx
new file mode 100644
index 000000000..0a31258d7
--- /dev/null
+++ b/desktop/source/splash/splash.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_SPLASH_SPLASH_HXX
+#define INCLUDED_DESKTOP_SOURCE_SPLASH_SPLASH_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ class XInterface;
+ }
+}
+
+namespace desktop::splash {
+
+css::uno::Reference< css::uno::XInterface >
+create( css::uno::Reference< css::uno::XComponentContext > const & );
+
+OUString getImplementationName();
+
+css::uno::Sequence< OUString >
+getSupportedServiceNames();
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/splash/unxsplash.cxx b/desktop/source/splash/unxsplash.cxx
new file mode 100644
index 000000000..cf1c66144
--- /dev/null
+++ b/desktop/source/splash/unxsplash.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "unxsplash.hxx"
+#include <stdio.h>
+#include <osl/process.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+namespace desktop
+{
+ UnxSplashScreen::UnxSplashScreen()
+ : m_pOutFd( nullptr )
+{
+}
+
+UnxSplashScreen::~UnxSplashScreen()
+{
+ SAL_INFO("desktop.splash", "UnxSplashScreen::~UnxSplashScreen()");
+ if ( m_pOutFd )
+ {
+ fclose( m_pOutFd );
+ m_pOutFd = nullptr;
+ }
+}
+
+void SAL_CALL UnxSplashScreen::start( const OUString& /*aText*/, sal_Int32 /*nRange*/ )
+{
+}
+
+void SAL_CALL UnxSplashScreen::end()
+{
+ SAL_INFO("desktop.splash", "UnxSplashScreen::end()");
+ if( !m_pOutFd )
+ return;
+
+ fprintf( m_pOutFd, "end\n" );
+ fflush( m_pOutFd );
+}
+
+void SAL_CALL UnxSplashScreen::reset()
+{
+ SAL_INFO("desktop.splash", "UNXSplashScreen::reset()");
+ if( !m_pOutFd )
+ return;
+
+ fprintf( m_pOutFd, "restart\n" );
+ fflush( m_pOutFd );
+}
+
+void SAL_CALL UnxSplashScreen::setText( const OUString& /*aText*/ )
+{
+ // TODO?
+}
+
+void SAL_CALL UnxSplashScreen::setValue( sal_Int32 nValue )
+{
+ if ( m_pOutFd )
+ {
+ fprintf( m_pOutFd, "%" SAL_PRIdINT32 "%%\n", nValue );
+ fflush( m_pOutFd );
+ }
+}
+
+// XInitialize
+void SAL_CALL
+UnxSplashScreen::initialize( const css::uno::Sequence< css::uno::Any>& )
+{
+ for ( sal_uInt32 i = 0; i < osl_getCommandArgCount(); i++ )
+ {
+ OUString aArg;
+ osl_getCommandArg( i, &aArg.pData );
+ OUString aNum;
+ if ( aArg.startsWithIgnoreAsciiCase("--splash-pipe=", &aNum) )
+ {
+ auto fd = aNum.toUInt32();
+ m_pOutFd = fdopen( fd, "w" );
+ SAL_INFO("desktop.splash", "Got argument '--splash-pipe=" << fd << " ('"
+ << aNum << "') ("
+ << static_cast<void *>(m_pOutFd) << ")");
+ }
+ }
+}
+
+OUString UnxSplashScreen::getImplementationName()
+{
+ return UnxSplash_getImplementationName();
+}
+
+sal_Bool UnxSplashScreen::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> UnxSplashScreen::getSupportedServiceNames()
+{
+ return UnxSplash_getSupportedServiceNames();
+}
+
+}
+
+using namespace desktop;
+
+// get service instance...
+static uno::Reference< uno::XInterface > m_xINSTANCE;
+
+uno::Reference< uno::XInterface > UnxSplash_createInstance(const uno::Reference< uno::XComponentContext > & )
+{
+ static osl::Mutex s_aMutex;
+ if ( !m_xINSTANCE.is() )
+ {
+ osl::MutexGuard guard( s_aMutex );
+ if ( !m_xINSTANCE.is() )
+ m_xINSTANCE = static_cast<cppu::OWeakObject*>(new UnxSplashScreen);
+ }
+
+ return m_xINSTANCE;
+}
+
+OUString UnxSplash_getImplementationName()
+{
+ return "com.sun.star.office.comp.PipeSplashScreen";
+}
+
+uno::Sequence< OUString > UnxSplash_getSupportedServiceNames() throw()
+{
+ return uno::Sequence< OUString > { "com.sun.star.office.PipeSplashScreen" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/splash/unxsplash.hxx b/desktop/source/splash/unxsplash.hxx
new file mode 100644
index 000000000..baa612a0d
--- /dev/null
+++ b/desktop/source/splash/unxsplash.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_DESKTOP_SOURCE_SPLASH_UNXSPLASH_HXX
+#define INCLUDED_DESKTOP_SOURCE_SPLASH_UNXSPLASH_HXX
+
+#include <stdio.h>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+
+namespace desktop {
+
+class UnxSplashScreen : public ::cppu::WeakImplHelper< css::task::XStatusIndicator, css::lang::XInitialization, css::lang::XServiceInfo >
+{
+private:
+ UnxSplashScreen( const UnxSplashScreen& ) = delete;
+ UnxSplashScreen operator =( const UnxSplashScreen& ) = delete;
+
+ virtual ~UnxSplashScreen() override;
+
+ static UnxSplashScreen *m_pINSTANCE;
+
+ static osl::Mutex m_aMutex;
+
+ FILE *m_pOutFd;
+
+public:
+ explicit UnxSplashScreen();
+
+ // XStatusIndicator
+ virtual void SAL_CALL start( const OUString& aText, sal_Int32 nRange ) override;
+ virtual void SAL_CALL end() override;
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL setText( const OUString& aText ) override;
+ virtual void SAL_CALL setValue( sal_Int32 nValue ) override;
+
+ // XInitialize
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any>& aArguments ) override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+/// @throws css::uno::Exception
+css::uno::Reference< css::uno::XInterface > UnxSplash_createInstance(const css::uno::Reference< css::uno::XComponentContext > & xCtx );
+OUString UnxSplash_getImplementationName();
+css::uno::Sequence< OUString > UnxSplash_getSupportedServiceNames() throw ();
+
+#endif // INCLUDED_DESKTOP_SOURCE_SPLASH_UNXSPLASH_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/active/Addons.xcu b/desktop/test/deployment/active/Addons.xcu
new file mode 100644
index 000000000..d3b481ef4
--- /dev/null
+++ b/desktop/test/deployment/active/Addons.xcu
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<o:component-data xmlns:o="http://openoffice.org/2001/registry"
+ o:package="org.openoffice.Office" o:name="Addons">
+ <node o:name="AddonUI">
+ <node o:name="OfficeMenuBar">
+ <node o:name="org.openoffice.test.desktop.deployment.active"
+ o:op="replace">
+ <prop o:name="Title" xml:lang="en-US">
+ <value>active</value>
+ </prop>
+ <node o:name="Submenu">
+ <node o:name="1" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_native:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>native</value>
+ </prop>
+ </node>
+ <node o:name="2" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_java:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>java</value>
+ </prop>
+ </node>
+ <node o:name="3" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_python:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>python</value>
+ </prop>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+</o:component-data>
diff --git a/desktop/test/deployment/active/MANIFEST.MF b/desktop/test/deployment/active/MANIFEST.MF
new file mode 100644
index 000000000..63480874d
--- /dev/null
+++ b/desktop/test/deployment/active/MANIFEST.MF
@@ -0,0 +1,3 @@
+Sealed: true
+RegistrationClassName: com.sun.star.comp.test.deployment.active_java.Services
+UNO-Type-Path:
diff --git a/desktop/test/deployment/active/META-INF/manifest.xml b/desktop/test/deployment/active/META-INF/manifest.xml
new file mode 100644
index 000000000..7cca7841d
--- /dev/null
+++ b/desktop/test/deployment/active/META-INF/manifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<m:manifest xmlns:m="http://openoffice.org/2001/manifest">
+ <m:file-entry m:media-type="application/vnd.sun.star.configuration-data"
+ m:full-path="Addons.xcu"/>
+ <m:file-entry m:media-type="application/vnd.sun.star.configuration-data"
+ m:full-path="ProtocolHandler.xcu"/>
+ <m:file-entry
+ m:media-type="application/vnd.sun.star.uno-component;type=native;platform=@PLATFORM@"
+ m:full-path="active_native.uno@SHARED_EXTENSION@"/>
+ <m:file-entry
+ m:media-type="application/vnd.sun.star.uno-component;type=Java"
+ m:full-path="active_java.jar"/>
+ <m:file-entry
+ m:media-type="application/vnd.sun.star.uno-component;type=Python"
+ m:full-path="active_python.py"/>
+</m:manifest>
diff --git a/desktop/test/deployment/active/ProtocolHandler.xcu b/desktop/test/deployment/active/ProtocolHandler.xcu
new file mode 100644
index 000000000..984ed981d
--- /dev/null
+++ b/desktop/test/deployment/active/ProtocolHandler.xcu
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<o:component-data xmlns:o="http://openoffice.org/2001/registry"
+ o:package="org.openoffice.Office" o:name="ProtocolHandler">
+ <node o:name="HandlerSet">
+ <node o:name="com.sun.star.test.deployment.active_native" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_native:*</value>
+ </prop>
+ </node>
+ <node o:name="com.sun.star.test.deployment.active_java" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_java:*</value>
+ </prop>
+ </node>
+ <node o:name="com.sun.star.test.deployment.active_python" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.active_python:*</value>
+ </prop>
+ </node>
+ </node>
+</o:component-data>
diff --git a/desktop/test/deployment/active/active_native.cxx b/desktop/test/deployment/active/active_native.cxx
new file mode 100644
index 000000000..b024b5e4f
--- /dev/null
+++ b/desktop/test/deployment/active/active_native.cxx
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <com/sun/star/awt/MessageBoxButtons.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/XMessageBox.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/implementationentry.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <uno/lbnames.h>
+
+namespace {
+
+class Provider:
+ public cppu::WeakImplHelper2<
+ css::lang::XServiceInfo, css::frame::XDispatchProvider >
+{
+public:
+ Provider(const Provider&) = delete;
+ const Provider& operator=(const Provider&) = delete;
+
+ static css::uno::Reference< css::uno::XInterface > SAL_CALL static_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext)
+ { return static_cast< cppu::OWeakObject * >(new Provider(xContext)); }
+
+ static rtl::OUString SAL_CALL static_getImplementationName();
+
+ static css::uno::Sequence< rtl::OUString > SAL_CALL
+ static_getSupportedServiceNames();
+
+private:
+ explicit Provider(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ context_(context) { assert(context.is()); }
+
+ virtual ~Provider() {}
+
+ virtual rtl::OUString SAL_CALL getImplementationName() override
+ { return static_getImplementationName(); }
+
+ virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< rtl::OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return static_getSupportedServiceNames(); }
+
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch(
+ css::util::URL const &, rtl::OUString const &, sal_Int32) override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > >
+ SAL_CALL queryDispatches(
+ css::uno::Sequence< css::frame::DispatchDescriptor > const & Requests) override;
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+};
+
+rtl::OUString Provider::static_getImplementationName() {
+ return rtl::OUString("com.sun.star.comp.test.deployment.active_native");
+}
+
+css::uno::Sequence< rtl::OUString > Provider::static_getSupportedServiceNames()
+{
+ rtl::OUString name("com.sun.star.test.deployment.active_native");
+ return css::uno::Sequence< rtl::OUString >(&name, 1);
+}
+
+css::uno::Reference< css::frame::XDispatch > Provider::queryDispatch(
+ css::util::URL const &, rtl::OUString const &, sal_Int32)
+{
+ css::uno::Reference< css::frame::XDispatch > dispatch;
+ if (!(context_->getValueByName(
+ "/singletons/com.sun.star.test.deployment."
+ "active_native_singleton") >>=
+ dispatch) ||
+ !dispatch.is())
+ {
+ throw css::uno::DeploymentException(
+ "component context fails to supply singleton"
+ " com.sun.star.test.deployment.active_native_singleton of type"
+ " com.sun.star.frame.XDispatch",
+ context_);
+ }
+ return dispatch;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > >
+Provider::queryDispatches(
+ css::uno::Sequence< css::frame::DispatchDescriptor > const & Requests)
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > s(
+ Requests.getLength());
+ for (sal_Int32 i = 0; i < s.(); ++i) {
+ s[i] = queryDispatch(
+ Requests[i].FeatureURL, Requests[i].FrameName,
+ Requests[i].SearchFlags);
+ }
+ return s;
+}
+
+class Dispatch:
+ public cppu::WeakImplHelper2<
+ css::lang::XServiceInfo, css::frame::XDispatch >
+{
+public:
+ Dispatch(const Dispatch&) = delete;
+ const Dispatch& operator=(const Dispatch&) = delete;
+
+ static css::uno::Reference< css::uno::XInterface > SAL_CALL static_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext)
+ { return static_cast< cppu::OWeakObject * >(new Dispatch(xContext)); }
+
+ static rtl::OUString SAL_CALL static_getImplementationName();
+
+ static css::uno::Sequence< rtl::OUString > SAL_CALL
+ static_getSupportedServiceNames()
+ { return css::uno::Sequence< rtl::OUString >(); }
+
+private:
+ explicit Dispatch(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ context_(context) { assert(context.is()); }
+
+ virtual ~Dispatch() {}
+
+ virtual rtl::OUString SAL_CALL getImplementationName() override
+ { return static_getImplementationName(); }
+
+ virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< rtl::OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return static_getSupportedServiceNames(); }
+
+ virtual void SAL_CALL dispatch(
+ css::util::URL const &,
+ css::uno::Sequence< css::beans::PropertyValue > const &) override;
+
+ virtual void SAL_CALL addStatusListener(
+ css::uno::Reference< css::frame::XStatusListener > const &,
+ css::util::URL const &) override
+ {}
+
+ virtual void SAL_CALL removeStatusListener(
+ css::uno::Reference< css::frame::XStatusListener > const &,
+ css::util::URL const &) override
+ {}
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+};
+
+rtl::OUString Dispatch::static_getImplementationName() {
+ return rtl::OUString(
+ "com.sun.star.comp.test.deployment.active_native_singleton");
+}
+
+void Dispatch::dispatch(
+ css::util::URL const &,
+ css::uno::Sequence< css::beans::PropertyValue > const &)
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(context_);
+ css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->getCurrentFrame();
+ css::uno::Reference< css::awt::XWindowPeer > xWindowPeer( xFrame->getComponentWindow(), css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create(context_);
+ css::uno::Reference< css::awt::XMessageBox > box(
+ xToolkit->createMessageBox(
+ xWindowPeer,
+ css::awt::MessageBoxType_INFOBOX,
+ css::awt::MessageBoxButtons::BUTTONS_OK, "active", "native"),
+ css::uno::UNO_SET_THROW);
+
+ box->execute();
+
+ css::uno::Reference< css::lang::XComponent > xComponent(box, css::uno::UNO_QUERY_THROW);
+ xComponent->dispose();
+}
+
+static cppu::ImplementationEntry const services[] = {
+ { &Provider::static_create, &Provider::static_getImplementationName,
+ &Provider::static_getSupportedServiceNames,
+ &cppu::createSingleComponentFactory, nullptr, 0 },
+ { &Dispatch::static_create, &Dispatch::static_getImplementationName,
+ &Dispatch::static_getSupportedServiceNames,
+ &cppu::createSingleComponentFactory, nullptr, 0 },
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * component_getFactory(
+ char const * pImplName, void * pServiceManager, void * pRegistryKey)
+{
+ return cppu::component_getFactoryHelper(
+ pImplName, pServiceManager, pRegistryKey, services);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void
+component_getImplementationEnvironment(
+ char const ** ppEnvTypeName, uno_Environment **)
+{
+ *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool component_writeInfo(
+ void * pServiceManager, void * pRegistryKey)
+{
+ if (!component_writeInfoHelper(pServiceManager, pRegistryKey, services)) {
+ return false;
+ }
+ try {
+ css::uno::Reference< css::registry::XRegistryKey >(
+ (css::uno::Reference< css::registry::XRegistryKey >(
+ static_cast< css::registry::XRegistryKey * >(pRegistryKey))->
+ createKey(
+ "/" + Dispatch::static_getImplementationName() +
+ ("/UNO/SINGLETONS/com.sun.star.test.deployment."
+ "active_native_singleton"))),
+ css::uno::UNO_SET_THROW)->
+ setStringValue(Dispatch::static_getImplementationName());
+ } catch (const css::uno::Exception & e) {
+ SAL_INFO(
+ "desktop.test",
+ "active_native component_writeInfo exception: " << e.Message);
+ return false;
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/active/active_python.py b/desktop/test/deployment/active/active_python.py
new file mode 100644
index 000000000..7f6e5bbd4
--- /dev/null
+++ b/desktop/test/deployment/active/active_python.py
@@ -0,0 +1,112 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+import uno
+import unohelper
+
+from com.sun.star.awt import Rectangle
+from com.sun.star.awt.MessageBoxButtons import BUTTONS_OK
+from com.sun.star.awt.MessageBoxType import INFOBOX
+from com.sun.star.frame import XDispatch, XDispatchProvider
+from com.sun.star.lang import XServiceInfo
+from com.sun.star.registry import InvalidRegistryException
+
+class Provider(unohelper.Base, XServiceInfo, XDispatchProvider):
+ implementationName = "com.sun.star.comp.test.deployment.active_python"
+
+ serviceNames = ("com.sun.star.test.deployment.active_python",)
+
+ def __init__(self, context):
+ self.context = context
+
+ def getImplementationName(self):
+ return self.implementationName
+
+ def supportsService(self, ServiceName):
+ return ServiceName in self.serviceNames
+
+ def getSupportedServiceNames(self):
+ return self.serviceNames
+
+ def queryDispatch(self, URL, TargetFrame, SearchFlags):
+ return self.context.getValueByName( \
+ "/singletons/com.sun.star.test.deployment.active_python_singleton")
+
+ def queryDispatches(self, Requests):
+ tuple( \
+ self.queryDispatch(i.FeatureURL, i.FrameName, i.SearchFlags) \
+ for i in Requests)
+
+class Dispatch(unohelper.Base, XServiceInfo, XDispatch):
+ implementationName = \
+ "com.sun.star.comp.test.deployment.active_python_singleton"
+
+ serviceNames = ()
+
+ def __init__(self, context):
+ self.context = context
+
+ def getImplementationName(self):
+ return self.implementationName
+
+ def supportsService(self, ServiceName):
+ return ServiceName in self.serviceNames
+
+ def getSupportedServiceNames(self):
+ return self.serviceNames
+
+ def dispatch(self, URL, Arguments):
+ smgr = self.context.getServiceManager()
+ box = smgr.createInstanceWithContext( \
+ "com.sun.star.awt.Toolkit", self.context).createMessageBox( \
+ smgr.createInstanceWithContext( \
+ "com.sun.star.frame.Desktop", self.context). \
+ getCurrentFrame().getComponentWindow(), \
+ INFOBOX, BUTTONS_OK, "active", "python")
+ box.execute();
+ box.dispose();
+
+ def addStatusListener(self, Control, URL):
+ pass
+
+ def removeStatusListener(self, Control, URL):
+ pass
+
+def getComponentFactory(implementationName, smgr, regKey):
+ if implementationName == Provider.implementationName:
+ return unohelper.createSingleServiceFactory( \
+ Provider, Provider.implementationName, Provider.serviceNames)
+ elif implementationName == Dispatch.implementationName:
+ return unohelper.createSingleServiceFactory( \
+ Dispatch, Dispatch.implementationName, Dispatch.serviceNames)
+ else:
+ return None
+
+def writeRegistryInfo(smgr, regKey):
+ try:
+ for i in (Provider, Dispatch):
+ key = regKey.createKey("/" + i.implementationName + "/UNO")
+ for j in i.serviceNames:
+ key.createKey("/SERVICES/" + j);
+ regKey.createKey( \
+ "/" + Dispatch.implementationName + "/UNO/SINGLETONS/" \
+ "com.sun.star.test.deployment.active_python_singleton"). \
+ setStringValue(Dispatch.implementationName)
+ except InvalidRegistryException:
+ return False
+ return True
diff --git a/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Dispatch.java b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Dispatch.java
new file mode 100644
index 000000000..e224c9418
--- /dev/null
+++ b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Dispatch.java
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.active_java;
+
+import com.sun.star.awt.MessageBoxButtons;
+import com.sun.star.awt.MessageBoxType;
+import com.sun.star.awt.Rectangle;
+import com.sun.star.awt.XMessageBox;
+import com.sun.star.awt.XMessageBoxFactory;
+import com.sun.star.awt.XWindowPeer;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.frame.DispatchDescriptor;
+import com.sun.star.frame.XDesktop;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XStatusListener;
+import com.sun.star.lang.WrappedTargetRuntimeException;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.URL;
+
+public final class Dispatch extends WeakBase implements XServiceInfo, XDispatch
+{
+ public Dispatch(XComponentContext context) {
+ this.context = context;
+ }
+
+ public String getImplementationName() { return implementationName; }
+
+ public boolean supportsService(String ServiceName) {
+ return false; //TODO
+ }
+
+ public String[] getSupportedServiceNames() {
+ return serviceNames;
+ }
+
+ public void dispatch(URL URL, PropertyValue[] Arguments) {
+ try {
+ XMultiComponentFactory smgr = UnoRuntime.queryInterface(
+ XMultiComponentFactory.class, context.getServiceManager());
+ XMessageBox box = UnoRuntime.queryInterface(
+ XMessageBoxFactory.class,
+ smgr.createInstanceWithContext(
+ "com.sun.star.awt.Toolkit", context)).
+ createMessageBox(
+ UnoRuntime.queryInterface(
+ XWindowPeer.class,
+ (UnoRuntime.queryInterface(
+ XDesktop.class,
+ smgr.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", context)).
+ getCurrentFrame().getComponentWindow())),
+ MessageBoxType.INFOBOX, MessageBoxButtons.BUTTONS_OK,
+ "active", "java");
+ box.execute();
+ UnoRuntime.queryInterface(XComponent.class, box).dispose();
+ } catch (com.sun.star.uno.RuntimeException e) {
+ throw e;
+ } catch (com.sun.star.uno.Exception e) {
+ throw new WrappedTargetRuntimeException(e,
+ "wrapped: " + e.getMessage(), this, e);
+ }
+ }
+
+ public void addStatusListener(XStatusListener Control, URL URL) {}
+
+ public void removeStatusListener(XStatusListener Control, URL URL) {}
+
+ private final XComponentContext context;
+
+ static final String implementationName =
+ "com.sun.star.comp.test.deployment.active_java_singleton";
+
+ static final String[] serviceNames = new String[0];
+}
diff --git a/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Provider.java b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Provider.java
new file mode 100644
index 000000000..3b9f8b88e
--- /dev/null
+++ b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Provider.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.active_java;
+
+import com.sun.star.frame.DispatchDescriptor;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XDispatchProvider;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.URL;
+
+public final class Provider extends WeakBase
+ implements XServiceInfo, XDispatchProvider
+{
+ public Provider(XComponentContext context) {
+ this.context = context;
+ }
+
+ public String getImplementationName() { return implementationName; }
+
+ public boolean supportsService(String ServiceName) {
+ return ServiceName.equals(getSupportedServiceNames()[0]); //TODO
+ }
+
+ public String[] getSupportedServiceNames() {
+ return serviceNames;
+ }
+
+ public XDispatch queryDispatch(
+ URL URL, String TargetFrameName, int SearchFlags)
+ {
+ return UnoRuntime.queryInterface(
+ XDispatch.class,
+ context.getValueByName(
+ "/singletons/" +
+ "com.sun.star.test.deployment.active_java_singleton"));
+ }
+
+ public XDispatch[] queryDispatches(DispatchDescriptor[] Requests) {
+ XDispatch[] s = new XDispatch[Requests.length];
+ for (int i = 0; i < s.length; ++i) {
+ s[i] = queryDispatch(
+ Requests[i].FeatureURL, Requests[i].FrameName,
+ Requests[i].SearchFlags);
+ }
+ return s;
+ }
+
+ private final XComponentContext context;
+
+ static final String implementationName =
+ "com.sun.star.comp.test.deployment.active_java";
+
+ static final String[] serviceNames = new String[] {
+ "com.sun.star.test.deployment.active_java" };
+}
diff --git a/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Services.java b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Services.java
new file mode 100644
index 000000000..1af3f42b7
--- /dev/null
+++ b/desktop/test/deployment/active/com/sun/star/comp/test/deployment/Services.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.active_java;
+
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lib.uno.helper.Factory;
+import com.sun.star.registry.InvalidRegistryException;
+import com.sun.star.registry.XRegistryKey;
+
+public final class Services {
+ private Services() {}
+
+ public static XSingleComponentFactory __getComponentFactory(
+ String implementation)
+ {
+ if (implementation.equals(Dispatch.implementationName)) {
+ return Factory.createComponentFactory(
+ Dispatch.class, Dispatch.implementationName,
+ Dispatch.serviceNames);
+ } else if (implementation.equals(Provider.implementationName)) {
+ return Factory.createComponentFactory(
+ Provider.class, Provider.implementationName,
+ Provider.serviceNames);
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean __writeRegistryServiceInfo(XRegistryKey key) {
+ if (!(Factory.writeRegistryServiceInfo(
+ Dispatch.implementationName, Dispatch.serviceNames, key) &&
+ Factory.writeRegistryServiceInfo(
+ Provider.implementationName, Provider.serviceNames, key)))
+ {
+ return false;
+ }
+ try {
+ key.
+ createKey(
+ "/" + Dispatch.implementationName +
+ "/UNO/SINGLETONS/" +
+ "com.sun.star.test.deployment.active_java_singleton").
+ setStringValue(Dispatch.implementationName);
+ } catch (InvalidRegistryException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/desktop/test/deployment/active/description.xml b/desktop/test/deployment/active/description.xml
new file mode 100644
index 000000000..685d72132
--- /dev/null
+++ b/desktop/test/deployment/active/description.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<d:description xmlns:d="http://openoffice.org/extensions/description/2006">
+ <d:identifier
+ value="org.openoffice/framework/desktop/test/deployment/active"/>
+ <d:version value="1"/>
+ <d:dependencies>
+ <d:OpenOffice.org-minimal-version d:name="OpenOffice.org 3.4" value="3.4"/>
+ </d:dependencies>
+</d:description>
diff --git a/desktop/test/deployment/dependencies/broken-dependency.oxt b/desktop/test/deployment/dependencies/broken-dependency.oxt
new file mode 100644
index 000000000..11bab0a95
--- /dev/null
+++ b/desktop/test/deployment/dependencies/broken-dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/double-dependencies.oxt b/desktop/test/deployment/dependencies/double-dependencies.oxt
new file mode 100644
index 000000000..055c27ea5
--- /dev/null
+++ b/desktop/test/deployment/dependencies/double-dependencies.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/empty-dependencies.oxt b/desktop/test/deployment/dependencies/empty-dependencies.oxt
new file mode 100644
index 000000000..ebb18dcbf
--- /dev/null
+++ b/desktop/test/deployment/dependencies/empty-dependencies.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/funny-dependency.oxt b/desktop/test/deployment/dependencies/funny-dependency.oxt
new file mode 100644
index 000000000..9b683e6d1
--- /dev/null
+++ b/desktop/test/deployment/dependencies/funny-dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/license-dependency.oxt b/desktop/test/deployment/dependencies/license-dependency.oxt
new file mode 100644
index 000000000..b01da4b5c
--- /dev/null
+++ b/desktop/test/deployment/dependencies/license-dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/loversion35.oxt b/desktop/test/deployment/dependencies/loversion35.oxt
new file mode 100644
index 000000000..ecd509cd9
--- /dev/null
+++ b/desktop/test/deployment/dependencies/loversion35.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/loversion36.oxt b/desktop/test/deployment/dependencies/loversion36.oxt
new file mode 100644
index 000000000..f38630e0c
--- /dev/null
+++ b/desktop/test/deployment/dependencies/loversion36.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/many-dependencies.oxt b/desktop/test/deployment/dependencies/many-dependencies.oxt
new file mode 100644
index 000000000..367568143
--- /dev/null
+++ b/desktop/test/deployment/dependencies/many-dependencies.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/maxversion33.oxt b/desktop/test/deployment/dependencies/maxversion33.oxt
new file mode 100644
index 000000000..fe0998c81
--- /dev/null
+++ b/desktop/test/deployment/dependencies/maxversion33.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/maxversion34.oxt b/desktop/test/deployment/dependencies/maxversion34.oxt
new file mode 100644
index 000000000..0a284b388
--- /dev/null
+++ b/desktop/test/deployment/dependencies/maxversion34.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/maxversion35.oxt b/desktop/test/deployment/dependencies/maxversion35.oxt
new file mode 100644
index 000000000..e95b97cd6
--- /dev/null
+++ b/desktop/test/deployment/dependencies/maxversion35.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/maxversion36.oxt b/desktop/test/deployment/dependencies/maxversion36.oxt
new file mode 100644
index 000000000..786ed4ce1
--- /dev/null
+++ b/desktop/test/deployment/dependencies/maxversion36.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/minattr22.oxt b/desktop/test/deployment/dependencies/minattr22.oxt
new file mode 100644
index 000000000..a6c8e3758
--- /dev/null
+++ b/desktop/test/deployment/dependencies/minattr22.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/minattr23.oxt b/desktop/test/deployment/dependencies/minattr23.oxt
new file mode 100644
index 000000000..83d17938c
--- /dev/null
+++ b/desktop/test/deployment/dependencies/minattr23.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/minattr24.oxt b/desktop/test/deployment/dependencies/minattr24.oxt
new file mode 100644
index 000000000..00f053f48
--- /dev/null
+++ b/desktop/test/deployment/dependencies/minattr24.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/no-dependencies.oxt b/desktop/test/deployment/dependencies/no-dependencies.oxt
new file mode 100644
index 000000000..6487eb66a
--- /dev/null
+++ b/desktop/test/deployment/dependencies/no-dependencies.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/no-description.oxt b/desktop/test/deployment/dependencies/no-description.oxt
new file mode 100644
index 000000000..1e6579cd7
--- /dev/null
+++ b/desktop/test/deployment/dependencies/no-description.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/readme.txt b/desktop/test/deployment/dependencies/readme.txt
new file mode 100644
index 000000000..8ba90ce4a
--- /dev/null
+++ b/desktop/test/deployment/dependencies/readme.txt
@@ -0,0 +1,73 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+no-description.oxt, no-dependencies.oxt, empty-dependencies.oxt effectively have
+no dependencies and should thus install successfully.
+
+broken-dependencies.oxt contains a malformed description.xml and should thus
+display an error and not install.
+
+double-dependencies.oxt contains a description.xml with two dependencies
+elements. This is not allowed by the spec but behaviour is unspecified. In the
+current implementation, it combines the two elements, and thus finds two
+unsatisfied dependencies, displays the Unsatisfied Dependencies dialog and does
+not install.
+
+version21.oxt contains a dependency on OOo 2.1 (and should thus only install in
+OOo 2.1 or later); version21ns.oxt is the same, but with a different way of
+using XML namespaces; version21other.oxt additionally contains an unsatisfied
+dependency (and should thus not install in any OOo version). version22.oxt
+contains a dependency on OOo 2.2 (and should thus only install in OOo 2.2 or
+later). version23.oxt contains a dependency on OOo 2.3 (and should thus only
+install in OOo 2.3 or later). version10000.oxt contains a dependency on the
+hypothetical OOo version 10000 (and should thus not install in any OOo version).
+versionempty.oxt contains an empty value attribute and versionnone.oxt lacks the
+value attribute; neither is allowed by the spec, but the current implementation
+treats both as pre OOo 2.1 versions (and the extensions should thus install in
+OOo 2.1 or later).
+
+maxversion30.oxt contains a maximal version dependency on OOo 3.0 (and should
+thus only install in OOo 3.0 or earlier, back to OOo 2.3, thanks to the
+additionally specified OpenOffice.org-minimal-version attribute).
+maxversion10000.oxt contains a maximal version dependency on the hypothetical
+OOo version 10000 (and should thus install in any OOo version 3.1 or later;
+OpenOffice.org-maximal-version was introduced in OOo 3.1, and no OpenOffice.org-
+minimal-version attribute is specified). bad-minmaxversion.oxt contains a
+minimal version dependency on OOo 3.2 and a maximal version dependency on
+OOo 3.1 (and should thus not install in any OOo version).
+
+minattr22.oxt contains a (hypothetical, most probably never satisfied)
+UNSATISFIED dependency with an OpenOffice.org-minimal-version attribute of
+"2.2" (and should thus install in OOo 2.3 or later); minattr23.oxt is similar,
+but with an OpenOffice.org-minimal-version attribute of "2.3" (and should thus
+also install in OOo 2.3 or later); minattr24.oxt is similar, but with an
+OpenOffice.org-minimal-version attribute of "2.4" (and should thus only install
+in OOo 2.4 or later).
+
+All of the following testcases should result in the Unsatisfied Dependencies
+dialog being displayed and the extension not being installed:
+
+unknown-dependency.oxt contains a dependency without a name attribute, and
+should thus display "Unknown" (localized).
+
+funny-dependency.oxt, many-dependencies.oxt contain somewhat extreme input.
+
+license-dependency.oxt contains both a license to be accepted by the user and
+dependencies. What is important here is that the Unsatisfied Dependencies
+dialog is displayed, but not the license (as installation aborts as soon as
+unsatisfied dependencies are found).
diff --git a/desktop/test/deployment/dependencies/unknown-dependency.oxt b/desktop/test/deployment/dependencies/unknown-dependency.oxt
new file mode 100644
index 000000000..7c2a22c6d
--- /dev/null
+++ b/desktop/test/deployment/dependencies/unknown-dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version10000.oxt b/desktop/test/deployment/dependencies/version10000.oxt
new file mode 100644
index 000000000..c15b7a117
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version10000.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version21.oxt b/desktop/test/deployment/dependencies/version21.oxt
new file mode 100644
index 000000000..922b27955
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version21.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version21ns.oxt b/desktop/test/deployment/dependencies/version21ns.oxt
new file mode 100644
index 000000000..5efb2ed90
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version21ns.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version21other.oxt b/desktop/test/deployment/dependencies/version21other.oxt
new file mode 100644
index 000000000..d88a8155a
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version21other.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version22.oxt b/desktop/test/deployment/dependencies/version22.oxt
new file mode 100644
index 000000000..4c8a207b6
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version22.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version23.oxt b/desktop/test/deployment/dependencies/version23.oxt
new file mode 100644
index 000000000..6c08d2949
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version23.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version34.oxt b/desktop/test/deployment/dependencies/version34.oxt
new file mode 100644
index 000000000..ee2a82d93
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version34.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/version35.oxt b/desktop/test/deployment/dependencies/version35.oxt
new file mode 100644
index 000000000..6e99cf1b2
--- /dev/null
+++ b/desktop/test/deployment/dependencies/version35.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/versionempty.oxt b/desktop/test/deployment/dependencies/versionempty.oxt
new file mode 100644
index 000000000..a06bb0129
--- /dev/null
+++ b/desktop/test/deployment/dependencies/versionempty.oxt
Binary files differ
diff --git a/desktop/test/deployment/dependencies/versionnone.oxt b/desktop/test/deployment/dependencies/versionnone.oxt
new file mode 100644
index 000000000..ace2a1165
--- /dev/null
+++ b/desktop/test/deployment/dependencies/versionnone.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/desc1.oxt b/desktop/test/deployment/description/desc1.oxt
new file mode 100644
index 000000000..e447fd6ea
--- /dev/null
+++ b/desktop/test/deployment/description/desc1.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/desc2.oxt b/desktop/test/deployment/description/desc2.oxt
new file mode 100644
index 000000000..8df2f33fa
--- /dev/null
+++ b/desktop/test/deployment/description/desc2.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/desc3.oxt b/desktop/test/deployment/description/desc3.oxt
new file mode 100644
index 000000000..fbd1136b0
--- /dev/null
+++ b/desktop/test/deployment/description/desc3.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/desc4.oxt b/desktop/test/deployment/description/desc4.oxt
new file mode 100644
index 000000000..0c97f5fd4
--- /dev/null
+++ b/desktop/test/deployment/description/desc4.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/desc5.oxt b/desktop/test/deployment/description/desc5.oxt
new file mode 100644
index 000000000..811007349
--- /dev/null
+++ b/desktop/test/deployment/description/desc5.oxt
Binary files differ
diff --git a/desktop/test/deployment/description/readme.txt b/desktop/test/deployment/description/readme.txt
new file mode 100644
index 000000000..bb133ba51
--- /dev/null
+++ b/desktop/test/deployment/description/readme.txt
@@ -0,0 +1,23 @@
+The folder contains extensions which use in the description.xml the following:
+-The <extension-description> element The element contains localized child
+elements.
+
+The following table shows what localized item is used, when the Office the locale
+en-US uses. The displayed extension description contains the locale.
+
+
+Localization:
+
+Installed office: en-US
+ | locale
+=========================
+desc1.oxt | en-US
+-------------------------
+desc2.oxt | en-US-region1
+--------------------------
+desc3.oxt | en
+--------------------------
+desc4.oxt | en-GB
+--------------------------
+desc5.oxt | de
+
diff --git a/desktop/test/deployment/display_name/name1.oxt b/desktop/test/deployment/display_name/name1.oxt
new file mode 100644
index 000000000..5a53690d6
--- /dev/null
+++ b/desktop/test/deployment/display_name/name1.oxt
Binary files differ
diff --git a/desktop/test/deployment/display_name/name2.oxt b/desktop/test/deployment/display_name/name2.oxt
new file mode 100644
index 000000000..f6cbcae3b
--- /dev/null
+++ b/desktop/test/deployment/display_name/name2.oxt
Binary files differ
diff --git a/desktop/test/deployment/display_name/name3.oxt b/desktop/test/deployment/display_name/name3.oxt
new file mode 100644
index 000000000..8df750ce6
--- /dev/null
+++ b/desktop/test/deployment/display_name/name3.oxt
Binary files differ
diff --git a/desktop/test/deployment/display_name/name4.oxt b/desktop/test/deployment/display_name/name4.oxt
new file mode 100644
index 000000000..6ce4822e3
--- /dev/null
+++ b/desktop/test/deployment/display_name/name4.oxt
Binary files differ
diff --git a/desktop/test/deployment/display_name/name5.oxt b/desktop/test/deployment/display_name/name5.oxt
new file mode 100644
index 000000000..56973be78
--- /dev/null
+++ b/desktop/test/deployment/display_name/name5.oxt
Binary files differ
diff --git a/desktop/test/deployment/display_name/readme.txt b/desktop/test/deployment/display_name/readme.txt
new file mode 100644
index 000000000..23173bde6
--- /dev/null
+++ b/desktop/test/deployment/display_name/readme.txt
@@ -0,0 +1,26 @@
+The folder contains extensions which use in the description.xml the following:
+-The <display-name> element
+The element contains localized child elements.
+
+To test the display name in the update dialog use the extensions in
+desktop/test/deployment/update/simple
+
+
+The following table shows what localized item is used, when the Office the locale
+en-US uses.
+
+
+Localization:
+
+Installed office: en-US
+ | publisher | release notes
+=============================================
+name1.oxt | en-US | en-US
+---------------------------------------------
+name2.oxt | en-US-region1 | en-US-region1
+---------------------------------------------
+name3.oxt | en | en
+---------------------------------------------
+name4.oxt | en-GB | en-GB
+---------------------------------------------
+name5.oxt | de | de
diff --git a/desktop/test/deployment/executable_content/build/hello.c b/desktop/test/deployment/executable_content/build/hello.c
new file mode 100644
index 000000000..b88f0e1c0
--- /dev/null
+++ b/desktop/test/deployment/executable_content/build/hello.c
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <stdio.h>
+
+int main(int argc , char** argv, char** envp)
+{
+ //prevent warning about unused parameters
+ //we need to provide parameter names in C
+ (void)argc;
+ (void)argv;
+ (void)envp;
+
+ fprintf(stdout,"Hello world!\n");
+ return 0;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/executable_content/build/makefile.mk b/desktop/test/deployment/executable_content/build/makefile.mk
new file mode 100644
index 000000000..513d640cf
--- /dev/null
+++ b/desktop/test/deployment/executable_content/build/makefile.mk
@@ -0,0 +1,42 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ = ..$/..$/..$/..
+
+PRJNAME = desktop
+TARGET = hello
+LIBTARGET=NO
+NO_DEFAULT_STL=TRUE
+LIBSALCPPRT=
+
+.INCLUDE : settings.mk
+
+
+APP1NOSAL = TRUE
+APP1OBJS = \
+ $(OBJ)$/hello.obj
+
+
+APP1TARGET = $(TARGET)
+
+DEPOBJFILES = \
+ $(OBJ)$/hello.obj
+
+
+.INCLUDE : target.mk
+
diff --git a/desktop/test/deployment/executable_content/build/readme.txt b/desktop/test/deployment/executable_content/build/readme.txt
new file mode 100644
index 000000000..4f956e573
--- /dev/null
+++ b/desktop/test/deployment/executable_content/build/readme.txt
@@ -0,0 +1,2 @@
+This folder contains the sources to build the hello executable which is contained
+in the hello.oxt.
diff --git a/desktop/test/deployment/executable_content/hello.oxt b/desktop/test/deployment/executable_content/hello.oxt
new file mode 100644
index 000000000..97d6d14a3
--- /dev/null
+++ b/desktop/test/deployment/executable_content/hello.oxt
Binary files differ
diff --git a/desktop/test/deployment/executable_content/readme.txt b/desktop/test/deployment/executable_content/readme.txt
new file mode 100644
index 000000000..2c336de72
--- /dev/null
+++ b/desktop/test/deployment/executable_content/readme.txt
@@ -0,0 +1,12 @@
+When the executable is installed try to execute the executable "hello". The executable
+file attribute (not on Windows) should be set.
+
+CD into the extension directory in /user|share)/uno_packages/cache/uno_packages/xyz_
+Then there are the directories for different platforms:
+
+windows,
+solaris,
+linux
+
+Each directory contains a hello executable. On linux one should execute it in a
+shell with a build environment, so that the C++ runtime is found.
diff --git a/desktop/test/deployment/identifier/explicit/identifier.oxt b/desktop/test/deployment/identifier/explicit/identifier.oxt
new file mode 100644
index 000000000..3851e291c
--- /dev/null
+++ b/desktop/test/deployment/identifier/explicit/identifier.oxt
Binary files differ
diff --git a/desktop/test/deployment/identifier/legacy/identifier.oxt b/desktop/test/deployment/identifier/legacy/identifier.oxt
new file mode 100644
index 000000000..df8bb8449
--- /dev/null
+++ b/desktop/test/deployment/identifier/legacy/identifier.oxt
Binary files differ
diff --git a/desktop/test/deployment/identifier/readme.txt b/desktop/test/deployment/identifier/readme.txt
new file mode 100644
index 000000000..c268ca4af
--- /dev/null
+++ b/desktop/test/deployment/identifier/readme.txt
@@ -0,0 +1,24 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+legacy/identifier.oxt and explicit/identifier.oxt are two different extensions
+that happen to have the same file name. legacy/identifier.oxt does not have an
+explicit extension identifier, so it gets the implicit one "org.openoffice.
+legacy.identifier.oxt". explicit/identifier.oxt has the
+explicit extension identifier "org.openoffice/framework/desktop/test/deployment/
+identifier/explicit/identifier.oxt".
diff --git a/desktop/test/deployment/locationtest/LocationTest.idl b/desktop/test/deployment/locationtest/LocationTest.idl
new file mode 100644
index 000000000..9e7881ce1
--- /dev/null
+++ b/desktop/test/deployment/locationtest/LocationTest.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef _com_sun_star_comp_smoketest_LocationTest_idl_
+#define _com_sun_star_comp_smoketest_LocationTest_idl_
+
+#include <com/sun/star/lang/XServiceInfo.idl>
+
+
+module com { module sun { module star { module comp { module smoketest {
+ // example service, XServiceInfo is implemented here for demonstration
+ // issues. XServiceInfo must be implemented by all components.
+ service TestExtension: ::com::sun::star::lang::XServiceInfo;
+};};};};};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/locationtest/LocationTest.java b/desktop/test/deployment/locationtest/LocationTest.java
new file mode 100644
index 000000000..ca70b5a1e
--- /dev/null
+++ b/desktop/test/deployment/locationtest/LocationTest.java
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.comp.smoketest;
+
+import com.sun.star.lib.uno.helper.Factory;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.registry.XRegistryKey;
+import com.sun.star.lang.XInitialization;
+import com.sun.star.lang.XTypeProvider;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.uno.Type;
+
+/** This class capsulates the class, that implements the minimal component, a
+ * factory for creating the service (<CODE>__getComponentFactory</CODE>) and a
+ * method, that writes the information into the given registry key
+ * (<CODE>__writeRegistryServiceInfo</CODE>).
+ */
+public class LocationTest {
+ /** This class implements the component. At least the interfaces XServiceInfo,
+ * XTypeProvider, and XInitialization should be provided by the service.
+ */
+ public static class _LocationTest extends WeakBase
+ implements XServiceInfo {
+ /** The service name, that must be used to get an instance of this service.
+ */
+ private static final String __serviceName =
+ "com.sun.star.comp.smoketest.LocationTest";
+
+ /** The initial component contextr, that gives access to
+ * the service manager, supported singletons, ...
+ * It's often later used
+ */
+ private XComponentContext m_cmpCtx;
+
+ /** The service manager, that gives access to all registered services.
+ * It's often later used
+ */
+ private XMultiComponentFactory m_xMCF;
+
+ /** The constructor of the inner class has a XMultiServiceFactory parameter.
+ * @param xmultiservicefactoryInitialization A special service factory
+ * could be introduced while initializing.
+ */
+ public _LocationTest(XComponentContext xCompContext) {
+ try {
+ m_cmpCtx = xCompContext;
+ m_xMCF = m_cmpCtx.getServiceManager();
+ }
+ catch( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+
+ /** This method returns an array of all supported service names.
+ * @return Array of supported service names.
+ */
+ public String[] getSupportedServiceNames() {
+ return getServiceNames();
+ }
+
+ /** This method is a simple helper function to used in the
+ * static component initialisation functions as well as in
+ * getSupportedServiceNames.
+ */
+ public static String[] getServiceNames() {
+ String[] sSupportedServiceNames = { __serviceName };
+ return sSupportedServiceNames;
+ }
+
+ /** This method returns true, if the given service will be
+ * supported by the component.
+ * @param sServiceName Service name.
+ * @return True, if the given service name will be supported.
+ */
+ public boolean supportsService( String sServiceName ) {
+ return sServiceName.equals( __serviceName );
+ }
+
+ /** Return the class name of the component.
+ * @return Class name of the component.
+ */
+ public String getImplementationName() {
+ return _LocationTest.class.getName();
+ }
+ }
+
+
+ /**
+ * Gives a factory for creating the service.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns a <code>XSingleComponentFactory</code> for creating
+ * the component
+ * @param sImplName the name of the implementation for which a
+ * service is desired
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleComponentFactory __getComponentFactory(String sImplName)
+ {
+ XSingleComponentFactory xFactory = null;
+
+ if ( sImplName.equals( _LocationTest.class.getName() ) )
+ xFactory = Factory.createComponentFactory(_LocationTest.class,
+ _LocationTest.getServiceNames());
+
+ return xFactory;
+ }
+
+ /**
+ * Writes the service information into the given registry key.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns true if the operation succeeded
+ * @param regKey the registryKey
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) {
+ return Factory.writeRegistryServiceInfo(_LocationTest.class.getName(),
+ _LocationTest.getServiceNames(),
+ regKey);
+ }
+
+ /** This method is a member of the interface for initializing an object
+ * directly after its creation.
+ * @param object This array of arbitrary objects will be passed to the
+ * component after its creation.
+ * @throws Exception Every exception will not be handled, but will be
+ * passed to the caller.
+ */
+ public void initialize( Object[] object )
+ throws com.sun.star.uno.Exception {
+ /* The component describes what arguments are expected and in which
+ * order! At this point you can read the objects and initialize
+ * your component using these objects.
+ */
+ }
+}
diff --git a/desktop/test/deployment/locationtest/LocationTest.odt b/desktop/test/deployment/locationtest/LocationTest.odt
new file mode 100644
index 000000000..8e1aa7007
--- /dev/null
+++ b/desktop/test/deployment/locationtest/LocationTest.odt
Binary files differ
diff --git a/desktop/test/deployment/locationtest/MANIFEST.MF b/desktop/test/deployment/locationtest/MANIFEST.MF
new file mode 100644
index 000000000..a2fa8c34b
--- /dev/null
+++ b/desktop/test/deployment/locationtest/MANIFEST.MF
@@ -0,0 +1,2 @@
+RegistrationClassName: com.sun.star.comp.smoketest.LocationTest
+
diff --git a/desktop/test/deployment/locationtest/description.xml b/desktop/test/deployment/locationtest/description.xml
new file mode 100644
index 000000000..0d2b71294
--- /dev/null
+++ b/desktop/test/deployment/locationtest/description.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:d="http://openoffice.org/extensions/description/2006" >
+ <identifier value="org.openoffice.extensions.testarea.desktop.location"/>
+ <version value="1.0" />
+ <dependencies >
+ <OpenOffice.org-minimal-version value="2.2" d:name="OpenOffice.org 2.2"/>
+ </dependencies>
+ <update-information>
+ <src xlink:href="http://update.services.openoffice.org/ProductUpdateService/check.Update?product=extension&amp;extensionid=org.openoffice.extensions.testarea.desktop.updateinfo&amp;refresh=true"/>
+ </update-information>
+</description>
diff --git a/desktop/test/deployment/locationtest/makefile.mk b/desktop/test/deployment/locationtest/makefile.mk
new file mode 100644
index 000000000..de586915e
--- /dev/null
+++ b/desktop/test/deployment/locationtest/makefile.mk
@@ -0,0 +1,76 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ = ..$/..$/..
+PRJNAME = location_test
+PACKAGE = com$/sun$/star$/comp$/smoketest
+TARGET = com_sun_star_comp_smoketest
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+JARFILES = ridl.jar jurt.jar unoil.jar juh.jar
+
+JARTARGET = LocationTest.jar
+JARCOMPRESS = TRUE
+CUSTOMMANIFESTFILE = MANIFEST.MF
+
+ZIP1TARGET=locationtest
+ZIP1LIST=*
+ZIPFLAGS=-r
+ZIP1DIR=$(MISC)$/$(TARGET)
+ZIP1EXT=.oxt
+
+# --- Files --------------------------------------------------------
+
+COPY_OXT_MANIFEST:= $(MISC)$/$(TARGET)$/META-INF$/manifest.xml
+JAVAFILES = LocationTest.java
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+
+$(JARTARGETN) : $(MISC)$/$(TARGET).javamaker.done
+
+$(JAVACLASSFILES) : $(MISC)$/$(TARGET).javamaker.done
+
+$(MISC)$/$(TARGET).javamaker.done: $(BIN)$/LocationTest.rdb
+ $(JAVAMAKER) -O$(CLASSDIR) -BUCR -nD -X$(SOLARBINDIR)/types.rdb $<
+ $(TOUCH) $@
+
+$(BIN)$/LocationTest.rdb: LocationTest.idl
+ $(IDLC) -O$(MISC) -I$(SOLARIDLDIR) -cid -we $<
+ +-$(RM) $@
+ $(REGMERGE) $@ /UCR $(MISC)$/LocationTest.urd
+
+$(MISC)$/$(ZIP1TARGET).createdir :
+ +$(MKDIRHIER) $(MISC)$/$(TARGET)$/META-INF >& $(NULLDEV) && $(TOUCH) $@
+
+$(MISC)$/$(TARGET)_resort : manifest.xml $(JARTARGETN) $(MISC)$/$(ZIP1TARGET).createdir $(BIN)$/LocationTest.rdb description.xml
+ $(COPY) manifest.xml $(MISC)$/$(TARGET)$/META-INF$/manifest.xml
+ $(COPY) $(JARTARGETN) $(MISC)$/$(TARGET)$/$(JARTARGET)
+ $(COPY) $(BIN)$/LocationTest.rdb $(MISC)$/$(TARGET)$/LocationTest.rdb
+ $(COPY) description.xml $(MISC)$/$(TARGET)$/description.xml
+ $(TOUCH) $@
+
+.IF "$(ZIP1TARGETN)"!=""
+$(ZIP1TARGETN) : $(MISC)$/$(TARGET)_resort $(MISC)$/$(ZIP1TARGET).createdir
+
+.ENDIF # "$(ZIP1TARGETN)"!=""
+
diff --git a/desktop/test/deployment/locationtest/manifest.xml b/desktop/test/deployment/locationtest/manifest.xml
new file mode 100644
index 000000000..3dd6460fa
--- /dev/null
+++ b/desktop/test/deployment/locationtest/manifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
+ <manifest:file-entry manifest:full-path="LocationTest.jar" manifest:media-type="application/vnd.sun.star.uno-component;type=Java"/>
+ <manifest:file-entry manifest:full-path="LocationTest.rdb" manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"/>
+</manifest:manifest>
diff --git a/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/MANIFEST.MF b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/MANIFEST.MF
new file mode 100644
index 000000000..fba55a6e0
--- /dev/null
+++ b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/MANIFEST.MF
@@ -0,0 +1,2 @@
+RegistrationClassName: com.sun.star.comp.extensionoptions.OptionsEventHandler
+
diff --git a/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/OptionsEventHandler.java b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/OptionsEventHandler.java
new file mode 100644
index 000000000..b360f470b
--- /dev/null
+++ b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/OptionsEventHandler.java
@@ -0,0 +1,417 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.comp.extensionoptions;
+
+import com.sun.star.configuration.theDefaultProvider;
+import com.sun.star.lib.uno.helper.Factory;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.AnyConverter;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.uno.Exception;
+import com.sun.star.registry.XRegistryKey;
+import com.sun.star.awt.XContainerWindowEventHandler;
+import com.sun.star.awt.XControl;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.awt.XControlContainer;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.beans.NamedValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.util.XChangesBatch;
+
+/** A handler which supports multiple options pages which all
+ * have the same controls.
+ */
+public class OptionsEventHandler {
+
+ public static class _OptionsEventHandler extends WeakBase
+ implements XServiceInfo, XContainerWindowEventHandler {
+
+ private static final String __serviceName =
+ "com.sun.star.comp.extensionoptions.OptionsEventHandler";
+
+ private final XComponentContext m_cmpCtx;
+
+ private XNameAccess m_xAccessLeaves;
+
+ /**Names of supported options pages.
+ */
+ private final String[] m_arWindowNames = {
+ "Writer1", "Writer2", "Writer3", "Calc1", "Calc2", "Calc3",
+ "Draw1", "Draw2", "Draw3", "Node1_1", "Node1_2", "Node1_3",
+ "Node2_1", "Node2_2", "Node2_3", "Node3_1", "Node3_2", "Node3_3"};
+
+ /**Names of the controls which are supported by this handler. All these
+ *controls must have a "Text" property.
+ */
+ private final String[] m_arStringControls = {
+ "String0", "String1", "String2", "String3", "String4"};
+
+ public _OptionsEventHandler(XComponentContext xCompContext) {
+ m_cmpCtx = xCompContext;
+
+ //Create the com.sun.star.configuration.ConfigurationUpdateAccess
+ //for the registry node which contains the data for our option
+ //pages.
+ XMultiServiceFactory xConfig = theDefaultProvider.get(m_cmpCtx);
+
+ //One argument for creating the ConfigurationUpdateAccess is the "nodepath".
+ //Our nodepath point to the node of which the direct subnodes represent the
+ //different options pages.
+ Object[] args = new Object[1];
+ args[0] = new NamedValue(
+ "nodepath",
+ "/org.openoffice.desktop.deployment.options.ExtensionData/Leaves");
+
+ //We get the com.sun.star.container.XNameAccess from the instance of
+ //ConfigurationUpdateAccess and save it for later use.
+ try {
+ m_xAccessLeaves = UnoRuntime.queryInterface(
+ XNameAccess.class, xConfig.createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess", args));
+
+ } catch (com.sun.star.uno.Exception e) {
+ e.printStackTrace();
+ return;
+ }
+ }
+
+ /** This method returns an array of all supported service names.
+ * @return Array of supported service names.
+ */
+ public String[] getSupportedServiceNames() {
+ return getServiceNames();
+ }
+
+ /** This method is a simple helper function to used in the
+ * static component initialisation functions as well as in
+ * getSupportedServiceNames.
+ */
+ private static String[] getServiceNames() {
+ String[] sSupportedServiceNames = { __serviceName };
+ return sSupportedServiceNames;
+ }
+
+ /** This method returns true, if the given service will be
+ * supported by the component.
+ * @param sServiceName Service name.
+ * @return True, if the given service name will be supported.
+ */
+ public boolean supportsService( String sServiceName ) {
+ return sServiceName.equals( __serviceName );
+ }
+
+ /** Return the class name of the component.
+ * @return Class name of the component.
+ */
+ public String getImplementationName() {
+ return _OptionsEventHandler.class.getName();
+ }
+
+ //XContainerWindowEventHandler
+ public boolean callHandlerMethod(com.sun.star.awt.XWindow aWindow,
+ Object aEventObject, String sMethod)
+ throws WrappedTargetException {
+ if (sMethod.equals("external_event") ){
+ try {
+ return handleExternalEvent(aWindow, aEventObject);
+ } catch (com.sun.star.uno.Exception e) {
+ throw new WrappedTargetException(e, sMethod, this, e);
+ }
+ }
+
+ return true;
+ }
+
+ //XContainerWindowEventHandler
+ public String[] getSupportedMethodNames() {
+ return new String[] {"external_event"};
+ }
+
+ private boolean handleExternalEvent(com.sun.star.awt.XWindow aWindow, Object aEventObject)
+ throws com.sun.star.uno.Exception {
+ try {
+ String sMethod = AnyConverter.toString(aEventObject);
+ if (sMethod.equals("ok")) {
+ saveData(aWindow);
+ } else if (sMethod.equals("back") || sMethod.equals("initialize")) {
+ loadData(aWindow);
+ }
+ } catch (com.sun.star.lang.IllegalArgumentException ex) {
+ throw new com.sun.star.lang.IllegalArgumentException(ex,
+ "Method external_event requires a string in the event object argument.",
+ this, (short) -1);
+ }
+
+ return true;
+ }
+
+ private void saveData(com.sun.star.awt.XWindow aWindow)
+ throws com.sun.star.lang.IllegalArgumentException,
+ com.sun.star.uno.Exception {
+
+ //Determine the name of the options page. This serves two purposes. First, if this
+ //options page is supported by this handler and second we use the name two locate
+ //the corresponding data in the registry.
+ String sWindowName = getWindowName(aWindow);
+ if (sWindowName == null)
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "This window is not supported by this handler", this, (short) -1);
+
+ //To access the separate controls of the window we need to obtain the
+ //XControlContainer from the window implementation
+ XControlContainer xContainer = UnoRuntime.queryInterface(
+ XControlContainer.class, aWindow);
+ if (xContainer == null)
+ throw new com.sun.star.uno.Exception(
+ "Could not get XControlContainer from window.", this);
+
+ //This is an implementation which will be used for several options pages
+ //which all have the same controls. m_arStringControls is an array which
+ //contains the names.
+ for (int i = 0; i < m_arStringControls.length; i++) {
+
+ //To obtain the data from the controls we need to get their model.
+ //First get the respective control from the XControlContainer.
+ XControl xControl = xContainer.getControl(m_arStringControls[i]);
+
+ //This generic handler and the corresponding registry schema support
+ //up to five text controls. However, if an options page does not use all
+ //five controls then we will not complain here.
+ if (xControl == null)
+ continue;
+
+ //From the control we get the model, which in turn supports the
+ //XPropertySet interface, which we finally use to get the data from
+ //the control.
+ XPropertySet xProp = UnoRuntime.queryInterface(
+ XPropertySet.class, xControl.getModel());
+
+ if (xProp == null)
+ throw new com.sun.star.uno.Exception(
+ "Could not get XPropertySet from control.", this);
+ //Get the "Text" property.
+ Object aText = xProp.getPropertyValue("Text");
+ String sValue = null;
+
+ //The value is still contained in a com.sun.star.uno.Any - so convert it.
+ try {
+ sValue = AnyConverter.toString(aText);
+ } catch (com.sun.star.lang.IllegalArgumentException ex) {
+ throw new com.sun.star.lang.IllegalArgumentException(ex,
+ "Wrong property type.", this, (short) -1);
+ }
+
+ //Now we have the actual string value of the control. What we need now is
+ //the XPropertySet of the respective property in the registry, so that we
+ //can store the value.
+ //To access the registry we have previously created a service instance
+ //of com.sun.star.configuration.ConfigurationUpdateAccess which supports
+ //com.sun.star.container.XNameAccess. The XNameAccess is used to get the
+ //particular registry node which represents this options page.
+ //Fortunately the name of the window is the same as the registry node.
+ XPropertySet xLeaf = UnoRuntime.queryInterface(
+ XPropertySet.class, m_xAccessLeaves.getByName(sWindowName));
+ if (xLeaf == null)
+ throw new com.sun.star.uno.Exception(
+ "XPropertySet not supported.", this);
+
+ //Finally we can set the value
+ xLeaf.setPropertyValue(m_arStringControls[i], sValue);
+ }
+
+ //Committing the changes will cause or changes to be written to the registry.
+ XChangesBatch xUpdateCommit =
+ UnoRuntime.queryInterface(XChangesBatch.class, m_xAccessLeaves);
+ xUpdateCommit.commitChanges();
+ }
+
+ private void loadData(com.sun.star.awt.XWindow aWindow)
+ throws com.sun.star.uno.Exception {
+
+ //Determine the name of the window. This serves two purposes. First, if this
+ //window is supported by this handler and second we use the name two locate
+ //the corresponding data in the registry.
+ String sWindowName = getWindowName(aWindow);
+ if (sWindowName == null)
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "The window is not supported by this handler", this, (short) -1);
+
+ //To access the separate controls of the window we need to obtain the
+ //XControlContainer from window implementation
+ XControlContainer xContainer = UnoRuntime.queryInterface(
+ XControlContainer.class, aWindow);
+ if (xContainer == null)
+ throw new com.sun.star.uno.Exception(
+ "Could not get XControlContainer from window.", this);
+
+ //This is an implementation which will be used for several options pages
+ //which all have the same controls. m_arStringControls is an array which
+ //contains the names.
+ for (int i = 0; i < m_arStringControls.length; i++) {
+
+ //load the values from the registry
+ //To access the registry we have previously created a service instance
+ //of com.sun.star.configuration.ConfigurationUpdateAccess which supports
+ //com.sun.star.container.XNameAccess. We obtain now the section
+ //of the registry which is assigned to this options page.
+ XPropertySet xLeaf = UnoRuntime.queryInterface(
+ XPropertySet.class, m_xAccessLeaves.getByName(sWindowName));
+ if (xLeaf == null)
+ throw new com.sun.star.uno.Exception(
+ "XPropertySet not supported.", this);
+
+ //The properties in the registry have the same name as the respective
+ //controls. We use the names now to obtain the property values.
+ Object aValue = xLeaf.getPropertyValue(m_arStringControls[i]);
+
+ //Now that we have the value we need to set it at the corresponding
+ //control in the window. The XControlContainer, which we obtained earlier
+ //is the means to get hold of all the controls.
+ XControl xControl = xContainer.getControl(m_arStringControls[i]);
+
+ //This generic handler and the corresponding registry schema support
+ //up to five text controls. However, if an options page does not use all
+ //five controls then we will not complain here.
+ if (xControl == null)
+ continue;
+
+ //From the control we get the model, which in turn supports the
+ //XPropertySet interface, which we finally use to set the data at the
+ //control
+ XPropertySet xProp = UnoRuntime.queryInterface(
+ XPropertySet.class, xControl.getModel());
+
+ if (xProp == null)
+ throw new com.sun.star.uno.Exception(
+ "Could not get XPropertySet from control.", this);
+
+ //This handler supports only text controls, which are named "Pattern Field"
+ //in the dialog editor. We set the "Text" property.
+ xProp.setPropertyValue("Text", aValue);
+ }
+ }
+
+ //Checks if the name property of the window is one of the supported names and returns
+ //always a valid string or null
+ private String getWindowName(com.sun.star.awt.XWindow aWindow)
+ throws com.sun.star.uno.Exception {
+
+ if (aWindow == null)
+ throw new com.sun.star.lang.IllegalArgumentException(
+ "Method external_event requires that a window is passed as argument",
+ this, (short) -1);
+
+ //We need to get the control model of the window. Therefore the first step is
+ //to query for it.
+ XControl xControlDlg = UnoRuntime.queryInterface(
+ XControl.class, aWindow);
+
+ if (xControlDlg == null)
+ throw new com.sun.star.uno.Exception(
+ "Cannot obtain XControl from XWindow in method external_event.");
+ //Now get model
+ XControlModel xModelDlg = xControlDlg.getModel();
+
+ if (xModelDlg == null)
+ throw new com.sun.star.uno.Exception(
+ "Cannot obtain XControlModel from XWindow in method external_event.", this);
+ //The model itself does not provide any information except that its
+ //implementation supports XPropertySet which is used to access the data.
+ XPropertySet xPropDlg = UnoRuntime.queryInterface(
+ XPropertySet.class, xModelDlg);
+ if (xPropDlg == null)
+ throw new com.sun.star.uno.Exception(
+ "Cannot obtain XPropertySet from window in method external_event.", this);
+
+ //Get the "Name" property of the window
+ Object aWindowName = xPropDlg.getPropertyValue("Name");
+
+ //Get the string from the returned com.sun.star.uno.Any
+ String sName = null;
+ try {
+ sName = AnyConverter.toString(aWindowName);
+ } catch (com.sun.star.lang.IllegalArgumentException ex) {
+ throw new com.sun.star.uno.Exception(ex,
+ "Name - property of window is not a string.", this);
+ }
+
+ //Eventually we can check if we this handler can "handle" this options page.
+ //The class has a member m_arWindowNames which contains all names of windows
+ //for which it is intended
+ for (int i = 0; i < m_arWindowNames.length; i++) {
+ if (m_arWindowNames[i].equals(sName)) {
+ return sName;
+ }
+ }
+ return null;
+ }
+ }
+
+
+ /**
+ * Gives a factory for creating the service.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns a <code>XSingleComponentFactory</code> for creating
+ * the component
+ * @param sImplName the name of the implementation for which a
+ * service is desired
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleComponentFactory __getComponentFactory(String sImplName)
+ {
+ XSingleComponentFactory xFactory = null;
+
+ if ( sImplName.equals( _OptionsEventHandler.class.getName() ) )
+ xFactory = Factory.createComponentFactory(_OptionsEventHandler.class,
+ _OptionsEventHandler.getServiceNames());
+
+ return xFactory;
+ }
+
+ /**
+ * Writes the service information into the given registry key.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns true if the operation succeeded
+ * @param regKey the registryKey
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) {
+ return Factory.writeRegistryServiceInfo(_OptionsEventHandler.class.getName(),
+ _OptionsEventHandler.getServiceNames(),
+ regKey);
+ }
+
+ /** This method is a member of the interface for initializing an object
+ * directly after its creation.
+ * @param object This array of arbitrary objects will be passed to the
+ * component after its creation.
+ * @throws Exception Every exception will not be handled, but will be
+ * passed to the caller.
+ */
+ public void initialize( Object[] object )
+ throws com.sun.star.uno.Exception {
+ }
+
+}
diff --git a/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/makefile.mk b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/makefile.mk
new file mode 100644
index 000000000..4174e3bd7
--- /dev/null
+++ b/desktop/test/deployment/options/handler/com/sun/star/comp/extensionoptions/makefile.mk
@@ -0,0 +1,44 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ = ..$/..$/..$/..$/..$/..$/..$/..$/..
+PRJNAME = desktop
+PACKAGE = com$/sun$/star$/comp$/extensionoptions
+TARGET = options
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+JARFILES = ridl.jar jurt.jar unoil.jar juh.jar
+
+
+JARTARGET = extensionoptions.jar
+JARCOMPRESS = TRUE
+CUSTOMMANIFESTFILE = MANIFEST.MF
+JARCLASSDIRS=com
+
+
+# --- Files --------------------------------------------------------
+
+JAVAFILES = OptionsEventHandler.java
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+
diff --git a/desktop/test/deployment/options/leaf1.oxt b/desktop/test/deployment/options/leaf1.oxt
new file mode 100644
index 000000000..9c3ff8698
--- /dev/null
+++ b/desktop/test/deployment/options/leaf1.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/leaf1mod.oxt b/desktop/test/deployment/options/leaf1mod.oxt
new file mode 100644
index 000000000..d5d9fe689
--- /dev/null
+++ b/desktop/test/deployment/options/leaf1mod.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/leaf2.oxt b/desktop/test/deployment/options/leaf2.oxt
new file mode 100644
index 000000000..b95628900
--- /dev/null
+++ b/desktop/test/deployment/options/leaf2.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/leaves1.oxt b/desktop/test/deployment/options/leaves1.oxt
new file mode 100644
index 000000000..037389a01
--- /dev/null
+++ b/desktop/test/deployment/options/leaves1.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/leaves2.oxt b/desktop/test/deployment/options/leaves2.oxt
new file mode 100644
index 000000000..531b38566
--- /dev/null
+++ b/desktop/test/deployment/options/leaves2.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/leaves3.oxt b/desktop/test/deployment/options/leaves3.oxt
new file mode 100644
index 000000000..f5bb0f226
--- /dev/null
+++ b/desktop/test/deployment/options/leaves3.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/modules1.oxt b/desktop/test/deployment/options/modules1.oxt
new file mode 100644
index 000000000..bae652ffb
--- /dev/null
+++ b/desktop/test/deployment/options/modules1.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/modules2.oxt b/desktop/test/deployment/options/modules2.oxt
new file mode 100644
index 000000000..d6d7956d4
--- /dev/null
+++ b/desktop/test/deployment/options/modules2.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/nodes1.oxt b/desktop/test/deployment/options/nodes1.oxt
new file mode 100644
index 000000000..b1dfa18d3
--- /dev/null
+++ b/desktop/test/deployment/options/nodes1.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/nodes2.oxt b/desktop/test/deployment/options/nodes2.oxt
new file mode 100644
index 000000000..a35cfaba9
--- /dev/null
+++ b/desktop/test/deployment/options/nodes2.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/nodes3.oxt b/desktop/test/deployment/options/nodes3.oxt
new file mode 100644
index 000000000..db0bc49da
--- /dev/null
+++ b/desktop/test/deployment/options/nodes3.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/nodes4.oxt b/desktop/test/deployment/options/nodes4.oxt
new file mode 100644
index 000000000..fe0550fdc
--- /dev/null
+++ b/desktop/test/deployment/options/nodes4.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/nodes5.oxt b/desktop/test/deployment/options/nodes5.oxt
new file mode 100644
index 000000000..893e9ee3e
--- /dev/null
+++ b/desktop/test/deployment/options/nodes5.oxt
Binary files differ
diff --git a/desktop/test/deployment/options/readme.txt b/desktop/test/deployment/options/readme.txt
new file mode 100644
index 000000000..58274ece7
--- /dev/null
+++ b/desktop/test/deployment/options/readme.txt
@@ -0,0 +1,200 @@
+Important: The handler component extensionoptions.jar in the extensions may not
+contain exactly the same sources as the one build in the handler directory. To
+make sure that debugging works build the handler directory and put the
+extensionoptions.jar into the extension.
+
+
+
+leaf1.oxt: Defines a leaf under the node WriterNode
+================================================================================
+
+leaf1mod.oxt: Defines a leaf under the node WriterNode
+
+It has a duplicate entry in the manifest.xml (OptionsDialog.xcu). This would cause a DisposedException when uninstalling on OOo 3.0 and prevent the extension from being uninstalled. This is actually a bug of the extensions. However, the error is difficult to investigate. Therefore this was fixed to make OOo more robust (i96690).
+================================================================================
+
+leaf2.oxt: Defines a leaf under a node that has a name which requires special
+"xml encoding". The name is "My Writer's & Settings". The node is not assigned
+to a Module and the Node/AllModules property is not true. This is a typical
+scenario when a Node had been added to an existing Module and later the Module
+was removed. This is a situation which actually should not occur. In this case
+DO NOT show the Node in the OOo's options dialog, because it shows only nodes
+for a particular module and in this case the Module for the Node is unknown.
+In the Extension Manager's
+options dialog this Node can be shown because the Module is irrelevant.
+See also nodes5.oxt.
+================================================================================
+
+leaves1.oxt: multiple ordered leaves under available nodes. The leaves Labels are
+localized for en-US and de. The following leaves should appear:
+
+Writer:
+-leaves1 Writer 1 en-US
+-leaves1 Writer 2 en-US
+-leaves1 Writer 3 en-US
+
+Calc:
+-leaves1 Calc 3 en-US
+-leaves1 Calc 3 en-US
+-leaves1 Calc 3 en-US
+
+Draw:
+-leaves1 Draw 3 en-US
+-leaves1 Draw 3 en-US
+-leaves1 Draw 3 en-US
+
+If a german office is used then the strings contain "de" instead of "en-US".
+================================================================================
+
+leaves2.oxt: Same as leaves1.oxt. Use together with leaves1.oxt to test the
+grouping of leaves.
+================================================================================
+
+leaves3.oxt: Same as leaves1.oxt, but the leaves are not ordered.
+================================================================================
+
+nodes1.oxt: Defines one node which has AllModules set and which has
+no children. Therefore this node should not be displayed.
+================================================================================
+
+nodes2.oxt: Defines 3 nodes which use AllModules and which form an
+ordered group. Every node defines also 3 nodes which have a determined order.
+
+-nodes2 node 1 en-US
+ -nodes2 node 1 leaf 1 en-US
+ -nodes2 node 1 leaf 2 en-US
+ -nodes2 node 1 leaf 3 en-US
+
+-nodes2 node 2 en-US
+ -nodes2 node 2 leaf 1 en-US
+ -nodes2 node 2 leaf 2 en-US
+ -nodes2 node 2 leaf 3 en-US
+
+-nodes2 node 3 en-US
+ -nodes2 node 3 leaf 1 en-US
+ -nodes2 node 3 leaf 2 en-US
+ -nodes2 node 3 leaf 3 en-US
+
+================================================================================
+
+nodes3.oxt: Defines 3 nodes which are placed under different existing Modules.
+The nodes and there leaves are ordered.
+
+Context Writer:
+- nodes3 node 1
+ nodes3 node 1 leaf 1 en-US
+ nodes3 node 1 leaf 2 en-US
+ nodes3 node 1 leaf 3 en-US
+
+- nodes3 node 2
+ nodes3 node 2 leaf 1 en-US
+ nodes3 node 2 leaf 2 en-US
+ nodes3 node 2 leaf 3 en-US
+
+- nodes3 node 3
+ nodes3 node 3 leaf 1 en-US
+ nodes3 node 3 leaf 2 en-US
+ nodes3 node 3 leaf 3 en-US
+
+Context Calc:
+- nodes3 node 1
+ nodes3 node 1 leaf 1 en-US
+ nodes3 node 1 leaf 2 en-US
+ nodes3 node 1 leaf 3 en-US
+
+- nodes3 node 3
+ nodes3 node 3 leaf 1 en-US
+ nodes3 node 3 leaf 2 en-US
+ nodes3 node 3 leaf 3 en-US
+
+Context Draw:
+- nodes3 node 2
+ nodes3 node 2 leaf 1 en-US
+ nodes3 node 2 leaf 2 en-US
+ nodes3 node 2 leaf 3 en-US
+
+================================================================================
+
+nodes4.oxt: Same as nodes3.oxt. Use together with nodes3.txt to test the
+grouping of nodes.
+================================================================================
+
+nodes5.oxt: Defines a node which in turn defines 3 leaves. The Node
+is not assigned to a Module and the AllModule property is false (which is the
+default).This may happen when a node
+had been added to an already existing Module and then this Module was removed. For
+example, an extension adds a node to the "Writer Module" and the
+next office update removes the "Writer Module" (which is rather inconceivable).
+Then the node and its leaves MUST NOT be displayed in OOo's options dialog,
+because the Module is not known. However, it can be displayed in the
+options dialog of the Extension Manager. See also the description for leaf2.oxt.
+================================================================================
+
+modules1.oxt: Defines two Modules and three Nodes. The Nodes may not
+be displayed in OOo's options dialog because there is currently no application
+which uses this Module. However the Nodes are displayed in the options dialog
+of the Extension Manager.
+There are three Nodes defined. The relationship is this:
+
+-module1
+ -node 1
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 2
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 3
+ -leaf 1
+ -leaf 2
+ -leaf 3
+
+-module2
+ -node1
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node3
+ -leaf 1
+ -leaf 2
+ -leaf 3
+
+The options dialog of the Extension Manager shall display only three nodes:
+
+ -node 1
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 2
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 3
+ -leaf 1
+ -leaf 2
+ -leaf 3
+
+or
+
+ -node 1
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 3
+ -leaf 1
+ -leaf 2
+ -leaf 3
+ -node 2
+ -leaf 1
+ -leaf 2
+ -leaf 3
+
+Since the order of Module|s is not defined, the dialog may display first the
+Nodes from module2 and then from module1. If a node is already displayed then
+it is not shown again.
+
+================================================================================
+
+modules2.oxt: Same as modules1, except that the order of nodes and leaves
+is not defined.
diff --git a/desktop/test/deployment/passive/Addons.xcu b/desktop/test/deployment/passive/Addons.xcu
new file mode 100644
index 000000000..3cc7c45ed
--- /dev/null
+++ b/desktop/test/deployment/passive/Addons.xcu
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<o:items xmlns:o="http://openoffice.org/2001/registry">
+ <item o:path="/org.openoffice.Office.Addons">
+ <node o:name="AddonUI">
+ <node o:name="OfficeMenuBar">
+ <node o:name="org.openoffice.test.desktop.deployment.passive"
+ o:op="replace">
+ <prop o:name="Title" xml:lang="en-US">
+ <value>passive</value>
+ </prop>
+ <node o:name="Submenu">
+ <node o:name="1" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_native:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>native</value>
+ </prop>
+ </node>
+ <node o:name="2" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_java:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>java</value>
+ </prop>
+ </node>
+ <node o:name="3" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_python:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>python</value>
+ </prop>
+ </node>
+ </node>
+ </node>
+ </node>
+ <node o:name="OfficeToolBar">
+ <node o:name="org.openoffice.test.desktop.deployment.passive" o:op="replace">
+ <node o:name="1" o:op="replace">
+ <prop o:name="URL">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_native:</value>
+ </prop>
+ <prop o:name="Title" xml:lang="en-US">
+ <value>native</value>
+ </prop>
+ <prop o:name="Context">
+ <value/>
+ </prop>
+ </node>
+ </node>
+ </node>
+ </node>
+ </item>
+</o:items>
diff --git a/desktop/test/deployment/passive/MANIFEST.MF b/desktop/test/deployment/passive/MANIFEST.MF
new file mode 100644
index 000000000..45a04bf26
--- /dev/null
+++ b/desktop/test/deployment/passive/MANIFEST.MF
@@ -0,0 +1,3 @@
+Sealed: true
+RegistrationClassName: com.sun.star.comp.test.deployment.passive_java.Services
+UNO-Type-Path:
diff --git a/desktop/test/deployment/passive/META-INF/manifest.xml b/desktop/test/deployment/passive/META-INF/manifest.xml
new file mode 100644
index 000000000..c387b4be4
--- /dev/null
+++ b/desktop/test/deployment/passive/META-INF/manifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<m:manifest xmlns:m="http://openoffice.org/2001/manifest">
+ <m:file-entry m:media-type="application/vnd.sun.star.configuration-data"
+ m:full-path="Addons.xcu"/>
+ <m:file-entry m:media-type="application/vnd.sun.star.configuration-data"
+ m:full-path="ProtocolHandler.xcu"/>
+ <m:file-entry m:media-type="application/vnd.sun.star.help" m:full-path="help"/>
+ <m:file-entry
+ m:media-type="application/vnd.sun.star.uno-components;platform=@PLATFORM@"
+ m:full-path="platform.components"/>
+ <m:file-entry
+ m:media-type="application/vnd.sun.star.uno-components"
+ m:full-path="generic.components"/>
+</m:manifest>
diff --git a/desktop/test/deployment/passive/ProtocolHandler.xcu b/desktop/test/deployment/passive/ProtocolHandler.xcu
new file mode 100644
index 000000000..f546fd119
--- /dev/null
+++ b/desktop/test/deployment/passive/ProtocolHandler.xcu
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<o:component-data xmlns:o="http://openoffice.org/2001/registry"
+ o:package="org.openoffice.Office" o:name="ProtocolHandler">
+ <node o:name="HandlerSet">
+ <node o:name="com.sun.star.test.deployment.passive_native" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_native:*</value>
+ </prop>
+ </node>
+ <node o:name="com.sun.star.test.deployment.passive_java" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_java:*</value>
+ </prop>
+ </node>
+ <node o:name="com.sun.star.test.deployment.passive_python" o:op="replace">
+ <prop o:name="Protocols">
+ <value>vnd.org.openoffice.test.desktop.deployment.passive_python:*</value>
+ </prop>
+ </node>
+ </node>
+</o:component-data>
diff --git a/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Dispatch.java b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Dispatch.java
new file mode 100644
index 000000000..74882bca5
--- /dev/null
+++ b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Dispatch.java
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.passive_java;
+
+import com.sun.star.awt.MessageBoxButtons;
+import com.sun.star.awt.MessageBoxType;
+import com.sun.star.awt.Rectangle;
+import com.sun.star.awt.XMessageBox;
+import com.sun.star.awt.XMessageBoxFactory;
+import com.sun.star.awt.XWindowPeer;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.frame.DispatchDescriptor;
+import com.sun.star.frame.XDesktop;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XStatusListener;
+import com.sun.star.lang.WrappedTargetRuntimeException;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.URL;
+
+public final class Dispatch extends WeakBase implements XServiceInfo, XDispatch
+{
+ public Dispatch(XComponentContext context) {
+ this.context = context;
+ }
+
+ public String getImplementationName() { return implementationName; }
+
+ public boolean supportsService(String ServiceName) {
+ return false; //TODO
+ }
+
+ public String[] getSupportedServiceNames() {
+ return serviceNames;
+ }
+
+ public void dispatch(URL URL, PropertyValue[] Arguments) {
+ try {
+ XMultiComponentFactory smgr = UnoRuntime.queryInterface(
+ XMultiComponentFactory.class, context.getServiceManager());
+ XMessageBox box = UnoRuntime.queryInterface(
+ XMessageBoxFactory.class,
+ smgr.createInstanceWithContext(
+ "com.sun.star.awt.Toolkit", context)).
+ createMessageBox(
+ UnoRuntime.queryInterface(
+ XWindowPeer.class,
+ (UnoRuntime.queryInterface(
+ XDesktop.class,
+ smgr.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", context)).
+ getCurrentFrame().getComponentWindow())),
+ MessageBoxType.INFOBOX, MessageBoxButtons.BUTTONS_OK,
+ "passive", "java");
+ box.execute();
+ UnoRuntime.queryInterface(XComponent.class, box).dispose();
+ } catch (com.sun.star.uno.RuntimeException e) {
+ throw e;
+ } catch (com.sun.star.uno.Exception e) {
+ throw new WrappedTargetRuntimeException(e,
+ "wrapped: " + e.getMessage(), this, e);
+ }
+ }
+
+ public void addStatusListener(XStatusListener Control, URL URL) {}
+
+ public void removeStatusListener(XStatusListener Control, URL URL) {}
+
+ private final XComponentContext context;
+
+ static final String implementationName =
+ "com.sun.star.comp.test.deployment.passive_java_singleton";
+
+ static final String[] serviceNames = new String[0];
+}
diff --git a/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Provider.java b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Provider.java
new file mode 100644
index 000000000..13d59ecf1
--- /dev/null
+++ b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Provider.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.passive_java;
+
+import com.sun.star.frame.DispatchDescriptor;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XDispatchProvider;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.URL;
+
+public final class Provider extends WeakBase
+ implements XServiceInfo, XDispatchProvider
+{
+ public Provider(XComponentContext context) {
+ this.context = context;
+ }
+
+ public String getImplementationName() { return implementationName; }
+
+ public boolean supportsService(String ServiceName) {
+ return ServiceName.equals(getSupportedServiceNames()[0]); //TODO
+ }
+
+ public String[] getSupportedServiceNames() {
+ return serviceNames;
+ }
+
+ public XDispatch queryDispatch(
+ URL URL, String TargetFrameName, int SearchFlags)
+ {
+ return UnoRuntime.queryInterface(
+ XDispatch.class,
+ context.getValueByName(
+ "/singletons/" +
+ "com.sun.star.test.deployment.passive_java_singleton"));
+ }
+
+ public XDispatch[] queryDispatches(DispatchDescriptor[] Requests) {
+ XDispatch[] s = new XDispatch[Requests.length];
+ for (int i = 0; i < s.length; ++i) {
+ s[i] = queryDispatch(
+ Requests[i].FeatureURL, Requests[i].FrameName,
+ Requests[i].SearchFlags);
+ }
+ return s;
+ }
+
+ private final XComponentContext context;
+
+ static final String implementationName =
+ "com.sun.star.comp.test.deployment.passive_java";
+
+ static final String[] serviceNames = new String[] {
+ "com.sun.star.test.deployment.passive_java" };
+}
diff --git a/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Services.java b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Services.java
new file mode 100644
index 000000000..b14ba7102
--- /dev/null
+++ b/desktop/test/deployment/passive/com/sun/star/comp/test/deployment/Services.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package com.sun.star.comp.test.deployment.passive_java;
+
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lib.uno.helper.Factory;
+
+public final class Services {
+ private Services() {}
+
+ public static XSingleComponentFactory __getComponentFactory(
+ String implementation)
+ {
+ if (implementation.equals(Dispatch.implementationName)) {
+ return Factory.createComponentFactory(
+ Dispatch.class, Dispatch.implementationName,
+ Dispatch.serviceNames);
+ } else if (implementation.equals(Provider.implementationName)) {
+ return Factory.createComponentFactory(
+ Provider.class, Provider.implementationName,
+ Provider.serviceNames);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/desktop/test/deployment/passive/description.xml b/desktop/test/deployment/passive/description.xml
new file mode 100644
index 000000000..b0fdea19e
--- /dev/null
+++ b/desktop/test/deployment/passive/description.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<d:description xmlns:d="http://openoffice.org/extensions/description/2006">
+ <d:identifier
+ value="org.openoffice/framework/desktop/test/deployment/passive"/>
+ <d:version value="1"/>
+ <d:dependencies>
+ <d:OpenOffice.org-minimal-version d:name="OpenOffice.org 3.4" value="3.4"/>
+ </d:dependencies>
+</d:description>
diff --git a/desktop/test/deployment/passive/help/en/help.tree b/desktop/test/deployment/passive/help/en/help.tree
new file mode 100644
index 000000000..76a07991d
--- /dev/null
+++ b/desktop/test/deployment/passive/help/en/help.tree
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<tree_view version="10-Aug-2010">
+ <help_section application="OrgOpenofficeFrameworkDesktopTestDeploymentPassive" id=""
+ title="The test-passive Extension">
+ <topic
+ id="OrgOpenofficeFrameworkDesktopTestDeploymentPassive/org.openoffice%2Fframework%2Fdesktop%2Ftest%2Fdeployment%2Fpassive/main.xhp">The test-passive Extension</topic>
+ </help_section>
+</tree_view>
diff --git a/desktop/test/deployment/passive/help/en/main.xhp b/desktop/test/deployment/passive/help/en/main.xhp
new file mode 100644
index 000000000..ff62c6b68
--- /dev/null
+++ b/desktop/test/deployment/passive/help/en/main.xhp
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<helpdocument version="1.0">
+ <meta>
+ <topic id="" indexer="include">
+ <title xml-lang="en" id="">The test-passive Extension</title>
+ <filename>/org.openoffice%2Fframework%2Fdesktop%2Ftest%2Fdeployment%2Fpassive/main.xhp</filename>
+ <bookmark branch="index" xml-lang="en" id="">
+ <bookmark_value>test-passive extension</bookmark_value>
+ <bookmark_value>extensions;test-passive</bookmark_value>
+ </bookmark>
+ <bookmark branch="index" xml-lang="en" id="_scalc_swriter">
+ <bookmark_value>test-passive extension in Calc and Writer</bookmark_value>
+ </bookmark>
+ </topic>
+ </meta>
+ <body>
+ <bookmark branch="hid/vnd.org.openoffice.test.desktop.deployment.passive_native:" xml-lang="en"
+ id=""/>
+ <paragraph role="paragraph" id="" xml-lang="en">
+ <ahelp hid="vnd.org.openoffice.test.desktop.deployment.passive_native:" visibility="hidden">
+ Show the test-passive extension's native dialog
+ </ahelp>
+ </paragraph>
+ <paragraph role="heading" level="1" id="" xml-lang="en">The test-passive Extension</paragraph>
+ <paragraph role="paragraph" id="" xml-lang="en">Bla bla bla.</paragraph>
+ </body>
+</helpdocument>
diff --git a/desktop/test/deployment/passive/passive_java.component b/desktop/test/deployment/passive/passive_java.component
new file mode 100644
index 000000000..35ff0fcdf
--- /dev/null
+++ b/desktop/test/deployment/passive/passive_java.component
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.Java2"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.test.deployment.passive_java">
+ <service name="com.sun.star.test.deployment.passive_java"/>
+ </implementation>
+ <implementation
+ name="com.sun.star.comp.test.deployment.passive_java_singleton">
+ <singleton name="com.sun.star.test.deployment.passive_java_singleton"/>
+ </implementation>
+</component>
diff --git a/desktop/test/deployment/passive/passive_native.component b/desktop/test/deployment/passive/passive_native.component
new file mode 100644
index 000000000..37d13e53d
--- /dev/null
+++ b/desktop/test/deployment/passive/passive_native.component
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.test.deployment.passive_native">
+ <service name="com.sun.star.test.deployment.passive_native"/>
+ </implementation>
+ <implementation
+ name="com.sun.star.comp.test.deployment.passive_native_singleton">
+ <singleton name="com.sun.star.test.deployment.passive_native_singleton"/>
+ </implementation>
+</component>
diff --git a/desktop/test/deployment/passive/passive_native.cxx b/desktop/test/deployment/passive/passive_native.cxx
new file mode 100644
index 000000000..7b95fff8b
--- /dev/null
+++ b/desktop/test/deployment/passive/passive_native.cxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <com/sun/star/awt/MessageBoxButtons.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/XMessageBox.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/implementationentry.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <uno/lbnames.h>
+
+namespace {
+
+class Provider:
+ public cppu::WeakImplHelper2<
+ css::lang::XServiceInfo, css::frame::XDispatchProvider >
+{
+public:
+ Provider(const Provider&) = delete;
+ const Provider& operator=(const Provider&) = delete;
+
+ static css::uno::Reference< css::uno::XInterface > SAL_CALL static_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext)
+ { return static_cast< cppu::OWeakObject * >(new Provider(xContext)); }
+
+ static rtl::OUString SAL_CALL static_getImplementationName();
+
+ static css::uno::Sequence< rtl::OUString > SAL_CALL
+ static_getSupportedServiceNames();
+
+private:
+ explicit Provider(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ context_(context) { assert(context.is()); }
+
+ virtual ~Provider() {}
+
+ virtual rtl::OUString SAL_CALL getImplementationName() override
+ { return static_getImplementationName(); }
+
+ virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< rtl::OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return static_getSupportedServiceNames(); }
+
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch(
+ css::util::URL const &, rtl::OUString const &, sal_Int32) override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > >
+ SAL_CALL queryDispatches(
+ css::uno::Sequence< css::frame::DispatchDescriptor > const & Requests) override;
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+};
+
+rtl::OUString Provider::static_getImplementationName() {
+ return rtl::OUString("com.sun.star.comp.test.deployment.passive_native");
+}
+
+css::uno::Sequence< rtl::OUString > Provider::static_getSupportedServiceNames()
+{
+ rtl::OUString name("com.sun.star.test.deployment.passive_native");
+ return css::uno::Sequence< rtl::OUString >(&name, 1);
+}
+
+css::uno::Reference< css::frame::XDispatch > Provider::queryDispatch(
+ css::util::URL const &, rtl::OUString const &, sal_Int32)
+{
+ css::uno::Reference< css::frame::XDispatch > dispatch;
+ if (!(context_->getValueByName(
+ "/singletons/com.sun.star.test.deployment."
+ "passive_native_singleton") >>=
+ dispatch) ||
+ !dispatch.is())
+ {
+ throw css::uno::DeploymentException(
+ "component context fails to supply singleton"
+ " com.sun.star.test.deployment.passive_native_singleton of type"
+ " com.sun.star.frame.XDispatch",
+ context_);
+ }
+ return dispatch;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > >
+Provider::queryDispatches(
+ css::uno::Sequence< css::frame::DispatchDescriptor > const & Requests)
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > s(
+ Requests.getLength());
+ for (sal_Int32 i = 0; i < s.(); ++i) {
+ s[i] = queryDispatch(
+ Requests[i].FeatureURL, Requests[i].FrameName,
+ Requests[i].SearchFlags);
+ }
+ return s;
+}
+
+class Dispatch:
+ public cppu::WeakImplHelper2<
+ css::lang::XServiceInfo, css::frame::XDispatch >
+{
+public:
+ Dispatch(const Dispatch&) = delete;
+ const Dispatch& operator=(const Dispatch&) = delete;
+
+ static css::uno::Reference< css::uno::XInterface > SAL_CALL static_create(
+ css::uno::Reference< css::uno::XComponentContext > const & xContext)
+ { return static_cast< cppu::OWeakObject * >(new Dispatch(xContext)); }
+
+ static rtl::OUString SAL_CALL static_getImplementationName();
+
+ static css::uno::Sequence< rtl::OUString > SAL_CALL
+ static_getSupportedServiceNames()
+ { return css::uno::Sequence< rtl::OUString >(); }
+
+private:
+ explicit Dispatch(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ context_(context) { assert(context.is()); }
+
+ virtual ~Dispatch() {}
+
+ virtual rtl::OUString SAL_CALL getImplementationName() override
+ { return static_getImplementationName(); }
+
+ virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< rtl::OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return static_getSupportedServiceNames(); }
+
+ virtual void SAL_CALL dispatch(
+ css::util::URL const &,
+ css::uno::Sequence< css::beans::PropertyValue > const &) override;
+
+ virtual void SAL_CALL addStatusListener(
+ css::uno::Reference< css::frame::XStatusListener > const &,
+ css::util::URL const &) override
+ {}
+
+ virtual void SAL_CALL removeStatusListener(
+ css::uno::Reference< css::frame::XStatusListener > const &,
+ css::util::URL const &) override
+ {}
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+};
+
+rtl::OUString Dispatch::static_getImplementationName() {
+ return rtl::OUString(
+ "com.sun.star.comp.test.deployment.passive_native_singleton");
+}
+
+void Dispatch::dispatch(
+ css::util::URL const &,
+ css::uno::Sequence< css::beans::PropertyValue > const &)
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(context_);
+ css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->getCurrentFrame();
+ css::uno::Reference< css::awt::XWindowPeer > xWindowPeer( xFrame->getComponentWindow(), css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create(context_);
+ css::uno::Reference< css::awt::XMessageBox > box(
+ xToolkit->createMessageBox(
+ xWindowPeer,
+ css::awt::MessageBoxType_INFOBOX,
+ css::awt::MessageBoxButtons::BUTTONS_OK, "passive", "native"),
+ css::uno::UNO_SET_THROW);
+
+ box->execute();
+
+ css::uno::Reference< css::lang::XComponent > xComponent(box, css::uno::UNO_QUERY_THROW);
+ xComponent->dispose();
+}
+
+static cppu::ImplementationEntry const services[] = {
+ { &Provider::static_create, &Provider::static_getImplementationName,
+ &Provider::static_getSupportedServiceNames,
+ &cppu::createSingleComponentFactory, nullptr, 0 },
+ { &Dispatch::static_create, &Dispatch::static_getImplementationName,
+ &Dispatch::static_getSupportedServiceNames,
+ &cppu::createSingleComponentFactory, nullptr, 0 },
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * component_getFactory(
+ char const * pImplName, void * pServiceManager, void * pRegistryKey)
+{
+ return cppu::component_getFactoryHelper(
+ pImplName, pServiceManager, pRegistryKey, services);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void
+component_getImplementationEnvironment(
+ char const ** ppEnvTypeName, uno_Environment **)
+{
+ *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/passive/passive_python.component b/desktop/test/deployment/passive/passive_python.component
new file mode 100644
index 000000000..6007f9fcf
--- /dev/null
+++ b/desktop/test/deployment/passive/passive_python.component
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.Python"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.test.deployment.passive_python">
+ <service name="com.sun.star.test.deployment.passive_python"/>
+ </implementation>
+ <implementation
+ name="com.sun.star.comp.test.deployment.passive_python_singleton">
+ <singleton name="com.sun.star.test.deployment.passive_python_singleton"/>
+ </implementation>
+</component>
diff --git a/desktop/test/deployment/passive/passive_python.py b/desktop/test/deployment/passive/passive_python.py
new file mode 100644
index 000000000..f16797e50
--- /dev/null
+++ b/desktop/test/deployment/passive/passive_python.py
@@ -0,0 +1,93 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+import uno
+import unohelper
+
+from com.sun.star.awt import Rectangle
+from com.sun.star.awt.MessageBoxButtons import BUTTONS_OK
+from com.sun.star.awt.MessageBoxType import INFOBOX
+from com.sun.star.frame import XDispatch, XDispatchProvider
+from com.sun.star.lang import XServiceInfo
+
+class Provider(unohelper.Base, XServiceInfo, XDispatchProvider):
+ implementationName = "com.sun.star.comp.test.deployment.passive_python"
+
+ serviceNames = ("com.sun.star.test.deployment.passive_python",)
+
+ def __init__(self, context):
+ self.context = context
+
+ def getImplementationName(self):
+ return self.implementationName
+
+ def supportsService(self, ServiceName):
+ return ServiceName in self.serviceNames
+
+ def getSupportedServiceNames(self):
+ return self.serviceNames
+
+ def queryDispatch(self, URL, TargetFrame, SearchFlags):
+ return self.context.getValueByName( \
+ "/singletons/com.sun.star.test.deployment.passive_python_singleton")
+
+ def queryDispatches(self, Requests):
+ tuple( \
+ self.queryDispatch(i.FeatureURL, i.FrameName, i.SearchFlags) \
+ for i in Requests)
+
+class Dispatch(unohelper.Base, XServiceInfo, XDispatch):
+ implementationName = \
+ "com.sun.star.comp.test.deployment.passive_python_singleton"
+
+ serviceNames = ()
+
+ def __init__(self, context):
+ self.context = context
+
+ def getImplementationName(self):
+ return self.implementationName
+
+ def supportsService(self, ServiceName):
+ return ServiceName in self.serviceNames
+
+ def getSupportedServiceNames(self):
+ return self.serviceNames
+
+ def dispatch(self, URL, Arguments):
+ smgr = self.context.getServiceManager()
+ box = smgr.createInstanceWithContext( \
+ "com.sun.star.awt.Toolkit", self.context).createMessageBox( \
+ smgr.createInstanceWithContext( \
+ "com.sun.star.frame.Desktop", self.context). \
+ getCurrentFrame().getComponentWindow(), \
+ INFOBOX, BUTTONS_OK, "passive", "python")
+ box.execute();
+ box.dispose();
+
+ def addStatusListener(self, Control, URL):
+ pass
+
+ def removeStatusListener(self, Control, URL):
+ pass
+
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_ImplementationHelper.addImplementation( \
+ Provider, Provider.implementationName, Provider.serviceNames)
+g_ImplementationHelper.addImplementation( \
+ Dispatch, Dispatch.implementationName, Dispatch.serviceNames)
diff --git a/desktop/test/deployment/simple_license/BadDesc.oxt b/desktop/test/deployment/simple_license/BadDesc.oxt
new file mode 100644
index 000000000..436778d54
--- /dev/null
+++ b/desktop/test/deployment/simple_license/BadDesc.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/BadNamespace.oxt b/desktop/test/deployment/simple_license/BadNamespace.oxt
new file mode 100644
index 000000000..e439c9e17
--- /dev/null
+++ b/desktop/test/deployment/simple_license/BadNamespace.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/BadRoot.oxt b/desktop/test/deployment/simple_license/BadRoot.oxt
new file mode 100644
index 000000000..1f6c60c99
--- /dev/null
+++ b/desktop/test/deployment/simple_license/BadRoot.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale1.oxt b/desktop/test/deployment/simple_license/Locale1.oxt
new file mode 100644
index 000000000..51ecb5c75
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale1.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale2.oxt b/desktop/test/deployment/simple_license/Locale2.oxt
new file mode 100644
index 000000000..bb6b236a5
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale2.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale3.oxt b/desktop/test/deployment/simple_license/Locale3.oxt
new file mode 100644
index 000000000..56bfedc24
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale3.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale4.oxt b/desktop/test/deployment/simple_license/Locale4.oxt
new file mode 100644
index 000000000..9a465bc7c
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale4.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale5.oxt b/desktop/test/deployment/simple_license/Locale5.oxt
new file mode 100644
index 000000000..ce16830c1
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale5.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Locale6.oxt b/desktop/test/deployment/simple_license/Locale6.oxt
new file mode 100644
index 000000000..770d32506
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Locale6.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/LongLic.oxt b/desktop/test/deployment/simple_license/LongLic.oxt
new file mode 100644
index 000000000..a0a49daeb
--- /dev/null
+++ b/desktop/test/deployment/simple_license/LongLic.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/MissingLic.oxt b/desktop/test/deployment/simple_license/MissingLic.oxt
new file mode 100644
index 000000000..04d58fd11
--- /dev/null
+++ b/desktop/test/deployment/simple_license/MissingLic.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/MissingLicRef.oxt b/desktop/test/deployment/simple_license/MissingLicRef.oxt
new file mode 100644
index 000000000..01c9d19a2
--- /dev/null
+++ b/desktop/test/deployment/simple_license/MissingLicRef.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/NoDefLang.oxt b/desktop/test/deployment/simple_license/NoDefLang.oxt
new file mode 100644
index 000000000..3eadd5254
--- /dev/null
+++ b/desktop/test/deployment/simple_license/NoDefLang.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/NoDesc.oxt b/desktop/test/deployment/simple_license/NoDesc.oxt
new file mode 100644
index 000000000..ac83dac97
--- /dev/null
+++ b/desktop/test/deployment/simple_license/NoDesc.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/NoLang.oxt b/desktop/test/deployment/simple_license/NoLang.oxt
new file mode 100644
index 000000000..a4f3dd43a
--- /dev/null
+++ b/desktop/test/deployment/simple_license/NoLang.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/Prefix.oxt b/desktop/test/deployment/simple_license/Prefix.oxt
new file mode 100644
index 000000000..3e09b8d80
--- /dev/null
+++ b/desktop/test/deployment/simple_license/Prefix.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/ShortLicense.oxt b/desktop/test/deployment/simple_license/ShortLicense.oxt
new file mode 100644
index 000000000..efcfdc98e
--- /dev/null
+++ b/desktop/test/deployment/simple_license/ShortLicense.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/ShortLicenseShared.oxt b/desktop/test/deployment/simple_license/ShortLicenseShared.oxt
new file mode 100644
index 000000000..775559a2c
--- /dev/null
+++ b/desktop/test/deployment/simple_license/ShortLicenseShared.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/suppress_license.oxt b/desktop/test/deployment/simple_license/suppress_license.oxt
new file mode 100644
index 000000000..2bacd6aa3
--- /dev/null
+++ b/desktop/test/deployment/simple_license/suppress_license.oxt
Binary files differ
diff --git a/desktop/test/deployment/simple_license/tests_simple_license.odt b/desktop/test/deployment/simple_license/tests_simple_license.odt
new file mode 100644
index 000000000..b0c86e11c
--- /dev/null
+++ b/desktop/test/deployment/simple_license/tests_simple_license.odt
Binary files differ
diff --git a/desktop/test/deployment/update/changing_display_name/change1.oxt b/desktop/test/deployment/update/changing_display_name/change1.oxt
new file mode 100644
index 000000000..c919129ab
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/change1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/changing_display_name/change1_mod.oxt b/desktop/test/deployment/update/changing_display_name/change1_mod.oxt
new file mode 100644
index 000000000..5ab99d7bf
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/change1_mod.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/changing_display_name/readme.txt b/desktop/test/deployment/update/changing_display_name/readme.txt
new file mode 100644
index 000000000..905f0be9a
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/readme.txt
@@ -0,0 +1,13 @@
+
+The default display name, if nothing is provided by the extension, is the file name.
+The display name could be changed in different versions. There are three versions
+of change1.oxt available:
+
+v1: no display name
+v2: change1 de
+v3: change1 de - changed display name -
+
+change1_mod.oxt is the same as change1.oxt version 1 except that is has a display name.
+This situation should actually never arise, because the version should always be
+changed when the extension is changed - and be it only the display name.
+
diff --git a/desktop/test/deployment/update/changing_display_name/update1/change1.oxt b/desktop/test/deployment/update/changing_display_name/update1/change1.oxt
new file mode 100644
index 000000000..ef034f944
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/update1/change1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/changing_display_name/update1/change1.update.xml b/desktop/test/deployment/update/changing_display_name/update1/change1.update.xml
new file mode 100644
index 000000000..a7010d13d
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/update1/change1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice/framework/desktop/changing_display_name/change1" />
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/changing_display_name/update1/change1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/changing_display_name/update2/change1.oxt b/desktop/test/deployment/update/changing_display_name/update2/change1.oxt
new file mode 100644
index 000000000..551f5a3f4
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/update2/change1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/changing_display_name/update2/change1.update.xml b/desktop/test/deployment/update/changing_display_name/update2/change1.update.xml
new file mode 100644
index 000000000..88458d670
--- /dev/null
+++ b/desktop/test/deployment/update/changing_display_name/update2/change1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice/framework/desktop/changing_display_name/change1" />
+ <version value="3.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/changing_display_name/update2/change1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/default_url/default1.oxt b/desktop/test/deployment/update/default_url/default1.oxt
new file mode 100644
index 000000000..3fa8c9f08
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/default1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/default_url/default2.oxt b/desktop/test/deployment/update/default_url/default2.oxt
new file mode 100644
index 000000000..d54ce88c5
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/default2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/default_url/readme.txt b/desktop/test/deployment/update/default_url/readme.txt
new file mode 100644
index 000000000..a34c77cea
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/readme.txt
@@ -0,0 +1,9 @@
+Tests for using the default URL for update information. This URL is currently contained in
+the version.ini (ExtensionUpdateURL) and is used to obtain update information for extensions which do not provide
+a URL themselves.
+
+The extensions default1.oxt and default2.oxt do not have a URL for update information.
+
+To test this one has to put this entry into the version.ini:
+
+ExtensionUpdateURL=http://extensions.openoffice.org/testarea/desktop/default_url/update/feed1.xml
diff --git a/desktop/test/deployment/update/default_url/update/default1.oxt b/desktop/test/deployment/update/default_url/update/default1.oxt
new file mode 100644
index 000000000..198395c76
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/update/default1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/default_url/update/default1.update.xml b/desktop/test/deployment/update/default_url/update/default1.update.xml
new file mode 100644
index 000000000..5c240d6d5
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/update/default1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.default1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/default_url/update/default1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/default_url/update/default2.oxt b/desktop/test/deployment/update/default_url/update/default2.oxt
new file mode 100644
index 000000000..198395c76
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/update/default2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/default_url/update/default2.update.xml b/desktop/test/deployment/update/default_url/update/default2.update.xml
new file mode 100644
index 000000000..9e7e5db1a
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/update/default2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.default2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/default_url/update/default2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/default_url/update/feed1.xml b/desktop/test/deployment/update/default_url/update/feed1.xml
new file mode 100644
index 000000000..3f9a2e1d2
--- /dev/null
+++ b/desktop/test/deployment/update/default_url/update/feed1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
+
+ <title>OpenOffice.org Update Feed</title>
+ <link rel="alternate" type="text/html" href="http://update.services.openoffice.org/ooo/snapshot.html"/>
+ <updated>2006-11-06T18:30:02Z</updated>
+ <author>
+ <name>The OpenOffice.org Project</name>
+ <uri>http://openoffice.org</uri>
+ <email>updatefeed@openoffice.org</email>
+ </author>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566e9d</id>
+ <entry>
+ <title>default1.oxt version 2.0 available</title>
+ <link rel="alternate" type="text/html"
+ href="http://extensions.openoffice.org"/>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566e9f</id>
+ <category term="org.openoffice.legacy.default1.oxt" label="default1.oxt" />
+ <updated>2006-11-06T18:30:02Z</updated>
+ <summary>Click here to go to the download page.</summary>
+ <content type="application/xml" src="http://extensions.openoffice.org/testarea/desktop/default_url/update/default1.update.xml" />
+ </entry>
+ <entry>
+ <title>default2.oxt version 2.0 available</title>
+ <link rel="alternate" type="text/html"
+ href="http://extensions.openoffice.org"/>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566eaf</id>
+ <category term="org.openoffice.legacy.default2.oxt" label="default2.oxt" />
+ <updated>2006-11-06T18:30:02Z</updated>
+ <summary>Click here to go to the download page.</summary>
+ <content type="application/xml" src="http://extensions.openoffice.org/testarea/desktop/default_url/update/default2.update.xml" />
+ </entry>
+</feed>
diff --git a/desktop/test/deployment/update/defect/fail1.oxt b/desktop/test/deployment/update/defect/fail1.oxt
new file mode 100644
index 000000000..5b5cdba2c
--- /dev/null
+++ b/desktop/test/deployment/update/defect/fail1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/fail2.oxt b/desktop/test/deployment/update/defect/fail2.oxt
new file mode 100644
index 000000000..61b0306f0
--- /dev/null
+++ b/desktop/test/deployment/update/defect/fail2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/fail3.oxt b/desktop/test/deployment/update/defect/fail3.oxt
new file mode 100644
index 000000000..9da26d48a
--- /dev/null
+++ b/desktop/test/deployment/update/defect/fail3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/fail4.oxt b/desktop/test/deployment/update/defect/fail4.oxt
new file mode 100644
index 000000000..66b87caa1
--- /dev/null
+++ b/desktop/test/deployment/update/defect/fail4.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/info1.oxt b/desktop/test/deployment/update/defect/info1.oxt
new file mode 100644
index 000000000..9ffd373fa
--- /dev/null
+++ b/desktop/test/deployment/update/defect/info1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/info2.oxt b/desktop/test/deployment/update/defect/info2.oxt
new file mode 100644
index 000000000..229a52c3b
--- /dev/null
+++ b/desktop/test/deployment/update/defect/info2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/info3.oxt b/desktop/test/deployment/update/defect/info3.oxt
new file mode 100644
index 000000000..b702f3e00
--- /dev/null
+++ b/desktop/test/deployment/update/defect/info3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/readme.txt b/desktop/test/deployment/update/defect/readme.txt
new file mode 100644
index 000000000..5e8322f5c
--- /dev/null
+++ b/desktop/test/deployment/update/defect/readme.txt
@@ -0,0 +1,15 @@
+The updates, that is the newer versions, are defect. However, only fail2.oxt fails to install. The other extensions can be installed directly and through an update.
+
+fail1.oxt: in version2 the contained t.rdb was renamed so that it is not found (t.rdb is referenced in the manifest.xml).
+
+fail2.oxt: in version 2 the contained t.rdb is corrupted. It is a renamed .txt file which contains some text.
+
+fail3.oxt: in version 2 the contained t.rdb is corrupted. It is a renamed .txt file which does not contain any text.
+
+fail4.oxt: the version 2 references by fail4.update.xml is empty.
+
+info1.oxt: The update information file has length null.
+
+info2.oxt: The update information does not contain xml.
+
+info3.oxt: The update information contain an error: the tag update information contains two opening brackets (<<update-information>) \ No newline at end of file
diff --git a/desktop/test/deployment/update/defect/update/fail1.oxt b/desktop/test/deployment/update/defect/update/fail1.oxt
new file mode 100644
index 000000000..dbcc7cd73
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/update/fail1.update.xml b/desktop/test/deployment/update/defect/update/fail1.update.xml
new file mode 100644
index 000000000..87de4a25a
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.fail1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/defect/update/fail1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/defect/update/fail2.oxt b/desktop/test/deployment/update/defect/update/fail2.oxt
new file mode 100644
index 000000000..6df0c3cf9
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/update/fail2.update.xml b/desktop/test/deployment/update/defect/update/fail2.update.xml
new file mode 100644
index 000000000..254f61f34
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.fail2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/defect/update/fail2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/defect/update/fail3.oxt b/desktop/test/deployment/update/defect/update/fail3.oxt
new file mode 100644
index 000000000..2d340f414
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/update/fail3.update.xml b/desktop/test/deployment/update/defect/update/fail3.update.xml
new file mode 100644
index 000000000..ff7f82db0
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail3.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.fail3.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/defect/update/fail3.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/defect/update/fail4.oxt b/desktop/test/deployment/update/defect/update/fail4.oxt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail4.oxt
diff --git a/desktop/test/deployment/update/defect/update/fail4.update.xml b/desktop/test/deployment/update/defect/update/fail4.update.xml
new file mode 100644
index 000000000..b321b16ba
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/fail4.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.fail4.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/defect/update/fail4.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/defect/update/info1.update.xml b/desktop/test/deployment/update/defect/update/info1.update.xml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/info1.update.xml
diff --git a/desktop/test/deployment/update/defect/update/info2.update.xml b/desktop/test/deployment/update/defect/update/info2.update.xml
new file mode 100644
index 000000000..7e26b0b17
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/info2.update.xml
@@ -0,0 +1 @@
+This is an invalid update information file!!!
diff --git a/desktop/test/deployment/update/defect/update/info3.oxt b/desktop/test/deployment/update/defect/update/info3.oxt
new file mode 100644
index 000000000..60debac57
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/info3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/defect/update/info3.update.xml b/desktop/test/deployment/update/defect/update/info3.update.xml
new file mode 100644
index 000000000..69b54c259
--- /dev/null
+++ b/desktop/test/deployment/update/defect/update/info3.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.info3.oxt"/>
+ <version value="2.0" />
+ <<update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/defect/update/info3.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/dependencies/publisher_en.html b/desktop/test/deployment/update/dependencies/publisher_en.html
new file mode 100644
index 000000000..37dbc2b9d
--- /dev/null
+++ b/desktop/test/deployment/update/dependencies/publisher_en.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My Extension Company</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/dependencies/readme.txt b/desktop/test/deployment/update/dependencies/readme.txt
new file mode 100644
index 000000000..a64f749b1
--- /dev/null
+++ b/desktop/test/deployment/update/dependencies/readme.txt
@@ -0,0 +1,23 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+update-dependencies.oxt is an extension that itself has no dependencies, but
+whose update has unsatisfied dependencies (and also uses update-website).
+
+The update information contain also publisher and release notes information,
+which should be displayed in the update dialog.
diff --git a/desktop/test/deployment/update/dependencies/release-notes_en.html b/desktop/test/deployment/update/dependencies/release-notes_en.html
new file mode 100644
index 000000000..0971f78d1
--- /dev/null
+++ b/desktop/test/deployment/update/dependencies/release-notes_en.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/dependencies/update-dependencies.oxt b/desktop/test/deployment/update/dependencies/update-dependencies.oxt
new file mode 100644
index 000000000..513b25d20
--- /dev/null
+++ b/desktop/test/deployment/update/dependencies/update-dependencies.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/dependencies/update/update-dependencies.update.xml b/desktop/test/deployment/update/dependencies/update/update-dependencies.update.xml
new file mode 100644
index 000000000..7f9d0606f
--- /dev/null
+++ b/desktop/test/deployment/update/dependencies/update/update-dependencies.update.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:d="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/dependencies/update-dependencies.oxt"/>
+ <version value="2.0"/>
+ <dependencies>
+ <dependency d:name="&amp; &lt; &gt; &apos; &quot; > ' tab&#x9;. crlf&#xD;&#xA;. em-dash&#x2014;. line-separator&#x2028;. paragraph-separator&#x2029;. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/>
+ <dependency d:name="Dependency 1"/>
+ <dependency d:name="Dependency 2"/>
+ <dependency d:name="Dependency 3"/>
+ <dependency d:name="Dependency 4"/>
+ <dependency d:name="Dependency 5"/>
+ <dependency d:name="Dependency 6"/>
+ <dependency d:name="Dependency 7"/>
+ <dependency d:name="Dependency 8"/>
+ <dependency d:name="Dependency 9"/>
+ <dependency d:name="Dependency 10"/>
+ <dependency d:name="Dependency 11"/>
+ <dependency d:name="Dependency 12"/>
+ <dependency d:name="Dependency 13"/>
+ <dependency d:name="Dependency 14"/>
+ <dependency d:name="Dependency 15"/>
+ <dependency d:name="Dependency 16"/>
+ <dependency d:name="Dependency 17"/>
+ <dependency d:name="Dependency 18"/>
+ <dependency d:name="Dependency 19"/>
+ <dependency d:name="Dependency 20"/>
+ <dependency/>
+ <d:OpenOffice.org-minimal-version value="2.1" d:name="OpenOffice.org 2.1"/>
+ </dependencies>
+ <update-website>
+ <src xlink:href="http://nowhere.openoffice.org"/>
+ <src xlink:href="http://nowhere.openoffice.org/2"/>
+ </update-website>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/dependencies/publisher_en.html" lang="en">My Extension Company</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/dependencies/release-notes_en.html" lang="en" />
+ </release-notes>
+
+</description>
diff --git a/desktop/test/deployment/update/license/lic1.oxt b/desktop/test/deployment/update/license/lic1.oxt
new file mode 100644
index 000000000..43bfe3b77
--- /dev/null
+++ b/desktop/test/deployment/update/license/lic1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/lic2.oxt b/desktop/test/deployment/update/license/lic2.oxt
new file mode 100644
index 000000000..266a45e9a
--- /dev/null
+++ b/desktop/test/deployment/update/license/lic2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/lic3.oxt b/desktop/test/deployment/update/license/lic3.oxt
new file mode 100644
index 000000000..3f1b98960
--- /dev/null
+++ b/desktop/test/deployment/update/license/lic3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/readme.txt b/desktop/test/deployment/update/license/readme.txt
new file mode 100644
index 000000000..6040da04c
--- /dev/null
+++ b/desktop/test/deployment/update/license/readme.txt
@@ -0,0 +1,9 @@
+The extensions contain a license which is displayed during installation. If the license is displayed during an update can be determined by the attribute
+/description/registration/simple-license/@suppress-on-update
+
+The default value is false, which means that the attribute is not set, then the license is displayed during an update.
+
+lic1.oxt: attribute not set
+lic2.oxt: attribute set to false
+lic3.oxt: attribute set to true
+
diff --git a/desktop/test/deployment/update/license/update/lic1.oxt b/desktop/test/deployment/update/license/update/lic1.oxt
new file mode 100644
index 000000000..cc91e1ff1
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/update/lic1.update.xml b/desktop/test/deployment/update/license/update/lic1.update.xml
new file mode 100644
index 000000000..2a2761aa9
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.lic1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/license/update/lic1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/license/update/lic2.oxt b/desktop/test/deployment/update/license/update/lic2.oxt
new file mode 100644
index 000000000..351000792
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/update/lic2.update.xml b/desktop/test/deployment/update/license/update/lic2.update.xml
new file mode 100644
index 000000000..7dc57ca0e
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.lic2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/license/update/lic2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/license/update/lic3.oxt b/desktop/test/deployment/update/license/update/lic3.oxt
new file mode 100644
index 000000000..6ac6e0fd0
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/license/update/lic3.update.xml b/desktop/test/deployment/update/license/update/lic3.update.xml
new file mode 100644
index 000000000..55f5556b6
--- /dev/null
+++ b/desktop/test/deployment/update/license/update/lic3.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.lic3.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/license/update/lic3.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/platform/all1.oxt b/desktop/test/deployment/update/platform/all1.oxt
new file mode 100644
index 000000000..ad9662a7c
--- /dev/null
+++ b/desktop/test/deployment/update/platform/all1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/all2.oxt b/desktop/test/deployment/update/platform/all2.oxt
new file mode 100644
index 000000000..632d11b42
--- /dev/null
+++ b/desktop/test/deployment/update/platform/all2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/all3.oxt b/desktop/test/deployment/update/platform/all3.oxt
new file mode 100644
index 000000000..ab781552a
--- /dev/null
+++ b/desktop/test/deployment/update/platform/all3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/freebsd_x86.oxt b/desktop/test/deployment/update/platform/freebsd_x86.oxt
new file mode 100644
index 000000000..338f5761d
--- /dev/null
+++ b/desktop/test/deployment/update/platform/freebsd_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/freebsd_x86_64.oxt b/desktop/test/deployment/update/platform/freebsd_x86_64.oxt
new file mode 100644
index 000000000..39fee6de1
--- /dev/null
+++ b/desktop/test/deployment/update/platform/freebsd_x86_64.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/invalid1.oxt b/desktop/test/deployment/update/platform/invalid1.oxt
new file mode 100644
index 000000000..13d709f43
--- /dev/null
+++ b/desktop/test/deployment/update/platform/invalid1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/invalid2.oxt b/desktop/test/deployment/update/platform/invalid2.oxt
new file mode 100644
index 000000000..f14257191
--- /dev/null
+++ b/desktop/test/deployment/update/platform/invalid2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/invalid3.oxt b/desktop/test/deployment/update/platform/invalid3.oxt
new file mode 100644
index 000000000..cadffa4f2
--- /dev/null
+++ b/desktop/test/deployment/update/platform/invalid3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_arm_eabi.oxt b/desktop/test/deployment/update/platform/linux_arm_eabi.oxt
new file mode 100644
index 000000000..9c504e841
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_arm_eabi.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_arm_oabi.oxt b/desktop/test/deployment/update/platform/linux_arm_oabi.oxt
new file mode 100644
index 000000000..f2c987f64
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_arm_oabi.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_ia64.oxt b/desktop/test/deployment/update/platform/linux_ia64.oxt
new file mode 100644
index 000000000..f579a18ab
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_ia64.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_mips64_eb.oxt b/desktop/test/deployment/update/platform/linux_mips64_eb.oxt
new file mode 100644
index 000000000..544121694
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_mips64_eb.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_mips64_el.oxt b/desktop/test/deployment/update/platform/linux_mips64_el.oxt
new file mode 100644
index 000000000..17499092f
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_mips64_el.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_mips_eb.oxt b/desktop/test/deployment/update/platform/linux_mips_eb.oxt
new file mode 100644
index 000000000..bf0bd9423
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_mips_eb.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_mips_el.oxt b/desktop/test/deployment/update/platform/linux_mips_el.oxt
new file mode 100644
index 000000000..6bd564468
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_mips_el.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_powerpc.oxt b/desktop/test/deployment/update/platform/linux_powerpc.oxt
new file mode 100644
index 000000000..e301a3fb3
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_powerpc.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_powerpc64.oxt b/desktop/test/deployment/update/platform/linux_powerpc64.oxt
new file mode 100644
index 000000000..e5f3ae063
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_powerpc64.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_s390.oxt b/desktop/test/deployment/update/platform/linux_s390.oxt
new file mode 100644
index 000000000..199702ebf
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_s390.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_s390x.oxt b/desktop/test/deployment/update/platform/linux_s390x.oxt
new file mode 100644
index 000000000..2ed250833
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_s390x.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_sparc.oxt b/desktop/test/deployment/update/platform/linux_sparc.oxt
new file mode 100644
index 000000000..53dfc71e0
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_sparc.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_x86.oxt b/desktop/test/deployment/update/platform/linux_x86.oxt
new file mode 100644
index 000000000..8379539ca
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/linux_x86_64.oxt b/desktop/test/deployment/update/platform/linux_x86_64.oxt
new file mode 100644
index 000000000..0fb182275
--- /dev/null
+++ b/desktop/test/deployment/update/platform/linux_x86_64.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/macosx_powerpc.oxt b/desktop/test/deployment/update/platform/macosx_powerpc.oxt
new file mode 100644
index 000000000..7c1463471
--- /dev/null
+++ b/desktop/test/deployment/update/platform/macosx_powerpc.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/macosx_x86.oxt b/desktop/test/deployment/update/platform/macosx_x86.oxt
new file mode 100644
index 000000000..a20aadfef
--- /dev/null
+++ b/desktop/test/deployment/update/platform/macosx_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/mul1.oxt b/desktop/test/deployment/update/platform/mul1.oxt
new file mode 100644
index 000000000..b3b555969
--- /dev/null
+++ b/desktop/test/deployment/update/platform/mul1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/os2_x86.oxt b/desktop/test/deployment/update/platform/os2_x86.oxt
new file mode 100644
index 000000000..1c7fd40be
--- /dev/null
+++ b/desktop/test/deployment/update/platform/os2_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/readme.txt b/desktop/test/deployment/update/platform/readme.txt
new file mode 100644
index 000000000..30c028f99
--- /dev/null
+++ b/desktop/test/deployment/update/platform/readme.txt
@@ -0,0 +1,51 @@
+Extension which only support one platform
+========================================================
+
+freebsd_x86.oxt: freebsd_x86
+freebsd_x86_86.oxt: freebsd_x86_64
+linux_arm_eabi.oxt: linux_arm_eabi
+linux_arm_oabi.oxt: linux_arm_oabi
+linux_ia64.oxt: linux_ia64
+linux_mips_eb.oxt: linux_mips_eb
+linux_mips64_eb.oxt: linux_mips64_eb
+linux_mips_el.oxt: linux_mips_el
+linux_mips64_el.oxt: linux_mips64_el
+linux_powerpc64.oxt: linux_powerpc64
+linux_powerpc.oxt: linux_powerpc
+linux_s390.oxt: linux_s390
+linux_s390x.oxt: linux_s390x
+linux_sparc.oxt: linux_sparc
+linux_x86.oxt: linux_x86
+linux_x86_64.oxt: linux_x86_64
+macos_powerpc.oxt: macos_powerpc
+macos_x86.oxt: macos_x86
+solaris_sparc.oxt: solaris_sparc
+solaris_x86.oxt: solaris_x86
+windows_x86.oxt: windows_x86
+os2_x86.oxt: os/2_x86
+
+Extensions which support multiple platforms
+=======================================================
+mul1.oxt: windows_x86, linux_x86, solaris_x86
+
+
+All platforms
+=========================================================
+all1.oxt: all, The <platform> element is missing. Default is "all".
+
+all2.oxt: all, <platform value="all" />
+
+all3.oxt: all, no description.xml
+
+
+
+Invalid platforms
+=========================================================
+The following extensions cannot be installed because the platform element
+is not correct. We assume that no valid platform is defined.
+
+invalid1.oxt: <platform />
+
+invalid2.oxt: <platform value=""/>
+
+invalid3.oxt: <platform value="," />
diff --git a/desktop/test/deployment/update/platform/solaris_sparc.oxt b/desktop/test/deployment/update/platform/solaris_sparc.oxt
new file mode 100644
index 000000000..a61f81f43
--- /dev/null
+++ b/desktop/test/deployment/update/platform/solaris_sparc.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/solaris_x86.oxt b/desktop/test/deployment/update/platform/solaris_x86.oxt
new file mode 100644
index 000000000..44d43df69
--- /dev/null
+++ b/desktop/test/deployment/update/platform/solaris_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/platform/windows_x86.oxt b/desktop/test/deployment/update/platform/windows_x86.oxt
new file mode 100644
index 000000000..c66a9b141
--- /dev/null
+++ b/desktop/test/deployment/update/platform/windows_x86.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub1.oxt b/desktop/test/deployment/update/publisher/pub1.oxt
new file mode 100644
index 000000000..c44ee9f3b
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub10.oxt b/desktop/test/deployment/update/publisher/pub10.oxt
new file mode 100644
index 000000000..1e7410ec1
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub10.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub11.oxt b/desktop/test/deployment/update/publisher/pub11.oxt
new file mode 100644
index 000000000..ef7fbca5e
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub11.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub2.oxt b/desktop/test/deployment/update/publisher/pub2.oxt
new file mode 100644
index 000000000..438bcae83
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub3.oxt b/desktop/test/deployment/update/publisher/pub3.oxt
new file mode 100644
index 000000000..62fd69f55
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub4.oxt b/desktop/test/deployment/update/publisher/pub4.oxt
new file mode 100644
index 000000000..4f6224f78
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub4.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub5.oxt b/desktop/test/deployment/update/publisher/pub5.oxt
new file mode 100644
index 000000000..1774e6cd3
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub5.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub6.oxt b/desktop/test/deployment/update/publisher/pub6.oxt
new file mode 100644
index 000000000..791a37f8e
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub6.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub7.oxt b/desktop/test/deployment/update/publisher/pub7.oxt
new file mode 100644
index 000000000..96e96887d
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub7.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub8.oxt b/desktop/test/deployment/update/publisher/pub8.oxt
new file mode 100644
index 000000000..dc9f0ce34
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub8.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/pub9.oxt b/desktop/test/deployment/update/publisher/pub9.oxt
new file mode 100644
index 000000000..5e8ba9ebc
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/pub9.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/publisher_de-DE-altmark.html b/desktop/test/deployment/update/publisher/publisher_de-DE-altmark.html
new file mode 100644
index 000000000..c770b914a
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_de-DE-altmark.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-DE-altmark</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_de-DE.html b/desktop/test/deployment/update/publisher/publisher_de-DE.html
new file mode 100644
index 000000000..b06ed7088
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_de-DE.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice de-DE</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_de.html b/desktop/test/deployment/update/publisher/publisher_de.html
new file mode 100644
index 000000000..4cba9f423
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_de.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice de</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en-GB.html b/desktop/test/deployment/update/publisher/publisher_en-GB.html
new file mode 100644
index 000000000..c73cf6219
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en-GB.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-GB</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en-US-region1.html b/desktop/test/deployment/update/publisher/publisher_en-US-region1.html
new file mode 100644
index 000000000..68beac724
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en-US-region1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-US-region1</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en-US-region2.html b/desktop/test/deployment/update/publisher/publisher_en-US-region2.html
new file mode 100644
index 000000000..501adb659
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en-US-region2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-US-region2</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en-US.html b/desktop/test/deployment/update/publisher/publisher_en-US.html
new file mode 100644
index 000000000..fd2575150
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en-US.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-US</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en-region3.html b/desktop/test/deployment/update/publisher/publisher_en-region3.html
new file mode 100644
index 000000000..b9fdc9d65
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en-region3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en-region3</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/publisher_en.html b/desktop/test/deployment/update/publisher/publisher_en.html
new file mode 100644
index 000000000..416ab8124
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/publisher_en.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>My OpenOffice en</H1>
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/publisher/readme.txt b/desktop/test/deployment/update/publisher/readme.txt
new file mode 100644
index 000000000..148dab776
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/readme.txt
@@ -0,0 +1,212 @@
+--The folder contains extensions which use in the description.xml the following:
+
+-The <publisher> element
+-The <release-notes> element
+
+Both element contain localized child elements.
+
+The following table shows what localized item is used, when the Office the locale
+en-US uses.
+
+
+Localization:
+
+Installed office en-US
+ | publisher | release notes
+=============================================
+pub1.oxt | en-US | en-US
+---------------------------------------------
+pub2.oxt | en-US-region1 | en-US-region1
+---------------------------------------------
+pub3.oxt | en | en
+---------------------------------------------
+pub4.oxt | en-GB | en-GB
+---------------------------------------------
+pub5.oxt | de | de
+
+
+================================================================================
+pub6.oxt
+================================================================================
+like pub1 but without release notes.
+
+
+================================================================================
+pub7.oxt
+================================================================================
+like pub1 but without publisher name.
+
+================================================================================
+pub8.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop.
+
+pub8.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+pub8.oxt provides <publisher> and <release-notes>. This information should be transferred
+in the update feed and not those entered in the repository.
+
+Test
+----
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. This setting should be ignored
+ when generating the update feed. Instead the publisher name from the extension is used.
+ Enter "Publisher Title" : some arbitrary company
+ "Publisher URL": any arbitrary URL but not:
+ http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html
+
+-Create a new release for the extension. Upload the version 2.0 (update/pub8.oxt).
+ Provide release notes. These release notes should later not be seen when clicking on
+ the release notes link.
+
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/publisher/pub8.oxt
+
+-Run the update in the Extension Manager
+
+
+Result:
+The Update Dialog should show the publisher name as provided in the description.xml. For example,
+when lang=en-US was selected: My OpenOffice en-US
+
+A release notes link is displayed with a URL to the release notes as provided in
+the description.xml. For example, when lang=en-US was selected:
+"http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html
+
+
+================================================================================
+pub9.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop.
+
+pub9.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+pub9.oxt provides <publisher>. That means the update feed should
+contain the <publisher> as provided by the extension and the release notes as entered
+in the repository.
+
+Test
+----
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. This setting should be ignored
+ when generating the update feed. Instead the publisher name from the extension is used.
+ Enter "Publisher Title" : some arbitrary company name
+ "Publisher URL": any arbitrary URL but not:
+ http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html
+
+-Create a new release for the extension. Upload the version 2.0 (update/pub9.oxt).
+ Provide release notes. These release notes should later be displayed when clicking on
+ the release notes link.
+
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/publisher/pub9.oxt
+
+-Run the update in the Extension Manager
+
+
+Result:
+The Update Dialog should show the publisher name as provided in the description.xml. For example,
+when lang=en-US was selected: My OpenOffice en-US
+
+A release notes link is displayed with a URL to the release notes as provided in the release notes
+field on the edit page for the extension in the repository.
+
+================================================================================
+pub10.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop.
+
+pub10.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+pub10.oxt provides <release-notes>. That means the update feed should
+contain the <release-notes> as provided by the extension and the publisher name/URLs as entered
+in the repository.
+
+Test
+----
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. They should then be in the update
+ feed.
+
+-Create a new release for the extension. Upload the version 2.0 (update/pub10.oxt).
+ Provide release notes. These release notes should NOT be displayed when clicking on
+ the release notes link. Instead the release notes provided by pub10.oxt should be displayed.
+
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/publisher/pub10.oxt
+
+-Run the update in the Extension Manager
+
+
+Result:
+The Update Dialog should show the publisher name as provided in the repository.
+
+A release notes link is displayed with a URL to the release notes as provided in the
+pub10.oxt. For example, when the locale of the office is en-US then this page will be
+displayed:
+For example,
+when lang=en-US was selected: My OpenOffice en-US
+
+================================================================================
+pub11.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop.
+
+pub11.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+pub10.oxt neither provides <release-notes> nor <publisher>. That means the update feed should
+contain these data as provided by the user on the repository web site.
+
+Test
+----
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. They should then be in the update
+ feed.
+
+-Create a new release for the extension. Upload the version 2.0 (update/pub11.oxt).
+ Provide release notes. These release notes should be displayed when clicking on
+ the release notes link.
+
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/publisher/pub11.oxt
+
+-Run the update in the Extension Manager
+
+
+Result:
+The Update Dialog should show the publisher name as provided in the repository.
+
+A release notes link is displayed which leads to the release notes kept in the repository.
+
diff --git a/desktop/test/deployment/update/publisher/release-notes_de-DE-altmark.html b/desktop/test/deployment/update/publisher/release-notes_de-DE-altmark.html
new file mode 100644
index 000000000..81b38a9f5
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_de-DE-altmark.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes de-DE-altmark</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_de-DE.html b/desktop/test/deployment/update/publisher/release-notes_de-DE.html
new file mode 100644
index 000000000..f8f0121f0
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_de-DE.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes de-DE</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_de.html b/desktop/test/deployment/update/publisher/release-notes_de.html
new file mode 100644
index 000000000..a9e1dc364
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_de.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes de</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en-GB.html b/desktop/test/deployment/update/publisher/release-notes_en-GB.html
new file mode 100644
index 000000000..ca72ec1b9
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en-GB.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en-GB</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en-US-region1.html b/desktop/test/deployment/update/publisher/release-notes_en-US-region1.html
new file mode 100644
index 000000000..0e6f99ce4
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en-US-region1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en-US-region1</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en-US-region2.html b/desktop/test/deployment/update/publisher/release-notes_en-US-region2.html
new file mode 100644
index 000000000..597bca0eb
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en-US-region2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en-US-region2</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en-US.html b/desktop/test/deployment/update/publisher/release-notes_en-US.html
new file mode 100644
index 000000000..7f9d73e33
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en-US.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en-US</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en-region3.html b/desktop/test/deployment/update/publisher/release-notes_en-region3.html
new file mode 100644
index 000000000..5d62c7bcb
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en-region3.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en-region3</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/release-notes_en.html b/desktop/test/deployment/update/publisher/release-notes_en.html
new file mode 100644
index 000000000..d02e4f333
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/release-notes_en.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Release Notes en</H1>
+</BODY>
+</HTML>
diff --git a/desktop/test/deployment/update/publisher/update/pub1.oxt b/desktop/test/deployment/update/publisher/update/pub1.oxt
new file mode 100644
index 000000000..cd04a58d5
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub1.update.xml b/desktop/test/deployment/update/publisher/update/pub1.update.xml
new file mode 100644
index 000000000..14a35d798
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub1.update.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub1"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en.html" lang="en">My OpenOffice en</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html" lang="en-US">My OpenOffice en-US</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region1.html" lang="en-US-region1">My OpenOffice en-US-region1</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region2.html" lang="en-US-region2">My OpenOffice en-US-region2</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US.html" lang="en-US" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub10.oxt b/desktop/test/deployment/update/publisher/update/pub10.oxt
new file mode 100644
index 000000000..501a84338
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub10.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub11.oxt b/desktop/test/deployment/update/publisher/update/pub11.oxt
new file mode 100644
index 000000000..692c0401f
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub11.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub2.oxt b/desktop/test/deployment/update/publisher/update/pub2.oxt
new file mode 100644
index 000000000..2a0bd6c21
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub2.update.xml b/desktop/test/deployment/update/publisher/update/pub2.update.xml
new file mode 100644
index 000000000..675ce484d
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub2.update.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub2"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en.html" lang="en">My OpenOffice en</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region1.html" lang="en-US-region1">My OpenOffice en-US-region1</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region2.html" lang="en-US-region2">My OpenOffice en-US-region2</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub3.oxt b/desktop/test/deployment/update/publisher/update/pub3.oxt
new file mode 100644
index 000000000..60675fc4d
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub3.update.xml b/desktop/test/deployment/update/publisher/update/pub3.update.xml
new file mode 100644
index 000000000..13199e375
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub3.update.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub3"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en.html" lang="en">My OpenOffice en</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub3.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub4.oxt b/desktop/test/deployment/update/publisher/update/pub4.oxt
new file mode 100644
index 000000000..19f7b7991
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub4.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub4.update.xml b/desktop/test/deployment/update/publisher/update/pub4.update.xml
new file mode 100644
index 000000000..800b68821
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub4.update.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub4"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub4.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub5.oxt b/desktop/test/deployment/update/publisher/update/pub5.oxt
new file mode 100644
index 000000000..afc632d57
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub5.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub5.update.xml b/desktop/test/deployment/update/publisher/update/pub5.update.xml
new file mode 100644
index 000000000..70bc7194f
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub5.update.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub5"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub5.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub6.oxt b/desktop/test/deployment/update/publisher/update/pub6.oxt
new file mode 100644
index 000000000..a68b445b8
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub6.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub6.update.xml b/desktop/test/deployment/update/publisher/update/pub6.update.xml
new file mode 100644
index 000000000..69ac58b28
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub6.update.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub6"/>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en.html" lang="en">My OpenOffice en</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html" lang="en-US">My OpenOffice en-US</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region1.html" lang="en-US-region1">My OpenOffice en-US-region1</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region2.html" lang="en-US-region2">My OpenOffice en-US-region2</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub6.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub7.oxt b/desktop/test/deployment/update/publisher/update/pub7.oxt
new file mode 100644
index 000000000..1b4bee044
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub7.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub7.update.xml b/desktop/test/deployment/update/publisher/update/pub7.update.xml
new file mode 100644
index 000000000..7ca456e95
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub7.update.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/publisher/pub7"/>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US.html" lang="en-US" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/update/pub7.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/publisher/update/pub8.oxt b/desktop/test/deployment/update/publisher/update/pub8.oxt
new file mode 100644
index 000000000..5688ab9d2
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub8.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/publisher/update/pub9.oxt b/desktop/test/deployment/update/publisher/update/pub9.oxt
new file mode 100644
index 000000000..752cfbbcf
--- /dev/null
+++ b/desktop/test/deployment/update/publisher/update/pub9.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/readme.txt b/desktop/test/deployment/update/readme.txt
new file mode 100644
index 000000000..fa1778308
--- /dev/null
+++ b/desktop/test/deployment/update/readme.txt
@@ -0,0 +1,58 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+The extensions in the subdirectories of the update folder are used for
+testing the online update feature of extensions. The folder such as
+simple,
+license,
+defect, etc. contain extensions which can be installed in OOo. The
+corresponding update information file and the update are located on
+the extensions.openoffice.org website (cvs: extensions/www/testarea). For example:
+
+desktop/test/deployment/update/simple/plain1.oxt
+
+is version 1 of this extension and it references
+
+http://extensions.openoffice.org/testarea/desktop/simple/update/plain1.update.xml
+
+which in turn references version 2 at
+
+http://extensions.openoffice.org/testarea/desktop/simple/update/plain1.oxt
+
+
+To have all in one place the update information file and the update are also contained
+in the desktop project. They are in the update subfolder of the different test folders.
+For example
+
+.../update/simple/update
+.../update/license/update
+.../update/updatefeed/update
+
+
+The different test folders for the update are also committed in project extensions/www
+so that the files can be obtain via a URL. The structure and the contents is about the
+same as the content
+of desktop/test/deployment/update
+For example in
+
+extensions/www/testarea/desktop
+
+are the subfolder defect, simple, updatefeed, wrong_url, etc.
+they contain the extensions which are installed directly by the Extension Manager.
+These folders contain also the update subfolder which contains the update information
+and the actual updates.
diff --git a/desktop/test/deployment/update/simple/plain1.oxt b/desktop/test/deployment/update/simple/plain1.oxt
new file mode 100644
index 000000000..6256f99d5
--- /dev/null
+++ b/desktop/test/deployment/update/simple/plain1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/plain2.oxt b/desktop/test/deployment/update/simple/plain2.oxt
new file mode 100644
index 000000000..03249c277
--- /dev/null
+++ b/desktop/test/deployment/update/simple/plain2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/plain3.oxt b/desktop/test/deployment/update/simple/plain3.oxt
new file mode 100644
index 000000000..64838932d
--- /dev/null
+++ b/desktop/test/deployment/update/simple/plain3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/readme.txt b/desktop/test/deployment/update/simple/readme.txt
new file mode 100644
index 000000000..34ad6beda
--- /dev/null
+++ b/desktop/test/deployment/update/simple/readme.txt
@@ -0,0 +1,31 @@
+The folder contains only simple extension. That is, they only contain
+- META-INF
+-t.rdb
+-description.xml
+
+The description.xml contains a version, a display name, and one URL to the update data
+
+For example:
+
+
+<?xml version="1.0" encoding="UTF-8"?>
+<description xmlns="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="1.0" />
+
+ <display-name>
+ <name lang="de">plain1 de</name>
+ </display-name>
+
+ <update-information>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/simple/plain1.update.xml" />
+ </update-information>
+</description>
+
+There is only one language as display name available, which will therefore always be displayed.
+
+The update information which is referenced in the update-information and the update is committed in the extensions/www project. To modify them get the project:
+
+cvs co extensions/wwww
+
+the files can be found under extensions/www/testarea/desktop
diff --git a/desktop/test/deployment/update/simple/update/plain1.oxt b/desktop/test/deployment/update/simple/update/plain1.oxt
new file mode 100644
index 000000000..d73362e87
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/update/plain1.update.xml b/desktop/test/deployment/update/simple/update/plain1.update.xml
new file mode 100644
index 000000000..741fcf774
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.plain1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/simple/update/plain1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/simple/update/plain2.oxt b/desktop/test/deployment/update/simple/update/plain2.oxt
new file mode 100644
index 000000000..3dc02aa97
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/update/plain2.update.xml b/desktop/test/deployment/update/simple/update/plain2.update.xml
new file mode 100644
index 000000000..883a82e62
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.plain2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/simple/update/plain2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/simple/update/plain3.oxt b/desktop/test/deployment/update/simple/update/plain3.oxt
new file mode 100644
index 000000000..575152403
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/simple/update/plain3.update.xml b/desktop/test/deployment/update/simple/update/plain3.update.xml
new file mode 100644
index 000000000..e6b3f383a
--- /dev/null
+++ b/desktop/test/deployment/update/simple/update/plain3.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.plain3.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/simple/update/plain3.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/updatefeed/feed1.oxt b/desktop/test/deployment/update/updatefeed/feed1.oxt
new file mode 100644
index 000000000..b1b11ecce
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/feed1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/updatefeed/feed2.oxt b/desktop/test/deployment/update/updatefeed/feed2.oxt
new file mode 100644
index 000000000..47dca1676
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/feed2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/updatefeed/update/feed1.oxt b/desktop/test/deployment/update/updatefeed/update/feed1.oxt
new file mode 100644
index 000000000..82bb9665a
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/update/feed1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/updatefeed/update/feed1.update.xml b/desktop/test/deployment/update/updatefeed/update/feed1.update.xml
new file mode 100644
index 000000000..90a7c23c9
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/update/feed1.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.feed1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/updatefeed/update/feed1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/updatefeed/update/feed1.xml b/desktop/test/deployment/update/updatefeed/update/feed1.xml
new file mode 100644
index 000000000..ed53012a4
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/update/feed1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
+
+ <title>OpenOffice.org Update Feed</title>
+ <link rel="alternate" type="text/html" href="http://update.services.openoffice.org/ooo/snapshot.html"/>
+ <updated>2006-11-06T18:30:02Z</updated>
+ <author>
+ <name>The OpenOffice.org Project</name>
+ <uri>http://openoffice.org</uri>
+ <email>updatefeed@openoffice.org</email>
+ </author>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566e9d</id>
+ <entry>
+ <title>feed1.oxt version 2.0 available</title>
+ <link rel="alternate" type="text/html"
+ href="http://extensions.openoffice.org"/>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566e9f</id>
+ <category term="org.openoffice.legacy.feed1.oxt" label="feed1.oxt" />
+ <updated>2006-11-06T18:30:02Z</updated>
+ <summary>Click here to go to the download page.</summary>
+ <content type="application/xml" src="http://extensions.openoffice.org/testarea/desktop/updatefeed/update/feed1.update.xml" />
+ </entry>
+ <entry>
+ <title>feed2.oxt version 2.0 available</title>
+ <link rel="alternate" type="text/html"
+ href="http://extensions.openoffice.org"/>
+ <id>urn:uuid:a4ccd383-1dd1-11b2-a95c-0003ba566eaf</id>
+ <category term="org.openoffice.legacy.feed2.oxt" label="feed2.oxt" />
+ <updated>2006-11-06T18:30:02Z</updated>
+ <summary>Click here to go to the download page.</summary>
+ <content type="application/xml" src="http://extensions.openoffice.org/testarea/desktop/updatefeed/update/feed2.update.xml" />
+ </entry>
+</feed>
diff --git a/desktop/test/deployment/update/updatefeed/update/feed2.oxt b/desktop/test/deployment/update/updatefeed/update/feed2.oxt
new file mode 100644
index 000000000..9c867ae4a
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/update/feed2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/updatefeed/update/feed2.update.xml b/desktop/test/deployment/update/updatefeed/update/feed2.update.xml
new file mode 100644
index 000000000..e37c692b0
--- /dev/null
+++ b/desktop/test/deployment/update/updatefeed/update/feed2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.feed2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/updatefeed/update/feed2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/updateinfocreation/build/MANIFEST.MF b/desktop/test/deployment/update/updateinfocreation/build/MANIFEST.MF
new file mode 100644
index 000000000..09e2f42ca
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/MANIFEST.MF
@@ -0,0 +1,2 @@
+RegistrationClassName: com.sun.star.comp.smoketest.TestExtension
+
diff --git a/desktop/test/deployment/update/updateinfocreation/build/TestExtension.idl b/desktop/test/deployment/update/updateinfocreation/build/TestExtension.idl
new file mode 100644
index 000000000..87b2540e4
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/TestExtension.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef _com_sun_star_comp_smoketest_TestExtension_idl_
+#define _com_sun_star_comp_smoketest_TestExtension_idl_
+
+#include <com/sun/star/lang/XServiceInfo.idl>
+
+
+module com { module sun { module star { module comp { module smoketest {
+ // example service, XServiceInfo is implemented here for demonstration
+ // issues. XServiceInfo must be implemented by all components.
+ service TestExtension: css::lang::XServiceInfo;
+};};};};};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/test/deployment/update/updateinfocreation/build/TestExtension.java b/desktop/test/deployment/update/updateinfocreation/build/TestExtension.java
new file mode 100644
index 000000000..8d48a30c8
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/TestExtension.java
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package com.sun.star.comp.smoketest;
+
+import com.sun.star.lib.uno.helper.Factory;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lib.uno.helper.WeakBase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.registry.XRegistryKey;
+import com.sun.star.lang.XInitialization;
+import com.sun.star.lang.XTypeProvider;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.uno.Type;
+
+/** This class capsulates the class, that implements the minimal component, a
+ * factory for creating the service (<CODE>__getComponentFactory</CODE>) and a
+ * method, that writes the information into the given registry key
+ * (<CODE>__writeRegistryServiceInfo</CODE>).
+ */
+public class TestExtension {
+ /** This class implements the component. At least the interfaces XServiceInfo,
+ * XTypeProvider, and XInitialization should be provided by the service.
+ */
+ public static class _TestExtension extends WeakBase
+ implements XServiceInfo {
+ /** The service name, that must be used to get an instance of this service.
+ */
+ private static final String __serviceName =
+ "com.sun.star.comp.smoketest.TestExtension";
+
+ /** The initial component contextr, that gives access to
+ * the service manager, supported singletons, ...
+ * It's often later used
+ */
+ private XComponentContext m_cmpCtx;
+
+ /** The service manager, that gives access to all registered services.
+ * It's often later used
+ */
+ private XMultiComponentFactory m_xMCF;
+
+ /** The constructor of the inner class has a XMultiServiceFactory parameter.
+ * @param xmultiservicefactoryInitialization A special service factory
+ * could be introduced while initializing.
+ */
+ public _TestExtension(XComponentContext xCompContext) {
+ try {
+ m_cmpCtx = xCompContext;
+ m_xMCF = m_cmpCtx.getServiceManager();
+ }
+ catch( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+
+ /** This method returns an array of all supported service names.
+ * @return Array of supported service names.
+ */
+ public String[] getSupportedServiceNames() {
+ return getServiceNames();
+ }
+
+ /** This method is a simple helper function to used in the
+ * static component initialisation functions as well as in
+ * getSupportedServiceNames.
+ */
+ public static String[] getServiceNames() {
+ String[] sSupportedServiceNames = { __serviceName };
+ return sSupportedServiceNames;
+ }
+
+ /** This method returns true, if the given service will be
+ * supported by the component.
+ * @param sServiceName Service name.
+ * @return True, if the given service name will be supported.
+ */
+ public boolean supportsService( String sServiceName ) {
+ return sServiceName.equals( __serviceName );
+ }
+
+ /** Return the class name of the component.
+ * @return Class name of the component.
+ */
+ public String getImplementationName() {
+ return _TestExtension.class.getName();
+ }
+ }
+
+
+ /**
+ * Gives a factory for creating the service.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns a <code>XSingleComponentFactory</code> for creating
+ * the component
+ * @param sImplName the name of the implementation for which a
+ * service is desired
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static XSingleComponentFactory __getComponentFactory(String sImplName)
+ {
+ XSingleComponentFactory xFactory = null;
+
+ if ( sImplName.equals( _TestExtension.class.getName() ) )
+ xFactory = Factory.createComponentFactory(_TestExtension.class,
+ _TestExtension.getServiceNames());
+
+ return xFactory;
+ }
+
+ /**
+ * Writes the service information into the given registry key.
+ * This method is called by the <code>JavaLoader</code>
+ * <p>
+ * @return returns true if the operation succeeded
+ * @param regKey the registryKey
+ * @see com.sun.star.comp.loader.JavaLoader
+ */
+ public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) {
+ return Factory.writeRegistryServiceInfo(_TestExtension.class.getName(),
+ _TestExtension.getServiceNames(),
+ regKey);
+ }
+ /** This method is a member of the interface for initializing an object
+ * directly after its creation.
+ * @param object This array of arbitrary objects will be passed to the
+ * component after its creation.
+ * @throws Exception Every exception will not be handled, but will be
+ * passed to the caller.
+ */
+ public void initialize( Object[] object )
+ throws com.sun.star.uno.Exception {
+ /* The component describes what arguments are expected and in which
+ * order! At this point you can read the objects and initialize
+ * your component using these objects.
+ */
+ }
+
+}
diff --git a/desktop/test/deployment/update/updateinfocreation/build/description.xml b/desktop/test/deployment/update/updateinfocreation/build/description.xml
new file mode 100644
index 000000000..283fb1cca
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/description.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:d="http://openoffice.org/extensions/description/2006" >
+ <identifier value="org.openoffice.extensions.testarea.desktop.updateinfo"/>
+ <version value="2.0" />
+ <dependencies >
+ <OpenOffice.org-minimal-version value="2.1" d:name="OpenOffice.org 2.1"/>
+ </dependencies>
+ <update-information>
+ <src xlink:href="http://update.services.openoffice.org/ProductUpdateService/check.Update?product=extension&amp;extensionid=org.openoffice.extensions.testarea.desktop.updateinfo&amp;refresh=true"/>
+ </update-information>
+</description>
diff --git a/desktop/test/deployment/update/updateinfocreation/build/makefile.mk b/desktop/test/deployment/update/updateinfocreation/build/makefile.mk
new file mode 100644
index 000000000..1cb59985e
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/makefile.mk
@@ -0,0 +1,80 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ = ..$/..$/..$/..$/..
+PRJNAME = updateinfo
+PACKAGE = com$/sun$/star$/comp$/smoketest
+TARGET = com_sun_star_comp_smoketest
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+JARFILES = ridl.jar jurt.jar unoil.jar juh.jar
+
+
+JARTARGET = TestExtension.jar
+JARCOMPRESS = TRUE
+CUSTOMMANIFESTFILE = MANIFEST.MF
+
+ZIP1TARGET=updateinfo
+ZIP1LIST=*
+ZIPFLAGS=-r
+ZIP1DIR=$(MISC)$/$(TARGET)
+ZIP1EXT=.oxt
+
+EXTUPDATEINFO_NAME=org.openoffice.extensions.testarea.desktop.updateinfo.update.xml
+EXTUPDATEINFO_SOURCE=description.xml
+EXTUPDATEINFO_URLS = http://extensions.openoffice.org/testarea/desktop/updateinfocreation/update/updateinfo.oxt
+# --- Files --------------------------------------------------------
+
+COPY_OXT_MANIFEST:= $(MISC)$/$(TARGET)$/META-INF$/manifest.xml
+JAVAFILES = TestExtension.java
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+
+$(JARTARGETN) : $(MISC)$/$(TARGET).javamaker.done
+
+$(JAVACLASSFILES) : $(MISC)$/$(TARGET).javamaker.done
+
+$(MISC)$/$(TARGET).javamaker.done: $(BIN)$/TestExtension.rdb
+ $(JAVAMAKER) -O$(CLASSDIR) -BUCR -nD -X$(SOLARBINDIR)/types.rdb $<
+ $(TOUCH) $@
+
+$(BIN)$/TestExtension.rdb: TestExtension.idl
+ $(IDLC) -O$(MISC) -I$(SOLARIDLDIR) -cid -we $<
+ +-$(RM) $@
+ $(REGMERGE) $@ /UCR $(MISC)$/TestExtension.urd
+
+$(MISC)$/$(ZIP1TARGET).createdir :
+ +$(MKDIRHIER) $(MISC)$/$(TARGET)$/META-INF >& $(NULLDEV) && $(TOUCH) $@
+
+$(MISC)$/$(TARGET)_resort : manifest.xml $(JARTARGETN) $(MISC)$/$(ZIP1TARGET).createdir $(BIN)$/TestExtension.rdb description.xml
+ $(GNUCOPY) -u manifest.xml $(MISC)$/$(TARGET)$/META-INF$/manifest.xml
+ $(GNUCOPY) -u $(JARTARGETN) $(MISC)$/$(TARGET)$/$(JARTARGET)
+ $(GNUCOPY) -u $(BIN)$/TestExtension.rdb $(MISC)$/$(TARGET)$/TestExtension.rdb
+ $(GNUCOPY) -u description.xml $(MISC)$/$(TARGET)$/description.xml
+ $(TOUCH) $@
+
+.IF "$(ZIP1TARGETN)"!=""
+$(ZIP1TARGETN) : $(MISC)$/$(TARGET)_resort $(MISC)$/$(ZIP1TARGET).createdir
+
+.ENDIF # "$(ZIP1TARGETN)"!=""
+
diff --git a/desktop/test/deployment/update/updateinfocreation/build/manifest.xml b/desktop/test/deployment/update/updateinfocreation/build/manifest.xml
new file mode 100644
index 000000000..fcedabf96
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/build/manifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
+ <manifest:file-entry manifest:full-path="TestExtension.jar" manifest:media-type="application/vnd.sun.star.uno-component;type=Java"/>
+ <manifest:file-entry manifest:full-path="TestExtension.rdb" manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"/>
+</manifest:manifest>
diff --git a/desktop/test/deployment/update/updateinfocreation/readme.txt b/desktop/test/deployment/update/updateinfocreation/readme.txt
new file mode 100644
index 000000000..20819bb70
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/readme.txt
@@ -0,0 +1,38 @@
+The extension build in this test uses an update information which is obtained
+through a http get request. That is the URL does not reference an update
+information file. Instead it invokes code on a webserver which returns the
+update information. The URL used in this example is:
+
+http://update.services.openoffice.org/ProductUpdateService/check.Update?product=extension&amp;extensionid=org.openoffice.extensions.testarea.desktop.updateinfo&amp;refresh=true
+
+The updateinfo.oxt in this directory has the version 1.0 and in the sub-directory "update is the version 2 of this extension. Version 1.0 is also available here
+/extensions/www/testarea/desktop/updateinfocreation/updateinfo.oxt
+and version 2.0 here
+/extensions/www/testarea/desktop/updateinfocreation/update/updateinfo.oxt
+
+Therefore they can be accessed through
+
+http://extensions.openoffice.org/testarea/desktop/updateinfocreation/updateinfo.oxt
+and
+http://extensions.openoffice.org/testarea/desktop/updateinfocreation/update/updateinfo.oxt
+
+The latter location (version 2.0) will also be referenced by the update information
+which are returned by the webserver.
+
+The build sub-directory contains the code of the extension (version 2.0) and can
+be build by calling dmake in this directory. The makefile uses the special macros:
+
+EXTUPDATEINFO_NAME=org.openoffice.extensions.testarea.desktop.updateinfo.update.xml
+EXTUPDATEINFO_SOURCE=description.xml
+EXTUPDATEINFO_URLS = http://extensions.openoffice.org/testarea/desktop/updateinfocreation/update/updateinfo.oxt
+
+This causes the generation of the update information file. This file could be
+directly references by the URL in the <update-information> of the description.xml.
+See also the Wiki entry at:
+http://wiki.openoffice.org/wiki/Creating_update_information_for_extensions
+This generated update information file can then be used by the webserver, when it
+sends back the requested update information. The update information file will be
+generated in the misc directory of the output directory.
+
+The update information file needs to be copied into common.pro/pus.mxyz directory.
+The project mwsfinish will process the files in the pus directory.
diff --git a/desktop/test/deployment/update/updateinfocreation/update/updateinfo.oxt b/desktop/test/deployment/update/updateinfocreation/update/updateinfo.oxt
new file mode 100644
index 000000000..52ddd3158
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/update/updateinfo.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/updateinfocreation/updateinfo.oxt b/desktop/test/deployment/update/updateinfocreation/updateinfo.oxt
new file mode 100644
index 000000000..43ac7003b
--- /dev/null
+++ b/desktop/test/deployment/update/updateinfocreation/updateinfo.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/readme.txt b/desktop/test/deployment/update/website_update/readme.txt
new file mode 100644
index 000000000..b888bba70
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/readme.txt
@@ -0,0 +1,133 @@
+The folder contains extensions which need to be updated through a web site.
+The "Updates dialog" of the Extension Manager will mark the updates for these
+extensions as "browser based update". The Extension Manager will open a browser
+for each of the extensions and navigate to the respective website.
+
+================================================================================
+web1.oxt - web5.oxt:
+================================================================================
+They contain <update-information>. That is they reference directly the respective
+webX.update.xml (for example, web1.update.xml) files which are available at
+http://extensions.openoffice.org/testarea/desktop/website_update/update/...
+For example:
+http://extensions.openoffice.org/testarea/desktop/website_update/update/web2.update.xml
+
+The update information contain multiple URLs to "localized" web sites. Each URL is
+assigned to a particular local. For example:
+
+<src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de"/>
+
+The Extension Manager will choose the URLs where the lang attribute matches most
+closely the locale of the office.
+
+The following table shows what localized web site is used, when the office uses the locale
+en-US. The web page will display the locale used. See update/web1_de.html, etc.
+
+
+Localization:
+
+Installed office en-US
+ | publisher | release notes
+=============================================
+web1.oxt | en-US | en-US
+---------------------------------------------
+web2.oxt | en-US-region1 | en-US-region1
+---------------------------------------------
+web3.oxt | en | en
+---------------------------------------------
+web4.oxt | en-GB | en-GB
+---------------------------------------------
+web5.oxt | de | de
+
+
+================================================================================
+web6.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop.
+
+web6.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+Test
+----
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. In our case this should be
+ http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
+
+-Create a new release for the extension. Upload the description.xml of version 2.0
+(update/web6/description.xml). Provide a download URL for the web site (field
+ "Download from page / Open follow up page URL", which should be
+ http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
+ Provide release notes.
+
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/website_update/web6.oxt
+
+-Run the update in the Extension Manager
+
+
+Result:
+The Update Dialog should show the publisher name as provided in "Provider URL" field
+of the extension edit page (not release).
+
+A release notes link is displayed with a URL to the release notes as provided in
+the "Provider Title" field of the extension release edit page.
+
+When running the update then the web browser should navigate to
+http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
+
+
+================================================================================
+web7.oxt
+================================================================================
+Need not be committed in extensions/www/testarea/desktop
+
+web7.oxt is intended for tests with extensions.services.openoffice.org.
+It does not contain <update-information>. That is, the Extension Manager will obtain
+the update information from the repository as feed:
+
+http://updateext.services.openoffice.org/ProductUpdateService/check.Update
+
+The description.xml which will be uploaded contains URLs for release notes and publisher
+names/ URLs. That is, this information is not generated from the information of the
+repository web site.
+
+Test
+-----------
+Repository:
+
+-Create the new extension in the repository.
+-Provide a company name and a URL to the company website. In our case these should be different
+ to those provided in the description.xml. These should NOT go into the update feed.
+ Choose for example as "Provider Title": FOO and as "Provider URL" some valid URL but NOT
+ http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
+
+-Create a new release for the extension. Upload the description.xml of version 2.0
+(update/web7/description.xml). Provide a download URL for the web site (field
+ "Download from page / Open follow up page URL", which should be
+ http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
+ Provide release notes.
+
+Office:
+-Install version 1.0 of the extension:
+ desktop/test/deployment/update/website_update/web7.oxt
+
+-Run the update in the Extension Manager
+
+Result:
+The Update Dialog should show the publisher name as provided in the description.xml.
+That is: My OpenOffice en-US and NOT "FOO".
+
+A release notes link is displayed with a URL to the release notes as provided in
+the description.xml. That is:
+http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_xxx.html
+
+When running the update then the web browser should navigate to
+http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html
diff --git a/desktop/test/deployment/update/website_update/update/web1.oxt b/desktop/test/deployment/update/website_update/update/web1.oxt
new file mode 100644
index 000000000..157d5d952
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web1.update.xml b/desktop/test/deployment/update/website_update/update/web1.update.xml
new file mode 100644
index 000000000..860c8ebc9
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1.update.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web1"/>
+
+ <update-website>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US.html" lang="en-US" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-region3.html" lang="en-region3" />
+ </update-website>
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_de-DE-altmark.html b/desktop/test/deployment/update/website_update/update/web1_de-DE-altmark.html
new file mode 100644
index 000000000..ffed5a52e
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_de-DE-altmark.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>de-DE-altmark</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_de-DE.html b/desktop/test/deployment/update/website_update/update/web1_de-DE.html
new file mode 100644
index 000000000..33fb7f2ec
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_de-DE.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>de-DE</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_de.html b/desktop/test/deployment/update/website_update/update/web1_de.html
new file mode 100644
index 000000000..31a53b91d
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_de.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>de</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en-GB.html b/desktop/test/deployment/update/website_update/update/web1_en-GB.html
new file mode 100644
index 000000000..c46328a82
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en-GB.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en-GB</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en-US-region1.html b/desktop/test/deployment/update/website_update/update/web1_en-US-region1.html
new file mode 100644
index 000000000..80b41823b
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en-US-region1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en-US-region1</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en-US-region2.html b/desktop/test/deployment/update/website_update/update/web1_en-US-region2.html
new file mode 100644
index 000000000..1a501f520
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en-US-region2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en-US-region2</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en-US.html b/desktop/test/deployment/update/website_update/update/web1_en-US.html
new file mode 100644
index 000000000..f861b09c0
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en-US.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en-US</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en-region3.html b/desktop/test/deployment/update/website_update/update/web1_en-region3.html
new file mode 100644
index 000000000..f55bcbe38
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en-region3.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en-region3</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web1_en.html b/desktop/test/deployment/update/website_update/update/web1_en.html
new file mode 100644
index 000000000..a0b422ebf
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web1_en.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+</HEAD>
+<BODY>
+<H1>Update Extensions</H1>
+<H1>en</H1>
+<p><a href="web1.oxt">web1.oxt</a></p>
+<p><a href="web2.oxt">web2.oxt</a></p>
+<p><a href="web3.oxt">web3.oxt</a></p>
+<p><a href="web4.oxt">web4.oxt</a></p>
+<p><a href="web5.oxt">web5.oxt</a></p>
+<p><a href="web6.oxt">web6.oxt</a></p>
+<p><a href="web7.oxt">web7.oxt</a></p>
+
+
+</BODY>
+</HTML>
+
diff --git a/desktop/test/deployment/update/website_update/update/web2.oxt b/desktop/test/deployment/update/website_update/update/web2.oxt
new file mode 100644
index 000000000..3a13e8114
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web2.update.xml b/desktop/test/deployment/update/website_update/update/web2.update.xml
new file mode 100644
index 000000000..18c871eab
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web2.update.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web2"/>
+
+ <update-website>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-region3.html" lang="en-region3" />
+ </update-website>
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web3.oxt b/desktop/test/deployment/update/website_update/update/web3.oxt
new file mode 100644
index 000000000..b3214a4e6
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web3.update.xml b/desktop/test/deployment/update/website_update/update/web3.update.xml
new file mode 100644
index 000000000..5d7711def
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web3.update.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web3"/>
+
+ <update-website>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-region3.html" lang="en-region3" />
+ </update-website>
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web4.oxt b/desktop/test/deployment/update/website_update/update/web4.oxt
new file mode 100644
index 000000000..93766fd44
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web4.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web4.update.xml b/desktop/test/deployment/update/website_update/update/web4.update.xml
new file mode 100644
index 000000000..e016ae3cb
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web4.update.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web4"/>
+
+ <update-website>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_en-region3.html" lang="en-region3" />
+ </update-website>
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web5.oxt b/desktop/test/deployment/update/website_update/update/web5.oxt
new file mode 100644
index 000000000..1ae8f01b1
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web5.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web5.update.xml b/desktop/test/deployment/update/website_update/update/web5.update.xml
new file mode 100644
index 000000000..951ef95a9
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web5.update.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web5"/>
+
+ <update-website>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/website_update/update/web1_de-DE-altmark.html" lang="de-DE-altmark" />
+ </update-website>
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web6.oxt b/desktop/test/deployment/update/website_update/update/web6.oxt
new file mode 100644
index 000000000..8bc16fb2c
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web6.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web6/description.xml b/desktop/test/deployment/update/website_update/update/web6/description.xml
new file mode 100644
index 000000000..47a32a287
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web6/description.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web6"/>
+ <display-name>
+ <name>web-based update test 1</name>
+ </display-name>
+
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web6/readme.txt b/desktop/test/deployment/update/website_update/update/web6/readme.txt
new file mode 100644
index 000000000..6756b8ef9
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web6/readme.txt
@@ -0,0 +1,5 @@
+This folder contains the description.xml from update/web6.oxt
+When creating the release 2.0 on the repository then this description.xml can be uploaded.
+
+
+This folder is not needed on extensions.openoffice.org/testarea/desktop/...
diff --git a/desktop/test/deployment/update/website_update/update/web7.oxt b/desktop/test/deployment/update/website_update/update/web7.oxt
new file mode 100644
index 000000000..4d6220a48
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web7.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/update/web7/description.xml b/desktop/test/deployment/update/website_update/update/web7/description.xml
new file mode 100644
index 000000000..9d5e4569d
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web7/description.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/description/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <version value="2.0" />
+ <identifier value="org.openoffice/framework/desktop/test/deployment/update/website_update/web7"/>
+ <display-name>
+ <name>web-based update test 1</name>
+ </display-name>
+
+ <publisher>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de.html" lang="de">My OpenOffice de</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en.html" lang="en">My OpenOffice en</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE.html" lang="de-DE">My OpenOffice de-DE</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_de-DE-altmark.html" lang="de-DE-altmark">My OpenOffice de-DE-altmark</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-GB.html" lang="en-GB">My OpenOffice en-GB</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US.html" lang="en-US">My OpenOffice en-US</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region1.html" lang="en-US-region1">My OpenOffice en-US-region1</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-US-region2.html" lang="en-US-region2">My OpenOffice en-US-region2</name>
+ <name xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/publisher_en-region3.html" lang="en-region3">My OpenOffice en-region3</name>
+ </publisher>
+
+ <release-notes>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de.html" lang="de" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en.html" lang="en" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE.html" lang="de-DE" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_de-DE-altmark.html" lang="de-DE-altmark" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-GB.html" lang="en-GB" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US.html" lang="en-US" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region1.html" lang="en-US-region1" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-US-region2.html" lang="en-US-region2" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/publisher/release-notes_en-region3.html" lang="en-region3" />
+ </release-notes>
+
+
+</description>
+
diff --git a/desktop/test/deployment/update/website_update/update/web7/readme.txt b/desktop/test/deployment/update/website_update/update/web7/readme.txt
new file mode 100644
index 000000000..45b2e2374
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/update/web7/readme.txt
@@ -0,0 +1,5 @@
+This folder contains the description.xml from update/web7.oxt
+When creating the release 2.0 on the repository then this description.xml can be uploaded.
+
+
+This folder is not needed on extensions.openoffice.org/testarea/desktop/...
diff --git a/desktop/test/deployment/update/website_update/web1.oxt b/desktop/test/deployment/update/website_update/web1.oxt
new file mode 100644
index 000000000..7c17586e0
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web2.oxt b/desktop/test/deployment/update/website_update/web2.oxt
new file mode 100644
index 000000000..705e70a75
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web3.oxt b/desktop/test/deployment/update/website_update/web3.oxt
new file mode 100644
index 000000000..4e63a75f0
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web4.oxt b/desktop/test/deployment/update/website_update/web4.oxt
new file mode 100644
index 000000000..e66513e68
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web4.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web5.oxt b/desktop/test/deployment/update/website_update/web5.oxt
new file mode 100644
index 000000000..65b02db93
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web5.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web6.oxt b/desktop/test/deployment/update/website_update/web6.oxt
new file mode 100644
index 000000000..98416edfa
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web6.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/website_update/web7.oxt b/desktop/test/deployment/update/website_update/web7.oxt
new file mode 100644
index 000000000..31ba45f03
--- /dev/null
+++ b/desktop/test/deployment/update/website_update/web7.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/readme.txt b/desktop/test/deployment/update/wrong_url/readme.txt
new file mode 100644
index 000000000..9e3bf8b96
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/readme.txt
@@ -0,0 +1,18 @@
+The extensions use either multiple urls to the update information file, or the update information file contains multiple urls. Some of those URLs point to locations which do not exist.
+
+url1.oxt: The corresponding url1.update.xml contains two download urls. The first Url points to a location that is not available. The second points to the new version.
+
+url2.oxt: Contains to URLs to update information files. The first URL in url2.oxt is wrong and the second is
+correct.
+
+url3.oxt: contains to URLs to update information files which point to locations which do not exist.
+
+wrongdownload1.oxt: The corresponding wrongdownload1.update.xml contains two download URLs which point to locations which are not available.
+
+wrongdownload2.oxt: same as wrongdownload1.oxt
+
+wrongdownload3.oxt: same as wrongdownload1.oxt
+
+
+Use the wrongdownload extensions to check the automatic scrolling of the text area that contains the results.
+
diff --git a/desktop/test/deployment/update/wrong_url/update/url1.oxt b/desktop/test/deployment/update/wrong_url/update/url1.oxt
new file mode 100644
index 000000000..479b546c8
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/url1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/update/url1.update.xml b/desktop/test/deployment/update/wrong_url/update/url1.update.xml
new file mode 100644
index 000000000..e6980d558
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/url1.update.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.url1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/notavailable/url1.oxt" />
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/update/url1.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/wrong_url/update/url2.oxt b/desktop/test/deployment/update/wrong_url/update/url2.oxt
new file mode 100644
index 000000000..ec2c5c652
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/url2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/update/url2.update.xml b/desktop/test/deployment/update/wrong_url/update/url2.update.xml
new file mode 100644
index 000000000..3ced2f9d2
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/url2.update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.url2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/update/url2.oxt" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/wrong_url/update/wrongdownload1.update.xml b/desktop/test/deployment/update/wrong_url/update/wrongdownload1.update.xml
new file mode 100644
index 000000000..916036e03
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/wrongdownload1.update.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.wrongdownload1.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/notavailable/wrongdownload1.oxt" />
+ <src xlink:href="http://extensions.openoffice.org/notavailable/" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/wrong_url/update/wrongdownload2.update.xml b/desktop/test/deployment/update/wrong_url/update/wrongdownload2.update.xml
new file mode 100644
index 000000000..36b24ed19
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/wrongdownload2.update.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.wrongdownload2.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/notavailable/wrongdownload2.oxt" />
+ <src xlink:href="http://extensions.openoffice.org/notavailable/" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/wrong_url/update/wrongdownload3.update.xml b/desktop/test/deployment/update/wrong_url/update/wrongdownload3.update.xml
new file mode 100644
index 000000000..f5a2bb8b1
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/update/wrongdownload3.update.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<description xmlns="http://openoffice.org/extensions/update/2006"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <identifier value="org.openoffice.legacy.wrongdownload3.oxt"/>
+ <version value="2.0" />
+ <update-download>
+ <src xlink:href="http://extensions.openoffice.org/testarea/desktop/wrong_url/notavailable/wrongdownload3.oxt" />
+ <src xlink:href="http://extensions.openoffice.org/notavailable/" />
+ </update-download>
+</description>
+
diff --git a/desktop/test/deployment/update/wrong_url/url1.oxt b/desktop/test/deployment/update/wrong_url/url1.oxt
new file mode 100644
index 000000000..41d8522fb
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/url1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/url2.oxt b/desktop/test/deployment/update/wrong_url/url2.oxt
new file mode 100644
index 000000000..d68e45e5e
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/url2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/url3.oxt b/desktop/test/deployment/update/wrong_url/url3.oxt
new file mode 100644
index 000000000..80f93b74d
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/url3.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/wrongdownload1.oxt b/desktop/test/deployment/update/wrong_url/wrongdownload1.oxt
new file mode 100644
index 000000000..535ae331a
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/wrongdownload1.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/wrongdownload2.oxt b/desktop/test/deployment/update/wrong_url/wrongdownload2.oxt
new file mode 100644
index 000000000..aafe2c246
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/wrongdownload2.oxt
Binary files differ
diff --git a/desktop/test/deployment/update/wrong_url/wrongdownload3.oxt b/desktop/test/deployment/update/wrong_url/wrongdownload3.oxt
new file mode 100644
index 000000000..fbdac925a
--- /dev/null
+++ b/desktop/test/deployment/update/wrong_url/wrongdownload3.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/readme.txt b/desktop/test/deployment/version/readme.txt
new file mode 100644
index 000000000..6135d08a2
--- /dev/null
+++ b/desktop/test/deployment/version/readme.txt
@@ -0,0 +1,76 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+There are three extensions in various versions:
+
+1 version_XXX/plain.oxt has no dependencies and no license.
+2a version_XXX/dependency.oxt has an unsatisfied dependency and no license.
+2b version_nodependencies_XXX/dependency.oxt is identical to 2a but without the
+ dependency.
+3 version_XXX/license.oxt has no dependencies and a license.
+
+The different versions are:
+
+A version_none contains no version element (treated as version "0").
+B version_badelement contains a bad <version val="1"/> (not allowed by the
+ specification, but treated by the current implementation as version "0").
+C version_badvalue contains a bad <version value="1.a"/> (not allowed by the
+ specification, but treated by the current implementation as version "1").
+D version_0.0 contains <version value="0.0"/> (same as version "0").
+E version_1.2.3 contains <version value="1.2.3"/>.
+F version_1.2.4.7 contains <version value="1.2.4.7"/>.
+G version_1.02.4.7.0 contains <version value="1.02.4.7.0"/> (same as version
+ "1.2.4.7").
+H version_1.2.15.3 contains <version value="1.2.15.3"/>.
+
+The total order among the various versions is thus
+
+ A = B = D < C < E < F = G < H.
+
+Things to watch for:
+
+- If version y of extension e is to be installed and version x < y of
+ extension e is already installed, then
+ unopkg add e
+ will replace x with y.
+
+- If version y of extension e is to be installed and version x >= y of
+ extension e is already installed, then
+ unopkg add e
+ will fail with an error message.
+
+- If version y of extension e is to be installed and any version x of
+ extension e is already installed, then
+ unopkg add -f e
+ will replace x with y.
+
+- If version y of extension e is to be installed and any version x of
+ extension e is already installed, then
+ unopkg gui "Add..."
+ and
+ soffice "Tools - Package Manager... - Add..."
+ will query with a dialog whether to replace x with y. The dialog will have
+ "OK" (replace) preselected if x < y, and "Cancel" otherwise.
+
+- If replacing an installed version x of an extension e with a version y fails
+ because y has unsatisfied dependencies, or because y has a license to which the
+ user does not agree, version x is left installed afterwards.
+
+- Checking for already installed versions of an extension is only done within a
+ single layer (unopkg versus unopkg --shared; "My Packages" versus
+ "OpenOffice Packages" in unopkg gui/soffice), not across layers.
diff --git a/desktop/test/deployment/version/version_0.0/dependency.oxt b/desktop/test/deployment/version/version_0.0/dependency.oxt
new file mode 100644
index 000000000..30c843225
--- /dev/null
+++ b/desktop/test/deployment/version/version_0.0/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_0.0/license.oxt b/desktop/test/deployment/version/version_0.0/license.oxt
new file mode 100644
index 000000000..b994ff71b
--- /dev/null
+++ b/desktop/test/deployment/version/version_0.0/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_0.0/plain.oxt b/desktop/test/deployment/version/version_0.0/plain.oxt
new file mode 100644
index 000000000..f156014eb
--- /dev/null
+++ b/desktop/test/deployment/version/version_0.0/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.02.4.7.0/dependency.oxt b/desktop/test/deployment/version/version_1.02.4.7.0/dependency.oxt
new file mode 100644
index 000000000..4d75f7076
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.02.4.7.0/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.02.4.7.0/license.oxt b/desktop/test/deployment/version/version_1.02.4.7.0/license.oxt
new file mode 100644
index 000000000..40938b754
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.02.4.7.0/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.02.4.7.0/plain.oxt b/desktop/test/deployment/version/version_1.02.4.7.0/plain.oxt
new file mode 100644
index 000000000..521a2b6c7
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.02.4.7.0/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.15.3/dependency.oxt b/desktop/test/deployment/version/version_1.2.15.3/dependency.oxt
new file mode 100644
index 000000000..6f2a301f3
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.15.3/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.15.3/license.oxt b/desktop/test/deployment/version/version_1.2.15.3/license.oxt
new file mode 100644
index 000000000..2e2a87575
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.15.3/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.15.3/plain.oxt b/desktop/test/deployment/version/version_1.2.15.3/plain.oxt
new file mode 100644
index 000000000..000f3a144
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.15.3/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.3/dependency.oxt b/desktop/test/deployment/version/version_1.2.3/dependency.oxt
new file mode 100644
index 000000000..c29663458
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.3/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.3/license.oxt b/desktop/test/deployment/version/version_1.2.3/license.oxt
new file mode 100644
index 000000000..9cd80e991
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.3/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.3/plain.oxt b/desktop/test/deployment/version/version_1.2.3/plain.oxt
new file mode 100644
index 000000000..e34264591
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.3/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.4.7/dependency.oxt b/desktop/test/deployment/version/version_1.2.4.7/dependency.oxt
new file mode 100644
index 000000000..53089e76b
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.4.7/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.4.7/license.oxt b/desktop/test/deployment/version/version_1.2.4.7/license.oxt
new file mode 100644
index 000000000..e283508d3
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.4.7/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_1.2.4.7/plain.oxt b/desktop/test/deployment/version/version_1.2.4.7/plain.oxt
new file mode 100644
index 000000000..d63c79a73
--- /dev/null
+++ b/desktop/test/deployment/version/version_1.2.4.7/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badelement/dependency.oxt b/desktop/test/deployment/version/version_badelement/dependency.oxt
new file mode 100644
index 000000000..3cb8faa2e
--- /dev/null
+++ b/desktop/test/deployment/version/version_badelement/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badelement/license.oxt b/desktop/test/deployment/version/version_badelement/license.oxt
new file mode 100644
index 000000000..7b2b7730e
--- /dev/null
+++ b/desktop/test/deployment/version/version_badelement/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badelement/plain.oxt b/desktop/test/deployment/version/version_badelement/plain.oxt
new file mode 100644
index 000000000..62267c212
--- /dev/null
+++ b/desktop/test/deployment/version/version_badelement/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badvalue/dependency.oxt b/desktop/test/deployment/version/version_badvalue/dependency.oxt
new file mode 100644
index 000000000..7d8103365
--- /dev/null
+++ b/desktop/test/deployment/version/version_badvalue/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badvalue/license.oxt b/desktop/test/deployment/version/version_badvalue/license.oxt
new file mode 100644
index 000000000..b97723ebb
--- /dev/null
+++ b/desktop/test/deployment/version/version_badvalue/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_badvalue/plain.oxt b/desktop/test/deployment/version/version_badvalue/plain.oxt
new file mode 100644
index 000000000..f9964ed8f
--- /dev/null
+++ b/desktop/test/deployment/version/version_badvalue/plain.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_0.0/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_0.0/dependency.oxt
new file mode 100644
index 000000000..f156014eb
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_0.0/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_1.02.4.7.0/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_1.02.4.7.0/dependency.oxt
new file mode 100644
index 000000000..521a2b6c7
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_1.02.4.7.0/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_1.2.15.3/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_1.2.15.3/dependency.oxt
new file mode 100644
index 000000000..000f3a144
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_1.2.15.3/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_1.2.3/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_1.2.3/dependency.oxt
new file mode 100644
index 000000000..e34264591
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_1.2.3/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_1.2.4.7/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_1.2.4.7/dependency.oxt
new file mode 100644
index 000000000..d63c79a73
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_1.2.4.7/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_badelement/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_badelement/dependency.oxt
new file mode 100644
index 000000000..62267c212
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_badelement/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_badvalue/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_badvalue/dependency.oxt
new file mode 100644
index 000000000..f9964ed8f
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_badvalue/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_nodependencies_none/dependency.oxt b/desktop/test/deployment/version/version_nodependencies_none/dependency.oxt
new file mode 100644
index 000000000..fc227b099
--- /dev/null
+++ b/desktop/test/deployment/version/version_nodependencies_none/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_none/dependency.oxt b/desktop/test/deployment/version/version_none/dependency.oxt
new file mode 100644
index 000000000..36a1854bf
--- /dev/null
+++ b/desktop/test/deployment/version/version_none/dependency.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_none/license.oxt b/desktop/test/deployment/version/version_none/license.oxt
new file mode 100644
index 000000000..1564c089b
--- /dev/null
+++ b/desktop/test/deployment/version/version_none/license.oxt
Binary files differ
diff --git a/desktop/test/deployment/version/version_none/plain.oxt b/desktop/test/deployment/version/version_none/plain.oxt
new file mode 100644
index 000000000..fc227b099
--- /dev/null
+++ b/desktop/test/deployment/version/version_none/plain.oxt
Binary files differ
diff --git a/desktop/uiconfig/ui/dependenciesdialog.ui b/desktop/uiconfig/ui/dependenciesdialog.ui
new file mode 100644
index 000000000..c78a33886
--- /dev/null
+++ b/desktop/uiconfig/ui/dependenciesdialog.ui
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="Dependencies">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="dependenciesdialog|Dependencies">System dependencies check</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">center</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="dependenciesdialog|label1">The extension cannot be installed as the following system dependencies are not fulfilled:</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">60</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="depListTreeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn5">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/extensionmanager.ui b/desktop/uiconfig/ui/extensionmanager.ui
new file mode 100644
index 000000000..be2ca3e37
--- /dev/null
+++ b/desktop/uiconfig/ui/extensionmanager.ui
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="ExtensionManagerDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="extensionmanager|ExtensionManagerDialog">Extension Manager</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="shared">
+ <property name="label" translatable="yes" context="extensionmanager|shared">Installed for all users</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="user">
+ <property name="label" translatable="yes" context="extensionmanager|user">Installed for current user</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="bundled">
+ <property name="label" translatable="yes" context="extensionmanager|bundled">Bundled with %PRODUCTNAME</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="extensionmanager|label1">Display Extensions</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="extensions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="optionsbtn">
+ <property name="label" translatable="yes" context="extensionmanager|optionsbtn">_Options</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="updatebtn">
+ <property name="label" translatable="yes" context="extensionmanager|updatebtn">Check for _Updates</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="addbtn">
+ <property name="label" translatable="yes" context="extensionmanager|addbtn">_Add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="removebtn">
+ <property name="label" translatable="yes" context="extensionmanager|removebtn">_Remove</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="enablebtn">
+ <property name="label" translatable="yes" context="extensionmanager|enablebtn">_Enable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="progressft">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes" context="extensionmanager|progressft">Adding %EXTENSION_NAME</property>
+ <property name="justify">right</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progressbar">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="getextensions">
+ <property name="label" translatable="yes" context="extensionmanager|getextensions">Get more extensions online...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-6">close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/extensionmenu.ui b/desktop/uiconfig/ui/extensionmenu.ui
new file mode 100644
index 000000000..da6c2f870
--- /dev/null
+++ b/desktop/uiconfig/ui/extensionmenu.ui
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkMenu" id="menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/installforalldialog.ui b/desktop/uiconfig/ui/installforalldialog.ui
new file mode 100644
index 000000000..e2bf2696a
--- /dev/null
+++ b/desktop/uiconfig/ui/installforalldialog.ui
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.16.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkMessageDialog" id="InstallForAllDialog">
+ <property name="can_focus">False</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">question</property>
+ <property name="text" translatable="yes" context="installforalldialog|InstallForAllDialog">For whom do you want to install the extension?</property>
+ <property name="secondary_text" translatable="yes" context="installforalldialog|InstallForAllDialog">Make sure that no further users are working with the same %PRODUCTNAME, when installing an extension for all users in a multi user environment.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="messagedialog-vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="messagedialog-action_area">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="no">
+ <property name="label" translatable="yes" context="installforalldialog|no">_For all users</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="yes">
+ <property name="label" translatable="yes" context="installforalldialog|yes">_Only for me</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-9">no</action-widget>
+ <action-widget response="-8">yes</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/licensedialog.ui b/desktop/uiconfig/ui/licensedialog.ui
new file mode 100644
index 000000000..bb139219e
--- /dev/null
+++ b/desktop/uiconfig/ui/licensedialog.ui
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="LicenseDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="licensedialog|LicenseDialog">Extension Software License Agreement</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="licensedialog|accept">Accept</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="licensedialog|decline">Decline</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="head">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="licensedialog|head">Please follow these steps to proceed with the installation of the extension:</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes" context="licensedialog|label2">1.</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes" context="licensedialog|label3">2.</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="arrow1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="icon_name">res/sc06300.png</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="arrow2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="icon_name">res/sc06300.png</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes" context="licensedialog|label4">Read the complete License Agreement. Use the scroll bar or the 'Scroll Down' button in this dialog to view the entire license text.</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">55</property>
+ <property name="max_width_chars">55</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes" context="licensedialog|label5">Accept the License Agreement for the extension by pressing the 'Accept' button.</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">55</property>
+ <property name="max_width_chars">55</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="down">
+ <property name="label" translatable="yes" context="licensedialog|down">_Scroll Down</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="valign">start</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="editable">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/showlicensedialog.ui b/desktop/uiconfig/ui/showlicensedialog.ui
new file mode 100644
index 000000000..6039c59fb
--- /dev/null
+++ b/desktop/uiconfig/ui/showlicensedialog.ui
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="ShowLicenseDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="showlicensedialog|ShowLicenseDialog">Extension Software License Agreement</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="editable">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/updatedialog.ui b/desktop/uiconfig/ui/updatedialog.ui
new file mode 100644
index 000000000..25ee24c01
--- /dev/null
+++ b/desktop/uiconfig/ui/updatedialog.ui
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="UpdateDialog">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="updatedialog|UpdateDialog">Extension Update</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="updatedialog|INSTALL">_Install</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="UPDATE_LABEL">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes" context="updatedialog|UPDATE_LABEL">_Available extension updates</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="UPDATE_CHECKING">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="updatedialog|UPDATE_CHECKING">Checking...</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="THROBBER">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="checklistwin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="checklist">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="alignment">0.5</property>
+ <child>
+ <object class="GtkCellRendererToggle" id="cellrenderer5"/>
+ <attributes>
+ <attribute name="visible">3</attribute>
+ <attribute name="active">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn5">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer4"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="UPDATE_ALL">
+ <property name="label" translatable="yes" context="updatedialog|UPDATE_ALL">_Show all updates</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="DESCRIPTION_LABEL">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="updatedialog|DESCRIPTION_LABEL">Description</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="PUBLISHER_LABEL">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="updatedialog|PUBLISHER_LABEL">Publisher:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">PUBLISHER_LINK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="PUBLISHER_LINK">
+ <property name="label" translatable="yes" context="updatedialog|PUBLISHER_LINK">button</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="RELEASE_NOTES_LABEL">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="updatedialog|RELEASE_NOTES_LABEL">What is new:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">RELEASE_NOTES_LINK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="RELEASE_NOTES_LINK">
+ <property name="label" translatable="yes" context="updatedialog|RELEASE_NOTES_LINK">Release notes</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="DESCRIPTIONSWIN">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="DESCRIPTIONS">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="cursor_visible">False</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name check1 -->
+ <column type="gboolean"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name checkvis1 -->
+ <column type="gboolean"/>
+ <!-- column-name checktri1 -->
+ <column type="gboolean"/>
+ </columns>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/updateinstalldialog.ui b/desktop/uiconfig/ui/updateinstalldialog.ui
new file mode 100644
index 000000000..973c28528
--- /dev/null
+++ b/desktop/uiconfig/ui/updateinstalldialog.ui
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="UpdateInstallDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="updateinstalldialog|UpdateInstallDialog">Download and Installation</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="DOWNLOADING">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="updateinstalldialog|DOWNLOADING">Downloading extensions...</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">STATUSBAR</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="STATUSBAR">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="EXTENSION_NAME">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="RESULTS">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="updateinstalldialog|RESULTS">Result</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">INFO</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="INFO">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="editable">False</property>
+ <property name="cursor_visible">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/uiconfig/ui/updaterequireddialog.ui b/desktop/uiconfig/ui/updaterequireddialog.ui
new file mode 100644
index 000000000..4e7b9921b
--- /dev/null
+++ b/desktop/uiconfig/ui/updaterequireddialog.ui
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="dkt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkDialog" id="UpdateRequiredDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes" context="updaterequireddialog|UpdateRequiredDialog">Extension Update Required</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="updaterequireddialog|check">Check for _Updates...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="disable">
+ <property name="label" translatable="yes" context="updaterequireddialog|disable">Disable all</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="updatelabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="updaterequireddialog|updatelabel">%PRODUCTNAME has been updated to a new version. Some installed %PRODUCTNAME extensions are not compatible with this version and need to be updated before they can be used.</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">95</property>
+ <property name="max_width_chars">95</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="extensions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="progresslabel">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes" context="updaterequireddialog|progresslabel">Adding %EXTENSION_NAME</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progress">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="101">ok</action-widget>
+ <action-widget response="102">disable</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/desktop/unx/source/args.c b/desktop/unx/source/args.c
new file mode 100644
index 000000000..199b58a8e
--- /dev/null
+++ b/desktop/unx/source/args.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <osl/process.h>
+
+#include "args.h"
+
+/* do we start -env: */
+static int
+is_env_arg (rtl_uString const *str)
+{
+ return !rtl_ustr_ascii_compare_WithLength (str->buffer, 5, "-env:");
+}
+
+static const struct {
+ const char *name;
+ unsigned int bInhibitSplash : 1;
+ unsigned int bInhibitPagein : 1;
+ unsigned int bInhibitJavaLdx : 1;
+ unsigned int bInhibitPipe : 1;
+ const char *pPageinType;
+} pArgDescr[] = {
+ /* have a trailing argument */
+ { "pt", 1, 0, 0, 0, NULL },
+ { "p", 1, 0, 0, 0, NULL },
+ { "display", 0, 0, 0, 0, NULL },
+
+ /* no splash */
+ { "nologo", 1, 0, 0, 0, NULL },
+ { "headless", 1, 0, 0, 0, NULL },
+ { "invisible", 1, 0, 0, 0, NULL },
+ { "quickstart", 1, 0, 0, 0, NULL },
+ { "minimized", 1, 0, 0, 0, NULL },
+ { "convert-to", 1, 0, 0, 0, NULL },
+ { "cat", 1, 0, 0, 0, NULL },
+
+ /* pagein bits */
+ { "writer", 0, 0, 0, 0, "pagein-writer" },
+ { "calc", 0, 0, 0, 0, "pagein-calc" },
+ { "draw", 0, 0, 0, 0, "pagein-draw" },
+ { "impress", 0, 0, 0, 0, "pagein-impress" },
+
+ /* Do not send --help/--version over the pipe, as their output shall go to
+ the calling process's stdout (ideally, this would also happen in the
+ presence of unknown options); also prevent splash/pagein/javaldx overhead
+ (as these options will be processed early in soffice_main): */
+ { "version", 1, 1, 1, 1, NULL },
+ { "help", 1, 1, 1, 1, NULL },
+ { "h", 1, 1, 1, 1, NULL },
+ { "?", 1, 1, 1, 1, NULL },
+};
+
+Args *args_parse (void)
+{
+ Args *args;
+ sal_uInt32 nArgs, i, j;
+
+ nArgs = osl_getCommandArgCount();
+ i = sizeof (Args) + sizeof (rtl_uString *) * nArgs;
+ args = malloc (i);
+ memset (args, 0, i);
+ args->nArgsTotal = nArgs;
+
+ j = 0;
+
+ /* sort the -env: args to the front */
+ for ( i = 0; i < nArgs; ++i )
+ {
+ rtl_uString *pTmp = NULL;
+ osl_getCommandArg( i, &pTmp );
+ if (is_env_arg (pTmp))
+ args->ppArgs[j++] = pTmp;
+ else
+ rtl_uString_release (pTmp);
+ }
+ args->nArgsEnv = j;
+
+ /* Then the other args */
+ for ( i = 0; i < nArgs; ++i )
+ {
+ rtl_uString *pTmp = NULL;
+
+ osl_getCommandArg( i, &pTmp );
+ if (!is_env_arg (pTmp))
+ args->ppArgs[j++] = pTmp;
+ else
+ rtl_uString_release (pTmp);
+ }
+
+ for ( i = args->nArgsEnv; i < args->nArgsTotal; i++ )
+ {
+ const sal_Unicode *arg = args->ppArgs[i]->buffer;
+ sal_Int32 length = args->ppArgs[i]->length;
+
+ /* grok only parameters */
+ if (arg[0] != '-')
+ continue;
+
+ while (length > 1 && arg[0] == '-') {
+ arg++;
+ length--;
+ }
+
+ for ( j = 0; j < SAL_N_ELEMENTS (pArgDescr); ++j ) {
+ if (rtl_ustr_ascii_compare_WithLength(
+ arg, length, pArgDescr[j].name)
+ == 0)
+ {
+ args->bInhibitSplash |= pArgDescr[j].bInhibitSplash;
+ args->bInhibitPagein |= pArgDescr[j].bInhibitPagein;
+ args->bInhibitJavaLdx |= pArgDescr[j].bInhibitJavaLdx;
+ args->bInhibitPipe |= pArgDescr[j].bInhibitPipe;
+ if (pArgDescr[j].pPageinType)
+ args->pPageinType = pArgDescr[j].pPageinType;
+ break;
+ }
+ }
+ }
+
+ return args;
+}
+
+void
+args_free (Args *args)
+{
+ /* FIXME: free ppArgs */
+ rtl_uString_release( args->pAppPath );
+ free (args);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/args.h b/desktop/unx/source/args.h
new file mode 100644
index 000000000..f0fe7ce39
--- /dev/null
+++ b/desktop/unx/source/args.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include <sal/types.h>
+#include <rtl/ustring.h>
+
+typedef struct {
+ rtl_uString *pAppPath;
+ const char *pPageinType; // @pagein-writer for - writer etc. else NULL
+ sal_Bool bInhibitSplash; // should we show a splash screen
+ sal_Bool bInhibitPagein; // should we run pagein ?
+ sal_Bool bInhibitJavaLdx; // should we run javaldx ?
+ sal_Bool bInhibitPipe; // for --help and --version
+
+ sal_uInt32 nArgsEnv; // number of -env: style args
+ sal_uInt32 nArgsTotal; // number of -env: as well as -writer style args
+ rtl_uString *ppArgs[1]; // sorted argument array
+} Args;
+
+Args *args_parse (void);
+void args_free (Args *args);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/file_image.h b/desktop/unx/source/file_image.h
new file mode 100644
index 000000000..60fdc8e96
--- /dev/null
+++ b/desktop/unx/source/file_image.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_UNX_SOURCE_FILE_IMAGE_H
+#define INCLUDED_DESKTOP_UNX_SOURCE_FILE_IMAGE_H
+
+#ifndef INCLUDED_STDDEF_H
+#include <stddef.h>
+#define INCLUDED_STDDEF_H
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** file_image.
+ */
+struct file_image_st
+{
+ void * m_base;
+ size_t m_size;
+};
+
+typedef struct file_image_st file_image;
+
+#define FILE_IMAGE_INITIALIZER { NULL, 0 }
+
+
+/** file_image_open.
+ */
+int file_image_open (
+ file_image * image,
+ const char * filename);
+
+
+/** file_image_pagein.
+ */
+int file_image_pagein (
+ file_image * image);
+
+
+/** file_image_close.
+ */
+int file_image_close (
+ file_image * image);
+
+
+/** Epilog.
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_ODEP_IMAGE_H */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/file_image_unx.c b/desktop/unx/source/file_image_unx.c
new file mode 100644
index 000000000..ec229f95f
--- /dev/null
+++ b/desktop/unx/source/file_image_unx.c
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "file_image.h"
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <string.h>
+
+/*
+ * file_image_open
+ */
+int file_image_open (file_image * image, const char * filename)
+{
+ int result = 0;
+ int fd;
+ struct stat st;
+ void * p;
+
+ if (image == NULL)
+ return EINVAL;
+
+ image->m_base = MAP_FAILED;
+ image->m_size = 0;
+
+ if ((fd = open (filename, O_RDONLY)) == -1)
+ return errno;
+
+ if (fstat (fd, &st) == -1)
+ {
+ result = errno;
+ goto cleanup_and_leave;
+ }
+
+ p = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (p == MAP_FAILED)
+ {
+ result = errno;
+ goto cleanup_and_leave;
+ }
+
+ image->m_base = p;
+ image->m_size = st.st_size;
+
+cleanup_and_leave:
+ close (fd);
+ return result;
+}
+
+/*
+ * file_image_pagein.
+ */
+int file_image_pagein (file_image * image)
+{
+ long s = -1;
+ volatile char c =0;
+ size_t idx;
+
+ if (image == NULL)
+ return EINVAL;
+ if (image->m_base == NULL)
+ return EINVAL;
+ if (image->m_size == 0)
+ return 0;
+
+ if (madvise (image->m_base, image->m_size, MADV_WILLNEED) == -1)
+ return errno;
+
+ s = sysconf (_SC_PAGESIZE);
+ if (s == -1)
+ s = 0x1000;
+ // force touching of each page despite the optimizer
+ for(idx = 0; idx < image->m_size; idx += (size_t)s)
+ {
+ c ^= ((volatile const char*)(image->m_base))[idx];
+ }
+ c ^= ((volatile const char*)(image->m_base))[image->m_size-1];
+
+ return 0;
+}
+
+/*
+ * file_image_close
+ */
+int file_image_close (file_image * image)
+{
+ if (image == NULL)
+ return EINVAL;
+
+ if (munmap (image->m_base, image->m_size) == -1)
+ return errno;
+
+ image->m_base = NULL;
+ image->m_size = 0;
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/pagein.c b/desktop/unx/source/pagein.c
new file mode 100644
index 000000000..03aa50d99
--- /dev/null
+++ b/desktop/unx/source/pagein.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "file_image.h"
+#include "pagein.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+/* do_pagein */
+static void do_pagein (const char * filename)
+{
+ int result;
+ file_image image = FILE_IMAGE_INITIALIZER;
+
+ if (file_image_open (&image, filename) != 0)
+ return;
+
+ if ((result = file_image_pagein (&image)) != 0)
+ {
+ fprintf (stderr, "file_image_pagein %s: %s\n", filename, strerror(result));
+ }
+
+ file_image_close (&image);
+}
+
+static int isRotational(char const * path)
+{
+ int ret = 1;
+#ifdef LINUX
+ FILE * fp = NULL;
+ char fullpath[4096];
+ struct stat out;
+ int major, minor;
+ char type;
+ if (stat(path , &out) == -1)
+ return ret;
+ major = major(out.st_dev);
+ minor = 0; /* minor(out.st_dev); only the device itself has a queue */
+ sprintf(fullpath,"/sys/dev/block/%d:%d/queue/rotational",major,minor);
+ if ((fp = fopen(fullpath, "r")) == NULL)
+ return ret;
+ if (fgets(&type, 1, fp))
+ ret = type == '1';
+ fclose(fp);
+#endif
+ return ret;
+}
+
+void pagein_execute(char const * path, char const * file)
+{
+ char fullpath[4096];
+ char *p = NULL;
+ FILE * fp = NULL;
+ if(!isRotational(path))
+ return;
+ memset(fullpath, 0, sizeof(fullpath));
+ strncpy (fullpath, path, 3000);
+ if (!(p = strrchr (fullpath, '/')))
+ p = fullpath;
+ else
+ p++;
+ strncpy(p, file, 1024);
+ p[strlen(p)] = '\0';
+ if ((fp = fopen (fullpath, "r")) == NULL)
+ {
+
+ fprintf (stderr, "fopen %s: %s\n", fullpath, strerror(errno));
+ return;
+ }
+ while (fgets (p, 1024, fp) != NULL)
+ {
+ p[strlen(p) - 1] = '\0';
+
+ /* paths relative to the location of the pagein file */
+ do_pagein (fullpath);
+ }
+ fclose (fp);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/pagein.h b/desktop/unx/source/pagein.h
new file mode 100644
index 000000000..308fca530
--- /dev/null
+++ b/desktop/unx/source/pagein.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_UNX_SOURCE_PAGEIN_H
+#define INCLUDED_DESKTOP_UNX_SOURCE_PAGEIN_H
+
+#include <sal/config.h>
+
+void pagein_execute(char const* path, char const* file);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/splashx.c b/desktop/unx/source/splashx.c
new file mode 100644
index 000000000..afd137d06
--- /dev/null
+++ b/desktop/unx/source/splashx.c
@@ -0,0 +1,817 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config_features.h>
+#include "splashx.h"
+
+#if defined(ENABLE_QUICKSTART_LIBPNG) && HAVE_FEATURE_UI
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#ifdef USE_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+#include <osl/endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <png.h>
+
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/bootstrap.h>
+#include <rtl/ustrbuf.h>
+
+typedef struct {
+ unsigned char b, g, r;
+} color_t;
+
+struct splash
+{
+ Display* display;
+ int screen;
+ int depth;
+ int display_width;
+ int display_height;
+ int display_x_pos;
+ int display_y_pos;
+ Visual* visual;
+
+ int width;
+ int height;
+
+ Colormap color_map;
+ Window win;
+ GC gc;
+ //true when intro-highres loaded successfully
+ sal_Bool bHasHiDpiImage;
+
+// Progress bar values
+// taken from desktop/source/splash/splash.cxx
+ int tlx;
+ int tly;
+ int barwidth;
+ int barheight;
+ int barspace;
+ color_t barcol;
+ color_t framecol;
+
+ XColor barcolor;
+ XColor framecolor;
+
+ unsigned char** bitmap_rows;
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+};
+
+#define WINDOW_WIDTH 440
+#define WINDOW_HEIGHT 299
+
+#define PROGRESS_XOFFSET 12
+#define PROGRESS_YOFFSET 18
+#define PROGRESS_BARSPACE 2
+
+/* libpng-1.2.41 */
+#ifndef PNG_TRANSFORM_GRAY_TO_RGB
+# define PNG_TRANSFORM_GRAY_TO_RGB 0x2000
+#endif
+
+static int splash_load_bmp( struct splash* splash, const char *filename )
+{
+ FILE *file;
+
+ if ( !(file = fopen( filename, "r" ) ) )
+ return 0;
+
+ splash->png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
+ splash->info_ptr = png_create_info_struct(splash->png_ptr);
+ png_init_io( splash->png_ptr, file );
+
+ if( setjmp( png_jmpbuf( splash->png_ptr ) ) )
+ {
+ png_destroy_read_struct( &(splash->png_ptr), &(splash->info_ptr), NULL );
+ fclose( file );
+ return 0;
+ }
+
+ png_read_png( splash->png_ptr, splash->info_ptr,
+ PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA |
+ PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);
+
+ splash->bitmap_rows = png_get_rows( splash->png_ptr, splash->info_ptr );
+ splash->width = png_get_image_width( splash->png_ptr, splash->info_ptr );
+ splash->height = png_get_image_height( splash->png_ptr, splash->info_ptr );
+
+ fclose( file );
+ return 1;
+}
+
+static void setup_color( int const val[3], color_t *col )
+{
+ if ( val[0] < 0 || val[1] < 0 || val[2] < 0 )
+ return;
+
+#define CONVERT_COLOR( from,to ) if ( from < 0 ) to = 0; else if ( from > 255 ) to = 255; else to = from;
+ CONVERT_COLOR( val[0], col->r );
+ CONVERT_COLOR( val[1], col->g );
+ CONVERT_COLOR( val[2], col->b );
+#undef CONVERT_COLOR
+}
+
+/* Fill 'array' with values of the key 'name'.
+ Its value is a comma delimited list of integers */
+static void get_bootstrap_value( int *array, int size, rtlBootstrapHandle handle, const char *name )
+{
+ rtl_uString *pKey = NULL, *pValue = NULL;
+
+ /* get the value from the ini file */
+ rtl_uString_newFromAscii( &pKey, name );
+ rtl_bootstrap_get_from_handle( handle, pKey, &pValue, NULL );
+
+ /* the value is several numbers delimited by ',' - parse it */
+ if ( rtl_uString_getLength( pValue ) > 0 )
+ {
+ rtl_uString *pToken = NULL;
+ int i = 0;
+ sal_Int32 nIndex = 0;
+ for ( ; ( nIndex >= 0 ) && ( i < size ); ++i )
+ {
+ nIndex = rtl_uString_getToken( &pToken, pValue, 0, ',', nIndex );
+ array[i] = rtl_ustr_toInt32( rtl_uString_getStr( pToken ), 10 );
+ }
+
+ rtl_uString_release( pToken );
+ }
+
+ /* cleanup */
+ rtl_uString_release( pKey );
+ rtl_uString_release( pValue );
+}
+
+// setup
+static void splash_setup( struct splash* splash, int const barc[3], int const framec[3], int posx, int posy, int w, int h )
+{
+ if ( splash->width <= 500 )
+ {
+ splash->barwidth = splash->width - ( 2 * PROGRESS_XOFFSET );
+ splash->barheight = 6;
+ splash->tlx = PROGRESS_XOFFSET;
+ splash->tly = splash->height - PROGRESS_YOFFSET;
+
+ splash->barcol.r = 0;
+ splash->barcol.g = 0;
+ splash->barcol.b = 128;
+ }
+
+ if ( posx >= 0 )
+ splash->tlx = posx;
+ if ( posy >= 0 )
+ splash->tly = posy;
+ if ( w >= 0 )
+ splash->barwidth = w;
+ if ( h >= 0 )
+ splash->barheight = h;
+
+ setup_color( barc, &(splash->barcol) );
+ setup_color( framec, &(splash->framecol) );
+}
+
+// Universal shift: bits >= 0 - left, otherwise right
+#define SHIFT( x, bits ) ( ( (bits) >= 0 )? ( (x) << (bits) ): ( (x) >> -(bits) ) )
+
+// Position of the highest bit (more or less integer log2)
+static int HIGHEST_BIT( unsigned long x )
+{
+ int i = 0;
+ for ( ; x; ++i )
+ x >>= 1;
+
+ return i;
+}
+
+// Number of bits set to 1
+static int BITS( unsigned long x )
+{
+ int i = 0;
+ for ( ; x; x >>= 1 )
+ if ( x & 1UL )
+ ++i;
+
+ return i;
+}
+
+// Set 'bitmap' as the background of our 'win' window
+static void create_pixmap(struct splash* splash)
+{
+ Pixmap pixmap;
+ GC pixmap_gc;
+ XGCValues values;
+
+ if ( !splash->bitmap_rows )
+ {
+ return;
+ }
+ pixmap = XCreatePixmap( splash->display, splash->win, splash->width, splash->height, splash->depth );
+
+ pixmap_gc = XCreateGC( splash->display, pixmap, 0/*value_mask*/, &values );
+
+ if ( splash->visual->class == TrueColor )
+ {
+ const unsigned long red_mask = splash->visual->red_mask;
+ const unsigned long green_mask = splash->visual->green_mask;
+ const unsigned long blue_mask = splash->visual->blue_mask;
+
+ const unsigned long red_delta_mask = ( 1UL << ( 8 - BITS( red_mask ) ) ) - 1;
+ const unsigned long green_delta_mask = ( 1UL << ( 8 - BITS( green_mask ) ) ) - 1;
+ const unsigned long blue_delta_mask = ( 1UL << ( 8 - BITS( blue_mask ) ) ) - 1;
+
+ const int red_shift = HIGHEST_BIT( red_mask ) - 8;
+ const int green_shift = HIGHEST_BIT( green_mask ) - 8;
+ const int blue_shift = HIGHEST_BIT( blue_mask ) - 8;
+
+ XImage* image = XCreateImage( splash->display, splash->visual, splash->depth, ZPixmap,
+ 0, NULL, splash->width, splash->height, 32, 0 );
+
+ const int bytes_per_line = image->bytes_per_line;
+ const int bpp = image->bits_per_pixel;
+ const int byte_order = image->byte_order;
+#if defined OSL_LITENDIAN
+ const int machine_byte_order = LSBFirst;
+#else /* OSL_BIGENDIAN */
+ const int machine_byte_order = MSBFirst;
+#endif
+
+ char *data = malloc( splash->height * bytes_per_line );
+ char *out = data;
+ image->data = data;
+
+ // The following dithers & converts the color_t color to one
+ // acceptable for the visual
+#define COPY_IN_OUT( pix_size, code ) \
+ { \
+ int x, y; \
+ for ( y = 0; y < splash->height; ++y ) \
+ { \
+ unsigned long red_delta = 0, green_delta = 0, blue_delta = 0; \
+ color_t *in = (color_t *)(splash->bitmap_rows[y]); \
+ out = data + y * bytes_per_line; \
+ for ( x = 0; x < splash->width; ++x, ++in ) \
+ { \
+ unsigned long red = in->r + red_delta; \
+ unsigned long green = in->g + green_delta; \
+ unsigned long blue = in->b + blue_delta; \
+ unsigned long pixel = 0; \
+ uint32_t tmp = 0; \
+ (void) tmp; \
+ red_delta = red & red_delta_mask; \
+ green_delta = green & green_delta_mask; \
+ blue_delta = blue & blue_delta_mask; \
+ if ( red > 255 ) \
+ red = 255; \
+ if ( green > 255 ) \
+ green = 255; \
+ if ( blue > 255 ) \
+ blue = 255; \
+ pixel = \
+ ( SHIFT( red, red_shift ) & red_mask ) | \
+ ( SHIFT( green, green_shift ) & green_mask ) | \
+ ( SHIFT( blue, blue_shift ) & blue_mask ); \
+ code \
+ } \
+ } \
+ }
+
+ if ( bpp == 32 )
+ {
+ if ( machine_byte_order == byte_order )
+ COPY_IN_OUT( 4, *( (uint32_t *)out ) = (uint32_t)pixel; out += 4; )
+ else
+ COPY_IN_OUT( 4, tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 3 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 3 ) = *( (uint8_t *)(&tmp) );
+ out += 4; )
+ }
+ else if ( bpp == 24 )
+ {
+ if (machine_byte_order == byte_order)
+ {
+#if defined OSL_LITENDIAN
+ COPY_IN_OUT( 3, memcpy(out, &pixel, sizeof (color_t)); out += 3; )
+#else /* OSL_BIGENDIAN */
+ COPY_IN_OUT( 3, tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 3 );
+ out += 3; )
+#endif
+ }
+ else
+ COPY_IN_OUT( 3, tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 3 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) + 2 );
+ *( (uint8_t *)out + 2 ) = *( (uint8_t *)(&tmp) + 1 );
+ out += 3; )
+ }
+ else if ( bpp == 16 )
+ {
+ if ( machine_byte_order == byte_order )
+ COPY_IN_OUT( 2, *( (uint16_t *)out ) = (uint16_t)pixel; out += 2; )
+ else
+ COPY_IN_OUT( 2, tmp = pixel;
+ *( (uint8_t *)out ) = *( (uint8_t *)(&tmp) + 1 );
+ *( (uint8_t *)out + 1 ) = *( (uint8_t *)(&tmp) );
+ out += 2; );
+ }
+ else if ( bpp == 8 )
+ {
+ COPY_IN_OUT( 1, *( (uint8_t *)out ) = (uint8_t)pixel; ++out; )
+ }
+ else
+ {
+ fprintf( stderr, "Unsupported depth: %d bits per pixel.\n", bpp );
+ XFreeGC( splash->display, pixmap_gc );
+ XFreePixmap( splash->display, pixmap );
+ XDestroyImage( image );
+ return;
+ }
+
+#undef COPY_IN_OUT
+
+ XPutImage( splash->display, pixmap, pixmap_gc, image, 0, 0, 0, 0, splash->width, splash->height );
+ XDestroyImage( image );
+ }
+
+ XSetWindowBackgroundPixmap( splash->display, splash->win, pixmap );
+
+ XFreeGC( splash->display, pixmap_gc );
+ XFreePixmap( splash->display, pixmap );
+}
+
+// The old method of hiding the window decorations
+static void suppress_decorations_motif(struct splash* splash)
+{
+ struct
+ {
+ unsigned long flags, functions, decorations;
+ long input_mode;
+ } mwmhints;
+
+ Atom a = XInternAtom( splash->display, "_MOTIF_WM_HINTS", False );
+
+ mwmhints.flags = 15; // functions, decorations, input_mode, status
+ mwmhints.functions = 2; // ?
+ mwmhints.decorations = 0;
+ mwmhints.input_mode = 0;
+
+ XChangeProperty( splash->display, splash->win, a, a, 32,
+ PropModeReplace, (unsigned char*)&mwmhints, 5 );
+}
+
+// This is a splash, set it as such.
+// If it fails, just hide the decorations...
+static void suppress_decorations(struct splash* splash)
+{
+ Atom atom_type = XInternAtom( splash->display, "_NET_WM_WINDOW_TYPE", True );
+ Atom atom_splash = XInternAtom( splash->display, "_NET_WM_WINDOW_TYPE_SPLASH", True );
+
+ if ( atom_type != None && atom_splash != None )
+ XChangeProperty( splash->display, splash->win, atom_type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&atom_splash, 1 );
+ //else
+ suppress_decorations_motif(splash); // FIXME: Unconditional until Metacity/compiz's SPLASH handling is fixed
+}
+
+/**
+ * Connects to the display and initializes splash with the screen details
+ *
+ * @return Success: 1; Failure: 0
+ */
+static int splash_init_display( struct splash* splash, int argc, char** argv )
+{
+ char *display_name = NULL;
+ int i;
+#ifdef USE_XINERAMA
+ int n_xinerama_screens = 1;
+ XineramaScreenInfo* p_screens = NULL;
+#endif
+
+ for ( i = 0; i < argc; i++ )
+ {
+ if ( !strcmp( argv[i], "-display" ) || !strcmp( argv[i], "--display" ) )
+ {
+ display_name = ( i + 1 < argc )? argv[i+1]: NULL;
+ }
+ }
+
+ if ( !display_name )
+ {
+ display_name = getenv( "DISPLAY" );
+ }
+ // init display
+ splash->display = XOpenDisplay( display_name );
+ if ( !splash->display )
+ {
+ fprintf( stderr, "Failed to open display\n" );
+ return 0;
+ }
+
+ // create the window
+ splash->screen = DefaultScreen( splash->display );
+ splash->depth = DefaultDepth( splash->display, splash->screen );
+ splash->color_map = DefaultColormap( splash->display, splash->screen );
+ splash->visual = DefaultVisual( splash->display, splash->screen );
+
+ splash->display_width = DisplayWidth( splash->display, splash->screen );
+ splash->display_height = DisplayHeight( splash->display, splash->screen );
+ splash->display_x_pos = 0;
+ splash->display_y_pos = 0;
+
+#ifdef USE_XINERAMA
+ p_screens = XineramaQueryScreens( splash->display, &n_xinerama_screens );
+ if( p_screens )
+ {
+ for( i=0; i < n_xinerama_screens; i++ )
+ {
+ if ( p_screens[i].screen_number == splash->screen )
+ {
+ splash->display_width = p_screens[i].width;
+ splash->display_height = p_screens[i].height;
+ splash->display_x_pos = p_screens[i].x_org;
+ splash->display_y_pos = p_screens[i].y_org;
+ break;
+ }
+ }
+ XFree( p_screens );
+ }
+#endif
+ return 1;
+}
+
+/**
+ * Create the window for the splash screen
+ *
+ * @return Success: 1; Failure: 0
+ */
+static int splash_create_window(struct splash* splash)
+{
+ Window root_win;
+ XGCValues values;
+ const char* name = "LibreOffice";
+ const char* icon = "icon"; // FIXME
+ XSizeHints size_hints;
+
+ root_win = RootWindow( splash->display, splash->screen );
+
+ splash->win = XCreateSimpleWindow( splash->display, root_win,
+ (splash->display_x_pos + (splash->display_width - splash->width)/2),
+ (splash->display_y_pos + (splash->display_height - splash->height)/2),
+ splash->width, splash->height, 0,
+ BlackPixel( splash->display, splash->screen ), BlackPixel( splash->display, splash->screen ) );
+
+ XSetWindowColormap( splash->display, splash->win, splash->color_map );
+
+ // setup colors
+#define FILL_COLOR( xcol,col ) xcol.red = 256*col.r; xcol.green = 256*col.g; xcol.blue = 256*col.b;
+ FILL_COLOR( splash->barcolor, splash->barcol );
+ FILL_COLOR( splash->framecolor, splash->framecol );
+#undef FILL_COLOR
+
+ XAllocColor( splash->display, splash->color_map, &(splash->barcolor) );
+ XAllocColor( splash->display, splash->color_map, &(splash->framecolor) );
+
+ // not resizable, no decorations, etc.
+ splash->gc = XCreateGC( splash->display, splash->win, 0/*value_mask*/, &values );
+
+ size_hints.flags = PPosition | PSize | PMinSize | PMaxSize;
+ size_hints.x = splash->display_x_pos;
+ size_hints.y = splash->display_y_pos;
+ size_hints.width = splash->width;
+ size_hints.height = splash->height;
+ size_hints.min_width = splash->width;
+ size_hints.max_width = splash->width;
+ size_hints.min_height = splash->height;
+ size_hints.max_height = splash->height;
+
+ XSetStandardProperties( splash->display, splash->win, name, icon, None,
+ NULL, 0, &size_hints );
+
+ // the actual work
+ suppress_decorations(splash);
+ create_pixmap(splash);
+
+ // show it
+ XSelectInput( splash->display, splash->win, 0 );
+ XMapWindow( splash->display, splash->win );
+
+ return 1;
+}
+
+// Re-draw & process the events
+// Just throwing them away - we do not need anything more...
+static void process_events(struct splash* splash)
+{
+ XEvent xev;
+ int num_events;
+
+ XFlush( splash->display );
+ num_events = XPending( splash->display );
+ while ( num_events > 0 )
+ {
+ num_events--;
+ XNextEvent( splash->display, &xev );
+ }
+}
+
+
+static rtl_String* ustr_to_str( rtl_uString* pStr )
+{
+ rtl_String *pOut = NULL;
+
+ rtl_uString2String( &pOut, rtl_uString_getStr( pStr ),
+ rtl_uString_getLength( pStr ), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ return pOut;
+}
+
+static sal_Bool isHiDPI(struct splash* splash)
+{
+ const char* pValStr;
+ double nDPI;
+
+ /*
+ * GNOME currently enables HiDPI support when the screen resolution is at least 192 dpi
+ * and the screen height (in device pixels) is at least 1200.
+ */
+
+ if (splash->display_height < 1200)
+ return sal_False;
+
+ pValStr = XGetDefault(splash->display, "Xft", "dpi");
+ /* if it's too old to have this, assume it's not hidpi */
+ if (!pValStr)
+ return sal_False;
+
+ nDPI = strtod(pValStr, NULL);
+ if (nDPI < 192)
+ return sal_False;
+
+ return sal_True;
+}
+
+#define IMG_SUFFIX ".png"
+
+static void splash_load_image( struct splash* splash, rtl_uString* pUAppPath )
+{
+ /* FIXME-BCP47: if we wanted to support language tags here that would get
+ * complicated, this is C-source not C++ so LanguageTag can't be used. For
+ * now the splash screen will have to get along with language-territory. */
+
+ char *pBuffer, *pSuffix, *pLocale;
+ int nLocSize;
+ rtl_Locale *pLoc = NULL;
+ rtl_String *pLang, *pCountry, *pAppPath;
+
+ osl_getProcessLocale (&pLoc);
+ pLang = ustr_to_str (pLoc->Language);
+ pCountry = ustr_to_str (pLoc->Country);
+
+ nLocSize = strlen (pLang->buffer) + strlen (pCountry->buffer) + 3;
+ pLocale = malloc (nLocSize);
+ pLocale[0] = '-';
+ strcpy (pLocale + 1, pLang->buffer);
+ strcat (pLocale, "_");
+ strcat (pLocale, pCountry->buffer);
+
+ rtl_string_release( pCountry );
+ rtl_string_release( pLang );
+
+ pAppPath = ustr_to_str (pUAppPath);
+ pBuffer = malloc (pAppPath->length + nLocSize + 256);
+ strcpy (pBuffer, pAppPath->buffer);
+ pSuffix = pBuffer + pAppPath->length;
+ rtl_string_release( pAppPath );
+
+ strcpy (pSuffix, "intro");
+ strcat (pSuffix, pLocale);
+ strcat (pSuffix, IMG_SUFFIX);
+ if ( splash_load_bmp( splash, pBuffer ) )
+ goto cleanup; /* success */
+
+ /* load high resolution splash image */
+ splash->bHasHiDpiImage = sal_False;
+ if (isHiDPI(splash))
+ {
+ strcpy (pSuffix, "intro-highres" IMG_SUFFIX);
+ if ( splash_load_bmp( splash, pBuffer ) )
+ {
+ splash->bHasHiDpiImage = sal_True;
+ goto cleanup; /* success */
+ }
+ }
+ /* load standard resolution splash image */
+ strcpy (pSuffix, "intro" IMG_SUFFIX);
+ if ( splash_load_bmp( splash, pBuffer ) )
+ goto cleanup; /* success */
+
+ fprintf (stderr, "Failed to find intro image\n");
+
+ cleanup:
+ free (pLocale);
+ free (pBuffer);
+}
+
+/* Load the colors and size of the splash. */
+static void splash_load_defaults( struct splash* splash, rtl_uString* pAppPath, sal_Bool* bNoDefaults )
+{
+ rtl_uString *pSettings = NULL, *pTmp = NULL;
+ rtlBootstrapHandle handle;
+ int logo[1] = { -1 },
+ bar[3] = { -1, -1, -1 },
+ frame[3] = { -1, -1, -1 },
+ pos[2] = { -1, -1 },
+ size[2] = { -1, -1 };
+
+ /* construct the sofficerc file location */
+ rtl_uString_newFromAscii( &pSettings, "file://" );
+ rtl_uString_newConcat( &pSettings, pSettings, pAppPath );
+ rtl_uString_newConcat( &pSettings, pSettings, pTmp );
+ rtl_uString_newFromAscii( &pTmp, SAL_CONFIGFILE( "soffice" ) );
+ rtl_uString_newConcat( &pSettings, pSettings, pTmp );
+
+ /* use it as the bootstrap file */
+ handle = rtl_bootstrap_args_open( pSettings );
+
+ /* get the values */
+ get_bootstrap_value( logo, 1, handle, "Logo" );
+ get_bootstrap_value( bar, 3, handle, "ProgressBarColor" );
+ get_bootstrap_value( frame, 3, handle, "ProgressFrameColor" );
+ if (isHiDPI(splash) && splash->bHasHiDpiImage)
+ {
+ get_bootstrap_value( pos, 2, handle, "ProgressPositionHigh" );
+ get_bootstrap_value( size, 2, handle, "ProgressSizeHigh" );
+ }
+ else
+ {
+ get_bootstrap_value( pos, 2, handle, "ProgressPosition" );
+ get_bootstrap_value( size, 2, handle, "ProgressSize" );
+ }
+
+ if ( logo[0] == 0 )
+ {
+ *bNoDefaults = sal_True;
+ }
+
+ splash_setup( splash, bar, frame, pos[0], pos[1], size[0], size[1] );
+
+ /* cleanup */
+ rtl_bootstrap_args_close( handle );
+ rtl_uString_release( pSettings );
+ rtl_uString_release( pTmp );
+}
+
+
+// Draw the progress
+void splash_draw_progress( struct splash* splash, int progress )
+{
+ int length = 0;
+
+ if (!splash)
+ {
+ return;
+ }
+ // sanity
+ if ( progress < 0 )
+ {
+ progress = 0;
+ }
+ if ( progress > 100 )
+ {
+ progress = 100;
+ }
+ // draw progress...
+ length = ( progress * splash->barwidth / 100 ) - ( 2 * splash->barspace );
+ if ( length < 0 )
+ {
+ length = 0;
+ }
+ // border
+ XSetForeground( splash->display, splash->gc, splash->framecolor.pixel );
+ XDrawRectangle( splash->display, splash->win, splash->gc, splash->tlx, splash->tly,
+ splash->barwidth, splash->barheight );
+
+ // progress bar
+ XSetForeground( splash->display, splash->gc, splash->barcolor.pixel );
+ XFillRectangle( splash->display, splash->win, splash->gc,
+ splash->tlx + splash->barspace, splash->tly + splash->barspace,
+ length + 1, splash->barheight - 2 * splash->barspace + 1 );
+
+ // pending events
+ process_events(splash);
+}
+
+void splash_destroy(struct splash* splash)
+{
+ if(!splash)
+ return;
+
+ if(splash->display)
+ {
+ if(splash->gc)
+ {
+ XFreeGC(splash->display, splash->gc);
+ splash->gc = NULL;
+ }
+
+ XCloseDisplay( splash->display );
+ splash->display = NULL;
+ png_destroy_read_struct( &(splash->png_ptr), &(splash->info_ptr), NULL );
+ }
+ free(splash);
+}
+
+struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv)
+{
+ struct splash* splash;
+ sal_Bool bNoDefaults = sal_False;
+
+ splash = calloc(1, sizeof(struct splash));
+ if (splash && !splash_init_display(splash, argc, argv))
+ {
+ splash_destroy(splash);
+ splash = NULL;
+ }
+
+ if (!splash)
+ return NULL;
+
+ splash->width = WINDOW_WIDTH;
+ splash->height = WINDOW_HEIGHT;
+
+ splash->tlx = 212;
+ splash->tly = 216;
+ splash->barwidth = 263;
+ splash->barheight = 8;
+ splash->barspace = PROGRESS_BARSPACE;
+ splash->barcol.b = 18;
+ splash->barcol.g = 202;
+ splash->barcol.r = 157;
+ splash->framecol.b = 0xD3;
+ splash->framecol.g = 0xD3;
+ splash->framecol.r = 0xD3;
+
+ splash_load_image( splash, pAppPath );
+ splash_load_defaults( splash, pAppPath, &bNoDefaults );
+
+ if (!bNoDefaults && splash_create_window(splash))
+ {
+ splash_draw_progress( splash, 0 );
+ return splash;
+ }
+
+ splash_destroy(splash);
+ return NULL;
+}
+
+#else /* not ENABLE_QUICKSTART_LIBPNG */
+
+struct splash
+{
+};
+
+/* Stubs that will never be called in this case */
+void splash_draw_progress( struct splash* splash, int progress )
+{
+ (void)splash; (void)progress;
+}
+
+void splash_destroy(struct splash* splash)
+{
+ (void)splash;
+}
+
+struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv)
+{
+ (void)pAppPath; (void)argc; (void)argv;
+ return NULL;
+}
+
+
+#endif // ENABLE_QUICKSTART_LIBPNG
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/splashx.h b/desktop/unx/source/splashx.h
new file mode 100644
index 000000000..55c128136
--- /dev/null
+++ b/desktop/unx/source/splashx.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef INCLUDED_DESKTOP_UNX_SOURCE_SPLASHX_H
+#define INCLUDED_DESKTOP_UNX_SOURCE_SPLASHX_H
+
+#include <rtl/ustring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct splash;
+
+struct splash* splash_create(rtl_uString* pAppPath, int argc, char** argv);
+
+void splash_destroy(struct splash* splash);
+
+void splash_draw_progress(struct splash* splash, int progress);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // INCLUDED_DESKTOP_UNX_SOURCE_SPLASHX_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/unx/source/start.c b/desktop/unx/source/start.c
new file mode 100644
index 000000000..e3e5441bf
--- /dev/null
+++ b/desktop/unx/source/start.c
@@ -0,0 +1,873 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config_java.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <errno.h>
+
+#include <desktop/exithelper.h>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/bootstrap.h>
+#include <rtl/digest.h>
+#include <rtl/process.h>
+#include <rtl/ustrbuf.h>
+#include <sal/main.h>
+
+#include "args.h"
+#include "pagein.h"
+#include "splashx.h"
+
+#define PIPEDEFAULTPATH "/tmp"
+#define PIPEALTERNATEPATH "/var/tmp"
+
+/* Easier conversions: rtl_uString to rtl_String */
+static rtl_String *ustr_to_str(rtl_uString *pStr)
+{
+ rtl_String *pOut = NULL;
+
+ rtl_uString2String(&pOut, rtl_uString_getStr(pStr),
+ rtl_uString_getLength(pStr), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ return pOut;
+}
+
+/* Easier conversions: char * to rtl_uString */
+static rtl_uString *charp_to_ustr(const char *pStr)
+{
+ rtl_uString *pOut = NULL;
+
+ rtl_string2UString(&pOut, pStr, strlen(pStr), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ return pOut;
+}
+
+typedef struct {
+ int status_fd;
+ oslProcess child;
+} ChildInfo;
+
+static int
+child_info_get_status_fd(ChildInfo const *info)
+{
+ return info->status_fd;
+}
+
+static void
+child_info_destroy(ChildInfo *info)
+{
+ close (info->status_fd);
+ osl_freeProcessHandle (info->child);
+ free (info);
+}
+
+static ChildInfo * child_spawn(Args *args, sal_Bool bAllArgs, sal_Bool bWithStatus)
+{
+ rtl_uString *pApp = NULL, *pTmp = NULL;
+ rtl_uString **ppArgs;
+ sal_uInt32 nArgs, i;
+ ChildInfo *info;
+ int status_pipe[2];
+ oslProcessError nError;
+
+ info = calloc (1, sizeof (ChildInfo));
+
+ /* create pipe */
+ if (pipe(status_pipe) < 0)
+ {
+ fprintf(stderr, "ERROR: no file handles\n");
+ exit(1);
+ }
+ info->status_fd = status_pipe[0];
+
+ /* application name */
+ rtl_uString_newFromAscii(&pApp, "file://");
+ rtl_uString_newConcat(&pApp, pApp, args->pAppPath);
+ rtl_uString_newFromAscii(&pTmp, "soffice.bin");
+ rtl_uString_newConcat(&pApp, pApp, pTmp);
+ rtl_uString_release(pTmp);
+ pTmp = NULL;
+
+ /* copy args */
+ nArgs = bAllArgs ? args->nArgsTotal : args->nArgsEnv;
+ ppArgs = (rtl_uString **)calloc(nArgs + 1, sizeof(rtl_uString*));
+ for (i = 0; i < nArgs; ++i)
+ ppArgs[i] = args->ppArgs[i];
+
+ if(bWithStatus)
+ {
+ char buffer[64];
+
+ /* add the pipe arg */
+ snprintf(buffer, 63, "--splash-pipe=%d", status_pipe[1]);
+ rtl_uString_newFromAscii( &pTmp, buffer );
+ ppArgs[nArgs] = pTmp;
+ ++nArgs;
+ }
+
+ /* start the main process */
+ nError = osl_executeProcess(pApp, ppArgs, nArgs,
+ osl_Process_NORMAL,
+ NULL,
+ NULL,
+ NULL, 0,
+ &info->child );
+
+ if (pTmp)
+ rtl_uString_release(pTmp);
+ free (ppArgs);
+
+ if (nError != osl_Process_E_None)
+ {
+ fprintf(stderr, "ERROR %d forking process\n", nError);
+ rtl_uString_release(pApp);
+ _exit (1);
+ }
+
+ rtl_uString_release(pApp);
+ close( status_pipe[1] );
+
+ return info;
+}
+
+static sal_Bool child_exited_wait(ChildInfo *info, sal_Bool bShortWait)
+{
+ TimeValue t = { 0, 250 /* ms */ * 1000 * 1000 };
+ if (!bShortWait)
+ t.Seconds = 1024;
+
+ return osl_joinProcessWithTimeout(info->child, &t) != osl_Process_E_TimedOut;
+}
+
+static int child_get_exit_code(ChildInfo *info)
+{
+ oslProcessInfo inf;
+
+ inf.Code = -1;
+ inf.Size = sizeof(inf);
+
+ if (osl_getProcessInfo(info->child, osl_Process_EXITCODE, &inf) != osl_Process_E_None)
+ {
+ fprintf(stderr, "Warning: failed to fetch libreoffice exit status\n");
+ return -1;
+ }
+
+ return inf.Code;
+}
+
+typedef enum { ProgressContinue, ProgressRestart, ProgressExit } ProgressStatus;
+
+/* Path of the application, with trailing slash. */
+static rtl_uString *get_app_path(const char *pAppExec)
+{
+ char pRealPath[PATH_MAX];
+ rtl_uString *pResult;
+ sal_Int32 len;
+ char* dummy;
+
+ char *pOrigPath = strdup(pAppExec);
+ char *pPath = dirname(pOrigPath);
+
+ dummy = realpath(pPath, pRealPath);
+ (void)dummy;
+ pResult = charp_to_ustr(pRealPath);
+ free(pOrigPath);
+
+ len = rtl_uString_getLength(pResult);
+ if (len > 0 && rtl_uString_getStr(pResult)[len - 1] != '/')
+ {
+ rtl_uString *pSlash = NULL;
+ rtl_uString_newFromAscii(&pSlash, "/");
+ rtl_uString_newConcat(&pResult, pResult, pSlash);
+ rtl_uString_release(pSlash);
+ }
+
+ return pResult;
+}
+
+/* Compute the OOo md5 hash from 'pText' */
+static rtl_uString *get_md5hash(rtl_uString *pText)
+{
+ rtl_uString *pResult = NULL;
+ sal_Int32 nCapacity = 100;
+ unsigned char *pData = NULL;
+ sal_uInt32 nSize = 0;
+ rtlDigest digest;
+ sal_uInt32 md5_key_len = 0;
+ sal_uInt8* md5_buf = NULL;
+ sal_uInt32 i = 0;
+
+ if ( !pText )
+ return NULL;
+
+ pData = (unsigned char *)rtl_uString_getStr(pText);
+ nSize = rtl_uString_getLength(pText) * sizeof(sal_Unicode);
+ if (!pData)
+ return NULL;
+
+ digest = rtl_digest_create(rtl_Digest_AlgorithmMD5);
+ if (!digest)
+ return NULL;
+
+ md5_key_len = rtl_digest_queryLength(digest);
+ md5_buf = (sal_uInt8 *)calloc(md5_key_len, sizeof(sal_uInt8));
+
+ rtl_digest_init(digest, pData , nSize);
+ rtl_digest_update(digest, pData, nSize);
+ rtl_digest_get(digest, md5_buf, md5_key_len);
+ rtl_digest_destroy(digest);
+
+ /* create hex-value string from the MD5 value to keep
+ the string size minimal */
+ rtl_uString_new_WithLength(&pResult, nCapacity);
+ for (; i < md5_key_len; ++i)
+ {
+ char val[3];
+ snprintf(val, 3, "%x", md5_buf[i]); /* sic! we ignore some of the 0's */
+
+ rtl_uStringbuffer_insert_ascii(&pResult, &nCapacity, rtl_uString_getLength(pResult),
+ val, strlen(val));
+ }
+
+ /* cleanup */
+ free(md5_buf);
+
+ return pResult;
+}
+
+/* Construct the pipe name */
+static rtl_uString *get_pipe_path(rtl_uString *pAppPath)
+{
+ rtl_uString *pPath = NULL, *pTmp = NULL, *pUserInstallation = NULL;
+ rtl_uString *pResult = NULL, *pBasePath = NULL, *pAbsUserInstallation = NULL;
+ rtlBootstrapHandle handle;
+ rtl_uString *pMd5hash = NULL;
+ sal_Unicode pUnicode[RTL_USTR_MAX_VALUEOFINT32];
+
+ /* setup bootstrap filename */
+ rtl_uString_newFromAscii(&pPath, "file://");
+ rtl_uString_newConcat(&pPath, pPath, pAppPath);
+ rtl_uString_newConcat(&pPath, pPath, pTmp);
+ rtl_uString_newFromAscii(&pTmp, SAL_CONFIGFILE("bootstrap"));
+ rtl_uString_newConcat(&pPath, pPath, pTmp);
+
+ /* read userinstallation value */
+ handle = rtl_bootstrap_args_open(pPath);
+
+ rtl_uString_newFromAscii(&pTmp, "UserInstallation");
+ rtl_bootstrap_get_from_handle(handle, pTmp, &pUserInstallation, NULL);
+
+ rtl_bootstrap_args_close(handle);
+
+ /* turn it into an absolute path - unwinding symlinks etc. */
+ if (osl_getProcessWorkingDir(&pBasePath) ||
+ osl_getAbsoluteFileURL(pBasePath, pUserInstallation, &pAbsUserInstallation))
+ rtl_uString_newFromString(&pAbsUserInstallation, pUserInstallation);
+
+ /* create the pipe name */
+ pMd5hash = get_md5hash(pAbsUserInstallation);
+ if (!pMd5hash)
+ rtl_uString_new(&pMd5hash);
+
+ if (access(PIPEDEFAULTPATH, W_OK) == 0)
+ {
+ rtl_uString_newFromAscii(&pResult, PIPEDEFAULTPATH);
+ }
+ else if (access(PIPEALTERNATEPATH, W_OK) == 0)
+ {
+ rtl_uString_newFromAscii(&pResult, PIPEALTERNATEPATH);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: no valid pipe path found.\n");
+ exit(1);
+ }
+
+ rtl_uString_newFromAscii(&pTmp, "/OSL_PIPE_");
+ rtl_uString_newConcat(&pResult, pResult, pTmp);
+
+ rtl_ustr_valueOfInt32(pUnicode, (int)getuid(), 10);
+ rtl_uString_newFromStr(&pTmp, pUnicode);
+ rtl_uString_newConcat(&pResult, pResult, pTmp);
+
+ rtl_uString_newFromAscii(&pTmp, "_SingleOfficeIPC_");
+ rtl_uString_newConcat(&pResult, pResult, pTmp);
+
+ rtl_uString_newConcat(&pResult, pResult, pMd5hash);
+
+ /* cleanup */
+ rtl_uString_release(pMd5hash);
+ rtl_uString_release(pPath);
+ rtl_uString_release(pTmp);
+
+ if (pBasePath)
+ rtl_uString_release(pBasePath);
+
+ rtl_uString_release(pUserInstallation);
+ rtl_uString_release(pAbsUserInstallation);
+
+ return pResult;
+}
+
+/* Get fd of the pipe of the already running OOo. */
+static int connect_pipe(rtl_uString *pPipePath)
+{
+ int fd;
+ size_t len;
+ struct sockaddr_un addr;
+
+ rtl_String *pPipeStr = ustr_to_str(pPipePath);
+
+ memset(&addr, 0, sizeof(addr));
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return fd;
+
+ (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, rtl_string_getStr(pPipeStr), sizeof(addr.sun_path) - 1);
+ rtl_string_release(pPipeStr);
+
+/* cut / paste from osl's pipe.c */
+#if defined(FREEBSD)
+ len = SUN_LEN(&addr);
+#else
+ len = sizeof(addr);
+#endif
+
+ if (connect(fd, (struct sockaddr *)&addr, len) < 0)
+ {
+ close(fd);
+ fd = -1;
+ }
+ return fd;
+}
+
+/* Escape: "," -> "\\,", "\0" -> "\\0", "\\" -> "\\\\" */
+static rtl_uString *escape_path(rtl_uString const *pToEscape)
+{
+ rtl_uString *pBuffer = NULL;
+ sal_Int32 nCapacity = 1000;
+ sal_Int32 i = 0;
+ sal_Int32 nEscapeLength = rtl_uString_getLength(pToEscape);
+
+ rtl_uString_new_WithLength(&pBuffer, nCapacity);
+
+ for (; i < nEscapeLength; ++i)
+ {
+ sal_Unicode c = pToEscape->buffer[i];
+ switch (c)
+ {
+ case '\0':
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("\\0"));
+ break;
+ case ',':
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("\\,"));
+ break;
+ case '\\':
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("\\\\"));
+ break;
+ default:
+ rtl_uStringbuffer_insert(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ &c, 1);
+ }
+ }
+
+ return pBuffer;
+}
+
+/* Send args to the LO instance (using the 'fd' file descriptor) */
+static sal_Bool send_args(int fd, rtl_uString const *pCwdPath)
+{
+ rtl_uString *pBuffer = NULL, *pTmp = NULL;
+ sal_Int32 nCapacity = 1000;
+ rtl_String *pOut = NULL;
+ sal_Bool bResult;
+ size_t nLen;
+ rtl_uString *pEscapedCwdPath = escape_path(pCwdPath);
+ sal_uInt32 nArg = 0;
+ sal_uInt32 nArgCount = rtl_getAppCommandArgCount();
+
+ rtl_uString_new_WithLength(&pBuffer, nCapacity);
+ rtl_uString_new(&pTmp);
+
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("InternalIPC::Arguments"));
+
+ if (rtl_uString_getLength(pEscapedCwdPath))
+ {
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("1"));
+
+ rtl_uStringbuffer_insert(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ rtl_uString_getStr(pEscapedCwdPath),
+ rtl_uString_getLength(pEscapedCwdPath));
+ }
+ else
+ {
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ RTL_CONSTASCII_STRINGPARAM("0"));
+ }
+
+ for (nArg = 0; nArg < nArgCount; ++nArg)
+ {
+ rtl_uString *pEscapedTmp = NULL;
+ rtl_uStringbuffer_insert_ascii(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ ",", 1);
+
+ rtl_getAppCommandArg(nArg, &pTmp);
+
+ pEscapedTmp = escape_path(pTmp);
+
+ rtl_uStringbuffer_insert(&pBuffer, &nCapacity,
+ rtl_uString_getLength(pBuffer),
+ rtl_uString_getStr(pEscapedTmp),
+ rtl_uString_getLength(pEscapedTmp));
+
+ rtl_uString_release(pEscapedTmp);
+ }
+
+ if (!rtl_convertUStringToString(
+ &pOut, rtl_uString_getStr(pBuffer),
+ rtl_uString_getLength(pBuffer), RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ fprintf(stderr, "ERROR: cannot convert arguments to UTF-8\n");
+ exit(1);
+ }
+
+ nLen = rtl_string_getLength(pOut) + 1;
+ bResult = (write(fd, rtl_string_getStr(pOut), nLen) == (ssize_t) nLen);
+
+ if ( bResult )
+ {
+ char resp[SAL_N_ELEMENTS("InternalIPC::ProcessingDone")];
+ ssize_t n = read(fd, resp, SAL_N_ELEMENTS(resp));
+ bResult = n == (ssize_t) SAL_N_ELEMENTS(resp)
+ && (memcmp(
+ resp, "InternalIPC::ProcessingDone",
+ SAL_N_ELEMENTS(resp))
+ == 0);
+ }
+
+ /* cleanup */
+ rtl_uString_release(pEscapedCwdPath);
+ rtl_uString_release(pBuffer);
+ rtl_uString_release(pTmp);
+ rtl_string_release(pOut);
+
+ return bResult;
+}
+
+
+#define BUFFER_LEN 255
+
+/* Read the percent to show in splash. */
+static ProgressStatus read_percent(ChildInfo const *info, int *pPercent)
+{
+ static char pBuffer[BUFFER_LEN + 1];
+ static char *pNext = pBuffer;
+ static ssize_t nRead = 0;
+
+ char *pBegin;
+ char *pIter;
+ char c;
+
+ /* from the last call */
+ int nNotProcessed = nRead - (pNext - pBuffer);
+ if (nNotProcessed >= BUFFER_LEN)
+ return ProgressContinue;
+
+ memmove(pBuffer, pNext, nNotProcessed);
+
+ /* read data */
+ nRead = read(child_info_get_status_fd(info),
+ pBuffer + nNotProcessed, BUFFER_LEN - nNotProcessed);
+
+ if (nRead < 0)
+ {
+ if (errno == EINTR)
+ return ProgressContinue;
+
+ return ProgressExit;
+ }
+
+ nRead += nNotProcessed;
+ pBuffer[nRead] = '\0';
+
+ /* skip old data */
+ pBegin = pBuffer;
+ pNext = pBuffer;
+ for (pIter = pBuffer; *pIter; ++pIter)
+ {
+ if (*pIter == '\n')
+ {
+ pBegin = pNext;
+ pNext = pIter + 1;
+ }
+ }
+
+ if (!strncasecmp(pBegin, "end", 3))
+ return ProgressExit;
+ else if (!strncasecmp(pBegin, "restart", 7))
+ return ProgressRestart;
+ else if (sscanf(pBegin, "%d%c", pPercent, &c) == 2 && c == '%')
+ return ProgressContinue;
+
+ /* unexpected - let's exit the splash to be safe */
+ return ProgressExit;
+}
+
+/* Simple system check. */
+static void system_checks(void)
+{
+#ifdef LINUX
+ struct stat buf;
+
+ /* check proc is mounted - lots of things fail otherwise */
+ if (stat("/proc/version", &buf) != 0)
+ {
+ fprintf(stderr, "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all\n");
+ exit(1);
+ }
+#endif
+}
+
+static void exec_pagein (Args *args)
+{
+ rtl_String * path = ustr_to_str(args->pAppPath);
+ pagein_execute(rtl_string_getStr(path), "pagein-common");
+
+ if (args->pPageinType)
+ pagein_execute(rtl_string_getStr(path), args->pPageinType);
+
+ rtl_string_release(path);
+}
+
+#if HAVE_FEATURE_JAVA
+
+static void extend_library_path(const char *new_element)
+{
+ rtl_uString *pEnvName=NULL, *pOrigEnvVar=NULL, *pNewEnvVar=NULL;
+ const char *pathname;
+#ifdef AIX
+ pathname = "LIBPATH";
+#else
+ pathname = "LD_LIBRARY_PATH";
+#endif
+
+ rtl_uString_newFromAscii(&pEnvName, pathname);
+ rtl_uString_newFromAscii(&pNewEnvVar, new_element);
+
+ osl_getEnvironment(pEnvName, &pOrigEnvVar);
+ if (pOrigEnvVar && pOrigEnvVar->length)
+ {
+ rtl_uString *pDelim = NULL;
+ rtl_uString_newFromAscii(&pDelim, ":");
+ rtl_uString_newConcat(&pNewEnvVar, pNewEnvVar, pDelim);
+ rtl_uString_newConcat(&pNewEnvVar, pNewEnvVar, pOrigEnvVar);
+ rtl_uString_release(pDelim);
+ }
+
+ osl_setEnvironment(pEnvName, pNewEnvVar);
+
+ if (pOrigEnvVar)
+ rtl_uString_release(pOrigEnvVar);
+
+ rtl_uString_release(pNewEnvVar);
+ rtl_uString_release(pEnvName);
+}
+
+static void exec_javaldx(Args *args)
+{
+ char newpath[4096];
+ sal_uInt32 nArgs;
+ rtl_uString *pApp;
+ rtl_uString **ppArgs;
+ rtl_uString *pTmp, *pTmp2;
+
+ oslProcess javaldx = NULL;
+ oslFileHandle fileOut = NULL;
+ oslProcessError err;
+
+ ppArgs = (rtl_uString **)calloc(args->nArgsEnv + 2, sizeof(rtl_uString*));
+
+ for (nArgs = 0; nArgs < args->nArgsEnv; ++nArgs)
+ ppArgs[nArgs] = args->ppArgs[nArgs];
+
+ /* Use absolute path to redirectrc */
+ pTmp = NULL;
+ rtl_uString_newFromAscii(&pTmp, "-env:INIFILENAME=vnd.sun.star.pathname:");
+ rtl_uString_newConcat(&pTmp, pTmp, args->pAppPath);
+ pTmp2 = NULL;
+ rtl_uString_newFromAscii(&pTmp2, "redirectrc");
+ rtl_uString_newConcat(&pTmp, pTmp, pTmp2);
+ ppArgs[nArgs] = pTmp;
+ rtl_uString_release (pTmp2);
+ nArgs++;
+
+ /* And also to javaldx */
+ pApp = NULL;
+ rtl_uString_newFromAscii(&pApp, "file://");
+ rtl_uString_newConcat(&pApp, pApp, args->pAppPath);
+ pTmp = NULL;
+ rtl_uString_newFromAscii(&pTmp, "javaldx");
+ rtl_uString_newConcat(&pApp, pApp, pTmp);
+ rtl_uString_release(pTmp);
+
+ err = osl_executeProcess_WithRedirectedIO(pApp, ppArgs, nArgs,
+ osl_Process_NORMAL,
+ NULL, // security
+ NULL, // work dir
+ NULL, 0,
+ &javaldx, // process handle
+ NULL,
+ &fileOut,
+ NULL);
+
+ rtl_uString_release(ppArgs[nArgs-1]);
+ rtl_uString_release(pApp);
+ free(ppArgs);
+
+ if(err != osl_Process_E_None)
+ {
+ fprintf (stderr, "Warning: failed to launch javaldx - java may not function correctly\n");
+
+ if (javaldx)
+ osl_freeProcessHandle(javaldx);
+ if (fileOut)
+ osl_closeFile(fileOut);
+ return;
+ }
+ else
+ {
+ char *chomp;
+ sal_uInt64 bytes_read;
+
+ /* Magically osl_readLine doesn't work with pipes with E_SPIPE - so be this lame instead: */
+ while (osl_readFile (fileOut, newpath, SAL_N_ELEMENTS (newpath), &bytes_read) == osl_File_E_INTR);
+
+ if (bytes_read <= 0)
+ {
+ fprintf (stderr, "Warning: failed to read path from javaldx\n");
+
+ if (javaldx)
+ osl_freeProcessHandle(javaldx);
+
+ if (fileOut)
+ osl_closeFile(fileOut);
+
+ return;
+ }
+
+ newpath[bytes_read] = '\0';
+
+ if ((chomp = strstr (newpath, "\n")))
+ *chomp = '\0';
+ }
+
+ extend_library_path(newpath);
+
+ if (javaldx)
+ osl_freeProcessHandle(javaldx);
+
+ if (fileOut)
+ osl_closeFile(fileOut);
+}
+
+#endif
+
+// has to be a global :(
+static oslProcess * volatile g_pProcess = NULL;
+
+static void sigterm_handler(int ignored)
+{
+ (void) ignored;
+
+ if (g_pProcess)
+ osl_terminateProcess(g_pProcess); // forward signal to soffice.bin
+
+ _exit(255);
+}
+
+
+SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
+{
+ sal_Bool bSentArgs = sal_False;
+ const char* pUsePlugin;
+ rtl_uString *pPipePath = NULL;
+ Args *args;
+ int status = 0;
+ struct splash* splash = NULL;
+ struct sigaction sigpipe_action;
+ struct sigaction sigterm_action;
+
+ /* turn SIGPIPE into an error */
+ memset(&sigpipe_action, 0, sizeof(struct sigaction));
+ sigpipe_action.sa_handler = SIG_IGN;
+ sigemptyset(&sigpipe_action.sa_mask);
+ sigaction(SIGPIPE, &sigpipe_action, NULL);
+ memset(&sigterm_action, 0, sizeof(struct sigaction));
+ sigterm_action.sa_handler = &sigterm_handler;
+ sigemptyset(&sigterm_action.sa_mask);
+ sigaction(SIGTERM, &sigterm_action, NULL);
+
+ args = args_parse();
+ args->pAppPath = get_app_path(argv[0]);
+ if (!args->pAppPath)
+ {
+ fprintf(stderr, "ERROR: Can't read app link\n");
+ exit(1);
+ }
+
+#ifndef ENABLE_QUICKSTART_LIBPNG
+ /* we can't load and render it anyway */
+ args->bInhibitSplash = sal_True;
+#endif
+
+ pUsePlugin = getenv("SAL_USE_VCLPLUGIN");
+ if (pUsePlugin && !strcmp(pUsePlugin, "svp"))
+ args->bInhibitSplash = sal_True;
+
+ if (!args->bInhibitPipe && !getenv("LIBO_FLATPAK"))
+ {
+ int fd = 0;
+ pPipePath = get_pipe_path(args->pAppPath);
+
+ if ((fd=connect_pipe(pPipePath)) >= 0)
+ {
+ // Wait for answer
+ char resp[strlen("InternalIPC::SendArguments") + 1];
+ ssize_t n = read(fd, resp, SAL_N_ELEMENTS(resp));
+ if (n == (ssize_t) SAL_N_ELEMENTS(resp) &&
+ (memcmp(resp, "InternalIPC::SendArguments",
+ SAL_N_ELEMENTS(resp) - 1) == 0))
+ {
+ rtl_uString *pCwdPath = NULL;
+ osl_getProcessWorkingDir(&pCwdPath);
+
+ // Then send args
+ bSentArgs = send_args(fd, pCwdPath);
+ }
+
+ close(fd);
+ }
+ }
+
+ if (!bSentArgs)
+ {
+ /* we have to prepare for, and exec the binary */
+ int nPercent = 0;
+ ChildInfo *info;
+ sal_Bool bAllArgs = sal_True;
+ sal_Bool bShortWait, bRestart;
+
+ /* sanity check pieces */
+ system_checks();
+
+ /* load splash image and create window */
+ if (!args->bInhibitSplash)
+ splash = splash_create(args->pAppPath, argc, argv);
+
+ /* pagein */
+ if (!args->bInhibitPagein)
+ exec_pagein(args);
+
+ /* javaldx */
+#if HAVE_FEATURE_JAVA
+ if (!args->bInhibitJavaLdx)
+ exec_javaldx (args);
+#endif
+
+ do
+ {
+ bRestart = sal_False;
+
+ /* fast updates if we have somewhere to update it to */
+ bShortWait = splash ? sal_True : sal_False;
+
+ /* Periodically update the splash & the percent according
+ to what status_fd says, poll quickly only while starting */
+ info = child_spawn (args, bAllArgs, bShortWait);
+ g_pProcess = info->child;
+
+ while (!child_exited_wait(info, bShortWait))
+ {
+ ProgressStatus eResult;
+
+ splash_draw_progress(splash, nPercent);
+ eResult = read_percent(info, &nPercent);
+
+ if (eResult != ProgressContinue)
+ {
+ splash_destroy(splash);
+ splash = NULL;
+ bShortWait = sal_False;
+ }
+ }
+
+ status = child_get_exit_code(info);
+ g_pProcess = NULL; // reset
+
+ switch (status)
+ {
+ case EXITHELPER_CRASH_WITH_RESTART: // re-start with just -env: parameters
+ bRestart = sal_True;
+ bAllArgs = sal_False;
+ break;
+ case EXITHELPER_NORMAL_RESTART: // re-start with all arguments
+ bRestart = sal_True;
+ bAllArgs = sal_True;
+ break;
+ default:
+ break;
+ }
+
+ child_info_destroy(info);
+ } while (bRestart);
+ }
+
+ /* cleanup */
+ if (pPipePath)
+ rtl_uString_release(pPipePath);
+
+ args_free(args);
+
+ return status;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/util/officeloader.rc b/desktop/util/officeloader.rc
new file mode 100644
index 000000000..0b1d4f104
--- /dev/null
+++ b/desktop/util/officeloader.rc
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include "version.hrc"
+
+1 ICON PPS(RES_APP_ICON)
+2 ICON "icons/oasis-text.ico"
+3 ICON "icons/oasis-text-template.ico"
+4 ICON "icons/oasis-spreadsheet.ico"
+5 ICON "icons/oasis-spreadsheet-template.ico"
+6 ICON "icons/oasis-drawing.ico"
+7 ICON "icons/oasis-drawing-template.ico"
+8 ICON "icons/oasis-presentation.ico"
+9 ICON "icons/oasis-presentation-template.ico"
+10 ICON "icons/oasis-master-document.ico"
+11 ICON "icons/oasis-web-template.ico"
+12 ICON "icons/oasis-database.ico"
+13 ICON "icons/oasis-formula.ico"
+14 ICON "icons/oxt-extension.ico"
diff --git a/desktop/win32/source/QuickStart/QuickStart.cxx b/desktop/win32/source/QuickStart/QuickStart.cxx
new file mode 100644
index 000000000..3277a6abf
--- /dev/null
+++ b/desktop/win32/source/QuickStart/QuickStart.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// QuickStart.cpp : Defines the entry point for the application.
+
+#include <sal/config.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+
+#include "resource.h"
+#include <systools/win32/uwinapi.h>
+#include <systools/win32/qswin32.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+
+static bool SofficeRuns()
+{
+ // check for soffice by searching the communication window
+ return FindWindowExW( nullptr, nullptr, QUICKSTART_CLASSNAME, nullptr ) != nullptr;
+}
+
+static bool launchSoffice( )
+{
+ if ( !SofficeRuns() )
+ {
+ wchar_t filename[_MAX_PATH + 1];
+
+ filename[_MAX_PATH] = 0;
+ GetModuleFileNameW( nullptr, filename, _MAX_PATH ); // soffice resides in the same dir
+ wchar_t *p = wcsrchr( filename, L'\\' );
+ if ( !p )
+ return false;
+
+ wcsncpy( p+1, L"soffice.exe", _MAX_PATH - (p+1 - filename) );
+
+ wchar_t imagename[_MAX_PATH + 1];
+
+ imagename[_MAX_PATH] = 0;
+ _snwprintf(imagename, _MAX_PATH, L"\"%s\" --quickstart", filename );
+
+ STARTUPINFOW aStartupInfo;
+ ZeroMemory(&aStartupInfo, sizeof(aStartupInfo));
+ aStartupInfo.cb = sizeof(aStartupInfo);
+ aStartupInfo.wShowWindow = SW_SHOW;
+ PROCESS_INFORMATION aProcessInfo;
+ bool bSuccess = CreateProcessW(filename, imagename, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &aStartupInfo, &aProcessInfo);
+ if ( !bSuccess )
+ return false;
+
+ return true;
+ }
+ else
+ return true;
+}
+
+int APIENTRY wWinMain(HINSTANCE /*hInstance*/,
+ HINSTANCE /*hPrevInstance*/,
+ LPWSTR /*lpCmdLine*/,
+ int /*nCmdShow*/)
+{
+ // Look for --killtray argument
+
+ for ( int i = 1; i < __argc; i++ )
+ {
+ if ( 0 == wcscmp( __wargv[i], L"--killtray" ) )
+ {
+ HWND hwndTray = FindWindowW( QUICKSTART_CLASSNAME, nullptr );
+
+ if ( hwndTray )
+ {
+ UINT uMsgKillTray = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE );
+ SendMessageW( hwndTray, uMsgKillTray, 0, 0 );
+ }
+
+ return 0;
+ }
+ }
+
+ launchSoffice();
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/QuickStart/QuickStart.rc b/desktop/win32/source/QuickStart/QuickStart.rc
new file mode 100644
index 000000000..cf9131479
--- /dev/null
+++ b/desktop/win32/source/QuickStart/QuickStart.rc
@@ -0,0 +1,3 @@
+#include "resource.h"
+
+ICON_ACTIVE ICON DISCARDABLE "icons/soffice.ico"
diff --git a/desktop/win32/source/QuickStart/resource.h b/desktop/win32/source/QuickStart/resource.h
new file mode 100644
index 000000000..3dfbabcd8
--- /dev/null
+++ b/desktop/win32/source/QuickStart/resource.h
@@ -0,0 +1,5 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#define ICON_ACTIVE 1
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/launcher.cxx b/desktop/win32/source/applauncher/launcher.cxx
new file mode 100644
index 000000000..bf80dba2c
--- /dev/null
+++ b/desktop/win32/source/applauncher/launcher.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+#include <stdlib.h>
+#include <malloc.h>
+
+extern "C" int APIENTRY wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int )
+{
+ // Retrieve startup info
+
+ STARTUPINFOW aStartupInfo;
+
+ ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
+ aStartupInfo.cb = sizeof( aStartupInfo );
+ GetStartupInfoW( &aStartupInfo );
+
+ // Retrieve command line
+
+ LPWSTR lpCommandLine = static_cast<LPWSTR>(_alloca( sizeof(WCHAR) * (wcslen(GetCommandLineW()) + wcslen(APPLICATION_SWITCH) + 2) ));
+
+ wcscpy( lpCommandLine, GetCommandLineW() );
+ wcscat( lpCommandLine, L" " );
+ wcscat( lpCommandLine, APPLICATION_SWITCH );
+
+ // Calculate application name
+
+ WCHAR szApplicationName[MAX_PATH];
+ WCHAR szDrive[MAX_PATH];
+ WCHAR szDir[MAX_PATH];
+ WCHAR szFileName[MAX_PATH];
+ WCHAR szExt[MAX_PATH];
+
+ GetModuleFileNameW( nullptr, szApplicationName, MAX_PATH );
+ _wsplitpath( szApplicationName, szDrive, szDir, szFileName, szExt );
+ _wmakepath( szApplicationName, szDrive, szDir, L"soffice", L".exe" );
+
+ PROCESS_INFORMATION aProcessInfo;
+
+ bool fSuccess = CreateProcessW(
+ szApplicationName,
+ lpCommandLine,
+ nullptr,
+ nullptr,
+ TRUE,
+ 0,
+ nullptr,
+ nullptr,
+ &aStartupInfo,
+ &aProcessInfo );
+
+ if ( fSuccess )
+ {
+ // Wait for soffice process to be terminated to allow other applications
+ // to wait for termination of started process
+
+ WaitForSingleObject( aProcessInfo.hProcess, INFINITE );
+
+ CloseHandle( aProcessInfo.hProcess );
+ CloseHandle( aProcessInfo.hThread );
+
+ return 0;
+ }
+
+ DWORD dwError = GetLastError();
+
+ LPWSTR lpMsgBuf = nullptr;
+
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ dwError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ reinterpret_cast<LPWSTR>(&lpMsgBuf),
+ 0,
+ nullptr
+ );
+
+ // Display the string.
+ MessageBoxW( nullptr, lpMsgBuf, nullptr, MB_OK | MB_ICONERROR );
+
+ // Free the buffer.
+ HeapFree( GetProcessHeap(), 0, lpMsgBuf );
+
+ return dwError;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/launcher.hxx b/desktop/win32/source/applauncher/launcher.hxx
new file mode 100644
index 000000000..e28cc7466
--- /dev/null
+++ b/desktop/win32/source/applauncher/launcher.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+extern WCHAR APPLICATION_SWITCH[];
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/launcher.rc b/desktop/win32/source/applauncher/launcher.rc
new file mode 100644
index 000000000..8e56ef754
--- /dev/null
+++ b/desktop/win32/source/applauncher/launcher.rc
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include "version.hrc"
+
+1 ICON PPS(RES_APP_ICON)
diff --git a/desktop/win32/source/applauncher/sbase.cxx b/desktop/win32/source/applauncher/sbase.cxx
new file mode 100644
index 000000000..a8e832a5b
--- /dev/null
+++ b/desktop/win32/source/applauncher/sbase.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--base";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/scalc.cxx b/desktop/win32/source/applauncher/scalc.cxx
new file mode 100644
index 000000000..27df42119
--- /dev/null
+++ b/desktop/win32/source/applauncher/scalc.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--calc";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/sdraw.cxx b/desktop/win32/source/applauncher/sdraw.cxx
new file mode 100644
index 000000000..3cfb7e425
--- /dev/null
+++ b/desktop/win32/source/applauncher/sdraw.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--draw";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/simpress.cxx b/desktop/win32/source/applauncher/simpress.cxx
new file mode 100644
index 000000000..c3b73bf40
--- /dev/null
+++ b/desktop/win32/source/applauncher/simpress.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--impress";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/smath.cxx b/desktop/win32/source/applauncher/smath.cxx
new file mode 100644
index 000000000..ae441b2e4
--- /dev/null
+++ b/desktop/win32/source/applauncher/smath.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--math";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/soffice_safe.cxx b/desktop/win32/source/applauncher/soffice_safe.cxx
new file mode 100644
index 000000000..241a03b76
--- /dev/null
+++ b/desktop/win32/source/applauncher/soffice_safe.cxx
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--safe-mode";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/sweb.cxx b/desktop/win32/source/applauncher/sweb.cxx
new file mode 100644
index 000000000..69aeda135
--- /dev/null
+++ b/desktop/win32/source/applauncher/sweb.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--web";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/applauncher/swriter.cxx b/desktop/win32/source/applauncher/swriter.cxx
new file mode 100644
index 000000000..fc470d3ac
--- /dev/null
+++ b/desktop/win32/source/applauncher/swriter.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "launcher.hxx"
+
+WCHAR APPLICATION_SWITCH[] = L"--writer";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/loader.cxx b/desktop/win32/source/loader.cxx
new file mode 100644
index 000000000..3adf34aa3
--- /dev/null
+++ b/desktop/win32/source/loader.cxx
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "loader.hxx"
+#include <cassert>
+#include <systools/win32/uwinapi.h>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+#include <desktop/exithelper.h>
+#include <tools/pathutils.hxx>
+
+#include <fstream>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+
+namespace {
+
+void fail()
+{
+ LPWSTR buf = nullptr;
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
+ GetLastError(), 0, reinterpret_cast< LPWSTR >(&buf), 0, nullptr);
+ MessageBoxW(nullptr, buf, nullptr, MB_OK | MB_ICONERROR);
+ HeapFree(GetProcessHeap(), 0, buf);
+ TerminateProcess(GetCurrentProcess(), 255);
+}
+
+LPWSTR* GetCommandArgs(int* pArgc) { return CommandLineToArgvW(GetCommandLineW(), pArgc); }
+
+// tdf#120249: quotes in arguments need to be escaped; backslashes before quotes need doubling. See
+// https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw
+std::wstring EscapeArg(LPCWSTR sArg)
+{
+ const size_t nOrigSize = wcslen(sArg);
+ LPCWSTR const end = sArg + nOrigSize;
+ std::wstring sResult(L"\"");
+
+ LPCWSTR lastPosQuote = sArg;
+ LPCWSTR posQuote;
+ while ((posQuote = std::find(lastPosQuote, end, L'"')) != end)
+ {
+ LPCWSTR posBackslash = posQuote;
+ while (posBackslash != lastPosQuote && *(posBackslash - 1) == L'\\')
+ --posBackslash;
+
+ sResult.append(lastPosQuote, posBackslash);
+ sResult.append((posQuote - posBackslash) * 2 + 1, L'\\'); // 2n+1 '\' to escape the '"'
+ sResult.append(1, L'"');
+ lastPosQuote = posQuote + 1;
+ }
+
+ LPCWSTR posTrailingBackslashSeq = end;
+ while (posTrailingBackslashSeq != lastPosQuote && *(posTrailingBackslashSeq - 1) == L'\\')
+ --posTrailingBackslashSeq;
+ sResult.append(lastPosQuote, posTrailingBackslashSeq);
+ sResult.append((end - posTrailingBackslashSeq) * 2, L'\\'); // 2n '\' before closing '"'
+ sResult.append(1, L'"');
+
+ return sResult;
+}
+
+}
+
+namespace desktop_win32 {
+
+void extendLoaderEnvironment(WCHAR * binPath, WCHAR * iniDirectory) {
+ if (!GetModuleFileNameW(nullptr, iniDirectory, MAX_PATH)) {
+ fail();
+ }
+ WCHAR * iniDirEnd = tools::filename(iniDirectory);
+ WCHAR name[MAX_PATH + MY_LENGTH(L".bin")];
+ // hopefully std::size_t is large enough to not overflow
+ WCHAR * nameEnd = name;
+ for (WCHAR * p = iniDirEnd; *p != L'\0'; ++p) {
+ *nameEnd++ = *p;
+ }
+ if (!(nameEnd - name >= 4 && nameEnd[-4] == L'.' &&
+ (((nameEnd[-3] == L'E' || nameEnd[-3] == L'e') &&
+ (nameEnd[-2] == L'X' || nameEnd[-2] == L'x') &&
+ (nameEnd[-1] == L'E' || nameEnd[-1] == L'e')) ||
+ ((nameEnd[-3] == L'C' || nameEnd[-3] == L'c') &&
+ (nameEnd[-2] == L'O' || nameEnd[-2] == L'o') &&
+ (nameEnd[-1] == L'M' || nameEnd[-1] == L'm')))))
+ {
+ *nameEnd = L'.';
+ nameEnd += 4;
+ }
+ nameEnd[-3] = 'b';
+ nameEnd[-2] = 'i';
+ nameEnd[-1] = 'n';
+ tools::buildPath(binPath, iniDirectory, iniDirEnd, name, nameEnd - name);
+ *iniDirEnd = L'\0';
+ std::size_t const maxEnv = 32767;
+ WCHAR env[maxEnv];
+ DWORD n = GetEnvironmentVariableW(L"PATH", env, maxEnv);
+ if ((n >= maxEnv || n == 0) && GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
+ fail();
+ }
+ // must be first in PATH to override other entries
+ assert(*(iniDirEnd - 1) == L'\\'); // hence -1 below
+ if (wcsncmp(env, iniDirectory, iniDirEnd - iniDirectory - 1) != 0
+ || env[iniDirEnd - iniDirectory - 1] != L';')
+ {
+ WCHAR pad[MAX_PATH + maxEnv];
+ // hopefully std::size_t is large enough to not overflow
+ WCHAR * p = commandLineAppend(pad, iniDirectory, iniDirEnd - iniDirectory - 1);
+ if (n != 0) {
+ *p++ = L';';
+ for (DWORD i = 0; i <= n; ++i) {
+ *p++ = env[i];
+ }
+ } else {
+ *p++ = L'\0';
+ }
+ if (!SetEnvironmentVariableW(L"PATH", pad)) {
+ fail();
+ }
+ }
+}
+
+int officeloader_impl(bool bAllowConsole)
+{
+ WCHAR szTargetFileName[MAX_PATH] = {};
+ WCHAR szIniDirectory[MAX_PATH];
+ STARTUPINFOW aStartupInfo;
+
+ desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);
+
+ ZeroMemory(&aStartupInfo, sizeof(aStartupInfo));
+ aStartupInfo.cb = sizeof(aStartupInfo);
+
+ // Create process with same command line, environment and stdio handles which
+ // are directed to the created pipes
+ GetStartupInfoW(&aStartupInfo);
+
+ DWORD dwExitCode = DWORD(-1);
+
+ bool fSuccess = false;
+ LPWSTR lpCommandLine = nullptr;
+ bool bFirst = true;
+ WCHAR cwd[MAX_PATH];
+ DWORD cwdLen = GetCurrentDirectoryW(MAX_PATH, cwd);
+ if (cwdLen >= MAX_PATH)
+ {
+ cwdLen = 0;
+ }
+ std::vector<std::wstring> aEscapedArgs;
+
+ // read limit values from bootstrap.ini
+ unsigned int nMaxMemoryInMB = 0;
+ bool bExcludeChildProcesses = true;
+
+ const WCHAR* szIniFile = L"\\bootstrap.ini";
+ const size_t nDirLen = wcslen(szIniDirectory);
+ if (wcslen(szIniFile) + nDirLen < MAX_PATH)
+ {
+ WCHAR szBootstrapIni[MAX_PATH];
+ wcscpy(szBootstrapIni, szIniDirectory);
+ wcscpy(&szBootstrapIni[nDirLen], szIniFile);
+
+ try
+ {
+ boost::property_tree::ptree pt;
+ std::ifstream aFile(szBootstrapIni);
+ boost::property_tree::ini_parser::read_ini(aFile, pt);
+ nMaxMemoryInMB = pt.get("Win32.LimitMaximumMemoryInMB", nMaxMemoryInMB);
+ bExcludeChildProcesses = pt.get("Win32.ExcludeChildProcessesFromLimit", bExcludeChildProcesses);
+ }
+ catch (...)
+ {
+ nMaxMemoryInMB = 0;
+ }
+ }
+
+ // create a Windows JobObject with a memory limit
+ HANDLE hJobObject = nullptr;
+ if (nMaxMemoryInMB > 0)
+ {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION aJobLimit;
+ aJobLimit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY;
+ if (bExcludeChildProcesses)
+ aJobLimit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
+ aJobLimit.JobMemoryLimit = nMaxMemoryInMB * 1024 * 1024;
+ hJobObject = CreateJobObjectW(nullptr, nullptr);
+ if (hJobObject != nullptr)
+ SetInformationJobObject(hJobObject, JobObjectExtendedLimitInformation, &aJobLimit,
+ sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
+ }
+
+ do
+ {
+ if (bFirst)
+ {
+ int argc = 0;
+ LPWSTR* argv = GetCommandArgs(&argc);
+ std::size_t n = 0;
+ for (int i = 0; i < argc; ++i)
+ {
+ std::wstring sEscapedArg = EscapeArg(argv[i]);
+ aEscapedArgs.push_back(sEscapedArg);
+ n += sEscapedArg.length() + 1; // a space between args
+ }
+ LocalFree(argv);
+ n += MY_LENGTH(L" \"-env:OOO_CWD=2") + 4 * cwdLen + MY_LENGTH(L"\"") + 1;
+ // 4 * cwdLen: each char preceded by backslash, each trailing
+ // backslash doubled
+ lpCommandLine = new WCHAR[n];
+ }
+ WCHAR* p = desktop_win32::commandLineAppend(lpCommandLine, aEscapedArgs[0].c_str(),
+ aEscapedArgs[0].length());
+ for (size_t i = 1; i < aEscapedArgs.size(); ++i)
+ {
+ const std::wstring& rArg = aEscapedArgs[i];
+ if (bFirst || EXITHELPER_NORMAL_RESTART == dwExitCode
+ || wcsncmp(rArg.c_str(), MY_STRING(L"\"-env:")) == 0)
+ {
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L" "));
+ p = desktop_win32::commandLineAppend(p, rArg.c_str(), rArg.length());
+ }
+ }
+
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L" \"-env:OOO_CWD="));
+ if (cwdLen == 0)
+ {
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L"0"));
+ }
+ else
+ {
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L"2"));
+ p = desktop_win32::commandLineAppendEncoded(p, cwd);
+ }
+ desktop_win32::commandLineAppend(p, MY_STRING(L"\""));
+ bFirst = false;
+
+ WCHAR szParentProcessId[64]; // This is more than large enough for a 128 bit decimal value
+ bool bHeadlessMode(false);
+
+ {
+ // Check command line arguments for "--headless" parameter. We only
+ // set the environment variable "ATTACHED_PARENT_PROCESSID" for the headless
+ // mode as self-destruction of the soffice.bin process can lead to
+ // certain side-effects (log-off can result in data-loss, ".lock" is not deleted.
+ // See 138244 for more information.
+ int argc2;
+ LPWSTR* argv2 = GetCommandArgs(&argc2);
+
+ if (argc2 > 1)
+ {
+ int n;
+
+ for (n = 1; n < argc2; n++)
+ {
+ if (0 == wcsnicmp(argv2[n], L"-headless", 9)
+ || 0 == wcsnicmp(argv2[n], L"--headless", 10))
+ {
+ bHeadlessMode = true;
+ }
+ }
+ }
+
+ LocalFree(argv2);
+ }
+
+ if (_ltow(static_cast<long>(GetCurrentProcessId()), szParentProcessId, 10) && bHeadlessMode)
+ SetEnvironmentVariableW(L"ATTACHED_PARENT_PROCESSID", szParentProcessId);
+
+ PROCESS_INFORMATION aProcessInfo;
+
+ fSuccess = CreateProcessW(szTargetFileName, lpCommandLine, nullptr, nullptr, TRUE,
+ bAllowConsole ? 0 : DETACHED_PROCESS, nullptr, szIniDirectory,
+ &aStartupInfo, &aProcessInfo);
+
+ if (fSuccess)
+ {
+ DWORD dwWaitResult;
+
+ if (hJobObject)
+ AssignProcessToJobObject(hJobObject, aProcessInfo.hProcess);
+
+ do
+ {
+ // On Windows XP it seems as the desktop calls WaitForInputIdle after "OpenWith" so
+ // we have to do so as if we where processing any messages
+
+ dwWaitResult = MsgWaitForMultipleObjects(1, &aProcessInfo.hProcess, FALSE, INFINITE,
+ QS_ALLEVENTS);
+
+ if (WAIT_OBJECT_0 + 1 == dwWaitResult)
+ {
+ MSG msg;
+
+ PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE);
+ }
+ } while (WAIT_OBJECT_0 + 1 == dwWaitResult);
+
+ dwExitCode = 0;
+ GetExitCodeProcess(aProcessInfo.hProcess, &dwExitCode);
+
+ CloseHandle(aProcessInfo.hProcess);
+ CloseHandle(aProcessInfo.hThread);
+ }
+ } while (fSuccess
+ && (EXITHELPER_CRASH_WITH_RESTART == dwExitCode
+ || EXITHELPER_NORMAL_RESTART == dwExitCode));
+
+ if (hJobObject)
+ CloseHandle(hJobObject);
+
+ delete[] lpCommandLine;
+
+ return fSuccess ? dwExitCode : -1;
+}
+
+int unopkgloader_impl(bool bAllowConsole)
+{
+ WCHAR szTargetFileName[MAX_PATH];
+ WCHAR szIniDirectory[MAX_PATH];
+ desktop_win32::extendLoaderEnvironment(szTargetFileName, szIniDirectory);
+
+ STARTUPINFOW aStartupInfo{};
+ aStartupInfo.cb = sizeof(aStartupInfo);
+ GetStartupInfoW(&aStartupInfo);
+
+ DWORD dwExitCode = DWORD(-1);
+
+ size_t iniDirLen = wcslen(szIniDirectory);
+ WCHAR cwd[MAX_PATH];
+ DWORD cwdLen = GetCurrentDirectoryW(MAX_PATH, cwd);
+ if (cwdLen >= MAX_PATH) {
+ cwdLen = 0;
+ }
+ WCHAR redirect[MAX_PATH];
+ DWORD dummy;
+ bool hasRedirect =
+ tools::buildPath(
+ redirect, szIniDirectory, szIniDirectory + iniDirLen,
+ MY_STRING(L"redirect.ini")) != nullptr &&
+ (GetBinaryTypeW(redirect, &dummy) || // cheaper check for file existence?
+ GetLastError() != ERROR_FILE_NOT_FOUND);
+ LPWSTR cl1 = GetCommandLineW();
+ WCHAR* cl2 = new WCHAR[
+ wcslen(cl1) +
+ (hasRedirect
+ ? (MY_LENGTH(L" \"-env:INIFILENAME=vnd.sun.star.pathname:") +
+ iniDirLen + MY_LENGTH(L"redirect.ini\""))
+ : 0) +
+ MY_LENGTH(L" \"-env:OOO_CWD=2") + 4 * cwdLen + MY_LENGTH(L"\"") + 1];
+ // 4 * cwdLen: each char preceded by backslash, each trailing backslash
+ // doubled
+ WCHAR* p = desktop_win32::commandLineAppend(cl2, cl1);
+ if (hasRedirect) {
+ p = desktop_win32::commandLineAppend(
+ p, MY_STRING(L" \"-env:INIFILENAME=vnd.sun.star.pathname:"));
+ p = desktop_win32::commandLineAppend(p, szIniDirectory);
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L"redirect.ini\""));
+ }
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L" \"-env:OOO_CWD="));
+ if (cwdLen == 0) {
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L"0"));
+ }
+ else {
+ p = desktop_win32::commandLineAppend(p, MY_STRING(L"2"));
+ p = desktop_win32::commandLineAppendEncoded(p, cwd);
+ }
+ desktop_win32::commandLineAppend(p, MY_STRING(L"\""));
+
+ PROCESS_INFORMATION aProcessInfo;
+
+ bool fSuccess = CreateProcessW(
+ szTargetFileName,
+ cl2,
+ nullptr,
+ nullptr,
+ TRUE,
+ bAllowConsole ? 0 : DETACHED_PROCESS,
+ nullptr,
+ szIniDirectory,
+ &aStartupInfo,
+ &aProcessInfo);
+
+ delete[] cl2;
+
+ if (fSuccess)
+ {
+ DWORD dwWaitResult;
+
+ do
+ {
+ // On Windows XP it seems as the desktop calls WaitForInputIdle after "OpenWidth" so we have to do so
+ // as if we where processing any messages
+
+ dwWaitResult = MsgWaitForMultipleObjects(1, &aProcessInfo.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
+
+ if (WAIT_OBJECT_0 + 1 == dwWaitResult)
+ {
+ MSG msg;
+
+ PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE);
+ }
+ } while (WAIT_OBJECT_0 + 1 == dwWaitResult);
+
+ dwExitCode = 0;
+ GetExitCodeProcess(aProcessInfo.hProcess, &dwExitCode);
+
+ CloseHandle(aProcessInfo.hProcess);
+ CloseHandle(aProcessInfo.hThread);
+ }
+
+ return dwExitCode;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/loader.hxx b/desktop/win32/source/loader.hxx
new file mode 100644
index 000000000..aed76b168
--- /dev/null
+++ b/desktop/win32/source/loader.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DESKTOP_WIN32_SOURCE_LOADER_HXX
+#define INCLUDED_DESKTOP_WIN32_SOURCE_LOADER_HXX
+
+#include <cstddef>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <string.h>
+
+#define MY_LENGTH(s) (sizeof (s) / sizeof *(s) - 1)
+#define MY_STRING(s) (s), MY_LENGTH(s)
+
+namespace desktop_win32 {
+
+inline WCHAR * commandLineAppend(
+ WCHAR * buffer, WCHAR const * text, std::size_t length)
+{
+ wcsncpy(buffer, text, length + 1); // trailing null
+ return buffer + length;
+}
+
+inline WCHAR * commandLineAppend(WCHAR * buffer, WCHAR const * text) {
+ return commandLineAppend(buffer, text, wcslen(text));
+}
+
+inline WCHAR * commandLineAppendEncoded(WCHAR * buffer, WCHAR const * text) {
+ std::size_t n = 0;
+ for (;;) {
+ WCHAR c = *text++;
+ if (c == L'\0') {
+ break;
+ } else if (c == L'$') {
+ buffer = commandLineAppend(buffer, MY_STRING(L"\\$"));
+ n = 0;
+ } else if (c == L'\\') {
+ buffer = commandLineAppend(buffer, MY_STRING(L"\\\\"));
+ n += 2;
+ } else {
+ *buffer++ = c;
+ n = 0;
+ }
+ }
+ // The command line will continue with a double quote, so double any
+ // preceding backslashes as required by Windows:
+ for (std::size_t i = 0; i < n; ++i) {
+ *buffer++ = L'\\';
+ }
+ *buffer = L'\0';
+ return buffer;
+}
+
+// Set the PATH environment variable in the current (loader) process, so that a
+// following CreateProcess has the necessary environment:
+// @param binPath
+// Must point to an array of size at least MAX_PATH. Is filled with the null
+// terminated full path to the "bin" file corresponding to the current
+// executable.
+// @param iniDirectory
+// Must point to an array of size at least MAX_PATH. Is filled with the null
+// terminated full directory path (ending in "\") to the "ini" file
+// corresponding to the current executable.
+void extendLoaderEnvironment(WCHAR * binPath, WCHAR * iniDirectory);
+
+// Implementation of the process guarding soffice.bin
+int officeloader_impl(bool bAllowConsole);
+
+// Implementation of the process guarding unopkg.bin
+int unopkgloader_impl(bool bAllowConsole);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/officeloader/soffice_com.cxx b/desktop/win32/source/officeloader/soffice_com.cxx
new file mode 100644
index 000000000..5c6974e66
--- /dev/null
+++ b/desktop/win32/source/officeloader/soffice_com.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "../loader.hxx"
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ // let soffice.bin use soffice.com's console
+ return desktop_win32::officeloader_impl(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/officeloader/soffice_exe.cxx b/desktop/win32/source/officeloader/soffice_exe.cxx
new file mode 100644
index 000000000..03ff0a546
--- /dev/null
+++ b/desktop/win32/source/officeloader/soffice_exe.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "../loader.hxx"
+
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
+{
+ // no console for soffice.bin when started by soffice.exe
+ return desktop_win32::officeloader_impl(false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/officeloader/unopkg_com.cxx b/desktop/win32/source/officeloader/unopkg_com.cxx
new file mode 100644
index 000000000..a93ac6036
--- /dev/null
+++ b/desktop/win32/source/officeloader/unopkg_com.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "../loader.hxx"
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ // let unopkg.bin use unopkg.com's console
+ return desktop_win32::unopkgloader_impl(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/officeloader/unopkg_exe.cxx b/desktop/win32/source/officeloader/unopkg_exe.cxx
new file mode 100644
index 000000000..40b1afa09
--- /dev/null
+++ b/desktop/win32/source/officeloader/unopkg_exe.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "../loader.hxx"
+
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
+{
+ // no console for unopkg.bin when started by unopkg.exe
+ return desktop_win32::unopkgloader_impl(false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/win32/source/unoinfo.cxx b/desktop/win32/source/unoinfo.cxx
new file mode 100644
index 000000000..14cee8819
--- /dev/null
+++ b/desktop/win32/source/unoinfo.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cstddef>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <tools/pathutils.hxx>
+
+#define MY_LENGTH(s) (sizeof (s) / sizeof *(s) - 1)
+#define MY_STRING(s) (s), MY_LENGTH(s)
+
+namespace {
+
+wchar_t * getBrandPath(wchar_t * path) {
+ DWORD n = GetModuleFileNameW(nullptr, path, MAX_PATH);
+ if (n == 0 || n >= MAX_PATH) {
+ exit(EXIT_FAILURE);
+ }
+ return tools::filename(path);
+}
+
+void writeNull() {
+ if (fwrite("\0\0", 1, 2, stdout) != 2) {
+ exit(EXIT_FAILURE);
+ }
+}
+
+void writePath(
+ wchar_t const * frontBegin, wchar_t const * frontEnd,
+ wchar_t const * backBegin, std::size_t backLength)
+{
+ wchar_t path[MAX_PATH];
+ wchar_t * end = tools::buildPath(
+ path, frontBegin, frontEnd, backBegin, backLength);
+ if (end == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ std::size_t n = (end - path) * sizeof (wchar_t);
+ if (fwrite(path, 1, n, stdout) != n) {
+ exit(EXIT_FAILURE);
+ }
+}
+
+}
+
+int wmain(int argc, wchar_t ** argv, wchar_t **) {
+ if (argc == 2 && wcscmp(argv[1], L"c++") == 0) {
+ wchar_t path[MAX_PATH];
+ wchar_t * pathEnd = getBrandPath(path);
+ writePath(path, pathEnd, MY_STRING(L""));
+ } else if (argc == 2 && wcscmp(argv[1], L"java") == 0) {
+ if (fwrite("1", 1, 1, stdout) != 1) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t path[MAX_PATH];
+ wchar_t * pathEnd = getBrandPath(path);
+ writePath(path, pathEnd, MY_STRING(L"classes\\libreoffice.jar"));
+ writeNull();
+ writePath(path, pathEnd, MY_STRING(L""));
+ } else {
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */