diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /shell/source | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
112 files changed, 17197 insertions, 0 deletions
diff --git a/shell/source/all/xml_parser.cxx b/shell/source/all/xml_parser.cxx new file mode 100644 index 000000000..b067c6231 --- /dev/null +++ b/shell/source/all/xml_parser.cxx @@ -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 . + */ + + +#include <xml_parser.hxx> +#include <i_xml_parser_event_handler.hxx> + +#ifdef _WIN32 +#include <utilities.hxx> +#else +#define UTF8ToWString(s) s +#endif + +#include <assert.h> + +namespace /* private */ +{ + + /* Extracts the local part of tag without + namespace decoration e.g. meta:creator -> creator */ + const XML_Char COLON = ':'; + + const XML_Char* get_local_name(const XML_Char* rawname) + { + const XML_Char* p = rawname; + + // go to the end + while (*p) p++; + + // go back until the first ':' + while (*p != COLON && p > rawname) + p--; + + // if we are on a colon one step forward + if (*p == COLON) + p++; + + return p; + } + + xml_parser* get_parser_instance(void* data) + { + return static_cast<xml_parser*>(XML_GetUserData( + static_cast<XML_Parser>(data))); + } + + bool has_only_whitespaces(const XML_Char* s, int len) + { + const XML_Char* p = s; + for (int i = 0; i < len; i++) + if (*p++ != ' ') return false; + return true; + } +} + +xml_parser::xml_parser() : + document_handler_(nullptr), + xml_parser_(XML_ParserCreate(nullptr)) +{ + init(); +} + +xml_parser::~xml_parser() +{ + XML_ParserFree(xml_parser_); +} + +/* Callback functions will be called by the parser on + different events */ + +extern "C" +{ + +static void xml_start_element_handler(void* UserData, const XML_Char* name, const XML_Char** atts) +{ + assert(UserData != nullptr); + + xml_parser* pImpl = get_parser_instance(UserData); + + i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler(); + if (!pDocHdl) + return; + + xml_tag_attribute_container_t attributes; + + int i = 0; + + while(atts[i]) + { + attributes[UTF8ToWString(reinterpret_cast<const char*>(get_local_name(atts[i])))] = UTF8ToWString(reinterpret_cast<const char*>(atts[i+1])); + i += 2; // skip to next pair + } + + pDocHdl->start_element( + UTF8ToWString(reinterpret_cast<const char*>(name)), UTF8ToWString(reinterpret_cast<const char*>(get_local_name(name))), attributes); +} + +static void xml_end_element_handler(void* UserData, const XML_Char* name) +{ + assert(UserData); + + xml_parser* pImpl = get_parser_instance(UserData); + i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler(); + if (pDocHdl) + pDocHdl->end_element(UTF8ToWString(reinterpret_cast<const char*>(name)), UTF8ToWString(reinterpret_cast<const char*>(get_local_name(name)))); +} + +static void xml_character_data_handler(void* UserData, const XML_Char* s, int len) +{ + assert(UserData); + + xml_parser* pImpl = get_parser_instance(UserData); + i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler(); + if (pDocHdl) + { + if (has_only_whitespaces(s,len)) + pDocHdl->ignore_whitespace(UTF8ToWString(std::string(reinterpret_cast<const char*>(s), len))); + else + pDocHdl->characters(UTF8ToWString(std::string(reinterpret_cast<const char*>(s), len))); + } +} + +static void xml_comment_handler(void* UserData, const XML_Char* Data) +{ + assert(UserData); + + xml_parser* pImpl = get_parser_instance(UserData); + i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler(); + if (pDocHdl) + pDocHdl->comment(UTF8ToWString(reinterpret_cast<const char*>(Data))); +} + +} // extern "C" + +void xml_parser::init() +{ + XML_SetUserData(xml_parser_, this); + + // we use the parser as handler argument, + // so we could use it if necessary, the + // UserData are usable anyway using + // XML_GetUserData(...) + XML_UseParserAsHandlerArg(xml_parser_); + + XML_SetElementHandler( + xml_parser_, + xml_start_element_handler, + xml_end_element_handler); + + XML_SetCharacterDataHandler( + xml_parser_, + xml_character_data_handler); + + XML_SetCommentHandler( + xml_parser_, + xml_comment_handler); +} + +void xml_parser::parse(const char* XmlData, size_t Length, bool IsFinal) +{ + if (XML_STATUS_ERROR == + XML_Parse(xml_parser_, XmlData, static_cast<int>(Length), IsFinal)) + { + throw xml_parser_exception( + XML_ErrorString(XML_GetErrorCode(xml_parser_))); + } +} + +void xml_parser::set_document_handler( + i_xml_parser_event_handler* event_handler) +{ + document_handler_ = event_handler; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/desktopbe/desktopbackend.cxx b/shell/source/backends/desktopbe/desktopbackend.cxx new file mode 100644 index 000000000..4dcc91967 --- /dev/null +++ b/shell/source/backends/desktopbe/desktopbackend.cxx @@ -0,0 +1,301 @@ +/* -*- 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 <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/XVetoableChangeListener.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.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/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/file.hxx> +#include <osl/security.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <tools/diagnose_ex.h> +#include <uno/current_context.hxx> + +namespace { + +class Default: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::beans::XPropertySet > +{ +public: + Default() {} + Default(const Default&) = delete; + Default& operator=(const Default&) = delete; + +private: + virtual ~Default() override {} + + virtual OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.configuration.backend.DesktopBackend"; } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return ServiceName == getSupportedServiceNames()[0]; } + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override + { return { "com.sun.star.configuration.backend.DesktopBackend" }; } + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override + { return css::uno::Reference< css::beans::XPropertySetInfo >(); } + + virtual void SAL_CALL setPropertyValue( + OUString const &, css::uno::Any const &) override; + + virtual css::uno::Any SAL_CALL getPropertyValue( + OUString const & PropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL removePropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL addVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + virtual void SAL_CALL removeVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} +}; + +void Default::setPropertyValue(OUString const &, css::uno::Any const &) +{ + throw css::lang::IllegalArgumentException( + "setPropertyValue not supported", + static_cast< cppu::OWeakObject * >(this), -1); +} + +OUString xdg_user_dir_lookup (const char *type, bool bAllowHomeDir) +{ + size_t nLenType = strlen(type); + char *config_home; + char *p; + bool bError = false; + + osl::Security aSecurity; + oslFileHandle handle; + OUString aHomeDirURL; + OUString aDocumentsDirURL; + OUString aConfigFileURL; + OUStringBuffer aUserDirBuf; + + if (!aSecurity.getHomeDir( aHomeDirURL ) ) + { + osl::FileBase::getFileURLFromSystemPath("/tmp", aDocumentsDirURL); + return aDocumentsDirURL; + } + + config_home = getenv ("XDG_CONFIG_HOME"); + if (config_home == nullptr || config_home[0] == 0) + { + aConfigFileURL = aHomeDirURL + "/.config/user-dirs.dirs"; + } + else + { + aConfigFileURL = OUString::createFromAscii(config_home) + "/user-dirs.dirs"; + } + + if(osl_File_E_None == osl_openFile(aConfigFileURL.pData, &handle, osl_File_OpenFlag_Read)) + { + rtl::ByteSequence seq; + while (osl_File_E_None == osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq))) + { + int relative = 0; + int len = seq.getLength(); + seq.realloc(len + 1); + seq[len] = 0; + + p = reinterpret_cast<char *>(seq.getArray()); + while (*p == ' ' || *p == '\t') + p++; + if (strncmp (p, "XDG_", 4) != 0) + continue; + p += 4; + if (strncmp (p, OString(type, nLenType).toAsciiUpperCase().getStr(), nLenType) != 0) + continue; + p += nLenType; + if (strncmp (p, "_DIR", 4) != 0) + continue; + p += 4; + while (*p == ' ' || *p == '\t') + p++; + if (*p != '=') + continue; + p++; + while (*p == ' ' || *p == '\t') + p++; + if (*p != '"') + continue; + p++; + if (strncmp (p, "$HOME/", 6) == 0) + { + p += 6; + relative = 1; + } + else if (*p != '/') + continue; + if (relative) + { + aUserDirBuf = aHomeDirURL + "/"; + } + else + { + aUserDirBuf.truncate(); + } + while (*p && *p != '"') + { + if ((*p == '\\') && (*(p+1) != 0)) + p++; + aUserDirBuf.append(static_cast<sal_Unicode>(*p++)); + } + }//end of while + osl_closeFile(handle); + } + else + bError = true; + if (aUserDirBuf.getLength()>0 && !bError) + { + aDocumentsDirURL = aUserDirBuf.makeStringAndClear(); + if ( bAllowHomeDir || + (aDocumentsDirURL != aHomeDirURL && aDocumentsDirURL != OUStringConcatenation(aHomeDirURL + "/")) ) + { + osl::Directory aDocumentsDir( aDocumentsDirURL ); + if( osl::FileBase::E_None == aDocumentsDir.open() ) + return aDocumentsDirURL; + } + } + /* Use fallbacks historical compatibility if nothing else exists */ + return aHomeDirURL + "/" + OUString::createFromAscii(type); +} + +css::uno::Any xdgDirectoryIfExists(char const * type, bool bAllowHomeDir) { + auto url = xdg_user_dir_lookup(type, bAllowHomeDir); + return css::uno::Any( + osl::Directory(url).open() == osl::FileBase::E_None + ? css::beans::Optional<css::uno::Any>(true, css::uno::Any(url)) + : css::beans::Optional<css::uno::Any>(false, css::uno::Any())); +} + +css::uno::Any Default::getPropertyValue(OUString const & PropertyName) +{ + if (PropertyName == "TemplatePathVariable") + { + // Never pick up the HOME directory as the default location of user's templates + return xdgDirectoryIfExists("Templates", false); + } + + if (PropertyName == "WorkPathVariable") + { + return xdgDirectoryIfExists("Documents", true); + } + + if ( PropertyName == "EnableATToolSupport" || + PropertyName == "ExternalMailer" || + PropertyName == "SourceViewFontHeight" || + PropertyName == "SourceViewFontName" || + PropertyName == "ooInetFTPProxyName" || + PropertyName == "ooInetFTPProxyPort" || + PropertyName == "ooInetHTTPProxyName" || + PropertyName == "ooInetHTTPProxyPort" || + PropertyName == "ooInetHTTPSProxyName" || + PropertyName == "ooInetHTTPSProxyPort" || + PropertyName == "ooInetNoProxy" || + PropertyName == "ooInetProxyType" || + PropertyName == "givenname" || + PropertyName == "sn" ) + { + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } + + throw css::beans::UnknownPropertyException( + PropertyName, static_cast< cppu::OWeakObject * >(this)); +} + +css::uno::Reference< css::uno::XInterface > createBackend( + css::uno::Reference< css::uno::XComponentContext > const & context, + OUString const & name) +{ + try { + return css::uno::Reference< css::lang::XMultiComponentFactory >( + context->getServiceManager(), css::uno::UNO_SET_THROW)-> + createInstanceWithContext(name, context); + } catch (css::uno::RuntimeException &) { + // Assuming these exceptions are real errors: + throw; + } catch (const css::uno::Exception &) { + // Assuming these exceptions indicate that the service is not installed: + TOOLS_WARN_EXCEPTION("shell", "createInstance(" << name << ") failed"); + return css::uno::Reference< css::uno::XInterface >(); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_DesktopBackend_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + OUString desktop; + css::uno::Reference< css::uno::XCurrentContext > current( + css::uno::getCurrentContext()); + if (current.is()) { + current->getValueByName("system.desktop-environment") >>= desktop; + } + + // Fall back to the default if the specific backend is not available: + css::uno::Reference< css::uno::XInterface > backend; + if (desktop == "PLASMA5") + backend = createBackend(context, + "com.sun.star.configuration.backend.KF5Backend"); + if (!backend) + backend = static_cast< cppu::OWeakObject * >(new Default); + backend->acquire(); + return backend.get(); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/desktopbe/desktopbe1.component b/shell/source/backends/desktopbe/desktopbe1.component new file mode 100644 index 000000000..abc47f042 --- /dev/null +++ b/shell/source/backends/desktopbe/desktopbe1.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.configuration.backend.DesktopBackend" + constructor="shell_DesktopBackend_get_implementation"> + <service name="com.sun.star.configuration.backend.DesktopBackend"/> + </implementation> +</component> diff --git a/shell/source/backends/kf5be/kf5access.cxx b/shell/source/backends/kf5be/kf5access.cxx new file mode 100644 index 000000000..bfb669cf2 --- /dev/null +++ b/shell/source/backends/kf5be/kf5access.cxx @@ -0,0 +1,315 @@ +/* -*- 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 "kf5access.hxx" + +#include <QtGui/QFont> +#include <QtCore/QString> +#include <QtGui/QFontDatabase> +#include <QtCore/QStandardPaths> +#include <QtCore/QDir> +#include <QtCore/QUrl> + +#include <kprotocolmanager.h> + +#include <kemailsettings.h> +// #include <kglobalsettings.h> + +#include <com/sun/star/uno/Any.hxx> +#include <osl/diagnose.h> +#include <osl/file.h> +#include <rtl/ustring.hxx> + +namespace kf5access +{ +namespace +{ +namespace uno = css::uno; +} + +namespace +{ +OUString fromQStringToOUString(QString const& s) +{ + // Conversion from QString size()'s int to OUString's sal_Int32 should be non-narrowing: + return { reinterpret_cast<char16_t const*>(s.utf16()), s.size() }; +} +} + +css::beans::Optional<css::uno::Any> getValue(std::u16string_view id) +{ + if (id == u"ExternalMailer") + { + KEMailSettings aEmailSettings; + QString aClientProgram; + OUString sClientProgram; + + aClientProgram = aEmailSettings.getSetting(KEMailSettings::ClientProgram); + if (aClientProgram.isEmpty()) + aClientProgram = QStringLiteral("kmail"); + else + aClientProgram = aClientProgram.section(QLatin1Char(' '), 0, 0); + sClientProgram = fromQStringToOUString(aClientProgram); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sClientProgram)); + } + else if (id == u"SourceViewFontHeight") + { + const QFont aFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); + const short nFontHeight = aFixedFont.pointSize(); + return css::beans::Optional<css::uno::Any>(true, uno::Any(nFontHeight)); + } + else if (id == u"SourceViewFontName") + { + const QFont aFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); + const QString aFontName = aFixedFont.family(); + const OUString sFontName = fromQStringToOUString(aFontName); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sFontName)); + } + else if (id == u"EnableATToolSupport") + { + /* does not make much sense without an accessibility bridge */ + bool ATToolSupport = false; + return css::beans::Optional<css::uno::Any>(true, + uno::Any(OUString::boolean(ATToolSupport))); + } + else if (id == u"WorkPathVariable") + { + QString aDocumentsDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); + if (aDocumentsDir.isEmpty()) + aDocumentsDir = QDir::homePath(); + OUString sDocumentsDir; + OUString sDocumentsURL; + if (aDocumentsDir.endsWith(QLatin1Char('/'))) + aDocumentsDir.truncate(aDocumentsDir.length() - 1); + sDocumentsDir = fromQStringToOUString(aDocumentsDir); + osl_getFileURLFromSystemPath(sDocumentsDir.pData, &sDocumentsURL.pData); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sDocumentsURL)); + } + else if (id == u"ooInetFTPProxyName") + { + QString aFTPProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aFTPProxy = KProtocolManager::proxyFor(QStringLiteral("FTP")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aFTPProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("ftp://ftp.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aFTPProxy.isEmpty()) + { + QUrl aProxy(aFTPProxy); + OUString sProxy = fromQStringToOUString(aProxy.host()); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sProxy)); + } + } + else if (id == u"ooInetFTPProxyPort") + { + QString aFTPProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aFTPProxy = KProtocolManager::proxyFor(QStringLiteral("FTP")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aFTPProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("ftp://ftp.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aFTPProxy.isEmpty()) + { + QUrl aProxy(aFTPProxy); + sal_Int32 nPort = aProxy.port(); + return css::beans::Optional<css::uno::Any>(true, uno::Any(nPort)); + } + } + else if (id == u"ooInetHTTPProxyName") + { + QString aHTTPProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aHTTPProxy = KProtocolManager::proxyFor(QStringLiteral("HTTP")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aHTTPProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("http://www.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aHTTPProxy.isEmpty()) + { + QUrl aProxy(aHTTPProxy); + OUString sProxy = fromQStringToOUString(aProxy.host()); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sProxy)); + } + } + else if (id == u"ooInetHTTPProxyPort") + { + QString aHTTPProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aHTTPProxy = KProtocolManager::proxyFor(QStringLiteral("HTTP")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aHTTPProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("http://www.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aHTTPProxy.isEmpty()) + { + QUrl aProxy(aHTTPProxy); + sal_Int32 nPort = aProxy.port(); + return css::beans::Optional<css::uno::Any>(true, uno::Any(nPort)); + } + } + else if (id == u"ooInetHTTPSProxyName") + { + QString aHTTPSProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aHTTPSProxy = KProtocolManager::proxyFor(QStringLiteral("HTTPS")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aHTTPSProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("https://www.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aHTTPSProxy.isEmpty()) + { + QUrl aProxy(aHTTPSProxy); + OUString sProxy = fromQStringToOUString(aProxy.host()); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sProxy)); + } + } + else if (id == u"ooInetHTTPSProxyPort") + { + QString aHTTPSProxy; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + aHTTPSProxy = KProtocolManager::proxyFor(QStringLiteral("HTTPS")); + break; + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + // In such cases, the proxy address is not stored in KDE, but determined dynamically. + // The proxy address may depend on the requested address, on the time of the day, on the speed of the wind... + // The best we can do here is to ask the current value for a given address. + aHTTPSProxy = KProtocolManager::proxyForUrl( + QUrl(QStringLiteral("https://www.libreoffice.org"))); + break; + default: // No proxy is used + break; + } + if (!aHTTPSProxy.isEmpty()) + { + QUrl aProxy(aHTTPSProxy); + sal_Int32 nPort = aProxy.port(); + return css::beans::Optional<css::uno::Any>(true, uno::Any(nPort)); + } + } + else if (id == u"ooInetNoProxy") + { + QString aNoProxyFor; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + aNoProxyFor = KProtocolManager::noProxyFor(); + break; + default: // No proxy is used + break; + } + if (!aNoProxyFor.isEmpty()) + { + OUString sNoProxyFor; + + aNoProxyFor = aNoProxyFor.replace(QLatin1Char(','), QLatin1Char(';')); + sNoProxyFor = fromQStringToOUString(aNoProxyFor); + return css::beans::Optional<css::uno::Any>(true, uno::Any(sNoProxyFor)); + } + } + else if (id == u"ooInetProxyType") + { + sal_Int32 nProxyType; + switch (KProtocolManager::proxyType()) + { + case KProtocolManager::ManualProxy: // Proxies are manually configured + case KProtocolManager::PACProxy: // A proxy configuration URL has been given + case KProtocolManager::WPADProxy: // A proxy should be automatically discovered + case KProtocolManager::EnvVarProxy: // Proxy values set through environment variables + nProxyType = 1; + break; + default: // No proxy is used + nProxyType = 0; + } + return css::beans::Optional<css::uno::Any>(true, uno::Any(nProxyType)); + } + else + { + OSL_ASSERT(false); // this cannot happen + } + return css::beans::Optional<css::uno::Any>(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/kf5be/kf5access.hxx b/shell/source/backends/kf5be/kf5access.hxx new file mode 100644 index 000000000..765d9c706 --- /dev/null +++ b/shell/source/backends/kf5be/kf5access.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_SHELL_SOURCE_BACKENDS_KF5BE_KF5ACCESS_HXX +#define INCLUDED_SHELL_SOURCE_BACKENDS_KF5BE_KF5ACCESS_HXX + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/beans/Optional.hpp> + +namespace com::sun::star::uno +{ +class Any; +} + +namespace kf5access +{ +css::beans::Optional<css::uno::Any> getValue(std::u16string_view id); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/kf5be/kf5backend.cxx b/shell/source/backends/kf5be/kf5backend.cxx new file mode 100644 index 000000000..7137a9999 --- /dev/null +++ b/shell/source/backends/kf5be/kf5backend.cxx @@ -0,0 +1,260 @@ +/* -*- 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 <QtWidgets/QApplication> + +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/XVetoableChangeListener.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <uno/current_context.hxx> +#include <vcl/svapp.hxx> + +#include <osl/process.h> +#include <osl/thread.h> + +#include "kf5access.hxx" + +namespace +{ +class Service : public cppu::WeakImplHelper<css::lang::XServiceInfo, css::beans::XPropertySet> +{ +public: + Service(); + +private: + // noncopyable until we have good reasons... + Service(const Service&) = delete; + Service& operator=(const Service&) = delete; + + virtual ~Service() override {} + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.configuration.backend.KF5Backend"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override + { + return ServiceName == getSupportedServiceNames()[0]; + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.configuration.backend.KF5Backend" }; + } + + virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override + { + return css::uno::Reference<css::beans::XPropertySetInfo>(); + } + + virtual void SAL_CALL setPropertyValue(OUString const&, css::uno::Any const&) override; + + virtual css::uno::Any SAL_CALL getPropertyValue(OUString const& PropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener( + OUString const&, css::uno::Reference<css::beans::XPropertyChangeListener> const&) override + { + } + + virtual void SAL_CALL removePropertyChangeListener( + OUString const&, css::uno::Reference<css::beans::XPropertyChangeListener> const&) override + { + } + + virtual void SAL_CALL addVetoableChangeListener( + OUString const&, css::uno::Reference<css::beans::XVetoableChangeListener> const&) override + { + } + + virtual void SAL_CALL removeVetoableChangeListener( + OUString const&, css::uno::Reference<css::beans::XVetoableChangeListener> const&) override + { + } + + std::map<OUString, css::beans::Optional<css::uno::Any>> m_KDESettings; +}; + +OString getDisplayArg() +{ + OUString aParam; + const sal_uInt32 nParams = osl_getCommandArgCount(); + for (sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx) + { + osl_getCommandArg(nIdx, &aParam.pData); + if (aParam != "-display") + continue; + + ++nIdx; + osl_getCommandArg(nIdx, &aParam.pData); + return OUStringToOString(aParam, osl_getThreadTextEncoding()); + } + return {}; +} + +OString getExecutable() +{ + OUString aParam, aBin; + osl_getExecutableFile(&aParam.pData); + osl_getSystemPathFromFileURL(aParam.pData, &aBin.pData); + return OUStringToOString(aBin, osl_getThreadTextEncoding()); +} + +void readKDESettings(std::map<OUString, css::beans::Optional<css::uno::Any>>& rSettings) +{ + const std::vector<OUString> aKeys + = { "EnableATToolSupport", "ExternalMailer", "SourceViewFontHeight", + "SourceViewFontName", "WorkPathVariable", "ooInetFTPProxyName", + "ooInetFTPProxyPort", "ooInetHTTPProxyName", "ooInetHTTPProxyPort", + "ooInetHTTPSProxyName", "ooInetHTTPSProxyPort", "ooInetNoProxy", + "ooInetProxyType" }; + + for (const OUString& aKey : aKeys) + { + css::beans::Optional<css::uno::Any> aValue = kf5access::getValue(aKey); + std::pair<OUString, css::beans::Optional<css::uno::Any>> elem + = std::make_pair(aKey, aValue); + rSettings.insert(elem); + } +} + +// init the QApplication when we load the kf5backend into a non-Qt vclplug (e.g. gtk3_kde5) +// TODO: use a helper process to read these values without linking to Qt directly? +// TODO: share this code somehow with Qt5Instance.cxx? +void initQApp(std::map<OUString, css::beans::Optional<css::uno::Any>>& rSettings) +{ + const auto aDisplay = getDisplayArg(); + int nFakeArgc = aDisplay.isEmpty() ? 2 : 3; + char** pFakeArgv = new char*[nFakeArgc]; + + pFakeArgv[0] = strdup(getExecutable().getStr()); + pFakeArgv[1] = strdup("--nocrashhandler"); + if (!aDisplay.isEmpty()) + pFakeArgv[2] = strdup(aDisplay.getStr()); + + char* session_manager = nullptr; + if (auto* session_manager_env = getenv("SESSION_MANAGER")) + { + session_manager = strdup(session_manager_env); + unsetenv("SESSION_MANAGER"); + } + + { + // rhbz#2047319 drop the SolarMutex during the execution of QApplication::init() + // https://invent.kde.org/qt/qt/qtwayland/-/merge_requests/24#note_383915 + SolarMutexReleaser aReleaser; // rhbz#2047319 drop the SolarMutex during the execution + + std::unique_ptr<QApplication> app(new QApplication(nFakeArgc, pFakeArgv)); + QObject::connect(app.get(), &QObject::destroyed, app.get(), [nFakeArgc, pFakeArgv]() { + for (int i = 0; i < nFakeArgc; ++i) + free(pFakeArgv[i]); + delete[] pFakeArgv; + }); + + readKDESettings(rSettings); + } + + if (session_manager != nullptr) + { + // coverity[tainted_string] - trusted source for setenv + setenv("SESSION_MANAGER", session_manager, 1); + free(session_manager); + } +} + +Service::Service() +{ + css::uno::Reference<css::uno::XCurrentContext> context(css::uno::getCurrentContext()); + if (!context.is()) + return; + + OUString desktop; + context->getValueByName("system.desktop-environment") >>= desktop; + + if (desktop == "PLASMA5") + { + if (!qApp) // no qt event loop yet + { + // so we start one and read KDE settings + initQApp(m_KDESettings); + } + else // someone else (most likely kde/qt vclplug) has started qt event loop + // all that is left to do is to read KDE settings + readKDESettings(m_KDESettings); + } +} + +void Service::setPropertyValue(OUString const&, css::uno::Any const&) +{ + throw css::lang::IllegalArgumentException("setPropertyValue not supported", + static_cast<cppu::OWeakObject*>(this), -1); +} + +css::uno::Any Service::getPropertyValue(OUString const& PropertyName) +{ + if (PropertyName == "EnableATToolSupport" || PropertyName == "ExternalMailer" + || PropertyName == "SourceViewFontHeight" || PropertyName == "SourceViewFontName" + || PropertyName == "WorkPathVariable" || PropertyName == "ooInetFTPProxyName" + || PropertyName == "ooInetFTPProxyPort" || PropertyName == "ooInetHTTPProxyName" + || PropertyName == "ooInetHTTPProxyPort" || PropertyName == "ooInetHTTPSProxyName" + || PropertyName == "ooInetHTTPSProxyPort" || PropertyName == "ooInetNoProxy" + || PropertyName == "ooInetProxyType") + { + std::map<OUString, css::beans::Optional<css::uno::Any>>::iterator it + = m_KDESettings.find(PropertyName); + if (it != m_KDESettings.end()) + return css::uno::Any(it->second); + else + return css::uno::Any(css::beans::Optional<css::uno::Any>()); + } + else if (PropertyName == "givenname" || PropertyName == "sn" + || PropertyName == "TemplatePathVariable") + { + return css::uno::Any(css::beans::Optional<css::uno::Any>()); + //TODO: obtain values from KDE? + } + throw css::beans::UnknownPropertyException(PropertyName, static_cast<cppu::OWeakObject*>(this)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_kf5desktop_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new Service()); +} +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/kf5be/kf5be1.component b/shell/source/backends/kf5be/kf5be1.component new file mode 100644 index 000000000..dd893bcb6 --- /dev/null +++ b/shell/source/backends/kf5be/kf5be1.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.configuration.backend.KF5Backend" + constructor="shell_kf5desktop_get_implementation"> + <service name="com.sun.star.configuration.backend.KF5Backend"/> + </implementation> +</component> diff --git a/shell/source/backends/localebe/localebackend.cxx b/shell/source/backends/localebe/localebackend.cxx new file mode 100644 index 000000000..558926d8f --- /dev/null +++ b/shell/source/backends/localebe/localebackend.cxx @@ -0,0 +1,333 @@ +/* -*- 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 <cassert> +#include <limits> + +#include "localebackend.hxx" +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/character.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/mslangid.hxx> + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +static css::beans::Optional<css::uno::Any> ImplGetLocale(LCID lcid) +{ + WCHAR buffer[8]; + PWSTR cp = buffer; + + cp += GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME, buffer, 4 ); + if( cp > buffer ) + { + if( 0 < GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME, cp, buffer + 8 - cp) ) + // #i50822# minus character must be written before cp + *(cp - 1) = '-'; + + return {true, css::uno::Any(OUString(o3tl::toU(buffer)))}; + } + + return {false, {}}; +} + +#elif defined(MACOSX) + +#include <rtl/ustrbuf.hxx> +#include <locale.h> +#include <string.h> + +#include <premac.h> +#include <CoreServices/CoreServices.h> +#include <CoreFoundation/CoreFoundation.h> +#include <postmac.h> + +namespace /* private */ +{ + + void OUStringBufferAppendCFString(OUStringBuffer& buffer, const CFStringRef s) + { + CFIndex lstr = CFStringGetLength(s); + for (CFIndex i = 0; i < lstr; i++) + buffer.append(sal_Unicode(CFStringGetCharacterAtIndex(s, i))); + } + + template <typename T> + class CFGuard + { + public: + explicit CFGuard(T& rT) : rT_(rT) {} + ~CFGuard() { if (rT_) CFRelease(rT_); } + private: + T& rT_; + }; + + typedef CFGuard<CFArrayRef> CFArrayGuard; + typedef CFGuard<CFStringRef> CFStringGuard; + typedef CFGuard<CFTypeRef> CFTypeRefGuard; + + /* For more information on the Apple locale concept please refer to + http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFLocales/Articles/CFLocaleConcepts.html + According to this documentation a locale identifier has the format: language[_country][_variant]* + e.g. es_ES_PREEURO -> spain prior Euro support + Note: The calling code should be able to handle locales with only language information e.g. 'en' for certain + UI languages just the language code will be returned. + */ + + CFStringRef ImplGetAppPreference(const char* pref) + { + CFStringRef csPref = CFStringCreateWithCString(nullptr, pref, kCFStringEncodingASCII); + CFStringGuard csRefGuard(csPref); + + CFTypeRef ref = CFPreferencesCopyAppValue(csPref, kCFPreferencesCurrentApplication); + CFTypeRefGuard refGuard(ref); + + if (ref == nullptr) + return nullptr; + + CFStringRef sref = (CFGetTypeID(ref) == CFArrayGetTypeID()) ? static_cast<CFStringRef>(CFArrayGetValueAtIndex(static_cast<CFArrayRef>(ref), 0)) : static_cast<CFStringRef>(ref); + + // NOTE: this API is only available with macOS >=10.3. We need to use it because + // Apple used non-ISO values on systems <10.2 like "German" for instance but didn't + // upgrade those values during upgrade to newer macOS versions. See also #i54337# + return CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, sref); + } + + css::beans::Optional<css::uno::Any> ImplGetLocale(const char* pref) + { + CFStringRef sref = ImplGetAppPreference(pref); + CFStringGuard srefGuard(sref); + + OUStringBuffer aLocaleBuffer; + aLocaleBuffer.append("en-US"); // initialize with fallback value + + if (sref != nullptr) + { + // split the string into substrings; the first two (if there are two) substrings + // are language and country + CFArrayRef subs = CFStringCreateArrayBySeparatingStrings(nullptr, sref, CFSTR("_")); + CFArrayGuard subsGuard(subs); + + if (subs != nullptr) + { + aLocaleBuffer.setLength(0); // clear buffer which still contains fallback value + + CFStringRef lang = static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 0)); + OUStringBufferAppendCFString(aLocaleBuffer, lang); + + // country also available? Assumption: if the array contains more than one + // value the second value is always the country! + if (CFArrayGetCount(subs) > 1) + { + aLocaleBuffer.append("-"); + CFStringRef country = static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 1)); + OUStringBufferAppendCFString(aLocaleBuffer, country); + } + } + } + return {true, css::uno::Any(aLocaleBuffer.makeStringAndClear())}; + } + +} // namespace /* private */ + +#else + +#include <rtl/ustrbuf.hxx> +#include <cstdlib> +#include <cstring> + +static css::beans::Optional<css::uno::Any> ImplGetLocale(char const * category) +{ + const char *locale = std::getenv("LC_ALL"); + if (locale == nullptr || *locale == '\0') { + locale = std::getenv(category); + if (locale == nullptr || *locale == '\0') { + locale = std::getenv("LANG"); + } + } + + // Return "en-US" for C locales + if( (locale == nullptr) || *locale == '\0' || std::strcmp(locale, "C") == 0 + || std::strcmp(locale, "POSIX") == 0 ) + return {true, css::uno::Any(OUString("en-US"))}; + + + const char *cp; + const char *uscore = nullptr; + const char *end = nullptr; + + // locale string have the format lang[_ctry][.encoding][@modifier] + // Let LanguageTag handle all conversion, but do a sanity and length check + // first. + // For the fallback we are only interested in the first two items, so we + // handle '.' and '@' as string end for that. + for (cp = locale; *cp; cp++) + { + if (*cp == '_' && !uscore) + uscore = cp; + if ((*cp == '.' || *cp == '@') && !end) + end = cp; + if (!rtl::isAscii(static_cast<unsigned char>(*cp))) { + SAL_INFO("shell", "locale env var with non-ASCII content"); + return {false, {}}; + } + } + assert(cp >= locale); + if (cp - locale > std::numeric_limits<sal_Int32>::max()) { + SAL_INFO("shell", "locale env var content too long"); + return {false, {}}; + } + + // This is a tad awkward... but the easiest way to obtain what we're + // actually interested in. For example this also converts + // "ca_ES.UTF-8@valencia" to "ca-ES-valencia". + const OString aLocaleStr(locale); + const LanguageType nLang = MsLangId::convertUnxByteStringToLanguage( aLocaleStr); + if (nLang != LANGUAGE_DONTKNOW) + { + const OUString aLangTagStr( LanguageTag::convertToBcp47( nLang)); + return {true, css::uno::Any(aLangTagStr)}; + } + + // As a fallback, strip encoding and modifier and return just a + // language-country combination and let the caller handle unknowns. + OUStringBuffer aLocaleBuffer; + if (!end) + end = cp; + if( uscore != nullptr ) + { + aLocaleBuffer.appendAscii(locale, uscore++ - locale); + aLocaleBuffer.append("-"); + aLocaleBuffer.appendAscii(uscore, end - uscore); + } + else + { + aLocaleBuffer.appendAscii(locale, end - locale); + } + + return {true, css::uno::Any(aLocaleBuffer.makeStringAndClear())}; +} + +#endif + + +LocaleBackend::LocaleBackend() +{ +} + + +LocaleBackend::~LocaleBackend() +{ +} + + + +css::beans::Optional<css::uno::Any> LocaleBackend::getLocale() +{ +#if defined(_WIN32) + return ImplGetLocale( GetUserDefaultLCID() ); +#elif defined (MACOSX) + return ImplGetLocale("AppleLocale"); +#else + return ImplGetLocale("LC_CTYPE"); +#endif +} + + +css::beans::Optional<css::uno::Any> LocaleBackend::getUILocale() +{ +#if defined(_WIN32) + return ImplGetLocale( MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT) ); +#elif defined(MACOSX) + return ImplGetLocale("AppleLanguages"); +#else + return ImplGetLocale("LC_MESSAGES"); +#endif +} + + +css::beans::Optional<css::uno::Any> LocaleBackend::getSystemLocale() +{ +// note: the implementation differs from getLocale() only on Windows +#if defined(_WIN32) + return ImplGetLocale( GetSystemDefaultLCID() ); +#else + return getLocale(); +#endif +} + + +void LocaleBackend::setPropertyValue( + OUString const &, css::uno::Any const &) +{ + throw css::lang::IllegalArgumentException( + "setPropertyValue not supported", + static_cast< cppu::OWeakObject * >(this), -1); +} + +css::uno::Any LocaleBackend::getPropertyValue( + OUString const & PropertyName) +{ + if ( PropertyName == "Locale" ) { + return css::uno::Any(getLocale()); + } else if (PropertyName == "SystemLocale") + { + return css::uno::Any(getSystemLocale()); + } else if (PropertyName == "UILocale") + { + return css::uno::Any(getUILocale()); + } else { + throw css::beans::UnknownPropertyException( + PropertyName, static_cast< cppu::OWeakObject * >(this)); + } +} + + +OUString SAL_CALL LocaleBackend::getImplementationName() +{ + return "com.sun.star.comp.configuration.backend.LocaleBackend" ; +} + +sal_Bool SAL_CALL LocaleBackend::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence<OUString> SAL_CALL LocaleBackend::getSupportedServiceNames() +{ + return { "com.sun.star.configuration.backend.LocaleBackend" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_LocaleBackend_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new LocaleBackend()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/localebe/localebackend.hxx b/shell/source/backends/localebe/localebackend.hxx new file mode 100644 index 000000000..e2e4a0b36 --- /dev/null +++ b/shell/source/backends/localebe/localebackend.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 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_SHELL_SOURCE_BACKENDS_LOCALEBE_LOCALEBACKEND_HXX +#define INCLUDED_SHELL_SOURCE_BACKENDS_LOCALEBE_LOCALEBACKEND_HXX + +#include <sal/config.h> + +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + + +namespace uno = css::uno ; +namespace lang = css::lang ; + +class LocaleBackend final : public ::cppu::WeakImplHelper < + css::beans::XPropertySet, + lang::XServiceInfo > +{ + + public: + + // XServiceInfo + virtual OUString SAL_CALL + getImplementationName( ) override ; + + virtual sal_Bool SAL_CALL + supportsService( const OUString& aServiceName ) override ; + + virtual uno::Sequence<OUString> SAL_CALL + getSupportedServiceNames( ) override ; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override + { return css::uno::Reference< css::beans::XPropertySetInfo >(); } + + virtual void SAL_CALL setPropertyValue( + OUString const &, css::uno::Any const &) override; + + virtual css::uno::Any SAL_CALL getPropertyValue( + OUString const & PropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL removePropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL addVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + virtual void SAL_CALL removeVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + /** + Service constructor from a service factory. + + @param xContext component context + */ + LocaleBackend(); + + /** Destructor */ + virtual ~LocaleBackend() override ; + + private: + // Returns the user locale + static css::beans::Optional<css::uno::Any> getLocale(); + + // Returns the user UI locale + static css::beans::Optional<css::uno::Any> getUILocale(); + + // Returns the system default locale + static css::beans::Optional<css::uno::Any> getSystemLocale(); +} ; + + +#endif // INCLUDED_SHELL_SOURCE_BACKENDS_LOCALEBE_LOCALEBACKEND_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/localebe/localebe1.component b/shell/source/backends/localebe/localebe1.component new file mode 100644 index 000000000..f19a3f9e8 --- /dev/null +++ b/shell/source/backends/localebe/localebe1.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.configuration.backend.LocaleBackend" + constructor="shell_LocaleBackend_get_implementation"> + <service name="com.sun.star.configuration.backend.LocaleBackend"/> + </implementation> +</component> diff --git a/shell/source/backends/macbe/macbackend.hxx b/shell/source/backends/macbe/macbackend.hxx new file mode 100644 index 000000000..9a19a3002 --- /dev/null +++ b/shell/source/backends/macbe/macbackend.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_SHELL_SOURCE_BACKENDS_MACBE_MACBACKEND_HXX +#define INCLUDED_SHELL_SOURCE_BACKENDS_MACBE_MACBACKEND_HXX + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/string.hxx> + +// FIXME: stdio.h only for debugging... +#include <stdio.h> + +namespace uno = css::uno; +namespace lang = css::lang; + +class MacOSXBackend : public ::cppu::WeakImplHelper <css::beans::XPropertySet, lang::XServiceInfo > +{ + +public: + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(const OUString& aServiceName) override; + + virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override + { return css::uno::Reference< css::beans::XPropertySetInfo >(); } + + virtual void SAL_CALL setPropertyValue( + OUString const &, css::uno::Any const &) override; + + virtual css::uno::Any SAL_CALL getPropertyValue( + OUString const & PropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL removePropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL addVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + virtual void SAL_CALL removeVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + /** + Service constructor from a service factory. + + @param xContext component context + */ + MacOSXBackend(); + + /** Destructor */ + virtual ~MacOSXBackend() override; +}; + +#endif // INCLUDED_SHELL_SOURCE_BACKENDS_MACBE_MACBACKEND_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/macbe/macbackend.mm b/shell/source/backends/macbe/macbackend.mm new file mode 100644 index 000000000..cb35a4b9d --- /dev/null +++ b/shell/source/backends/macbe/macbackend.mm @@ -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 . + */ + + +// For MAXHOSTNAMELEN constant +#include <sys/param.h> + +#include <premac.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <Foundation/NSPathUtilities.h> +#include <postmac.h> + +#include "macbackend.hxx" + +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <osl/file.h> + +#define SPACE ' ' +#define SEMI_COLON ';' + +namespace +{ + +typedef enum { + sHTTP, + sHTTPS, + sFTP +} ServiceType; + +/* + * Returns current proxy settings for selected service type (HTTP or + * FTP) as a C string (in the buffer specified by host and hostSize) + * and a port number. + */ + +bool GetProxySetting(ServiceType sType, char *host, size_t hostSize, UInt16 *port) +{ + bool result; + CFDictionaryRef proxyDict; + CFNumberRef enableNum; + int enable; + CFStringRef hostStr; + CFNumberRef portNum; + int portInt; + + proxyDict = SCDynamicStoreCopyProxies(nullptr); + + if (!proxyDict) + return false; + + CFStringRef proxiesEnable; + CFStringRef proxiesProxy; + CFStringRef proxiesPort; + + switch ( sType ) + { + case sHTTP : proxiesEnable = kSCPropNetProxiesHTTPEnable; + proxiesProxy = kSCPropNetProxiesHTTPProxy; + proxiesPort = kSCPropNetProxiesHTTPPort; + break; + case sHTTPS: proxiesEnable = kSCPropNetProxiesHTTPSEnable; + proxiesProxy = kSCPropNetProxiesHTTPSProxy; + proxiesPort = kSCPropNetProxiesHTTPSPort; + break; + default: proxiesEnable = kSCPropNetProxiesFTPEnable; + proxiesProxy = kSCPropNetProxiesFTPProxy; + proxiesPort = kSCPropNetProxiesFTPPort; + break; + } + // Proxy enabled? + enableNum = static_cast<CFNumberRef>(CFDictionaryGetValue( proxyDict, + proxiesEnable )); + + result = (enableNum != nullptr) && (CFGetTypeID(enableNum) == CFNumberGetTypeID()); + + if (result) + result = CFNumberGetValue(enableNum, kCFNumberIntType, &enable) && (enable != 0); + + // Proxy enabled -> get hostname + if (result) + { + hostStr = static_cast<CFStringRef>(CFDictionaryGetValue( proxyDict, + proxiesProxy )); + + result = (hostStr != nullptr) && (CFGetTypeID(hostStr) == CFStringGetTypeID()); + } + + if (result) + result = CFStringGetCString(hostStr, host, static_cast<CFIndex>(hostSize), kCFStringEncodingASCII); + + // Get proxy port + if (result) + { + portNum = static_cast<CFNumberRef>(CFDictionaryGetValue( proxyDict, + proxiesPort )); + + result = (portNum != nullptr) && (CFGetTypeID(portNum) == CFNumberGetTypeID()); + } + else + { + CFRelease(proxyDict); + return false; + } + + if (result) + result = CFNumberGetValue(portNum, kCFNumberIntType, &portInt); + + if (result) + *port = static_cast<UInt16>(portInt); + + if (proxyDict) + CFRelease(proxyDict); + + if (!result) + { + *host = 0; + *port = 0; + } + + return result; +} + +} // unnamed namespace + +MacOSXBackend::MacOSXBackend() +{ +} + +MacOSXBackend::~MacOSXBackend(void) +{ +} + +static OUString CFStringToOUString(const CFStringRef sOrig) { + CFRetain(sOrig); + + CFIndex nStringLen = CFStringGetLength(sOrig)+1; + + // Allocate a c string buffer + char sBuffer[nStringLen]; + + CFStringGetCString(sOrig, sBuffer, nStringLen, kCFStringEncodingASCII); + + CFRelease(sOrig); + + return OUString::createFromAscii(sBuffer); +} + +static OUString GetOUString( NSString* pStr ) +{ + if( ! pStr ) + return OUString(); + int nLen = [pStr length]; + if( nLen == 0 ) + return OUString(); + + OUStringBuffer aBuf( nLen+1 ); + aBuf.setLength( nLen ); + [pStr getCharacters: + reinterpret_cast<unichar *>(const_cast<sal_Unicode*>(aBuf.getStr()))]; + return aBuf.makeStringAndClear(); +} + +void MacOSXBackend::setPropertyValue( + OUString const &, css::uno::Any const &) +{ + throw css::lang::IllegalArgumentException( + "setPropertyValue not supported", + static_cast< cppu::OWeakObject * >(this), -1); +} + +css::uno::Any MacOSXBackend::getPropertyValue( + OUString const & PropertyName) +{ + if ( PropertyName == "WorkPathVariable" ) + { + OUString aDocDir; + NSArray* pPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, true ); + if( pPaths && [pPaths count] > 0 ) + { + aDocDir = GetOUString( [pPaths objectAtIndex: 0] ); + + OUString aDocURL; + if( aDocDir.getLength() > 0 && + osl_getFileURLFromSystemPath( aDocDir.pData, &aDocURL.pData ) == osl_File_E_None ) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, css::uno::Any( aDocURL ) ) ); + } + else + { + SAL_WARN("shell", "user documents list contains empty file path or conversion failed" ); + } + } + else + { + SAL_WARN("shell", "Got nil or empty list of user document directories" ); + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetFTPProxyName" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sFTP, host, 100, &port); + + if (retVal) + { + auto const Server = OUString::createFromAscii( host ); + if( Server.getLength() > 0 ) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( Server ) ) ); + } + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetFTPProxyPort" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sFTP, host, 100, &port); + + if (retVal && port > 0) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( sal_Int32(port) ) ) ); + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetHTTPProxyName" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sHTTP, host, 100, &port); + + if (retVal) + { + auto const Server = OUString::createFromAscii( host ); + if( Server.getLength() > 0 ) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( Server ) ) ); + } + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetHTTPProxyPort" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sHTTP, host, 100, &port); + + if (retVal && port > 0) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( sal_Int32(port) ) ) ); + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetHTTPSProxyName" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sHTTPS, host, 100, &port); + + if (retVal) + { + auto const Server = OUString::createFromAscii( host ); + if( Server.getLength() > 0 ) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( Server ) ) ); + } + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetHTTPSProxyPort" ) + { + char host[MAXHOSTNAMELEN]; + UInt16 port; + bool retVal; + + retVal = GetProxySetting(sHTTPS, host, 100, &port); + + if (retVal && port > 0) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( sal_Int32(port) ) ) ); + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else if ( PropertyName == "ooInetProxyType" ) + { + // override default for ProxyType, which is "0" meaning "No proxies". + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, uno::Any( sal_Int32(1) ) ) ); + } else if ( PropertyName == "ooInetNoProxy" ) + { + OUString aProxyBypassList; + + CFArrayRef rExceptionsList; + CFDictionaryRef rProxyDict = SCDynamicStoreCopyProxies(nullptr); + + if (!rProxyDict) + rExceptionsList = nullptr; + else + rExceptionsList = static_cast<CFArrayRef>(CFDictionaryGetValue(rProxyDict, kSCPropNetProxiesExceptionsList)); + + if (rExceptionsList) + { + for (CFIndex idx = 0; idx < CFArrayGetCount(rExceptionsList); idx++) + { + CFStringRef rException = static_cast<CFStringRef>(CFArrayGetValueAtIndex(rExceptionsList, idx)); + + if (idx>0) + aProxyBypassList += ";"; + + aProxyBypassList += CFStringToOUString(rException); + } + } + + if (rProxyDict) + CFRelease(rProxyDict); + + // fill proxy bypass list + if( aProxyBypassList.getLength() > 0 ) + { + return css::uno::Any( + css::beans::Optional< css::uno::Any >( + true, + uno::Any( aProxyBypassList.replace( SPACE, SEMI_COLON ) ) ) ); + } + return css::uno::Any(css::beans::Optional< css::uno::Any >()); + } else { + throw css::beans::UnknownPropertyException( + PropertyName, static_cast< cppu::OWeakObject * >(this)); + } +} + +OUString SAL_CALL MacOSXBackend::getImplementationName(void) +{ + return "com.sun.star.comp.configuration.backend.MacOSXBackend"; +} + +sal_Bool SAL_CALL MacOSXBackend::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence<OUString> SAL_CALL MacOSXBackend::getSupportedServiceNames(void) +{ + return { "com.sun.star.configuration.backend.MacOSXBackend" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_MacOSXBackend_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MacOSXBackend()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/macbe/macbe1.component b/shell/source/backends/macbe/macbe1.component new file mode 100644 index 000000000..8b42dc2e4 --- /dev/null +++ b/shell/source/backends/macbe/macbe1.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.configuration.backend.MacOSXBackend" + constructor="shell_MacOSXBackend_get_implementation"> + <service name="com.sun.star.configuration.backend.MacOSXBackend"/> + </implementation> +</component> diff --git a/shell/source/backends/wininetbe/wininetbackend.cxx b/shell/source/backends/wininetbe/wininetbackend.cxx new file mode 100644 index 000000000..71de3b135 --- /dev/null +++ b/shell/source/backends/wininetbe/wininetbackend.cxx @@ -0,0 +1,363 @@ +/* -*- 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 <cstddef> +#include <string_view> + +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <o3tl/string_view.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include "wininetbackend.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <wininet.h> +#include <sal/alloca.h> + +#define WININET_DLL_NAME L"wininet.dll" +#define EQUAL_SIGN '=' +#define COLON ':' +#define SPACE ' ' +#define SEMI_COLON ';' + +namespace { + +struct Library { + HMODULE module; + + explicit Library(HMODULE theModule): module(theModule) {} + + ~Library() { if (module) FreeLibrary(module); } +}; + +struct ProxyEntry +{ + OUString Server; + OUString Port; +}; + + ProxyEntry ReadProxyEntry(std::u16string_view aProxy, std::size_t& i) + { + ProxyEntry aProxyEntry; + + aProxyEntry.Server = o3tl::getToken( aProxy, COLON, i ); + if ( i != std::u16string_view::npos ) + aProxyEntry.Port = o3tl::getToken( aProxy, COLON, i ); + + return aProxyEntry; + } + + ProxyEntry FindProxyEntry(std::u16string_view aProxyList, std::u16string_view aType) + { + std::size_t nIndex = 0; + + do + { + // get the next token, e.g. ftp=server:port + std::u16string_view nextToken = o3tl::getToken( aProxyList, SPACE, nIndex ); + + // split the next token again into the parts separated + // through '=', e.g. ftp=server:port -> ftp and server:port + std::size_t i = 0; + if( nextToken.find( EQUAL_SIGN ) != std::u16string_view::npos ) + { + if( aType == o3tl::getToken( nextToken, EQUAL_SIGN, i ) ) + return ReadProxyEntry(nextToken, i); + } + else if( aType.empty()) + return ReadProxyEntry(nextToken, i); + + } while ( nIndex != std::u16string_view::npos ); + + return ProxyEntry(); + } + +} // unnamed namespace + +WinInetBackend::WinInetBackend() +{ + Library hWinInetDll( LoadLibraryW( WININET_DLL_NAME ) ); + if( hWinInetDll.module ) + { + typedef BOOL ( WINAPI *InternetQueryOption_Proc_T )( HINTERNET, DWORD, LPVOID, LPDWORD ); + + InternetQueryOption_Proc_T lpfnInternetQueryOption = + reinterpret_cast< InternetQueryOption_Proc_T >( + GetProcAddress( hWinInetDll.module, "InternetQueryOptionW" ) ); + if (lpfnInternetQueryOption) + { + // Some Windows versions would fail the InternetQueryOption call + // with ERROR_OUTOFMEMORY when the initial dwLength were zero (and + // are apparently fine with the initial sizeof (INTERNET_PROXY_INFO) + // and need no reallocation), while other versions fail with + // ERROR_INSUFFICIENT_BUFFER upon that initial dwLength and need a + // reallocation: + INTERNET_PROXY_INFO pi; + LPINTERNET_PROXY_INFO lpi = π + DWORD dwLength = sizeof (pi); + bool ok = lpfnInternetQueryOption( + nullptr, + INTERNET_OPTION_PROXY, + lpi, + &dwLength ); + if (!ok) + { + DWORD err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER) + { + // allocate sufficient space on the stack + // insufficient space on the stack results + // in a stack overflow exception, we assume + // this never happens, because of the relatively + // small amount of memory we need + // alloca is nice because it is fast and we don't + // have to free the allocated memory, it will be + // automatically done + lpi = static_cast< LPINTERNET_PROXY_INFO >( + alloca( dwLength ) ); + ok = lpfnInternetQueryOption( + nullptr, + INTERNET_OPTION_PROXY, + lpi, + &dwLength ); + if (!ok) + { + err = GetLastError(); + } + } + if (!ok) + { + SAL_WARN( + "shell", + "InternetQueryOption INTERNET_OPTION_PROXY" + " GetLastError=" << err); + return; + } + } + + // if a proxy is disabled, InternetQueryOption returns + // an empty proxy list, so we don't have to check if + // proxy is enabled or not + + // We use InternetQueryOptionW (see https://msdn.microsoft.com/en-us/library/aa385101); + // it fills INTERNET_PROXY_INFO struct which is defined in WinInet.h to have LPCTSTR + // (i.e., the UNICODE-dependent generic string type expanding to const wchar_t* when + // UNICODE is defined, and InternetQueryOption macro expands to InternetQueryOptionW). + // Thus, it's natural to expect that W version would return wide strings. But it's not + // true. The W version still returns const char* in INTERNET_PROXY_INFO. + OUString aProxyList = OUString::createFromAscii( lpi->lpszProxy ); + OUString aProxyBypassList = OUString::createFromAscii( lpi->lpszProxyBypass ); + + // override default for ProxyType, which is "0" meaning "No proxies". + valueProxyType_.IsPresent = true; + valueProxyType_.Value <<= sal_Int32(1); + + // fill proxy bypass list + if( aProxyBypassList.getLength() > 0 ) + { + OUStringBuffer aReverseList; + sal_Int32 nIndex = 0; + do + { + OUString aToken = aProxyBypassList.getToken( 0, SPACE, nIndex ); + if ( aProxyList.indexOf( aToken ) == -1 ) + { + if ( aReverseList.getLength() ) + { + aReverseList.insert( 0, sal_Unicode( SEMI_COLON ) ); + aReverseList.insert( 0, aToken ); + } + else + aReverseList = aToken; + } + } + while ( nIndex >= 0 ); + + aProxyBypassList = aReverseList.makeStringAndClear(); + + valueNoProxy_.IsPresent = true; + valueNoProxy_.Value <<= aProxyBypassList.replace( SPACE, SEMI_COLON ); + } + + if( aProxyList.getLength() > 0 ) + { + + // this implementation follows the algorithm + // of the internet explorer + // if there are type-dependent proxy settings + // and type independent proxy settings in the + // registry the internet explorer chooses the + // type independent proxy for all settings + // e.g. imagine the following registry entry + // ftp=server:port;http=server:port;server:port + // the last token server:port is type independent + // so the ie chooses this proxy server + + // if there is no port specified for a type independent + // server the ie uses the port of an http server if + // there is one and it has a port + + + ProxyEntry aTypeIndepProxy = FindProxyEntry( aProxyList, u""); + ProxyEntry aHttpProxy = FindProxyEntry( aProxyList, u"http" ); + ProxyEntry aHttpsProxy = FindProxyEntry( aProxyList, u"https" ); + + ProxyEntry aFtpProxy = FindProxyEntry( aProxyList, u"ftp" ); + + if( aTypeIndepProxy.Server.getLength() ) + { + aHttpProxy.Server = aTypeIndepProxy.Server; + aHttpsProxy.Server = aTypeIndepProxy.Server; + aFtpProxy.Server = aTypeIndepProxy.Server; + + if( aTypeIndepProxy.Port.getLength() ) + { + aHttpProxy.Port = aTypeIndepProxy.Port; + aHttpsProxy.Port = aTypeIndepProxy.Port; + aFtpProxy.Port = aTypeIndepProxy.Port; + } + else + { + aFtpProxy.Port = aHttpProxy.Port; + aHttpsProxy.Port = aHttpProxy.Port; + } + } + + // http proxy name + if( aHttpProxy.Server.getLength() > 0 ) + { + valueHttpProxyName_.IsPresent = true; + valueHttpProxyName_.Value <<= aHttpProxy.Server; + } + + // http proxy port + if( aHttpProxy.Port.getLength() > 0 ) + { + valueHttpProxyPort_.IsPresent = true; + valueHttpProxyPort_.Value <<= aHttpProxy.Port.toInt32(); + } + + // https proxy name + if( aHttpsProxy.Server.getLength() > 0 ) + { + valueHttpsProxyName_.IsPresent = true; + valueHttpsProxyName_.Value <<= aHttpsProxy.Server; + } + + // https proxy port + if( aHttpsProxy.Port.getLength() > 0 ) + { + valueHttpsProxyPort_.IsPresent = true; + valueHttpsProxyPort_.Value <<= aHttpsProxy.Port.toInt32(); + } + + // ftp proxy name + if( aFtpProxy.Server.getLength() > 0 ) + { + valueFtpProxyName_.IsPresent = true; + valueFtpProxyName_.Value <<= aFtpProxy.Server; + } + + // ftp proxy port + if( aFtpProxy.Port.getLength() > 0 ) + { + valueFtpProxyPort_.IsPresent = true; + valueFtpProxyPort_.Value <<= aFtpProxy.Port.toInt32(); + } + } + } + } +} + +WinInetBackend::~WinInetBackend() +{ +} + +void WinInetBackend::setPropertyValue( + OUString const &, css::uno::Any const &) +{ + throw css::lang::IllegalArgumentException( + "setPropertyValue not supported", + static_cast< cppu::OWeakObject * >(this), -1); +} + +css::uno::Any WinInetBackend::getPropertyValue( + OUString const & PropertyName) +{ + if ( PropertyName == "ooInetFTPProxyName" ) + { + return css::uno::Any(valueFtpProxyName_); + } else if ( PropertyName == "ooInetFTPProxyPort" ) + { + return css::uno::Any(valueFtpProxyPort_); + } else if ( PropertyName == "ooInetHTTPProxyName" ) + { + return css::uno::Any(valueHttpProxyName_); + } else if ( PropertyName == "ooInetHTTPProxyPort" ) + { + return css::uno::Any(valueHttpProxyPort_); + } else if ( PropertyName == "ooInetHTTPSProxyName" ) + { + return css::uno::Any(valueHttpsProxyName_); + } else if ( PropertyName == "ooInetHTTPSProxyPort" ) + { + return css::uno::Any(valueHttpsProxyPort_); + } else if ( PropertyName == "ooInetNoProxy" ) + { + return css::uno::Any(valueNoProxy_); + } else if ( PropertyName == "ooInetProxyType" ) + { + return css::uno::Any(valueProxyType_); + } else { + throw css::beans::UnknownPropertyException( + PropertyName, static_cast< cppu::OWeakObject * >(this)); + } +} + +OUString SAL_CALL WinInetBackend::getImplementationName() +{ + return "com.sun.star.comp.configuration.backend.WinInetBackend" ; +} + +sal_Bool SAL_CALL WinInetBackend::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence<OUString> SAL_CALL WinInetBackend::getSupportedServiceNames() +{ + return { "com.sun.star.configuration.backend.WinInetBackend" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_WinInetBackend_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new WinInetBackend); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/wininetbe/wininetbackend.hxx b/shell/source/backends/wininetbe/wininetbackend.hxx new file mode 100644 index 000000000..1b5f4ecbf --- /dev/null +++ b/shell/source/backends/wininetbe/wininetbackend.hxx @@ -0,0 +1,110 @@ +/* -*- 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_SHELL_SOURCE_BACKENDS_WININETBE_WININETBACKEND_HXX +#define INCLUDED_SHELL_SOURCE_BACKENDS_WININETBE_WININETBACKEND_HXX + +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + +namespace uno = css::uno ; +namespace lang = css::lang ; + +class WinInetBackend : public ::cppu::WeakImplHelper < + css::beans::XPropertySet, + lang::XServiceInfo > { + + public: + + // XServiceInfo + virtual OUString SAL_CALL + getImplementationName( ) override; + + virtual sal_Bool SAL_CALL + supportsService( const OUString& aServiceName ) override; + + virtual uno::Sequence<OUString> SAL_CALL + getSupportedServiceNames( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override + { return css::uno::Reference< css::beans::XPropertySetInfo >(); } + + virtual void SAL_CALL setPropertyValue( + OUString const &, css::uno::Any const &) override; + + virtual css::uno::Any SAL_CALL getPropertyValue( + OUString const & PropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL removePropertyChangeListener( + OUString const &, + css::uno::Reference< css::beans::XPropertyChangeListener > const &) override + {} + + virtual void SAL_CALL addVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + virtual void SAL_CALL removeVetoableChangeListener( + OUString const &, + css::uno::Reference< css::beans::XVetoableChangeListener > const &) override + {} + + /** + Service constructor from a service factory. + + @param xContext component context + */ + WinInetBackend(); + + /** Destructor */ + ~WinInetBackend() override; + + private: + css::beans::Optional< css::uno::Any > + valueProxyType_; + css::beans::Optional< css::uno::Any > + valueNoProxy_; + css::beans::Optional< css::uno::Any > + valueHttpProxyName_; + css::beans::Optional< css::uno::Any > + valueHttpProxyPort_; + css::beans::Optional< css::uno::Any > + valueHttpsProxyName_; + css::beans::Optional< css::uno::Any > + valueHttpsProxyPort_; + css::beans::Optional< css::uno::Any > + valueFtpProxyName_; + css::beans::Optional< css::uno::Any > + valueFtpProxyPort_; +} ; + + +#endif // INCLUDED_SHELL_SOURCE_BACKENDS_WININETBE_WININETBACKEND_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/backends/wininetbe/wininetbe1.component b/shell/source/backends/wininetbe/wininetbe1.component new file mode 100644 index 000000000..1acdee781 --- /dev/null +++ b/shell/source/backends/wininetbe/wininetbe1.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.configuration.backend.WinInetBackend" + constructor="shell_WinInetBackend_get_implementation"> + <service name="com.sun.star.configuration.backend.WinInetBackend"/> + </implementation> +</component> diff --git a/shell/source/cmdmail/cmdmail.component b/shell/source/cmdmail/cmdmail.component new file mode 100644 index 000000000..6b1106269 --- /dev/null +++ b/shell/source/cmdmail/cmdmail.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.system.SimpleCommandMail" + constructor="shell_CmdMailSuppl_get_implementation"> + <service name="com.sun.star.system.SimpleCommandMail"/> + </implementation> +</component> diff --git a/shell/source/cmdmail/cmdmailmsg.cxx b/shell/source/cmdmail/cmdmailmsg.cxx new file mode 100644 index 000000000..8d6c0865a --- /dev/null +++ b/shell/source/cmdmail/cmdmailmsg.cxx @@ -0,0 +1,214 @@ +/* -*- 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 "cmdmailmsg.hxx" + +using com::sun::star::container::NoSuchElementException; +using com::sun::star::container::XNameAccess; + +using namespace cppu; +using namespace com::sun::star::uno; + + +void SAL_CALL CmdMailMsg::setBody( const OUString& aBody ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_aBody = aBody; +} + +OUString SAL_CALL CmdMailMsg::getBody( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_aBody; +} + +void SAL_CALL CmdMailMsg::setRecipient( const OUString& aRecipient ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_aRecipient = aRecipient; +} + +OUString SAL_CALL CmdMailMsg::getRecipient( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_aRecipient; +} + +void SAL_CALL CmdMailMsg::setCcRecipient( const Sequence< OUString >& aCcRecipient ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_CcRecipients = aCcRecipient; +} + +Sequence< OUString > SAL_CALL CmdMailMsg::getCcRecipient( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_CcRecipients; +} + +void SAL_CALL CmdMailMsg::setBccRecipient( const Sequence< OUString >& aBccRecipient ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_BccRecipients = aBccRecipient; +} + +Sequence< OUString > SAL_CALL CmdMailMsg::getBccRecipient( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_BccRecipients; +} + +void SAL_CALL CmdMailMsg::setOriginator( const OUString& aOriginator ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_aOriginator = aOriginator; +} + +OUString SAL_CALL CmdMailMsg::getOriginator( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_aOriginator; +} + +void SAL_CALL CmdMailMsg::setSubject( const OUString& aSubject ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_aSubject = aSubject; +} + +OUString SAL_CALL CmdMailMsg::getSubject( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_aSubject; +} + +void SAL_CALL CmdMailMsg::setAttachement( const Sequence< OUString >& aAttachment ) +{ + std::scoped_lock aGuard( m_aMutex ); + m_Attachments = aAttachment; +} + +Sequence< OUString > SAL_CALL CmdMailMsg::getAttachement( ) +{ + std::scoped_lock aGuard( m_aMutex ); + return m_Attachments; +} + +Any SAL_CALL CmdMailMsg::getByName( const OUString& aName ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if( aName == "body" && !m_aBody.isEmpty() ) + return Any( m_aBody ); + + if( aName == "from" && !m_aOriginator.isEmpty() ) + return Any( m_aOriginator ); + + else if( aName == "to" && !m_aRecipient.isEmpty() ) + return Any( m_aRecipient ); + + else if( aName == "cc" && m_CcRecipients.hasElements() ) + return Any( m_CcRecipients ); + + else if( aName == "bcc" && m_BccRecipients.hasElements() ) + return Any( m_BccRecipients ); + + else if( aName == "subject" && !m_aSubject.isEmpty() ) + return Any( m_aSubject ); + + else if( aName == "attachment" && m_Attachments.hasElements() ) + return Any( m_Attachments ); + + throw NoSuchElementException("key not found: " + aName, + static_cast < XNameAccess * > (this) ); +} + +Sequence< OUString > SAL_CALL CmdMailMsg::getElementNames( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + sal_Int32 nItems = 0; + Sequence< OUString > aRet( 7 ); + auto pRet = aRet.getArray(); + + if( !m_aBody.isEmpty() ) + pRet[nItems++] = "body"; + + if( !m_aOriginator.isEmpty() ) + pRet[nItems++] = "from"; + + if( !m_aRecipient.isEmpty() ) + pRet[nItems++] = "to"; + + if( m_CcRecipients.hasElements() ) + pRet[nItems++] = "cc"; + + if( m_BccRecipients.hasElements() ) + pRet[nItems++] = "bcc"; + + if( !m_aSubject.isEmpty() ) + pRet[nItems++] = "subject"; + + if( m_Attachments.hasElements() ) + pRet[nItems++] = "attachment"; + + aRet.realloc( nItems ); + return aRet; +} + + sal_Bool SAL_CALL CmdMailMsg::hasByName( const OUString& aName ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if( aName == "body" && !m_aBody.isEmpty() ) + return true; + + if( aName == "from" && !m_aOriginator.isEmpty() ) + return true; + + else if( aName == "to" && !m_aRecipient.isEmpty() ) + return true; + + else if( aName == "cc" && m_CcRecipients.hasElements() ) + return true; + + else if( aName == "bcc" && m_BccRecipients.hasElements() ) + return true; + + else if( aName == "subject" && !m_aSubject.isEmpty() ) + return true; + + else if( aName == "attachment" && m_Attachments.hasElements() ) + return true; + + return false; +} + +Type SAL_CALL CmdMailMsg::getElementType( ) +{ + // returning void for multi type container + return Type(); +} + +sal_Bool SAL_CALL CmdMailMsg::hasElements( ) +{ + return getElementNames().hasElements(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/cmdmail/cmdmailmsg.hxx b/shell/source/cmdmail/cmdmailmsg.hxx new file mode 100644 index 000000000..b48140395 --- /dev/null +++ b/shell/source/cmdmail/cmdmailmsg.hxx @@ -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 . + */ + +#ifndef INCLUDED_SHELL_SOURCE_CMDMAIL_CMDMAILMSG_HXX +#define INCLUDED_SHELL_SOURCE_CMDMAIL_CMDMAILMSG_HXX + +#include <mutex> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/container/XNameAccess.hpp> + +#include <com/sun/star/system/XSimpleMailMessage2.hpp> + + + + +class CmdMailMsg : + public cppu::WeakImplHelper< + css::system::XSimpleMailMessage2, + css::container::XNameAccess > +{ + OUString m_aBody; + OUString m_aRecipient; + OUString m_aOriginator; + OUString m_aSubject; + css::uno::Sequence< OUString > m_CcRecipients; + css::uno::Sequence< OUString > m_BccRecipients; + css::uno::Sequence< OUString > m_Attachments; + + std::mutex m_aMutex; + +public: + + CmdMailMsg() {}; + + + // XSimpleMailMessage + + + virtual void SAL_CALL setBody( const OUString& aBody ) override; + + virtual OUString SAL_CALL getBody( ) override; + + virtual void SAL_CALL setRecipient( const OUString& aRecipient ) override; + + virtual OUString SAL_CALL getRecipient( ) override; + + virtual void SAL_CALL setCcRecipient( const css::uno::Sequence< OUString >& aCcRecipient ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getCcRecipient( ) override; + + virtual void SAL_CALL setBccRecipient( const css::uno::Sequence< OUString >& aBccRecipient ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getBccRecipient( ) override; + + virtual void SAL_CALL setOriginator( const OUString& aOriginator ) override; + + virtual OUString SAL_CALL getOriginator( ) override; + + virtual void SAL_CALL setSubject( const OUString& aSubject ) override; + + virtual OUString SAL_CALL getSubject( ) override; + + virtual void SAL_CALL setAttachement( const css::uno::Sequence< OUString >& aAttachement ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAttachement( ) override; + + + // XNameAccess + + + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override ; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + + // XElementAccess + + + virtual css::uno::Type SAL_CALL getElementType( ) override; + + virtual sal_Bool SAL_CALL hasElements( ) override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/cmdmail/cmdmailsuppl.cxx b/shell/source/cmdmail/cmdmailsuppl.cxx new file mode 100644 index 000000000..00b6517c4 --- /dev/null +++ b/shell/source/cmdmail/cmdmailsuppl.cxx @@ -0,0 +1,287 @@ +/* -*- 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 <osl/thread.h> + +#include <rtl/bootstrap.hxx> + +#include <osl/file.hxx> +#include <rtl/strbuf.hxx> +#include "cmdmailsuppl.hxx" +#include "cmdmailmsg.hxx" +#include <com/sun/star/system/SimpleMailClientFlags.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <tools/diagnose_ex.h> + +using com::sun::star::beans::PropertyValue; +using com::sun::star::system::XSimpleMailClientSupplier; +using com::sun::star::system::XSimpleMailClient; +using com::sun::star::system::XSimpleMailMessage; +using com::sun::star::system::XSimpleMailMessage2; +using com::sun::star::container::XNameAccess; +using osl::FileBase; + +using namespace cppu; +using namespace com::sun::star::system::SimpleMailClientFlags; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::configuration; + +CmdMailSuppl::CmdMailSuppl( const Reference< XComponentContext >& xContext ) +{ + m_xConfigurationProvider = theDefaultProvider::get(xContext); +} + +// XSimpleMailClientSupplier + +Reference< XSimpleMailClient > SAL_CALL CmdMailSuppl::querySimpleMailClient( ) +{ + return static_cast < XSimpleMailClient * > (this); +} + +// XSimpleMailClient + +Reference< XSimpleMailMessage > SAL_CALL CmdMailSuppl::createSimpleMailMessage( ) +{ + return Reference< XSimpleMailMessage >( new CmdMailMsg( ) ); +} + +namespace { + +void appendShellWord(OStringBuffer & buffer, OUString const & word, bool strict) +{ + OString sys; + if (!word.convertToString( + &sys, osl_getThreadTextEncoding(), + (strict + ? (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR) + : OUSTRING_TO_OSTRING_CVTFLAGS))) + { + throw css::uno::Exception( + ("Could not convert \"" + word + "\" to encoding #" + + OUString::number(osl_getThreadTextEncoding())), + css::uno::Reference<css::uno::XInterface>()); + } + buffer.append('\''); + for (sal_Int32 i = 0; i != sys.getLength(); ++i) { + char c = sys[i]; + switch (c) { + case 0: + if (strict) { + throw css::uno::Exception( + "Could not convert word containing NUL, \"" + word + "\"", + css::uno::Reference<css::uno::XInterface>()); + } + break; + case '\'': + buffer.append("'\\''"); + break; + default: + buffer.append(c); + break; + } + } + buffer.append('\''); +} + +} + +void SAL_CALL CmdMailSuppl::sendSimpleMailMessage( const Reference< XSimpleMailMessage >& xSimpleMailMessage, sal_Int32 /*aFlag*/ ) +{ + if ( ! xSimpleMailMessage.is() ) + { + throw css::lang::IllegalArgumentException( "No message specified" , + static_cast < XSimpleMailClient * > (this), 1 ); + } + + if( ! m_xConfigurationProvider.is() ) + { + throw css::uno::Exception( "Can not access configuration" , + static_cast < XSimpleMailClient * > (this) ); + } + + + OUString aProgramURL("$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/senddoc"); + rtl::Bootstrap::expandMacros(aProgramURL); + + OUString aProgram; + if ( FileBase::E_None != FileBase::getSystemPathFromFileURL(aProgramURL, aProgram)) + { + throw css::uno::Exception("Could not convert executable path", + static_cast < XSimpleMailClient * > (this)); + } + + OStringBuffer aBuffer; + appendShellWord(aBuffer, aProgram, true); + + try + { + // Query XNameAccess interface of the org.openoffice.Office.Common/ExternalMailer + // configuration node to retrieve the users preferred email application. This may + // transparently by redirected to e.g. the corresponding GConf setting in GNOME. + + PropertyValue aProperty; + aProperty.Name = "nodepath"; + aProperty.Value <<= OUString("org.openoffice.Office.Common/ExternalMailer"); + + Sequence< Any > aArgumentList{ Any(aProperty) }; + + Reference< XNameAccess > xNameAccess( + m_xConfigurationProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgumentList ), + UNO_QUERY ); + + if( xNameAccess.is() ) + { + OUString aMailer; + + // Retrieve the value for "Program" node and append it feed senddoc with it + // using the (undocumented) --mailclient switch + xNameAccess->getByName("Program") >>= aMailer; + + if( !aMailer.isEmpty() ) + { + // make sure we have a system path + FileBase::getSystemPathFromFileURL( aMailer, aMailer ); + + aBuffer.append(" --mailclient "); + appendShellWord(aBuffer, aMailer, true); + } +#ifdef MACOSX + else + aBuffer.append(" --mailclient Mail"); +#endif + } + + } + + catch(const RuntimeException & ) + { + TOOLS_WARN_EXCEPTION("shell", "RuntimeException caught accessing configuration provider" ); + m_xConfigurationProvider.clear(); + throw; + } + + Reference< XSimpleMailMessage2 > xMessage( xSimpleMailMessage, UNO_QUERY ); + if ( xMessage.is() ) + { + OUString sBody = xMessage->getBody(); + if ( sBody.getLength() > 0 ) + { + aBuffer.append(" --body "); + appendShellWord(aBuffer, sBody, false); + } + } + + // Convert from, to, etc. in a best-effort rather than a strict way to the + // system encoding, based on the assumption that the relevant address parts + // of those strings are ASCII anyway and any problematic characters are only + // in the human-readable, informational-only parts: + + // Append originator if set in the message + if ( !xSimpleMailMessage->getOriginator().isEmpty() ) + { + aBuffer.append(" --from "); + appendShellWord(aBuffer, xSimpleMailMessage->getOriginator(), false); + } + + // Append recipient if set in the message + if ( !xSimpleMailMessage->getRecipient().isEmpty() ) + { + aBuffer.append(" --to "); + appendShellWord(aBuffer, xSimpleMailMessage->getRecipient(), false); + } + + // Append carbon copy recipients set in the message + Sequence< OUString > aStringList = xSimpleMailMessage->getCcRecipient(); + for ( const auto& rString : std::as_const(aStringList) ) + { + aBuffer.append(" --cc "); + appendShellWord(aBuffer, rString, false); + } + + // Append blind carbon copy recipients set in the message + aStringList = xSimpleMailMessage->getBccRecipient(); + for ( const auto& rString : std::as_const(aStringList) ) + { + aBuffer.append(" --bcc "); + appendShellWord(aBuffer, rString, false); + } + + // Append subject if set in the message + if ( !xSimpleMailMessage->getSubject().isEmpty() ) + { + aBuffer.append(" --subject "); + appendShellWord(aBuffer, xSimpleMailMessage->getSubject(), false); + } + + // Append attachments set in the message + aStringList = xSimpleMailMessage->getAttachement(); + for ( const auto& rString : std::as_const(aStringList) ) + { + OUString aSystemPath; + if ( FileBase::E_None == FileBase::getSystemPathFromFileURL(rString, aSystemPath) ) + { + aBuffer.append(" --attach "); + appendShellWord(aBuffer, aSystemPath, true); + } + } + + OString cmd = aBuffer.makeStringAndClear(); + FILE * f = popen(cmd.getStr(), "w"); + if (f == nullptr || pclose(f) != 0) + { + throw css::uno::Exception("No mail client configured", + static_cast < XSimpleMailClient * > (this) ); + } +} + +// XServiceInfo + +OUString SAL_CALL CmdMailSuppl::getImplementationName( ) +{ + return "com.sun.star.comp.system.SimpleCommandMail"; +} + +sal_Bool SAL_CALL CmdMailSuppl::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL CmdMailSuppl::getSupportedServiceNames( ) +{ + return { "com.sun.star.system.SimpleCommandMail" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_CmdMailSuppl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CmdMailSuppl(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/cmdmail/cmdmailsuppl.hxx b/shell/source/cmdmail/cmdmailsuppl.hxx new file mode 100644 index 000000000..43915d662 --- /dev/null +++ b/shell/source/cmdmail/cmdmailsuppl.hxx @@ -0,0 +1,74 @@ +/* -*- 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_SHELL_SOURCE_CMDMAIL_CMDMAILSUPPL_HXX +#define INCLUDED_SHELL_SOURCE_CMDMAIL_CMDMAILSUPPL_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/system/XSimpleMailClient.hpp> + +#include <com/sun/star/system/XSimpleMailClientSupplier.hpp> + + + + +class CmdMailSuppl : + public cppu::WeakImplHelper< + css::system::XSimpleMailClientSupplier, + css::system::XSimpleMailClient, + css::lang::XServiceInfo > +{ + + css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigurationProvider; + +public: + explicit CmdMailSuppl( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + + // XSimpleMailClientSupplier + + + virtual css::uno::Reference< css::system::XSimpleMailClient > SAL_CALL querySimpleMailClient( ) override; + + + // XSimpleMailClient + + + virtual css::uno::Reference< css::system::XSimpleMailMessage > SAL_CALL createSimpleMailMessage( ) override; + + virtual void SAL_CALL sendSimpleMailMessage( const css::uno::Reference< css::system::XSimpleMailMessage >& xSimpleMailMessage, sal_Int32 aFlag ) override; + + + // 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; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/mingw_intel.map b/shell/source/mingw_intel.map new file mode 100644 index 000000000..3b46742fc --- /dev/null +++ b/shell/source/mingw_intel.map @@ -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 . +# + +UDK_3_0_0 { # should have been UDK_3.0 + global: + _ZN9salhelper18ORealDynamicLoader11newInstanceEPPS0_RKN3rtl8OUStringES6_; + _ZN9salhelper18ORealDynamicLoader7acquireEv; + _ZN9salhelper18ORealDynamicLoader7releaseEv; + _ZN9salhelper18ORealDynamicLoaderC1EPPS0_RKN3rtl8OUStringES6_PvS7_; + _ZN9salhelper18ORealDynamicLoaderC2EPPS0_RKN3rtl8OUStringES6_PvS7_; + _ZN9salhelper18ORealDynamicLoaderD0Ev; + _ZN9salhelper18ORealDynamicLoaderD1Ev; + _ZN9salhelper18ORealDynamicLoaderD2Ev; + _ZN9salhelper21SimpleReferenceObjectD0Ev; + _ZN9salhelper21SimpleReferenceObjectD1Ev; + _ZN9salhelper21SimpleReferenceObjectD2Ev; + _ZN9salhelper21SimpleReferenceObjectdlEPv; + _ZN9salhelper21SimpleReferenceObjectnwEj; + _ZNK9salhelper18ORealDynamicLoader6getApiEv; + # _ZTIN9salhelper18ORealDynamicLoaderE; + # _ZTSN9salhelper18ORealDynamicLoaderE; + _ZTVN9salhelper18ORealDynamicLoaderE; + # _ZTIN9salhelper21SimpleReferenceObjectE; + # _ZTSN9salhelper21SimpleReferenceObjectE; + _ZTVN9salhelper21SimpleReferenceObjectE; + + _ZN9salhelper21SimpleReferenceObjectdlEPvRKSt9nothrow_t; + _ZN9salhelper21SimpleReferenceObjectnwEjRKSt9nothrow_t; + _ZN9salhelper9ConditionC1ERN3osl5MutexE; + _ZN9salhelper9ConditionC2ERN3osl5MutexE; + _ZN9salhelper9ConditionD0Ev; + _ZN9salhelper9ConditionD1Ev; + _ZN9salhelper9ConditionD2Ev; + # _ZTIN9salhelper9ConditionE; + # _ZTIS9salhelper9ConditionE; + + _ZN9salhelper17ConditionModifierC1ERNS_9ConditionE; + _ZN9salhelper17ConditionModifierC2ERNS_9ConditionE; + _ZN9salhelper17ConditionModifierD1Ev; + _ZN9salhelper17ConditionModifierD2Ev; + + _ZN9salhelper15ConditionWaiterC1ERNS_9ConditionE; + _ZN9salhelper15ConditionWaiterC1ERNS_9ConditionEm; + _ZN9salhelper15ConditionWaiterC2ERNS_9ConditionE; + _ZN9salhelper15ConditionWaiterC2ERNS_9ConditionEm; + _ZN9salhelper15ConditionWaiterD1Ev; + _ZN9salhelper15ConditionWaiterD2Ev; + + _ZN9salhelper15ConditionWaiter8timedoutaSERKS1_; + _ZN9salhelper15ConditionWaiter8timedoutC1ERKS1_; + _ZN9salhelper15ConditionWaiter8timedoutC1Ev; + _ZN9salhelper15ConditionWaiter8timedoutC2ERKS1_; + _ZN9salhelper15ConditionWaiter8timedoutC2Ev; + _ZN9salhelper15ConditionWaiter8timedoutD0Ev; + _ZN9salhelper15ConditionWaiter8timedoutD1Ev; + _ZN9salhelper15ConditionWaiter8timedoutD2Ev; + # _ZTIN9salhelper15ConditionWaiter8timedoutE; + # _ZTSN9salhelper15ConditionWaiter8timedoutE; +} UDK_3_0_0; diff --git a/shell/source/sessioninstall/SyncDbusSessionHelper.cxx b/shell/source/sessioninstall/SyncDbusSessionHelper.cxx new file mode 100644 index 000000000..606eeb8fe --- /dev/null +++ b/shell/source/sessioninstall/SyncDbusSessionHelper.cxx @@ -0,0 +1,212 @@ +/* -*- 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 "SyncDbusSessionHelper.hxx" + +#include <cppuhelper/supportsservice.hxx> +#include <gio/gio.h> +#include <cstring> +#include <memory> +#include <string_view> +#include <vector> + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +namespace +{ + struct GVariantDeleter { void operator()(GVariant* pV) { if (pV) g_variant_unref(pV); } }; + struct GVariantBuilderDeleter { void operator()(GVariantBuilder* pVB) { g_variant_builder_unref(pVB); } }; + template <typename T> struct GObjectDeleter { void operator()(T* pO) { g_object_unref(pO); } }; + class GErrorWrapper + { + GError* m_pError; + public: + explicit GErrorWrapper() : m_pError(nullptr) {} + ~GErrorWrapper() noexcept(false) + { + if(!m_pError) + return; + OUString sMsg( + m_pError->message, std::strlen(m_pError->message), RTL_TEXTENCODING_UTF8); + g_error_free(m_pError); + throw RuntimeException(sMsg); + } + GError*& getRef() { return m_pError; } + }; + GDBusProxy* lcl_GetPackageKitProxy(std::u16string_view sInterface) + { + const OString sFullInterface = "org.freedesktop.PackageKit." + OUStringToOString(sInterface, RTL_TEXTENCODING_ASCII_US); + GDBusProxy* proxy = nullptr; + { + GErrorWrapper error; + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, nullptr, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + reinterpret_cast<const gchar*>(sFullInterface.getStr()), + nullptr, + &error.getRef()); + } + if(!proxy) + throw RuntimeException("couldn't get a proxy!"); + return proxy; + } + + GVariant* pk_make_platform_data() + { + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + return g_variant_builder_end(&builder); + } + +void request( + char const * method, + css::uno::Sequence<OUString> const & resources, + std::u16string_view interaction) +{ + // Keep strings alive until after call to g_dbus_proxy_call_sync + std::vector<OString> resUtf8; + std::shared_ptr<GVariantBuilder> builder( + g_variant_builder_new(G_VARIANT_TYPE ("as")), GVariantBuilderDeleter()); + for (auto & i: resources) { + auto s(OUStringToOString(i, RTL_TEXTENCODING_UTF8)); + resUtf8.push_back(s); + g_variant_builder_add(builder.get(), "s", s.getStr()); + } + auto iactUtf8(OUStringToOString(interaction, RTL_TEXTENCODING_UTF8)); + std::shared_ptr<GDBusProxy> proxy( + lcl_GetPackageKitProxy(u"Modify2"), GObjectDeleter<GDBusProxy>()); + GErrorWrapper error; + std::shared_ptr<GVariant> result(g_dbus_proxy_call_sync( + proxy.get(), method, + g_variant_new( + "(asss@a{sv})", builder.get(), iactUtf8.getStr(), + "libreoffice-startcenter.desktop", pk_make_platform_data()), + G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error.getRef()), GVariantDeleter()); +} + +} + +namespace shell::sessioninstall +{ + SyncDbusSessionHelper::SyncDbusSessionHelper(Reference<XComponentContext> const&) + { +#if !GLIB_CHECK_VERSION(2,36,0) + g_type_init (); +#endif + } + +Sequence< OUString > SAL_CALL SyncDbusSessionHelper::getSupportedServiceNames() +{ + return { "org.freedesktop.PackageKit.SyncDbusSessionHelper" }; +} + +OUString SAL_CALL SyncDbusSessionHelper::getImplementationName() +{ + return "org.libreoffice.comp.shell.sessioninstall.SyncDbusSessionHelper"; +} + +sal_Bool SAL_CALL SyncDbusSessionHelper::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + +void SyncDbusSessionHelper::InstallPackageFiles( + css::uno::Sequence<OUString> const & files, + OUString const & interaction) +{ + request("InstallPackageFiles", files, interaction); +} + +void SyncDbusSessionHelper::InstallProvideFiles( + css::uno::Sequence<OUString> const & files, + OUString const & interaction) +{ + request("InstallProvideFiles", files, interaction); +} + +void SyncDbusSessionHelper::InstallCatalogs( + css::uno::Sequence<OUString> const & files, + OUString const & interaction) +{ + request("InstallCatalogs", files, interaction); +} + +void SyncDbusSessionHelper::InstallPackageNames( + css::uno::Sequence<OUString> const & packages, + OUString const & interaction) +{ + request("InstallPackageNames", packages, interaction); +} + +void SyncDbusSessionHelper::InstallMimeTypes( + css::uno::Sequence<OUString> const & mimeTypes, + OUString const & interaction) +{ + request("InstallMimeTypes", mimeTypes, interaction); +} + +void SyncDbusSessionHelper::InstallFontconfigResources( + css::uno::Sequence<OUString> const & resources, + OUString const & interaction) +{ + request("InstallFontconfigResources", resources, interaction); +} + +void SyncDbusSessionHelper::InstallGStreamerResources( + css::uno::Sequence<OUString> const & resources, + OUString const & interaction) +{ + request("InstallGStreamerResources", resources, interaction); +} + +void SyncDbusSessionHelper::RemovePackageByFiles( + css::uno::Sequence<OUString> const & files, + OUString const & interaction) +{ + request("RemovePackageByFiles", files, interaction); +} + +void SyncDbusSessionHelper::InstallPrinterDrivers( + css::uno::Sequence<OUString> const & files, + OUString const & interaction) +{ + request("InstallPrinterDrivers", files, interaction); +} + +void SAL_CALL SyncDbusSessionHelper::IsInstalled( const OUString& sPackagename, const OUString& sInteraction, sal_Bool& o_isInstalled ) +{ + const OString sPackagenameAscii = OUStringToOString(sPackagename, RTL_TEXTENCODING_ASCII_US); + const OString sInteractionAscii = OUStringToOString(sInteraction, RTL_TEXTENCODING_ASCII_US); + std::shared_ptr<GDBusProxy> proxy(lcl_GetPackageKitProxy(u"Query"), GObjectDeleter<GDBusProxy>()); + GErrorWrapper error; + std::shared_ptr<GVariant> result(g_dbus_proxy_call_sync (proxy.get(), + "IsInstalled", + g_variant_new ("(ss)", + sPackagenameAscii.getStr(), + sInteractionAscii.getStr()), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + nullptr, /* cancellable */ + &error.getRef()),GVariantDeleter()); + if(result) + o_isInstalled = bool(g_variant_get_boolean(g_variant_get_child_value(result.get(),0))); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_sessioninstall_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SyncDbusSessionHelper(context)); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/sessioninstall/SyncDbusSessionHelper.hxx b/shell/source/sessioninstall/SyncDbusSessionHelper.hxx new file mode 100644 index 000000000..7812604c9 --- /dev/null +++ b/shell/source/sessioninstall/SyncDbusSessionHelper.hxx @@ -0,0 +1,65 @@ +/* -*- 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_SHELL_SOURCE_SESSIONINSTALL_SYNCDBUSSESSIONHELPER_HXX +#define INCLUDED_SHELL_SOURCE_SESSIONINSTALL_SYNCDBUSSESSIONHELPER_HXX + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp> +#include <cppuhelper/implbase.hxx> + +namespace shell::sessioninstall +{ + class SyncDbusSessionHelper : public ::cppu::WeakImplHelper< ::org::freedesktop::PackageKit::XSyncDbusSessionHelper, css::lang::XServiceInfo > + { + public: + SyncDbusSessionHelper(css::uno::Reference< css::uno::XComponentContext> const&); + + // 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; + + // XModify Methods + virtual void SAL_CALL InstallPackageFiles( const css::uno::Sequence< OUString >& files, const OUString& interaction ) override; + + virtual void SAL_CALL InstallProvideFiles( const css::uno::Sequence< OUString >& files, const OUString& interaction ) override; + + virtual void SAL_CALL InstallCatalogs( const css::uno::Sequence< OUString >& files, const OUString& interaction ) override; + + virtual void SAL_CALL InstallPackageNames( const css::uno::Sequence< OUString >& packages, const OUString& interaction ) override; + + virtual void SAL_CALL InstallMimeTypes( const css::uno::Sequence< OUString >& mimeTypes, const OUString& interaction ) override; + + virtual void SAL_CALL InstallFontconfigResources( const css::uno::Sequence< OUString >& resources, const OUString& interaction ) override; + + virtual void SAL_CALL InstallGStreamerResources( const css::uno::Sequence< OUString >& resources, const OUString& interaction ) override; + + virtual void SAL_CALL InstallResources( const css::uno::Sequence< OUString >& /* types */, const css::uno::Sequence< OUString >& /* resources */, const OUString& /* interaction */ ) override + { throw css::uno::RuntimeException(); } // not implemented + + virtual void SAL_CALL RemovePackageByFiles( const css::uno::Sequence< OUString >& files, const OUString& interaction ) override; + + virtual void SAL_CALL InstallPrinterDrivers( const css::uno::Sequence< OUString >& files, const OUString& interaction ) override; + + // XQuery Methods + virtual void SAL_CALL IsInstalled( const OUString& /* package_name */, const OUString& /* interaction */, sal_Bool& /* installed */ ) override; + + virtual void SAL_CALL SearchFile( const OUString& /* file_name */, const OUString& /* interaction */, sal_Bool& /* installed */, OUString& /* package_name */ ) override + { throw css::uno::RuntimeException(); } // not implemented + + private: + SyncDbusSessionHelper( const SyncDbusSessionHelper& ) = delete; + SyncDbusSessionHelper& operator=( const SyncDbusSessionHelper& ) = delete; + }; +} + +#endif // INCLUDED_SHELL_SOURCE_SESSIONINSTALL_SYNCDBUSSESSIONHELPER_HXX +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/sessioninstall/losessioninstall.component b/shell/source/sessioninstall/losessioninstall.component new file mode 100644 index 000000000..4dbc3bf0f --- /dev/null +++ b/shell/source/sessioninstall/losessioninstall.component @@ -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/. + * +--> +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.libreoffice.comp.shell.sessioninstall.SyncDbusSessionHelper" + constructor="shell_sessioninstall_get_implementation"> + <service name="org.freedesktop.PackageKit.SyncDbusSessionHelper"/> + </implementation> +</component> diff --git a/shell/source/tools/lngconvex/cmdline.cxx b/shell/source/tools/lngconvex/cmdline.cxx new file mode 100644 index 000000000..e3f24e1d8 --- /dev/null +++ b/shell/source/tools/lngconvex/cmdline.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 <stdexcept> +#include "cmdline.hxx" + +/** Simple command line abstraction +*/ + +// Creation + +CommandLine::CommandLine(size_t argc, char* argv[]) + : m_argc(argc) + , m_argv(argv) +{ +} + +// Query + +/** Returns an argument by name. If there are + duplicate argument names in the command line, + the first one wins. + Argument name and the argument value must be separated + by spaces. If the argument value starts with an + argument prefix use quotes else the return value is + an empty string because the value will be interpreted + as the next argument name. + If an argument value contains spaces use quotes. + + @precond GetArgumentNames() -> has element ArgumentName + + @throws std::invalid_argument exception + if the specified argument could not be + found +*/ +std::string CommandLine::get_arg(const std::string& ArgumentName) const +{ + std::string arg_value; + size_t i; + for (i = 0; i < m_argc; i++) + { + std::string arg = m_argv[i]; + + if (ArgumentName == arg && ((i + 1) < m_argc) && !is_arg_name(m_argv[i + 1])) + { + arg_value = m_argv[i + 1]; + break; + } + } + + if (i == m_argc) + throw std::invalid_argument("Invalid argument name"); + + return arg_value; +} + +// Command + +/** Returns whether a given argument is an argument name +*/ +bool CommandLine::is_arg_name(const std::string& Argument) +{ + return (Argument.length() > 0 && Argument[0] == '-'); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/tools/lngconvex/cmdline.hxx b/shell/source/tools/lngconvex/cmdline.hxx new file mode 100644 index 000000000..507ace617 --- /dev/null +++ b/shell/source/tools/lngconvex/cmdline.hxx @@ -0,0 +1,74 @@ +/* -*- 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_SHELL_SOURCE_TOOLS_LNGCONVEX_CMDLINE_HXX +#define INCLUDED_SHELL_SOURCE_TOOLS_LNGCONVEX_CMDLINE_HXX + +#include <string> + +#include <sal/config.h> + +/** Simple command line abstraction +*/ + +class CommandLine +{ +public: + // Creation + + CommandLine(size_t argc, char* argv[]); + + // Query + + /** Returns an argument by name. If there are + duplicate argument names in the command line, + the first one wins. + Argument name and the argument value must be separated + by spaces. If the argument value starts with an + argument prefix use quotes else the return value is + an empty string because the value will be interpreted + as the next argument name. + If an argument value contains spaces use quotes. + + @precond GetArgumentNames() -> has element ArgumentName + + @throws std::invalid_argument exception + if the specified argument could not be + found + */ + std::string get_arg(const std::string& ArgumentName) const; + +private: + /** Returns whether a given argument is an argument name + */ + static bool is_arg_name(const std::string& Argument); + +private: + size_t m_argc; + char** m_argv; + + // prevent copy and assignment +private: + CommandLine(const CommandLine&) = delete; + CommandLine& operator=(const CommandLine&) = delete; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/tools/lngconvex/lngconvex.cxx b/shell/source/tools/lngconvex/lngconvex.cxx new file mode 100644 index 000000000..fc8196449 --- /dev/null +++ b/shell/source/tools/lngconvex/lngconvex.cxx @@ -0,0 +1,537 @@ +/* -*- 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 . + */ + +#ifdef AIX +# undef _THREAD_SAFE +#endif + + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#else +// From MinGW +typedef unsigned short WORD; +#define PRIMARYLANGID(lgid) (static_cast<WORD>(lgid) & 0x3ff) +#define SUBLANGID(lgid) (static_cast<WORD>(lgid) >> 10) +#define LANG_SPANISH 0x0a +#define SUBLANG_NEUTRAL 0x00 +#define SUBLANG_SPANISH 0x01 +#endif + +#include "cmdline.hxx" + +#include <comphelper/string.hxx> +#include <osl/thread.h> +#include <osl/process.h> +#include <osl/file.hxx> +#include <sal/main.h> + +#include <tools/config.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <iostream> +#include <fstream> +#include <map> +#include <iterator> +#include <string> +#include <string_view> + +#ifndef _WIN32 +#include <cstring> +#endif + +namespace /* private */ +{ + + +void ShowUsage() +{ + std::cout << "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl; + std::cout << "-ulf Name of the ulf file" << std::endl; + std::cout << "-rc Name of the resulting resource file" << std::endl; + std::cout << "-rct Name of the resource template file" << std::endl; + std::cout << "-rch Name of the resource file header" << std::endl; + std::cout << "-rcf Name of the resource file footer" << std::endl; +} + +OUString OStringToOUString(std::string_view str) +{ return rtl::OStringToOUString(str, osl_getThreadTextEncoding()); } + +OString OUStringToOString(std::u16string_view str) +{ return rtl::OUStringToOString(str, osl_getThreadTextEncoding()); } + +/** Get the directory where the module + is located as system directory, the + returned directory has a trailing '\' */ +OUString get_module_path() +{ + OUString cwd_url; + OUString module_path; + if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData)) + osl::FileBase::getSystemPathFromFileURL(cwd_url, module_path); + + return module_path; +} + +/** Make the absolute directory of a base and + a relative directory, if the relative + directory is absolute the relative + directory will be returned unchanged. + Base and relative directory should be + system paths the returned directory is + a system path too */ +OUString get_absolute_path( + const OUString& BaseDir, const OUString& RelDir) +{ + OUString base_url; + OUString rel_url; + + osl::FileBase::getFileURLFromSystemPath(BaseDir, base_url); + osl::FileBase::getFileURLFromSystemPath(RelDir, rel_url); + + OUString abs_url; + (void)osl::FileBase::getAbsoluteFileURL(base_url, rel_url, abs_url); + + OUString abs_sys_path; + osl::FileBase::getSystemPathFromFileURL(abs_url, abs_sys_path); + + return abs_sys_path; +} + +OString get_absolute_file_path(const std::string& file_name) +{ + OUString fp = get_absolute_path( + get_module_path(), OStringToOUString(file_name.c_str())); + return OUStringToOString(fp); +} + +/** A helper class, enables stream exceptions + on construction, restores the old exception + state on destruction */ +class StreamExceptionsEnabler +{ +public: + explicit StreamExceptionsEnabler( + std::ios& iostrm ) : + m_IoStrm(iostrm), + m_OldIos(m_IoStrm.exceptions()) + { + m_IoStrm.exceptions(std::ios::failbit | std::ios::badbit); + } + + ~StreamExceptionsEnabler() + { + m_IoStrm.exceptions(m_OldIos); + } +private: + std::ios& m_IoStrm; + std::ios::iostate m_OldIos; +}; + +class iso_lang_identifier +{ +public: + iso_lang_identifier() {}; + + explicit iso_lang_identifier(const OString& str) : + maBcp47(str) + { } + + explicit iso_lang_identifier(const std::string& str) : + maBcp47(str.c_str()) + { } + + OUString make_OUString() const + { return OStringToOUString( maBcp47, RTL_TEXTENCODING_ASCII_US); } + + std::string make_std_string() const + { return maBcp47.getStr(); } + +private: + OString maBcp47; +}; + +/** Convert an OUString to the MS resource + file format string e.g. + OUString -> L"\x1A00\x2200\x3400" */ +std::string make_winrc_unicode_string(const OUString& str) +{ + std::ostringstream oss; + oss << "L\""; + + size_t length = str.getLength(); + const sal_Unicode* pchr = str.getStr(); + + for (size_t i = 0; i < length; i++) + oss << "\\x" << std::hex << static_cast<int>(*pchr++); + + oss << "\""; + return oss.str(); +} + +std::string make_winrc_unicode_string(const std::string& str) +{ + return make_winrc_unicode_string( + OUString::createFromAscii(str.c_str())); +} + +/** A replacement table contains pairs of + placeholders and the appropriate substitute */ +class Substitutor +{ +private: + typedef std::map<std::string, std::string> replacement_table_t; + typedef std::map<std::string, replacement_table_t> iso_lang_replacement_table_t; + +public: + typedef iso_lang_replacement_table_t::iterator iterator; + typedef iso_lang_replacement_table_t::const_iterator const_iterator; + + iterator begin() + { return iso_lang_replacement_table_.begin(); } + + iterator end() + { return iso_lang_replacement_table_.end(); } + +public: + + Substitutor() {}; + + void set_language(const iso_lang_identifier& iso_lang) + { + active_iso_lang_ = iso_lang; + } + + // If Text is a placeholder substitute it with + //its substitute else leave it unchanged + void substitute(std::string& Text) + { + replacement_table_t& prt = get_replacement_table(active_iso_lang_.make_std_string()); + replacement_table_t::iterator iter = prt.find(Text); + if (iter != prt.end()) + Text = iter->second; + } + + void add_substitution( + const std::string& Placeholder, const std::string& Substitute) + { + replacement_table_t& prt = get_replacement_table(active_iso_lang_.make_std_string()); + prt.insert(std::make_pair(Placeholder, Substitute)); + } + + +private: + // Return the replacement table for the iso lang id + // create a new one if not already present + replacement_table_t& get_replacement_table(const std::string& iso_lang) + { + return iso_lang_replacement_table_[iso_lang]; + } + +private: + iso_lang_replacement_table_t iso_lang_replacement_table_; + iso_lang_identifier active_iso_lang_; +}; + +void add_group_entries( + Config& aConfig, + const OString& GroupName, + Substitutor& Substitutor) +{ + OSL_ASSERT(aConfig.HasGroup(GroupName)); + + aConfig.SetGroup(GroupName); + size_t key_count = aConfig.GetKeyCount(); + std::map< LanguageType, std::string > map; + + for (size_t i = 0; i < key_count; i++) + { + OString iso_lang = aConfig.GetKeyName(sal::static_int_cast<sal_uInt16>(i)); + OString key_value_utf8 = aConfig.ReadKey(sal::static_int_cast<sal_uInt16>(i)); + iso_lang_identifier myiso_lang( iso_lang ); + LanguageType ltype = LanguageTag( myiso_lang.make_OUString()).makeFallback().getLanguageType(); + if( ( static_cast<sal_uInt16>(ltype) & 0x0200 ) == 0 && map[ ltype ].empty() ) + { + Substitutor.set_language(iso_lang_identifier(iso_lang)); + + key_value_utf8 = comphelper::string::strip(key_value_utf8, '\"'); + + OUString key_value_utf16 = + OStringToOUString(key_value_utf8, RTL_TEXTENCODING_UTF8); + + Substitutor.add_substitution( + GroupName.getStr(), make_winrc_unicode_string(key_value_utf16)); + map[ ltype ] = std::string( iso_lang.getStr() ); + } + else + { + if( !map[ ltype ].empty() ) + { + printf("ERROR: Duplicated ms id %d found for the languages %s and %s !!!! This does not work in microsoft resources\nPlease remove one!\n", static_cast<sal_uInt16>(ltype) , map[ ltype ].c_str() , iso_lang.getStr()); + exit( -1 ); + } + } + } +} + +void read_ulf_file(const std::string& FileName, Substitutor& Substitutor) +{ + // work-around for #i32420# + + // as the Config class is currently not able to deal correctly with + // UTF8 files starting with a byte-order-mark we create a copy of the + // original file without the byte-order-mark + OUString tmpfile_url; + osl_createTempFile(nullptr, nullptr, &tmpfile_url.pData); + + OUString tmpfile_sys; + osl::FileBase::getSystemPathFromFileURL(tmpfile_url, tmpfile_sys); + + std::ifstream in(FileName.c_str()); + std::ofstream out(OUStringToOString(tmpfile_sys).getStr()); + + try + { + StreamExceptionsEnabler sexc_out(out); + StreamExceptionsEnabler sexc_in(in); + + //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files + unsigned char const BOM[3] = {0xEF, 0xBB, 0xBF}; + char buff[3]; + in.read(&buff[0], 3); + + if (memcmp(buff, BOM, 3) != 0) + in.seekg(0); + + std::string line; + while (std::getline(in, line)) + out << line << std::endl; + } + catch (const std::ios::failure&) + { + if (!in.eof()) + throw; + } + + + // end work-around for #i32420# + + Config config(tmpfile_url); + size_t grpcnt = config.GetGroupCount(); + for (size_t i = 0; i < grpcnt; i++) + add_group_entries(config, config.GetGroupName(sal::static_int_cast<sal_uInt16>(i)), Substitutor); +} + +void read_file( + const std::string& fname, + std::vector<std::string>& string_container) +{ + std::ifstream file(fname.c_str()); + StreamExceptionsEnabler sexc(file); + + try + { + std::string line; + while (std::getline(file, line)) + string_container.push_back(line); + } + catch(const std::ios::failure&) + { + if (!file.eof()) + throw; + } +} + +/** A simple helper function that happens the + content of one file to another one */ +void concatenate_files(std::ostream& os, std::istream& is) +{ + StreamExceptionsEnabler os_sexc(os); + StreamExceptionsEnabler is_sexc(is); + + try + { + std::string line; + while (std::getline(is, line)) + os << line << std::endl; + } + catch(const std::ios::failure&) + { + if (!is.eof()) + throw; + } +} + +bool is_placeholder(const std::string& str) +{ + return ((str.length() > 1) && + ('%' == str[0]) && + ('%' == str[str.length() - 1])); +} + +void start_language_section( + std::ostream_iterator<std::string>& ostream_iter, const iso_lang_identifier& iso_lang) +{ + ostream_iter = std::string(); + + std::string lang_section("LANGUAGE "); + + LanguageType ltype = LanguageTag( iso_lang.make_OUString()).makeFallback().getLanguageType(); + + char buff[10]; + int primLangID = PRIMARYLANGID(ltype); + int subLangID = SUBLANGID(ltype); + // Our resources are normally not sub language dependent. + // Esp. for spanish we don't want to distinguish between trad. + // and international sorting (which leads to two different sub languages) + // Setting the sub language to neutral allows us to use one + // stringlist for all spanish variants + if ( ( primLangID == LANG_SPANISH ) && + ( subLangID == SUBLANG_SPANISH ) ) + subLangID = SUBLANG_NEUTRAL; + +#ifdef _WIN32 + _itoa(primLangID, buff, 16); +#else + sprintf(buff, "%x", primLangID); +#endif + lang_section += std::string("0x") + std::string(buff); + + lang_section += std::string(" , "); + +#ifdef _WIN32 + _itoa(subLangID, buff, 16); +#else + sprintf(buff, "%x", subLangID); +#endif + lang_section += std::string("0x") + std::string(buff); + ostream_iter = lang_section; +} + +/** Iterate all languages in the substitutor, + replace the all placeholder and append the + result to the output file */ +void inflate_rc_template_to_file( + std::ostream& os, const std::vector<std::string>& rctmpl, Substitutor& substitutor) +{ + StreamExceptionsEnabler sexc(os); + + Substitutor::const_iterator iter = substitutor.begin(); + Substitutor::const_iterator iter_end = substitutor.end(); + + std::ostream_iterator<std::string> oi(os, "\n"); + + for ( /**/ ;iter != iter_end; ++iter) + { + substitutor.set_language(iso_lang_identifier(iter->first)); + + if (!rctmpl.empty()) + start_language_section(oi, iso_lang_identifier(iter->first)); + + for ( auto& rct : rctmpl) + { + std::istringstream iss(rct); + std::string line; + + while (iss) + { + std::string token; + iss >> token; + substitutor.substitute(token); + + // HACK for partially merged + // *.lng files where some strings have + // a particular language that others + // don't have in order to keep the + // build + // coverity[tainted_data] - trusted data source + if (is_placeholder(token)) + token = make_winrc_unicode_string(token); + + line += token; + line += " "; + } + oi = line; + } + } +} + +} // namespace /* private */ + +/* MAIN + The file names provided via command line should be + absolute or relative to the directory of this module. + + Algo: + 1. read the ulf file and initialize the substitutor + 2. read the resource template file + 3. create the output file and append the header + 4. inflate the resource template to the output file + for every language using the substitutor + 5. append the footer +*/ +#define MAKE_ABSOLUTE(s) (get_absolute_file_path((s)).getStr()) +#define ULF_FILE(c) MAKE_ABSOLUTE((c).get_arg("-ulf")) +#define RC_TEMPLATE(c) MAKE_ABSOLUTE((c).get_arg("-rct")) +#define RC_FILE(c) MAKE_ABSOLUTE((c).get_arg("-rc")) +#define RC_HEADER(c) MAKE_ABSOLUTE((c).get_arg("-rch")) +#define RC_FOOTER(c) MAKE_ABSOLUTE((c).get_arg("-rcf")) + +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) +{ + try + { + CommandLine cmdline(argc, argv); + + Substitutor substitutor; + read_ulf_file(ULF_FILE(cmdline), substitutor); + + std::vector<std::string> rc_tmpl; + read_file(RC_TEMPLATE(cmdline), rc_tmpl); + + std::ofstream rc_file(RC_FILE(cmdline)); + std::ifstream in_header(RC_HEADER(cmdline)); + concatenate_files(rc_file, in_header); + + inflate_rc_template_to_file(rc_file, rc_tmpl, substitutor); + + std::ifstream in_footer(RC_FOOTER(cmdline)); + concatenate_files(rc_file, in_footer); + } + catch(const std::ios::failure& ex) + { + std::cout << ex.what() << std::endl; + return 1; + } + catch(const std::exception& ex) + { + std::cout << ex.what() << std::endl; + ShowUsage(); + return 1; + } + catch(...) + { + std::cout << "Unexpected error..." << std::endl; + return 1; + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/tools/regsvrex/regsvrex.cxx b/shell/source/tools/regsvrex/regsvrex.cxx new file mode 100644 index 000000000..c7fb87943 --- /dev/null +++ b/shell/source/tools/regsvrex/regsvrex.cxx @@ -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 . + */ + + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef HRESULT (__stdcall *lpfnDllRegisterServer)(); +typedef HRESULT (__stdcall *lpfnDllUnregisterServer)(); + +static bool IsUnregisterParameter(const wchar_t* Param) +{ + return ((0 == _wcsicmp(Param, L"/u")) || + (0 == _wcsicmp(Param, L"-u"))); +} + +int wmain(int argc, wchar_t* argv[]) +{ + HMODULE hmod; + + if (2 == argc) + { + hmod = LoadLibraryW(argv[1]); + + if (hmod) + { + lpfnDllRegisterServer lpfn_register = reinterpret_cast<lpfnDllRegisterServer>(GetProcAddress( + hmod, "DllRegisterServer")); + + if (lpfn_register) + lpfn_register(); + + FreeLibrary(hmod); + } + } + else if (3 == argc && IsUnregisterParameter(argv[1])) + { + hmod = LoadLibraryW(argv[2]); + + if (hmod) + { + lpfnDllUnregisterServer lpfn_unregister = reinterpret_cast<lpfnDllUnregisterServer>(GetProcAddress( + hmod, "DllUnregisterServer")); + + if (lpfn_unregister) + lpfn_unregister(); + + FreeLibrary(hmod); + } + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/unix/exec/shellexec.cxx b/shell/source/unix/exec/shellexec.cxx new file mode 100644 index 000000000..195a69788 --- /dev/null +++ b/shell/source/unix/exec/shellexec.cxx @@ -0,0 +1,291 @@ +/* -*- 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 <osl/thread.h> +#include <osl/file.hxx> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> + +#include "shellexec.hxx" +#include <com/sun/star/system/SystemShellExecuteException.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/security/AccessControlException.hpp> +#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/lok.hxx> + +#include <string.h> +#include <errno.h> + +#if defined MACOSX +#include <sys/stat.h> +#endif + +#ifdef EMSCRIPTEN +#include <rtl/uri.hxx> +extern void execute_browser(const char* sUrl); +#endif + +using com::sun::star::system::XSystemShellExecute; +using com::sun::star::system::SystemShellExecuteException; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::system::SystemShellExecuteFlags; +using namespace cppu; + +#ifndef EMSCRIPTEN +namespace +{ + void escapeForShell( OStringBuffer & rBuffer, const OString & rURL) + { + sal_Int32 nmax = rURL.getLength(); + for(sal_Int32 n=0; n < nmax; ++n) + { + // escape every non alpha numeric characters (excluding a few "known good") by prepending a '\' + char c = rURL[n]; + if( ( c < 'A' || c > 'Z' ) && ( c < 'a' || c > 'z' ) && ( c < '0' || c > '9' ) && c != '/' && c != '.' ) + rBuffer.append( '\\' ); + + rBuffer.append( c ); + } + } +} +#endif + +ShellExec::ShellExec( const Reference< XComponentContext >& xContext ) : + m_xContext(xContext) +{ +} + +void SAL_CALL ShellExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) +{ +#ifndef EMSCRIPTEN + OStringBuffer aBuffer, aLaunchBuffer; + + if (comphelper::LibreOfficeKit::isActive()) + { + SAL_WARN("shell", "Unusual - shell attempt to launch " << aCommand << " with params " << aParameter << " under lok"); + return; + } + + // DESKTOP_LAUNCH, see http://freedesktop.org/pipermail/xdg/2004-August/004489.html + static const char *pDesktopLaunch = getenv( "DESKTOP_LAUNCH" ); + + // Check whether aCommand contains an absolute URI reference: + css::uno::Reference< css::uri::XUriReference > uri( + css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand)); + if (uri.is() && uri->isAbsolute()) + { + // It seems to be a URL... + // We need to re-encode file urls because osl_getFileURLFromSystemPath converts + // to UTF-8 before encoding non ascii characters, which is not what other apps + // expect. + OUString aURL = css::uri::ExternalUriReferenceTranslator::create( + m_xContext)->translateToExternal(aCommand); + if ( aURL.isEmpty() && !aCommand.isEmpty() ) + { + throw RuntimeException( + "Cannot translate URI reference to external format: " + + aCommand, + static_cast< cppu::OWeakObject * >(this)); + } + +#ifdef MACOSX + bool dir = false; + if (uri->getScheme().equalsIgnoreAsciiCase("file")) { + OUString pathname; + auto const e1 = osl::FileBase::getSystemPathFromFileURL(aCommand, pathname); + if (e1 != osl::FileBase::E_None) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, getSystemPathFromFileURL <" + aCommand + + "> failed with " + OUString::number(e1)), + {}, 0); + } + OString pathname8; + if (!pathname.convertToString( + &pathname8, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute, cannot convert \"" + pathname + "\" to UTF-8", {}, + 0); + } + struct stat st; + auto const e2 = lstat(pathname8.getStr(), &st); + if (e2 != 0) { + auto const e3 = errno; + SAL_INFO("shell", "lstat(" << pathname8 << ") failed with errno " << e3); + } + if (e2 != 0) { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute, cannot process <" + aCommand + ">", {}, 0); + } else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) { + dir = true; + } else if ((nFlags & css::system::SystemShellExecuteFlags::URIS_ONLY) != 0 + && (!S_ISREG(st.st_mode) + || (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) + { + throw css::security::AccessControlException( + "XSystemShellExecute.execute, bad <" + aCommand + ">", {}, {}); + } else if (pathname.endsWithIgnoreAsciiCase(".class") + || pathname.endsWithIgnoreAsciiCase(".dmg") + || pathname.endsWithIgnoreAsciiCase(".fileloc") + || pathname.endsWithIgnoreAsciiCase(".inetloc") + || pathname.endsWithIgnoreAsciiCase(".ipa") + || pathname.endsWithIgnoreAsciiCase(".jar") + || pathname.endsWithIgnoreAsciiCase(".terminal")) + { + dir = true; + } + } + + //TODO: Using open(1) with an argument that syntactically is an absolute + // URI reference does not necessarily give expected results: + // 1 If the given URI reference matches a supported scheme (e.g., + // "mailto:foo"): + // 1.1 If it matches an existing pathname (relative to CWD): Results + // in "mailto:foo?\n[0]\tcancel\n[1]\tOpen the file\tmailto:foo\n[2]\t + // Open the URL\tmailto:foo\n\nWhich did you mean? Cancelled." on + // stderr and SystemShellExecuteException. + // 1.2 If it does not match an exitsting pathname (relative to CWD): + // Results in the corresponding application being opened with the given + // document (e.g., Mail with a New Message). + // 2 If the given URI reference does not match a supported scheme + // (e.g., "foo:bar"): + // 2.1 If it matches an existing pathname (relative to CWD) pointing to + // an executable: Results in execution of that executable. + // 2.2 If it matches an existing pathname (relative to CWD) pointing to + // a non-executable regular file: Results in opening it in TextEdit. + // 2.3 If it matches an existing pathname (relative to CWD) pointing to + // a directory: Results in opening it in Finder. + // 2.4 If it does not match an exitsting pathname (relative to CWD): + // Results in "The file /.../foo:bar does not exits." (where "/..." is + // the CWD) on stderr and SystemShellExecuteException. + aBuffer.append("open"); + if (dir) { + aBuffer.append(" -R"); + } + aBuffer.append(" --"); +#else + // Just use xdg-open on non-Mac + aBuffer.append("xdg-open"); +#endif + aBuffer.append(" "); + escapeForShell(aBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding())); + + if ( pDesktopLaunch && *pDesktopLaunch ) + { + aLaunchBuffer.append( pDesktopLaunch + OString::Concat(" ")); + escapeForShell(aLaunchBuffer, OUStringToOString(aURL, osl_getThreadTextEncoding())); + } + } else if ((nFlags & css::system::SystemShellExecuteFlags::URIS_ONLY) != 0) + { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute URIS_ONLY with non-absolute" + " URI reference " + + aCommand, + static_cast< cppu::OWeakObject * >(this), 0); + } else { + escapeForShell(aBuffer, OUStringToOString(aCommand, osl_getThreadTextEncoding())); + aBuffer.append(" "); + if( nFlags != 42 ) + escapeForShell(aBuffer, OUStringToOString(aParameter, osl_getThreadTextEncoding())); + else + aBuffer.append(OUStringToOString(aParameter, osl_getThreadTextEncoding())); + } + + // Prefer DESKTOP_LAUNCH when available + if ( !aLaunchBuffer.isEmpty() ) + { + FILE *pLaunch = popen( aLaunchBuffer.makeStringAndClear().getStr(), "w" ); + if ( pLaunch != nullptr ) + { + if ( 0 == pclose( pLaunch ) ) + return; + } + // Failed, do not try DESKTOP_LAUNCH any more + pDesktopLaunch = nullptr; + } + + OString cmd = +#ifdef LINUX + // avoid blocking (call it in background) + "( " + aBuffer.makeStringAndClear() + " ) &"; +#else + aBuffer.makeStringAndClear(); +#endif + FILE *pLaunch = popen(cmd.getStr(), "w"); + if ( pLaunch != nullptr ) + { + if ( 0 == pclose( pLaunch ) ) + return; + } + + int nerr = errno; + throw SystemShellExecuteException(OUString::createFromAscii( strerror( nerr ) ), + static_cast < XSystemShellExecute * > (this), nerr ); +#else // EMSCRIPTEN + (void)nFlags; + + css::uno::Reference< css::uri::XUriReference > uri( + css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand)); + if (!uri.is() || !uri->isAbsolute()) + throw SystemShellExecuteException("Emscripten can just open absolute URIs.", + static_cast<XSystemShellExecute*>(this), 42); + if (!aParameter.isEmpty()) + throw SystemShellExecuteException("Emscripten can't process parameters; encode in URI.", + static_cast<XSystemShellExecute*>(this), 42); + + OUString sEscapedURI(rtl::Uri::encode(aCommand, rtl_UriCharClassUric, + rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8)); + execute_browser(sEscapedURI.toUtf8().getStr()); +#endif +} + +// XServiceInfo + +OUString SAL_CALL ShellExec::getImplementationName( ) +{ + return "com.sun.star.comp.system.SystemShellExecute"; +} + +sal_Bool SAL_CALL ShellExec::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL ShellExec::getSupportedServiceNames( ) +{ + return { "com.sun.star.system.SystemShellExecute" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_ShellExec_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ShellExec(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/unix/exec/shellexec.hxx b/shell/source/unix/exec/shellexec.hxx new file mode 100644 index 000000000..fd084a567 --- /dev/null +++ b/shell/source/unix/exec/shellexec.hxx @@ -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 . + */ + +#ifndef INCLUDED_SHELL_SOURCE_UNIX_EXEC_SHELLEXEC_HXX +#define INCLUDED_SHELL_SOURCE_UNIX_EXEC_SHELLEXEC_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/system/XSystemShellExecute.hpp> + + + + +class ShellExec : public ::cppu::WeakImplHelper< css::system::XSystemShellExecute, css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit ShellExec(const css::uno::Reference< css::uno::XComponentContext >& xContext); + + + // XSystemShellExecute + + + virtual void SAL_CALL execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) override; + + + // 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; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/unix/exec/shellexec_em.cxx b/shell/source/unix/exec/shellexec_em.cxx new file mode 100644 index 000000000..219eac969 --- /dev/null +++ b/shell/source/unix/exec/shellexec_em.cxx @@ -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/. + */ + +/** + * Some of our templating stuff clashes with EM_ASM / MAIN_THREAD_EM_ASM: + * + * shellexec.cxx:250:5: error: called object type 'const char *' is not a function or function pointer + * MAIN_THREAD_EM_ASM( + * ^ + * git_emsdk/upstream/emscripten/cache/sysroot/include/emscripten/em_asm.h:208:39: note: expanded from macro 'MAIN_THREAD_EM_ASM' + * #define MAIN_THREAD_EM_ASM(code, ...) ((void)emscripten_asm_const_int_sync_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))) + * ^ + * 1 error generated. + * + * so as a workaround the EM_ASM call is now in an extra file. + */ + +#include <emscripten.h> + +void execute_browser(const char* sUrl) { EM_ASM("window.open(UTF8ToString($0));", sUrl); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/unix/exec/syssh.component b/shell/source/unix/exec/syssh.component new file mode 100644 index 000000000..a41328d34 --- /dev/null +++ b/shell/source/unix/exec/syssh.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.system.SystemShellExecute" + constructor="shell_ShellExec_get_implementation"> + <service name="com.sun.star.system.SystemShellExecute"/> + </implementation> +</component> diff --git a/shell/source/unix/misc/senddoc.sh b/shell/source/unix/misc/senddoc.sh new file mode 100755 index 000000000..c37a2dcf8 --- /dev/null +++ b/shell/source/unix/misc/senddoc.sh @@ -0,0 +1,446 @@ +#!/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 . +# + +URI_ENCODE=$(dirname "$0")/uri-encode +FOPTS="" +sd_platform=$(uname -s) + +# linux file utility needs -L option to resolve symlinks +if [ "$sd_platform" = "Linux" ] ; then + FOPTS="-L" +fi + +# do not confuse the system mail clients with OOo and Java libraries +case $sd_platform in + AIX) + unset LIBPATH + ;; + *) + unset LD_LIBRARY_PATH + ;; +esac + +# tries to locate the executable specified +# as first parameter in the user's path. +which() { + if [ ! -z "$1" ]; then + for i in $(echo "$PATH" | sed -e 's/^:/.:/g' -e 's/:$/:./g' -e 's/::/:.:/g' -e 's/:/ /g'); do + if [ -x "$i/$1" -a ! -d "$i/$1" ]; then + echo "$i/$1" + break; + fi + done + fi +} + +# checks for the original mozilla start script(s) +# and restrict the "-remote" semantics to those. +run_mozilla() { + # find mozilla script in PATH if necessary + if [ "$(basename "$1")" = "$1" ]; then + moz=$(which "$1") + else + moz=$1 + fi + + if file $FOPTS "$moz" | grep "script" > /dev/null && grep "[NM]PL" "$moz" > /dev/null; then + "$moz" -remote 'ping()' 2>/dev/null >/dev/null + if [ $? -eq 2 ]; then + "$1" -compose "$2" & + else + "$1" -remote "xfeDoCommand(composeMessage,$2)" & + fi + else + "$1" -compose "$2" & + fi +} + +if [ "$1" = "--mailclient" ]; then + shift + MAILER=$1 + shift +fi + +# autodetect mail client from executable name +case $(basename "$MAILER" | sed 's/-.*$//') in + + iceape | mozilla | netscape | seamonkey | icedove | thunderbird | betterbird) + + while [ "$1" != "" ]; do + case $1 in + --to) + TO=${TO:-}${TO:+,}$2 + shift + ;; + --cc) + CC=${CC:-}${CC:+,}$2 + shift + ;; + --bcc) + BCC=${BCC:-}${BCC:+,}$2 + shift + ;; + --subject) + SUBJECT=$2 + shift + ;; + --body) + BODY=$2 + shift + ;; + --attach) + ATTACH=${ATTACH:-}${ATTACH:+,}$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + + if [ "$TO" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}to=\'${TO}\' + fi + if [ "$CC" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}cc=\'${CC}\' + fi + if [ "$BCC" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}bcc=\'${BCC}\' + fi + if [ "$SUBJECT" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}subject=\'${SUBJECT}\' + fi + if [ "$BODY" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}body=\'${BODY}\' + fi + if [ "$ATTACH" != "" ]; then + COMMAND=${COMMAND:-}${COMMAND:+,}attachment=\'${ATTACH}\' + fi + + run_mozilla "$MAILER" "$COMMAND" + ;; + + kmail) + + while [ "$1" != "" ]; do + case $1 in + --to) + TO="${TO:-}${TO:+,}$2" + shift + ;; + --cc) + CC="${CC:-}${CC:+,}$2" + shift + ;; + --bcc) + BCC="${BCC:-}${BCC:+,}$2" + shift + ;; + --subject) + SUBJECT="$2" + shift + ;; + --body) + BODY="$2" + shift + ;; + --from) + FROM="$2" + shift + ;; + --attach) + ATTACH="${ATTACH:-}${ATTACH:+ }--attach "$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + + ${MAILER} --composer \ + ${CC:+--cc} ${CC:+"${CC}"} \ + ${BCC:+--bcc} ${BCC:+"${BCC}"} \ + ${SUBJECT:+--subject} ${SUBJECT:+"${SUBJECT}"} \ + ${BODY:+--body} ${BODY:+"${BODY}"} \ + ${FROM:+--header} ${FROM:+"From: ${FROM}"} \ + ${ATTACH:+${ATTACH}} \ + ${TO:+"${TO}"} + ;; + + mutt) + + while [ "$1" != "" ]; do + case $1 in + --from) + FROM="$2" + shift + ;; + --to) + TO="${TO:-}${TO:+,}$2" + shift + ;; + --cc) + CC="${CC:-}${CC:+,}$2" + shift + ;; + --bcc) + BCC="${BCC:-}${BCC:+,}$2" + shift + ;; + --subject) + SUBJECT="$2" + shift + ;; + --body) + TEMPLATE="$(basename "$0").mutt.XXXXXXXX" + BODY=$(mktemp -q -t "${TEMPLATE}") + echo "$2" > "$BODY" + shift + ;; + --attach) + ATTACH="$2" + shift + ;; + *) + ;; + esac + shift; + done + + x-terminal-emulator -e ${MAILER} \ + ${FROM:+-e} ${FROM:+"set from=\"${FROM}\""} \ + ${CC:+-c} ${CC:+"${CC}"} \ + ${BCC:+-b} ${BCC:+"${BCC}"} \ + ${SUBJECT:+-s} ${SUBJECT:+"${SUBJECT}"} \ + ${BODY:+-i} ${BODY:+"${BODY}"} \ + ${ATTACH:+-a} ${ATTACH:+"${ATTACH}"} \ + ${TO:+"${TO}"} & + rm -f "$BODY" + ;; + + evolution | gnome | xdg) # NB. shortened from the dash on + + while [ "$1" != "" ]; do + case $1 in + --to) + if [ "${TO}" != "" ]; then + MAILTO="${MAILTO:-}${MAILTO:+&}to=$2" + else + TO="$2" + fi + shift + ;; + --cc) + MAILTO="${MAILTO:-}${MAILTO:+&}cc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --bcc) + MAILTO="${MAILTO:-}${MAILTO:+&}bcc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --subject) + MAILTO="${MAILTO:-}${MAILTO:+&}subject"=$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --body) + MAILTO="${MAILTO:-}${MAILTO:+&}body="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --attach) + MAILTO="${MAILTO:-}${MAILTO:+&}attach="$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + + MAILTO="mailto:${TO}?${MAILTO}" + ${MAILER} "${MAILTO}" & + ;; + + groupwise) + + while [ "$1" != "" ]; do + case $1 in + --to) + if [ "${TO}" != "" ]; then + MAILTO="${MAILTO:-}${MAILTO:+&}to=$2" + else + TO="$2" + fi + shift + ;; + --cc) + MAILTO="${MAILTO:-}${MAILTO:+&}cc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --bcc) + MAILTO="${MAILTO:-}${MAILTO:+&}bcc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --subject) + MAILTO="${MAILTO:-}${MAILTO:+&}subject"=$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --body) + MAILTO="${MAILTO:-}${MAILTO:+&}body="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --attach) + MAILTO="${MAILTO:-}${MAILTO:+&}attachment="$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + + MAILTO="mailto:${TO}?${MAILTO}" + ${MAILER} "${MAILTO}" & + ;; + + dtmail) + + while [ "$1" != "" ]; do + case $1 in + --to) + TO=${TO:-}${TO:+,}$2 + shift + ;; + --attach) + ATTACH="$2" + shift + ;; + *) + ;; + esac + shift; + done + + ${MAILER} ${TO:+-T} ${TO:-} ${ATTACH:+-a} ${ATTACH:+"${ATTACH}"} + ;; + + sylpheed | claws) + + while [ "$1" != "" ]; do + case $1 in + --to) + TO=${TO:-}${TO:+,}$2 + shift + ;; + --attach) + ATTACH=${ATTACH:-}${ATTACH:+,}$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + + ${MAILER} ${TO:+--compose} ${TO:-} ${ATTACH:+--attach} ${ATTACH:-} + ;; + + Mail | Thunderbird | Betterbird | *.app ) + + while [ "$1" != "" ]; do + case $1 in + --attach) + #i95688# fix filenames containing accented chars, whatever alien + ATTACH="${ATTACH:-}${ATTACH:+ }"$(echo "file://$2" | "${URI_ENCODE}") + shift + ;; + *) + ;; + esac + shift; + done + /usr/bin/open -a "${MAILER}" ${ATTACH} + ;; + + *) + + # LO is configured to use something we do not recognize, or is not configured. + # Try to be smart, and send the mail anyway, if we have the + # possibility to do so. + + if [ -x /usr/bin/xdg-email ] ; then + MAILER=/usr/bin/xdg-email + elif [ -n "$DESKTOP_LAUNCH" ]; then + # http://lists.freedesktop.org/pipermail/xdg/2004-August/002873.html + MAILER=${DESKTOP_LAUNCH} + elif [ -n "$KDE_FULL_SESSION" -a -x /usr/bin/kde-open ] ; then + MAILER=/usr/bin/kde-open + elif [ -x /usr/bin/xdg-open ] ; then + MAILER=/usr/bin/xdg-open + elif command -v xdg-open >/dev/null 2>&1 ; then + MAILER=$(command -v xdg-open) + else + echo "Unsupported mail client: $(basename $MAILER | sed 's/-.*^//')" + exit 2 + fi + + while [ "$1" != "" ]; do + case $1 in + --to) + if [ "${TO}" != "" ]; then + MAILTO="${MAILTO:-}${MAILTO:+&}to=$2" + else + TO="$2" + fi + shift + ;; + --cc) + MAILTO="${MAILTO:-}${MAILTO:+&}cc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --bcc) + MAILTO="${MAILTO:-}${MAILTO:+&}bcc="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --subject) + MAILTO="${MAILTO:-}${MAILTO:+&}subject"=$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --body) + MAILTO="${MAILTO:-}${MAILTO:+&}body="$(echo "$2" | "${URI_ENCODE}") + shift + ;; + --attach) + if [ "$MAILER" = "/usr/bin/xdg-email" ]; then + MAILTO="${MAILTO:-}${MAILTO:+&}attach="$(echo "file://$2" | "${URI_ENCODE}") + else + MAILTO="${MAILTO:-}${MAILTO:+&}attachment="$(echo "file://$2" | "${URI_ENCODE}") + fi + shift + ;; + *) + ;; + esac + shift; + done + + MAILTO="mailto:${TO}?${MAILTO}" + ${MAILER} "${MAILTO}" & + ;; +esac diff --git a/shell/source/unix/misc/uri-encode.c b/shell/source/unix/misc/uri-encode.c new file mode 100644 index 000000000..f7bca6ff6 --- /dev/null +++ b/shell/source/unix/misc/uri-encode.c @@ -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 . + */ + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(void) { + for (;;) { + int c; + errno = 0; + c = getchar(); + if (c == EOF) { + exit(errno == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + } else if (isalnum(c) || strchr("!$'()*+,-.:=@_~/\n", c) != NULL) { + /* valid RFC 2396 pchar characters + '/' + newline */ + if (putchar(c) == EOF) { + exit(EXIT_FAILURE); + } + } else if (printf("%%%02X", (unsigned char) (char) c) < 0) { + exit(EXIT_FAILURE); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/SysShExec.cxx b/shell/source/win32/SysShExec.cxx new file mode 100644 index 000000000..7be77d634 --- /dev/null +++ b/shell/source/win32/SysShExec.cxx @@ -0,0 +1,436 @@ +/* -*- 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 <cassert> +#include <cstddef> +#include <string_view> + +#include <osl/diagnose.h> +#include <osl/process.h> +#include <sal/log.hxx> +#include "SysShExec.hxx" +#include <osl/file.hxx> +#include <sal/macros.h> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/security/AccessControlException.hpp> +#include <com/sun/star/system/SystemShellExecuteException.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/runtimetooustring.hxx> +#include <o3tl/safeCoInitUninit.hxx> +#include <o3tl/string_view.hxx> + +#include <prewin.h> +#include <Shlobj.h> +#include <systools/win32/comtools.hxx> +#include <postwin.h> + +using namespace ::com::sun::star::system::SystemShellExecuteFlags; + +namespace +{ + /* This is the error table that defines the mapping between OS error + codes and errno values */ + + struct errentry { + unsigned long oscode; /* OS return value */ + int errnocode; /* System V error code */ + }; + + struct errentry errtable[] = { + { ERROR_SUCCESS, osl_File_E_None }, /* 0 */ + { ERROR_INVALID_FUNCTION, osl_File_E_INVAL }, /* 1 */ + { ERROR_FILE_NOT_FOUND, osl_File_E_NOENT }, /* 2 */ + { ERROR_PATH_NOT_FOUND, osl_File_E_NOENT }, /* 3 */ + { ERROR_TOO_MANY_OPEN_FILES, osl_File_E_MFILE }, /* 4 */ + { ERROR_ACCESS_DENIED, osl_File_E_ACCES }, /* 5 */ + { ERROR_INVALID_HANDLE, osl_File_E_BADF }, /* 6 */ + { ERROR_ARENA_TRASHED, osl_File_E_NOMEM }, /* 7 */ + { ERROR_NOT_ENOUGH_MEMORY, osl_File_E_NOMEM }, /* 8 */ + { ERROR_INVALID_BLOCK, osl_File_E_NOMEM }, /* 9 */ + { ERROR_BAD_ENVIRONMENT, osl_File_E_2BIG }, /* 10 */ + { ERROR_BAD_FORMAT, osl_File_E_NOEXEC }, /* 11 */ + { ERROR_INVALID_ACCESS, osl_File_E_INVAL }, /* 12 */ + { ERROR_INVALID_DATA, osl_File_E_INVAL }, /* 13 */ + { ERROR_INVALID_DRIVE, osl_File_E_NOENT }, /* 15 */ + { ERROR_CURRENT_DIRECTORY, osl_File_E_ACCES }, /* 16 */ + { ERROR_NOT_SAME_DEVICE, osl_File_E_XDEV }, /* 17 */ + { ERROR_NO_MORE_FILES, osl_File_E_NOENT }, /* 18 */ + { ERROR_LOCK_VIOLATION, osl_File_E_ACCES }, /* 33 */ + { ERROR_BAD_NETPATH, osl_File_E_NOENT }, /* 53 */ + { ERROR_NETWORK_ACCESS_DENIED, osl_File_E_ACCES }, /* 65 */ + { ERROR_BAD_NET_NAME, osl_File_E_NOENT }, /* 67 */ + { ERROR_FILE_EXISTS, osl_File_E_EXIST }, /* 80 */ + { ERROR_CANNOT_MAKE, osl_File_E_ACCES }, /* 82 */ + { ERROR_FAIL_I24, osl_File_E_ACCES }, /* 83 */ + { ERROR_INVALID_PARAMETER, osl_File_E_INVAL }, /* 87 */ + { ERROR_NO_PROC_SLOTS, osl_File_E_AGAIN }, /* 89 */ + { ERROR_DRIVE_LOCKED, osl_File_E_ACCES }, /* 108 */ + { ERROR_BROKEN_PIPE, osl_File_E_PIPE }, /* 109 */ + { ERROR_DISK_FULL, osl_File_E_NOSPC }, /* 112 */ + { ERROR_INVALID_TARGET_HANDLE, osl_File_E_BADF }, /* 114 */ + { ERROR_INVALID_HANDLE, osl_File_E_INVAL }, /* 124 */ + { ERROR_WAIT_NO_CHILDREN, osl_File_E_CHILD }, /* 128 */ + { ERROR_CHILD_NOT_COMPLETE, osl_File_E_CHILD }, /* 129 */ + { ERROR_DIRECT_ACCESS_HANDLE, osl_File_E_BADF }, /* 130 */ + { ERROR_NEGATIVE_SEEK, osl_File_E_INVAL }, /* 131 */ + { ERROR_SEEK_ON_DEVICE, osl_File_E_ACCES }, /* 132 */ + { ERROR_DIR_NOT_EMPTY, osl_File_E_NOTEMPTY }, /* 145 */ + { ERROR_NOT_LOCKED, osl_File_E_ACCES }, /* 158 */ + { ERROR_BAD_PATHNAME, osl_File_E_NOENT }, /* 161 */ + { ERROR_MAX_THRDS_REACHED, osl_File_E_AGAIN }, /* 164 */ + { ERROR_LOCK_FAILED, osl_File_E_ACCES }, /* 167 */ + { ERROR_ALREADY_EXISTS, osl_File_E_EXIST }, /* 183 */ + { ERROR_FILENAME_EXCED_RANGE, osl_File_E_NOENT }, /* 206 */ + { ERROR_NESTING_NOT_ALLOWED, osl_File_E_AGAIN }, /* 215 */ + { ERROR_NOT_ENOUGH_QUOTA, osl_File_E_NOMEM } /* 1816 */ + }; + + /* size of the table */ + #define ERRTABLESIZE (SAL_N_ELEMENTS(errtable)) + + /* The following two constants must be the minimum and maximum + values in the (contiguous) range of osl_File_E_xec Failure errors. */ + #define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG + #define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN + + /* These are the low and high value in the range of errors that are + access violations */ + #define MIN_EACCES_RANGE ERROR_WRITE_PROTECT + #define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED + + + /*******************************************************************************/ + + oslFileError _mapError( DWORD dwError ) + { + unsigned i; + + /* check the table for the OS error code */ + for ( i = 0; i < ERRTABLESIZE; ++i ) + { + if ( dwError == errtable[i].oscode ) + return static_cast<oslFileError>(errtable[i].errnocode); + } + + /* The error code wasn't in the table. We check for a range of */ + /* osl_File_E_ACCES errors or exec failure errors (ENOEXEC). Otherwise */ + /* osl_File_E_INVAL is returned. */ + + if ( dwError >= MIN_EACCES_RANGE && dwError <= MAX_EACCES_RANGE) + return osl_File_E_ACCES; + else if ( dwError >= MIN_EXEC_ERROR && dwError <= MAX_EXEC_ERROR) + return osl_File_E_NOEXEC; + else + return osl_File_E_INVAL; + } + + #define MapError( oserror ) _mapError( oserror ) + + #define E_UNKNOWN_EXEC_ERROR -1 +} + +CSysShExec::CSysShExec( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + WeakComponentImplHelper< css::system::XSystemShellExecute, css::lang::XServiceInfo >( m_aMutex ), + m_xContext(xContext), + mnNbCallCoInitializeExForReinit(0) +{ + /* + * As this service is declared thread-affine, it is ensured to be called from a + * dedicated thread, so initialize COM here. + * + * We need COM to be initialized for STA, but osl thread get initialized for MTA. + * Once this changed, we can remove the uninitialize call. + */ + o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, mnNbCallCoInitializeExForReinit); +} +CSysShExec::~CSysShExec() +{ + o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, mnNbCallCoInitializeExForReinit); +} + +namespace +{ +bool checkExtension(std::u16string_view extension, std::u16string_view denylist) { + assert(!extension.empty()); + for (std::size_t i = 0; i != std::u16string_view::npos;) { + std::u16string_view tok = o3tl::getToken(denylist, ';', i); + o3tl::starts_with(tok, u'.', &tok); + if (o3tl::equalsIgnoreAsciiCase(extension, tok)) { + return false; + } + } + return true; +} + +// This callback checks if the found window is the specified process's top-level window, +// and activates the first found such window. +BOOL CALLBACK FindAndActivateProcWnd(HWND hwnd, LPARAM lParam) +{ + if (!IsWindowVisible(hwnd)) + return TRUE; // continue enumeration + if (GetWindow(hwnd, GW_OWNER)) // not a top-level window + return TRUE; // continue enumeration + const DWORD nParamProcId = static_cast<DWORD>(lParam); + assert(nParamProcId != 0); + DWORD nWndProcId = 0; + (void)GetWindowThreadProcessId(hwnd, &nWndProcId); + if (nWndProcId != nParamProcId) + return TRUE; // continue enumeration + + // Found it! Bring it to front + if (IsIconic(hwnd)) + { + ShowWindow(hwnd, SW_RESTORE); + } + SetForegroundWindow(hwnd); + SetActiveWindow(hwnd); + return FALSE; // stop enumeration +} +} + +void SAL_CALL CSysShExec::execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) +{ + // parameter checking + if (0 == aCommand.getLength()) + throw css::lang::IllegalArgumentException( + "Empty command", + static_cast< css::system::XSystemShellExecute* >( this ), + 1 ); + + if ((nFlags & ~(NO_SYSTEM_ERROR_MESSAGE | URIS_ONLY)) != 0) + throw css::lang::IllegalArgumentException( + "Invalid Flags specified", + static_cast< css::system::XSystemShellExecute* >( this ), + 3 ); + + OUString preprocessed_command(aCommand); + if ((nFlags & URIS_ONLY) != 0) + { + css::uno::Reference< css::uri::XUriReference > uri( + css::uri::UriReferenceFactory::create(m_xContext)->parse(aCommand)); + if (!(uri.is() && uri->isAbsolute())) + { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute URIS_ONLY with" + " non-absolute URI reference " + + aCommand, + static_cast< cppu::OWeakObject * >(this), 0); + } + if (uri->getScheme().equalsIgnoreAsciiCase("file")) { + // ShellExecuteExW appears to ignore the fragment of a file URL anyway, so remove it: + uri->clearFragment(); + OUString pathname; + auto const e1 + = osl::FileBase::getSystemPathFromFileURL(uri->getUriReference(), pathname); + if (e1 != osl::FileBase::E_None) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, getSystemPathFromFileURL <" + aCommand + + "> failed with " + OUString::number(e1)), + {}, 0); + } + const int MAX_LONG_PATH = 32767; // max longpath on WinNT + if (pathname.getLength() >= MAX_LONG_PATH) + { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute, path <" + pathname + "> too long", {}, 0); + } + preprocessed_command = pathname; + wchar_t path[MAX_LONG_PATH]; + wcscpy_s(path, o3tl::toW(pathname.getStr())); + for (int i = 0;; ++i) { + // tdf#130216: normalize c:\path\to\something\..\else into c:\path\to\else + if (PathResolve(path, nullptr, PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE) == 0) + { + throw css::lang::IllegalArgumentException( + OUString::Concat(u"XSystemShellExecute.execute, PathResolve(") + o3tl::toU(path) + + ") failed", + {}, 0); + } + SHFILEINFOW info; + if (SHGetFileInfoW(path, 0, &info, sizeof info, SHGFI_EXETYPE) != 0) + { + throw css::security::AccessControlException( + "XSystemShellExecute.execute, cannot process <" + aCommand + ">", {}, {}); + } + if (SHGetFileInfoW(path, 0, &info, sizeof info, SHGFI_ATTRIBUTES) == 0) + { + throw css::lang::IllegalArgumentException( + OUString::Concat(u"XSystemShellExecute.execute, SHGetFileInfoW(") + o3tl::toU(path) + ") failed", {}, + 0); + } + if ((info.dwAttributes & SFGAO_LINK) == 0) { + break; + } + sal::systools::COMReference<IShellLinkW> link; + try + { + link.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER); + } + catch (sal::systools::ComError& e) + { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, CoCreateInstance failed with " + + OUString::number(e.GetHresult())), + {}, 0); + } + sal::systools::COMReference<IPersistFile> file; + try { + file = link.QueryInterface<IPersistFile>(sal::systools::COM_QUERY_THROW); + } catch(sal::systools::ComError & e3) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, QueryInterface failed with: " + + o3tl::runtimeToOUString(e3.what())), + {}, 0); + } + HRESULT e2 = file->Load(path, STGM_READ); + if (FAILED(e2)) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, IPersistFile.Load failed with " + + OUString::number(e2)), + {}, 0); + } + e2 = link->Resolve(nullptr, SLR_UPDATE | SLR_NO_UI); + if (FAILED(e2)) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, IShellLink.Resolve failed with " + + OUString::number(e2)), + {}, 0); + } + WIN32_FIND_DATAW wfd; + e2 = link->GetPath(path, SAL_N_ELEMENTS(path), &wfd, SLGP_RAWPATH); + if (FAILED(e2)) { + throw css::lang::IllegalArgumentException( + ("XSystemShellExecute.execute, IShellLink.GetPath failed with " + + OUString::number(e2)), + {}, 0); + } + // Fail at some arbitrary nesting depth, to avoid an infinite loop: + if (i == 30) { + throw css::lang::IllegalArgumentException( + "XSystemShellExecute.execute, link depth exceeded for <" + aCommand + ">", + {}, 0); + } + } + pathname = o3tl::toU(path); + // ShellExecuteExW appears to ignore trailing dots, so remove them: + while (pathname.endsWith(".", &pathname)) {} + auto const n = pathname.lastIndexOf('.'); + if (n > pathname.lastIndexOf('\\')) { + auto const ext = pathname.copy(n + 1); + if (!ext.isEmpty()) { + OUString env; + if (osl_getEnvironment(OUString("PATHEXT").pData, &env.pData) + != osl_Process_E_None) + { + SAL_INFO("shell", "osl_getEnvironment(PATHEXT) failed"); + } + if (!(checkExtension(ext, env) + && checkExtension( + ext, + u".ADE;.ADP;.APK;.APPLICATION;.APPX;.APPXBUNDLE;.BAT;.CAB;.CHM;.CLASS;" + ".CMD;.COM;.CPL;.DLL;.DMG;.EX;.EX_;.EXE;.GADGET;.HTA;.INF;.INS;.IPA;" + ".ISO;.ISP;.JAR;.JS;.JSE;.LIB;.LNK;.MDE;.MSC;.MSH;.MSH1;.MSH2;.MSHXML;" + ".MSH1XML;.MSH2XML;.MSI;.MSIX;.MSIXBUNDLE;.MSP;.MST;.NSH;.PIF;.PS1;" + ".PS1XML;.PS2;.PS2XML;.PSC1;.PSC2;.PY;.REG;.SCF;.SCR;.SCT;.SHB;.SYS;" + ".VB;.VBE;.VBS;.VXD;.WS;.WSC;.WSF;.WSH;"))) + { + throw css::security::AccessControlException( + "XSystemShellExecute.execute, cannot process <" + aCommand + ">", {}, + {}); + } + } + } + } + } + + SHELLEXECUTEINFOW sei; + ZeroMemory(&sei, sizeof( sei)); + + sei.cbSize = sizeof(sei); + sei.lpFile = o3tl::toW(preprocessed_command.getStr()); + sei.lpParameters = o3tl::toW(aParameter.getStr()); + sei.nShow = SW_SHOWNORMAL; + sei.fMask = SEE_MASK_NOCLOSEPROCESS; // we need sei.hProcess + + if (NO_SYSTEM_ERROR_MESSAGE & nFlags) + sei.fMask |= SEE_MASK_FLAG_NO_UI; + + SetLastError( 0 ); + + bool bRet = ShellExecuteExW(&sei); + + if (!bRet && (nFlags & NO_SYSTEM_ERROR_MESSAGE)) + { + // ShellExecuteEx fails to set an error code + // we return osl_File_E_INVAL + sal_Int32 psxErr = GetLastError(); + if (ERROR_SUCCESS == psxErr) + psxErr = E_UNKNOWN_EXEC_ERROR; + else + psxErr = MapError(psxErr); + + throw css::system::SystemShellExecuteException( + "Error executing command", + static_cast< css::system::XSystemShellExecute* >(this), + psxErr); + } + else + { + // Get Permission make changes to the Window of the created Process + const DWORD procId = GetProcessId(sei.hProcess); + if (procId != 0) + { + AllowSetForegroundWindow(procId); + WaitForInputIdle(sei.hProcess, 1000); // so that main window is created; imperfect + EnumWindows(FindAndActivateProcWnd, static_cast<LPARAM>(procId)); + } + } + + // Close the handle for the created childprocess when we are done + CloseHandle(sei.hProcess); +} + +// XServiceInfo + +OUString SAL_CALL CSysShExec::getImplementationName( ) +{ + return "com.sun.star.sys.shell.SystemShellExecute"; +} + +sal_Bool SAL_CALL CSysShExec::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL CSysShExec::getSupportedServiceNames( ) +{ + return { "com.sun.star.system.SystemShellExecute" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_CSysShExec_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CSysShExec(context)); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/SysShExec.hxx b/shell/source/win32/SysShExec.hxx new file mode 100644 index 000000000..8a4e65ffa --- /dev/null +++ b/shell/source/win32/SysShExec.hxx @@ -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_SHELL_SOURCE_WIN32_SYSSHEXEC_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SYSSHEXEC_HXX + +#include <cppuhelper/compbase.hxx> +#include <osl/mutex.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/system/XSystemShellExecute.hpp> + + + + +class CSysShExecBase +{ +protected: + osl::Mutex m_aMutex; +}; + +class CSysShExec : + public CSysShExecBase, + public cppu::WeakComponentImplHelper< + css::system::XSystemShellExecute, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + // to put back all the inits with COINIT_MULTITHREADED if needed + int mnNbCallCoInitializeExForReinit; + +public: + explicit CSysShExec(const css::uno::Reference< css::uno::XComponentContext >& xContext); + ~CSysShExec(); + + + // XSystemShellExecute + + + virtual void SAL_CALL execute( const OUString& aCommand, const OUString& aParameter, sal_Int32 nFlags ) override; + + + // 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; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/jumplist/JumpList.cxx b/shell/source/win32/jumplist/JumpList.cxx new file mode 100644 index 000000000..06a62a110 --- /dev/null +++ b/shell/source/win32/jumplist/JumpList.cxx @@ -0,0 +1,532 @@ +/* -*- 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 <sal/config.h> + +#include <algorithm> +#include <cassert> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/runtimetooustring.hxx> +#include <o3tl/safeCoInitUninit.hxx> +#include <osl/file.hxx> +#include <osl/mutex.hxx> +#include <osl/process.h> +#include <sal/log.hxx> +#include <systools/win32/comtools.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/system/windows/JumpListItem.hpp> +#include <com/sun/star/system/windows/XJumpList.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/InvalidStateException.hpp> + +#include <prewin.h> +#include <Shlobj.h> +#include <propkey.h> +#include <propvarutil.h> +#include <postwin.h> + +using namespace comphelper; +using namespace cppu; +using namespace css; +using namespace css::uno; +using namespace css::lang; +using namespace css::system::windows; +using namespace css::util; +using namespace osl; +using namespace sal::systools; + +namespace +{ +class JumpListImpl : public BaseMutex, public WeakComponentImplHelper<XJumpList, XServiceInfo> +{ + Reference<XComponentContext> m_xContext; + COMReference<ICustomDestinationList> m_aDestinationList; + COMReference<IObjectArray> m_aRemoved; + bool m_isListOpen; + +public: + explicit JumpListImpl(const Reference<XComponentContext>& xContext); + + // XJumpList + virtual void SAL_CALL beginList(const OUString& sApplication) override; + virtual void SAL_CALL appendCategory(const OUString& sCategory, + const Sequence<JumpListItem>& aJumpListItems) override; + virtual void SAL_CALL addTasks(const Sequence<JumpListItem>& aJumpListItems) override; + virtual void SAL_CALL showRecentFiles() override; + virtual void SAL_CALL showFrequentFiles() override; + virtual void SAL_CALL commitList() override; + virtual void SAL_CALL abortList() override; + virtual void SAL_CALL deleteList(const OUString& sApplication) override; + virtual Sequence<JumpListItem> SAL_CALL getRemovedItems(const OUString& sApplication) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +JumpListImpl::JumpListImpl(const Reference<XComponentContext>& xContext) + : WeakComponentImplHelper(m_aMutex) + , m_xContext(xContext) + , m_aDestinationList(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER) + , m_isListOpen(false) +{ +} + +// Determines if the provided IShellLinkItem is listed in the array of items that the user has removed +bool lcl_isItemInArray(COMReference<IShellLinkW> pShellLinkItem, + COMReference<IObjectArray> poaRemoved) +{ + UINT nItems; + ThrowIfFailed(poaRemoved->GetCount(&nItems), "GetCount failed."); + + COMReference<IShellLinkW> pShellLinkItemCompare; + for (UINT i = 0; i < nItems; i++) + { + if (!SUCCEEDED(poaRemoved->GetAt(i, IID_PPV_ARGS(&pShellLinkItemCompare)))) + continue; + + PROPVARIANT propvar; + COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW); + ThrowIfFailed(pps->GetValue(PKEY_Title, &propvar), "GetValue failed."); + OUString title(o3tl::toU(PropVariantToStringWithDefault(propvar, L""))); + + COMReference<IPropertyStore> ppsCompare(pShellLinkItemCompare, COM_QUERY_THROW); + ThrowIfFailed(ppsCompare->GetValue(PKEY_Title, &propvar), "GetValue failed."); + OUString titleCompare(o3tl::toU(PropVariantToStringWithDefault(propvar, L""))); + PropVariantClear(&propvar); + + if (title == titleCompare) + return true; + } + + return false; +} +} + +void SAL_CALL JumpListImpl::beginList(const OUString& sApplication) +{ + if (m_isListOpen) + throw InvalidStateException("There is already a list open. Close it with 'commitList'"); + + if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress" + && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base" + && sApplication != "Startcenter") + { + throw IllegalArgumentException( + "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', " + "'Math', 'Base', 'Startcenter'.", + static_cast<OWeakObject*>(this), 1); + } + OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication); + + try + { + m_aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr())); + + UINT min_slots; + + ThrowIfFailed(m_aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&m_aRemoved)), + "BeginList failed"); + m_isListOpen = true; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, + const Sequence<JumpListItem>& aJumpListItems) +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + if (sCategory.isEmpty()) + { + throw IllegalArgumentException("Parameter 'category' must not be empty", + static_cast<OWeakObject*>(this), 1); + } + + try + { + OUString sofficeURL; + OUString sofficePath; + oslProcessError err = osl_getExecutableFile(&sofficeURL.pData); + FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath); + if (err != osl_Process_E_None) + { + SAL_WARN("shell.jumplist", "osl_getExecutableFile failed"); + return; + } + // We need to run soffice.exe, not soffice.bin + sofficePath = sofficePath.replaceFirst("soffice.bin", "soffice.exe"); + + COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection, nullptr, + CLSCTX_INPROC_SERVER); + + for (auto const& item : aJumpListItems) + { + if (item.name.isEmpty()) + continue; + try + { + COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink, nullptr, + CLSCTX_INPROC_SERVER); + + { + COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW); + + PROPVARIANT propvar; + ThrowIfFailed( + InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar), + "InitPropVariantFromString failed."); + + ThrowIfFailed(pps->SetValue(PKEY_Title, propvar), "SetValue failed."); + + ThrowIfFailed(pps->Commit(), "Commit failed."); + + PropVariantClear(&propvar); + } + ThrowIfFailed(pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())), + OStringConcatenation("Setting description '" + + item.description.toUtf8() + "' failed.")); + + ThrowIfFailed( + pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())), + OStringConcatenation("Setting path '" + sofficePath.toUtf8() + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())), + OStringConcatenation("Setting arguments '" + item.arguments.toUtf8() + + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0), + OStringConcatenation("Setting icon path '" + item.iconPath.toUtf8() + + "' failed.")); + + if (lcl_isItemInArray(pShellLinkItem, m_aRemoved)) + { + SAL_INFO("shell.jumplist", "Ignoring item '" + << item.name + << "' (was removed by user). See output of " + "XJumpList::getRemovedItems()."); + continue; + } + aCollection->AddObject(pShellLinkItem); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + continue; + } + } + + COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW); + UINT nItems; + ThrowIfFailed(pObjectArray->GetCount(&nItems), "GetCount failed."); + if (nItems == 0) + { + throw IllegalArgumentException( + "No valid items given. `jumpListItems` is either empty, or contains only items " + "which were removed by the user. See `XJumpList::getRemovedItems()`.", + static_cast<OWeakObject*>(this), 1); + } + + ThrowIfFailed( + m_aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray), + "AppendCategory failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::addTasks(const Sequence<JumpListItem>& aJumpListItems) +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + OUString sofficeURL; + OUString sofficePath; + oslProcessError err = osl_getExecutableFile(&sofficeURL.pData); + FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath); + if (err != osl_Process_E_None) + { + SAL_WARN("shell.jumplist", "osl_getExecutableFile failed"); + return; + } + // We need to run soffice.exe, not soffice.bin + sofficePath = sofficePath.replaceFirst("soffice.bin", "soffice.exe"); + + COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection, nullptr, + CLSCTX_INPROC_SERVER); + + for (auto const& item : aJumpListItems) + { + if (item.name.isEmpty()) + continue; + try + { + COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink, nullptr, + CLSCTX_INPROC_SERVER); + + { + COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW); + + PROPVARIANT propvar; + ThrowIfFailed( + InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar), + "InitPropVariantFromString failed."); + + ThrowIfFailed(pps->SetValue(PKEY_Title, propvar), "SetValue failed."); + + ThrowIfFailed(pps->Commit(), "Commit failed."); + + PropVariantClear(&propvar); + } + ThrowIfFailed(pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())), + OStringConcatenation("Setting description '" + + item.description.toUtf8() + "' failed.")); + + ThrowIfFailed( + pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())), + OStringConcatenation("Setting path '" + sofficePath.toUtf8() + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())), + OStringConcatenation("Setting arguments '" + item.arguments.toUtf8() + + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0), + OStringConcatenation("Setting icon path '" + item.iconPath.toUtf8() + + "' failed.")); + + aCollection->AddObject(pShellLinkItem); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + continue; + } + } + + COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW); + UINT nItems; + ThrowIfFailed(pObjectArray->GetCount(&nItems), "GetCount failed."); + if (nItems == 0) + { + throw IllegalArgumentException("No valid items given. `jumpListItems` is empty.", + static_cast<OWeakObject*>(this), 1); + } + + ThrowIfFailed(m_aDestinationList->AddUserTasks(pObjectArray), "AddUserTasks failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::showRecentFiles() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_RECENT), + "AppendKnownCategory(KDC_RECENT) failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::showFrequentFiles() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_FREQUENT), + "AppendKnownCategory(KDC_FREQUENT) failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::commitList() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->CommitList(), "CommitList failed."); + m_isListOpen = false; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::abortList() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open."); + + try + { + ThrowIfFailed(m_aDestinationList->AbortList(), "AbortList failed."); + m_isListOpen = false; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::deleteList(const OUString& sApplication) +{ + if (m_isListOpen) + throw InvalidStateException("You are in a list building session. Close it with " + "'commitList', or abort with 'abortList'"); + + if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress" + && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base" + && sApplication != "Startcenter") + { + throw IllegalArgumentException( + "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', " + "'Math', 'Base', 'Startcenter'.", + static_cast<OWeakObject*>(this), 1); + } + OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication); + + try + { + COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList, nullptr, + CLSCTX_INPROC_SERVER); + aDestinationList->DeleteList(o3tl::toW(sApplicationID.getStr())); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +Sequence<JumpListItem> SAL_CALL JumpListImpl::getRemovedItems(const OUString& sApplication) +{ + if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress" + && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base" + && sApplication != "Startcenter") + { + throw IllegalArgumentException( + "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', " + "'Math', 'Base', 'Startcenter'.", + static_cast<OWeakObject*>(this), 1); + } + OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication); + + std::vector<JumpListItem> removedItems; + try + { + COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList, nullptr, + CLSCTX_INPROC_SERVER); + + aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr())); + + COMReference<IObjectArray> removed; + ThrowIfFailed(aDestinationList->GetRemovedDestinations(IID_PPV_ARGS(&removed)), + "GetRemovedDestinations failed"); + + UINT removed_count; + if (SUCCEEDED(removed->GetCount(&removed_count) && (removed_count > 0))) + { + JumpListItem item; + COMReference<IShellLinkW> pShellLinkItem; + for (UINT i = 0; i < removed_count; ++i) + { + if (SUCCEEDED(removed->GetAt(i, IID_PPV_ARGS(&pShellLinkItem)))) + { + COMReference<IPropertyStore> propertyStore(pShellLinkItem, COM_QUERY_THROW); + PROPVARIANT propvar; + ThrowIfFailed(propertyStore->GetValue(PKEY_Title, &propvar), + "GetValue failed."); + item.name = o3tl::toU(PropVariantToStringWithDefault(propvar, L"")); + + ThrowIfFailed(propertyStore->GetValue(PKEY_Link_Arguments, &propvar), + "GetValue failed."); + item.arguments = o3tl::toU(PropVariantToStringWithDefault(propvar, L"")); + PropVariantClear(&propvar); + + wchar_t itemDesc[MAX_PATH]; + ThrowIfFailed(pShellLinkItem->GetDescription( + itemDesc, std::extent<decltype(itemDesc)>::value), + "GetDescription failed."); + item.description = o3tl::toU(itemDesc); + + wchar_t path[MAX_PATH]; + int icon_index; + ThrowIfFailed(pShellLinkItem->GetIconLocation( + path, std::extent<decltype(path)>::value, &icon_index), + "GetIconLocation failed."); + item.iconPath = o3tl::toU(path); + + removedItems.emplace_back(item); + } + } + } + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } + + return containerToSequence(removedItems); +} + +// XServiceInfo + +OUString SAL_CALL JumpListImpl::getImplementationName() +{ + return "com.sun.star.system.windows.JumpListImpl"; +} + +sal_Bool SAL_CALL JumpListImpl::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL JumpListImpl::getSupportedServiceNames() +{ + return { "com.sun.star.system.windows.JumpList" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT XInterface* +shell_JumpListExec_get_implementation(XComponentContext* context, Sequence<Any> const&) +{ + return acquire(new JumpListImpl(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/shell/source/win32/jumplist/jumplist.component b/shell/source/win32/jumplist/jumplist.component new file mode 100644 index 000000000..b6b6a9aa6 --- /dev/null +++ b/shell/source/win32/jumplist/jumplist.component @@ -0,0 +1,17 @@ +<?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/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.system.windows.JumpListImpl" + constructor="shell_JumpListExec_get_implementation"> + <service name="com.sun.star.system.windows.JumpList"/> + </implementation> +</component> diff --git a/shell/source/win32/ooofilereader/autostyletag.cxx b/shell/source/win32/ooofilereader/autostyletag.cxx new file mode 100644 index 000000000..f2220d915 --- /dev/null +++ b/shell/source/win32/ooofilereader/autostyletag.cxx @@ -0,0 +1,91 @@ +/* -*- 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 "autostyletag.hxx" + +/*********************** CAutoStyleTag ***********************/ + +CAutoStyleTag::CAutoStyleTag( const XmlTagAttributes_t& attributes ): + m_CurrentStyleLocalePair( EMPTY_STYLELOCALE_PAIR ) +{ + addAttributes( attributes); +}; + +void CAutoStyleTag::startTag() +{ +} + +void CAutoStyleTag::endTag() +{ +} + +void CAutoStyleTag::addCharacters(const std::wstring&) +{ +} + +void CAutoStyleTag::addAttributes(const XmlTagAttributes_t& attributes) +{ + if ( EMPTY_STYLELOCALE_PAIR == m_CurrentStyleLocalePair ) + { + // the style-locale pair should be empty when entering STYLE_STYLE + // tag, and otherwise should be STYLE_PROPERTIES. + + XmlTagAttributes_t::const_iterator iter = attributes.find(CONTENT_STYLE_STYLE_NAME); + + if ( iter != attributes.end()) + setStyle( iter->second ); + } + else + { + // tag STYLE_PROPERTIES entered. + + XmlTagAttributes_t::const_iterator iter_lan = attributes.find(CONTENT_STYLE_PROPERTIES_LANGUAGE); + XmlTagAttributes_t::const_iterator iter_con = attributes.find(CONTENT_STYLE_PROPERTIES_COUNTRY); + XmlTagAttributes_t::const_iterator iter_lan_asain = attributes.find(CONTENT_STYLE_PROPERTIES_LANGUAGEASIAN); + XmlTagAttributes_t::const_iterator iter_con_asain = attributes.find(CONTENT_STYLE_PROPERTIES_COUNTRYASIAN); + + // if style:properties | fo:language or style:language-asian is exist, + // set the locale field, otherwise clear the style-locale pair; + if ( ( iter_lan!= attributes.end() ) && ( iter_con != attributes.end() ) ) + setLocale( ::std::make_pair( iter_lan->second,iter_con->second ) ); + else if ( ( iter_lan_asain!= attributes.end() ) && ( iter_con_asain != attributes.end() ) ) + setLocale( ::std::make_pair( iter_lan_asain->second,iter_con_asain->second ) ); + else + clearStyleLocalePair(); + } + +} + +void CAutoStyleTag::setStyle( ::std::wstring const & Style ) +{ + m_CurrentStyleLocalePair.first = Style; +} + +void CAutoStyleTag::setLocale(const LocaleSet_t& Locale) +{ + m_CurrentStyleLocalePair.second = Locale; +} + +void CAutoStyleTag::clearStyleLocalePair( ) +{ + m_CurrentStyleLocalePair = EMPTY_STYLELOCALE_PAIR; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/autostyletag.hxx b/shell/source/win32/ooofilereader/autostyletag.hxx new file mode 100644 index 000000000..2bfe4f8f4 --- /dev/null +++ b/shell/source/win32/ooofilereader/autostyletag.hxx @@ -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_SHELL_SOURCE_WIN32_OOOFILEREADER_AUTOSTYLETAG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_AUTOSTYLETAG_HXX + +#include "itag.hxx" + +/*************************** CAutoStyleTag tag readers ***************************/ + +/** Implements the ITag interface for + building a Style-Locale list + + Usage sample: + + LocaleSet_t locale = meta_info_accessor.getDefaultLocale(); + CContentReader content( m_szFileName, locale ); + CStyleMap map = content.getStyleMap(); +*/ +class CAutoStyleTag : public ITag +{ + public: + CAutoStyleTag():m_CurrentStyleLocalePair( EMPTY_STYLELOCALE_PAIR ){} + explicit CAutoStyleTag(const XmlTagAttributes_t& attributes); + + virtual void startTag() override; + virtual void endTag() override; + virtual void addCharacters(const std::wstring& characters) override; + virtual void addAttributes(const XmlTagAttributes_t& attributes) override; + virtual std::wstring getTagContent() override { return EMPTY_STRING; }; + virtual ::std::wstring getTagAttribute( ::std::wstring const & /*attrname*/ ) override { return ::std::wstring() ; } + + void setStyle( ::std::wstring const & Style ); + void setLocale(const LocaleSet_t& Locale); + void clearStyleLocalePair(); + StyleLocalePair_t getStyleLocalePair() const{ return m_CurrentStyleLocalePair; } + bool isFull() const + { + return (( m_CurrentStyleLocalePair.first != EMPTY_STRING )&& + ( m_CurrentStyleLocalePair.second != EMPTY_LOCALE)); + } + + private: + StyleLocalePair_t m_CurrentStyleLocalePair; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/basereader.cxx b/shell/source/win32/ooofilereader/basereader.cxx new file mode 100644 index 000000000..7bf53b275 --- /dev/null +++ b/shell/source/win32/ooofilereader/basereader.cxx @@ -0,0 +1,83 @@ +/* -*- 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 <basereader.hxx> + +#include <xml_parser.hxx> + +#include <assert.h> +#include <memory> + +/** constructor of CBaseReader. +*/ +CBaseReader::CBaseReader(const Filepath_t& DocumentName): +m_ZipFile( DocumentName ) +{ +} + + +CBaseReader::CBaseReader(StreamInterface * sw): +m_ZipFile( sw ) +{ +} + + +CBaseReader::~CBaseReader() +{ +} + + +void CBaseReader::start_document() +{ +} + + +void CBaseReader::end_document() +{ +} + +/** Read interested tag content into respective structure then start parsing process. + @param ContentName + the xml file name in the zipped document which we interest. +*/ +void CBaseReader::Initialize( const std::string& ContentName) +{ + try + { + if (m_ZipContent.empty()) + m_ZipFile.GetUncompressedContent( ContentName, m_ZipContent ); + + if (!m_ZipContent.empty()) + { + xml_parser parser; + parser.set_document_handler(this); // pass current reader as reader to the sax parser + parser.parse(m_ZipContent.data(), m_ZipContent.size(), true/*IsFinal*/); + } + } + catch(std::exception&) + { + // OSL_ENSURE( false, ex.what() ); + } + catch(...) + { + // OSL_ENSURE(false, "Unknown error"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/contentreader.cxx b/shell/source/win32/ooofilereader/contentreader.cxx new file mode 100644 index 000000000..efdf5ba1e --- /dev/null +++ b/shell/source/win32/ooofilereader/contentreader.cxx @@ -0,0 +1,218 @@ +/* -*- 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 <contentreader.hxx> +#include "dummytag.hxx" +#include "simpletag.hxx" +#include "autostyletag.hxx" + +#include <assert.h> + +/** constructor. +*/ +CContentReader::CContentReader( const Filepath_t& DocumentName, LocaleSet_t const & DocumentLocale ): +CBaseReader( DocumentName ) +{ + try + { + m_DefaultLocale = DocumentLocale; + Initialize( DOC_CONTENT_NAME ); + } + catch(xml_parser_exception&) + { + // OSL_ENSURE(false, ex.what()); + } + catch(...) + { + // OSL_ENSURE(false, "Unknown error"); + } +} + +CContentReader::CContentReader( StreamInterface* stream, LocaleSet_t const & DocumentLocale ) : +CBaseReader( stream ) +{ +try + { + m_DefaultLocale = DocumentLocale; + Initialize( DOC_CONTENT_NAME ); + } + catch(xml_parser_exception&) + { + // OSL_ENSURE(false, ex.what()); + } + catch(...) + { + // OSL_ENSURE(false, "Unknown error"); + } +} + + +/** destructor. +*/ + +CContentReader::~CContentReader() +{ +} + +/*********************** helper functions ***********************/ + +/** choose an appropriate tag reader +*/ + +ITag* CContentReader::chooseTagReader( const std::wstring& tag_name, const XmlTagAttributes_t& XmlAttributes ) +{ + if (( tag_name == CONTENT_TEXT_A )||( tag_name == CONTENT_TEXT_P )|| + ( tag_name == CONTENT_TEXT_SPAN ) ||( tag_name == CONTENT_TEXT_H )|| + ( tag_name == CONTENT_TEXT_SEQUENCE ) ||( tag_name == CONTENT_TEXT_BOOKMARK_REF )|| + ( tag_name == CONTENT_TEXT_INDEX_TITLE_TEMPLATE ) ) + return new CSimpleTag(XmlAttributes); + else if ( tag_name == CONTENT_STYLE_STYLE ) + { + // if style:style | style:name is exist,, fill the style field, otherwise do nothing; + if ( XmlAttributes.find(CONTENT_STYLE_STYLE_NAME) != XmlAttributes.end()) + return new CAutoStyleTag(XmlAttributes); + else + return new CDummyTag; + } + else if ( ( tag_name == CONTENT_STYLE_PROPERTIES ) || ( tag_name == CONTENT_TEXT_STYLE_PROPERTIES ) ) + { + assert( !m_TagBuilderStack.empty() ); + + //here we presume that if CONTENT_STYLE_PROPERTIES tag is present, it just follow CONTENT_STYLE_STYLE; + ITag* pTagBuilder = m_TagBuilderStack.top(); + pTagBuilder->addAttributes( XmlAttributes ); + + return new CDummyTag; + } + else + return new CDummyTag; +} + +/** get style of the current content. +*/ +::std::wstring CContentReader::getCurrentContentStyle() +{ + assert( !m_TagBuilderStack.empty() ); + ITag* pTagBuilder = m_TagBuilderStack.top(); + + return pTagBuilder->getTagAttribute(CONTENT_TEXT_STYLENAME); +} + +/** add chunk into Chunk Buffer. +*/ +void CContentReader::addChunk( LocaleSet_t const & Locale, Content_t const & Content ) +{ + if ( Content == EMPTY_STRING ) + return; + + if ( ( ( m_ChunkBuffer.empty() ) || ( m_ChunkBuffer.back().first != Locale ) ) && + ( ( Content != SPACE ) && ( Content != LF ) ) ) + { + // if met a new locale, add a blank new chunk; + Chunk_t Chunk; + Chunk.first = Locale; + Chunk.second = EMPTY_STRING; + m_ChunkBuffer.push_back( Chunk ); + } + + if ( !m_ChunkBuffer.empty() ) + m_ChunkBuffer.back().second += Content; +} + +/** get a style's locale field. +*/ + +LocaleSet_t const & CContentReader::getLocale( const StyleName_t& Style ) +{ + if ( m_StyleMap.empty() ) + return m_DefaultLocale; + + StyleLocaleMap_t::const_iterator style_Iter; + + if ( ( style_Iter = m_StyleMap.find( Style ) ) == m_StyleMap.end( ) ) + return m_DefaultLocale; + else + return style_Iter->second; + +} + +/*********************** event handler functions ***********************/ + + +// start_element occurs when a tag is start + + +void CContentReader::start_element( + const string_t& /*raw_name*/, + const string_t& local_name, + const xml_tag_attribute_container_t& attributes) +{ + //get appropriate Xml Tag Builder using MetaInfoBuilderFactory; + ITag* pTagBuilder = chooseTagReader( local_name,attributes ); + assert( pTagBuilder != nullptr ); + pTagBuilder->startTag( ); + m_TagBuilderStack.push( pTagBuilder ); + +} + + +// end_element occurs when a tag is closed + + +void CContentReader::end_element(const string_t& /*raw_name*/, const string_t& local_name) +{ + assert( !m_TagBuilderStack.empty() ); + ITag* pTagBuilder = m_TagBuilderStack.top(); + + if ( local_name == CONTENT_STYLE_STYLE ) + { + StyleLocalePair_t StyleLocalePair = static_cast<CAutoStyleTag * >( pTagBuilder)->getStyleLocalePair(); + if ( ( static_cast<CAutoStyleTag * >( pTagBuilder)->isFull() ) && ( StyleLocalePair.second != m_DefaultLocale ) ) + m_StyleMap.insert( StyleLocalePair ); + } + if (( local_name == CONTENT_TEXT_A )||( local_name == CONTENT_TEXT_SPAN ) || + ( local_name == CONTENT_TEXT_SEQUENCE )||( local_name == CONTENT_TEXT_BOOKMARK_REF )) + addChunk( getLocale( getCurrentContentStyle() ), ::std::wstring( SPACE ) ); + if ((( local_name == CONTENT_TEXT_P )||( local_name == CONTENT_TEXT_H ) || + ( local_name == CONTENT_TEXT_INDEX_TITLE_TEMPLATE ) )&& + ( EMPTY_STRING != pTagBuilder->getTagContent( ) ) ) + addChunk( getLocale( getCurrentContentStyle() ), ::std::wstring( LF ) ); + + m_TagBuilderStack.pop(); + pTagBuilder->endTag(); + delete pTagBuilder; + +} + + +// characters occurs when receiving characters + + +void CContentReader::characters( const string_t& character ) +{ + if ( character.length() > 0 && !HasOnlySpaces( character ) ) + { + addChunk( getLocale( getCurrentContentStyle() ), character ); + + ITag* pTagBuilder = m_TagBuilderStack.top(); + pTagBuilder->addCharacters( character ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/dummytag.hxx b/shell/source/win32/ooofilereader/dummytag.hxx new file mode 100644 index 000000000..8af406d35 --- /dev/null +++ b/shell/source/win32/ooofilereader/dummytag.hxx @@ -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_SHELL_SOURCE_WIN32_OOOFILEREADER_DUMMYTAG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_DUMMYTAG_HXX + +#include "itag.hxx" + +/*************************** dummy tag readers ***************************/ + +/** Implements the ITag interface but does + nothing (Null object pattern), may be used for + tags we are not interested in to avoid if-else + branches. +*/ +class CDummyTag : public ITag +{ + public: + virtual void startTag() override {}; + + virtual void endTag() override {}; + + virtual void addCharacters(const std::wstring& /*characters*/) override {}; + + virtual void addAttributes(const XmlTagAttributes_t& /*attributes*/) override {}; + + virtual std::wstring getTagContent() override + { + return EMPTY_STRING; + }; + + virtual ::std::wstring getTagAttribute( ::std::wstring const & /*attrname*/ ) override { return ::std::wstring(EMPTY_STRING); }; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/itag.hxx b/shell/source/win32/ooofilereader/itag.hxx new file mode 100644 index 000000000..64a3f8bd7 --- /dev/null +++ b/shell/source/win32/ooofilereader/itag.hxx @@ -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 . + */ + + +#ifndef INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_ITAG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_ITAG_HXX + +#include <config.hxx> +#include <types.hxx> + +/*************************** interface of tag readers ***************************/ + +/** Interface for a xml tag character builder +*/ +class ITag +{ + public: + virtual ~ITag() {}; + + virtual void startTag() = 0; + virtual void endTag() = 0; + virtual void addCharacters(const std::wstring& characters) = 0; + virtual void addAttributes(const XmlTagAttributes_t& attributes) = 0; + virtual ::std::wstring getTagContent() = 0; + virtual ::std::wstring getTagAttribute( ::std::wstring const & attrname ) = 0; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/keywordstag.cxx b/shell/source/win32/ooofilereader/keywordstag.cxx new file mode 100644 index 000000000..4bc63e042 --- /dev/null +++ b/shell/source/win32/ooofilereader/keywordstag.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 "keywordstag.hxx" + +/*********************** CKeywordsTag ***********************/ + +void CKeywordsTag::startTag() { m_sCurrentKeyword.clear(); } + +void CKeywordsTag::endTag() { m_slKeywords.push_back(m_sCurrentKeyword); } + +void CKeywordsTag::addCharacters(const std::wstring& characters) +{ + m_sCurrentKeyword += characters; +} + +void CKeywordsTag::addAttributes(const XmlTagAttributes_t& /*attributes*/) +{ + // there are no attributes for keywords +} + +std::wstring CKeywordsTag::getTagContent() +{ + auto keywords_Iter = m_slKeywords.cbegin(); + auto keywords_Iter_end = m_slKeywords.cend(); + + std::wstring ret_KeyWord_String = (keywords_Iter != keywords_Iter_end) ? *keywords_Iter++ : L""; + for (; keywords_Iter != keywords_Iter_end; ++keywords_Iter) + ret_KeyWord_String += L"," + *keywords_Iter; + return ret_KeyWord_String; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/keywordstag.hxx b/shell/source/win32/ooofilereader/keywordstag.hxx new file mode 100644 index 000000000..2ec3de1cf --- /dev/null +++ b/shell/source/win32/ooofilereader/keywordstag.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_SHELL_SOURCE_WIN32_OOOFILEREADER_KEYWORDSTAG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_KEYWORDSTAG_HXX + +#include "itag.hxx" + +/*************************** metainfo tag readers ***************************/ + +/** Implements the ITag interface for + building a keyword list +*/ +class CKeywordsTag : public ITag +{ + public: + virtual void startTag() override; + virtual void endTag() override; + virtual void addCharacters(const std::wstring& characters) override; + virtual void addAttributes(const XmlTagAttributes_t& attributes) override; + virtual std::wstring getTagContent() override; + virtual ::std::wstring getTagAttribute( ::std::wstring const & /*attrname*/ ) override { return ::std::wstring(); }; + + private: + std::vector<std::wstring> m_slKeywords; + std::wstring m_sCurrentKeyword; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/metainforeader.cxx b/shell/source/win32/ooofilereader/metainforeader.cxx new file mode 100644 index 000000000..9eab4d5e0 --- /dev/null +++ b/shell/source/win32/ooofilereader/metainforeader.cxx @@ -0,0 +1,285 @@ +/* -*- 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 <metainforeader.hxx> +#include "dummytag.hxx" +#include "simpletag.hxx" +#include "keywordstag.hxx" + +#include <assert.h> + +/** constructor. +*/ +CMetaInfoReader::CMetaInfoReader( const Filepath_t& DocumentName ): +CBaseReader( DocumentName ) +{ + try + { + m_pKeywords_Builder = new CKeywordsTag; + m_pSimple_Builder = new CSimpleTag( ); + m_pDummy_Builder = new CDummyTag; + + //retrieve all information that is useful + m_AllMetaInfo[META_INFO_AUTHOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_TITLE] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_SUBJECT] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_KEYWORDS] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DESCRIPTION] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DOCUMENT_STATISTIC] = EMPTY_XML_TAG; + + m_AllMetaInfo[META_INFO_GENERATOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_CREATION] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_CREATOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_MODIFIED] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_LANGUAGE] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DOCUMENT_NUMBER] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_EDITING_TIME] = EMPTY_XML_TAG; + + Initialize( META_CONTENT_NAME ); + } + catch(xml_parser_exception&) + { + // OSL_ENSURE(false, ex.what()); + } + catch(...) + { + // OSL_ENSURE(false, "Unknown error"); + } +} + +CMetaInfoReader::CMetaInfoReader( StreamInterface* stream ) : +CBaseReader( stream ) +{ +try + { + m_pKeywords_Builder = new CKeywordsTag; + m_pSimple_Builder = new CSimpleTag( ); + m_pDummy_Builder = new CDummyTag; + + //retrieve all information that is useful + m_AllMetaInfo[META_INFO_AUTHOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_TITLE] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_SUBJECT] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_KEYWORDS] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DESCRIPTION] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DOCUMENT_STATISTIC] = EMPTY_XML_TAG; + + m_AllMetaInfo[META_INFO_GENERATOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_CREATION] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_CREATOR] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_MODIFIED] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_LANGUAGE] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_DOCUMENT_NUMBER] = EMPTY_XML_TAG; + m_AllMetaInfo[META_INFO_EDITING_TIME] = EMPTY_XML_TAG; + + Initialize( META_CONTENT_NAME ); + } + catch(xml_parser_exception&) + { + // OSL_ENSURE(false, ex.what()); + } + catch(...) + { + // OSL_ENSURE(false, "Unknown error"); + } + +} + +/** destructor. +*/ + +CMetaInfoReader::~CMetaInfoReader() +{ + delete m_pKeywords_Builder; + delete m_pSimple_Builder; + delete m_pDummy_Builder; +} + + +/*********************** export functions ***********************/ + +/** check if the Tag is in the target meta.xml file. + + @param TagName + the name of the tag that will be retrieve. +*/ +bool CMetaInfoReader::hasTag(const std::wstring& TagName) const +{ + return ( m_AllMetaInfo.find(TagName) != m_AllMetaInfo.end()); +} + +/** Get a specific tag content, compound tags will be returned as comma separated list. + + @param TagName + the name of the tag that will be retrieve. +*/ +std::wstring CMetaInfoReader::getTagData( const std::wstring& TagName) +{ + if( hasTag( TagName ) ) + return m_AllMetaInfo[TagName].first; + else + return EMPTY_STRING; +} + +/** check if the a tag has the specific attribute. + + @param TagName + the name of the tag. + @param AttributeName + the name of the attribute. +*/ +bool CMetaInfoReader::hasTagAttribute(const std::wstring& TagName, const std::wstring& AttributeName) +{ + return ( m_AllMetaInfo[TagName].second.find( AttributeName) != m_AllMetaInfo[TagName].second.end() ); +} + +/** Get a specific attribute content. + + @param TagName + the name of the tag. + @param AttributeName + the name of the attribute. +*/ +std::wstring CMetaInfoReader::getTagAttribute(const std::wstring& TagName, const std::wstring& AttributeName) +{ + if (hasTagAttribute(TagName, AttributeName)) + return m_AllMetaInfo[ TagName ].second[AttributeName]; + else + return EMPTY_STRING; +} + +/** helper function for getDefaultLocale(). +*/ +const LocaleSet_t EN_US_LOCALE( ::std::make_pair( ::std::wstring( L"en" ), ::std::wstring( L"US" ))); + +static bool isValidLocale(const ::std::wstring& Locale) +{ + return ( Locale.length() == 5 ); +} + +/** Get the default language of the whole document, if no such field, en-US is returned. +*/ +LocaleSet_t CMetaInfoReader::getDefaultLocale() +{ + if (hasTag(META_INFO_LANGUAGE)) + { + ::std::wstring locale = m_AllMetaInfo[META_INFO_LANGUAGE].first; + return isValidLocale(locale) ? ::std::make_pair(locale.substr( 0,2), locale.substr( 3,2)) : EN_US_LOCALE; + } + else + return EN_US_LOCALE; +} + +/*********************** tag related functions ***********************/ + +/** choose an appropriate tag reader +*/ + +ITag* CMetaInfoReader::chooseTagReader( const std::wstring& tag_name, const XmlTagAttributes_t& XmlAttributes ) +{ + if ( tag_name == META_INFO_KEYWORD ) + { + m_AllMetaInfo[META_INFO_KEYWORDS].second = XmlAttributes; + return m_pKeywords_Builder; + } + if (( tag_name == META_INFO_TITLE )||( tag_name == META_INFO_AUTHOR )||( tag_name == META_INFO_SUBJECT )||( tag_name == META_INFO_DESCRIPTION )|| + ( tag_name == META_INFO_DOCUMENT_STATISTIC )||( tag_name == META_INFO_GENERATOR )||( tag_name == META_INFO_CREATION )||( tag_name == META_INFO_CREATOR )|| + ( tag_name == META_INFO_MODIFIED )||( tag_name == META_INFO_LANGUAGE )||( tag_name == META_INFO_DOCUMENT_NUMBER )||( tag_name == META_INFO_EDITING_TIME ) ) + { + m_AllMetaInfo[tag_name].second = XmlAttributes; + return m_pSimple_Builder; + } + else + return m_pDummy_Builder; + +} + + +// save the received content into structure. + +void CMetaInfoReader::saveTagContent( const std::wstring& tag_name ) +{ + ITag* pTagBuilder; + if ( tag_name == META_INFO_KEYWORDS ) + pTagBuilder = m_pKeywords_Builder; + else if ( tag_name == META_INFO_KEYWORD ) + { + // added for support for OASIS xml file format. + m_AllMetaInfo[META_INFO_KEYWORDS].first =m_pKeywords_Builder->getTagContent( ); + return; + } + else if (( tag_name == META_INFO_TITLE )||( tag_name == META_INFO_AUTHOR )||( tag_name == META_INFO_SUBJECT )||( tag_name == META_INFO_DESCRIPTION )|| + ( tag_name == META_INFO_DOCUMENT_STATISTIC )||( tag_name == META_INFO_GENERATOR )||( tag_name == META_INFO_CREATION )||( tag_name == META_INFO_CREATOR )|| + ( tag_name == META_INFO_MODIFIED )||( tag_name == META_INFO_LANGUAGE )||( tag_name == META_INFO_DOCUMENT_NUMBER )||( tag_name == META_INFO_EDITING_TIME ) ) + pTagBuilder = m_pSimple_Builder; + else + pTagBuilder = m_pDummy_Builder; + + m_AllMetaInfo[tag_name].first =pTagBuilder->getTagContent( ); +} + + +/*********************** event handler functions ***********************/ + + +// start_element occurs when a tag is start + + +void CMetaInfoReader::start_element( + const string_t& /*raw_name*/, + const string_t& local_name, + const xml_tag_attribute_container_t& attributes) +{ + //get appropriate Xml Tag Builder using MetaInfoBuilderFactory; + ITag* pTagBuilder = chooseTagReader( local_name,attributes ); + assert( pTagBuilder!= nullptr ); + pTagBuilder->startTag( ); + m_TagBuilderStack.push( pTagBuilder ); + +} + + +// end_element occurs when a tag is closed + + +void CMetaInfoReader::end_element(const string_t& /*raw_name*/, const string_t& local_name) +{ + assert( !m_TagBuilderStack.empty() ); + ITag* pTagBuilder = m_TagBuilderStack.top(); + m_TagBuilderStack.pop(); + pTagBuilder->endTag(); + + saveTagContent( local_name ); + +} + + +// characters occurs when receiving characters + + +void CMetaInfoReader::characters( const string_t& character ) +{ + if ( character.length() > 0 && !HasOnlySpaces( character ) ) + { + ITag* pTagBuilder = m_TagBuilderStack.top(); + pTagBuilder->addCharacters( character ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/simpletag.cxx b/shell/source/win32/ooofilereader/simpletag.cxx new file mode 100644 index 000000000..82b85aec5 --- /dev/null +++ b/shell/source/win32/ooofilereader/simpletag.cxx @@ -0,0 +1,57 @@ +/* -*- 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 "simpletag.hxx" + +/*********************** CSimpleTag ***********************/ +void CSimpleTag::startTag() +{ + m_SimpleContent = L""; +} + + +void CSimpleTag::endTag() +{ + +} + +void CSimpleTag::addCharacters(const std::wstring& characters) +{ + m_SimpleContent += characters; +} + +void CSimpleTag::addAttributes(const XmlTagAttributes_t& attributes ) +{ + m_SimpleAttributes = attributes; +} + +std::wstring CSimpleTag::getTagContent( ) +{ + return m_SimpleContent; +} + +::std::wstring CSimpleTag::getTagAttribute( ::std::wstring const & attrname ) +{ + if ( m_SimpleAttributes.find(attrname) != m_SimpleAttributes.end()) + return m_SimpleAttributes[attrname]; + else + return ( ::std::wstring( EMPTY_STRING ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/ooofilereader/simpletag.hxx b/shell/source/win32/ooofilereader/simpletag.hxx new file mode 100644 index 000000000..4d878fc48 --- /dev/null +++ b/shell/source/win32/ooofilereader/simpletag.hxx @@ -0,0 +1,52 @@ +/* -*- 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_SHELL_SOURCE_WIN32_OOOFILEREADER_SIMPLETAG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_OOOFILEREADER_SIMPLETAG_HXX + +#include "itag.hxx" + +/*************************** simple tag readers ***************************/ + +/** Implements the ITag interface for + building a general info that is not a compound tag. +*/ +class CSimpleTag : public ITag +{ + public: + CSimpleTag(){}; + explicit CSimpleTag( const XmlTagAttributes_t& attributes ):m_SimpleAttributes(attributes){} + + virtual void startTag() override; + virtual void endTag() override; + virtual void addCharacters(const std::wstring& characters) override; + virtual void addAttributes(const XmlTagAttributes_t& attributes) override; + virtual std::wstring getTagContent() override; + + virtual ::std::wstring getTagAttribute( ::std::wstring const & attrname ) override; + + private: + std::wstring m_SimpleContent; + XmlTagAttributes_t m_SimpleAttributes; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/classfactory.cxx b/shell/source/win32/shlxthandler/classfactory.cxx new file mode 100644 index 000000000..95da69252 --- /dev/null +++ b/shell/source/win32/shlxthandler/classfactory.cxx @@ -0,0 +1,137 @@ +/* -*- 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 <global.hxx> +#include "classfactory.hxx" +#include <infotips.hxx> +#include <propsheets.hxx> +#include <columninfo.hxx> +#include <thumbviewer.hxx> +#include <shlxthdl.hxx> + + +LONG CClassFactory::s_ServerLocks = 0; + + +CClassFactory::CClassFactory(const CLSID& clsid) : + m_RefCnt(1), + m_Clsid(clsid) +{ + InterlockedIncrement(&g_DllRefCnt); +} + + +CClassFactory::~CClassFactory() +{ + InterlockedDecrement(&g_DllRefCnt); +} + + +// IUnknown methods + + +HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + + if (IID_IUnknown == riid || IID_IClassFactory == riid) + { + IUnknown* pUnk = this; + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + + return E_NOINTERFACE; +} + + +ULONG STDMETHODCALLTYPE CClassFactory::AddRef() +{ + return InterlockedIncrement(&m_RefCnt); +} + + +ULONG STDMETHODCALLTYPE CClassFactory::Release() +{ + LONG refcnt = InterlockedDecrement(&m_RefCnt); + + if (0 == refcnt) + delete this; + + return refcnt; +} + + +// IClassFactory methods + + +HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance( + IUnknown __RPC_FAR *pUnkOuter, + REFIID riid, + void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (pUnkOuter != nullptr) + return CLASS_E_NOAGGREGATION; + + IUnknown* pUnk = nullptr; + + if (CLSID_PROPERTYSHEET_HANDLER == m_Clsid) + pUnk = static_cast<IShellExtInit*>(new CPropertySheet()); + + else if (CLSID_INFOTIP_HANDLER == m_Clsid) + pUnk = static_cast<IQueryInfo*>(new CInfoTip()); + + else if (CLSID_COLUMN_HANDLER == m_Clsid) + pUnk = new CColumnInfo(); + + else if (CLSID_THUMBVIEWER_HANDLER == m_Clsid) + pUnk = static_cast<IExtractImage*>(new CThumbviewer()); + + if (nullptr == pUnk) + { + return E_OUTOFMEMORY; + } + + HRESULT hr = pUnk->QueryInterface(riid, ppvObject); + + // if QueryInterface failed the component will destroy itself + pUnk->Release(); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + InterlockedIncrement(&s_ServerLocks); + else + InterlockedDecrement(&s_ServerLocks); + + return S_OK; +} + + +bool CClassFactory::IsLocked() +{ + return (s_ServerLocks > 0); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/classfactory.hxx b/shell/source/win32/shlxthandler/classfactory.hxx new file mode 100644 index 000000000..8dd01aa0e --- /dev/null +++ b/shell/source/win32/shlxthandler/classfactory.hxx @@ -0,0 +1,65 @@ +/* -*- 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_SHELL_SOURCE_WIN32_SHLXTHANDLER_CLASSFACTORY_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_CLASSFACTORY_HXX + +#include <objidl.h> + +class CClassFactory : public IClassFactory +{ +public: + explicit CClassFactory(const CLSID& clsid); + virtual ~CClassFactory(); + + + // IUnknown methods + + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void __RPC_FAR *__RPC_FAR *ppvObject) override; + + virtual ULONG STDMETHODCALLTYPE AddRef() override; + + virtual ULONG STDMETHODCALLTYPE Release() override; + + + // IClassFactory methods + + + virtual HRESULT STDMETHODCALLTYPE CreateInstance( + IUnknown __RPC_FAR *pUnkOuter, + REFIID riid, + void __RPC_FAR *__RPC_FAR *ppvObject) override; + + virtual HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) override; + + static bool IsLocked(); + +private: + LONG m_RefCnt; + CLSID m_Clsid; + + static LONG s_ServerLocks; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/columninfo/columninfo.cxx b/shell/source/win32/shlxthandler/columninfo/columninfo.cxx new file mode 100644 index 000000000..81393140d --- /dev/null +++ b/shell/source/win32/shlxthandler/columninfo/columninfo.cxx @@ -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 . + */ + +#include <global.hxx> +#include <columninfo.hxx> +#include <fileextensions.hxx> +#include <metainforeader.hxx> +#include <utilities.hxx> +#include <config.hxx> + +#include <sal/macros.h> +#include <malloc.h> + +namespace /* private */ +{ + const SHCOLUMNINFO ColumnInfoTable[] = + { + {{PSGUID_SUMMARYINFORMATION, PIDSI_TITLE}, VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Title", L"Title"}, + {{PSGUID_SUMMARYINFORMATION, PIDSI_AUTHOR}, VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Author", L"Author"}, + {{PSGUID_SUMMARYINFORMATION, PIDSI_SUBJECT}, VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Subject", L"Subject"}, + {{PSGUID_SUMMARYINFORMATION, PIDSI_KEYWORDS}, VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Keywords", L"Keywords"}, + {{PSGUID_SUMMARYINFORMATION, PIDSI_COMMENTS}, VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Comments", L"Comments"}, + {{PSGUID_SUMMARYINFORMATION, PIDSI_PAGECOUNT},VT_BSTR, LVCFMT_LEFT, 30, SHCOLSTATE_TYPE_STR, L"Pagecount", L"Pagecount"} + }; + + size_t ColumnInfoTableSize = SAL_N_ELEMENTS(ColumnInfoTable); + +bool IsOOFileExtension(wchar_t const * Extension) +{ + for (size_t i = 0; i < OOFileExtensionTableSize; i++) + { + if (0 == _wcsicmp(Extension, OOFileExtensionTable[i].ExtensionU)) + return true; + } + + return false; +} + +} + + +CColumnInfo::CColumnInfo(LONG RefCnt) : + m_RefCnt(RefCnt) +{ + InterlockedIncrement(&g_DllRefCnt); +} + + +CColumnInfo::~CColumnInfo() +{ + InterlockedDecrement(&g_DllRefCnt); +} + + +// IUnknown methods + + +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CColumnInfo::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + + if (IID_IUnknown == riid || IID_IColumnProvider == riid) + { + IUnknown* pUnk = this; + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + + return E_NOINTERFACE; +} + + +COM_DECLSPEC_NOTHROW ULONG STDMETHODCALLTYPE CColumnInfo::AddRef() +{ + return InterlockedIncrement(&m_RefCnt); +} + + +COM_DECLSPEC_NOTHROW ULONG STDMETHODCALLTYPE CColumnInfo::Release() +{ + LONG refcnt = InterlockedDecrement(&m_RefCnt); + + if (0 == m_RefCnt) + delete this; + + return refcnt; +} + + +// IColumnProvider + + +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CColumnInfo::Initialize(LPCSHCOLUMNINIT /*psci*/) +{ + return S_OK; +} + +// Register all columns we support +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CColumnInfo::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci) +{ + if (dwIndex >= ColumnInfoTableSize) + return S_FALSE; + + // Return information on each column we support. Return S_FALSE + // to indicate that we have returned information on all our + // columns. GetColumnInfo will be called repeatedly until S_FALSE + // or an error is returned + psci->scid.fmtid = ColumnInfoTable[dwIndex].scid.fmtid; + psci->scid.pid = ColumnInfoTable[dwIndex].scid.pid; + ZeroMemory(psci->wszTitle, sizeof(psci->wszTitle)); + wcsncpy(psci->wszTitle, ColumnInfoTable[dwIndex].wszTitle, + SAL_N_ELEMENTS(psci->wszTitle) - 1); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CColumnInfo::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) +{ + if (IsOOFileExtension(pscd->pwszExt)) + { + try + { + std::wstring fname = getShortPathName( std::wstring( pscd->wszFile ) ); + + CMetaInfoReader meta_info_accessor(fname); + + VariantClear(pvarData); + + if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_TITLE) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagData( META_INFO_TITLE ).c_str()); + + return S_OK; + } + else if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_AUTHOR) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagData( META_INFO_AUTHOR).c_str()); + + return S_OK; + } + else if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_SUBJECT) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagData( META_INFO_SUBJECT).c_str()); + + return S_OK; + } + else if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_KEYWORDS) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagData( META_INFO_KEYWORDS).c_str()); + + return S_OK; + } + else if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_COMMENTS) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagData( META_INFO_DESCRIPTION).c_str()); + + return S_OK; + } + else if (IsEqualGUID (pscid->fmtid, FMTID_SummaryInformation) && pscid->pid == PIDSI_PAGECOUNT) + { + pvarData->vt = VT_BSTR; + pvarData->bstrVal = SysAllocString(meta_info_accessor.getTagAttribute( META_INFO_DOCUMENT_STATISTIC, META_INFO_PAGES).c_str()); + + return S_OK; + } + } + catch (const std::exception&) + { + return S_FALSE; + } + } + + return S_FALSE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/infotips/infotips.cxx b/shell/source/win32/shlxthandler/infotips/infotips.cxx new file mode 100644 index 000000000..af03f4a32 --- /dev/null +++ b/shell/source/win32/shlxthandler/infotips/infotips.cxx @@ -0,0 +1,365 @@ +/* -*- 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 <global.hxx> +#include <infotips.hxx> +#include <shlxthdl.hxx> +#include <metainforeader.hxx> +#include <contentreader.hxx> +#include <utilities.hxx> +#include <registry.hxx> +#include <fileextensions.hxx> +#include <iso8601_converter.hxx> +#include <config.hxx> + +#include <resource.h> +#include <stdio.h> +#include <utility> +#include <stdlib.h> + + +#define MAX_STRING 80 +#define KB 1024.0 +const std::wstring WSPACE(SPACE); + + +CInfoTip::CInfoTip(LONG RefCnt) : + m_RefCnt(RefCnt) +{ + ZeroMemory(m_szFileName, sizeof(m_szFileName)); + InterlockedIncrement(&g_DllRefCnt); +} + + +CInfoTip::~CInfoTip() +{ + InterlockedDecrement(&g_DllRefCnt); +} + + +// IUnknown methods + + +HRESULT STDMETHODCALLTYPE CInfoTip::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + + IUnknown* pUnk = nullptr; + + if (IID_IUnknown == riid || IID_IQueryInfo == riid) + { + pUnk = static_cast<IQueryInfo*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + else if (IID_IPersistFile == riid) + { + pUnk = static_cast<IPersistFile*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + + return E_NOINTERFACE; +} + + +ULONG STDMETHODCALLTYPE CInfoTip::AddRef() +{ + return InterlockedIncrement(&m_RefCnt); +} + + +ULONG STDMETHODCALLTYPE CInfoTip::Release() +{ + LONG refcnt = InterlockedDecrement(&m_RefCnt); + + if (0 == m_RefCnt) + delete this; + + return refcnt; +} + +//********************helper functions for GetInfoTip functions********************** + +/** get file type information from registry. +*/ +static std::wstring getFileTypeInfo(const std::wstring& file_extension) +{ + wchar_t extKeyValue[MAX_STRING]; + wchar_t typeKeyValue[MAX_STRING]; + ::std::wstring sDot(L"."); + if (QueryRegistryKey(HKEY_CLASSES_ROOT, sDot.append(file_extension).c_str(), L"", extKeyValue, MAX_STRING)) + if (QueryRegistryKey( HKEY_CLASSES_ROOT, extKeyValue, L"",typeKeyValue, MAX_STRING)) + return typeKeyValue; + + return EMPTY_STRING; +} + +/** get file size. +*/ +static DWORD getSizeOfFile( wchar_t const * FileName ) +{ + HANDLE hFile = CreateFileW(FileName, // open file + GENERIC_READ, // open for reading + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // share for all operations + nullptr, // no security + OPEN_EXISTING, // existing file only + FILE_ATTRIBUTE_NORMAL, // normal file + nullptr); // no attr. template + + if (hFile != INVALID_HANDLE_VALUE) + { + DWORD dwSize = GetFileSize( hFile, nullptr ); + CloseHandle( hFile ); + return dwSize; + } + + return INVALID_FILE_SIZE; +} + +/** format file size in to be more readable. +*/ +static std::wstring formatSizeOfFile( DWORD dwSize ) +{ + if ( dwSize < 1000 ) + { + char buffer[3]; + int dFileSize = dwSize; + + _itoa( dFileSize, buffer, 10 ); + return StringToWString( buffer ).append(StringToWString("B")); + } + + char *buffer=nullptr; + int decimal, sign; + double dFileSize = static_cast<double>(dwSize)/KB; + + buffer = _fcvt( dFileSize, 1, &decimal, &sign ); + + ::std::wstring wsTemp = StringToWString( buffer ); + int pos=decimal % 3; + ::std::wstring wsBuffer = wsTemp.substr( 0,pos); + + if ( decimal ) + for (;decimal - pos > 2;pos += 3) + { + if (pos) + wsBuffer.append(StringToWString(",")); + wsBuffer.append( wsTemp.substr( pos, 3) ); + } + else + wsBuffer.append(StringToWString("0")); + + wsBuffer.append(StringToWString(".")); + wsBuffer.append(wsTemp.substr( decimal, wsTemp.size()-decimal )); + wsBuffer.append(StringToWString("KB")); + + return wsBuffer; +} + + +/** get file size information. +*/ +static std::wstring getFileSizeInfo(wchar_t const * FileName) +{ + DWORD dwSize=getSizeOfFile(FileName); + if (dwSize != INVALID_FILE_SIZE) + return formatSizeOfFile( dwSize ); + + return EMPTY_STRING; +} + + +// IQueryInfo methods + + +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CInfoTip::GetInfoTip(DWORD /*dwFlags*/, PWSTR* ppwszTip) +{ + std::wstring msg; + const std::wstring CONST_SPACE(SPACE); + + //display File Type, no matter other info is loaded successfully or not. + std::wstring tmpTypeStr = getFileTypeInfo( get_file_name_extension(m_szFileName) ); + if ( tmpTypeStr != EMPTY_STRING ) + { + msg += GetResString(IDS_TYPE_COLON) + CONST_SPACE; + msg += tmpTypeStr; + } + + try + { + CMetaInfoReader meta_info_accessor(m_szFileName); + + //display document title; + if ( meta_info_accessor.getTagData( META_INFO_TITLE ).length() > 0) + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString(IDS_TITLE_COLON) + CONST_SPACE; + msg += meta_info_accessor.getTagData( META_INFO_TITLE ); + } + else + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString(IDS_TITLE_COLON) + CONST_SPACE; + msg += m_FileNameOnly; + } + + //display document author; + if ( meta_info_accessor.getTagData( META_INFO_AUTHOR ).length() > 0) + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString( IDS_AUTHOR_COLON ) + CONST_SPACE; + msg += meta_info_accessor.getTagData( META_INFO_AUTHOR ); + } + + //display document subject; + if ( meta_info_accessor.getTagData( META_INFO_SUBJECT ).length() > 0) + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString(IDS_SUBJECT_COLON) + CONST_SPACE; + msg += meta_info_accessor.getTagData( META_INFO_SUBJECT ); + } + + //display document description; + if ( meta_info_accessor.getTagData( META_INFO_DESCRIPTION ).length() > 0) + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString( IDS_COMMENTS_COLON ) + CONST_SPACE; + msg += meta_info_accessor.getTagData( META_INFO_DESCRIPTION ); + } + + //display modified time formatted into locale representation. + if ( iso8601_date_to_local_date(meta_info_accessor.getTagData(META_INFO_MODIFIED )).length() > 0) + { + if ( msg != EMPTY_STRING ) + msg += L"\n"; + msg += GetResString( IDS_MODIFIED_COLON ) + CONST_SPACE; + msg += iso8601_date_to_local_date(meta_info_accessor.getTagData(META_INFO_MODIFIED )); + } + } + catch (const std::exception&) + { + } + + //display file size, no matter other information is loaded successfully or not. + std::wstring tmpSizeStr = getFileSizeInfo( m_szFileName ); + if ( tmpSizeStr != EMPTY_STRING ) + { + msg += L"\n"; + msg += GetResString( IDS_SIZE_COLON ) + CONST_SPACE; + msg += tmpSizeStr; + } + + + //finalize and assign the string. + LPMALLOC lpMalloc; + HRESULT hr = SHGetMalloc(&lpMalloc); + + if (SUCCEEDED(hr)) + { + size_t len = sizeof(wchar_t) * msg.length() + sizeof(wchar_t); + wchar_t* pMem = static_cast<wchar_t*>(lpMalloc->Alloc(len)); + + ZeroMemory(pMem, len); + + wcscpy_s(pMem, msg.length()+1, msg.c_str()); + + *ppwszTip = pMem; + lpMalloc->Release(); + + return S_OK; + } + + return E_FAIL; +} + + +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CInfoTip::GetInfoFlags(DWORD * /*pdwFlags*/ ) +{ + return E_NOTIMPL; +} + + +// IPersist methods + + +HRESULT STDMETHODCALLTYPE CInfoTip::GetClassID(CLSID* pClassID) +{ + *pClassID = CLSID_INFOTIP_HANDLER; + return S_OK; +} + + +// IPersistFile methods + + +HRESULT STDMETHODCALLTYPE CInfoTip::Load(LPCOLESTR pszFileName, DWORD /*dwMode*/) +{ + std::wstring fname = pszFileName; + + // there must be a '\' and there must even be an + // extension, else we would not have been called + std::wstring::iterator begin = fname.begin() + fname.find_last_of(L"\\") + 1; + std::wstring::iterator end = fname.end(); + + m_FileNameOnly = std::wstring(begin, end); + + fname = getShortPathName( fname ); + + // ZeroMemory because strncpy doesn't '\0'-terminates the destination + // string; reserve the last place in the buffer for the final '\0' + // that's why '(sizeof(m_szFileName) - 1)' + ZeroMemory(m_szFileName, sizeof(m_szFileName)); + wcsncpy(m_szFileName, fname.c_str(), (sizeof(m_szFileName)/sizeof(*m_szFileName) - 1)); + + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE CInfoTip::IsDirty() +{ + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE CInfoTip::Save(LPCOLESTR /*pszFileName*/, BOOL /*fRemember*/) +{ + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE CInfoTip::SaveCompleted(LPCOLESTR /*pszFileName*/) +{ + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE CInfoTip::GetCurFile(LPOLESTR __RPC_FAR * /*ppszFileName*/) +{ + return E_NOTIMPL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/ooofilt/ooofilt.cxx b/shell/source/win32/shlxthandler/ooofilt/ooofilt.cxx new file mode 100644 index 000000000..c45c69930 --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/ooofilt.cxx @@ -0,0 +1,956 @@ +/* -*- 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 . + */ + + +// File: ooofilt.cxx +// Contents: Filter Implementation for OpenOffice.Org Document using +// Indexing Service +// Summary: The LibreOffice filter reads OpenOffice.org XML files (with +// the extension .sxw .sxi, etc) and ODF files and extract +// their content, author, keywords,subject,comments and title +// to be filtered. + +// Platform: Windows 2000, Windows XP + +#include <contentreader.hxx> +#include <metainforeader.hxx> +#include <registry.hxx> +#include <fileextensions.hxx> + + +// Include file Purpose +// windows.h Win32 declarations +// string.h string wstring declarations +// filter.h IFilter interface declarations +// filterr.h FACILITY_ITF error definitions for IFilter +// ntquery.h Indexing Service declarations +// assert.h assertion function. +// ooofilt.hxx LibreOffice filter declarations +// propspec.hxx PROPSPEC + + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <string.h> +#include <filter.h> +#include <filterr.h> +#include <ntquery.h> +#include <assert.h> +#include "ooofilt.hxx" +#include <objidl.h> +#include <stdio.h> +#include "propspec.hxx" + +#include <stream_helper.hxx> + +#include <olectl.h> // declarations of DllRegisterServer/DllUnregisterServer + +//C------------------------------------------------------------------------- +// Class: COooFilter +// Summary: Implements LibreOffice filter class + +//M------------------------------------------------------------------------- +// Method: COooFilter::COooFilter +// Summary: Class constructor +// Arguments: void +// Purpose: Manages global instance count + +COooFilter::COooFilter() : + m_lRefs(1), + m_pContentReader(nullptr), + m_pMetaInfoReader(nullptr), + m_eState(FilterState::FilteringContent), + m_ulUnicodeBufferLen(0), + m_ulUnicodeCharsRead(0), + m_ulPropertyNum(0), + m_ulCurrentPropertyNum(0), + m_ulChunkID(1), + m_fContents(false), + m_fEof(false), + m_ChunkPosition(0), + m_cAttributes(0), + m_pAttributes(nullptr), + m_pStream(nullptr) + +{ + InterlockedIncrement( &g_lInstances ); +} +//M------------------------------------------------------------------------- +// Method: COooFilter::~COooFilter +// Summary: Class destructor +// Arguments: void +// Purpose: Manages global instance count and file handle + +COooFilter::~COooFilter() +{ + delete [] m_pAttributes; + delete m_pContentReader; + delete m_pMetaInfoReader; + delete m_pStream; + + InterlockedDecrement( &g_lInstances ); +} + +//M------------------------------------------------------------------------- +// Method: COooFilter::QueryInterface (IUnknown::QueryInterface) +// Summary: Queries for requested interface // Arguments: riid +// [in] Reference IID of requested interface +// ppvObject +// [out] Address that receives requested interface pointer +// Returns: S_OK +// Interface is supported +// E_NOINTERFACE +// Interface is not supported + +HRESULT STDMETHODCALLTYPE COooFilter::QueryInterface( + REFIID riid, + void ** ppvObject) +{ + IUnknown *pUnkTemp = nullptr; + if ( IID_IFilter == riid ) + pUnkTemp = static_cast<IFilter *>(this); + else if ( IID_IPersistFile == riid ) + pUnkTemp = static_cast<IPersistFile *>(this); + else if ( IID_IPersist == riid ) + pUnkTemp = static_cast<IPersistFile *>(this); + else if (IID_IPersistStream == riid) + pUnkTemp = static_cast<IPersistStream *>(this); + else if ( IID_IUnknown == riid ) + pUnkTemp = static_cast<IPersistFile *>(this); + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + *ppvObject = pUnkTemp; + pUnkTemp->AddRef(); + return S_OK; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::AddRef (IUnknown::AddRef) +// Summary: Increments interface refcount +// Arguments: void +// Returns: Value of incremented interface refcount + +ULONG STDMETHODCALLTYPE COooFilter::AddRef() +{ + return InterlockedIncrement( &m_lRefs ); +} +//M------------------------------------------------------------------------- +// Method: COooFilter::Release (IUnknown::Release) +// Summary: Decrements interface refcount, deleting if unreferenced +// Arguments: void +// Returns: Value of decremented interface refcount + +ULONG STDMETHODCALLTYPE COooFilter::Release() +{ + ULONG ulTmp = InterlockedDecrement( &m_lRefs ); + + if ( 0 == ulTmp ) + delete this; + return ulTmp; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::Init (IFilter::Init) +// Summary: Initializes LibreOffice filter instance +// Arguments: grfFlags +// [in] Flags for filter behavior +// cAttributes +// [in] Number attributes in array aAttributes +// aAttributes +// [in] Array of requested attribute strings +// pFlags +// [out] Pointer to return flags for additional properties +// Returns: S_OK +// Initialization succeeded +// E_FAIL +// File not previously loaded +// E_INVALIDARG +// Count and contents of attributes do not agree +// FILTER_E_ACCESS +// Unable to access file to be filtered +// FILTER_E_PASSWORD +// (not implemented) + +const int COUNT_ATTRIBUTES = 5; + +SCODE STDMETHODCALLTYPE COooFilter::Init( + ULONG grfFlags, + ULONG cAttributes, + FULLPROPSPEC const * aAttributes, + ULONG * pFlags) +{ + // Enumerate OLE properties, since any NTFS file can have them + *pFlags = IFILTER_FLAGS_OLE_PROPERTIES; + try + { + m_fContents = false; + m_ulPropertyNum = 0; + m_ulCurrentPropertyNum = 0; + if ( m_cAttributes > 0 ) + { + delete[] m_pAttributes; + m_pAttributes = nullptr; + m_cAttributes = 0; + } + if( 0 < cAttributes ) + { + // Filter properties specified in aAttributes + if ( nullptr == aAttributes ) + return E_INVALIDARG; + m_pAttributes = new CFullPropSpec[cAttributes]; + m_cAttributes = cAttributes; + // Is caller want to filter contents? + CFullPropSpec const *pAttrib = reinterpret_cast<CFullPropSpec const *>(aAttributes); + ULONG ulNumAttr; + for ( ulNumAttr = 0 ; ulNumAttr < cAttributes; ulNumAttr++ ) + { + if ( pAttrib[ulNumAttr].IsPropertyPropid() && + pAttrib[ulNumAttr].GetPropertyPropid() == PID_STG_CONTENTS && + pAttrib[ulNumAttr].GetPropSet() == guidStorage ) + { + m_fContents = true; + } + // save the requested properties. + m_pAttributes[ulNumAttr] = pAttrib[ulNumAttr]; + } + } + else if ( grfFlags & IFILTER_INIT_APPLY_INDEX_ATTRIBUTES ) + { + // Filter contents and all pseudo-properties + m_fContents = true; + + m_pAttributes = new CFullPropSpec[COUNT_ATTRIBUTES]; + m_cAttributes = COUNT_ATTRIBUTES; + m_pAttributes[0].SetPropSet( FMTID_SummaryInformation ); + m_pAttributes[0].SetProperty( PIDSI_AUTHOR ); + m_pAttributes[1].SetPropSet( FMTID_SummaryInformation ); + m_pAttributes[1].SetProperty( PIDSI_TITLE ); + m_pAttributes[2].SetPropSet( FMTID_SummaryInformation ); + m_pAttributes[2].SetProperty( PIDSI_SUBJECT ); + m_pAttributes[3].SetPropSet( FMTID_SummaryInformation ); + m_pAttributes[3].SetProperty( PIDSI_KEYWORDS ); + m_pAttributes[4].SetPropSet( FMTID_SummaryInformation ); + m_pAttributes[4].SetProperty( PIDSI_COMMENTS ); + } + else if ( 0 == grfFlags ) + { + // Filter only contents + m_fContents = true; + } + else + m_fContents = false; + // Re-initialize + if ( m_fContents ) + { + m_fEof = false; + m_eState = FilterState::FilteringContent; + m_ulUnicodeCharsRead = 0; + m_ChunkPosition = 0; + } + else + { + m_fEof = true; + m_eState = FilterState::FilteringProperty; + } + m_ulChunkID = 1; + } + catch (const std::exception&) + { + return E_FAIL; + } + + return S_OK; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::GetChunk (IFilter::GetChunk) +// Summary: Gets the next chunk +// Arguments: ppStat +// [out] Pointer to description of current chunk +// Returns: S_OK +// Chunk was successfully retrieved +// E_FAIL +// Character conversion failed +// FILTER_E_ACCESS +// General access failure occurred +// FILTER_E_END_OF_CHUNKS +// Previous chunk was the last chunk +// FILTER_E_EMBEDDING_UNAVAILABLE +// (not implemented) +// FILTER_E_LINK_UNAVAILABLE +// (not implemented) +// FILTER_E_PASSWORD +// (not implemented) + +SCODE STDMETHODCALLTYPE COooFilter::GetChunk(STAT_CHUNK * pStat) +{ + for(;;) + { + switch ( m_eState ) + { + case FilterState::FilteringContent: + { + if( m_ChunkPosition == m_pContentReader ->getChunkBuffer().size() ) + { + m_ulUnicodeBufferLen=0; + m_fEof = true; + } + + if ( !m_fContents || m_fEof ) + { + m_eState = FilterState::FilteringProperty; + continue; + } + m_pwsBuffer = m_pContentReader -> getChunkBuffer()[m_ChunkPosition].second; + m_ulUnicodeBufferLen = static_cast<ULONG>(m_pwsBuffer.length()); + DWORD ChunkLCID = LocaleSetToLCID( m_pContentReader -> getChunkBuffer()[m_ChunkPosition].first ); + // Set chunk description + pStat->idChunk = m_ulChunkID; + pStat->breakType = CHUNK_NO_BREAK; + pStat->flags = CHUNK_TEXT; + pStat->locale = ChunkLCID; + pStat->attribute.guidPropSet = guidStorage; + pStat->attribute.psProperty.ulKind = PRSPEC_PROPID; + pStat->attribute.psProperty.propid = PID_STG_CONTENTS; + pStat->idChunkSource = m_ulChunkID; + pStat->cwcStartSource = 0; + pStat->cwcLenSource = 0; + m_ulUnicodeCharsRead = 0; + m_ulChunkID++; + m_ChunkPosition++; + return S_OK; + } + case FilterState::FilteringProperty: + { + if ( m_cAttributes == 0 ) + return FILTER_E_END_OF_CHUNKS; + while( !( ( m_pAttributes[m_ulPropertyNum].IsPropertyPropid() ) && + ( m_pAttributes[m_ulPropertyNum].GetPropSet() == FMTID_SummaryInformation ) )|| + ( ( m_pAttributes[m_ulPropertyNum].GetPropertyPropid() != PIDSI_AUTHOR ) && + ( m_pAttributes[m_ulPropertyNum].GetPropertyPropid() != PIDSI_TITLE ) && + ( m_pAttributes[m_ulPropertyNum].GetPropertyPropid() != PIDSI_SUBJECT ) && + ( m_pAttributes[m_ulPropertyNum].GetPropertyPropid() != PIDSI_KEYWORDS ) && + ( m_pAttributes[m_ulPropertyNum].GetPropertyPropid() != PIDSI_COMMENTS ) ) ) + { + if ( m_ulPropertyNum < m_cAttributes ) + m_ulPropertyNum++; + else + break; + } + if ( m_ulPropertyNum == m_cAttributes) + return FILTER_E_END_OF_CHUNKS; + else + { + // Set chunk description + pStat->idChunk = m_ulChunkID; + pStat->breakType = CHUNK_EOS; + pStat->flags = CHUNK_VALUE; + pStat->locale = GetSystemDefaultLCID(); + pStat->attribute.guidPropSet = FMTID_SummaryInformation; + pStat->attribute.psProperty.ulKind = PRSPEC_PROPID; + pStat->attribute.psProperty.propid = m_pAttributes[m_ulPropertyNum].GetPropertyPropid(); + pStat->idChunkSource = m_ulChunkID; + pStat->cwcStartSource = 0; + pStat->cwcLenSource = 0; + m_ulCurrentPropertyNum = m_ulPropertyNum; + m_ulPropertyNum++; + m_ulChunkID++; + return S_OK; + } + } + }//switch(...) + }//for(;;) +} +//M------------------------------------------------------------------------- +// Method: COooFilter::GetText (IFilter::GetText) +// Summary: Retrieves UNICODE text for index +// Arguments: pcwcBuffer +// [in] Pointer to size of UNICODE buffer +// [out] Pointer to count of UNICODE characters returned +// awcBuffer +// [out] Pointer to buffer to receive UNICODE text +// Returns: S_OK +// Text successfully retrieved, but text remains in chunk +// FILTER_E_NO_MORE_TEXT +// All of the text in the current chunk has been returned +// FILTER_S_LAST_TEXT +// Next call to GetText will return FILTER_E_NO_MORE_TEXT + +SCODE STDMETHODCALLTYPE COooFilter::GetText(ULONG * pcwcBuffer, WCHAR * awcBuffer) +{ + switch ( m_eState ) + { + case FilterState::FilteringProperty: + return FILTER_E_NO_TEXT; + case FilterState::FilteringContent: + { + if ( !m_fContents || 0 == m_ulUnicodeBufferLen ) + { + *pcwcBuffer = 0; + return FILTER_E_NO_MORE_TEXT; + } + // Copy UNICODE characters in chunk buffer to output UNICODE buffer + ULONG ulToCopy = min( *pcwcBuffer, m_ulUnicodeBufferLen - m_ulUnicodeCharsRead ); + wmemcpy( awcBuffer, m_pwsBuffer.c_str() + m_ulUnicodeCharsRead, ulToCopy ); + ZeroMemory( + awcBuffer + ulToCopy, (*pcwcBuffer - ulToCopy) * sizeof (WCHAR)); + m_ulUnicodeCharsRead += ulToCopy; + *pcwcBuffer = ulToCopy; + if ( m_ulUnicodeBufferLen == m_ulUnicodeCharsRead ) + { + m_ulUnicodeCharsRead = 0; + m_ulUnicodeBufferLen = 0; + return FILTER_S_LAST_TEXT; + } + return S_OK; + } + } + return E_FAIL; // Should not happen! +} +//M------------------------------------------------------------------------- +// Method: GetMetaInfoNameFromPropertyId +// Summary: helper function to convert PropertyID into respective +// MetaInfo names. +// Arguments: ulPropID +// [in] property ID +// Returns: corresponding metainfo names. + + +static ::std::wstring GetMetaInfoNameFromPropertyId( ULONG ulPropID ) +{ + switch ( ulPropID ) + { + case PIDSI_AUTHOR: return META_INFO_AUTHOR; + case PIDSI_TITLE: return META_INFO_TITLE; + case PIDSI_SUBJECT: return META_INFO_SUBJECT; + case PIDSI_KEYWORDS: return META_INFO_KEYWORDS; + case PIDSI_COMMENTS: return META_INFO_DESCRIPTION; + default: return EMPTY_STRING; + } +} +//M------------------------------------------------------------------------- +// Method: COooFilter::GetValue (IFilter::GetValue) +// Summary: Retrieves properties for index +// Arguments: ppPropValue +// [out] Address that receives pointer to property value +// Returns: FILTER_E_NO_VALUES +// Always +// FILTER_E_NO_MORE_VALUES +// (not implemented) + + +SCODE STDMETHODCALLTYPE COooFilter::GetValue(PROPVARIANT ** ppPropValue) +{ + if (m_eState == FilterState::FilteringContent) + return FILTER_E_NO_VALUES; + else // m_eState == FilteringProperty + { + if ( m_cAttributes == 0 || ( m_ulCurrentPropertyNum == m_ulPropertyNum ) ) + return FILTER_E_NO_MORE_VALUES; + PROPVARIANT *pPropVar = static_cast<PROPVARIANT *>(CoTaskMemAlloc( sizeof (PROPVARIANT) )); + if ( pPropVar == nullptr ) + return E_OUTOFMEMORY; + ::std::wstring wsTagName= GetMetaInfoNameFromPropertyId( m_pAttributes[m_ulCurrentPropertyNum].GetPropertyPropid() ); + if ( wsTagName == EMPTY_STRING ) + return FILTER_E_NO_VALUES; + ::std::wstring wsTagData = m_pMetaInfoReader->getTagData(wsTagName); + pPropVar->vt = VT_LPWSTR; + size_t cw = wsTagData.length() + 1; // reserve one for the '\0' + pPropVar->pwszVal = static_cast<WCHAR*>( CoTaskMemAlloc(cw*sizeof(WCHAR)) ); + if (pPropVar->pwszVal == nullptr) + { + CoTaskMemFree(pPropVar); + return E_OUTOFMEMORY; + } + wmemcpy(pPropVar->pwszVal, wsTagData.c_str(), cw); + *ppPropValue = pPropVar; + m_ulCurrentPropertyNum = m_ulPropertyNum; + return S_OK; + } +} +//M------------------------------------------------------------------------- +// Method: COooFilter::BindRegion (IFilter::BindRegion) +// Summary: Creates moniker or other interface for indicated text +// Arguments: origPos +// [in] Description of text location and extent +// riid +// [in] Reference IID of specified interface +// ppunk +// [out] Address that receives requested interface pointer +// Returns: E_NOTIMPL +// Always +// FILTER_W_REGION_CLIPPED +// (not implemented) + + +SCODE STDMETHODCALLTYPE COooFilter::BindRegion( + FILTERREGION /*origPos*/, + REFIID /*riid*/, + void ** /*ppunk*/) +{ + // BindRegion is currently reserved for future use + return E_NOTIMPL; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::GetClassID (IPersist::GetClassID) +// Summary: Retrieves the class id of the filter class +// Arguments: pClassID +// [out] Pointer to the class ID of the filter +// Returns: S_OK +// Always +// E_FAIL +// (not implemented) + +HRESULT STDMETHODCALLTYPE COooFilter::GetClassID(CLSID * pClassID) +{ + *pClassID = CLSID_COooFilter; + return S_OK; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::IsDirty (IPersistFile::IsDirty) +// Summary: Checks whether file has changed since last save +// Arguments: void +// Returns: S_FALSE +// Always +// S_OK +// (not implemented) + +HRESULT STDMETHODCALLTYPE COooFilter::IsDirty() +{ + // File is opened read-only and never changes + return S_FALSE; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::Load (IPersistFile::Load) +// Summary: Opens and initializes the specified file +// Arguments: pszFileName +// [in] Pointer to zero-terminated string +// of absolute path of file to open +// dwMode +// [in] Access mode to open the file +// Returns: S_OK +// File was successfully loaded +// E_OUTOFMEMORY +// File could not be loaded due to insufficient memory +// E_FAIL +// (not implemented) + +HRESULT STDMETHODCALLTYPE COooFilter::Load(LPCOLESTR pszFileName, DWORD /*dwMode*/) +{ + // Load just sets the filename for GetChunk to read and ignores the mode + m_pwszFileName = getShortPathName( pszFileName ); + + // Open the file previously specified in call to IPersistFile::Load and get content. + try + { + delete m_pMetaInfoReader; + m_pMetaInfoReader = new CMetaInfoReader(m_pwszFileName); + + delete m_pContentReader; + m_pContentReader = new CContentReader(m_pwszFileName, m_pMetaInfoReader->getDefaultLocale()); + } + catch (const std::exception&) + { + return E_FAIL; + } + return S_OK; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::Save (IPersistFile::Save) +// Summary: Saves a copy of the current file being filtered +// Arguments: pszFileName +// [in] Pointer to zero-terminated string of +// absolute path of where to save file +// fRemember +// [in] Whether the saved copy is made the current file +// Returns: E_FAIL +// Always +// S_OK +// (not implemented) + +HRESULT STDMETHODCALLTYPE COooFilter::Save(LPCOLESTR /*pszFileName*/, BOOL /*fRemember*/) +{ + // File is opened read-only; saving it is an error + return E_FAIL; +} +//M------------------------------------------------------------------------- +// Method: COooFilter::SaveCompleted (IPersistFile::SaveCompleted) +// Summary: Determines whether a file save is completed +// Arguments: pszFileName +// [in] Pointer to zero-terminated string of +// absolute path where file was previously saved +// Returns: S_OK +// Always + +HRESULT STDMETHODCALLTYPE COooFilter::SaveCompleted(LPCOLESTR /*pszFileName*/) +{ + // File is opened read-only, so "save" is always finished + return S_OK; +} + +//M------------------------------------------------------------------------- +// Method: COooFilter::Load (IPersistStream::Load) +// Summary: Initializes an object from the stream where it was previously saved +// Arguments: pStm +// [in] Pointer to stream from which object should be loaded +// Returns: S_OK +// E_OUTOFMEMORY +// E_FAIL + +HRESULT STDMETHODCALLTYPE COooFilter::Load(IStream *pStm) +{ + m_pStream = new BufferStream(pStm); + try + { + delete m_pMetaInfoReader; + m_pMetaInfoReader = new CMetaInfoReader(m_pStream); + + delete m_pContentReader; + m_pContentReader = new CContentReader(m_pStream, m_pMetaInfoReader->getDefaultLocale()); + } + catch (const std::exception&) + { + return E_FAIL; + } + return S_OK; +} + +//M------------------------------------------------------------------------- +// Method: COooFilter::GetSizeMax (IPersistStream::GetSizeMax) +// Summary: Returns the size in bytes of the stream needed to save the object. +// Arguments: pcbSize +// [out] Pointer to a 64 bit unsigned int indicating the size needed +// Returns: E_NOTIMPL + +HRESULT STDMETHODCALLTYPE COooFilter::GetSizeMax(ULARGE_INTEGER * /*pcbSize*/) +{ + return E_NOTIMPL; +} + +//M------------------------------------------------------------------------- +// Method: COooFilter::Save (IPersistStream::Save) +// Summary: Save object to specified stream +// Arguments: pStm +// [in] Pointer to stream +// fClearDirty +// [in] Indicates whether to clear dirty flag +// Returns: E_NOTIMPL + +HRESULT STDMETHODCALLTYPE COooFilter::Save(IStream * /*pStm*/, BOOL ) +{ + return E_NOTIMPL; +} + +//M------------------------------------------------------------------------- +// Method: COooFilter::GetCurFile (IPersistFile::GetCurFile) +// Summary: Returns a copy of the current file name +// Arguments: ppszFileName +// [out] Address to receive pointer to zero-terminated +// string for absolute path to current file +// Returns: S_OK +// A valid absolute path was successfully returned +// S_FALSE +// (not implemented) +// E_OUTOFMEMORY +// Operation failed due to insufficient memory +// E_FAIL +// Operation failed due to some reason +// other than insufficient memory + +HRESULT STDMETHODCALLTYPE COooFilter::GetCurFile(LPOLESTR * ppszFileName) +{ + if ( EMPTY_STRING == m_pwszFileName ) + return E_FAIL; + else + *ppszFileName = const_cast<LPWSTR>(m_pwszFileName.c_str()); + return S_OK; +} + +//M------------------------------------------------------------------------- +// Method: COooFilterCF::COooFilterCF +// Summary: Class factory constructor +// Arguments: void +// Purpose: Manages global instance count + +COooFilterCF::COooFilterCF() : + m_lRefs(1) +{ + InterlockedIncrement( &g_lInstances ); +} +//M------------------------------------------------------------------------- +// Method: COooFilterCF::~COooFilterCF +// Summary: Class factory destructor +// Arguments: void +// Purpose: Manages global instance count + +COooFilterCF::~COooFilterCF() +{ + InterlockedDecrement( &g_lInstances ); +} +//M------------------------------------------------------------------------- +// Method: COooFilterCF::QueryInterface (IUnknown::QueryInterface) +// Summary: Queries for requested interface +// Arguments: riid +// [in] Reference IID of requested interface +// ppvObject +// [out] Address that receives requested interface pointer +// Returns: S_OK +// Interface is supported +// E_NOINTERFACE +// Interface is not supported + +HRESULT STDMETHODCALLTYPE COooFilterCF::QueryInterface(REFIID riid, void ** ppvObject) +{ + IUnknown *pUnkTemp; + + if ( IID_IClassFactory == riid ) + pUnkTemp = this; + else if ( IID_IUnknown == riid ) + pUnkTemp = this; + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + *ppvObject = pUnkTemp; + pUnkTemp->AddRef(); + return S_OK; +} +//M------------------------------------------------------------------------- +// Method: COooFilterCF::AddRef (IUnknown::AddRef) +// Summary: Increments interface refcount +// Arguments: void +// Returns: Value of incremented interface refcount + +ULONG STDMETHODCALLTYPE COooFilterCF::AddRef() +{ + return InterlockedIncrement( &m_lRefs ); +} +//M------------------------------------------------------------------------- +// Method: COooFilterCF::Release (IUnknown::Release) +// Summary: Decrements interface refcount, deleting if unreferenced +// Arguments: void +// Returns: Value of decremented refcount + +ULONG STDMETHODCALLTYPE COooFilterCF::Release() +{ + ULONG ulTmp = InterlockedDecrement( &m_lRefs ); + + if ( 0 == ulTmp ) + delete this; + return ulTmp; +} +//M------------------------------------------------------------------------- +// Method: COooFilterCF::CreateInstance (IClassFactory::CreateInstance) +// Summary: Creates new LibreOffice filter object +// Arguments: pUnkOuter +// [in] Pointer to IUnknown interface of aggregating object +// riid +// [in] Reference IID of requested interface +// ppvObject +// [out] Address that receives requested interface pointer +// Returns: S_OK +// LibreOffice filter object was successfully created +// CLASS_E_NOAGGREGATION +// pUnkOuter parameter was non-NULL +// E_NOINTERFACE +// (not implemented) +// E_OUTOFMEMORY +// LibreOffice filter object could not be created +// due to insufficient memory +// E_UNEXPECTED +// Unsuccessful due to an unexpected condition + +HRESULT STDMETHODCALLTYPE COooFilterCF::CreateInstance( + IUnknown * pUnkOuter, + REFIID riid, + void * * ppvObject) +{ + COooFilter *pIUnk = nullptr; + if ( nullptr != pUnkOuter ) + return CLASS_E_NOAGGREGATION; + pIUnk = new COooFilter(); + if ( SUCCEEDED( pIUnk->QueryInterface( riid , ppvObject ) ) ) + { + // Release extra refcount from QueryInterface + pIUnk->Release(); + } + else + { + delete pIUnk; + return E_UNEXPECTED; + } + return S_OK; +} + +//M------------------------------------------------------------------------- +// Method: COooFilterCF::LockServer (IClassFactory::LockServer) +// Summary: Forces/allows filter class to remain loaded/be unloaded +// Arguments: fLock +// [in] TRUE to lock, FALSE to unlock +// Returns: S_OK +// Always +// E_FAIL +// (not implemented) +// E_OUTOFMEMORY +// (not implemented) +// E_UNEXPECTED +// (not implemented) + +HRESULT STDMETHODCALLTYPE COooFilterCF::LockServer(BOOL fLock) +{ + if( fLock ) + InterlockedIncrement( &g_lInstances ); + else + InterlockedDecrement( &g_lInstances ); + return S_OK; +} +//+------------------------------------------------------------------------- +// DLL: ooofilt.dll +// Summary: Implements Dynamic Link Library functions for LibreOffice filter + +//F------------------------------------------------------------------------- +// Function: DllMain +// Summary: Called from C-Runtime on process/thread attach/detach +// Arguments: hInstance +// [in] Handle to the DLL +// fdwReason +// [in] Reason for calling DLL entry point +// lpReserve +// [in] Details of DLL initialization and cleanup +// Returns: TRUE +// Always + +extern "C" BOOL WINAPI DllMain( + HINSTANCE hInstance, + DWORD fdwReason, + LPVOID /*lpvReserved*/ +) +{ + if ( DLL_PROCESS_ATTACH == fdwReason ) + DisableThreadLibraryCalls( hInstance ); + return TRUE; +} +//F------------------------------------------------------------------------- +// Function: DllGetClassObject +// Summary: Create LibreOffice filter class factory object +// Arguments: cid +// [in] Class ID of class that class factory creates +// iid +// [in] Reference IID of requested class factory interface +// ppvObj +// [out] Address that receives requested interface pointer +// Returns: S_OK +// Class factory object was created successfully +// CLASS_E_CLASSNOTAVAILABLE +// DLL does not support the requested class +// E_INVALIDARG +// (not implemented +// E_OUTOFMEMORY +// Insufficient memory to create the class factory object +// E_UNEXPECTED +// Unsuccessful due to an unexpected condition + +extern "C" HRESULT STDMETHODCALLTYPE DllGetClassObject( + REFCLSID cid, + REFIID iid, + LPVOID * ppvObj +) +{ + COooFilterCF* pImpl = nullptr; + IUnknown *pResult = nullptr; + + if ( CLSID_COooFilter == cid ) + { + pImpl = new COooFilterCF; + pResult = pImpl; + } + else + return CLASS_E_CLASSNOTAVAILABLE; + if( SUCCEEDED( pResult->QueryInterface( iid, ppvObj ) ) ) + // Release extra refcount from QueryInterface + pResult->Release(); + else + { + delete pImpl; + return E_UNEXPECTED; + } + return S_OK; +} +//F------------------------------------------------------------------------- +// Function: DllCanUnloadNow +// Summary: Indicates whether it is possible to unload DLL +// Arguments: void +// Returns: S_OK +// DLL can be unloaded now +// S_FALSE +// DLL must remain loaded + +extern "C" HRESULT STDMETHODCALLTYPE DllCanUnloadNow() +{ + if ( 0 >= g_lInstances ) + return S_OK; + else + return S_FALSE; +} +//F------------------------------------------------------------------------- +// Function: DllRegisterServer +// DllUnregisterServer +// Summary: Registers and unregisters DLL server +// Returns: DllRegisterServer +// S_OK +// Registration was successful +// SELFREG_E_CLASS +// Registration was unsuccessful +// SELFREG_E_TYPELIB +// (not implemented) +// E_OUTOFMEMORY +// (not implemented) +// E_UNEXPECTED +// (not implemented) +// DllUnregisterServer +// S_OK +// Unregistration was successful +// S_FALSE +// Unregistration was successful, but other +// entries still exist for the DLL's classes +// SELFREG_E_CLASS +// (not implemented) +// SELFREG_E_TYPELIB +// (not implemented) +// E_OUTOFMEMORY +// (not implemented) +// E_UNEXPECTED +// (not implemented) + +STDAPI DllRegisterServer() +{ + return S_OK; +} + + +STDAPI DllUnregisterServer() +{ + return S_OK; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/ooofilt/ooofilt.def b/shell/source/win32/shlxthandler/ooofilt/ooofilt.def new file mode 100644 index 000000000..d731a151f --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/ooofilt.def @@ -0,0 +1,5 @@ +EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/shell/source/win32/shlxthandler/ooofilt/ooofilt.hxx b/shell/source/win32/shlxthandler/ooofilt/ooofilt.hxx new file mode 100644 index 000000000..c178452ff --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/ooofilt.hxx @@ -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_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_OOOFILT_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_OOOFILT_HXX + +#include <types.hxx> + +//+------------------------------------------------------------------------- +// Contents: LibreOffice filter declarations +// Platform: Windows 2000, Windows XP + +//+------------------------------------------------------------------------- + +class CContentReader; +class CMetaInfoReader; +class CFullPropSpec; + +LONG g_lInstances = 0; // Global count of COooFilter and COooFilterCF instances +GUID const guidStorage = PSGUID_STORAGE; // GUID for storage property set + +//C------------------------------------------------------------------------- +// Class: COooFilter +// Purpose: Implements interfaces of LibreOffice filter + +// OooFilter Class ID +// {7BC0E710-5703-45be-A29D-5D46D8B39262} +GUID const CLSID_COooFilter = +{ + 0x7bc0e710, + 0x5703, + 0x45be, + { 0xa2, 0x9d, 0x5d, 0x46, 0xd8, 0xb3, 0x92, 0x62 } +}; + +// LibreOffice Persistent Handler Class ID +// {7BC0E713-5703-45be-A29D-5D46D8B39262} +const CLSID CLSID_PERSISTENT_HANDLER = +{0x7bc0e713, 0x5703, 0x45be, {0xa2, 0x9d, 0x5d, 0x46, 0xd8, 0xb3, 0x92, 0x62}}; + +// LibreOffice Persistent Handler Addin Registered Class ID +// {89BCB740-6119-101A-BCB7-00DD010655AF} +const CLSID CLSID_PERSISTENT_HANDLER_ADDIN = +{0x89bcb740, 0x6119, 0x101a, {0xbc, 0xb7, 0x00, 0xdd, 0x01, 0x06, 0x55, 0xaf}}; + +// LibreOffice Filter Handler Class ID +// {7BC0E710-5703-45be-A29D-5D46D8B39262} +const CLSID CLSID_FILTER_HANDLER = +{0x7bc0e710, 0x5703, 0x45be, {0xa2, 0x9d, 0x5d, 0x46, 0xd8, 0xb3, 0x92, 0x62}}; + +enum class FilterState +{ + FilteringContent, // Filtering the content property + FilteringProperty // Filtering the pseudo property +}; +class COooFilter : public IFilter, public IPersistFile, public IPersistStream +{ +public: + // From IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void ** ppvObject) override; + virtual ULONG STDMETHODCALLTYPE AddRef() override; + virtual ULONG STDMETHODCALLTYPE Release() override; + + // From IFilter + virtual SCODE STDMETHODCALLTYPE Init( + ULONG grfFlags, + ULONG cAttributes, + FULLPROPSPEC const * aAttributes, + ULONG * pFlags) override; + virtual SCODE STDMETHODCALLTYPE GetChunk( + STAT_CHUNK * pStat) override; + virtual SCODE STDMETHODCALLTYPE GetText( + ULONG * pcwcBuffer, + WCHAR * awcBuffer) override; + + virtual SCODE STDMETHODCALLTYPE GetValue( + PROPVARIANT ** ppPropValue) override; + + virtual SCODE STDMETHODCALLTYPE BindRegion( + FILTERREGION origPos, + REFIID riid, + void ** ppunk) override; + + // From IPersistFile + virtual HRESULT STDMETHODCALLTYPE GetClassID( + CLSID * pClassID) override; + virtual HRESULT STDMETHODCALLTYPE IsDirty() override; + virtual HRESULT STDMETHODCALLTYPE Load( + LPCOLESTR pszFileName, + DWORD dwMode) override; + virtual HRESULT STDMETHODCALLTYPE Save( + LPCOLESTR pszFileName, + BOOL fRemember) override; + + virtual HRESULT STDMETHODCALLTYPE SaveCompleted( + LPCOLESTR pszFileName) override; + + virtual HRESULT STDMETHODCALLTYPE GetCurFile( + LPOLESTR * ppszFileName) override; + + // From IPersistStream + virtual HRESULT STDMETHODCALLTYPE Load( + IStream *pStm) override; + + virtual HRESULT STDMETHODCALLTYPE Save( + IStream *pStm, + BOOL fClearDirty) override; + + virtual HRESULT STDMETHODCALLTYPE GetSizeMax( + ULARGE_INTEGER *pcbSize) override; + + +private: + friend class COooFilterCF; + + COooFilter(); + virtual ~COooFilter(); + + LONG m_lRefs; // Reference count + CContentReader * m_pContentReader; // A content reader that retrieves document content. + CMetaInfoReader * m_pMetaInfoReader; // A metainfo reader that retrieves document metainfo. + FilterState m_eState; // State of filtering + ::std::wstring m_pwszFileName; // Name of input file to filter + ULONG m_ulUnicodeBufferLen; // UNICODE Characters read from file to chunk buffer + ULONG m_ulUnicodeCharsRead; // UNICODE Characters read from chunk buffer + ULONG m_ulPropertyNum; // Number of properties that has been processed + ULONG m_ulCurrentPropertyNum; // Current Property that is processing; + ULONG m_ulChunkID; // Current chunk id + bool m_fContents; // TRUE if contents requested + bool m_fEof; // TRUE if end of file reached + ::std::wstring m_pwsBuffer; // Buffer to save UNICODE content from ChunkBuffer. + ULONG m_ChunkPosition; // Chunk pointer to specify the current Chunk; + ULONG m_cAttributes; // Count of attributes + CFullPropSpec * m_pAttributes; // Attributes to filter + StreamInterface * m_pStream; + +}; + +//C------------------------------------------------------------------------- +// Class: COooFilterCF +// Purpose: Implements class factory for LibreOffice filter + + +class COooFilterCF : public IClassFactory +{ +public: + // From IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void ** ppvObject) override; + + virtual ULONG STDMETHODCALLTYPE AddRef() override; + virtual ULONG STDMETHODCALLTYPE Release() override; + + // From IClassFactory + virtual HRESULT STDMETHODCALLTYPE CreateInstance( + IUnknown * pUnkOuter, + REFIID riid, void ** ppvObject) override; + + virtual HRESULT STDMETHODCALLTYPE LockServer( + BOOL fLock) override; + +private: + friend HRESULT STDMETHODCALLTYPE DllGetClassObject( + REFCLSID cid, + REFIID iid, + LPVOID * ppvObj); + + COooFilterCF(); + virtual ~COooFilterCF(); + + LONG m_lRefs; // Reference count +}; + +#endif // INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_OOOFILT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/ooofilt/propspec.cxx b/shell/source/win32/shlxthandler/ooofilt/propspec.cxx new file mode 100644 index 000000000..f03cef033 --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/propspec.cxx @@ -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 . + */ + + +//+------------------------------------------------------------------------- +// File: propspec.cxx +// Contents: C++ wrappers for FULLPROPSPEC + +#include <sal/config.h> + +#include <new> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <filter.h> + +#include "propspec.hxx" + +//refer to ms-help://MS.VSCC/MS.MSDNVS.2052/com/stgasstg_7agk.htm +//FMTID_SummaryInformation +//GUID CLSID_SummaryInformation = { +// 0xF29F85E0, +// 0x4FF9, +// 0x1068, +// { 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 } +//}; +//+------------------------------------------------------------------------- +// Member: CFullPropSpec::CFullPropSpec, public +// Synopsis: Default constructor +// Effects: Defines property with null guid and propid 0 + + +CFullPropSpec::CFullPropSpec() +{ + _psProperty.ulKind = PRSPEC_PROPID; + _psProperty.propid = 0; +} +//+------------------------------------------------------------------------- +// Member: CFullPropSpec::CFullPropSpec, public +// Synopsis: Construct propid based propspec +// Arguments: [guidPropSet] -- Property set +// [pidProperty] -- Property + +CFullPropSpec::CFullPropSpec( GUID const & guidPropSet, PROPID pidProperty ) : + _guidPropSet( guidPropSet ) +{ + _psProperty.ulKind = PRSPEC_PROPID; + _psProperty.propid = pidProperty; +} +//+------------------------------------------------------------------------- +// Member: CFullPropSpec::CFullPropSpec, public +// Synopsis: Construct name based propspec +// Arguments: [guidPropSet] -- Property set +// [wcsProperty] -- Property + +CFullPropSpec::CFullPropSpec( GUID const & guidPropSet, + WCHAR const * wcsProperty ) : + _guidPropSet( guidPropSet ) +{ + _psProperty.ulKind = PRSPEC_PROPID; + SetProperty( wcsProperty ); +} +//+------------------------------------------------------------------------- +// Member: CFullPropSpec::CFullPropSpec, public +// Synopsis: Copy constructor +// Arguments: [src] -- Source property spec + +CFullPropSpec::CFullPropSpec( CFullPropSpec const & src ) : + _guidPropSet( src._guidPropSet ) +{ + _psProperty.ulKind = src._psProperty.ulKind; + if ( _psProperty.ulKind == PRSPEC_LPWSTR ) + { + if ( src._psProperty.lpwstr ) + { + _psProperty.ulKind = PRSPEC_PROPID; + SetProperty( src._psProperty.lpwstr ); + } + else + _psProperty.lpwstr = nullptr; + } + else + { + _psProperty.propid = src._psProperty.propid; + } +} + +//+------------------------------------------------------------------------- +// Member: CFullPropSpec::operator=, public +// Synopsis: Assignment operator +// Arguments: [Property] -- Source property + +CFullPropSpec & CFullPropSpec::operator=( CFullPropSpec const & Property ) +{ + if (this != &Property) + { + // Clean up. + this->CFullPropSpec::~CFullPropSpec(); + + ::new (this) CFullPropSpec( Property ); + } + return *this; +} + +CFullPropSpec::~CFullPropSpec() +{ + if ( _psProperty.ulKind == PRSPEC_LPWSTR && + _psProperty.lpwstr ) + { + CoTaskMemFree( _psProperty.lpwstr ); + } +} + +void CFullPropSpec::SetProperty( PROPID pidProperty ) +{ + if ( _psProperty.ulKind == PRSPEC_LPWSTR && + nullptr != _psProperty.lpwstr ) + { + CoTaskMemFree( _psProperty.lpwstr ); + } + _psProperty.ulKind = PRSPEC_PROPID; + _psProperty.propid = pidProperty; +} +BOOL CFullPropSpec::SetProperty( WCHAR const * wcsProperty ) +{ + if ( _psProperty.ulKind == PRSPEC_LPWSTR && + nullptr != _psProperty.lpwstr ) + { + CoTaskMemFree( _psProperty.lpwstr ); + } + _psProperty.ulKind = PRSPEC_LPWSTR; + int len = static_cast<int>( (wcslen( wcsProperty ) + 1) * sizeof( WCHAR ) ); + _psProperty.lpwstr = static_cast<WCHAR *>(CoTaskMemAlloc( len )); + if ( nullptr != _psProperty.lpwstr ) + { + memcpy( _psProperty.lpwstr, + wcsProperty, + len ); + return TRUE; + } + else + { + _psProperty.lpwstr = nullptr; + return FALSE; + } +} +bool CFullPropSpec::operator==( CFullPropSpec const & prop ) const +{ + if ( memcmp( &prop._guidPropSet, + &_guidPropSet, + sizeof( _guidPropSet ) ) != 0 || + prop._psProperty.ulKind != _psProperty.ulKind ) + { + return false; + } + switch( _psProperty.ulKind ) + { + case PRSPEC_LPWSTR: + return( _wcsicmp( GetPropertyName(), prop.GetPropertyName() ) == 0 ); + case PRSPEC_PROPID: + return( GetPropertyPropid() == prop.GetPropertyPropid() ); + default: + return false; + } +} +bool CFullPropSpec::operator!=( CFullPropSpec const & prop ) const +{ + if (*this == prop) + return false; + else + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/ooofilt/propspec.hxx b/shell/source/win32/shlxthandler/ooofilt/propspec.hxx new file mode 100644 index 000000000..f9e1fc289 --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/propspec.hxx @@ -0,0 +1,127 @@ +/* -*- 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_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_PROPSPEC_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_PROPSPEC_HXX + +//+------------------------------------------------------------------------- +// File: propspec.hxx +// Contents: C++ wrapper(s) for FULLPROPSPEC + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> +#include <ntquery.h> +//+------------------------------------------------------------------------- +// Declare: CLSID_SummaryInformation, GUID +// CLSID_Storage, GUID +// Contents: Definitions of OpenOffice.org Document properties + + +//+------------------------------------------------------------------------- +// Class: CFullPropertySpec +// Purpose: Describes full (PropertySet\Property) name of a property. + + +class CFullPropSpec +{ +public: + CFullPropSpec(); + CFullPropSpec( GUID const & guidPropSet, PROPID pidProperty ); + CFullPropSpec( GUID const & guidPropSet, WCHAR const * wcsProperty ); + // Validity check + inline BOOL IsValid() const; + + // Copy constructors/assignment/clone + CFullPropSpec( CFullPropSpec const & Property ); + CFullPropSpec & operator=( CFullPropSpec const & Property ); + ~CFullPropSpec(); + // Memory allocation + void * operator new( size_t size ); + void operator delete( void * p ); + inline FULLPROPSPEC * CastToStruct(); + inline FULLPROPSPEC const * CastToStruct() const; + // Comparators + bool operator==( CFullPropSpec const & prop ) const; + bool operator!=( CFullPropSpec const & prop ) const; + // Member variable access + inline void SetPropSet( GUID const & guidPropSet ); + inline GUID const & GetPropSet() const; + + void SetProperty( PROPID pidProperty ); + BOOL SetProperty( WCHAR const * wcsProperty ); + inline WCHAR const * GetPropertyName() const; + inline PROPID GetPropertyPropid() const; + inline PROPSPEC GetPropSpec() const; + inline BOOL IsPropertyName() const; + inline BOOL IsPropertyPropid() const; +private: + GUID _guidPropSet = {}; + PROPSPEC _psProperty; +}; +// Inline methods for CFullPropSpec +inline void * CFullPropSpec::operator new( size_t size ) +{ + void * p = CoTaskMemAlloc( size ); + return p; +} +inline void CFullPropSpec::operator delete( void * p ) +{ + if ( p ) + CoTaskMemFree( p ); +} +inline BOOL CFullPropSpec::IsValid() const +{ + return ( _psProperty.ulKind == PRSPEC_PROPID || + nullptr != _psProperty.lpwstr ); +} +inline void CFullPropSpec::SetPropSet( GUID const & guidPropSet ) +{ + _guidPropSet = guidPropSet; +} +inline GUID const & CFullPropSpec::GetPropSet() const +{ + return _guidPropSet; +} +inline PROPSPEC CFullPropSpec::GetPropSpec() const +{ + return _psProperty; +} +inline WCHAR const * CFullPropSpec::GetPropertyName() const +{ + return _psProperty.lpwstr; +} +inline PROPID CFullPropSpec::GetPropertyPropid() const +{ + return _psProperty.propid; +} +inline BOOL CFullPropSpec::IsPropertyName() const +{ + return _psProperty.ulKind == PRSPEC_LPWSTR; +} +inline BOOL CFullPropSpec::IsPropertyPropid() const +{ + return _psProperty.ulKind == PRSPEC_PROPID; +} + +#endif // INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_OOOFILT_PROPSPEC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/ooofilt/stream_helper.cxx b/shell/source/win32/shlxthandler/ooofilt/stream_helper.cxx new file mode 100644 index 000000000..76578ba70 --- /dev/null +++ b/shell/source/win32/shlxthandler/ooofilt/stream_helper.cxx @@ -0,0 +1,137 @@ +/* -*- 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 . + */ + + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <stdio.h> +#include <objidl.h> +#include <stream_helper.hxx> + +BufferStream::BufferStream(IStream *str) : + stream(str) +{ + // These next few lines work around the "Seek pointer" bug found on Vista. + char cBuf[20]; + unsigned long nCount; + ULARGE_INTEGER nNewPosition; + LARGE_INTEGER nMove; + nMove.QuadPart = 0; + stream->Seek( nMove, STREAM_SEEK_SET, &nNewPosition ); + stream->Read( cBuf, 20, &nCount ); +} + +BufferStream::~BufferStream() +{ +} + +unsigned long BufferStream::sread (unsigned char *buf, unsigned long size) +{ + unsigned long newsize; + HRESULT hr; + + hr = stream->Read (buf, size, &newsize); + if (hr == S_OK) + return newsize; + else + return static_cast<unsigned long>(0); +} + +long BufferStream::stell () +{ + HRESULT hr; + LARGE_INTEGER Move; + ULARGE_INTEGER NewPosition; + Move.QuadPart = 0; + NewPosition.QuadPart = 0; + + hr = stream->Seek (Move, STREAM_SEEK_CUR, &NewPosition); + if (hr == S_OK) + return static_cast<long>(NewPosition.QuadPart); + else + return -1; +} + +long BufferStream::sseek (long offset, int origin) +{ + HRESULT hr; + LARGE_INTEGER Move; + DWORD dwOrigin; + Move.QuadPart = static_cast<__int64>(offset); + + switch (origin) + { + case SEEK_CUR: + dwOrigin = STREAM_SEEK_CUR; + break; + case SEEK_END: + dwOrigin = STREAM_SEEK_END; + break; + case SEEK_SET: + dwOrigin = STREAM_SEEK_SET; + break; + default: + return -1; + } + + hr = stream->Seek (Move, dwOrigin, nullptr); + if (hr == S_OK) + return 0; + else + return -1; +} + +FileStream::FileStream(const Filepath_char_t *filename) : + file(nullptr) +{ + // fdo#67534: avoid locking to not interfere with soffice opening the file + file = _wfsopen(filename, L"rb", _SH_DENYNO); +} + +FileStream::~FileStream() +{ + if (file) + fclose(file); +} + +unsigned long FileStream::sread (unsigned char *buf, unsigned long size) +{ + if (file) + return static_cast<unsigned long>(fread(buf, 1, size, file)); + return 0; +} + +long FileStream::stell () +{ + if (file) + return ftell(file); + return -1; +} + +long FileStream::sseek (long offset, int origin) +{ + if (file) + return fseek(file, offset, origin); + return -1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/prophdl/propertyhdl.cxx b/shell/source/win32/shlxthandler/prophdl/propertyhdl.cxx new file mode 100644 index 000000000..49a8b8d06 --- /dev/null +++ b/shell/source/win32/shlxthandler/prophdl/propertyhdl.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 <global.hxx> +#include <propertyhdl.hxx> +#include <fileextensions.hxx> +#include <metainforeader.hxx> +#include <utilities.hxx> +#include <config.hxx> + +#include <propkey.h> +#include <propvarutil.h> +#include <sal/macros.h> + +#include <malloc.h> +#include <strsafe.h> + +#include <stream_helper.hxx> + + +// Module global + +LONG g_DllRefCnt = 0; +static HINSTANCE g_hModule = nullptr; + +const PROPERTYKEY g_rgPROPERTIES[] = +{ + PKEY_Title, + PKEY_Author, + PKEY_Subject, + PKEY_Keywords, + PKEY_Comment +}; + +size_t const gPropertyTableSize = SAL_N_ELEMENTS(g_rgPROPERTIES); + + +CPropertyHdl::CPropertyHdl( LONG nRefCnt ) : + m_RefCnt( nRefCnt ), + m_pCache( nullptr ) +{ + OutputDebugStringFormatW( L"CPropertyHdl: CTOR\n" ); + InterlockedIncrement( &g_DllRefCnt ); +} + + +CPropertyHdl::~CPropertyHdl() +{ + if ( m_pCache ) + { + m_pCache->Release(); + m_pCache = nullptr; + } + InterlockedDecrement( &g_DllRefCnt ); +} + + +// IUnknown methods + +HRESULT STDMETHODCALLTYPE CPropertyHdl::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + + if (IID_IUnknown == riid || IID_IPropertyStore == riid) + { + OutputDebugStringFormatW( L"CPropertyHdl: QueryInterface (IID_IPropertyStore)\n" ); + IUnknown* pUnk = static_cast<IPropertyStore*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + else if (IID_IPropertyStoreCapabilities == riid) + { + OutputDebugStringFormatW( L"CPropertyHdl: QueryInterface (IID_IPropertyStoreCapabilities)\n" ); + IUnknown* pUnk = static_cast<IPropertyStore*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + else if (IID_IInitializeWithStream == riid) + { + OutputDebugStringFormatW( L"CPropertyHdl: QueryInterface (IID_IInitializeWithStream)\n" ); + IUnknown* pUnk = static_cast<IInitializeWithStream*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + OutputDebugStringFormatW( L"CPropertyHdl: QueryInterface (something different)\n" ); + + return E_NOINTERFACE; +} + + +ULONG STDMETHODCALLTYPE CPropertyHdl::AddRef() +{ + return InterlockedIncrement( &m_RefCnt ); +} + + +ULONG STDMETHODCALLTYPE CPropertyHdl::Release() +{ + LONG refcnt = InterlockedDecrement( &m_RefCnt ); + + if ( 0 == m_RefCnt ) + delete this; + + return refcnt; +} + + +// IPropertyStore + +HRESULT STDMETHODCALLTYPE CPropertyHdl::GetCount( DWORD *pcProps ) +{ + HRESULT hr = E_UNEXPECTED; + if ( m_pCache && pcProps ) + { + hr = m_pCache->GetCount( pcProps ); + } + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CPropertyHdl::GetAt( DWORD iProp, PROPERTYKEY *pKey ) +{ + HRESULT hr = E_UNEXPECTED; + if ( m_pCache ) + { + hr = m_pCache->GetAt( iProp, pKey ); + } + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CPropertyHdl::GetValue( REFPROPERTYKEY key, PROPVARIANT *pPropVar ) +{ + HRESULT hr = E_UNEXPECTED; + if ( m_pCache ) + { + hr = m_pCache->GetValue( key, pPropVar ); + } + + return hr; +} + + +HRESULT STDMETHODCALLTYPE +CPropertyHdl::SetValue(REFPROPERTYKEY /*key*/, REFPROPVARIANT /*propVar*/) +{ + HRESULT hr = E_UNEXPECTED; + if ( m_pCache ) + { + hr = STG_E_ACCESSDENIED; + } + return hr; +} + + +HRESULT STDMETHODCALLTYPE CPropertyHdl::Commit() +{ + return S_OK; +} + + +// IPropertyStore + +HRESULT STDMETHODCALLTYPE +CPropertyHdl::IsPropertyWritable(REFPROPERTYKEY /*key*/) +{ + // We start with read only properties only + return S_FALSE; +} + + +// IInitializeWithStream + +HRESULT STDMETHODCALLTYPE CPropertyHdl::Initialize( IStream *pStream, DWORD grfMode ) +{ + if ( grfMode & STGM_READWRITE ) + return STG_E_ACCESSDENIED; + + if ( !m_pCache ) + { + if ( FAILED( PSCreateMemoryPropertyStore( IID_PPV_ARGS( &m_pCache ) ) ) ) + OutputDebugStringFormatW( L"CPropertyHdl::Initialize: PSCreateMemoryPropertyStore failed" ); + + BufferStream tmpStream(pStream); + + CMetaInfoReader *pMetaInfoReader = nullptr; + + try + { + pMetaInfoReader = new CMetaInfoReader( &tmpStream ); + LoadProperties( pMetaInfoReader ); + delete pMetaInfoReader; + } + catch (const std::exception& e) + { + // To output 8-bit string using unicode version of formatting functions, use capital %S type + // see https://msdn.microsoft.com/en-us/library/hf4y5e3w + OutputDebugStringFormatW( L"CPropertyHdl::Initialize: Caught exception [%S]", e.what() ); + return E_FAIL; + } + } + + return S_OK; +} + +namespace { + +HRESULT GetItemData( CMetaInfoReader *pMetaInfoReader, UINT nIndex, PROPVARIANT *pVarData ) +{ + switch (nIndex) { + case 0: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagData( META_INFO_TITLE ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Title=%s.\n", pMetaInfoReader->getTagData( META_INFO_TITLE ).c_str() ); + return S_OK; + } + case 1: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagData( META_INFO_AUTHOR ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Author=%s.\n", pMetaInfoReader->getTagData( META_INFO_AUTHOR ).c_str() ); + return S_OK; + } + case 2: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagData( META_INFO_SUBJECT ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Subject=%s.\n", pMetaInfoReader->getTagData( META_INFO_SUBJECT ).c_str() ); + return S_OK; + } + case 3: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagData( META_INFO_KEYWORDS ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Keywords=%s.\n", pMetaInfoReader->getTagData( META_INFO_KEYWORDS ).c_str() ); + return S_OK; + } + case 4: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagData( META_INFO_DESCRIPTION ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Description=%s.\n", pMetaInfoReader->getTagData( META_INFO_DESCRIPTION ).c_str() ); + return S_OK; + } + case 5: { + pVarData->vt = VT_BSTR; + pVarData->bstrVal = SysAllocString( pMetaInfoReader->getTagAttribute( META_INFO_DOCUMENT_STATISTIC, META_INFO_PAGES ).c_str() ); + OutputDebugStringFormatW( L"CPropertyHdl::GetItemData: Pages=%s.\n", pMetaInfoReader->getTagAttribute( META_INFO_DOCUMENT_STATISTIC, META_INFO_PAGES ).c_str() ); + return S_OK; + } + } + + return S_FALSE; +} + +} + +void CPropertyHdl::LoadProperties( CMetaInfoReader *pMetaInfoReader ) +{ + OutputDebugStringFormatW( L"CPropertyHdl: LoadProperties\n" ); + PROPVARIANT propvarValues; + + for ( UINT i = 0; i < UINT(gPropertyTableSize); ++i ) + { + PropVariantClear( &propvarValues ); + HRESULT hr = GetItemData( pMetaInfoReader, i, &propvarValues); + if (hr == S_OK) + { + // coerce the value(s) to the appropriate type for the property key + hr = PSCoerceToCanonicalValue( g_rgPROPERTIES[i], &propvarValues ); + if (SUCCEEDED(hr)) + { + // cache the value(s) loaded + hr = m_pCache->SetValueAndState( g_rgPROPERTIES[i], &propvarValues, PSC_NORMAL ); + } + } + } +} + +// CClassFactory + + +LONG CClassFactory::s_ServerLocks = 0; + + +CClassFactory::CClassFactory( const CLSID& clsid ) : + m_RefCnt(1), + m_Clsid(clsid) +{ + InterlockedIncrement( &g_DllRefCnt ); +} + + +CClassFactory::~CClassFactory() +{ + InterlockedDecrement( &g_DllRefCnt ); +} + + +// IUnknown methods + +HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface( REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject ) +{ + *ppvObject = nullptr; + + if ( IID_IUnknown == riid || IID_IClassFactory == riid ) + { + IUnknown* pUnk = this; + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + + return E_NOINTERFACE; +} + + +ULONG STDMETHODCALLTYPE CClassFactory::AddRef() +{ + return InterlockedIncrement( &m_RefCnt ); +} + + +ULONG STDMETHODCALLTYPE CClassFactory::Release() +{ + LONG refcnt = InterlockedDecrement( &m_RefCnt ); + + if (0 == refcnt) + delete this; + + return refcnt; +} + + +// IClassFactory methods + +HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance( + IUnknown __RPC_FAR *pUnkOuter, + REFIID riid, + void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if ( pUnkOuter != nullptr ) + return CLASS_E_NOAGGREGATION; + + IUnknown* pUnk = nullptr; + + if ( CLSID_PROPERTY_HANDLER == m_Clsid ) + pUnk = static_cast<IPropertyStore*>( new CPropertyHdl() ); + + if (nullptr == pUnk) + { + return E_OUTOFMEMORY; + } + + HRESULT hr = pUnk->QueryInterface( riid, ppvObject ); + + // if QueryInterface failed the component will destroy itself + pUnk->Release(); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CClassFactory::LockServer( BOOL fLock ) +{ + if ( fLock ) + InterlockedIncrement( &s_ServerLocks ); + else + InterlockedDecrement( &s_ServerLocks ); + + return S_OK; +} + + +bool CClassFactory::IsLocked() +{ + return ( s_ServerLocks > 0 ); +} + + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + OutputDebugStringFormatW( L"DllGetClassObject.\n" ); + *ppv = nullptr; + + if ( rclsid != CLSID_PROPERTY_HANDLER ) + return CLASS_E_CLASSNOTAVAILABLE; + + if ( (riid != IID_IUnknown) && (riid != IID_IClassFactory) ) + return E_NOINTERFACE; + + IUnknown* pUnk = new CClassFactory( rclsid ); + *ppv = pUnk; + return S_OK; +} + + +STDAPI DllCanUnloadNow() +{ + OutputDebugStringFormatW( L"DllCanUnloadNow.\n" ); + if (CClassFactory::IsLocked() || g_DllRefCnt > 0) + return S_FALSE; + + return S_OK; +} + + +BOOL WINAPI DllMain( HINSTANCE hInst, ULONG /*ul_reason_for_call*/, LPVOID /*lpReserved*/ ) +{ + OutputDebugStringFormatW( L"DllMain.\n" ); + g_hModule = hInst; + return TRUE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/prophdl/propertyhdl.def b/shell/source/win32/shlxthandler/prophdl/propertyhdl.def new file mode 100644 index 000000000..22b670b3c --- /dev/null +++ b/shell/source/win32/shlxthandler/prophdl/propertyhdl.def @@ -0,0 +1,3 @@ +EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
diff --git a/shell/source/win32/shlxthandler/propsheets/document_statistic.cxx b/shell/source/win32/shlxthandler/propsheets/document_statistic.cxx new file mode 100644 index 000000000..6a27e1ab1 --- /dev/null +++ b/shell/source/win32/shlxthandler/propsheets/document_statistic.cxx @@ -0,0 +1,145 @@ +/* -*- 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 "document_statistic.hxx" +#include <utilities.hxx> +#include <metainforeader.hxx> +#include <resource.h> +#include <fileextensions.hxx> +#include <config.hxx> +#include <iso8601_converter.hxx> + +const bool READONLY = false; + +document_statistic_reader_ptr create_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor) +{ + File_Type_t file_type = get_file_type(document_name); + + if (WRITER == file_type) + return document_statistic_reader_ptr(new writer_document_statistic_reader(document_name, meta_info_accessor)); + else if (CALC == file_type) + return document_statistic_reader_ptr(new calc_document_statistic_reader(document_name, meta_info_accessor)); + else + return document_statistic_reader_ptr(new draw_impress_math_document_statistic_reader(document_name, meta_info_accessor)); +} + + +document_statistic_reader::document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor) : + document_name_(document_name), + meta_info_accessor_(meta_info_accessor) +{} + +document_statistic_reader::~document_statistic_reader() +{} + +void document_statistic_reader::read(statistic_group_list_t* group_list) +{ + group_list->clear(); + fill_description_section(meta_info_accessor_, group_list); + fill_origin_section(meta_info_accessor_, group_list); +} + +std::wstring document_statistic_reader::get_document_name() const +{ + return document_name_; +} + +void document_statistic_reader::fill_origin_section(CMetaInfoReader *meta_info_accessor, statistic_group_list_t* group_list) +{ + statistic_item_list_t il; + + il.push_back(statistic_item(GetResString(IDS_AUTHOR), meta_info_accessor->getTagData( META_INFO_AUTHOR ), READONLY)); + + il.push_back(statistic_item(GetResString(IDS_MODIFIED), + iso8601_date_to_local_date(meta_info_accessor->getTagData(META_INFO_MODIFIED )), READONLY)); + + il.push_back(statistic_item(GetResString(IDS_DOCUMENT_NUMBER), meta_info_accessor->getTagData( META_INFO_DOCUMENT_NUMBER ), READONLY)); + + il.push_back(statistic_item(GetResString(IDS_EDITING_TIME), + iso8601_duration_to_local_duration(meta_info_accessor->getTagData( META_INFO_EDITING_TIME )), READONLY)); + + group_list->push_back(statistic_group_t(GetResString(IDS_ORIGIN), il)); +} + +writer_document_statistic_reader::writer_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor) : + document_statistic_reader(document_name, meta_info_accessor) +{} + +void writer_document_statistic_reader::fill_description_section(CMetaInfoReader *meta_info_accessor, statistic_group_list_t* group_list) +{ + statistic_item_list_t il; + + il.push_back(statistic_item(GetResString(IDS_TITLE), meta_info_accessor->getTagData( META_INFO_TITLE ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_COMMENTS), meta_info_accessor->getTagData( META_INFO_DESCRIPTION ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_SUBJECT), meta_info_accessor->getTagData( META_INFO_SUBJECT ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_KEYWORDS), meta_info_accessor->getTagData(META_INFO_KEYWORDS ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_PAGES), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_PAGES) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_TABLES), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_TABLES) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_GRAPHICS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_DRAWS) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_OLE_OBJECTS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_OBJECTS) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_PARAGRAPHS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_PARAGRAPHS) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_WORDS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_WORDS) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_CHARACTERS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_CHARACTERS) , READONLY)); + + group_list->push_back(statistic_group_t(GetResString(IDS_DESCRIPTION), il)); +} + +calc_document_statistic_reader::calc_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor) : + document_statistic_reader(document_name, meta_info_accessor) +{} + +void calc_document_statistic_reader::fill_description_section( + CMetaInfoReader *meta_info_accessor,statistic_group_list_t* group_list) +{ + statistic_item_list_t il; + + il.push_back(statistic_item(GetResString(IDS_TITLE), meta_info_accessor->getTagData( META_INFO_TITLE ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_COMMENTS), meta_info_accessor->getTagData( META_INFO_DESCRIPTION ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_SUBJECT), meta_info_accessor->getTagData( META_INFO_SUBJECT ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_KEYWORDS), meta_info_accessor->getTagData(META_INFO_KEYWORDS ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_TABLES), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_TABLES) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_CELLS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_CELLS) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_OLE_OBJECTS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_OBJECTS) , READONLY)); + + group_list->push_back(statistic_group_t(GetResString(IDS_DESCRIPTION), il)); +} + +draw_impress_math_document_statistic_reader::draw_impress_math_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor) : + document_statistic_reader(document_name, meta_info_accessor) +{} + +void draw_impress_math_document_statistic_reader::fill_description_section( + CMetaInfoReader *meta_info_accessor, statistic_group_list_t* group_list) +{ + statistic_item_list_t il; + + il.push_back(statistic_item(GetResString(IDS_TITLE), meta_info_accessor->getTagData( META_INFO_TITLE ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_COMMENTS), meta_info_accessor->getTagData( META_INFO_DESCRIPTION ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_SUBJECT), meta_info_accessor->getTagData( META_INFO_SUBJECT ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_KEYWORDS), meta_info_accessor->getTagData(META_INFO_KEYWORDS ), READONLY)); + il.push_back(statistic_item(GetResString(IDS_PAGES), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_PAGES) , READONLY)); + il.push_back(statistic_item(GetResString(IDS_OLE_OBJECTS), meta_info_accessor->getTagAttribute( META_INFO_DOCUMENT_STATISTIC,META_INFO_OBJECTS) , READONLY)); + + group_list->push_back(statistic_group_t(GetResString(IDS_DESCRIPTION), il)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/propsheets/document_statistic.hxx b/shell/source/win32/shlxthandler/propsheets/document_statistic.hxx new file mode 100644 index 000000000..e6f0dc5dc --- /dev/null +++ b/shell/source/win32/shlxthandler/propsheets/document_statistic.hxx @@ -0,0 +1,122 @@ +/* -*- 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_SHELL_SOURCE_WIN32_SHLXTHANDLER_PROPSHEETS_DOCUMENT_STATISTIC_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_PROPSHEETS_DOCUMENT_STATISTIC_HXX + +#include <utility> +#include <string> +#include <memory> +#include <vector> +#include <metainforeader.hxx> + + +struct statistic_item +{ + statistic_item(); + + statistic_item( + const std::wstring& title, + const std::wstring& value, + bool editable) : + title_(title), + value_(value), + editable_(editable) + {} + + std::wstring title_; + std::wstring value_; + bool editable_; +}; + + +typedef std::vector<statistic_item> statistic_item_list_t; +typedef std::pair<std::wstring, statistic_item_list_t> statistic_group_t; +typedef std::vector<statistic_group_t> statistic_group_list_t; + + +class document_statistic_reader; +typedef std::unique_ptr<document_statistic_reader> document_statistic_reader_ptr; + +document_statistic_reader_ptr create_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); + + +class document_statistic_reader +{ +public: + virtual ~document_statistic_reader(); + + void read(statistic_group_list_t* group_list); + + std::wstring get_document_name() const; + +protected: + document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); + + virtual void fill_description_section(CMetaInfoReader *meta_info_accessor,statistic_group_list_t* group_list) = 0; + + virtual void fill_origin_section( CMetaInfoReader *meta_info_accessor,statistic_group_list_t* group_list); + +private: + std::wstring document_name_; + CMetaInfoReader* meta_info_accessor_; + + friend document_statistic_reader_ptr create_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); +}; + + +class writer_document_statistic_reader : public document_statistic_reader +{ +protected: + writer_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); + + virtual void fill_description_section(CMetaInfoReader *meta_info_accessor, statistic_group_list_t* group_list) override; + + friend document_statistic_reader_ptr create_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); +}; + + +class calc_document_statistic_reader : public document_statistic_reader +{ +protected: + calc_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); + + virtual void fill_description_section( CMetaInfoReader *meta_info_accessor,statistic_group_list_t* group_list) override; + + friend document_statistic_reader_ptr create_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); +}; + + +class draw_impress_math_document_statistic_reader : public document_statistic_reader +{ +protected: + draw_impress_math_document_statistic_reader(const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); + + virtual void fill_description_section(CMetaInfoReader *meta_info_accessor, statistic_group_list_t* group_list) override; + + friend document_statistic_reader_ptr create_document_statistic_reader( + const std::wstring& document_name, CMetaInfoReader* meta_info_accessor); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/propsheets/listviewbuilder.cxx b/shell/source/win32/shlxthandler/propsheets/listviewbuilder.cxx new file mode 100644 index 000000000..5f0705c74 --- /dev/null +++ b/shell/source/win32/shlxthandler/propsheets/listviewbuilder.cxx @@ -0,0 +1,175 @@ +/* -*- 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 "listviewbuilder.hxx" +#include "document_statistic.hxx" +#include <utilities.hxx> +#include <config.hxx> +#include <global.hxx> + +#include <commctrl.h> +#include <resource.h> + +// Unicode-only defines to break dependence on UNICODE define +#if !defined ListView_InsertColumnW +#define ListView_InsertColumnW(hwnd, iCol, pcol) \ + static_cast<int>(SNDMSG((hwnd), LVM_INSERTCOLUMNW, WPARAM(int(iCol)), reinterpret_cast<LPARAM>(pcol))) +#endif + +#if !defined ListView_InsertItemW +#define ListView_InsertItemW(hwnd, pitem) \ + static_cast<int>(SNDMSG((hwnd), LVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(pitem))) +#endif + +#if !defined ListView_SetItemW +#define ListView_SetItemW(hwnd, pitem) \ + static_cast<bool>(SNDMSG((hwnd), LVM_SETITEMW, 0, reinterpret_cast<LPARAM>(pitem))) +#endif + + +list_view_builder_ptr create_list_view_builder( + HWND hwnd_lv, const std::wstring& col1, const std::wstring& col2) +{ + return list_view_builder_ptr(new list_view_builder(hwnd_lv, col1, col2)); +} + + +list_view_builder::list_view_builder( + HWND hwnd_list_view, + const std::wstring& column1_title, + const std::wstring& column2_title) : + row_index_(-1), + hwnd_list_view_(hwnd_list_view), + column1_title_(column1_title), + column2_title_(column2_title), + group_count_(-1), + row_count_(0) +{ +} + + +list_view_builder::~list_view_builder() +{ +} + + +void list_view_builder::build(statistic_group_list_t& gl) +{ + setup_list_view(); + + for (const auto& group : gl) + { + if (!group.second.empty()) + insert_group(group.first); + + for (const auto& item : group.second) + insert_item(item.title_, item.value_, item.editable_); + } +} + + +void list_view_builder::setup_list_view() +{ + HIMAGELIST h_ils = ImageList_Create(16,15,ILC_MASK, 7, 0); + HBITMAP h_bmp = LoadBitmapW(GetCurrentModuleHandle(), MAKEINTRESOURCEW(IDB_PROPERTY_IMAGES)); + ImageList_AddMasked(h_ils, h_bmp, RGB(255, 0, 255)); + + (void) ListView_SetImageList(hwnd_list_view_, h_ils, LVSIL_SMALL); + + std::wstring header = GetResString(IDS_PROPERTY); + + LVCOLUMNW lvc; + lvc.mask = LVCF_FMT | + LVCF_WIDTH | + LVCF_TEXT | + LVCF_SUBITEM; + + lvc.iSubItem = 0; + lvc.pszText = const_cast<wchar_t*>(header.c_str()); + lvc.cx = 120; + lvc.fmt = LVCFMT_LEFT; + + ListView_InsertColumnW(hwnd_list_view_, 0, &lvc); + lvc.iSubItem = 1; + header = GetResString(IDS_PROPERTY_VALUE); + lvc.pszText = const_cast<wchar_t*>(header.c_str()); + ListView_InsertColumnW(hwnd_list_view_, 1, &lvc); + ListView_EnableGroupView(hwnd_list_view_, TRUE); +} + + +void list_view_builder::insert_group(const std::wstring& name) +{ + LVGROUP lvg; + + ZeroMemory(&lvg, sizeof(lvg)); + + lvg.cbSize = sizeof(lvg); + lvg.mask = LVGF_HEADER | LVGF_STATE | LVGF_GROUPID; + lvg.pszHeader = const_cast<wchar_t*>(name.c_str()); + lvg.cchHeader = static_cast<int>(name.size() + 1); + lvg.iGroupId = ++group_count_; + lvg.state = LVGS_NORMAL; + lvg.uAlign = LVGA_HEADER_CENTER; + + ListView_InsertGroup(get_list_view(), row_count_++, &lvg); +} + + +void list_view_builder::insert_item(const std::wstring& title, const std::wstring& value, bool is_editable) +{ + LVITEMW lvi; + + lvi.iItem = ++row_index_; + lvi.iSubItem = 0; + lvi.mask = LVIF_TEXT | LVIF_GROUPID; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = const_cast<wchar_t*>(title.c_str()); + lvi.iGroupId = group_count_; + + if (title.length() > 0) + { + lvi.mask |= LVIF_IMAGE; + + if (is_editable) + lvi.iImage = 4; + else + lvi.iImage = 3; + } + + ListView_InsertItemW(get_list_view(), &lvi); + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + lvi.pszText = const_cast<wchar_t*>(value.c_str()); + + ListView_SetItemW(get_list_view(), &lvi); + + row_count_++; +} + + +HWND list_view_builder::get_list_view() const +{ + return hwnd_list_view_; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/propsheets/listviewbuilder.hxx b/shell/source/win32/shlxthandler/propsheets/listviewbuilder.hxx new file mode 100644 index 000000000..689da439d --- /dev/null +++ b/shell/source/win32/shlxthandler/propsheets/listviewbuilder.hxx @@ -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_SHELL_SOURCE_WIN32_SHLXTHANDLER_PROPSHEETS_LISTVIEWBUILDER_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SHLXTHANDLER_PROPSHEETS_LISTVIEWBUILDER_HXX + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <string> +#include <memory> +#include "document_statistic.hxx" + + +class list_view_builder; +typedef std::unique_ptr<list_view_builder> list_view_builder_ptr; + +// factory method for list_view_builder +list_view_builder_ptr create_list_view_builder( + HWND hwnd_lv, const std::wstring& col1, const std::wstring& col2); + + +class list_view_builder +{ +public: + virtual ~list_view_builder(); + + void build(statistic_group_list_t& gl); + +protected: + list_view_builder( + HWND hwnd_list_view, + const std::wstring& column1_title, + const std::wstring& column2_title); + + virtual void setup_list_view(); + virtual void insert_group(const std::wstring& title); + virtual void insert_item(const std::wstring& title, const std::wstring& value, bool is_editable); + + HWND get_list_view() const; +private: + int row_index_; + HWND hwnd_list_view_; + std::wstring column1_title_; + std::wstring column2_title_; + int group_count_; + int row_count_; + + friend list_view_builder_ptr create_list_view_builder(HWND hwnd_lv, const std::wstring& col1, const std::wstring& col2); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/propsheets/propsheets.cxx b/shell/source/win32/shlxthandler/propsheets/propsheets.cxx new file mode 100644 index 000000000..48a125d58 --- /dev/null +++ b/shell/source/win32/shlxthandler/propsheets/propsheets.cxx @@ -0,0 +1,308 @@ +/* -*- 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.hxx> +#include <global.hxx> + +#include <propsheets.hxx> +#include <utilities.hxx> +#include <resource.h> +#include "listviewbuilder.hxx" + +#include <shellapi.h> + +#include <sal/macros.h> + +#include <memory> +#include <string> +#include <vector> +#include <utility> +#include <strsafe.h> + + +/*--------------------------------------------- + INFO - INFO - INFO - INFO - INFO - INFO + + See MSDN "Using Windows XP Visual Styles" + for hints how to enable the new common + control library for our property sheet. + + INFO - INFO - INFO - INFO - INFO - INFO +----------------------------------------------*/ + + +CPropertySheet::CPropertySheet(LONG RefCnt) : + m_RefCnt(RefCnt) +{ + OutputDebugStringFormatW(L"CPropertySheet::CTor [%d], [%ld]", m_RefCnt, g_DllRefCnt ); + InterlockedIncrement(&g_DllRefCnt); +} + + +CPropertySheet::~CPropertySheet() +{ + OutputDebugStringFormatW(L"CPropertySheet::DTor [%d], [%ld]", m_RefCnt, g_DllRefCnt ); + InterlockedDecrement(&g_DllRefCnt); +} + + +// IUnknown methods + + +HRESULT STDMETHODCALLTYPE CPropertySheet::QueryInterface( + REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + + IUnknown* pUnk = nullptr; + if (IID_IUnknown == riid || IID_IShellExtInit == riid) + { + pUnk = static_cast<IShellExtInit*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + else if (IID_IShellPropSheetExt == riid) + { + pUnk = static_cast<IShellPropSheetExt*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + + return E_NOINTERFACE; +} + + +ULONG STDMETHODCALLTYPE CPropertySheet::AddRef() +{ + OutputDebugStringFormatW(L"CPropertySheet::AddRef [%d]", m_RefCnt ); + return InterlockedIncrement(&m_RefCnt); +} + + +ULONG STDMETHODCALLTYPE CPropertySheet::Release() +{ + OutputDebugStringFormatW(L"CPropertySheet::Release [%d]", m_RefCnt ); + LONG refcnt = InterlockedDecrement(&m_RefCnt); + + if (0 == refcnt) + delete this; + + return refcnt; +} + + +// IShellExtInit + + +HRESULT STDMETHODCALLTYPE CPropertySheet::Initialize( + LPCITEMIDLIST /*pidlFolder*/, IDataObject * lpdobj, HKEY /*hkeyProgID*/) +{ + InitCommonControls(); + + STGMEDIUM medium; + FORMATETC fe = {CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + + HRESULT hr = lpdobj->GetData(&fe, &medium); + + // save the file name + if (SUCCEEDED(hr) && + (1 == DragQueryFileW( + static_cast<HDROP>(medium.hGlobal), + 0xFFFFFFFF, + nullptr, + 0))) + { + UINT size = DragQueryFileW( static_cast<HDROP>(medium.hGlobal), 0, nullptr, 0 ); + if ( size != 0 ) + { + auto buffer = std::make_unique<WCHAR[]>( size + 1 ); + UINT result_size = DragQueryFileW( static_cast<HDROP>(medium.hGlobal), + 0, buffer.get(), size + 1 ); + if ( result_size != 0 ) + { + std::wstring fname = getShortPathName( buffer.get() ); + ZeroMemory( m_szFileName, sizeof( m_szFileName ) ); + wcsncpy( m_szFileName, fname.c_str(), ( SAL_N_ELEMENTS( m_szFileName ) - 1 ) ); + hr = S_OK; + } + else + hr = E_INVALIDARG; + } + else + hr = E_INVALIDARG; + } + else + hr = E_INVALIDARG; + + ReleaseStgMedium(&medium); + + return hr; +} + + +// IShellPropSheetExt + + +HRESULT STDMETHODCALLTYPE CPropertySheet::AddPages(LPFNSVADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) +{ + std::wstring proppage_header; + + PROPSHEETPAGEW psp; + ZeroMemory(&psp, sizeof(psp)); + + // add the summary property page + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_DEFAULT | PSP_USETITLE | PSP_USECALLBACK; + psp.hInstance = GetCurrentModuleHandle(); + psp.lParam = reinterpret_cast<LPARAM>(this); + psp.pfnCallback = reinterpret_cast<LPFNPSPCALLBACKW>(CPropertySheet::PropPageSummaryCallback); + + HPROPSHEETPAGE hPage = nullptr; + + // add the statistics property page + proppage_header = GetResString(IDS_PROPPAGE_STATISTICS_TITLE); + + psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROPPAGE_STATISTICS); + psp.pszTitle = proppage_header.c_str(); + psp.pfnDlgProc = reinterpret_cast<DLGPROC>(CPropertySheet::PropPageStatisticsProc); + + hPage = CreatePropertySheetPageW(&psp); + + if (hPage) + { + if (lpfnAddPage(hPage, lParam)) + AddRef(); + else + DestroyPropertySheetPage(hPage); + } + + // always return success else no property sheet will be displayed at all + return NOERROR; +} + + +HRESULT STDMETHODCALLTYPE CPropertySheet::ReplacePage( + EXPPS /*uPageID*/, LPFNSVADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/) +{ + return E_NOTIMPL; +} + + +UINT CALLBACK CPropertySheet::PropPageSummaryCallback( + HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp) +{ + CPropertySheet* pImpl = + reinterpret_cast<CPropertySheet*>(ppsp->lParam); + + // release this instance, acquired in the AddPages method + if (PSPCB_RELEASE == uMsg) + { + pImpl->Release(); + } + + return TRUE; +} + + +bool CALLBACK CPropertySheet::PropPageSummaryProc(HWND hwnd, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) +{ + switch (uiMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE psp = reinterpret_cast<LPPROPSHEETPAGE>(lParam); + CPropertySheet* pImpl = reinterpret_cast<CPropertySheet*>(psp->lParam); + pImpl->InitPropPageSummary(hwnd, psp); + return true; + } + } + + return false; +} + + +BOOL CALLBACK CPropertySheet::PropPageStatisticsProc(HWND hwnd, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) +{ + switch (uiMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE psp = reinterpret_cast<LPPROPSHEETPAGE>(lParam); + CPropertySheet* pImpl = reinterpret_cast<CPropertySheet*>(psp->lParam); + pImpl->InitPropPageStatistics(hwnd, psp); + return TRUE; + } + } + + return FALSE; +} + +void CPropertySheet::InitPropPageSummary(HWND hwnd, LPPROPSHEETPAGE /*lppsp*/) +{ + try + { + CMetaInfoReader metaInfo(m_szFileName); + + SetWindowTextW(GetDlgItem(hwnd,IDC_TITLE), metaInfo.getTagData( META_INFO_TITLE ).c_str() ); + SetWindowTextW(GetDlgItem(hwnd,IDC_AUTHOR), metaInfo.getTagData( META_INFO_AUTHOR ).c_str() ); + SetWindowTextW(GetDlgItem(hwnd,IDC_SUBJECT), metaInfo.getTagData( META_INFO_SUBJECT ).c_str() ); + SetWindowTextW(GetDlgItem(hwnd,IDC_KEYWORDS), metaInfo.getTagData( META_INFO_KEYWORDS ).c_str() ); + + // comments read from meta.xml use "\n" for return, but this will not displayable in Edit control, add + // "\r" before "\n" to form "\r\n" in order to display return in Edit control. + std::wstring tempStr = metaInfo.getTagData( META_INFO_DESCRIPTION ); + std::wstring::size_type itor = tempStr.find ( L"\n" , 0 ); + while (itor != std::wstring::npos) + { + tempStr.insert(itor, L"\r"); + itor = tempStr.find(L"\n", itor + 2); + } + SetWindowTextW(GetDlgItem(hwnd,IDC_COMMENTS), tempStr.c_str()); + } + catch (const std::exception&) + { + } +} + + +void CPropertySheet::InitPropPageStatistics(HWND hwnd, LPPROPSHEETPAGE /*lppsp*/) +{ + try + { + CMetaInfoReader metaInfo(m_szFileName); + + document_statistic_reader_ptr doc_stat_reader = create_document_statistic_reader(m_szFileName, &metaInfo); + + statistic_group_list_t sgl; + doc_stat_reader->read(&sgl); + + list_view_builder_ptr lv_builder = create_list_view_builder( + GetDlgItem(hwnd, IDC_STATISTICSLIST), + GetResString(IDS_PROPERTY), + GetResString(IDS_PROPERTY_VALUE)); + + lv_builder->build(sgl); + } + catch (const std::exception&) + { + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/res/ctrylnglist.txt b/shell/source/win32/shlxthandler/res/ctrylnglist.txt new file mode 100644 index 000000000..ec2196e4d --- /dev/null +++ b/shell/source/win32/shlxthandler/res/ctrylnglist.txt @@ -0,0 +1,29 @@ +1 LANG_ENGLISH SUBLANG_ENGLISH_US +3 LANG_PORTUGUESE SUBLANG_PORTUGUESE +4 LANG_GERMAN SUBLANG_GERMAN +7 LANG_RUSSIAN SUBLANG_DEFAULT +30 LANG_GREEK SUBLANG_DEFAULT +31 LANG_DUTCH SUBLANG_DUTCH +33 LANG_FRENCH SUBLANG_FRENCH +34 LANG_SPANISH SUBLANG_SPANISH +35 LANG_FINNISH SUBLANG_DEFAULT +36 LANG_HUNGARIAN SUBLANG_DEFAULT +39 LANG_ITALIAN SUBLANG_ITALIAN +42 LANG_CZECH SUBLANG_DEFAULT +43 LANG_SLOVAK SUBLANG_DEFAULT +44 LANG_ENGLISH SUBLANG_ENGLISH_UK +45 LANG_DANISH SUBLANG_DEFAULT +46 LANG_SWEDISH SUBLANG_SWEDISH +47 LANG_NORWEGIAN SUBLANG_NORWEGIAN_BOKMAL +48 LANG_POLISH SUBLANG_DEFAULT +49 LANG_GERMAN SUBLANG_DEFAULT +55 LANG_PORTUGUESE SUBLANG_PORTUGUESE_BRAZILIAN +81 LANG_JAPANESE SUBLANG_DEFAULT +82 LANG_KOREAN SUBLANG_KOREAN +86 LANG_CHINESE SUBLANG_CHINESE_SIMPLIFIED +88 LANG_CHINESE SUBLANG_CHINESE_TRADITIONAL +90 LANG_TURKISH SUBLANG_DEFAULT +96 LANG_ARABIC SUBLANG_DEFAULT +97 LANG_HEBREW SUBLANG_DEFAULT +37 LANG_CATALAN SUBLANG_DEFAULT +66 LANG_THAI SUBLANG_DEFAULT diff --git a/shell/source/win32/shlxthandler/res/prop_img.bmp b/shell/source/win32/shlxthandler/res/prop_img.bmp Binary files differnew file mode 100644 index 000000000..1849a8bb6 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/prop_img.bmp diff --git a/shell/source/win32/shlxthandler/res/rcfooter.txt b/shell/source/win32/shlxthandler/res/rcfooter.txt new file mode 100644 index 000000000..ce333b32a --- /dev/null +++ b/shell/source/win32/shlxthandler/res/rcfooter.txt @@ -0,0 +1,4 @@ + + +IDP_SIGNET RCDATA "signet.png" + diff --git a/shell/source/win32/shlxthandler/res/rcheader.txt b/shell/source/win32/shlxthandler/res/rcheader.txt new file mode 100644 index 000000000..42fb81ee5 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/rcheader.txt @@ -0,0 +1,10 @@ +#include <winresrc.h> +#define LB_ADDSTRING (WM_USER+1) +#define CB_ADDSTRING (WM_USER+3) +#define IDC_STATIC (-1) + +#include "resource.h" + +MANIFEST_RESOURCE_ID RT_MANIFEST "shlxthdl.manifest" + +IDB_PROPERTY_IMAGES BITMAP "prop_img.bmp" diff --git a/shell/source/win32/shlxthandler/res/rctmpl.txt b/shell/source/win32/shlxthandler/res/rctmpl.txt new file mode 100644 index 000000000..c48d7f61f --- /dev/null +++ b/shell/source/win32/shlxthandler/res/rctmpl.txt @@ -0,0 +1,88 @@ +// +// This file is part of the LibreOffice project. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If 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 . +// + +// Dialog + +IDD_PROPPAGE_SUMMARY DIALOGEX 0, 0, 222, 211 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT %TITLE_COLON% ,IDC_STATIC,7,14,39,10 + LTEXT %AUTHOR_COLON% ,IDC_STATIC,7,31,47,10 + LTEXT %SUBJECT_COLON% ,IDC_STATIC,7,47,39,10 + LTEXT %KEYWORDS_COLON% ,IDC_STATIC,7,94,49,10 + EDITTEXT IDC_TITLE,63,12,154,12,ES_AUTOHSCROLL | ES_READONLY | + NOT WS_BORDER,WS_EX_CLIENTEDGE + EDITTEXT IDC_AUTHOR,63,29,154,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER, + WS_EX_CLIENTEDGE + EDITTEXT IDC_SUBJECT,63,46,154,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER, + WS_EX_CLIENTEDGE + EDITTEXT IDC_KEYWORDS,64,93,154,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER, + WS_EX_CLIENTEDGE + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,7,75,211,1 + EDITTEXT IDC_COMMENTS,64,111,154,83,ES_MULTILINE | ES_LEFT | ES_READONLY | ES_WANTRETURN | + NOT WS_BORDER | WS_VSCROLL,WS_EX_CLIENTEDGE + LTEXT %COMMENTS_COLON% ,IDC_STATIC,7,112,49,10 +END + +IDD_PROPPAGE_STATISTICS DIALOG DISCARDABLE 0, 0, 222, 215 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_STATISTICSLIST,"SysListView32",LVS_REPORT | + WS_BORDER | WS_TABSTOP,6,7,214,186 +END + +// String Table + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TITLE %TITLE% + IDS_TITLE_COLON %TITLE_COLON% + IDS_SUBJECT %SUBJECT% + IDS_SUBJECT_COLON %SUBJECT_COLON% + IDS_AUTHOR %AUTHOR% + IDS_AUTHOR_COLON %AUTHOR_COLON% + IDS_KEYWORDS %KEYWORDS% + IDS_COMMENTS %COMMENTS% + IDS_COMMENTS_COLON %COMMENTS_COLON% + IDS_PAGES %PAGES% + IDS_TABLES %TABLES% + IDS_GRAPHICS %GRAPHICS% + IDS_OBJECTS %OBJECTS% + IDS_OLE_OBJECTS %OLE_OBJECTS% + IDS_PARAGRAPHS %PARAGRAPHS% + IDS_WORDS %WORDS% + IDS_CHARACTERS %CHARACTERS% + IDS_ROWS %ROWS% + IDS_ORIGIN %ORIGIN% + IDS_VERSION %VERSION% + IDS_SHEETS %SHEETS% + IDS_CELLS %CELLS% + IDS_MODIFIED %MODIFIED% + IDS_MODIFIED_COLON %MODIFIED_COLON% + IDS_DOCUMENT_NUMBER %DOCUMENT_NUMBER% + IDS_EDITING_TIME %EDITING_TIME% + IDS_PROPPAGE_STATISTICS_TITLE %STATISTICS_TITLE% + IDS_PROPPAGE_SUMMARY_TITLE %SUMMARY_TITLE% + IDS_PROPERTY %PROPERTY% + IDS_PROPERTY_VALUE %PROPERTY_VALUE% + IDS_DESCRIPTION %DESCRIPTION% + IDS_SIZE_COLON %SIZE_COLON% + IDS_TYPE_COLON %TYPE_COLON% +END diff --git a/shell/source/win32/shlxthandler/res/shlxthdl.manifest b/shell/source/win32/shlxthandler/res/shlxthdl.manifest new file mode 100644 index 000000000..36131a343 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/shlxthdl.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity version="1.0.0.0" processorArchitecture="x86" name="LibreOffice.shlxthdl" type="win32" /> +<description>LibreOffice Shell Extension</description> +<dependency> +<dependentAssembly> +<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" language="*"/> +</dependentAssembly> +</dependency> +</assembly> diff --git a/shell/source/win32/shlxthandler/res/shlxthdl.rc b/shell/source/win32/shlxthandler/res/shlxthdl.rc new file mode 100644 index 000000000..4c3433738 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/shlxthdl.rc @@ -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 "shlxthdl_impl.rc" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/res/shlxthdl.ulf b/shell/source/win32/shlxthandler/res/shlxthdl.ulf new file mode 100644 index 000000000..57a286605 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/shlxthdl.ulf @@ -0,0 +1,148 @@ +[%TITLE%] +en-US = "Title" + +[%TITLE_COLON%] +en-US = "Title:" + +[%SUBJECT%] +en-US = "Subject" + +[%SUBJECT_COLON%] +en-US = "Subject:" + +[%AUTHOR%] +en-US = "Author" + +[%AUTHOR_COLON%] +en-US = "Author:" + +[%KEYWORDS%] +en-US = "Keywords" + +[%KEYWORDS_COLON%] +en-US = "Keywords:" + +[%COMMENTS%] +en-US = "Comments" + +[%COMMENTS_COLON%] +en-US = "Comments:" + +[%PAGES%] +en-US = "Pages" + +[%TABLES%] +en-US = "Tables" + +[%GRAPHICS%] +en-US = "Images" + +[%OBJECTS%] +en-US = "Objects" + +[%OLE_OBJECTS%] +en-US = "OLE Objects" + +[%PARAGRAPHS%] +en-US = "Paragraphs" + +[%WORDS%] +en-US = "Words" + +[%CHARACTERS%] +en-US = "Characters" + +[%ROWS%] +en-US = "Lines" + +[%ORIGIN%] +en-US = "Origin" + +[%VERSION%] +en-US = "Version" + +[%SHEETS%] +en-US = "Sheets" + +[%CELLS%] +en-US = "Cells" + +[%STATISTICS_TITLE%] +en-US = "Document Statistics" + +[%SUMMARY_TITLE%] +en-US = "Summary" + +[%PROPERTY%] +en-US = "Property" + +[%PROPERTY_VALUE%] +en-US = "Value" + +[%MODIFIED%] +en-US = "Modified" + +[%MODIFIED_COLON%] +en-US = "Modified:" + +[%DOCUMENT_NUMBER%] +en-US = "Revision number" + +[%DOCUMENT_NUMBER_COLON%] +en-US = "Revision number:" + +[%EDITING_TIME%] +en-US = "Total editing time" + +[%EDITING_TIME_COLON%] +en-US = "Total editing time:" + +[%DESCRIPTION%] +en-US = "Description" + +[%DESCRIPTION_COLON%] +en-US = "Description:" + +[%SIZE_COLON%] +en-US = "Size:" + +[%TYPE_COLON%] +en-US = "Type:" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell/source/win32/shlxthandler/res/signet.png b/shell/source/win32/shlxthandler/res/signet.png Binary files differnew file mode 100644 index 000000000..bccaaff81 --- /dev/null +++ b/shell/source/win32/shlxthandler/res/signet.png diff --git a/shell/source/win32/shlxthandler/shlxthdl.cxx b/shell/source/win32/shlxthandler/shlxthdl.cxx new file mode 100644 index 000000000..9a5b8a379 --- /dev/null +++ b/shell/source/win32/shlxthandler/shlxthdl.cxx @@ -0,0 +1,409 @@ +/* -*- 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.hxx> +#include <global.hxx> +#include <shlxthdl.hxx> +#include "classfactory.hxx" +#include <registry.hxx> +#include <fileextensions.hxx> +#include <utilities.hxx> + +#include <string> +#include <shlobj.h> + +#include <olectl.h> // declarations of DllRegisterServer/DllUnregisterServer + +// Module global + +LONG g_DllRefCnt = 0; +HINSTANCE g_hModule = nullptr; + +namespace /* private */ +{ + const wchar_t* const GUID_PLACEHOLDER = L"{GUID}"; + const wchar_t* const EXTENSION_PLACEHOLDER = L"{EXT}"; + const wchar_t* const FORWARDKEY_PLACEHOLDER = L"{FWDKEY}"; + + const wchar_t* const CLSID_ENTRY = L"CLSID\\{GUID}\\InProcServer32"; + const wchar_t* const SHELLEX_IID_ENTRY = L"{EXT}\\shellex\\{GUID}"; + const wchar_t* const SHELLEX_ENTRY = L"{EXT}\\shellex"; + const wchar_t* const FORWARD_PROPSHEET_MYPROPSHEET_ENTRY = L"{FWDKEY}\\shellex\\PropertySheetHandlers\\MyPropSheet1"; + const wchar_t* const FORWARD_PROPSHEET_ENTRY = L"{FWDKEY}\\shellex\\PropertySheetHandlers"; + const wchar_t* const FORWARD_SHELLEX_ENTRY = L"{FWDKEY}\\shellex"; + + const wchar_t* const SHELL_EXTENSION_APPROVED_KEY_NAME = L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"; + + + // "String Placeholder" -> + // "String Replacement" + + void SubstitutePlaceholder(std::wstring& String, const std::wstring& Placeholder, const std::wstring& Replacement) + { + std::wstring::size_type idx = String.find(Placeholder); + std::wstring::size_type len = Placeholder.length(); + + while (std::wstring::npos != idx) + { + String.replace(idx, len, Replacement); + idx = String.find(Placeholder); + } + } + + /* Make the registry entry + HKCR\CLSID\{GUID} + InProcServer32 = Path\shlxthdl.dll + ThreadingModel = Apartment + */ + HRESULT RegisterComComponent(const wchar_t* FilePath, const CLSID& Guid) + { + std::wstring ClsidEntry = CLSID_ENTRY; + SubstitutePlaceholder(ClsidEntry, GUID_PLACEHOLDER, ClsidToString(Guid)); + + if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), L"", FilePath)) + return E_FAIL; + + if (!SetRegistryKey(HKEY_CLASSES_ROOT, ClsidEntry.c_str(), L"ThreadingModel", L"Apartment")) + return E_FAIL; + + return S_OK; + } + + HRESULT UnregisterComComponent(const CLSID& Guid) + { + std::wstring tmp = L"CLSID\\"; + tmp += ClsidToString(Guid); + return DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()) ? S_OK : E_FAIL; + } + + HRESULT RegisterColumnHandler(const wchar_t* ModuleFileName) + { + if (FAILED(RegisterComComponent(ModuleFileName, CLSID_COLUMN_HANDLER))) + return E_FAIL; + + std::wstring tmp = L"Folder\\shellex\\ColumnHandlers\\"; + tmp += ClsidToString(CLSID_COLUMN_HANDLER); + + return SetRegistryKey( + HKEY_CLASSES_ROOT, + tmp.c_str(), + L"", + COLUMN_HANDLER_DESCRIPTIVE_NAME) ? S_OK : E_FAIL; + } + + HRESULT UnregisterColumnHandler() + { + std::wstring tmp = L"Folder\\shellex\\ColumnHandlers\\"; + tmp += ClsidToString(CLSID_COLUMN_HANDLER); + + if (!DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str())) + return E_FAIL; + + return UnregisterComComponent(CLSID_COLUMN_HANDLER); + } + + HRESULT RegisterInfotipHandler(const wchar_t* ModuleFileName) + { + if (FAILED(RegisterComComponent(ModuleFileName, CLSID_INFOTIP_HANDLER))) + return E_FAIL; + + std::wstring iid = ClsidToString(IID_IQueryInfo); + std::wstring tmp; + + for(size_t i = 0; i < OOFileExtensionTableSize; i++) + { + tmp = SHELLEX_IID_ENTRY; + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid); + + if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), L"", ClsidToString(CLSID_INFOTIP_HANDLER).c_str())) + return E_FAIL; + } + return S_OK; + } + + HRESULT UnregisterInfotipHandler() + { + std::wstring iid = ClsidToString(IID_IQueryInfo); + std::wstring tmp; + + for (size_t i = 0; i < OOFileExtensionTableSize; i++) + { + tmp = SHELLEX_IID_ENTRY; + + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid); + + DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()); + + // if there are no further subkey below .ext\\shellex + // delete the whole subkey + tmp = SHELLEX_ENTRY; + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + + bool HasSubKeys = true; + if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys) + DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()); + } + return UnregisterComComponent(CLSID_INFOTIP_HANDLER); + } + + HRESULT RegisterPropSheetHandler(const wchar_t* ModuleFileName) + { + std::wstring FwdKeyEntry; + + if (FAILED(RegisterComComponent(ModuleFileName, CLSID_PROPERTYSHEET_HANDLER))) + return E_FAIL; + + for (size_t i = 0; i < OOFileExtensionTableSize; i++) + { + FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY; + SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey); + + if (!SetRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), L"", ClsidToString(CLSID_PROPERTYSHEET_HANDLER).c_str())) + return E_FAIL; + } + return S_OK; + } + + HRESULT UnregisterPropSheetHandler() + { + std::wstring FwdKeyEntry; + + for (size_t i = 0; i < OOFileExtensionTableSize; i++) + { + FwdKeyEntry = FORWARD_PROPSHEET_MYPROPSHEET_ENTRY; + SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey); + + DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str()); + + FwdKeyEntry = FORWARD_PROPSHEET_ENTRY; + SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey); + + bool HasSubKeys = true; + if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys) + DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str()); + + FwdKeyEntry = FORWARD_SHELLEX_ENTRY; + SubstitutePlaceholder(FwdKeyEntry, FORWARDKEY_PLACEHOLDER, OOFileExtensionTable[i].RegistryForwardKey); + + HasSubKeys = true; + if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str(), HasSubKeys) && !HasSubKeys) + DeleteRegistryKey(HKEY_CLASSES_ROOT, FwdKeyEntry.c_str()); + } + + return UnregisterComComponent(CLSID_PROPERTYSHEET_HANDLER); + } + + HRESULT RegisterThumbviewerHandler(const wchar_t* ModuleFileName) + { + if (FAILED(RegisterComComponent(ModuleFileName, CLSID_THUMBVIEWER_HANDLER))) + return E_FAIL; + + std::wstring iid = ClsidToString(IID_IExtractImage); + std::wstring tmp; + + for(size_t i = 0; i < OOFileExtensionTableSize; i++) + { + tmp = SHELLEX_IID_ENTRY; + + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid); + + if (!SetRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), L"", ClsidToString(CLSID_THUMBVIEWER_HANDLER).c_str())) + return E_FAIL; + } + return S_OK; + } + + HRESULT UnregisterThumbviewerHandler() + { + std::wstring iid = ClsidToString(IID_IExtractImage); + std::wstring tmp; + + for (size_t i = 0; i < OOFileExtensionTableSize; i++) + { + tmp = SHELLEX_IID_ENTRY; + + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + SubstitutePlaceholder(tmp, GUID_PLACEHOLDER, iid); + + DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()); + + // if there are no further subkey below .ext\\shellex + // delete the whole subkey + tmp = SHELLEX_ENTRY; + SubstitutePlaceholder(tmp, EXTENSION_PLACEHOLDER, OOFileExtensionTable[i].ExtensionU); + + bool HasSubKeys = true; + if (HasSubkeysRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str(), HasSubKeys) && !HasSubKeys) + DeleteRegistryKey(HKEY_CLASSES_ROOT, tmp.c_str()); + } + return UnregisterComComponent(CLSID_THUMBVIEWER_HANDLER); + } + + /** Approving/Unapproving the Shell Extension, it's important under Windows + NT/2000/XP, see MSDN: Creating Shell Extension Handlers */ + HRESULT ApproveShellExtension(const CLSID& clsid, const std::wstring& Description) + { + bool bRet = SetRegistryKey( + HKEY_LOCAL_MACHINE, + SHELL_EXTENSION_APPROVED_KEY_NAME, + ClsidToString(clsid).c_str(), + Description.c_str()); + + return bRet ? S_OK : E_FAIL; + } + + HRESULT UnapproveShellExtension(const CLSID& Clsid) + { + HKEY hkey; + + LONG rc = RegOpenKeyW( + HKEY_LOCAL_MACHINE, + SHELL_EXTENSION_APPROVED_KEY_NAME, + &hkey); + + if (ERROR_SUCCESS == rc) + { + rc = RegDeleteValueW( + hkey, + ClsidToString(Clsid).c_str()); + + rc |= RegCloseKey(hkey); + } + + return rc == ERROR_SUCCESS ? S_OK : E_FAIL; + } + +} // namespace /* private */ + + +// COM exports + + +STDAPI DllRegisterServer() +{ + WCHAR ModuleFileName[MAX_PATH]; + + GetModuleFileNameW( + GetCurrentModuleHandle(), + ModuleFileName, + sizeof(ModuleFileName)/sizeof(ModuleFileName[0])); + + HRESULT hr = S_OK; + + if (SUCCEEDED(RegisterColumnHandler(ModuleFileName))) + ApproveShellExtension(CLSID_COLUMN_HANDLER, COLUMN_HANDLER_DESCRIPTIVE_NAME); + else + hr = E_FAIL; + + if (SUCCEEDED(RegisterInfotipHandler(ModuleFileName))) + ApproveShellExtension(CLSID_INFOTIP_HANDLER, INFOTIP_HANDLER_DESCRIPTIVE_NAME); + else + hr = E_FAIL; + + if (SUCCEEDED(RegisterPropSheetHandler(ModuleFileName))) + ApproveShellExtension(CLSID_PROPERTYSHEET_HANDLER, PROPSHEET_HANDLER_DESCRIPTIVE_NAME); + else + hr = E_FAIL; + + if (SUCCEEDED(RegisterThumbviewerHandler(ModuleFileName))) + ApproveShellExtension(CLSID_THUMBVIEWER_HANDLER, THUMBVIEWER_HANDLER_DESCRIPTIVE_NAME); + else + hr = E_FAIL; + + // notify the Shell that something has changed + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); + + return hr; +} + +STDAPI DllUnregisterServer() +{ + HRESULT hr = S_OK; + + if (FAILED(UnregisterColumnHandler())) + hr = E_FAIL; + + UnapproveShellExtension(CLSID_COLUMN_HANDLER); + + if (FAILED(UnregisterInfotipHandler())) + hr = E_FAIL; + + UnapproveShellExtension(CLSID_INFOTIP_HANDLER); + + if (FAILED(UnregisterPropSheetHandler())) + hr = E_FAIL; + + UnapproveShellExtension(CLSID_PROPERTYSHEET_HANDLER); + + if (FAILED(UnregisterThumbviewerHandler())) + hr = E_FAIL; + + UnapproveShellExtension(CLSID_THUMBVIEWER_HANDLER); + + // notify the Shell that something has changed + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); + + return hr; +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + *ppv = nullptr; + + if ((rclsid != CLSID_INFOTIP_HANDLER) && + (rclsid != CLSID_COLUMN_HANDLER) && + (rclsid != CLSID_PROPERTYSHEET_HANDLER) && + (rclsid != CLSID_THUMBVIEWER_HANDLER)) + return CLASS_E_CLASSNOTAVAILABLE; + + if ((riid != IID_IUnknown) && (riid != IID_IClassFactory)) + return E_NOINTERFACE; + + if ( rclsid == CLSID_INFOTIP_HANDLER ) + OutputDebugStringFormatW( L"DllGetClassObject: Create CLSID_INFOTIP_HANDLER\n" ); + else if ( rclsid == CLSID_COLUMN_HANDLER ) + OutputDebugStringFormatW( L"DllGetClassObject: Create CLSID_COLUMN_HANDLER\n" ); + else if ( rclsid == CLSID_PROPERTYSHEET_HANDLER ) + OutputDebugStringFormatW( L"DllGetClassObject: Create CLSID_PROPERTYSHEET_HANDLER\n" ); + else if ( rclsid == CLSID_THUMBVIEWER_HANDLER ) + OutputDebugStringFormatW( L"DllGetClassObject: Create CLSID_THUMBVIEWER_HANDLER\n" ); + + IUnknown* pUnk = new CClassFactory(rclsid); + *ppv = pUnk; + return S_OK; +} + +STDAPI DllCanUnloadNow() +{ + if (CClassFactory::IsLocked() || g_DllRefCnt > 0) + return S_FALSE; + + return S_OK; +} + +BOOL WINAPI DllMain(HINSTANCE hInst, ULONG /*ul_reason_for_call*/, LPVOID /*lpReserved*/) +{ + g_hModule = hInst; + return TRUE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/shlxthdl.def b/shell/source/win32/shlxthandler/shlxthdl.def new file mode 100644 index 000000000..d731a151f --- /dev/null +++ b/shell/source/win32/shlxthandler/shlxthdl.def @@ -0,0 +1,5 @@ +EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/shell/source/win32/shlxthandler/thumbviewer/thumbviewer.cxx b/shell/source/win32/shlxthandler/thumbviewer/thumbviewer.cxx new file mode 100644 index 000000000..657664ff5 --- /dev/null +++ b/shell/source/win32/shlxthandler/thumbviewer/thumbviewer.cxx @@ -0,0 +1,500 @@ +/* -*- 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 DONT_HAVE_GDIPLUS + + +#include <global.hxx> + +#include <thumbviewer.hxx> +#include <shlxthdl.hxx> +#include <registry.hxx> +#include <fileextensions.hxx> +#include <config.hxx> +#include <zipfile.hxx> +#include <utilities.hxx> + +#include <resource.h> + +#include <stdio.h> +#include <utility> +#include <stdlib.h> + +#include <shellapi.h> + +#include <memory> + +namespace internal +{ + /* The signet.png used for thumbnails of signed documents + is contained as resource in this module, the resource + id is 2000 */ + static void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer) + { + HRSRC hrc = FindResourceW(g_hModule, L"#2000", reinterpret_cast<LPWSTR>(RT_RCDATA)); + DWORD size = SizeofResource(g_hModule, hrc); + HGLOBAL hglob = LoadResource(g_hModule, hrc); + char* data = static_cast<char*>(LockResource(hglob)); + buffer = ZipFile::ZipContentBuffer_t(data, data + size); + } + + static bool IsSignedDocument(const ZipFile* zipfile) + { + return zipfile->HasContent("META-INF/documentsignatures.xml"); + } + + static Gdiplus::Point CalcSignetPosition( + const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet) + { + int x = 0; + int y = 0; + int hoffset = canvas.GetRight() - thumbnail_border.GetRight(); + int voffset = canvas.GetBottom() - thumbnail_border.GetBottom(); + + if (hoffset > voffset) + { + x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset); + y = thumbnail_border.GetBottom() - signet.GetBottom(); + } + else + { + x = thumbnail_border.GetRight() - signet.GetRight(); + y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset); + } + + return Gdiplus::Point(x,y); + } +} + +namespace { + +Gdiplus::Rect CalcScaledAspectRatio(const Gdiplus::Rect& src, const Gdiplus::Rect& dest) +{ + Gdiplus::Rect result; + if (src.Width >= src.Height) + result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width); + else + result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height); + + return result; +} + +class StreamOnZipBuffer final : public IStream +{ +public: + explicit StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer); + + // IUnknown + virtual ULONG STDMETHODCALLTYPE AddRef() override; + virtual ULONG STDMETHODCALLTYPE Release() override; + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) override; + + // IStream + virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) override; + virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten) override; + virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) override; + virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) override; + virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) override; + virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags) override; + virtual HRESULT STDMETHODCALLTYPE Revert() override; + virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override; + virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) override; + virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag) override; + virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm) override; + +private: + LONG ref_count_; + const ZipFile::ZipContentBuffer_t& ref_zip_buffer_; + size_t pos_; +}; + +} + +StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) : + ref_count_(1), + ref_zip_buffer_(zip_buffer), + pos_(0) +{ +} + +// IUnknown methods + +ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef() +{ + return InterlockedIncrement(&ref_count_); +} + +ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release() +{ + LONG refcnt = InterlockedDecrement(&ref_count_); + + if (0 == ref_count_) + delete this; + + return refcnt; +} + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + IUnknown* pUnk = nullptr; + + if ((IID_IUnknown == riid) || (IID_IStream == riid)) + { + pUnk = this; + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead) +{ + if (pv == nullptr) + return STG_E_INVALIDPOINTER; + + size_t size = ref_zip_buffer_.size(); + + if (pos_ > size) + return S_FALSE; + + char* p = static_cast<char*>(pv); + ULONG read = 0; + + for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++) + *p++ = ref_zip_buffer_[pos_]; + + if (pcbRead) + *pcbRead = read; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *) +{ + __int64 size = static_cast<__int64>(ref_zip_buffer_.size()); + __int64 p = 0; + + switch (dwOrigin) + { + case STREAM_SEEK_SET: + break; + case STREAM_SEEK_CUR: + p = static_cast<__int64>(pos_); + break; + case STREAM_SEEK_END: + p = size - 1; + break; + } + + HRESULT hr = STG_E_INVALIDFUNCTION; + + p += dlibMove.QuadPart; + + if ( ( p >= 0 ) && (p < size) ) + { + pos_ = static_cast<size_t>(p); + hr = S_OK; + } + return hr; +} + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag) +{ + if (pstatstg == nullptr) + return STG_E_INVALIDPOINTER; + + ZeroMemory(pstatstg, sizeof(STATSTG)); + + if (grfStatFlag == STATFLAG_DEFAULT) + { + size_t sz = 4 * sizeof(wchar_t); + wchar_t* name = static_cast<wchar_t*>(CoTaskMemAlloc(sz)); + ZeroMemory(name, sz); + memcpy(name, L"png", 3 * sizeof(wchar_t)); + pstatstg->pwcsName = name; + } + + pstatstg->type = STGTY_LOCKBYTES; + + ULARGE_INTEGER uli; + uli.LowPart = static_cast<DWORD>(ref_zip_buffer_.size()); + uli.HighPart = 0; + + pstatstg->cbSize = uli; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert() +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **) +{ return E_NOTIMPL; } + + +CThumbviewer::CThumbviewer(LONG RefCnt) : + ref_count_(RefCnt) +{ + InterlockedIncrement(&g_DllRefCnt); + + thumbnail_size_.cx = 0; + thumbnail_size_.cy = 0; + + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, nullptr); + + ZipFile::ZipContentBuffer_t img_data; + internal::LoadSignetImageFromResource(img_data); + IStream* stream = new StreamOnZipBuffer(img_data); + signet_ = new Gdiplus::Bitmap(stream, TRUE); + stream->Release(); +} + +CThumbviewer::~CThumbviewer() +{ + delete signet_; + Gdiplus::GdiplusShutdown(gdiplus_token_); + InterlockedDecrement(&g_DllRefCnt); +} + +// IUnknown methods + +HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) +{ + *ppvObject = nullptr; + IUnknown* pUnk = nullptr; + + if ((IID_IUnknown == riid) || (IID_IPersistFile == riid)) + { + pUnk = static_cast<IPersistFile*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + else if (IID_IExtractImage == riid) + { + pUnk = static_cast<IExtractImage*>(this); + pUnk->AddRef(); + *ppvObject = pUnk; + return S_OK; + } + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE CThumbviewer::AddRef() +{ + return InterlockedIncrement(&ref_count_); +} + +ULONG STDMETHODCALLTYPE CThumbviewer::Release() +{ + LONG refcnt = InterlockedDecrement(&ref_count_); + + if (0 == ref_count_) + delete this; + + return refcnt; +} + +// IExtractImage2 methods + +const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png"; + +HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage) +{ + HRESULT hr = E_FAIL; + + try + { + std::wstring fname = getShortPathName( filename_ ); + std::unique_ptr<ZipFile> zipfile( new ZipFile( fname ) ); + + if (zipfile->HasContent(THUMBNAIL_CONTENT)) + { + ZipFile::ZipContentBuffer_t thumbnail; + zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail); + IStream* stream = new StreamOnZipBuffer(thumbnail); + + Gdiplus::Bitmap thumbnail_png(stream, TRUE); + + if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0)) + { + stream->Release(); + return E_FAIL; + } + + HWND hwnd = GetDesktopWindow(); + HDC hdc = GetDC(hwnd); + HDC memDC = CreateCompatibleDC(hdc); + + if (memDC) + { + UINT offset = 3; // reserve a little border space + + Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy); + Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset); + + Gdiplus::Rect scaledRect = CalcScaledAspectRatio( + Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail); + + struct { + BITMAPINFOHEADER bi; + DWORD ct[256]; + } dib; + + ZeroMemory(&dib, sizeof(dib)); + + dib.bi.biSize = sizeof(BITMAPINFOHEADER); + dib.bi.biWidth = thumbnail_size_.cx; + dib.bi.biHeight = thumbnail_size_.cy; + dib.bi.biPlanes = 1; + dib.bi.biBitCount = static_cast<WORD>(color_depth_); + dib.bi.biCompression = BI_RGB; + + LPVOID lpBits; + HBITMAP hMemBmp = CreateDIBSection(memDC, reinterpret_cast<LPBITMAPINFO>(&dib), DIB_RGB_COLORS, &lpBits, nullptr, 0); + HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp); + + Gdiplus::Graphics graphics(memDC); + Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1); + + Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255)); + graphics.FillRectangle(&whiteBrush, canvas); + + scaledRect.X = (canvas.Width - scaledRect.Width) / 2; + scaledRect.Y = (canvas.Height - scaledRect.Height) / 2; + + Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height); + graphics.DrawRectangle(&blackPen, border_rect); + + scaledRect.X += 1; + scaledRect.Y += 1; + scaledRect.Width -= 1; + scaledRect.Height -= 1; + + graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic); + Gdiplus::Status stat = graphics.DrawImage( + &thumbnail_png, scaledRect, 0 , 0, + thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel); + + /* Add a signet sign to the thumbnail of signed documents */ + if (internal::IsSignedDocument(zipfile.get())) + { + double SCALING_FACTOR = 0.6; + Gdiplus::Rect signet_scaled( + 0, 0, static_cast<INT>(signet_->GetWidth() * SCALING_FACTOR), static_cast<INT>(signet_->GetHeight() * SCALING_FACTOR)); + Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled); + Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom()); + + stat = graphics.DrawImage( + signet_, dest, + 0, 0, signet_->GetWidth(), signet_->GetHeight(), + Gdiplus::UnitPixel); + } + + if (stat == Gdiplus::Ok) + { + *phBmpImage = hMemBmp; + hr = NOERROR; + } + + SelectObject(memDC, hOldObj); + DeleteDC(memDC); + } + + ReleaseDC(hwnd, hdc); + stream->Release(); + } + } + catch(std::exception&) + { + OutputDebugStringFormatW( L"CThumbviewer Extract ERROR!\n" ); + hr = E_FAIL; + } + return hr; +} + +HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation( + LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags) +{ + if ((prgSize == nullptr) || (pdwFlags == nullptr) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == nullptr))) + return E_INVALIDARG; + + thumbnail_size_ = *prgSize; + color_depth_ = dwRecClrDepth; + + *pdwFlags = IEIFLAG_CACHE; // we don't cache the image + + wcsncpy(pszPathBuffer, filename_.c_str(), cchMax); + + return NOERROR; +} + +// IPersist methods + +HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID) +{ + *pClassID = CLSID_THUMBVIEWER_HANDLER; + return S_OK; +} + +// IPersistFile methods + +HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD) +{ + filename_ = pszFileName; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty() +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR) +{ return E_NOTIMPL; } + +HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*) +{ return E_NOTIMPL; } + + +#endif // DONT_HAVE_GDIPLUS + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/util/fileextensions.cxx b/shell/source/win32/shlxthandler/util/fileextensions.cxx new file mode 100644 index 000000000..57c8068ef --- /dev/null +++ b/shell/source/win32/shlxthandler/util/fileextensions.cxx @@ -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 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 <fileextensions.hxx> +#include <rtl/character.hxx> +#include <sal/macros.h> + + +const std::wstring WRITER_FILE_EXTENSIONS = L"sxwstwsxgodtottodm"; +const std::wstring CALC_FILE_EXTENSIONS = L"sxcstcodsots"; +const std::wstring DRAW_FILE_EXTENSIONS = L"sxdstdodgotg"; +const std::wstring IMPRESS_FILE_EXTENSIONS = L"sxistiodpotp"; +const std::wstring MATH_FILE_EXTENSIONS = L"sxmodf"; +const std::wstring WEB_FILE_EXTENSIONS = L"oth"; +const std::wstring DATABASE_FILE_EXTENSIONS = L"odb"; + +const FileExtensionEntry OOFileExtensionTable[] = { + { ".sxw", L".sxw", L"soffice.StarWriterDocument.6" }, + { ".sxc", L".sxc", L"soffice.StarCalcDocument.6" }, + { ".sxi", L".sxi", L"soffice.StarImpressDocument.6" }, + { ".sxd", L".sxd", L"soffice.StarDrawDocument.6" }, + { ".sxm", L".sxm", L"soffice.StarMathDocument.6" }, + { ".stw", L".stw", L"soffice.StarWriterTemplate.6" }, + { ".sxg", L".sxg", L"soffice.StarWriterGlobalDocument.6" }, + { ".std", L".std", L"soffice.StarDrawTemplate.6" }, + { ".sti", L".sti", L"soffice.StarImpressTemplate.6" }, + { ".stc", L".stc", L"soffice.StarCalcTemplate.6" }, + { ".odt", L".odt", L"LibreOffice.WriterDocument.1" }, + { ".ott", L".ott", L"LibreOffice.WriterTemplate.1" }, + { ".odm", L".odm", L"LibreOffice.WriterGlobalDocument.1" }, + { ".oth", L".oth", L"LibreOffice.WriterWebTemplate.1" }, + { ".ods", L".ods", L"LibreOffice.CalcDocument.1" }, + { ".ots", L".ots", L"LibreOffice.CalcTemplate.1" }, + { ".odg", L".odg", L"LibreOffice.DrawDocument.1" }, + { ".otg", L".otg", L"LibreOffice.DrawTemplate.1" }, + { ".odp", L".odp", L"LibreOffice.ImpressDocument.1" }, + { ".otp", L".otp", L"LibreOffice.ImpressTemplate.1" }, + { ".odf", L".odf", L"LibreOffice.MathDocument.1" }, + { ".odb", L".odb", L"LibreOffice.DatabaseDocument.1" } + }; + + +const size_t OOFileExtensionTableSize = SAL_N_ELEMENTS(OOFileExtensionTable); + + +/** Return the extension of a file + name without the '.' +*/ +Filepath_t get_file_name_extension(const Filepath_t& file_name) +{ + std::wstring::size_type idx = file_name.find_last_of(L"."); + + if (std::wstring::npos != idx++) + return std::wstring(file_name.begin() + idx, file_name.end()); + + return std::wstring(); +} + + +/** Return the type of a file +*/ + +File_Type_t get_file_type(const Filepath_t& file_name) +{ + std::wstring fext = get_file_name_extension(file_name); + std::transform( + fext.begin(), fext.end(), fext.begin(), + [](wchar_t c) { + return rtl::toAsciiLowerCase(c); }); + + if (std::wstring::npos != WRITER_FILE_EXTENSIONS.find(fext)) + return WRITER; + else if (std::wstring::npos != CALC_FILE_EXTENSIONS.find(fext)) + return CALC; + else if (std::wstring::npos != DRAW_FILE_EXTENSIONS.find(fext)) + return DRAW; + else if (std::wstring::npos != IMPRESS_FILE_EXTENSIONS.find(fext)) + return IMPRESS; + else if (std::wstring::npos != MATH_FILE_EXTENSIONS.find(fext)) + return MATH; + else if (std::wstring::npos != WEB_FILE_EXTENSIONS.find(fext)) + return WEB; + else if (std::wstring::npos != DATABASE_FILE_EXTENSIONS.find(fext)) + return DATABASE; + else + return UNKNOWN; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/util/iso8601_converter.cxx b/shell/source/win32/shlxthandler/util/iso8601_converter.cxx new file mode 100644 index 000000000..c0415deb9 --- /dev/null +++ b/shell/source/win32/shlxthandler/util/iso8601_converter.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include <stdlib.h> + +#include <iso8601_converter.hxx> +#include <utilities.hxx> + +#include <sstream> +#include <iomanip> + +#include <rtl/character.hxx> + +/* Converts ISO 8601 compliant date/time + representation to the representation + conforming to the current locale +*/ +std::wstring iso8601_date_to_local_date(const std::wstring& isoDate ) +{ + ::std::wstring ws8601DateTime(isoDate); + + // Get rid of the optional milliseconds part if it exists. + // Function accepts date/time as a combined date/time string in extended ISO8601 format, + // which is yyyy-mm-ddThh:mm:ss[.mmm]. Last part is the optional "fraction of second" part, + // that's why we cut off at 19. + if (ws8601DateTime.length() > 19) + { + ws8601DateTime.erase(19, ::std::basic_string<char>::npos); + } + + if ( ws8601DateTime.length() == 19 ) + { + std::string asDateTime = WStringToString( ws8601DateTime ); + SYSTEMTIME DateTime; + DateTime.wYear = static_cast<unsigned short>(strtol( asDateTime.substr( 0, 4 ).c_str(), nullptr, 10 )); + DateTime.wMonth = static_cast<unsigned short>(strtol( asDateTime.substr( 5, 2 ).c_str(), nullptr, 10 )); + DateTime.wDayOfWeek = 0; + DateTime.wDay = static_cast<unsigned short>(strtol( asDateTime.substr( 8, 2 ).c_str(), nullptr, 10 )); + DateTime.wHour = static_cast<unsigned short>(strtol( asDateTime.substr( 11,2 ).c_str(), nullptr, 10 )); + DateTime.wMinute = static_cast<unsigned short>(strtol( asDateTime.substr( 14,2 ).c_str(), nullptr, 10 )); + DateTime.wSecond = static_cast<unsigned short>(strtol( asDateTime.substr( 17,2 ).c_str(), nullptr, 10 )); + DateTime.wMilliseconds = 0; + + //get Date info from structure + WCHAR DateBuffer[ MAX_PATH ]; + int DateSize = GetDateFormatW( + LOCALE_SYSTEM_DEFAULT, + 0, + &DateTime, + nullptr, + DateBuffer, + MAX_PATH ); + + if ( DateSize ) + ws8601DateTime.assign(DateBuffer); + else + ws8601DateTime = StringToWString( asDateTime ); + + //get Time info from structure + WCHAR TimeBuffer[ MAX_PATH ]; + + int TimeSize = GetTimeFormatW( + LOCALE_SYSTEM_DEFAULT, + 0, + &DateTime, + nullptr, + TimeBuffer, + MAX_PATH ); + + if ( TimeSize ) + { + ws8601DateTime.append(L" "); + ws8601DateTime.append(TimeBuffer); + } + else + ws8601DateTime = StringToWString( asDateTime ); + } + + return ws8601DateTime; +} + + +/* Converts ISO 8601 conform duration + representation to the representation + conforming to the current locale + + Expect format PTnHnMnS according to + ISO 8601 where n is arbitrary number + of digits +*/ + +std::wstring iso8601_duration_to_local_duration(const std::wstring& iso8601duration) +{ + std::wstring days; + std::wstring hours; + std::wstring minutes; + std::wstring seconds; + + std::wstring num; + + for (const auto& w_ch : iso8601duration) + { + if (rtl::isAsciiDigit(w_ch)) // wchar_t is unsigned under MSVC + { + num += w_ch; + } + else + { + if (w_ch == L'D' || w_ch == L'd') + days = num; + else if (w_ch == L'H' || w_ch == L'h') + hours = num; + else if (w_ch == L'M' || w_ch == L'm') + minutes = num; + else if (w_ch == L'S' || w_ch == L's') + seconds = num; + + num.clear(); + } + } + + if (days.length() > 0) + { + int h = (_wtoi(days.c_str()) * 24) + _wtoi(hours.c_str()); + wchar_t buff[10]; + _itow(h, buff, 10); + hours = buff; + } + + std::wostringstream oss; + oss << std::setw(2) << std::setfill(wchar_t('0')) << hours << L":" << + std::setw(2) << std::setfill(wchar_t('0')) << minutes << L":" << + std::setw(2) << std::setfill(wchar_t('0')) << seconds; + return oss.str(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/util/registry.cxx b/shell/source/win32/shlxthandler/util/registry.cxx new file mode 100644 index 000000000..cc42c092e --- /dev/null +++ b/shell/source/win32/shlxthandler/util/registry.cxx @@ -0,0 +1,173 @@ +/* -*- 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 . + */ + + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <malloc.h> +#include <registry.hxx> + +#include <objbase.h> + +bool SetRegistryKey(HKEY RootKey, const Filepath_char_t* KeyName, const Filepath_char_t* ValueName, const Filepath_char_t* Value) +{ + HKEY hSubKey; + + // open or create the desired key + wchar_t dummy[] = L""; + int rc = RegCreateKeyExW( + RootKey, KeyName, 0, dummy, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hSubKey, nullptr); + + if (ERROR_SUCCESS == rc) + { + rc = RegSetValueExW( + hSubKey, ValueName, 0, REG_SZ, reinterpret_cast<const BYTE*>(Value), + static_cast<DWORD>((wcslen(Value) + 1) * sizeof(*Value))); + + RegCloseKey(hSubKey); + } + + return (ERROR_SUCCESS == rc); +} + + +bool DeleteRegistryKey(HKEY RootKey, const Filepath_char_t* KeyName) +{ + HKEY hKey; + + int rc = RegOpenKeyExW( + RootKey, + KeyName, + 0, + KEY_READ | DELETE, + &hKey); + + if ( rc == ERROR_FILE_NOT_FOUND ) + return true; + + if (ERROR_SUCCESS == rc) + { + wchar_t* SubKey; + DWORD nMaxSubKeyLen; + + rc = RegQueryInfoKeyW( + hKey, nullptr, nullptr, nullptr, nullptr, + &nMaxSubKeyLen, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + + nMaxSubKeyLen++; // space for trailing '\0' + + SubKey = static_cast<wchar_t*>( + _alloca(nMaxSubKeyLen*sizeof(wchar_t))); + + while (ERROR_SUCCESS == rc) + { + DWORD nLen = nMaxSubKeyLen; + + rc = RegEnumKeyExW( + hKey, + 0, // always index zero + SubKey, + &nLen, + nullptr, nullptr, nullptr, nullptr); + + if (ERROR_NO_MORE_ITEMS == rc) + { + rc = RegDeleteKeyW(RootKey, KeyName); + break; + } + else if (rc == ERROR_SUCCESS) + { + DeleteRegistryKey(hKey, SubKey); + } + + } // while + + RegCloseKey(hKey); + + } // if + + return (ERROR_SUCCESS == rc); +} + +/** May be used to determine if the specified registry key has subkeys + The function returns true on success else if an error occurs false +*/ +bool HasSubkeysRegistryKey(HKEY RootKey, const Filepath_char_t* KeyName, /* out */ bool& bResult) +{ + HKEY hKey; + + LONG rc = RegOpenKeyExW(RootKey, KeyName, 0, KEY_READ, &hKey); + + if (ERROR_SUCCESS == rc) + { + DWORD nSubKeys = 0; + + rc = RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &nSubKeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + + RegCloseKey(hKey); + bResult = (nSubKeys > 0); + } + + return (ERROR_SUCCESS == rc); +} + +// Convert a CLSID to a char string. +Filepath_t ClsidToString(const CLSID& clsid) +{ + // Get CLSID + LPOLESTR wszCLSID = nullptr; + StringFromCLSID(clsid, &wszCLSID); + + std::wstring sResult = wszCLSID; + + // Free memory. + CoTaskMemFree(wszCLSID) ; + + return sResult; +} + + +bool QueryRegistryKey(HKEY RootKey, const Filepath_char_t* KeyName, const Filepath_char_t* ValueName, Filepath_char_t *pszData, DWORD dwBufLen) +{ + HKEY hKey; + + int rc = RegOpenKeyExW( + RootKey, + KeyName, + 0, + KEY_READ, + &hKey); + + if (ERROR_SUCCESS == rc) + { + DWORD dwBytes = dwBufLen * sizeof(*pszData); + rc = RegQueryValueExW( + hKey, ValueName, nullptr, nullptr, reinterpret_cast<LPBYTE>(pszData),&dwBytes); + + RegCloseKey(hKey); + } + + return (ERROR_SUCCESS == rc); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/shlxthandler/util/utilities.cxx b/shell/source/win32/shlxthandler/util/utilities.cxx new file mode 100644 index 000000000..27bf12c21 --- /dev/null +++ b/shell/source/win32/shlxthandler/util/utilities.cxx @@ -0,0 +1,565 @@ +/* -*- 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 <config.hxx> +#include <global.hxx> +#include <utilities.hxx> + +// constants + + +const size_t MAX_RES_STRING = 1024; +const wchar_t SPACE_CHAR = L' '; + +static std::wstring StringToWString(const std::string& String, int codepage) +{ + int len = MultiByteToWideChar( + codepage, 0, String.c_str(), -1, nullptr, 0); + + wchar_t* buff = static_cast<wchar_t*>( + _alloca(len * sizeof(wchar_t))); + + MultiByteToWideChar( + codepage, 0, String.c_str(), -1, buff, len); + + return std::wstring(buff); +} + +static std::string WStringToString(const std::wstring& String, int codepage) +{ + int len = WideCharToMultiByte( + codepage, 0, String.c_str(), -1, nullptr, 0, nullptr, nullptr); + + char* buff = static_cast<char*>( + _alloca(len * sizeof(char))); + + WideCharToMultiByte( + codepage, 0, String.c_str(), -1, buff, len, nullptr, nullptr); + + return std::string(buff); +} + + +std::wstring StringToWString(const std::string& String) +{ + return StringToWString(String, CP_ACP); +} + + +std::string WStringToString(const std::wstring& String) +{ + return WStringToString(String, CP_ACP); +} + + +std::wstring UTF8ToWString(const std::string& String) +{ + return StringToWString(String, CP_UTF8); +} + + +std::wstring GetResString(int ResId) +{ + wchar_t szResStr[MAX_RES_STRING]; + + int rc = LoadStringW( GetCurrentModuleHandle(), ResId, szResStr, sizeof(szResStr) ); + + OutputDebugStringFormatW( L"GetResString: read %d chars\n", rc ); + // OSL_ENSURE(rc, "String resource not found"); + + return std::wstring(szResStr); +} + + +/** helper function to judge if the string is only has spaces. + @returns + <TRUE>if the provided string contains only but at least one space + character else <FALSE/>. +*/ + +bool HasOnlySpaces(const std::wstring& String) +{ + if ( String.length() == 0 ) + return false; + + const wchar_t* p = String.c_str(); + + while (*p) + { + if (*p++ != SPACE_CHAR) + return false; + } + + return true; +} + + +/** helper function to convert windows paths to short form. + @returns + shortened path. +*/ + +std::wstring getShortPathName( const std::wstring& aLongName ) +{ + std::wstring shortName = aLongName; + DWORD length = GetShortPathNameW( aLongName.c_str(), nullptr, 0 ); + + if ( length != 0 ) + { + auto buffer = std::make_unique<WCHAR[]>( length+1 ); + length = GetShortPathNameW( aLongName.c_str(), buffer.get(), length ); + if ( length != 0 ) + shortName = std::wstring( buffer.get() ); + } + return shortName; +} + + +/** convert LocaleSet pair into Microsoft List of Locale ID (LCID) + according to ISO-639 and ISO-3166. + http://etext.lib.virginia.edu/tei/iso639.html + http://nl.ijs.si/gnusl/cee/std/ISO_3166.html + @param + Locale, LocaleSet + @returns + Windows Locale Identifier corresponding to input LocaleSet. + @Usage Sample + LocaleSet_t myDefaultLocale( ::std::wstring( L"zh" ),::std::wstring(L"HK") ); + DWORD myLCID = LocaleSetToLCID( myDefaultLocale ); + wchar_t buffer[20]; + _ultow( myLCID, buffer, 16 ); + MessageBox( NULL, buffer,L"the LCID is:",MB_OK ); +*/ + +LCID LocaleSetToLCID( const LocaleSet_t & Locale ) +{ + if ( EMPTY_LOCALE == Locale ) + return GetSystemDefaultLCID(); + + USHORT usPrimaryLang= LANG_NEUTRAL; + USHORT usSubLang=SUBLANG_DEFAULT; + + ::std::wstring wsLanguage(Locale.first); + ::std::wstring wsCountry(Locale.second); + + if ( wsLanguage == L"ar" ) + { + usPrimaryLang = LANG_ARABIC; // Arabic 01 + + if ( wsCountry == L"SA" ) + usSubLang = SUBLANG_ARABIC_SAUDI_ARABIA; // Arabic (Saudi Arabia) + else if ( wsCountry == L"IQ" ) + usSubLang = SUBLANG_ARABIC_IRAQ; // Arabic (Iraq) + else if ( wsCountry == L"EG" ) + usSubLang = SUBLANG_ARABIC_EGYPT; // Arabic (Egypt) + else if ( wsCountry == L"LY" ) + usSubLang = SUBLANG_ARABIC_LIBYA; // Arabic (Libya) + else if ( wsCountry == L"DZ" ) + usSubLang = SUBLANG_ARABIC_ALGERIA; // Arabic (Algeria) + else if ( wsCountry == L"MA" ) + usSubLang = SUBLANG_ARABIC_MOROCCO; // Arabic (Morocco) + else if ( wsCountry == L"TN" ) + usSubLang = SUBLANG_ARABIC_TUNISIA; // Arabic (Tunisia) + else if ( wsCountry == L"OM" ) + usSubLang = SUBLANG_ARABIC_OMAN; // Arabic (Oman) + else if ( wsCountry == L"YE" ) + usSubLang = SUBLANG_ARABIC_YEMEN; // Arabic (Yemen) + else if ( wsCountry == L"SY" ) + usSubLang = SUBLANG_ARABIC_SYRIA; // Arabic (Syria) + else if ( wsCountry == L"JO" ) + usSubLang = SUBLANG_ARABIC_JORDAN; // Arabic (Jordan) + else if ( wsCountry == L"LB" ) + usSubLang = SUBLANG_ARABIC_LEBANON; // Arabic (Lebanon) + else if ( wsCountry == L"KW" ) + usSubLang = SUBLANG_ARABIC_KUWAIT; // Arabic (Kuwait) + else if ( wsCountry == L"AE" ) + usSubLang = SUBLANG_ARABIC_UAE; // Arabic (U.A.E.) + else if ( wsCountry == L"BH" ) + usSubLang = SUBLANG_ARABIC_BAHRAIN; // Arabic (Bahrain) + else if ( wsCountry == L"QA" ) + usSubLang = SUBLANG_ARABIC_QATAR; // Arabic (Qatar) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"bg" ) + usPrimaryLang = LANG_BULGARIAN; //Bulgarian 02 + else if ( wsLanguage == L"ca" ) + usPrimaryLang = LANG_CATALAN; //Catalan 03 + else if ( wsLanguage == L"zh" ) + { + usPrimaryLang = LANG_CHINESE; //Chinese + if ( wsCountry == L"TW" ) + usSubLang = SUBLANG_CHINESE_TRADITIONAL; // Chinese (Traditional) + else if ( wsCountry == L"CN" ) + usSubLang = SUBLANG_CHINESE_SIMPLIFIED; // Chinese (Simplified) + else if ( wsCountry == L"HK" ) + usSubLang = SUBLANG_CHINESE_HONGKONG; // Chinese (Hong Kong SAR, PRC) + else if ( wsCountry == L"SG" ) + usSubLang = SUBLANG_CHINESE_SINGAPORE; // Chinese (Singapore) + else if ( wsCountry == L"MO" ) + usSubLang = SUBLANG_CHINESE_MACAU; // Chinese (Macau SAR) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"cs" ) + usPrimaryLang = LANG_CZECH; //Czech + else if ( wsLanguage == L"da" ) + usPrimaryLang = LANG_DANISH; //Danish + else if ( wsLanguage == L"de" ) + { + usPrimaryLang = LANG_GERMAN; //German + if ( wsCountry == L"DE" ) + usSubLang = SUBLANG_GERMAN; // German + else if ( wsCountry == L"CH" ) + usSubLang = SUBLANG_GERMAN_SWISS; // German (Swiss) + else if ( wsCountry == L"AT" ) + usSubLang = SUBLANG_GERMAN_AUSTRIAN; // German (Austrian) + else if ( wsCountry == L"LU" ) + usSubLang = SUBLANG_GERMAN_LUXEMBOURG; // German (Luxembourg) + else if ( wsCountry == L"LI" ) + usSubLang = SUBLANG_GERMAN_LIECHTENSTEIN; // German (Liechtenstein) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"el" ) + usPrimaryLang = LANG_GREEK; //Greek + else if ( wsLanguage == L"en" ) + { + usPrimaryLang = LANG_ENGLISH; //English + if ( wsCountry == L"US" ) + usSubLang = SUBLANG_ENGLISH_US; // English (US) + else if ( wsCountry == L"GB" ) + usSubLang = SUBLANG_ENGLISH_UK; // English (UK) + else if ( wsCountry == L"AU" ) + usSubLang = SUBLANG_ENGLISH_AUS; // English (Australian) + else if ( wsCountry == L"CA" ) + usSubLang = SUBLANG_ENGLISH_CAN; // English (Canadian) + else if ( wsCountry == L"NZ" ) + usSubLang = SUBLANG_ENGLISH_NZ; // English (New Zealand) + else if ( wsCountry == L"IE" ) + usSubLang = SUBLANG_ENGLISH_EIRE; // English (Ireland) + else if ( wsCountry == L"ZA" ) + usSubLang = SUBLANG_ENGLISH_SOUTH_AFRICA; // English (South Africa) + else if ( wsCountry == L"JM" ) + usSubLang = SUBLANG_ENGLISH_JAMAICA; // English (Jamaica) + else if ( wsCountry == L"GD" ) + usSubLang = SUBLANG_ENGLISH_CARIBBEAN; // English (Caribbean) Grenada + else if ( wsCountry == L"BZ" ) + usSubLang = SUBLANG_ENGLISH_BELIZE; // English (Belize) + else if ( wsCountry == L"TT" ) + usSubLang = SUBLANG_ENGLISH_TRINIDAD; // English (Trinidad) + else if ( wsCountry == L"ZW" ) + usSubLang = SUBLANG_ENGLISH_ZIMBABWE; // English (Zimbabwe) + else if ( wsCountry == L"PH" ) + usSubLang = SUBLANG_ENGLISH_PHILIPPINES; // English (Philippines) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"es" ) + { + usPrimaryLang = LANG_SPANISH; //Spanish + if ( wsCountry == L"MX" ) + usSubLang = SUBLANG_SPANISH_MEXICAN; // Spanish (Mexican) + else if ( wsCountry == L"ES" ) + usSubLang = SUBLANG_SPANISH_MODERN; // Spanish (Spain) + else if ( wsCountry == L"GT" ) + usSubLang = SUBLANG_SPANISH_GUATEMALA; // Spanish (Guatemala) + else if ( wsCountry == L"CR" ) + usSubLang = SUBLANG_SPANISH_COSTA_RICA; // Spanish (Costa Rica) + else if ( wsCountry == L"PA" ) + usSubLang = SUBLANG_SPANISH_PANAMA; // Spanish (Panama) + else if ( wsCountry == L"DO" ) + usSubLang = SUBLANG_SPANISH_DOMINICAN_REPUBLIC; // Spanish (Dominican Republic) + else if ( wsCountry == L"VE" ) + usSubLang = SUBLANG_SPANISH_VENEZUELA; // Spanish (Venezuela) + else if ( wsCountry == L"CO" ) + usSubLang = SUBLANG_SPANISH_COLOMBIA; // Spanish (Colombia) + else if ( wsCountry == L"PE" ) + usSubLang = SUBLANG_SPANISH_PERU; // Spanish (Peru) + else if ( wsCountry == L"AR" ) + usSubLang = SUBLANG_SPANISH_ARGENTINA; // Spanish (Argentina) + else if ( wsCountry == L"EC" ) + usSubLang = SUBLANG_SPANISH_ECUADOR; // Spanish (Ecuador) + else if ( wsCountry == L"CL" ) + usSubLang = SUBLANG_SPANISH_CHILE; // Spanish (Chile) + else if ( wsCountry == L"UY" ) + usSubLang = SUBLANG_SPANISH_URUGUAY; // Spanish (Uruguay) + else if ( wsCountry == L"PY" ) + usSubLang = SUBLANG_SPANISH_PARAGUAY; // Spanish (Paraguay) + else if ( wsCountry == L"BO" ) + usSubLang = SUBLANG_SPANISH_BOLIVIA; // Spanish (Bolivia) + else if ( wsCountry == L"SV" ) + usSubLang = SUBLANG_SPANISH_EL_SALVADOR; // Spanish (El Salvador) + else if ( wsCountry == L"HN" ) + usSubLang = SUBLANG_SPANISH_HONDURAS; // Spanish (Honduras) + else if ( wsCountry == L"NI" ) + usSubLang = SUBLANG_SPANISH_NICARAGUA; // Spanish (Nicaragua) + else if ( wsCountry == L"PR" ) + usSubLang = SUBLANG_SPANISH_PUERTO_RICO; // Spanish (Puerto Rico) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"fi" ) + usPrimaryLang = LANG_FINNISH; //Finnish + else if ( wsLanguage == L"fr" ) + { + usPrimaryLang = LANG_FRENCH; //French + if ( wsCountry == L"FR" ) + usSubLang = SUBLANG_FRENCH; // French + else if ( wsCountry == L"BE" ) + usSubLang = SUBLANG_FRENCH_BELGIAN; // French (Belgian) + else if ( wsCountry == L"CA" ) + usSubLang = SUBLANG_FRENCH_CANADIAN; // French (Canadian) + else if ( wsCountry == L"CH" ) + usSubLang = SUBLANG_FRENCH_SWISS; // French (Swiss) + else if ( wsCountry == L"LU" ) + usSubLang = SUBLANG_FRENCH_LUXEMBOURG; // French (Luxembourg) + else if ( wsCountry == L"MC" ) + usSubLang = SUBLANG_FRENCH_MONACO; // French (Monaco) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"iw" ) + usPrimaryLang = LANG_HEBREW; //Hebrew + else if ( wsLanguage == L"hu" ) + usPrimaryLang = LANG_HUNGARIAN; //Hungarian + else if ( wsLanguage == L"is" ) + usPrimaryLang = LANG_ICELANDIC; //Icelandic + else if ( wsLanguage == L"it" ) + { + usPrimaryLang = LANG_ITALIAN; //Italian + if ( wsCountry == L"IT" ) + usSubLang = SUBLANG_ITALIAN; // Italian + else if ( wsCountry == L"CH" ) + usSubLang = SUBLANG_ITALIAN_SWISS; // Italian (Swiss) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"ja" ) + usPrimaryLang = LANG_JAPANESE; //Japanese + else if ( wsLanguage == L"ko" ) + { + usPrimaryLang = LANG_KOREAN; //Korean + if ( wsCountry == L"KR" ) + usSubLang = SUBLANG_KOREAN; // Korean + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"nl" ) + { + usPrimaryLang = LANG_DUTCH; //Dutch + if ( wsCountry == L"NL" ) + usSubLang = SUBLANG_DUTCH; // Dutch + else if ( wsCountry == L"BE" ) + usSubLang = SUBLANG_DUTCH_BELGIAN; // Dutch (Belgian) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"no" ) + { + usPrimaryLang = LANG_NORWEGIAN; //Norwegian + if ( wsCountry == L"NO" ) + usSubLang = SUBLANG_NORWEGIAN_BOKMAL; // Norwegian (Bokmal) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"pl" ) + usPrimaryLang = LANG_POLISH; //Polish + else if ( wsLanguage == L"pt" ) + { + usPrimaryLang = LANG_PORTUGUESE; //Portuguese + if ( wsCountry == L"BR" ) + usSubLang = SUBLANG_PORTUGUESE_BRAZILIAN; // Portuguese (Brazil) + else if ( wsCountry == L"PT" ) + usSubLang = SUBLANG_PORTUGUESE; // Portuguese (Portugal) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"ro" ) + usPrimaryLang = LANG_ROMANIAN; //Romanian + else if ( wsLanguage == L"ru" ) + usPrimaryLang = LANG_RUSSIAN; //Russian + else if ( wsLanguage == L"hr" ) + usPrimaryLang = LANG_CROATIAN; //Croatian + else if ( wsLanguage == L"sr" ) + { + usPrimaryLang = LANG_SERBIAN; //Serbian + if ( wsCountry == L"VA" ) + usSubLang = SUBLANG_SERBIAN_LATIN; // Serbian (Latin) + else if ( wsCountry == L"HR" ) + usSubLang = SUBLANG_SERBIAN_CYRILLIC; // Serbian (Cyrillic) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"sk" ) + usPrimaryLang = LANG_SLOVAK; //Slovak + else if ( wsLanguage == L"sq" ) + usPrimaryLang = LANG_ALBANIAN; //Albanian + else if ( wsLanguage == L"sv" ) + { + usPrimaryLang = LANG_SWEDISH; //Swedish + if ( wsCountry == L"SE" ) + usSubLang = SUBLANG_SWEDISH; // Swedish + else if ( wsCountry == L"FI" ) + usSubLang = SUBLANG_SWEDISH_FINLAND; // Swedish (Finland) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"th" ) + usPrimaryLang = LANG_THAI; //Thai + else if ( wsLanguage == L"tr" ) + usPrimaryLang = LANG_TURKISH; //Turkish + else if ( wsLanguage == L"ur" ) + { + usPrimaryLang = LANG_URDU; //Urdu + if ( wsCountry == L"PK" ) + usSubLang = SUBLANG_URDU_PAKISTAN; // Urdu (Pakistan) + else if ( wsCountry == L"IN" ) + usSubLang = SUBLANG_URDU_INDIA; // Urdu (India) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"in" ) + usPrimaryLang = LANG_INDONESIAN; //Indonesian + else if ( wsLanguage == L"uk" ) + usPrimaryLang = LANG_UKRAINIAN; //Ukrainian + else if ( wsLanguage == L"be" ) + usPrimaryLang = LANG_BELARUSIAN; //Belarusian + else if ( wsLanguage == L"sl" ) + usPrimaryLang = LANG_SLOVENIAN; //Slovenian + else if ( wsLanguage == L"et" ) + usPrimaryLang = LANG_ESTONIAN; //Estonian + else if ( wsLanguage == L"lv" ) + usPrimaryLang = LANG_LATVIAN; //Latvian + else if ( wsLanguage == L"lt" ) + { + usPrimaryLang = LANG_LITHUANIAN; //Lithuanian + if ( wsCountry == L"LT" ) + usSubLang = SUBLANG_LITHUANIAN; // Lithuanian + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"fa" ) + usPrimaryLang = LANG_FARSI; //Farsi + else if ( wsLanguage == L"vi" ) + usPrimaryLang = LANG_VIETNAMESE; //Vietnamese + else if ( wsLanguage == L"hy" ) + usPrimaryLang = LANG_ARMENIAN; //Armenian + else if ( wsLanguage == L"az" ) + usPrimaryLang = LANG_AZERI; //Azeri + else if ( wsLanguage == L"eu" ) + usPrimaryLang = LANG_BASQUE; //Basque + else if ( wsLanguage == L"mk" ) + usPrimaryLang = LANG_MACEDONIAN; //FYRO Macedonian + else if ( wsLanguage == L"af" ) + usPrimaryLang = LANG_AFRIKAANS; //Afrikaans + else if ( wsLanguage == L"ka" ) + usPrimaryLang = LANG_GEORGIAN; //Georgian + else if ( wsLanguage == L"fo" ) + usPrimaryLang = LANG_FAEROESE; //Faeroese + else if ( wsLanguage == L"hi" ) + usPrimaryLang = LANG_HINDI; //Hindi + else if ( wsLanguage == L"ms" ) + { + usPrimaryLang = LANG_MALAY; //Malay + if ( wsCountry == L"MY" ) + usSubLang = SUBLANG_MALAY_MALAYSIA; // Malay (Malaysia) + else if ( wsCountry == L"BN" ) + usSubLang = SUBLANG_MALAY_BRUNEI_DARUSSALAM; // Malay (Brunei Darassalam) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"kk" ) + usPrimaryLang = LANG_KAZAK; //Kazakh + else if ( wsLanguage == L"ky" ) + usPrimaryLang = LANG_KYRGYZ; //Kyrgyz + else if ( wsLanguage == L"sw" ) + usPrimaryLang = LANG_SWAHILI; //Swahili + else if ( wsLanguage == L"uz" ) + { + usPrimaryLang = LANG_UZBEK; //Uzbek + if ( wsCountry == L"UZ" ) + usSubLang = SUBLANG_UZBEK_LATIN; // Uzbek (Latin) + else if ( wsCountry == L"DE" ) + usSubLang = SUBLANG_UZBEK_CYRILLIC; // Uzbek (Cyrillic) + else + usSubLang = SUBLANG_DEFAULT; //default sub language + } + else if ( wsLanguage == L"tt" ) + usPrimaryLang = LANG_TATAR; //Tatar + else if ( wsLanguage == L"bn" ) + usPrimaryLang = LANG_BENGALI; //Not supported. + else if ( wsLanguage == L"pa" ) + usPrimaryLang = LANG_PUNJABI; //Punjabi + else if ( wsLanguage == L"gu" ) + usPrimaryLang = LANG_GUJARATI; //Gujarati + else if ( wsLanguage == L"or" ) + usPrimaryLang = LANG_ORIYA; //Not supported. + else if ( wsLanguage == L"ta" ) + usPrimaryLang = LANG_TAMIL; //Tamil + else if ( wsLanguage == L"te" ) + usPrimaryLang = LANG_TELUGU; //Telugu + else if ( wsLanguage == L"kn" ) + usPrimaryLang = LANG_KANNADA; //Kannada + else if ( wsLanguage == L"ml" ) + usPrimaryLang = LANG_MALAYALAM; //Not supported. + else if ( wsLanguage == L"as" ) + usPrimaryLang = LANG_ASSAMESE; //Not supported. + else if ( wsLanguage == L"mr" ) + usPrimaryLang = LANG_MARATHI; //Marathi + else if ( wsLanguage == L"sa" ) + usPrimaryLang = LANG_SANSKRIT; //Sanskrit + else if ( wsLanguage == L"mn" ) + usPrimaryLang = LANG_MONGOLIAN; //Mongolian + else if ( wsLanguage == L"gl" ) + usPrimaryLang = LANG_GALICIAN; //Galician + else if ( wsLanguage == L"sd" ) + usPrimaryLang = LANG_SINDHI; //Not supported. + else if ( wsLanguage == L"ks" ) + usPrimaryLang = LANG_KASHMIRI; //Not supported. + else if ( wsLanguage == L"ne" ) + usPrimaryLang = LANG_NEPALI; //Not supported. + else + return GetSystemDefaultLCID(); //System Default Locale + + return MAKELCID( MAKELANGID( usPrimaryLang, usSubLang ), SORT_DEFAULT ); +} + +// The function is defined in the static library, and thus its address is local to current module +HMODULE GetCurrentModuleHandle() +{ + HMODULE h{}; + + if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<LPCWSTR>(&GetCurrentModuleHandle), &h) + == 0) + { + const DWORD dwError = GetLastError(); + OutputDebugStringFormatW( + L"GetCurrentModuleHandle: GetModuleHandleExW failed, error is 0x%X", dwError); + } + return h; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/senddoc.cxx b/shell/source/win32/simplemail/senddoc.cxx new file mode 100644 index 000000000..e34412cbf --- /dev/null +++ b/shell/source/win32/simplemail/senddoc.cxx @@ -0,0 +1,474 @@ +/* -*- 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 <osl/diagnose.h> +#include <sal/macros.h> + +#include <o3tl/char16_t2wchar_t.hxx> +#include <rtl/bootstrap.hxx> +#include <sfx2/strings.hrc> +#include <unotools/resmgr.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <wchar.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mapi.h> +#include <MapiUnicodeHelp.h> + +#include <string> +#include <vector> +#if OSL_DEBUG_LEVEL > 0 +#include <sstream> +#endif +#include <stdexcept> + +#if OSL_DEBUG_LEVEL > 0 + static void dumpParameter(); +#endif + +typedef std::vector<MapiRecipDescW> MapiRecipientList_t; +typedef std::vector<MapiFileDescW> MapiAttachmentList_t; + +const int LEN_SMTP_PREFIX = 5; // "SMTP:" + +namespace /* private */ +{ + OUString gLangTag; + OUString gBootstrap; + std::wstring gFrom; + std::wstring gSubject; + std::wstring gBody; + std::vector<std::wstring> gTo; + std::vector<std::wstring> gCc; + std::vector<std::wstring> gBcc; + // Keep temp filepath, displayed name, and "do not delete" flag + std::vector<std::tuple<std::wstring, std::wstring, bool>> gAttachments; + int gMapiFlags = 0; +} + +/** + Add a prefix to an email address. MAPI requires that + email addresses have an 'SMTP:' prefix. + + @param aEmailAddress + [in] the email address. + + @param aPrefix + [in] the prefix to be added to the email address. + + @returns + the email address prefixed with the specified prefix. +*/ +static std::wstring prefixEmailAddress( + const std::wstring& aEmailAddress, + const std::wstring& aPrefix = L"SMTP:") +{ + return (aPrefix + aEmailAddress); +} + +/** @internal */ +static void addRecipient( + ULONG recipClass, + const std::wstring& recipAddress, + MapiRecipientList_t* pMapiRecipientList) +{ + MapiRecipDescW mrd; + ZeroMemory(&mrd, sizeof(mrd)); + + mrd.ulRecipClass = recipClass; + mrd.lpszName = const_cast<wchar_t*>(recipAddress.c_str()) + LEN_SMTP_PREFIX; + mrd.lpszAddress = const_cast<wchar_t*>(recipAddress.c_str()); + pMapiRecipientList->push_back(mrd); +} + +/** @internal */ +static void initRecipientList(MapiRecipientList_t* pMapiRecipientList) +{ + OSL_ASSERT(pMapiRecipientList->empty()); + + // add to recipients + for (const auto& address : gTo) + addRecipient(MAPI_TO, address, pMapiRecipientList); + + // add cc recipients + for (const auto& address : gCc) + addRecipient(MAPI_CC, address, pMapiRecipientList); + + // add bcc recipients + for (const auto& address : gBcc) + addRecipient(MAPI_BCC, address, pMapiRecipientList); +} + +/** @internal */ +static void initAttachmentList(MapiAttachmentList_t* pMapiAttachmentList) +{ + OSL_ASSERT(pMapiAttachmentList->empty()); + + for (const auto& [filepath, attachname, nodelete] : gAttachments) + { + (void)nodelete; + MapiFileDescW mfd; + ZeroMemory(&mfd, sizeof(mfd)); + mfd.lpszPathName = const_cast<wchar_t*>(filepath.c_str()); + // MapiFileDesc documentation (https://msdn.microsoft.com/en-us/library/hh707272) + // allows using here either nullptr, or a pointer to empty string. However, + // for Outlook 2013, we cannot use nullptr here, and must point to a (possibly + // empty) string: otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE. + // See http://peach.ease.lsoft.com/scripts/wa-PEACH.exe?A2=MAPI-L;d2bf3060.1604 + // Since C++11, c_str() must return a pointer to single null character when the + // string is empty, so we are OK here in case when there's no explicit file name + // passed + mfd.lpszFileName = const_cast<wchar_t*>(attachname.c_str()); + mfd.nPosition = sal::static_int_cast<ULONG>(-1); + pMapiAttachmentList->push_back(mfd); + } +} + +/** @internal */ +static void initMapiOriginator(MapiRecipDescW* pMapiOriginator) +{ + ZeroMemory(pMapiOriginator, sizeof(*pMapiOriginator)); + + pMapiOriginator->ulRecipClass = MAPI_ORIG; + pMapiOriginator->lpszName = const_cast<wchar_t*>(L""); + pMapiOriginator->lpszAddress = const_cast<wchar_t*>(gFrom.c_str()); +} + +/** @internal */ +static void initMapiMessage( + MapiRecipDescW* aMapiOriginator, + MapiRecipientList_t& aMapiRecipientList, + MapiAttachmentList_t& aMapiAttachmentList, + MapiMessageW* pMapiMessage) +{ + ZeroMemory(pMapiMessage, sizeof(*pMapiMessage)); + + pMapiMessage->lpszSubject = const_cast<wchar_t*>(gSubject.c_str()); + pMapiMessage->lpszNoteText = (gBody.length() ? const_cast<wchar_t*>(gBody.c_str()) : nullptr); + pMapiMessage->lpOriginator = aMapiOriginator; + pMapiMessage->lpRecips = aMapiRecipientList.size() ? aMapiRecipientList.data() : nullptr; + pMapiMessage->nRecipCount = aMapiRecipientList.size(); + if (!aMapiAttachmentList.empty()) + pMapiMessage->lpFiles = aMapiAttachmentList.data(); + pMapiMessage->nFileCount = aMapiAttachmentList.size(); +} + +const wchar_t* const KnownParameters[] = +{ + L"--to", + L"--cc", + L"--bcc", + L"--from", + L"--subject", + L"--body", + L"--attach", + L"--mapi-dialog", + L"--mapi-logon-ui", + L"--langtag", + L"--bootstrap", +}; + +/** @internal */ +static bool isKnownParameter(const wchar_t* aParameterName) +{ + for (const wchar_t* KnownParameter : KnownParameters) + if (_wcsicmp(aParameterName, KnownParameter) == 0) + return true; + + return false; +} + +/** @internal */ +static void initParameter(int argc, wchar_t* argv[]) +{ + for (int i = 1; i < argc; i++) + { + if (!isKnownParameter(argv[i])) + { + OSL_FAIL("Wrong parameter received"); + continue; + } + + if (_wcsicmp(argv[i], L"--mapi-dialog") == 0) + { + // MAPI_DIALOG_MODELESS has many problems and crashes Outlook 2016. + // see the commit message for a lengthy description. + gMapiFlags |= MAPI_DIALOG; + } + else if (_wcsicmp(argv[i], L"--mapi-logon-ui") == 0) + { + gMapiFlags |= MAPI_LOGON_UI; + } + else if ((i+1) < argc) // is the value of a parameter available too? + { + if (_wcsicmp(argv[i], L"--to") == 0) + gTo.push_back(prefixEmailAddress(argv[i+1])); + else if (_wcsicmp(argv[i], L"--cc") == 0) + gCc.push_back(prefixEmailAddress(argv[i+1])); + else if (_wcsicmp(argv[i], L"--bcc") == 0) + gBcc.push_back(prefixEmailAddress(argv[i+1])); + else if (_wcsicmp(argv[i], L"--from") == 0) + gFrom = prefixEmailAddress(argv[i+1]); + else if (_wcsicmp(argv[i], L"--subject") == 0) + gSubject = argv[i+1]; + else if (_wcsicmp(argv[i], L"--body") == 0) + gBody = argv[i+1]; + else if (_wcsicmp(argv[i], L"--attach") == 0) + { + std::wstring sPath(argv[i + 1]); + // An attachment may optionally be immediately followed by --attach-name and user-visible name + std::wstring sName; + if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0) + { + sName = argv[i+3]; + i += 2; + } + // Also there may be --nodelete to keep the attachment on exit + bool nodelete = false; + if ((i + 2) < argc && _wcsicmp(argv[i+2], L"--nodelete") == 0) + { + nodelete = true; + ++i; + } + gAttachments.emplace_back(sPath, sName, nodelete); + } + else if (_wcsicmp(argv[i], L"--langtag") == 0) + gLangTag = o3tl::toU(argv[i+1]); + else if (_wcsicmp(argv[i], L"--bootstrap") == 0) + gBootstrap = o3tl::toU(argv[i+1]); + + i++; + } + } +} + +static void ShowError(ULONG nMAPIResult) +{ + if (!gBootstrap.isEmpty()) + rtl::Bootstrap::setIniFilename(gBootstrap); + LanguageTag aLangTag(gLangTag); + std::locale aLocale = Translate::Create("sfx", aLangTag); + OUString sMessage = Translate::get(STR_ERROR_SEND_MAIL_CODE, aLocale); + OUString sErrorId; + switch (nMAPIResult) + { + case MAPI_E_FAILURE: + sErrorId = "MAPI_E_FAILURE"; + break; + case MAPI_E_LOGON_FAILURE: + sErrorId = "MAPI_E_LOGON_FAILURE"; + break; + case MAPI_E_DISK_FULL: + sErrorId = "MAPI_E_DISK_FULL"; + break; + case MAPI_E_INSUFFICIENT_MEMORY: + sErrorId = "MAPI_E_INSUFFICIENT_MEMORY"; + break; + case MAPI_E_ACCESS_DENIED: + sErrorId = "MAPI_E_ACCESS_DENIED"; + break; + case MAPI_E_TOO_MANY_SESSIONS: + sErrorId = "MAPI_E_ACCESS_DENIED"; + break; + case MAPI_E_TOO_MANY_FILES: + sErrorId = "MAPI_E_TOO_MANY_FILES"; + break; + case MAPI_E_TOO_MANY_RECIPIENTS: + sErrorId = "MAPI_E_TOO_MANY_RECIPIENTS"; + break; + case MAPI_E_ATTACHMENT_NOT_FOUND: + sErrorId = "MAPI_E_ATTACHMENT_NOT_FOUND"; + break; + case MAPI_E_ATTACHMENT_OPEN_FAILURE: + sErrorId = "MAPI_E_ATTACHMENT_OPEN_FAILURE"; + break; + case MAPI_E_ATTACHMENT_WRITE_FAILURE: + sErrorId = "MAPI_E_ATTACHMENT_WRITE_FAILURE"; + break; + case MAPI_E_UNKNOWN_RECIPIENT: + sErrorId = "MAPI_E_UNKNOWN_RECIPIENT"; + break; + case MAPI_E_BAD_RECIPTYPE: + sErrorId = "MAPI_E_BAD_RECIPTYPE"; + break; + case MAPI_E_NO_MESSAGES: + sErrorId = "MAPI_E_NO_MESSAGES"; + break; + case MAPI_E_INVALID_MESSAGE: + sErrorId = "MAPI_E_INVALID_MESSAGE"; + break; + case MAPI_E_TEXT_TOO_LARGE: + sErrorId = "MAPI_E_TEXT_TOO_LARGE"; + break; + case MAPI_E_INVALID_SESSION: + sErrorId = "MAPI_E_INVALID_SESSION"; + break; + case MAPI_E_TYPE_NOT_SUPPORTED: + sErrorId = "MAPI_E_TYPE_NOT_SUPPORTED"; + break; + case MAPI_E_AMBIGUOUS_RECIPIENT: + sErrorId = "MAPI_E_AMBIGUOUS_RECIPIENT"; + break; + case MAPI_E_MESSAGE_IN_USE: + sErrorId = "MAPI_E_MESSAGE_IN_USE"; + break; + case MAPI_E_NETWORK_FAILURE: + sErrorId = "MAPI_E_NETWORK_FAILURE"; + break; + case MAPI_E_INVALID_EDITFIELDS: + sErrorId = "MAPI_E_INVALID_EDITFIELDS"; + break; + case MAPI_E_INVALID_RECIPS: + sErrorId = "MAPI_E_INVALID_RECIPS"; + break; + case MAPI_E_NOT_SUPPORTED: + sErrorId = "MAPI_E_NOT_SUPPORTED"; + break; + case MAPI_E_UNICODE_NOT_SUPPORTED: + sErrorId = "MAPI_E_UNICODE_NOT_SUPPORTED"; + break; + default: + sErrorId = OUString::number(nMAPIResult); + } + sMessage = sMessage.replaceAll("$1", sErrorId); + OUString sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER, aLocale)); + + MessageBoxW(nullptr, o3tl::toW(sMessage.getStr()), o3tl::toW(sTitle.getStr()), + MB_OK | MB_ICONINFORMATION); +} + +/** + Main. + NOTE: Because this is program only serves implementation + purposes and should not be used by any end user the + parameter checking is very limited. Every unknown parameter + will be ignored. +*/ +int wmain(int argc, wchar_t* argv[]) +{ + + initParameter(argc, argv); + +#if OSL_DEBUG_LEVEL > 0 + dumpParameter(); +#endif + + ULONG ulRet = MAPI_E_FAILURE; + + try + { + LHANDLE const hSession = 0; + + MapiRecipDescW mapiOriginator; + MapiRecipientList_t mapiRecipientList; + MapiAttachmentList_t mapiAttachmentList; + MapiMessageW mapiMsg; + + initMapiOriginator(&mapiOriginator); + initRecipientList(&mapiRecipientList); + initAttachmentList(&mapiAttachmentList); + initMapiMessage((gFrom.length() ? &mapiOriginator : nullptr), mapiRecipientList, mapiAttachmentList, &mapiMsg); + + ulRet = MAPISendMailHelper(hSession, 0, &mapiMsg, gMapiFlags, 0); + + // There is no point in treating an aborted mail sending + // dialog as an error to be returned as our exit + // status. If the user decided to abort sending a document + // as mail, OK, that is not an error. + + // Also, it seems that GroupWise makes MAPISendMail() + // return MAPI_E_USER_ABORT even if the mail sending + // dialog was not aborted by the user, and the mail was + // actually sent just fine. See bnc#660241 (visible to + // Novell people only, sorry). + + if (ulRet == MAPI_E_USER_ABORT) + ulRet = SUCCESS_SUCCESS; + + } + catch (const std::runtime_error& ex) + { + OSL_FAIL(ex.what()); + } + + // Now cleanup the temporary attachment files + for (const auto& [filepath, attachname, nodelete] : gAttachments) + { + (void)attachname; + if (!nodelete) + DeleteFileW(filepath.c_str()); + } + + // Only show the error message if UI was requested + if ((ulRet != SUCCESS_SUCCESS) && (gMapiFlags & (MAPI_DIALOG | MAPI_LOGON_UI))) + ShowError(ulRet); + + return ulRet; +} + +#if OSL_DEBUG_LEVEL > 0 + void dumpParameter() + { + std::wostringstream oss; + + if (gFrom.length() > 0) + oss << "--from " << gFrom << std::endl; + + if (gSubject.length() > 0) + oss << "--subject " << gSubject << std::endl; + + if (gBody.length() > 0) + oss << "--body " << gBody << std::endl; + + for (const auto& address : gTo) + oss << "--to " << address << std::endl; + + for (const auto& address : gCc) + oss << "--cc " << address << std::endl; + + for (const auto& address : gBcc) + oss << "--bcc " << address << std::endl; + + for (const auto& [filepath, attachname, nodelete] : gAttachments) + { + oss << "--attach " << filepath << std::endl; + if (!attachname.empty()) + oss << "--attach-name " << attachname << std::endl; + if (nodelete) + oss << "--nodelete" << std::endl; + } + + if (gMapiFlags & MAPI_DIALOG) + oss << "--mapi-dialog" << std::endl; + + if (gMapiFlags & MAPI_LOGON_UI) + oss << "--mapi-logon-ui" << std::endl; + + if (!gLangTag.isEmpty()) + oss << "--langtag " << gLangTag << std::endl; + + if (!gBootstrap.isEmpty()) + oss << "--bootstrap " << gBootstrap << std::endl; + + MessageBoxW(nullptr, oss.str().c_str(), L"Arguments", MB_OK | MB_ICONINFORMATION); + } +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmail.component b/shell/source/win32/simplemail/smplmail.component new file mode 100644 index 000000000..32446884d --- /dev/null +++ b/shell/source/win32/simplemail/smplmail.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sys.shell.SimpleSystemMail" + constructor="shell_CSmplMailSuppl_get_implementation" single-instance="true"> + <service name="com.sun.star.system.SimpleSystemMail"/> + </implementation> +</component> diff --git a/shell/source/win32/simplemail/smplmailclient.cxx b/shell/source/win32/simplemail/smplmailclient.cxx new file mode 100644 index 000000000..8e85ca086 --- /dev/null +++ b/shell/source/win32/simplemail/smplmailclient.cxx @@ -0,0 +1,389 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/process.h> +#include <rtl/bootstrap.hxx> +#include "smplmailclient.hxx" +#include "smplmailmsg.hxx" +#include <com/sun/star/system/SimpleMailClientFlags.hpp> +#include <com/sun/star/system/XSimpleMailMessage2.hpp> +#include <osl/file.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <tools/urlobj.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mapi.h> +#if defined GetTempPath +#undef GetTempPath +#endif + +#include <process.h> +#include <vector> + +using css::uno::UNO_QUERY; +using css::uno::Reference; +using css::uno::Exception; +using css::uno::RuntimeException; +using css::uno::Sequence; +using css::lang::IllegalArgumentException; + +using css::system::XSimpleMailClient; +using css::system::XSimpleMailMessage; +using css::system::XSimpleMailMessage2; +using css::system::SimpleMailClientFlags::NO_USER_INTERFACE; +using css::system::SimpleMailClientFlags::NO_LOGON_DIALOG; + +namespace /* private */ +{ + /** @internal + look if an alternative program is configured + which should be used as senddoc executable */ + OUString getAlternativeSenddocUrl() + { + OUString altSenddocUrl; + HKEY hkey; + LONG lret = RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\LibreOffice\\SendAsEMailClient", &hkey); + if (lret == ERROR_SUCCESS) + { + wchar_t buff[MAX_PATH]; + LONG sz = sizeof(buff); + lret = RegQueryValueW(hkey, nullptr, buff, &sz); + if (lret == ERROR_SUCCESS) + { + osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(buff)), altSenddocUrl); + } + RegCloseKey(hkey); + } + return altSenddocUrl; + } + + /** + Returns the absolute file Url of the senddoc executable. + + @returns + the absolute file Url of the senddoc executable. In case + of an error an empty string will be returned. + */ + OUString getSenddocUrl() + { + OUString senddocUrl = getAlternativeSenddocUrl(); + + if (senddocUrl.isEmpty()) + { + senddocUrl = "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/senddoc.exe"; + rtl::Bootstrap::expandMacros(senddocUrl); //TODO: detect failure + } + return senddocUrl; + } + + /** + Execute Senddoc.exe which a MAPI wrapper. + + @param rCommandArgs + [in] the arguments to be passed to Senddoc.exe + + @returns + <TRUE/> on success. + */ + bool executeSenddoc(const std::vector<OUString>& rCommandArgs, bool bWait) + { + OUString senddocUrl = getSenddocUrl(); + if (senddocUrl.getLength() == 0) + return false; + + oslProcessOption nProcOption = osl_Process_DETACHED | (bWait ? osl_Process_WAIT : 0); + + oslProcess proc; + + /* for efficiency reasons we are using a 'bad' cast here + as a vector or OUStrings is nothing else than + an array of pointers to rtl_uString's */ + oslProcessError err = osl_executeProcess( + senddocUrl.pData, + const_cast<rtl_uString**>(reinterpret_cast<rtl_uString * const *>(rCommandArgs.data())), + rCommandArgs.size(), + nProcOption, + nullptr, + nullptr, + nullptr, + 0, + &proc); + + if (err != osl_Process_E_None) + return false; + + if (!bWait) + return true; + + oslProcessInfo procInfo; + procInfo.Size = sizeof(oslProcessInfo); + osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo); + osl_freeProcessHandle(proc); + return (procInfo.Code == SUCCESS_SUCCESS); + } +} // namespace private + +Reference<XSimpleMailMessage> SAL_CALL CSmplMailClient::createSimpleMailMessage() +{ + return Reference<XSimpleMailMessage>(new CSmplMailMsg()); +} + +namespace { +// We cannot use the session-local temporary directory for the attachment, +// because it will get removed upon program exit; and it must be alive for +// senddoc process lifetime. So we use base temppath for the attachments, +// and let the senddoc to do the cleanup if it was started successfully. +// This function works like Desktop::CreateTemporaryDirectory() +OUString InitBaseTempDirURL() +{ + // No need to intercept an exception here, since + // Desktop::CreateTemporaryDirectory() has ensured that path manager is available + SvtPathOptions aOpt; + OUString aRetURL = aOpt.GetTempPath(); + if (aRetURL.isEmpty()) + { + osl::File::getTempDirURL(aRetURL); + } + if (aRetURL.endsWith("/")) + aRetURL = aRetURL.copy(0, aRetURL.getLength() - 1); + + return aRetURL; +} + +const OUString& GetBaseTempDirURL() +{ + static const OUString aRetURL(InitBaseTempDirURL()); + return aRetURL; +} +} + +OUString CSmplMailClient::CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName, + bool& nodelete) +{ + // We do two things here: + // 1. Make the attachment temporary filename to not contain any fancy characters possible in + // original filename, that could confuse mailer, and extract the original filename to explicitly + // define it; + // 2. Allow the copied files be outside of the session's temporary directory, and thus not be + // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the + // mailer finishes using them. + + maAttachmentFiles.emplace_back(std::make_unique<utl::TempFile>(&GetBaseTempDirURL())); + maAttachmentFiles.back()->EnableKillingFile(); + INetURLObject aFilePathObj(maAttachmentFiles.back()->GetURL()); + OUString sNewAttachmentURL = aFilePathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE); + OUString sCorrectedOrigAttachURL(sOrigAttachURL); + // Make sure to convert to URL, if a system path was passed to XSimpleMailMessage + // Ignore conversion error, in which case sCorrectedOrigAttachURL is unchanged + osl::FileBase::getFileURLFromSystemPath(sCorrectedOrigAttachURL, sCorrectedOrigAttachURL); + if (osl::File::copy(sCorrectedOrigAttachURL, sNewAttachmentURL) == osl::FileBase::RC::E_None) + { + INetURLObject url(sCorrectedOrigAttachURL, INetURLObject::EncodeMechanism::WasEncoded); + sUserVisibleName = url.getName(INetURLObject::LAST_SEGMENT, true, + INetURLObject::DecodeMechanism::WithCharset); + nodelete = false; + } + else + { + // Failed to copy original; the best effort is to use original file. It is possible that + // the file gets deleted before used in spawned process; but let's hope... the worst thing + // is the absent attachment file anyway. + sNewAttachmentURL = sOrigAttachURL; + maAttachmentFiles.pop_back(); + nodelete = true; // Do not delete a non-temporary in senddoc + } + return sNewAttachmentURL; +} + +void CSmplMailClient::ReleaseAttachments() +{ + for (auto& pTempFile : maAttachmentFiles) + { + if (pTempFile) + pTempFile->EnableKillingFile(false); + } + maAttachmentFiles.clear(); +} + +/** + Assemble a command line for SendDoc.exe out of the members + of the supplied SimpleMailMessage. + + @param xSimpleMailMessage + [in] the mail message. + + @param aFlags + [in] different flags to be used with the simple mail service. + + @param rCommandArgs + [in|out] a buffer for the command line arguments. The buffer + is assumed to be empty. + + @throws css::lang::IllegalArgumentException + if an invalid file URL has been detected in the attachment list. +*/ +void CSmplMailClient::assembleCommandLine( + const Reference<XSimpleMailMessage>& xSimpleMailMessage, + sal_Int32 aFlag, std::vector<OUString>& rCommandArgs) +{ + OSL_ENSURE(rCommandArgs.empty(), "Provided command argument buffer not empty"); + + Reference<XSimpleMailMessage2> xMessage( xSimpleMailMessage, UNO_QUERY ); + if (xMessage.is()) + { + OUString body = xMessage->getBody(); + if (body.getLength()>0) + { + rCommandArgs.push_back("--body"); + rCommandArgs.push_back(body); + } + } + + OUString to = xSimpleMailMessage->getRecipient(); + if (to.getLength() > 0) + { + rCommandArgs.push_back("--to"); + rCommandArgs.push_back(to); + } + + const Sequence<OUString> ccRecipients = xSimpleMailMessage->getCcRecipient(); + for (OUString const & s : ccRecipients) + { + rCommandArgs.push_back("--cc"); + rCommandArgs.push_back(s); + } + + const Sequence<OUString> bccRecipients = xSimpleMailMessage->getBccRecipient(); + for (OUString const & s : bccRecipients) + { + rCommandArgs.push_back("--bcc"); + rCommandArgs.push_back(s); + } + + OUString from = xSimpleMailMessage->getOriginator(); + if (from.getLength() > 0) + { + rCommandArgs.push_back("--from"); + rCommandArgs.push_back(from); + } + + OUString subject = xSimpleMailMessage->getSubject(); + if (subject.getLength() > 0) + { + rCommandArgs.push_back("--subject"); + rCommandArgs.push_back(subject); + } + + auto const attachments = xSimpleMailMessage->getAttachement(); + for (const auto& attachment : attachments) + { + OUString sDisplayName; + bool nodelete = false; + OUString sTempFileURL(CopyAttachment(attachment, sDisplayName, nodelete)); + OUString sysPath; + osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(sTempFileURL, sysPath); + if (err != osl::FileBase::E_None) + throw IllegalArgumentException( + "Invalid attachment file URL", + static_cast<XSimpleMailClient*>(this), + 1); + + rCommandArgs.push_back("--attach"); + rCommandArgs.push_back(sysPath); + if (!sDisplayName.isEmpty()) + { + rCommandArgs.push_back("--attach-name"); + rCommandArgs.push_back(sDisplayName); + } + if (nodelete) + rCommandArgs.push_back("--nodelete"); + } + + if (!(aFlag & NO_USER_INTERFACE)) + rCommandArgs.push_back("--mapi-dialog"); + + if (!(aFlag & NO_LOGON_DIALOG)) + rCommandArgs.push_back("--mapi-logon-ui"); + + rCommandArgs.push_back("--langtag"); + rCommandArgs.push_back(SvtSysLocale().GetUILanguageTag().getBcp47()); + + rtl::Bootstrap aBootstrap; + OUString sBootstrapPath; + aBootstrap.getIniName(sBootstrapPath); + if (!sBootstrapPath.isEmpty()) + { + rCommandArgs.push_back("--bootstrap"); + rCommandArgs.push_back(sBootstrapPath); + } + +} + +void SAL_CALL CSmplMailClient::sendSimpleMailMessage( + const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag) +{ + validateParameter(xSimpleMailMessage, aFlag); + + std::vector<OUString> senddocParams; + assembleCommandLine(xSimpleMailMessage, aFlag, senddocParams); + + const bool bWait = aFlag & NO_USER_INTERFACE; + if (!executeSenddoc(senddocParams, bWait)) + throw Exception( + "Send email failed", + static_cast<XSimpleMailClient*>(this)); + // Let the launched senddoc to cleanup the attachments temporary files + if (!bWait) + ReleaseAttachments(); +} + +void CSmplMailClient::validateParameter( + const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag ) +{ + if (!xSimpleMailMessage.is()) + throw IllegalArgumentException( + "Empty mail message reference", + static_cast<XSimpleMailClient*>(this), + 1); + + OSL_ENSURE(!(aFlag & NO_LOGON_DIALOG), "Flag NO_LOGON_DIALOG has currently no effect"); + + // check the flags, the allowed range is 0 - (2^n - 1) + if (aFlag < 0 || aFlag > 3) + throw IllegalArgumentException( + "Invalid flag value", + static_cast<XSimpleMailClient*>(this), + 2); + + // check if a recipient is specified of the flags NO_USER_INTERFACE is specified + if ((aFlag & NO_USER_INTERFACE) && !xSimpleMailMessage->getRecipient().getLength()) + throw IllegalArgumentException( + "No recipient specified", + static_cast<XSimpleMailClient*>(this), + 1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmailclient.hxx b/shell/source/win32/simplemail/smplmailclient.hxx new file mode 100644 index 000000000..6f71a1a2a --- /dev/null +++ b/shell/source/win32/simplemail/smplmailclient.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_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILCLIENT_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILCLIENT_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <com/sun/star/system/XSimpleMailClient.hpp> +#include <unotools/tempfile.hxx> +#include <vector> +#include <memory> + +class CSmplMailClient : public cppu::WeakImplHelper<css::system::XSimpleMailClient> +{ +public: + virtual css::uno::Reference<css::system::XSimpleMailMessage> SAL_CALL createSimpleMailMessage() override; + + virtual void SAL_CALL sendSimpleMailMessage(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag) override; + +private: + void validateParameter(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag); + void assembleCommandLine(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag, std::vector<OUString>& rCommandArgs); + OUString CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName, bool& nodelete); + // Don't try to delete the copied attachment files; let the spawned process cleanup them + void ReleaseAttachments(); + + std::vector< std::unique_ptr<utl::TempFile> > maAttachmentFiles; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmailmsg.cxx b/shell/source/win32/simplemail/smplmailmsg.cxx new file mode 100644 index 000000000..7a622b3fe --- /dev/null +++ b/shell/source/win32/simplemail/smplmailmsg.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 . + */ + + +#include <osl/file.h> +#include "smplmailmsg.hxx" + +using com::sun::star::uno::RuntimeException; +using com::sun::star::uno::Sequence; +using com::sun::star::lang::IllegalArgumentException; + +using namespace cppu; + +CSmplMailMsg::CSmplMailMsg( ) +{ +} + +void SAL_CALL CSmplMailMsg::setBody( const OUString& aBody ) +{ + m_aBody = aBody; +} + +OUString SAL_CALL CSmplMailMsg::getBody( ) +{ + return m_aBody; +} + +void SAL_CALL CSmplMailMsg::setRecipient( const OUString& aRecipient ) +{ + m_aRecipient = aRecipient; +} + +OUString SAL_CALL CSmplMailMsg::getRecipient( ) +{ + return m_aRecipient; +} + +void SAL_CALL CSmplMailMsg::setCcRecipient( const Sequence< OUString >& aCcRecipient ) +{ + m_CcRecipients = aCcRecipient; +} + +Sequence< OUString > SAL_CALL CSmplMailMsg::getCcRecipient( ) +{ + return m_CcRecipients; +} + +void SAL_CALL CSmplMailMsg::setBccRecipient( const Sequence< OUString >& aBccRecipient ) +{ + m_BccRecipients = aBccRecipient; +} + +Sequence< OUString > SAL_CALL CSmplMailMsg::getBccRecipient( ) +{ + return m_BccRecipients; +} + +void SAL_CALL CSmplMailMsg::setOriginator( const OUString& aOriginator ) +{ + m_aOriginator = aOriginator; +} + +OUString SAL_CALL CSmplMailMsg::getOriginator( ) +{ + return m_aOriginator; +} + +void SAL_CALL CSmplMailMsg::setSubject( const OUString& aSubject ) +{ + m_aSubject = aSubject; +} + +OUString SAL_CALL CSmplMailMsg::getSubject( ) +{ + return m_aSubject; +} + +void SAL_CALL CSmplMailMsg::setAttachement( const Sequence< OUString >& aAttachement ) +{ + m_Attachements = aAttachement; +} + +Sequence< OUString > SAL_CALL CSmplMailMsg::getAttachement( ) +{ + return m_Attachements; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmailmsg.hxx b/shell/source/win32/simplemail/smplmailmsg.hxx new file mode 100644 index 000000000..87269910b --- /dev/null +++ b/shell/source/win32/simplemail/smplmailmsg.hxx @@ -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 . + */ + +#ifndef INCLUDED_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILMSG_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILMSG_HXX + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/system/XSimpleMailMessage2.hpp> + + + + +class CSmplMailMsg : public cppu::WeakImplHelper< css::system::XSimpleMailMessage2 > +{ +public: + CSmplMailMsg( ); + + virtual void SAL_CALL setBody( const OUString& aBody ) override; + + virtual OUString SAL_CALL getBody( ) override; + + + virtual void SAL_CALL setRecipient( const OUString& aRecipient ) override; + + virtual OUString SAL_CALL getRecipient( ) override; + + + virtual void SAL_CALL setCcRecipient( const css::uno::Sequence< OUString >& aCcRecipient ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getCcRecipient( ) override; + + + virtual void SAL_CALL setBccRecipient( const css::uno::Sequence< OUString >& aBccRecipient ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getBccRecipient( ) override; + + + virtual void SAL_CALL setOriginator( const OUString& aOriginator ) override; + + virtual OUString SAL_CALL getOriginator( ) override; + + + virtual void SAL_CALL setSubject( const OUString& aSubject ) override; + + virtual OUString SAL_CALL getSubject( ) override; + + + virtual void SAL_CALL setAttachement( const css::uno::Sequence< OUString >& aAttachement ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAttachement( ) override; + +private: + OUString m_aBody; + OUString m_aRecipient; + OUString m_aOriginator; + OUString m_aSubject; + css::uno::Sequence< OUString > m_CcRecipients; + css::uno::Sequence< OUString > m_BccRecipients; + css::uno::Sequence< OUString > m_Attachements; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmailsuppl.cxx b/shell/source/win32/simplemail/smplmailsuppl.cxx new file mode 100644 index 000000000..8fbd89b21 --- /dev/null +++ b/shell/source/win32/simplemail/smplmailsuppl.cxx @@ -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 . + */ + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include "smplmailsuppl.hxx" +#include "smplmailclient.hxx" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +using com::sun::star::uno::Reference; +using com::sun::star::uno::RuntimeException; +using com::sun::star::uno::Sequence; +using com::sun::star::lang::XServiceInfo; +using com::sun::star::system::XSimpleMailClientSupplier; +using com::sun::star::system::XSimpleMailClient; + +using namespace cppu; + +CSmplMailSuppl::CSmplMailSuppl() : + WeakComponentImplHelper<XSimpleMailClientSupplier, XServiceInfo>(m_aMutex) +{ +} + +CSmplMailSuppl::~CSmplMailSuppl() +{ +} + +Reference<XSimpleMailClient> SAL_CALL CSmplMailSuppl::querySimpleMailClient() +{ + /* We just try to load the MAPI dll as a test + if a mail client is available */ + Reference<XSimpleMailClient> xSmplMailClient; + HMODULE handle = LoadLibraryW(L"mapi32.dll"); + if ((handle != INVALID_HANDLE_VALUE) && (handle != nullptr)) + { + FreeLibrary(handle); + xSmplMailClient.set(new CSmplMailClient); + } + return xSmplMailClient; +} + +// XServiceInfo + +OUString SAL_CALL CSmplMailSuppl::getImplementationName() +{ + return "com.sun.star.sys.shell.SimpleSystemMail"; +} + +sal_Bool SAL_CALL CSmplMailSuppl::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL CSmplMailSuppl::getSupportedServiceNames() +{ + return { "com.sun.star.system.SimpleSystemMail" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +shell_CSmplMailSuppl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CSmplMailSuppl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/simplemail/smplmailsuppl.hxx b/shell/source/win32/simplemail/smplmailsuppl.hxx new file mode 100644 index 000000000..3c7fbe0f2 --- /dev/null +++ b/shell/source/win32/simplemail/smplmailsuppl.hxx @@ -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 . + */ + +#ifndef INCLUDED_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILSUPPL_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_SIMPLEMAIL_SMPLMAILSUPPL_HXX + +#include <cppuhelper/compbase.hxx> +#include <osl/mutex.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/system/XSimpleMailClientSupplier.hpp> + + +class CSmplMailSupplBase +{ +protected: + osl::Mutex m_aMutex; +}; + +class CSmplMailSuppl : + public CSmplMailSupplBase, + public cppu::WeakComponentImplHelper< + css::system::XSimpleMailClientSupplier, + css::lang::XServiceInfo > +{ +public: + CSmplMailSuppl( ); + ~CSmplMailSuppl( ) override; + + // XSimpleMailClientSupplier + virtual css::uno::Reference< css::system::XSimpleMailClient > SAL_CALL querySimpleMailClient( ) override; + + // 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; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/COMOpenDocuments.cxx b/shell/source/win32/spsupp/COMOpenDocuments.cxx new file mode 100644 index 000000000..87a61691d --- /dev/null +++ b/shell/source/win32/spsupp/COMOpenDocuments.cxx @@ -0,0 +1,424 @@ +/* -*- 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 <cstring> +#include <string> +#include <vector> + +#include <COMOpenDocuments.hpp> +#include <spsuppServ.hpp> +#include <stdio.h> + +namespace +{ +template<class... Args> +HRESULT LOStart(Args... args) +{ + auto quote = [](const std::wstring& s) { return L"\"" + s + L"\""; }; + std::wstring sCmdLine((quote(GetHelperExe()) + ... + (L" " + quote(args)))); + LPWSTR pCmdLine = const_cast<LPWSTR>(sCmdLine.c_str()); + + STARTUPINFOW si = {}; + si.cb = sizeof si; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + PROCESS_INFORMATION pi = {}; + if (!CreateProcessW(nullptr, pCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) + return HRESULT_FROM_WIN32(GetLastError()); + + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD nExitCode; + const bool bGotExitCode = GetExitCodeProcess(pi.hProcess, &nExitCode); + const DWORD nGetExitCodeError = GetLastError(); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (!bGotExitCode) + return HRESULT_FROM_WIN32(nGetExitCodeError); + if (nExitCode == 0) + return S_OK; + if (nExitCode == 1) + return S_FALSE; + return E_FAIL; +} + +VARIANT_BOOL toVBool(bool b) { return b ? VARIANT_TRUE : VARIANT_FALSE; } + +HRESULT ImplCreateNewDocument(IDispatch* /*pdisp*/, BSTR bstrTemplateLocation, + BSTR bstrDefaultSaveLocation, VARIANT_BOOL* pbResult) +{ + HRESULT hr = LOStart(L"CreateNewDocument", bstrTemplateLocation, bstrDefaultSaveLocation); + *pbResult = toVBool(hr == S_OK); + return hr; +} + +HRESULT ImplEditDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation, + VARIANT_BOOL fUseLocalCopy, const VARIANT& varProgID, + VARIANT_BOOL* pbResult) +{ + const wchar_t* sUseLocalCopy = (fUseLocalCopy == VARIANT_FALSE) ? L"0" : L"1"; + const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L""; + HRESULT hr = LOStart(L"EditDocument", bstrDocumentLocation, sUseLocalCopy, sProgId); + *pbResult = toVBool(hr == S_OK); + return hr; +} + +HRESULT ImplViewDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation, int OpenType, + const VARIANT& varProgID, VARIANT_BOOL* pbResult) +{ + wchar_t sOpenType[16]{}; + swprintf(sOpenType, L"%d", OpenType); + const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L""; + HRESULT hr = LOStart(L"ViewDocument", bstrDocumentLocation, sOpenType, sProgId); + *pbResult = toVBool(hr == S_OK); + return hr; +} +} // namespace + +LONG COMOpenDocuments::m_nObjCount = 0; +ITypeInfo* COMOpenDocuments::m_pTypeInfo = nullptr; + +COMOpenDocuments::COMOpenDocuments() +{ + ::InterlockedIncrement(&m_nObjCount); + if (m_pTypeInfo == nullptr) + { + ITypeLib* pITypeLib = GetTypeLib(); + HRESULT hr = pITypeLib->GetTypeInfoOfGuid(__uuidof(IOWSNewDocument3), &m_pTypeInfo); + if (FAILED(hr)) + throw Error(hr); + } +} + +COMOpenDocuments::~COMOpenDocuments() +{ + if (::InterlockedDecrement(&m_nObjCount) == 0 && m_pTypeInfo) + { + m_pTypeInfo->Release(); + m_pTypeInfo = nullptr; + } +} + +// IUnknown methods + +STDMETHODIMP COMOpenDocuments::QueryInterface(REFIID riid, void **ppvObject) +{ + *ppvObject = nullptr; + if (IsEqualIID(riid, __uuidof(IUnknown)) || + IsEqualIID(riid, __uuidof(IDispatch)) || + IsEqualIID(riid, __uuidof(IOWSNewDocument)) || + IsEqualIID(riid, __uuidof(IOWSNewDocument2)) || + IsEqualIID(riid, __uuidof(IOWSNewDocument3))) + { + *ppvObject = static_cast<IOWSNewDocument3*>(this); + } + else if (IsEqualIID(riid, __uuidof(IObjectSafety))) + { + *ppvObject = static_cast<IObjectSafety*>(this); + } + else + { + return E_NOINTERFACE; + } + + static_cast<IUnknown*>(*ppvObject)->AddRef(); + return S_OK; +} + +// IDispatch methods + +STDMETHODIMP COMOpenDocuments::GetTypeInfoCount(UINT *pctinfo) +{ + if (pctinfo == nullptr) + return E_INVALIDARG; + + *pctinfo = 1; + return S_OK; +} + +STDMETHODIMP COMOpenDocuments::GetTypeInfo(UINT iTInfo, LCID /*lcid*/, ITypeInfo **ppTInfo) +{ + if (ppTInfo == nullptr) + return E_INVALIDARG; + *ppTInfo = nullptr; + + if (iTInfo != 0) + return DISP_E_BADINDEX; + + (*ppTInfo = m_pTypeInfo)->AddRef(); + return S_OK; +} + +STDMETHODIMP COMOpenDocuments::GetIDsOfNames( + REFIID /*riid*/, + LPOLESTR *rgszNames, + UINT cNames, + LCID /*lcid*/, + DISPID *rgDispId) +{ + return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); +} + +STDMETHODIMP COMOpenDocuments::Invoke( + DISPID dispIdMember, + REFIID /*riid*/, // IID_NULL (see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479) + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +// IOWSNewDocument methods + +// Creates a document based on the specified document template +STDMETHODIMP COMOpenDocuments::CreateNewDocument( + BSTR bstrTemplateLocation, // A string that contains the URL of the document template from which the document is created, or the programmatic identifier (progID) of the application to invoke when creating the document + BSTR bstrDefaultSaveLocation, // A string that contains the path that specifies a suggested default location for saving the new document + VARIANT_BOOL *pbResult) // true if the document creation succeeds; otherwise false +{ + return CreateNewDocument2(nullptr, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult); +} + +// Opens the specified document for editing with its associated application +// or with the specified editor +STDMETHODIMP COMOpenDocuments::EditDocument( + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + return EditDocument3(nullptr, bstrDocumentLocation, FALSE, varProgID, pbResult); +} + +// IOWSNewDocument2 methods + +// Opens the document for reading instead of editing, so that the document is not locked on the server +// +// Use the ViewDocument method to open a document in its appropriate application, +// instead of inside of an application instance embedded within the browser +STDMETHODIMP COMOpenDocuments::ViewDocument( + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + return ViewDocument3(nullptr, bstrDocumentLocation, 0, varProgID, pbResult); +} + +// Opens the document for reading instead of editing, so that the document +// is not locked on the server and in a specified window +// +// Use the ViewDocument method to open a document in its appropriate application, +// instead of inside of an application instance embedded within the browser +STDMETHODIMP COMOpenDocuments::ViewDocument2( + IDispatch *pdisp, // An Object that represents the window from which the ViewDocument2 method is being activated + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + return ViewDocument3(pdisp, bstrDocumentLocation, 0, varProgID, pbResult); +} + +// Opens the specified document for editing with its associated application +// or with the specified editor based on the specified window object +STDMETHODIMP COMOpenDocuments::EditDocument2( + IDispatch *pdisp, // An Object that represents the window from which the EditDocument2 method is being activated + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + return EditDocument3(pdisp, bstrDocumentLocation, FALSE, varProgID, pbResult); +} + +// Creates a document based on the specified document template and window object +STDMETHODIMP COMOpenDocuments::CreateNewDocument2( + IDispatch* pdisp, // An Object that represents the window from which the CreateNewDocument2 method is being activated + BSTR bstrTemplateLocation, // A string that contains the URL of the document template from which the document is created, or the programmatic identifier (progID) of the application to invoke when creating the document + BSTR bstrDefaultSaveLocation, // A string that contains the path that specifies a suggested default location for saving the new document + VARIANT_BOOL* pbResult) // true if the document creation succeeds; otherwise false +{ + if (!pbResult) + return E_POINTER; + // TODO: resolve the program from varProgID (nullptr -> default?) + return ImplCreateNewDocument(pdisp, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult); +} + +// Used with the OpenDocuments.CreateNewDocument2 method to determine +// whether the security dialog box that appears when a document is opened has already appeared +// +// If the PromptedOnLastOpen method returns true, the window containing the document library view +// refreshes itself the next time it receives focus. One refresh can occur after the new document +// is saved to the server +STDMETHODIMP COMOpenDocuments::PromptedOnLastOpen( + VARIANT_BOOL* pbResult) // true if the security dialog box that appears when a document is opened has already appeared; otherwise false +{ + // This method is used by SharePoint e.g. after calling ViewDocument3. Needs to be implemented, + // otherwise IE would show download bar ("Do you want to open Foo.xls?"), as if opening with + // LibreOffice failed, even if actually it succeeded. + if (!pbResult) + return E_POINTER; + // Returning true makes SharePoint library to refresh only when focused next time; false makes + // it refresh instantly. The JavaScript code involved is this: + // var fRefreshOnNextFocus=stsOpen.PromptedOnLastOpen(); + // if (fRefreshOnNextFocus) + // window.onfocus=RefreshOnNextFocus; + // else + // SetWindowRefreshOnFocus(); + // It seems to be no reason to require immediate refresh, so just return true. + *pbResult = VARIANT_TRUE; + return S_OK; +} + +// IOWSNewDocument3 methods + +// Opens the document for reading instead of editing, so that the document +// is not locked on the server in a specified window, and with a specified type +// +// The following table shows possible values for OpenType +// +// 0 When checked out, or when the document library does not require check out, the user can read or edit the document +// 1 When another user has checked it out, the user can only read the document +// 2 When the current user has checked it out, the user can only edit the document +// 3 When the document is not checked out and the document library requires that documents be checked out to be edited, the user can only read the document, or check it out and edit it +// 4 When the current user has checked it out, the user can only edit the local copy of the document +STDMETHODIMP COMOpenDocuments::ViewDocument3( + IDispatch* pdisp, // An Object that represents the window from which the ViewDocument3 method is being activated + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading + int OpenType, // A Long integer that specifies the rights for opening the document + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + if (!pbResult) + return E_POINTER; + return ImplViewDocument(pdisp, bstrDocumentLocation, OpenType, varProgID, pbResult); +} + +// Checks in the specified document to a library +STDMETHODIMP COMOpenDocuments::CheckinDocument( + BSTR /*bstrDocumentLocation*/, // A string that contains the URL of the document to check in + int /*CheckinType*/, // A Long that specifies the type of check-in, where 0 = minor check-in, 1 = major check-in, and 2 = overwrite check-in + BSTR /*CheckinComment*/, // A string that contains a comment for checking in the document + VARIANT_BOOL /*bKeepCheckout*/, // Optional. true to check in changes that have been made to the document yet keep the document checked out; otherwise, false. The default value is false + VARIANT_BOOL* /*pbResult*/) // true if the document is successfully checked in; otherwise, false +{ + // TODO + return E_NOTIMPL; +} + +// Discards the check out of a document to the client computer and deletes the local draft +STDMETHODIMP COMOpenDocuments::DiscardLocalCheckout( + BSTR /*bstrDocumentLocationRaw*/, // A string that contains the URL of the document + VARIANT_BOOL* /*pbResult*/) // true if the operation to discard the local checkout of the document is successful; otherwise, false +{ + // TODO + return E_NOTIMPL; +} + +// Deprecated. Returns E_NOTIMPL +STDMETHODIMP COMOpenDocuments::ViewInExcel( + BSTR /*SiteUrl*/, + BSTR /*FileName*/, + BSTR /*SessionId*/, + BSTR /*Cmd*/, + BSTR /*Sheet*/, + int /*Row*/, + int /*Column*/, + VARIANT /*varProgID*/) +{ + return E_NOTIMPL; +} + +// Checks out a document from a library +STDMETHODIMP COMOpenDocuments::CheckoutDocumentPrompt( + BSTR /*bstrDocumentLocationRaw*/, // A string that contains the URL of the document to check out + VARIANT_BOOL /*fEditAfterCheckout*/, // true to open the document in an editing application; otherwise, false + VARIANT /*varProgID*/, // An optional string that contains the ProgID of the application that is used to work with the document. If this argument is omitted, the default application for the document is used + VARIANT_BOOL* /*pbResult*/) // true if the document is successfully checked out; otherwise, false +{ + // TODO + return E_NOTIMPL; +} + +// Opens the specified document for editing with its associated application +// or with the specified editor based on the specified window object, +// and specifies whether to use a local copy +STDMETHODIMP COMOpenDocuments::EditDocument3( + IDispatch* pdisp, // An Object that represents the window from which the EditDocument3 method is being activated + BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing + VARIANT_BOOL fUseLocalCopy, // true to use a local copy; otherwise false + VARIANT varProgID, // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used + VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false +{ + if (!pbResult) + return E_POINTER; + // TODO: resolve the program from varProgID (nullptr -> default?) + return ImplEditDocument(pdisp, bstrDocumentLocation, fUseLocalCopy, varProgID, pbResult); +} + +// Creates a new blog post in the editing application +STDMETHODIMP COMOpenDocuments::NewBlogPost( + BSTR /*bstrProviderId*/, // A string that contains the GUID of the blog provider + BSTR /*bstrBlogUrl*/, // A string that contains the absolute URL of the blog site + BSTR /*bstrBlogName*/) // A string that contains the GUID of the blog site and the GUID of the post list separated by the pound sign (#) +{ + return E_NOTIMPL; +} + +// IObjectSafety methods + +HRESULT STDMETHODCALLTYPE COMOpenDocuments::GetInterfaceSafetyOptions( + REFIID riid, + DWORD *pdwSupportedOptions, + DWORD *pdwEnabledOptions) +{ + IUnknown* pUnk; + HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk)); + if (FAILED(hr)) + { + return hr; + } + + // We know about it; release reference and return required information + pUnk->Release(); + *pdwSupportedOptions = iSupportedOptionsMask; + *pdwEnabledOptions = m_iEnabledOptions; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE COMOpenDocuments::SetInterfaceSafetyOptions( + REFIID riid, + DWORD dwOptionSetMask, + DWORD dwEnabledOptions) +{ + IUnknown* pUnk; + HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk)); + if (FAILED(hr)) + { + return hr; + } + pUnk->Release(); + + // Are there unsupported options in mask? + if (dwOptionSetMask & ~iSupportedOptionsMask) + return E_FAIL; + + m_iEnabledOptions = (m_iEnabledOptions & ~dwOptionSetMask) | (dwOptionSetMask & dwEnabledOptions); + return S_OK; +} + +// Non-COM methods + +LONG COMOpenDocuments::GetObjectCount() { return m_nObjCount; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx b/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx new file mode 100644 index 000000000..2a155fefa --- /dev/null +++ b/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx @@ -0,0 +1,13 @@ +/* -*- 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/. +*/ + +// A stub for generating x64 DLL without the need to copy source files to a temporary directory +#include "COMOpenDocuments.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/registrar.cxx b/shell/source/win32/spsupp/registrar.cxx new file mode 100644 index 000000000..1d647f3a5 --- /dev/null +++ b/shell/source/win32/spsupp/registrar.cxx @@ -0,0 +1,290 @@ +/* -*- 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 <registrar.hpp> +#include <wchar.h> +#include <objbase.h> + +namespace { + + HRESULT RegRead(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, wchar_t* valData, size_t cchData) + { + HKEY hKey; + LSTATUS iRetVal = RegCreateKeyExW( + hRootKey, + subKey, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_READ, + nullptr, + &hKey, + nullptr); + if (iRetVal != ERROR_SUCCESS) + return HRESULT_FROM_WIN32(iRetVal); + + DWORD cbData = cchData * sizeof(valData[0]); + DWORD dwType; + iRetVal = RegQueryValueExW(hKey, valName, nullptr, &dwType, reinterpret_cast<LPBYTE>(valData), &cbData); + RegCloseKey(hKey); + if ((iRetVal == ERROR_SUCCESS) && (dwType != REG_SZ)) + { + return E_FAIL; + } + return HRESULT_FROM_WIN32(iRetVal); + } + + HRESULT RegWrite(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, const wchar_t* valData, HKEY *hKeyResult = nullptr) + { + HKEY hKey; + LSTATUS iRetVal = RegCreateKeyExW( + hRootKey, + subKey, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + nullptr, + &hKey, + nullptr); + if (iRetVal != ERROR_SUCCESS) + return HRESULT_FROM_WIN32(iRetVal); + + if (valData) + { + DWORD cbData = static_cast<DWORD>(wcslen(valData)*sizeof(valData[0])); + iRetVal = RegSetValueExW(hKey, valName, 0, REG_SZ, reinterpret_cast<const BYTE *>(valData), cbData); + } + + if (hKeyResult && (iRetVal == ERROR_SUCCESS)) + *hKeyResult = hKey; + else + RegCloseKey(hKey); + + return HRESULT_FROM_WIN32(iRetVal); + } + + HRESULT RegDel(HKEY hRootKey, const wchar_t* subKey) + { + LSTATUS iRetVal = RegDeleteKeyW(hRootKey, subKey); + return HRESULT_FROM_WIN32(iRetVal); + } + +} // namespace + +// see http://stackoverflow.com/questions/284619 +// see https://msdn.microsoft.com/en-us/library/ms691424 +// see https://msdn.microsoft.com/en-us/library/ms694514 + +Registrar::Registrar(REFIID riidCLSID) +{ + m_ConstructionResult = (StringFromGUID2(riidCLSID, m_sCLSID, nGUIDlen) == 0) ? + E_UNEXPECTED: S_OK; +} + +HRESULT Registrar::RegisterObject(REFIID riidTypeLib, const wchar_t* sProgram, + const wchar_t* sComponent, std::initializer_list<int> aVersions, + const wchar_t* Path) +{ + if (!wcslen(sComponent) || !wcslen(sProgram)) + return E_INVALIDARG; + + if (FAILED(m_ConstructionResult)) + return m_ConstructionResult; + + // HKEY_CLASSES_ROOT + // \CLSID + // \{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} + // (default) = "MyLibrary MyControl Class" + // \InprocServer32 + // (default) = "c:\foo\control.dll" + // ThreadingModel = "Apartment" + // \ProgID + // (default) = "MyLibrary.MyControl" + // \Programmable + // \TypeLib + // (default) = "{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}" + + wchar_t sBufKey[MAX_PATH]; + wchar_t sBufVal[MAX_PATH]; + + // CLSID + swprintf(sBufKey, MAX_PATH, L"CLSID\\%s", m_sCLSID); + swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); + HKEY hKeyCLSID; + HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, &hKeyCLSID); + if (FAILED(hr)) + return hr; + { + class HKeyGuard { + public: + HKeyGuard(HKEY aKey) : m_hKey(aKey) {} + ~HKeyGuard() { RegCloseKey(m_hKey); } + private: + HKEY m_hKey; + }; + + HKeyGuard hKeyCLSIDGuard(hKeyCLSID); + + // InprocServer32 + HKEY hKeyInprocServer32; + hr = RegWrite(hKeyCLSID, L"InprocServer32", L"", Path, &hKeyInprocServer32); + if (FAILED(hr)) + return hr; + { + HKeyGuard hKeyInProcServer32Guard(hKeyInprocServer32); + hr = RegWrite(hKeyInprocServer32, L"", L"ThreadingModel", L"Apartment"); + if (FAILED(hr)) + return hr; + } + + // ProgID + swprintf(sBufVal, MAX_PATH, L"%s.%s", sProgram, sComponent); + hr = RegWrite(hKeyCLSID, L"ProgID", L"", sBufVal); + if (FAILED(hr)) + return hr; + + // Programmable + hr = RegWrite(hKeyCLSID, L"Programmable", nullptr, nullptr); + if (FAILED(hr)) + return hr; + + // TypeLib + if (::StringFromGUID2(riidTypeLib, sBufVal, nGUIDlen) == 0) + return E_UNEXPECTED; + hr = RegWrite(hKeyCLSID, L"TypeLib", L"", sBufVal); + if (FAILED(hr)) + return hr; + } + + // ProgID + return RegisterProgIDs(sProgram, sComponent, aVersions); +} + +HRESULT Registrar::UnRegisterObject(const wchar_t* sProgram, const wchar_t* sComponent, + std::initializer_list<int> aVersions) +{ + if (FAILED(m_ConstructionResult)) + return m_ConstructionResult; + // ProgID + UnRegisterProgIDs(sProgram, sComponent, aVersions); + // CLSID + wchar_t sBuf[MAX_PATH]; + swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\InProcServer32", m_sCLSID); + RegDel(HKEY_CLASSES_ROOT, sBuf); + swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\ProgId", m_sCLSID); + RegDel(HKEY_CLASSES_ROOT, sBuf); + swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\Programmable", m_sCLSID); + RegDel(HKEY_CLASSES_ROOT, sBuf); + swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\TypeLib", m_sCLSID); + RegDel(HKEY_CLASSES_ROOT, sBuf); + swprintf(sBuf, MAX_PATH, L"CLSID\\%s", m_sCLSID); + RegDel(HKEY_CLASSES_ROOT, sBuf); + return S_OK; +} + +HRESULT Registrar::RegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion, bool bSetDefault) +{ + // HKEY_CLASSES_ROOT + // \MyLibrary.MyControl + // (default) = "MyLibrary MyControl Class" + // \CurVer + // (default) = "MyLibrary.MyControl.1" + // \MyLibrary.MyControl.1 + // (default) = "MyLibrary MyControl Class" + // \CLSID + // (default) = "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" + if (FAILED(m_ConstructionResult)) + return m_ConstructionResult; + wchar_t sBufKey[MAX_PATH]; + swprintf(sBufKey, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); + wchar_t sBufVal[MAX_PATH]; + swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); + RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); + swprintf(sBufKey, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion); + HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", m_sCLSID); + if (SUCCEEDED(hr) && bSetDefault) + { + swprintf(sBufKey, MAX_PATH, L"%s.%s", sProgram, sComponent); + swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); + hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); + swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent); + swprintf(sBufVal, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); + hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); + } + return hr; +} + +HRESULT Registrar::RegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent, + std::initializer_list<int> aVersions) +{ + HRESULT hr = S_OK; + bool bDefaultRegistered = false; + for (int nVersion : aVersions) + { + if (SUCCEEDED(hr)) + { + hr = RegisterProgID(sProgram, sComponent, nVersion, !bDefaultRegistered); + bDefaultRegistered = true; + } + } + return hr; +} + +HRESULT Registrar::UnRegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion) +{ + if (FAILED(m_ConstructionResult)) + return m_ConstructionResult; + wchar_t sBuf[MAX_PATH]; + swprintf(sBuf, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion); + wchar_t sCurCLSID[nGUIDlen]; + HRESULT hr = RegRead(HKEY_CLASSES_ROOT, sBuf, L"", sCurCLSID, nGUIDlen); + if (FAILED(hr)) + return hr; + if (wcsncmp(sCurCLSID, m_sCLSID, nGUIDlen) != 0) + { + // The ProgID points to a different CLSID; most probably it's intercepted + // by a different application, so don't remove it + return S_FALSE; + } + RegDel(HKEY_CLASSES_ROOT, sBuf); + swprintf(sBuf, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); + hr = RegDel(HKEY_CLASSES_ROOT, sBuf); + + wchar_t sBufKey[MAX_PATH]; + swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent); + wchar_t sBufVal[MAX_PATH]; + if (SUCCEEDED(RegRead(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, MAX_PATH)) && (wcsncmp(sBufVal, sBuf, MAX_PATH) == 0)) + { + // Only unreg default if this version is current default + RegDel(HKEY_CLASSES_ROOT, sBufKey); + swprintf(sBuf, MAX_PATH, L"%s.%s", sProgram, sComponent); + HRESULT hr1 = RegDel(HKEY_CLASSES_ROOT, sBuf); + // Always return a failure result if we failed somewhere + if (FAILED(hr1)) + hr = hr1; + } + return hr; +} + +HRESULT Registrar::UnRegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent, + std::initializer_list<int> aVersions) +{ + HRESULT hr = S_OK; + // Try all ProgIDs regardless of error, but make sure to return failure result if some failed + for (int nVersion : aVersions) + { + HRESULT hrLast = UnRegisterProgID(sProgram, sComponent, nVersion); + if (SUCCEEDED(hr)) + hr = hrLast; + } + return hr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/registrar_x64.cxx b/shell/source/win32/spsupp/registrar_x64.cxx new file mode 100644 index 000000000..18372bb27 --- /dev/null +++ b/shell/source/win32/spsupp/registrar_x64.cxx @@ -0,0 +1,13 @@ +/* -*- 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/. +*/ + +// A stub for generating x64 DLL without the need to copy source files to a temporary directory +#include "registrar.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/res/spsupp.rc b/shell/source/win32/spsupp/res/spsupp.rc new file mode 100644 index 000000000..20e323e16 --- /dev/null +++ b/shell/source/win32/spsupp/res/spsupp.rc @@ -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/. +*/ + +// Type library + +1 TYPELIB TLB_FILE diff --git a/shell/source/win32/spsupp/res/spsuppDlg.h b/shell/source/win32/spsupp/res/spsuppDlg.h new file mode 100644 index 000000000..f4b6ccb34 --- /dev/null +++ b/shell/source/win32/spsupp/res/spsuppDlg.h @@ -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/. +*/ + +#define IDD_EDIT_OR_RO 101 +#define IDC_STATIC -1 +#define ID_RO 1000 +#define ID_EDIT 1001 +#define IDC_EDIT_OR_RO 1002 + +// Next default values for new objects + +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/res/spsuppDlg.rc b/shell/source/win32/spsupp/res/spsuppDlg.rc new file mode 100644 index 000000000..f4ce83a15 --- /dev/null +++ b/shell/source/win32/spsupp/res/spsuppDlg.rc @@ -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/.
+*/
+
+#include "spsuppDlg.h"
+// We need to include windows.h to use IDI_QUESTION
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT
+
+// Dialog
+
+IDD_EDIT_OR_RO DIALOGEX 0, 0, 309, 87
+STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOPMOST
+CAPTION "Open Document"
+BEGIN
+ ICON IDI_QUESTION,IDC_STATIC,7,7,21,20
+ LTEXT "Do you want to open the document to view or to edit?",IDC_EDIT_OR_RO,36,7,266,44
+ DEFPUSHBUTTON "View",ID_RO,91,66,77,14
+ PUSHBUTTON "Edit",ID_EDIT,171,66,77,14
+ PUSHBUTTON "Cancel",IDCANCEL,252,66,50,14
+END
diff --git a/shell/source/win32/spsupp/spsupp.def b/shell/source/win32/spsupp/spsupp.def new file mode 100644 index 000000000..f6828a3e8 --- /dev/null +++ b/shell/source/win32/spsupp/spsupp.def @@ -0,0 +1,8 @@ +LIBRARY + +EXPORTS + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllInstall PRIVATE diff --git a/shell/source/win32/spsupp/spsupp.idl b/shell/source/win32/spsupp/spsupp.idl new file mode 100644 index 000000000..45d5f47b1 --- /dev/null +++ b/shell/source/win32/spsupp/spsupp.idl @@ -0,0 +1,147 @@ +/* -*- 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/. +*/ + +[ + uuid(580411ED-80EC-4834-BA1F-2EB07A49C80B), + version(0.1), + helpstring("LibreOffice SharePoint Client Support Type Library v.0.1") +] +library spsupp +{ + // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} + importlib("stdole2.tlb"); + + // Forward declare all types defined in this typelib + interface IOWSNewDocument; + interface IOWSNewDocument2; + interface IOWSNewDocument3; + + [ + odl, + uuid(7B678CDE-D71C-4954-ACC7-A92A96BF70DB), + helpstring("IOWSNewDocument Interface"), + dual, + oleautomation + ] + interface IOWSNewDocument : IDispatch { + [id(0x60020000)] + HRESULT CreateNewDocument( + [in] BSTR bstrTemplateLocation, + [in] BSTR bstrDefaultSaveLocation, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60020001)] + HRESULT EditDocument( + [in] BSTR bstrDocumentLocation, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + }; + + [ + odl, + uuid(470D72F8-C6E2-40D1-B844-4FF73DB69EC5), + helpstring("IOWSNewDocument2 Interface"), + dual, + oleautomation + ] + interface IOWSNewDocument2 : IOWSNewDocument { + [id(0x60030000)] + HRESULT ViewDocument( + [in] BSTR bstrDocumentLocation, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60030001)] + HRESULT ViewDocument2( + [in] IDispatch* pdisp, + [in] BSTR bstrDocumentLocation, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60030002)] + HRESULT EditDocument2( + [in] IDispatch* pdisp, + [in] BSTR bstrDocumentLocation, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60030003)] + HRESULT CreateNewDocument2( + [in] IDispatch* pdisp, + [in] BSTR bstrTemplateLocation, + [in] BSTR bstrDefaultSaveLocation, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60030004)] + HRESULT PromptedOnLastOpen([out, retval] VARIANT_BOOL* pbResult); + }; + + [ + odl, + uuid(4D144CA3-2336-4E15-A7D1-A4B151D07CC7), + helpstring("IOWSNewDocument3 Interface"), + dual, + oleautomation + ] + interface IOWSNewDocument3 : IOWSNewDocument2 { + [id(0x60040000)] + HRESULT ViewDocument3( + [in] IDispatch* pdisp, + [in] BSTR bstrDocumentLocation, + [in] int OpenType, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60040001)] + HRESULT CheckinDocument( + [in] BSTR bstrDocumentLocation, + [in] int CheckinType, + [in] BSTR CheckinComment, + [in, defaultvalue(FALSE)] VARIANT_BOOL bKeepCheckout, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60040002)] + HRESULT DiscardLocalCheckout( + [in] BSTR bstrDocumentLocationRaw, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60040003)] + HRESULT ViewInExcel( + [in] BSTR SiteUrl, + [in] BSTR FileName, + [in] BSTR SessionId, + [in] BSTR Cmd, + [in] BSTR Sheet, + [in] int Row, + [in] int Column, + [in, optional] VARIANT varProgID); + [id(0x60040004)] + HRESULT CheckoutDocumentPrompt( + [in] BSTR bstrDocumentLocationRaw, + [in] VARIANT_BOOL fEditAfterCheckout, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60040005)] + HRESULT EditDocument3( + [in] IDispatch* pdisp, + [in] BSTR bstrDocumentLocation, + [in] VARIANT_BOOL fUseLocalCopy, + [in, optional] VARIANT varProgID, + [out, retval] VARIANT_BOOL* pbResult); + [id(0x60040006)] + HRESULT NewBlogPost( + [in] BSTR bstrProviderId, + [in] BSTR bstrBlogUrl, + [in] BSTR bstrBlogName); + }; + + [ + uuid(4AD14812-2807-48B1-A27F-BA836D874E45), + helpstring("COMOpenDocuments Class") + ] + coclass COMOpenDocuments { + [default] interface IOWSNewDocument; + interface IOWSNewDocument2; + interface IOWSNewDocument3; + }; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/spsuppClassFactory.cxx b/shell/source/win32/spsupp/spsuppClassFactory.cxx new file mode 100644 index 000000000..2a95cf050 --- /dev/null +++ b/shell/source/win32/spsupp/spsuppClassFactory.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <spsuppClassFactory.hpp> +#include <COMOpenDocuments.hpp> + +LONG ClassFactory::m_nObjCount = 0; +LONG ClassFactory::m_nLockCount = 0; + +ClassFactory::ClassFactory() +{ + ::InterlockedIncrement(&m_nObjCount); +}; + +ClassFactory::~ClassFactory() +{ + ::InterlockedDecrement(&m_nObjCount); +}; + +// IUnknown methods + +STDMETHODIMP ClassFactory::QueryInterface( + REFIID riid, + void **ppvObject) +{ + *ppvObject = nullptr; + if (IsEqualIID(riid, __uuidof(IUnknown)) || + IsEqualIID(riid, __uuidof(IClassFactory))) + { + *ppvObject = static_cast<IClassFactory*>(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +// IClassFactory methods + +STDMETHODIMP ClassFactory::CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject) +{ + *ppvObject = nullptr; + if (pUnkOuter) + { + return CLASS_E_NOAGGREGATION; + } + + COMOpenDocuments* pObj; + try { + pObj = new COMOpenDocuments; + } + catch (const COMOpenDocuments::Error& e) { + return e.val(); + } + catch (...) { + return E_OUTOFMEMORY; + } + + HRESULT hr = pObj->QueryInterface(riid, ppvObject); + pObj->Release(); + return hr; +} + +STDMETHODIMP ClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + ::InterlockedIncrement(&m_nLockCount); + else + ::InterlockedDecrement(&m_nLockCount); + return S_OK; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx b/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx new file mode 100644 index 000000000..099a305d9 --- /dev/null +++ b/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx @@ -0,0 +1,13 @@ +/* -*- 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/. +*/ + +// A stub for generating x64 DLL without the need to copy source files to a temporary directory +#include "spsuppClassFactory.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/spsuppHelper.cxx b/shell/source/win32/spsupp/spsuppHelper.cxx new file mode 100644 index 000000000..af3509b87 --- /dev/null +++ b/shell/source/win32/spsupp/spsuppHelper.cxx @@ -0,0 +1,225 @@ +/* -*- 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 <prewin.h> +#include <postwin.h> + +#include <comphelper/windowserrorstring.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <osl/file.hxx> +#include <rtl/bootstrap.hxx> +#include <spsuppStrings.hrc> +#include <unotools/resmgr.hxx> +#include "res/spsuppDlg.h" + +// Since we need to show localized messages to user before starting LibreOffice, we need to +// bootstrap part of LO (l10n machinery). This implies loading some LO libraries, and since +// there are ActiveX controls for both x86 and x64 for use in corresponding clients, they +// can't both load the libraries that exist only for one architecture, like sal. Thus we need +// a dedicated helper process, which is launched by ActiveX, and handle user interactions. + +namespace +{ +const OUString& GetSofficeExe() +{ + static const OUString s_sPath = []() { + OUString result; + wchar_t sPath[MAX_PATH]; + if (GetModuleFileNameW(nullptr, sPath, MAX_PATH) == 0) + return result; + wchar_t* pSlashPos = wcsrchr(sPath, L'\\'); + if (pSlashPos == nullptr) + return result; + wcscpy(pSlashPos + 1, L"soffice.exe"); + result = o3tl::toU(sPath); + return result; + }(); + return s_sPath; +} + +OUString GetString(TranslateId pResId) +{ + static const std::locale s_pLocale = [] { + // Initialize soffice bootstrap: see getIniFileName_Impl for reference + OUString sPath = GetSofficeExe(); + if (const sal_Int32 nDotPos = sPath.lastIndexOf('.'); nDotPos >= 0) + { + sPath = sPath.replaceAt(nDotPos, sPath.getLength() - nDotPos, SAL_CONFIGFILE(u"")); + if (osl::FileBase::getFileURLFromSystemPath(sPath, sPath) == osl::FileBase::E_None) + rtl::Bootstrap::setIniFilename(sPath); + } + return Translate::Create("shell", LanguageTag("")); // Use system language + }(); + return Translate::get(pResId, s_pLocale); +} + +INT_PTR CALLBACK EditOrRODlgproc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + switch (Msg) + { + case WM_INITDIALOG: + { + if (const wchar_t* sFilePath = reinterpret_cast<const wchar_t*>(lParam)) + { + OUString sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_TITLE); + SetWindowTextW(hDlg, o3tl::toW(sMsg.getStr())); + sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_MESSAGE) + .replaceFirst("%DOCNAME", o3tl::toU(sFilePath)); + SetWindowTextW(GetDlgItem(hDlg, IDC_EDIT_OR_RO), o3tl::toW(sMsg.getStr())); + sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_VIEW); + SetWindowTextW(GetDlgItem(hDlg, ID_RO), o3tl::toW(sMsg.getStr())); + sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_EDIT); + SetWindowTextW(GetDlgItem(hDlg, ID_EDIT), o3tl::toW(sMsg.getStr())); + sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_CANCEL); + SetWindowTextW(GetDlgItem(hDlg, IDCANCEL), o3tl::toW(sMsg.getStr())); + } + return TRUE; // set default focus + } + case WM_COMMAND: + { + WORD nId = LOWORD(wParam); + switch (nId) + { + case IDCANCEL: + case ID_RO: + case ID_EDIT: + EndDialog(hDlg, nId); + return TRUE; + } + break; + } + } + return FALSE; +} + +enum class Answer +{ + Cancel, + ReadOnly, + Edit +}; + +Answer AskIfUserWantsToEdit(const wchar_t* sFilePath) +{ + Answer res = Answer::Cancel; + INT_PTR nResult = DialogBoxParamW(nullptr, MAKEINTRESOURCEW(IDD_EDIT_OR_RO), nullptr, + EditOrRODlgproc, reinterpret_cast<LPARAM>(sFilePath)); + if (nResult == ID_RO) + res = Answer::ReadOnly; + else if (nResult == ID_EDIT) + res = Answer::Edit; + return res; +} + +// Returns ERROR_SUCCESS or Win32 error code +DWORD LOStart(const wchar_t* sModeArg, const wchar_t* sFilePath) +{ + OUString sCmdLine = "\"" + GetSofficeExe() + "\" " + o3tl::toU(sModeArg) + " \"" + + o3tl::toU(sFilePath) + "\""; + LPWSTR pCmdLine = const_cast<LPWSTR>(o3tl::toW(sCmdLine.getStr())); + + STARTUPINFOW si = {}; + si.cb = sizeof si; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + PROCESS_INFORMATION pi{}; + if (!CreateProcessW(nullptr, pCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) + { + DWORD dwError = GetLastError(); + const OUString sErrorMsg = "Could not start LibreOffice. Error is 0x" + + OUString::number(dwError, 16) + ":\n\n" + + WindowsErrorString(dwError); + + // Report the error to user and return error + MessageBoxW(nullptr, o3tl::toW(sErrorMsg.getStr()), nullptr, MB_ICONERROR); + return dwError; + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return ERROR_SUCCESS; +} + +int CreateNewDocument(LPCWSTR TemplateLocation, LPCWSTR /*DefaultSaveLocation*/) +{ + // TODO: resolve the program from varProgID (nullptr -> default?) + DWORD nResult = LOStart(L"-n", TemplateLocation); + return nResult == ERROR_SUCCESS ? 0 : 2; +} + +// UseLocalCopy would be either "0" or "1", denoting boolean value +int EditDocument(LPCWSTR DocumentLocation, LPCWSTR /*UseLocalCopy*/, LPCWSTR /*varProgID*/) +{ + // TODO: resolve the program from varProgID (nullptr -> default?) + DWORD nResult = LOStart(L"-o", DocumentLocation); + return nResult == ERROR_SUCCESS ? 0 : 2; +} + +// Possible values for OpenType +// +// "0" When checked out, or when the document library does not require check out, the user can read or edit the document +// "1" When another user has checked it out, the user can only read the document +// "2" When the current user has checked it out, the user can only edit the document +// "3" When the document is not checked out and the document library requires that documents be checked out to be edited, the user can only read the document, or check it out and edit it +// "4" When the current user has checked it out, the user can only edit the local copy of the document +int ViewDocument(LPCWSTR DocumentLocation, LPCWSTR OpenType, LPCWSTR varProgID) +{ + if (wcscmp(OpenType, L"0") == 0) + { + switch (AskIfUserWantsToEdit(DocumentLocation)) + { + case Answer::Cancel: + return 1; + case Answer::ReadOnly: + break; + case Answer::Edit: + return EditDocument(DocumentLocation, L"0", varProgID); + } + } + // TODO: resolve the program from varProgID (nullptr -> default?) + DWORD nResult = LOStart(L"--view", DocumentLocation); + return nResult == ERROR_SUCCESS ? 0 : 2; +} +} // namespace + +// Returns 0 on success, 1 when operation wasn't performed because user cancelled, 2 on an error +int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) +{ + int argc = 0; + const LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (argc < 2) + return 2; // Wrong argument count + + if (wcscmp(argv[1], L"CreateNewDocument") == 0) + { + if (argc != 4) + return 2; // Wrong argument count + return CreateNewDocument(argv[2], argv[3]); + } + + if (wcscmp(argv[1], L"ViewDocument") == 0) + { + if (argc != 4 && argc != 5) + return 2; // Wrong argument count + LPCWSTR pProgId = argc == 5 ? argv[4] : nullptr; + return ViewDocument(argv[2], argv[3], pProgId); + } + + if (wcscmp(argv[1], L"EditDocument") == 0) + { + if (argc != 4 && argc != 5) + return 2; // Wrong argument count + LPCWSTR pProgId = argc == 5 ? argv[4] : nullptr; + return EditDocument(argv[2], argv[3], pProgId); + } + + return 2; // Wrong command +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/spsuppServ.cxx b/shell/source/win32/spsupp/spsuppServ.cxx new file mode 100644 index 000000000..abd5ec607 --- /dev/null +++ b/shell/source/win32/spsupp/spsuppServ.cxx @@ -0,0 +1,180 @@ +/* -*- 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 MIDL-generated file +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextra-tokens" + // "#endif !_MIDL_USE_GUIDDEF_" in midl-generated code +#endif +#include <spsupp_i.c> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <memory> +#include <olectl.h> +#include <wchar.h> +#include <spsuppServ.hpp> +#include <spsuppClassFactory.hpp> +#include <COMOpenDocuments.hpp> +#include <registrar.hpp> + +#include <shlwapi.h> // declaration of DllInstall + +namespace +{ +HANDLE g_hModule; + +HMODULE GetHModule() { return static_cast<HMODULE>(g_hModule); } +} // namespace + +ITypeLib* GetTypeLib() +{ + typedef std::unique_ptr<ITypeLib, void(*)(IUnknown* p)> ITypeLibGuard; + static ITypeLibGuard s_aITypeLibGuard = [] { + ITypeLibGuard aITypeLibGuard(nullptr, [](IUnknown* p) { if (p) p->Release(); }); + wchar_t szFile[MAX_PATH]; + if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0) + return aITypeLibGuard; + ITypeLib* pTypeLib; + if (FAILED(LoadTypeLib(szFile, &pTypeLib))) + return aITypeLibGuard; + aITypeLibGuard.reset(pTypeLib); + return aITypeLibGuard; + }(); + return s_aITypeLibGuard.get(); +} + +const wchar_t* GetHelperExe() +{ + static wchar_t* s_sPath = []() -> wchar_t* { + static wchar_t sPath[MAX_PATH]; + if (GetModuleFileNameW(GetHModule(), sPath, MAX_PATH) == 0) + return nullptr; + wchar_t* pSlashPos = wcsrchr(sPath, L'\\'); + if (pSlashPos == nullptr) + return nullptr; + wcscpy(pSlashPos + 1, L"spsupp_helper.exe"); + return sPath; + }(); + return s_sPath; +} + +BOOL APIENTRY DllMain( HANDLE hinstDLL, + DWORD fdwReason, + LPVOID /*lpvReserved*/ ) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + g_hModule = hinstDLL; + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +namespace { + // {F1924D0C-9B35-4A46-BCDE-CFEF2CE67A17} + const IID CLSID_spsupp = + { 0xf1924d0c, 0x9b35, 0x4a46, { 0xbc, 0xde, 0xcf, 0xef, 0x2c, 0xe6, 0x7a, 0x17 } }; +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) +{ + *ppvOut = nullptr; + if (IsEqualIID(rclsid, CLSID_spsupp)) + { + ClassFactory *pCf = new ClassFactory; + HRESULT hr = pCf->QueryInterface(riid, ppvOut); + pCf->Release(); + return hr; + } + return CLASS_E_CLASSNOTAVAILABLE; +} + +STDAPI DllCanUnloadNow(void) +{ + if (ClassFactory::GetLockCount() == 0 && + ClassFactory::GetObjectCount() == 0 && + COMOpenDocuments::GetObjectCount() == 0) + return S_OK; + else + return S_FALSE; +} + +STDAPI DllRegisterServer(void) +{ + ITypeLib* pTypeLib = GetTypeLib(); + if (!pTypeLib) + return ResultFromScode(SELFREG_E_TYPELIB); + + wchar_t szFile[MAX_PATH]; + if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0) + return HRESULT_FROM_WIN32(GetLastError()); + + HRESULT hr = RegisterTypeLib(pTypeLib, szFile, nullptr); + if (FAILED(hr)) + return hr; + + // Default is v.5 + return Registrar(CLSID_spsupp) + .RegisterObject(LIBID_spsupp, L"LOSPSupport", L"OpenDocuments", { 5, 1, 2, 3, 4 }, szFile); +} + +STDAPI DllUnregisterServer(void) +{ + ITypeLib* pTypeLib = GetTypeLib(); + if (!pTypeLib) + return ResultFromScode(SELFREG_E_TYPELIB); + TLIBATTR* pLibAttr; + HRESULT hr = pTypeLib->GetLibAttr(&pLibAttr); + if (FAILED(hr)) + return hr; + auto ReleaseFunc = [pTypeLib](TLIBATTR* p) { if (p) pTypeLib->ReleaseTLibAttr(p); }; + typedef std::unique_ptr<TLIBATTR, decltype(ReleaseFunc)> TLIBATTRGuard; + static TLIBATTRGuard aTLIBATTRGuard(nullptr, ReleaseFunc); + + hr = UnRegisterTypeLib(pLibAttr->guid, pLibAttr->wMajorVerNum, pLibAttr->wMinorVerNum, pLibAttr->lcid, pLibAttr->syskind); + if (FAILED(hr)) + return hr; + + return Registrar(CLSID_spsupp) + .UnRegisterObject(L"LOSPSupport", L"OpenDocuments", { 1, 2, 3, 4, 5 }); +} + +// This is called when regsvr32.exe is called with "/i" flag +// pszCmdLine is the string passed to "/i:<string>" +// See https://msdn.microsoft.com/library/windows/desktop/bb759846 +STDAPI DllInstall(BOOL bInstall, _In_opt_ PCWSTR pszCmdLine) +{ + if (wcscmp(pszCmdLine, L"Substitute_OWSSUPP") == 0) + { + HRESULT hr; + Registrar registrar(CLSID_spsupp); + if (bInstall) + { + // Default is v.5 + hr = registrar.RegisterProgIDs(L"SharePoint", L"OpenDocuments", { 5, 1, 2, 3, 4 }); + } + else + { + hr = registrar.UnRegisterProgIDs(L"SharePoint", L"OpenDocuments", { 1, 2, 3, 4, 5 }); + } + return hr; + } + return E_INVALIDARG; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/spsupp/spsuppServ_x64.cxx b/shell/source/win32/spsupp/spsuppServ_x64.cxx new file mode 100644 index 000000000..41046bf13 --- /dev/null +++ b/shell/source/win32/spsupp/spsuppServ_x64.cxx @@ -0,0 +1,13 @@ +/* -*- 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/. +*/ + +// A stub for generating x64 DLL without the need to copy source files to a temporary directory +#include "spsuppServ.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/syssh.component b/shell/source/win32/syssh.component new file mode 100644 index 000000000..d5ddeaa71 --- /dev/null +++ b/shell/source/win32/syssh.component @@ -0,0 +1,26 @@ +<?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@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.sys.shell.SystemShellExecute" + constructor="shell_CSysShExec_get_implementation"> + <service name="com.sun.star.system.SystemShellExecute"/> + </implementation> +</component> diff --git a/shell/source/win32/workbench/TestSmplMail.cxx b/shell/source/win32/workbench/TestSmplMail.cxx new file mode 100644 index 000000000..18b1741e8 --- /dev/null +++ b/shell/source/win32/workbench/TestSmplMail.cxx @@ -0,0 +1,160 @@ +/* -*- 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/lang/XComponent.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/system/XSimpleMailClientSupplier.hpp> +#include <cppuhelper/servicefactory.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <osl/diagnose.h> + +#include <stdio.h> + +#include <windows.h> + +#include <osl/file.hxx> + + +// namespaces + + +using namespace ::cppu ; +using namespace ::com::sun::star::uno ; +using namespace ::com::sun::star::lang ; +using namespace std ; +using namespace com::sun::star::system; + +#define RDB_SYSPATH "D:\\Projects\\gsl\\shell\\wntmsci7\\bin\\applicat.rdb" + + +// global variables + + +Reference< XMultiServiceFactory > g_xFactory; + + +// main + + +// int SAL_CALL main(int nArgc, char* Argv[], char* pEnv[] ) +// make Warning free, leave out typename +int SAL_CALL main(int , char*, char* ) +{ + + // get the global service-manager + + + // Get global factory for uno services. + OUString rdbName = OUString( RDB_SYSPATH ); + Reference< XMultiServiceFactory > g_xFactory( createRegistryServiceFactory( rdbName ) ); + + // Print a message if an error occurred. + if ( g_xFactory.is() == sal_False ) + { + OSL_FAIL("Can't create RegistryServiceFactory"); + return(-1); + } + + printf("Creating RegistryServiceFactory successful\n"); + + + // try to get an Interface to a XFilePicker Service + + + try + { + Reference< XSimpleMailClientSupplier > xSmplMailClientSuppl( + g_xFactory->createInstance("com.sun.star.system.SimpleSystemMail"), UNO_QUERY ); + + if ( !xSmplMailClientSuppl.is() ) + { + OSL_FAIL( "Error creating SimpleSystemMail Service" ); + return(-1); + } + + Reference< XSimpleMailClient > xSmplMailClient( + xSmplMailClientSuppl->querySimpleMailClient( ) ); + + if ( xSmplMailClient.is( ) ) + { + Reference< XSimpleMailMessage > xSmplMailMsg( + xSmplMailClient->createSimpleMailMessage( ) ); + + if ( xSmplMailMsg.is( ) ) + { + xSmplMailMsg->setRecipient( OUString("tino.rachui@germany.sun.com") ); + xSmplMailMsg->setOriginator( OUString("tino.rachui@germany.sun.com") ); + + Sequence<OUString> ccRecips { "tino.rachui@germany.sun.com" }; + + xSmplMailMsg->setCcRecipient( ccRecips ); + + Sequence<OUString> bccRecips { "tino.rachui@germany.sun.com" }; + + xSmplMailMsg->setBccRecipient( bccRecips ); + + xSmplMailMsg->setSubject( OUString("Mapi Test") ); + + Sequence< OUString > attachments( 2 ); + + OUString aFile("D:\\Projects\\gsl\\shell\\wntmsci7\\bin\\testprx.exe"); + OUString aFileURL; + + osl::FileBase::getFileURLFromSystemPath( aFile, aFileURL ); + attachments[0] = aFileURL; + + aFile = "D:\\Projects\\gsl\\shell\\wntmsci7\\bin\\testsyssh.exe"; + osl::FileBase::getFileURLFromSystemPath( aFile, aFileURL ); + + attachments[1] = aFile; + + xSmplMailMsg->setAttachement( attachments ); + + xSmplMailClient->sendSimpleMailMessage( xSmplMailMsg, 0 ); + } + } + } + catch( Exception& ) + { + } + + + // shutdown + + + // Cast factory to XComponent + Reference< XComponent > xComponent( g_xFactory, UNO_QUERY ); + + // Print a message if an error occurred. + if ( xComponent.is() == sal_False ) + { + OSL_FAIL("Error shutting down"); + } + + // Dispose and clear factory + xComponent->dispose(); + g_xFactory.clear(); + + printf("Test successful\n"); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/workbench/TestSysShExec.cxx b/shell/source/win32/workbench/TestSysShExec.cxx new file mode 100644 index 000000000..2dd1ba246 --- /dev/null +++ b/shell/source/win32/workbench/TestSysShExec.cxx @@ -0,0 +1,132 @@ +/* -*- 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/lang/XComponent.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> + +#include <com/sun/star/system/XSystemShellExecute.hpp> +#include <cppuhelper/servicefactory.hxx> + +#include <osl/file.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <osl/diagnose.h> + +#include <stdio.h> + +#include <windows.h> + + +// namespaces + + +using namespace ::cppu ; +using namespace ::com::sun::star::uno ; +using namespace ::com::sun::star::lang ; +using namespace std ; +using namespace com::sun::star::system; + +#define RDB_SYSPATH "D:\\Projects\\gsl\\shell\\wntmsci7\\bin\\applicat.rdb" + + +// global variables + + +Reference< XMultiServiceFactory > g_xFactory; + + +// main + + +// int SAL_CALL main(int nArgc, char* Argv[], char* Env[] ) +int SAL_CALL main(int nArgc, char* Argv[], char* ) +{ + + // get the global service-manager + + + if ( nArgc < 4 ) + return 0; + + // Get global factory for uno services. + OUString rdbName = OUString( RDB_SYSPATH ); + Reference< XMultiServiceFactory > g_xFactory( createRegistryServiceFactory( rdbName ) ); + + // Print a message if an error occurred. + if ( g_xFactory.is() == sal_False ) + { + OSL_FAIL("Can't create RegistryServiceFactory"); + return(-1); + } + + printf("Creating RegistryServiceFactory successful\n"); + + + // try to get an Interface to a XFilePicker Service + + + Reference< XSystemShellExecute > xSysShExec( + g_xFactory->createInstance("com.sun.star.system.SystemShellExecute"), UNO_QUERY ); + + if ( !xSysShExec.is() ) + { + OSL_FAIL( "Error creating SystemShellExecute Service" ); + return(-1); + } + + //"c:\\winnt\\notepad.exe" + OUString cmd = OUString::createFromAscii( Argv[1] ); + OUString param = OUString::createFromAscii( Argv[2] ); //c:\\winnt\\iis5.log + + try + { + xSysShExec->execute( cmd, param, atoi( Argv[3] ) ); + } + catch( SystemShellExecuteException& ) + { + OSL_FAIL( "Error executing system command" ); + } + catch( IllegalArgumentException& ) + { + OSL_FAIL( "Invalid parameter" ); + } + + + // shutdown + + + // Cast factory to XComponent + Reference< XComponent > xComponent( g_xFactory, UNO_QUERY ); + + // Print a message if an error occurred. + if ( xComponent.is() == sal_False ) + { + OSL_FAIL("Error shutting down"); + } + + // Dispose and clear factory + xComponent->dispose(); + g_xFactory.clear(); + + printf("Test successful\n"); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/workbench/makefile.mk b/shell/source/win32/workbench/makefile.mk new file mode 100644 index 000000000..d55a7999d --- /dev/null +++ b/shell/source/win32/workbench/makefile.mk @@ -0,0 +1,79 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If 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=shell +TARGET=testsyssh +TARGET1=testsmplmail +TARGET4=testfopen +LIBTARGET=NO +TARGETTYPE=CUI + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +CFLAGS+=/GX + +OBJFILES1=$(OBJ)$/TestSysShExec.obj +OBJFILES2=$(OBJ)$/TestSmplMail.obj +OBJFILES4=$(OBJ)$/Testfopen.obj + +OBJFILES=$(OBJFILES1)\ + $(OBJFILES2) + +APP1TARGET=$(TARGET) +APP1OBJS=$(OBJFILES1) + +APP1STDLIBS+= $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) \ + $(USER32LIB) + +APP1DEF= $(MISC)$/$(APP1TARGET).def + + +# --- TestSmplMail --- + +APP2TARGET=$(TARGET1) +APP2OBJS=$(OBJFILES2) + +APP2STDLIBS+= $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) \ + $(USER32LIB) + +APP2DEF= $(MISC)$/$(APP2TARGET).def + +# --- Testfopen --- + +APP4TARGET=$(TARGET4) +APP4OBJS=$(OBJFILES4) + +APP4STDLIBS+= $(SALLIB) + +APP4DEF= $(MISC)$/$(APP4TARGET).def + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + + diff --git a/shell/source/win32/zipfile/zipexcptn.cxx b/shell/source/win32/zipfile/zipexcptn.cxx new file mode 100644 index 000000000..9b9054ceb --- /dev/null +++ b/shell/source/win32/zipfile/zipexcptn.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 . + */ + +#include <global.hxx> +#include "zipexcptn.hxx" + + +RuntimeException::RuntimeException(int Error) : + m_Error(Error) +{ +} + + +RuntimeException::~RuntimeException() noexcept +{ +} + + +int RuntimeException::GetErrorCode() const +{ + return m_Error; +} + + +ZipException::ZipException(int Error) : + RuntimeException(Error) +{ +} + + +const char* ZipException::what() const noexcept +{ + return nullptr; +} + + +Win32Exception::Win32Exception(int Error) : + RuntimeException(Error), + m_MsgBuff(nullptr) +{ +} + + +Win32Exception::~Win32Exception() noexcept +{ + if (m_MsgBuff) + LocalFree(m_MsgBuff); +} + + +const char* Win32Exception::what() const noexcept +{ + if (m_MsgBuff == nullptr) + { + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + GetErrorCode(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + reinterpret_cast<LPSTR>(&m_MsgBuff), + 0, + nullptr); + } + + return m_MsgBuff; +} + + +ZipContentMissException::ZipContentMissException(int Error) : + ZipException(Error) +{ +} + + +AccessViolationException::AccessViolationException(int Error) : + Win32Exception(Error) +{ +} + + +IOException::IOException(int Error) : + Win32Exception(Error) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/zipfile/zipexcptn.hxx b/shell/source/win32/zipfile/zipexcptn.hxx new file mode 100644 index 000000000..eb4911d9b --- /dev/null +++ b/shell/source/win32/zipfile/zipexcptn.hxx @@ -0,0 +1,79 @@ +/* -*- 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 . + */ + +// ZipException.h: interface for the ZipException class. + +#ifndef INCLUDED_SHELL_SOURCE_WIN32_ZIPFILE_ZIPEXCPTN_HXX +#define INCLUDED_SHELL_SOURCE_WIN32_ZIPFILE_ZIPEXCPTN_HXX + +#include <stdexcept> + +class RuntimeException : public std::exception +{ +public: + explicit RuntimeException(int Error); + virtual ~RuntimeException() noexcept override; + + int GetErrorCode() const; + +private: + int m_Error; +}; + +class ZipException : public RuntimeException +{ +public: + explicit ZipException(int Error); + + virtual const char* what() const noexcept override; +}; + +class Win32Exception : public RuntimeException +{ +public: + explicit Win32Exception(int Error); + virtual ~Win32Exception() noexcept override; + + virtual const char* what() const noexcept override; + +private: + mutable char* m_MsgBuff; +}; + +class ZipContentMissException : public ZipException +{ +public: + explicit ZipContentMissException(int Error); +}; + +class AccessViolationException : public Win32Exception +{ +public: + explicit AccessViolationException(int Error); +}; + +class IOException : public Win32Exception +{ +public: + explicit IOException(int Error); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/shell/source/win32/zipfile/zipfile.cxx b/shell/source/win32/zipfile/zipfile.cxx new file mode 100644 index 000000000..19203e631 --- /dev/null +++ b/shell/source/win32/zipfile/zipfile.cxx @@ -0,0 +1,552 @@ +/* -*- 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 "zipexcptn.hxx" +#include <zipfile.hxx> +#include <global.hxx> +#include <types.hxx> +#include <stream_helper.hxx> + +#include <malloc.h> +#include <algorithm> +#include <memory> + +#include <string.h> + +#include <o3tl/safeint.hxx> + +#include <zlib.h> + +namespace +{ + +struct LocalFileHeader +{ + unsigned short min_version; + unsigned short general_flag; + unsigned short compression; + unsigned short lastmod_time; + unsigned short lastmod_date; + unsigned crc32; + unsigned compressed_size; + unsigned uncompressed_size; + unsigned short filename_size; + unsigned short extra_field_size; + std::string filename; + std::string extra_field; + LocalFileHeader() + : min_version(0), general_flag(0), compression(0), lastmod_time(0), lastmod_date(0), + crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0), extra_field_size(0), + filename(), extra_field() {} +}; + +struct CentralDirectoryEntry +{ + unsigned short creator_version; + unsigned short min_version; + unsigned short general_flag; + unsigned short compression; + unsigned short lastmod_time; + unsigned short lastmod_date; + unsigned crc32; + unsigned compressed_size; + unsigned uncompressed_size; + unsigned short filename_size; + unsigned short extra_field_size; + unsigned short file_comment_size; + unsigned short disk_num; + unsigned short internal_attr; + unsigned external_attr; + unsigned offset; + std::string filename; + std::string extra_field; + std::string file_comment; + CentralDirectoryEntry() + : creator_version(0), min_version(0), general_flag(0), compression(0), lastmod_time(0), + lastmod_date(0), crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0), + extra_field_size(0), file_comment_size(0), disk_num(0), internal_attr(0), + external_attr(0), offset(0), filename(), extra_field(), file_comment() {} +}; + +struct CentralDirectoryEnd +{ + unsigned short disk_num; + unsigned short cdir_disk; + unsigned short disk_entries; + unsigned short cdir_entries; + unsigned cdir_size; + unsigned cdir_offset; + unsigned short comment_size; + std::string comment; + CentralDirectoryEnd() + : disk_num(0), cdir_disk(0), disk_entries(0), cdir_entries(0), + cdir_size(0), cdir_offset(0), comment_size(0), comment() {} +}; + +#define CDIR_ENTRY_SIG 0x02014b50 +#define LOC_FILE_HEADER_SIG 0x04034b50 +#define CDIR_END_SIG 0x06054b50 + +// This little lot performs in a truly appalling way without +// buffering eg. on an IStream. + +unsigned short readShort(StreamInterface *stream) +{ + if (!stream || stream->stell() == -1) + throw IOException(-1); + unsigned short tmpBuf; + unsigned long numBytesRead = stream->sread( + reinterpret_cast<unsigned char *>( &tmpBuf ), 2); + if (numBytesRead != 2) + throw IOException(-1); + return tmpBuf; +} + +unsigned readInt(StreamInterface *stream) +{ + if (!stream || stream->stell() == -1) + throw IOException(-1); + unsigned tmpBuf; + unsigned long numBytesRead = stream->sread( + reinterpret_cast<unsigned char *>( &tmpBuf ), 4); + if (numBytesRead != 4) + throw IOException(-1); + return tmpBuf; +} + +std::string readString(StreamInterface *stream, unsigned long size) +{ + if (!stream || stream->stell() == -1) + throw IOException(-1); + auto tmp = std::make_unique<unsigned char[]>(size); + unsigned long numBytesRead = stream->sread(tmp.get(), size); + if (numBytesRead != size) + { + throw IOException(-1); + } + + std::string aStr(reinterpret_cast<char *>(tmp.get()), size); + return aStr; +} + +bool readCentralDirectoryEnd(StreamInterface *stream, CentralDirectoryEnd &end) +{ + try + { + unsigned signature = readInt(stream); + if (signature != CDIR_END_SIG) + return false; + + end.disk_num = readShort(stream); + end.cdir_disk = readShort(stream); + end.disk_entries = readShort(stream); + end.cdir_entries = readShort(stream); + end.cdir_size = readInt(stream); + end.cdir_offset = readInt(stream); + end.comment_size = readShort(stream); + end.comment.assign(readString(stream, end.comment_size)); + } + catch (...) + { + return false; + } + return true; +} + +bool readCentralDirectoryEntry(StreamInterface *stream, CentralDirectoryEntry &entry) +{ + try + { + unsigned signature = readInt(stream); + if (signature != CDIR_ENTRY_SIG) + return false; + + entry.creator_version = readShort(stream); + entry.min_version = readShort(stream); + entry.general_flag = readShort(stream); + entry.compression = readShort(stream); + entry.lastmod_time = readShort(stream); + entry.lastmod_date = readShort(stream); + entry.crc32 = readInt(stream); + entry.compressed_size = readInt(stream); + entry.uncompressed_size = readInt(stream); + entry.filename_size = readShort(stream); + entry.extra_field_size = readShort(stream); + entry.file_comment_size = readShort(stream); + entry.disk_num = readShort(stream); + entry.internal_attr = readShort(stream); + entry.external_attr = readInt(stream); + entry.offset = readInt(stream); + entry.filename.assign(readString(stream, entry.filename_size)); + entry.extra_field.assign(readString(stream, entry.extra_field_size)); + entry.file_comment.assign(readString(stream, entry.file_comment_size)); + } + catch (...) + { + return false; + } + return true; +} + +bool readLocalFileHeader(StreamInterface *stream, LocalFileHeader &header) +{ + try + { + unsigned signature = readInt(stream); + if (signature != LOC_FILE_HEADER_SIG) + return false; + + header.min_version = readShort(stream); + header.general_flag = readShort(stream); + header.compression = readShort(stream); + header.lastmod_time = readShort(stream); + header.lastmod_date = readShort(stream); + header.crc32 = readInt(stream); + header.compressed_size = readInt(stream); + header.uncompressed_size = readInt(stream); + header.filename_size = readShort(stream); + header.extra_field_size = readShort(stream); + header.filename.assign(readString(stream, header.filename_size)); + header.extra_field.assign(readString(stream, header.extra_field_size)); + } + catch (...) + { + return false; + } + return true; +} + +bool areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry) +{ + if (header.min_version != entry.min_version) + return false; + if (header.general_flag != entry.general_flag) + return false; + if (header.compression != entry.compression) + return false; + if (!(header.general_flag & 0x08)) + { + if (header.crc32 != entry.crc32) + return false; + if (header.compressed_size != entry.compressed_size) + return false; + if (header.uncompressed_size != entry.uncompressed_size) + return false; + } + return true; +} + +#define BLOCK_SIZE 0x800 + +bool findSignatureAtOffset(StreamInterface *stream, unsigned long nOffset) +{ + // read in reasonably sized chunk, and read more, to get overlapping sigs + unsigned char aBuffer[ BLOCK_SIZE + 4 ]; + + stream->sseek(nOffset, SEEK_SET); + + unsigned long nBytesRead = stream->sread(aBuffer, sizeof(aBuffer)); + + for (long n = nBytesRead - 4; n >= 0; n--) + { + if (aBuffer[n ] == 0x50 && aBuffer[n+1] == 0x4b && + aBuffer[n+2] == 0x05 && aBuffer[n+3] == 0x06) + { // a palpable hit ... + stream->sseek(nOffset + n, SEEK_SET); + return true; + } + } + + return false; +} + +bool findCentralDirectoryEnd(StreamInterface *stream) +{ + if (!stream) + return false; + + stream->sseek(0,SEEK_END); + + long nLength = stream->stell(); + if (nLength == -1) + return false; + + try + { + for (long nOffset = nLength - BLOCK_SIZE - 4; + nOffset > 0; nOffset -= BLOCK_SIZE) + { + if (findSignatureAtOffset(stream, nOffset)) + return true; + } + return findSignatureAtOffset(stream, 0); + } + catch (...) + { + return false; + } +} + +bool isZipStream(StreamInterface *stream) +{ + if (!findCentralDirectoryEnd(stream)) + return false; + CentralDirectoryEnd end; + if (!readCentralDirectoryEnd(stream, end)) + return false; + stream->sseek(end.cdir_offset, SEEK_SET); + CentralDirectoryEntry entry; + if (!readCentralDirectoryEntry(stream, entry)) + return false; + stream->sseek(entry.offset, SEEK_SET); + LocalFileHeader header; + if (!readLocalFileHeader(stream, header)) + return false; + if (!areHeadersConsistent(header, entry)) + return false; + return true; +} + +} // anonymous namespace + +namespace internal +{ + +namespace { + +/* for case in-sensitive string comparison */ +struct stricmp +{ + explicit stricmp(const std::string &str) : str_(str) + {} + + bool operator() (const std::string &other) + { + return (0 == _stricmp(str_.c_str(), other.c_str())); + } + + std::string str_; +}; + +} + +} // namespace internal + +/** Checks whether a file is a zip file or not + + @precond The given parameter must be a string with length > 0 + The file must exist + The file must be readable for the current user + + @returns true if the file is a zip file + false if the file is not a zip file + + @throws ParameterException if the given file name is empty + IOException if the specified file doesn't exist + AccessViolationException if read access to the file is denied +*/ +bool ZipFile::IsZipFile(const Filepath_t& /*FileName*/) +{ + return true; +} + +bool ZipFile::IsZipFile(void* /*stream*/) +{ + return true; +} + + +/** Returns whether the version of the specified zip file may be uncompressed with the + currently used zlib version or not + + @precond The given parameter must be a string with length > 0 + The file must exist + The file must be readable for the current user + The file must be a valid zip file + + @returns true if the file may be uncompressed with the currently used zlib + false if the file may not be uncompressed with the currently used zlib + + @throws ParameterException if the given file name is empty + IOException if the specified file doesn't exist or is no zip file + AccessViolationException if read access to the file is denied +*/ +bool ZipFile::IsValidZipFileVersionNumber(const Filepath_t& /*FileName*/) +{ + return true; +} + +bool ZipFile::IsValidZipFileVersionNumber(void* /* stream*/) +{ + return true; +} + + +/** Constructs a zip file from a zip file + + @precond The given parameter must be a string with length > 0 + The file must exist + The file must be readable for the current user + + @throws ParameterException if the given file name is empty + IOException if the specified file doesn't exist or is no valid zip file + AccessViolationException if read access to the file is denied + WrongZipVersionException if the zip file cannot be uncompressed + with the used zlib version +*/ +ZipFile::ZipFile(const Filepath_t &FileName) : + m_pStream(nullptr), + m_bShouldFree(true) +{ + m_pStream = new FileStream(FileName.c_str()); + if (!isZipStream(m_pStream)) + { + delete m_pStream; + m_pStream = nullptr; + } +} + +ZipFile::ZipFile(StreamInterface *stream) : + m_pStream(stream), + m_bShouldFree(false) +{ + if (!isZipStream(stream)) + m_pStream = nullptr; +} + + +/** Destroys a zip file +*/ +ZipFile::~ZipFile() +{ + if (m_pStream && m_bShouldFree) + delete m_pStream; +} + +/** Provides an interface to read the uncompressed data of a content of the zip file + + @precond The specified content must exist in this file + ppstm must not be NULL +*/ +void ZipFile::GetUncompressedContent( + const std::string &ContentName, /*inout*/ ZipContentBuffer_t &ContentBuffer) +{ + if (!findCentralDirectoryEnd(m_pStream)) + return; + CentralDirectoryEnd end; + if (!readCentralDirectoryEnd(m_pStream, end)) + return; + m_pStream->sseek(end.cdir_offset, SEEK_SET); + CentralDirectoryEntry entry; + while (m_pStream->stell() != -1 && o3tl::make_unsigned(m_pStream->stell()) < end.cdir_offset + end.cdir_size) + { + if (!readCentralDirectoryEntry(m_pStream, entry)) + return; + if (ContentName.length() == entry.filename_size && !_stricmp(entry.filename.c_str(), ContentName.c_str())) + break; + } + if (ContentName.length() != entry.filename_size) + return; + if (_stricmp(entry.filename.c_str(), ContentName.c_str())) + return; + m_pStream->sseek(entry.offset, SEEK_SET); + LocalFileHeader header; + if (!readLocalFileHeader(m_pStream, header)) + return; + if (!areHeadersConsistent(header, entry)) + return; + ContentBuffer.clear(); + ContentBuffer = ZipContentBuffer_t(entry.uncompressed_size); + if (!entry.compression) + m_pStream->sread(reinterpret_cast<unsigned char *>(ContentBuffer.data()), entry.uncompressed_size); + else + { + int ret; + z_stream strm; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm,-MAX_WBITS); + if (ret != Z_OK) + return; + + std::vector<unsigned char> tmpBuffer(entry.compressed_size); + if (entry.compressed_size != m_pStream->sread(tmpBuffer.data(), entry.compressed_size)) + return; + + strm.avail_in = entry.compressed_size; + strm.next_in = reinterpret_cast<Bytef *>(tmpBuffer.data()); + + strm.avail_out = entry.uncompressed_size; + strm.next_out = reinterpret_cast<Bytef *>(ContentBuffer.data()); + ret = inflate(&strm, Z_FINISH); + switch (ret) + { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + ContentBuffer.clear(); + return; + } + (void)inflateEnd(&strm); + } +} + +/** Returns a list with the content names contained within this file + +*/ +ZipFile::DirectoryPtr_t ZipFile::GetDirectory() const +{ + DirectoryPtr_t dir(new Directory_t()); + if (!findCentralDirectoryEnd(m_pStream)) + return dir; + CentralDirectoryEnd end; + if (!readCentralDirectoryEnd(m_pStream, end)) + return dir; + m_pStream->sseek(end.cdir_offset, SEEK_SET); + CentralDirectoryEntry entry; + while (m_pStream->stell() != -1 && o3tl::make_unsigned(m_pStream->stell()) < end.cdir_offset + end.cdir_size) + { + if (!readCentralDirectoryEntry(m_pStream, entry)) + return dir; + if (entry.filename_size) + dir->push_back(entry.filename); + } + return dir; +} + +/** Convenience query function may even realized with + iterating over a ZipFileDirectory returned by + GetDirectory */ +bool ZipFile::HasContent(const std::string &ContentName) const +{ + //#i34314# we need to compare package content names + //case in-sensitive as it is not defined that such + //names must be handled case sensitive + DirectoryPtr_t dir = GetDirectory(); + + return std::any_of(dir->begin(), dir->end(), internal::stricmp(ContentName)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |