8a83a62410
and cid#1555172 COPY_INSTEAD_OF_MOVE cid#1555184 COPY_INSTEAD_OF_MOVE cid#1555188 COPY_INSTEAD_OF_MOVE cid#1555197 COPY_INSTEAD_OF_MOVE cid#1555209 COPY_INSTEAD_OF_MOVE cid#1555211 COPY_INSTEAD_OF_MOVE cid#1555215 COPY_INSTEAD_OF_MOVE cid#1555216 COPY_INSTEAD_OF_MOVE cid#1555217 COPY_INSTEAD_OF_MOVE cid#1555218 COPY_INSTEAD_OF_MOVE cid#1555222 COPY_INSTEAD_OF_MOVE cid#1556674 COPY_INSTEAD_OF_MOVE cid#1555229 COPY_INSTEAD_OF_MOVE cid#1555233 COPY_INSTEAD_OF_MOVE cid#1555234 COPY_INSTEAD_OF_MOVE cid#1555242 COPY_INSTEAD_OF_MOVE cid#1555250 COPY_INSTEAD_OF_MOVE cid#1555251 COPY_INSTEAD_OF_MOVE cid#1555254 COPY_INSTEAD_OF_MOVE cid#1555304 COPY_INSTEAD_OF_MOVE cid#1555307 COPY_INSTEAD_OF_MOVE cid#1555317 COPY_INSTEAD_OF_MOVE cid#1555329 COPY_INSTEAD_OF_MOVE cid#1555340 COPY_INSTEAD_OF_MOVE cid#1555347 COPY_INSTEAD_OF_MOVE cid#1555352 COPY_INSTEAD_OF_MOVE cid#1555358 COPY_INSTEAD_OF_MOVE cid#1555363 COPY_INSTEAD_OF_MOVE cid#1555365 COPY_INSTEAD_OF_MOVE cid#1555367 COPY_INSTEAD_OF_MOVE cid#1555374 COPY_INSTEAD_OF_MOVE cid#1555380 COPY_INSTEAD_OF_MOVE Change-Id: I343194c10749488a1143e2517ee0638ab19da218 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170888 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
1357 lines
46 KiB
C++
1357 lines
46 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <config_dbus.h>
|
|
#include <config_features.h>
|
|
#include <config_feature_desktop.h>
|
|
|
|
#include <app.hxx>
|
|
#include "officeipcthread.hxx"
|
|
#include "cmdlineargs.hxx"
|
|
#include "dispatchwatcher.hxx"
|
|
#include <com/sun/star/frame/TerminationVetoException.hpp>
|
|
#include <salhelper/thread.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <unotools/bootstrap.hxx>
|
|
#include <utility>
|
|
#include <vcl/svapp.hxx>
|
|
#include <unotools/configmgr.hxx>
|
|
#include <osl/pipe.hxx>
|
|
#include <rtl/digest.h>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <osl/conditn.hxx>
|
|
#include <unotools/moduleoptions.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <osl/file.hxx>
|
|
#include <rtl/process.h>
|
|
#include <o3tl/string_view.hxx>
|
|
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <memory>
|
|
#include <thread>
|
|
|
|
#if ENABLE_DBUS
|
|
#include <dbus/dbus.h>
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
using namespace desktop;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::frame;
|
|
|
|
namespace {
|
|
|
|
char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
|
|
char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
|
|
char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
|
|
|
|
// Receives packets from the pipe until a packet ends in a NUL character (that
|
|
// will not be included in the returned string) or it cannot read anything (due
|
|
// to error or closed pipe, in which case an empty string will be returned to
|
|
// signal failure):
|
|
OString readStringFromPipe(osl::StreamPipe const & pipe) {
|
|
for (OStringBuffer str;;) {
|
|
char buf[1024];
|
|
sal_Int32 n = pipe.recv(buf, std::size(buf));
|
|
if (n <= 0) {
|
|
SAL_INFO("desktop.app", "read empty string");
|
|
return ""_ostr;
|
|
}
|
|
bool end = false;
|
|
if (buf[n - 1] == '\0') {
|
|
end = true;
|
|
--n;
|
|
}
|
|
str.append(buf, n);
|
|
//TODO: how does OStringBuffer.append handle overflow?
|
|
if (end) {
|
|
auto s = str.makeStringAndClear();
|
|
SAL_INFO("desktop.app", "read <" << s << ">");
|
|
return s;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
namespace desktop
|
|
{
|
|
|
|
namespace {
|
|
|
|
class Parser: public CommandLineArgs::Supplier {
|
|
public:
|
|
explicit Parser(OString input): m_input(std::move(input)) {
|
|
if (!m_input.match(ARGUMENT_PREFIX) ||
|
|
m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
|
|
{
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
|
|
switch (m_input[m_index++]) {
|
|
case '0':
|
|
break;
|
|
case '1':
|
|
{
|
|
OUString url;
|
|
if (!next(url, false)) {
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
m_cwdUrl = url;
|
|
break;
|
|
}
|
|
case '2':
|
|
{
|
|
OUString path;
|
|
if (!next(path, false)) {
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
OUString url;
|
|
if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
|
|
osl::FileBase::E_None)
|
|
{
|
|
m_cwdUrl = url;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
}
|
|
|
|
virtual std::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
|
|
|
|
virtual bool next(OUString& argument) override { return next(argument, true); }
|
|
|
|
private:
|
|
bool next(OUString& argument, bool prefix) {
|
|
if (m_index < m_input.getLength()) {
|
|
if (prefix) {
|
|
if (m_input[m_index] != ',') {
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
++m_index;
|
|
}
|
|
OStringBuffer b;
|
|
while (m_index < m_input.getLength()) {
|
|
char c = m_input[m_index];
|
|
if (c == ',') {
|
|
break;
|
|
}
|
|
++m_index;
|
|
if (c == '\\') {
|
|
if (m_index >= m_input.getLength())
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
c = m_input[m_index++];
|
|
switch (c) {
|
|
case '0':
|
|
c = '\0';
|
|
break;
|
|
case ',':
|
|
case '\\':
|
|
break;
|
|
default:
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
}
|
|
b.append(c);
|
|
}
|
|
OString b2(b.makeStringAndClear());
|
|
if (!rtl_convertStringToUString(
|
|
&argument.pData, b2.getStr(), b2.getLength(),
|
|
RTL_TEXTENCODING_UTF8,
|
|
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
|
|
RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
|
|
RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
|
|
{
|
|
throw CommandLineArgs::Supplier::Exception();
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::optional< OUString > m_cwdUrl;
|
|
OString m_input;
|
|
sal_Int32 m_index;
|
|
};
|
|
|
|
bool addArgument(OStringBuffer &rArguments, char prefix,
|
|
const OUString &rArgument)
|
|
{
|
|
OString utf8;
|
|
if (!rArgument.convertToString(
|
|
&utf8, RTL_TEXTENCODING_UTF8,
|
|
(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
|
|
RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
|
|
{
|
|
return false;
|
|
}
|
|
rArguments.append(prefix);
|
|
for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
|
|
char c = utf8[i];
|
|
switch (c) {
|
|
case '\0':
|
|
rArguments.append("\\0");
|
|
break;
|
|
case ',':
|
|
rArguments.append("\\,");
|
|
break;
|
|
case '\\':
|
|
rArguments.append("\\\\");
|
|
break;
|
|
default:
|
|
rArguments.append(c);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
rtl::Reference< RequestHandler > RequestHandler::pGlobal;
|
|
|
|
// Turns a string in aMsg such as file:///home/foo/.libreoffice/3
|
|
// Into a hex string of well known length ff132a86...
|
|
static OUString CreateMD5FromString( const OUString& aMsg )
|
|
{
|
|
SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'");
|
|
|
|
rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
|
|
if ( handle )
|
|
{
|
|
const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr());
|
|
sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode );
|
|
sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
|
|
std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
|
|
|
|
rtl_digest_init( handle, pData, nSize );
|
|
rtl_digest_update( handle, pData, nSize );
|
|
rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
|
|
rtl_digest_destroy( handle );
|
|
|
|
// Create hex-value string from the MD5 value to keep the string size minimal
|
|
OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
|
|
for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
|
|
aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 );
|
|
|
|
return aBuffer.makeStringAndClear();
|
|
}
|
|
|
|
return OUString();
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ProcessEventsClass_Impl
|
|
{
|
|
public:
|
|
DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, void );
|
|
DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, void );
|
|
};
|
|
|
|
}
|
|
|
|
IMPL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, pEvent, void )
|
|
{
|
|
// Application events are processed by the Desktop::HandleAppEvent implementation.
|
|
Desktop::HandleAppEvent( *static_cast<ApplicationEvent*>(pEvent) );
|
|
delete static_cast<ApplicationEvent*>(pEvent);
|
|
}
|
|
|
|
IMPL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent, void )
|
|
{
|
|
// Documents requests are processed by the RequestHandler implementation
|
|
ProcessDocumentsRequest* pDocsRequest = static_cast<ProcessDocumentsRequest*>(pEvent);
|
|
RequestHandler::ExecuteCmdLineRequests(*pDocsRequest, false);
|
|
delete pDocsRequest;
|
|
}
|
|
|
|
static void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
|
|
{
|
|
Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, CallEvent ), pEvent );
|
|
}
|
|
|
|
static void ImplPostProcessDocumentsEvent( std::unique_ptr<ProcessDocumentsRequest> pEvent )
|
|
{
|
|
Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent.release() );
|
|
}
|
|
|
|
oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void* /*pData*/, oslSignalInfo* pInfo)
|
|
{
|
|
if( pInfo->Signal == osl_Signal_Terminate )
|
|
RequestHandler::Disable();
|
|
return osl_Signal_ActCallNextHdl;
|
|
}
|
|
|
|
|
|
// The RequestHandlerController implementation is a bookkeeper for all pending requests
|
|
// that were created by the RequestHandler. The requests are waiting to be processed by
|
|
// our framework loadComponentFromURL function (e.g. open/print request).
|
|
// During shutdown the framework is asking RequestHandlerController about pending requests.
|
|
// If there are pending requests framework has to stop the shutdown process. It is waiting
|
|
// for these requests because framework is not able to handle shutdown and open a document
|
|
// concurrently.
|
|
|
|
|
|
// XServiceInfo
|
|
OUString SAL_CALL RequestHandlerController::getImplementationName()
|
|
{
|
|
return u"com.sun.star.comp.RequestHandlerController"_ustr;
|
|
}
|
|
|
|
sal_Bool RequestHandlerController::supportsService(
|
|
OUString const & ServiceName)
|
|
{
|
|
return cppu::supportsService(this, ServiceName);
|
|
}
|
|
|
|
Sequence< OUString > SAL_CALL RequestHandlerController::getSupportedServiceNames()
|
|
{
|
|
return { };
|
|
}
|
|
|
|
// XEventListener
|
|
void SAL_CALL RequestHandlerController::disposing( const EventObject& )
|
|
{
|
|
}
|
|
|
|
// XTerminateListener
|
|
void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
|
|
{
|
|
// Desktop ask about pending request through our office ipc pipe. We have to
|
|
// be sure that no pending request is waiting because framework is not able to
|
|
// handle shutdown and open a document concurrently.
|
|
|
|
if ( RequestHandler::AreRequestsPending() )
|
|
throw TerminationVetoException();
|
|
RequestHandler::SetDowning();
|
|
}
|
|
|
|
void SAL_CALL RequestHandlerController::notifyTermination( const EventObject& )
|
|
{
|
|
}
|
|
|
|
class IpcThread: public salhelper::Thread {
|
|
public:
|
|
void start(RequestHandler * handler) {
|
|
m_handler = handler;
|
|
launch();
|
|
}
|
|
|
|
virtual void close() = 0;
|
|
|
|
protected:
|
|
explicit IpcThread(char const * name): Thread(name), m_handler(nullptr) {}
|
|
|
|
virtual ~IpcThread() override {}
|
|
|
|
bool process(OString const & arguments, bool * waitProcessed);
|
|
|
|
RequestHandler * m_handler;
|
|
};
|
|
|
|
class PipeIpcThread: public IpcThread {
|
|
public:
|
|
static RequestHandler::Status enable(rtl::Reference<IpcThread> * thread);
|
|
|
|
private:
|
|
explicit PipeIpcThread(osl::Pipe pipe):
|
|
IpcThread("PipeIPC"), pipe_(std::move(pipe))
|
|
{}
|
|
|
|
virtual ~PipeIpcThread() override {}
|
|
|
|
void execute() override;
|
|
|
|
void close() override { pipe_.close(); }
|
|
|
|
osl::Pipe pipe_;
|
|
};
|
|
|
|
#if ENABLE_DBUS
|
|
|
|
namespace {
|
|
|
|
struct DbusConnectionHolder {
|
|
explicit DbusConnectionHolder(DBusConnection * theConnection):
|
|
connection(theConnection)
|
|
{}
|
|
|
|
DbusConnectionHolder(DbusConnectionHolder && other): connection(nullptr)
|
|
{ std::swap(connection, other.connection); }
|
|
|
|
~DbusConnectionHolder() {
|
|
if (connection != nullptr) {
|
|
dbus_connection_close(connection);
|
|
dbus_connection_unref(connection);
|
|
}
|
|
}
|
|
|
|
DBusConnection * connection;
|
|
};
|
|
|
|
struct DbusMessageHolder {
|
|
explicit DbusMessageHolder(DBusMessage * theMessage): message(theMessage) {}
|
|
|
|
~DbusMessageHolder() { clear(); }
|
|
|
|
void clear() {
|
|
if (message != nullptr) {
|
|
dbus_message_unref(message);
|
|
}
|
|
message = nullptr;
|
|
}
|
|
|
|
DBusMessage * message;
|
|
|
|
private:
|
|
DbusMessageHolder(DbusMessageHolder const &) = delete;
|
|
DbusMessageHolder& operator =(DbusMessageHolder const &) = delete;
|
|
};
|
|
|
|
}
|
|
|
|
class DbusIpcThread: public IpcThread {
|
|
public:
|
|
static RequestHandler::Status enable(rtl::Reference<IpcThread> * thread);
|
|
|
|
private:
|
|
explicit DbusIpcThread(DbusConnectionHolder && connection):
|
|
IpcThread("DbusIPC"), connection_(std::move(connection))
|
|
{}
|
|
|
|
virtual ~DbusIpcThread() override {}
|
|
|
|
void execute() override;
|
|
|
|
void close() override;
|
|
|
|
DbusConnectionHolder connection_;
|
|
};
|
|
|
|
RequestHandler::Status DbusIpcThread::enable(rtl::Reference<IpcThread> * thread)
|
|
{
|
|
assert(thread != nullptr);
|
|
if (!dbus_threads_init_default()) {
|
|
SAL_WARN("desktop.app", "dbus_threads_init_default failed");
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
DBusError e;
|
|
dbus_error_init(&e);
|
|
DbusConnectionHolder con(dbus_bus_get_private(DBUS_BUS_SESSION, &e));
|
|
assert((con.connection == nullptr) == bool(dbus_error_is_set(&e)));
|
|
if (con.connection == nullptr) {
|
|
SAL_WARN(
|
|
"desktop.app",
|
|
"dbus_bus_get_private failed with: " << e.name << ": "
|
|
<< e.message);
|
|
dbus_error_free(&e);
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
for (;;) {
|
|
int n = dbus_bus_request_name(
|
|
con.connection, "org.libreoffice.LibreOfficeIpc0",
|
|
DBUS_NAME_FLAG_DO_NOT_QUEUE, &e);
|
|
assert((n == -1) == bool(dbus_error_is_set(&e)));
|
|
switch (n) {
|
|
case -1:
|
|
SAL_WARN(
|
|
"desktop.app",
|
|
"dbus_bus_request_name failed with: " << e.name << ": "
|
|
<< e.message);
|
|
dbus_error_free(&e);
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
|
|
*thread = new DbusIpcThread(std::move(con));
|
|
return RequestHandler::IPC_STATUS_OK;
|
|
case DBUS_REQUEST_NAME_REPLY_EXISTS:
|
|
{
|
|
OStringBuffer buf(ARGUMENT_PREFIX);
|
|
OUString arg;
|
|
if (!(utl::Bootstrap::getProcessWorkingDir(arg)
|
|
&& addArgument(buf, '1', arg)))
|
|
{
|
|
buf.append('0');
|
|
}
|
|
sal_uInt32 narg = rtl_getAppCommandArgCount();
|
|
for (sal_uInt32 i = 0; i != narg; ++i) {
|
|
rtl_getAppCommandArg(i, &arg.pData);
|
|
if (!addArgument(buf, ',', arg)) {
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
}
|
|
char const * argstr = buf.getStr();
|
|
DbusMessageHolder msg(
|
|
dbus_message_new_method_call(
|
|
"org.libreoffice.LibreOfficeIpc0",
|
|
"/org/libreoffice/LibreOfficeIpc0",
|
|
"org.libreoffice.LibreOfficeIpcIfc0", "Execute"));
|
|
if (msg.message == nullptr) {
|
|
SAL_WARN(
|
|
"desktop.app", "dbus_message_new_method_call failed");
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
DBusMessageIter it;
|
|
dbus_message_iter_init_append(msg.message, &it);
|
|
if (!dbus_message_iter_append_basic(
|
|
&it, DBUS_TYPE_STRING, &argstr))
|
|
{
|
|
SAL_WARN(
|
|
"desktop.app", "dbus_message_iter_append_basic failed");
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
DbusMessageHolder repl(
|
|
dbus_connection_send_with_reply_and_block(
|
|
con.connection, msg.message, 0x7FFFFFFF, &e));
|
|
assert(
|
|
(repl.message == nullptr) == bool(dbus_error_is_set(&e)));
|
|
if (repl.message == nullptr) {
|
|
SAL_INFO(
|
|
"desktop.app",
|
|
"dbus_connection_send_with_reply_and_block failed"
|
|
" with: " << e.name << ": " << e.message);
|
|
dbus_error_free(&e);
|
|
break;
|
|
}
|
|
return RequestHandler::IPC_STATUS_2ND_OFFICE;
|
|
}
|
|
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
|
|
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
|
|
SAL_WARN(
|
|
"desktop.app",
|
|
"dbus_bus_request_name failed with unexpected " << +n);
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
default:
|
|
for (;;) std::abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbusIpcThread::execute()
|
|
{
|
|
assert(m_handler != nullptr);
|
|
m_handler->cReady.wait();
|
|
for (;;) {
|
|
{
|
|
osl::MutexGuard g(RequestHandler::GetMutex());
|
|
if (m_handler->mState == RequestHandler::State::Downing) {
|
|
break;
|
|
}
|
|
}
|
|
if (!dbus_connection_read_write(connection_.connection, -1)) {
|
|
break;
|
|
}
|
|
for (;;) {
|
|
DbusMessageHolder msg(
|
|
dbus_connection_pop_message(connection_.connection));
|
|
if (msg.message == nullptr) {
|
|
break;
|
|
}
|
|
if (!dbus_message_is_method_call(
|
|
msg.message, "org.libreoffice.LibreOfficeIpcIfc0",
|
|
"Execute"))
|
|
{
|
|
SAL_INFO("desktop.app", "unknown DBus message ignored");
|
|
continue;
|
|
}
|
|
DBusMessageIter it;
|
|
if (!dbus_message_iter_init(msg.message, &it)) {
|
|
SAL_WARN(
|
|
"desktop.app", "DBus message without argument ignored");
|
|
continue;
|
|
}
|
|
if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
|
|
SAL_WARN(
|
|
"desktop.app",
|
|
"DBus message with non-string argument ignored");
|
|
continue;
|
|
}
|
|
char const * argstr;
|
|
dbus_message_iter_get_basic(&it, &argstr);
|
|
bool waitProcessed = false;
|
|
{
|
|
osl::MutexGuard g(RequestHandler::GetMutex());
|
|
if (!process(argstr, &waitProcessed)) {
|
|
continue;
|
|
}
|
|
}
|
|
if (waitProcessed) {
|
|
m_handler->cProcessed.wait();
|
|
}
|
|
DbusMessageHolder repl(dbus_message_new_method_return(msg.message));
|
|
if (repl.message == nullptr) {
|
|
SAL_WARN(
|
|
"desktop.app", "dbus_message_new_method_return failed");
|
|
continue;
|
|
}
|
|
dbus_uint32_t serial = 0;
|
|
if (!dbus_connection_send(
|
|
connection_.connection, repl.message, &serial)) {
|
|
SAL_WARN("desktop.app", "dbus_connection_send failed");
|
|
continue;
|
|
}
|
|
dbus_connection_flush(connection_.connection);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbusIpcThread::close() {
|
|
assert(connection_.connection != nullptr);
|
|
// Make dbus_connection_read_write fall out of internal poll call blocking
|
|
// on POLLIN:
|
|
int fd;
|
|
if (!dbus_connection_get_socket(connection_.connection, &fd)) {
|
|
SAL_WARN("desktop.app", "dbus_connection_get_socket failed");
|
|
return;
|
|
}
|
|
if (shutdown(fd, SHUT_RD) == -1) {
|
|
auto const e = errno;
|
|
SAL_WARN("desktop.app", "shutdown failed with errno " << e);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
::osl::Mutex& RequestHandler::GetMutex()
|
|
{
|
|
static ::osl::Mutex theRequestHandlerMutex;
|
|
return theRequestHandlerMutex;
|
|
}
|
|
|
|
void RequestHandler::SetDowning()
|
|
{
|
|
// We have the order to block all incoming requests. Framework
|
|
// wants to shutdown and we have to make sure that no loading/printing
|
|
// requests are executed anymore.
|
|
::osl::MutexGuard aGuard( GetMutex() );
|
|
|
|
if ( pGlobal.is() )
|
|
pGlobal->mState = State::Downing;
|
|
}
|
|
|
|
void RequestHandler::EnableRequests()
|
|
{
|
|
// switch between just queueing the requests and executing them
|
|
::osl::MutexGuard aGuard( GetMutex() );
|
|
|
|
if ( pGlobal.is() )
|
|
{
|
|
if (pGlobal->mState != State::Downing) {
|
|
pGlobal->mState = State::RequestsEnabled;
|
|
}
|
|
ProcessDocumentsRequest aEmptyReq(std::nullopt);
|
|
// trigger already queued requests
|
|
RequestHandler::ExecuteCmdLineRequests(aEmptyReq, true);
|
|
}
|
|
}
|
|
|
|
bool RequestHandler::AreRequestsPending()
|
|
{
|
|
// Give info about pending requests
|
|
::osl::MutexGuard aGuard( GetMutex() );
|
|
if ( pGlobal.is() )
|
|
return ( pGlobal->mnPendingRequests > 0 );
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void RequestHandler::RequestsCompleted()
|
|
{
|
|
// Remove nCount pending requests from our internal counter
|
|
::osl::MutexGuard aGuard( GetMutex() );
|
|
if ( pGlobal.is() )
|
|
{
|
|
if ( pGlobal->mnPendingRequests > 0 )
|
|
pGlobal->mnPendingRequests --;
|
|
}
|
|
}
|
|
|
|
RequestHandler::Status RequestHandler::Enable(bool ipc)
|
|
{
|
|
::osl::MutexGuard aGuard( GetMutex() );
|
|
|
|
if( pGlobal.is() )
|
|
return IPC_STATUS_OK;
|
|
|
|
#if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX || defined(EMSCRIPTEN)
|
|
ipc = false;
|
|
#endif
|
|
|
|
if (!ipc) {
|
|
pGlobal = new RequestHandler;
|
|
return IPC_STATUS_OK;
|
|
}
|
|
|
|
enum class Kind { Pipe, Dbus };
|
|
Kind kind;
|
|
#if ENABLE_DBUS
|
|
kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
|
|
#else
|
|
kind = Kind::Pipe;
|
|
#endif
|
|
rtl::Reference<IpcThread> thread;
|
|
Status stat = Status(); // silence bogus potentially-uninitialized warnings
|
|
switch (kind) {
|
|
case Kind::Pipe:
|
|
stat = PipeIpcThread::enable(&thread);
|
|
break;
|
|
case Kind::Dbus:
|
|
#if ENABLE_DBUS
|
|
stat = DbusIpcThread::enable(&thread);
|
|
break;
|
|
#endif
|
|
default:
|
|
assert(false);
|
|
}
|
|
assert(thread.is() == (stat == IPC_STATUS_OK));
|
|
if (stat == IPC_STATUS_OK) {
|
|
pGlobal = new RequestHandler;
|
|
pGlobal->mIpcThread = thread;
|
|
pGlobal->mIpcThread->start(pGlobal.get());
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
RequestHandler::Status PipeIpcThread::enable(rtl::Reference<IpcThread> * thread)
|
|
{
|
|
assert(thread != nullptr);
|
|
|
|
// The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
|
|
// this information from a unotools implementation.
|
|
OUString aUserInstallPath;
|
|
::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
|
|
if (aLocateResult != utl::Bootstrap::PATH_EXISTS
|
|
&& aLocateResult != utl::Bootstrap::PATH_VALID)
|
|
{
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
|
|
// Try to determine if we are the first office or not! This should prevent multiple
|
|
// access to the user directory !
|
|
// First we try to create our pipe if this fails we try to connect. We have to do this
|
|
// in a loop because the other office can crash or shutdown between createPipe
|
|
// and connectPipe!!
|
|
auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
|
|
|
|
// Check result to create a hash code from the user install path
|
|
if ( aUserInstallPathHashCode.isEmpty() )
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
|
|
|
|
osl::Pipe pipe;
|
|
enum PipeMode
|
|
{
|
|
PIPEMODE_DONTKNOW,
|
|
PIPEMODE_CREATED,
|
|
PIPEMODE_CONNECTED
|
|
};
|
|
PipeMode nPipeMode = PIPEMODE_DONTKNOW;
|
|
|
|
OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
|
|
do
|
|
{
|
|
osl::Security security;
|
|
|
|
// Try to create pipe
|
|
if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
|
|
{
|
|
// Pipe created
|
|
nPipeMode = PIPEMODE_CREATED;
|
|
}
|
|
else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
|
|
{
|
|
osl::StreamPipe aStreamPipe(pipe.getHandle());
|
|
if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
|
|
{
|
|
// Pipe connected to first office
|
|
nPipeMode = PIPEMODE_CONNECTED;
|
|
}
|
|
else
|
|
{
|
|
// Pipe connection failed (other office exited or crashed)
|
|
std::this_thread::sleep_for( std::chrono::milliseconds(500) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
oslPipeError eReason = pipe.getError();
|
|
if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
|
|
return RequestHandler::IPC_STATUS_PIPE_ERROR;
|
|
|
|
// Wait for second office to be ready
|
|
std::this_thread::sleep_for( std::chrono::milliseconds(10) );
|
|
}
|
|
|
|
} while ( nPipeMode == PIPEMODE_DONTKNOW );
|
|
|
|
if ( nPipeMode == PIPEMODE_CREATED )
|
|
{
|
|
// Seems we are the one and only, so create listening thread
|
|
*thread = new PipeIpcThread(std::move(pipe));
|
|
return RequestHandler::IPC_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
// Seems another office is running. Pipe arguments to it and self terminate
|
|
osl::StreamPipe aStreamPipe(pipe.getHandle());
|
|
|
|
OStringBuffer aArguments(ARGUMENT_PREFIX);
|
|
OUString cwdUrl;
|
|
if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
|
|
addArgument(aArguments, '1', cwdUrl)))
|
|
{
|
|
aArguments.append('0');
|
|
}
|
|
sal_uInt32 nCount = rtl_getAppCommandArgCount();
|
|
for( sal_uInt32 i=0; i < nCount; i++ )
|
|
{
|
|
rtl_getAppCommandArg( i, &aUserInstallPath.pData );
|
|
if (!addArgument(aArguments, ',', aUserInstallPath)) {
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
}
|
|
aArguments.append('\0');
|
|
// finally, write the string onto the pipe
|
|
SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
|
|
sal_Int32 n = aStreamPipe.write(
|
|
aArguments.getStr(), aArguments.getLength());
|
|
if (n != aArguments.getLength()) {
|
|
SAL_INFO("desktop.app", "short write: " << n);
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
|
|
if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
|
|
{
|
|
// something went wrong
|
|
return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR;
|
|
}
|
|
|
|
return RequestHandler::IPC_STATUS_2ND_OFFICE;
|
|
}
|
|
}
|
|
|
|
void RequestHandler::Disable()
|
|
{
|
|
osl::ClearableMutexGuard aMutex( GetMutex() );
|
|
|
|
if( !pGlobal.is() )
|
|
return;
|
|
|
|
rtl::Reference< RequestHandler > handler(pGlobal);
|
|
pGlobal.clear();
|
|
|
|
handler->mState = State::Downing;
|
|
if (handler->mIpcThread.is()) {
|
|
handler->mIpcThread->close();
|
|
}
|
|
|
|
// release mutex to avoid deadlocks
|
|
aMutex.clear();
|
|
|
|
handler->cReady.set();
|
|
|
|
// exit gracefully and join
|
|
if (handler->mIpcThread.is())
|
|
{
|
|
handler->mIpcThread->join();
|
|
handler->mIpcThread.clear();
|
|
}
|
|
|
|
handler->cReady.reset();
|
|
}
|
|
|
|
RequestHandler::RequestHandler() :
|
|
mState( State::Starting ),
|
|
mnPendingRequests( 0 )
|
|
{
|
|
}
|
|
|
|
RequestHandler::~RequestHandler()
|
|
{
|
|
assert(!mIpcThread.is());
|
|
}
|
|
|
|
void RequestHandler::SetReady(bool bIsReady)
|
|
{
|
|
osl::MutexGuard g(GetMutex());
|
|
if (pGlobal.is())
|
|
{
|
|
if (bIsReady)
|
|
pGlobal->cReady.set();
|
|
else
|
|
pGlobal->cReady.reset();
|
|
}
|
|
}
|
|
|
|
void RequestHandler::WaitForReady()
|
|
{
|
|
rtl::Reference<RequestHandler> t;
|
|
{
|
|
osl::MutexGuard g(GetMutex());
|
|
t = pGlobal;
|
|
}
|
|
if (t.is())
|
|
{
|
|
t->cReady.wait();
|
|
}
|
|
}
|
|
|
|
bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
|
|
assert(waitProcessed != nullptr);
|
|
|
|
std::unique_ptr< CommandLineArgs > aCmdLineArgs;
|
|
try
|
|
{
|
|
Parser p(arguments);
|
|
aCmdLineArgs.reset( new CommandLineArgs( p ) );
|
|
}
|
|
catch ( const CommandLineArgs::Supplier::Exception & )
|
|
{
|
|
SAL_WARN("desktop.app", "Error in received command line arguments");
|
|
return false;
|
|
}
|
|
|
|
bool bDocRequestSent = false;
|
|
|
|
OUString aUnknown( aCmdLineArgs->GetUnknown() );
|
|
if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
|
|
{
|
|
const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
|
|
|
|
if ( aCmdLineArgs->IsQuickstart() )
|
|
{
|
|
// we have to use application event, because we have to start quickstart service in main thread!!
|
|
ApplicationEvent* pAppEvent =
|
|
new ApplicationEvent(ApplicationEvent::Type::QuickStart);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
|
|
// handle request for acceptor
|
|
std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
|
|
for (auto const& elem : accept)
|
|
{
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::Type::Accept, elem);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
// handle acceptor removal
|
|
std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
|
|
for (auto const& elem : unaccept)
|
|
{
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::Type::Unaccept, elem);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
|
|
std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
|
|
aCmdLineArgs->getCwdUrl()));
|
|
m_handler->cProcessed.reset();
|
|
pRequest->pcProcessed = &m_handler->cProcessed;
|
|
m_handler->mbSuccess = false;
|
|
pRequest->mpbSuccess = &m_handler->mbSuccess;
|
|
|
|
// Print requests are not dependent on the --invisible cmdline argument as they are
|
|
// loaded with the "hidden" flag! So they are always checked.
|
|
pRequest->aPrintList = aCmdLineArgs->GetPrintList();
|
|
bDocRequestSent |= !pRequest->aPrintList.empty();
|
|
pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
|
|
pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
|
|
bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
|
|
pRequest->aConversionList = aCmdLineArgs->GetConversionList();
|
|
pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
|
|
pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
|
|
pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
|
|
pRequest->aStartListParams = aCmdLineArgs->GetStartListParams();
|
|
pRequest->aInFilter = aCmdLineArgs->GetInFilter();
|
|
pRequest->bTextCat = aCmdLineArgs->IsTextCat();
|
|
pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
|
|
bDocRequestSent |= !pRequest->aConversionList.empty();
|
|
|
|
if ( !rCurrentCmdLineArgs.IsInvisible() )
|
|
{
|
|
// Read cmdline args that can open/create documents. As they would open a window
|
|
// they are only allowed if the "--invisible" is currently not used!
|
|
pRequest->aOpenList = aCmdLineArgs->GetOpenList();
|
|
bDocRequestSent |= !pRequest->aOpenList.empty();
|
|
pRequest->aViewList = aCmdLineArgs->GetViewList();
|
|
bDocRequestSent |= !pRequest->aViewList.empty();
|
|
pRequest->aStartList = aCmdLineArgs->GetStartList();
|
|
bDocRequestSent |= !pRequest->aStartList.empty();
|
|
pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
|
|
bDocRequestSent |= !pRequest->aForceOpenList.empty();
|
|
pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
|
|
bDocRequestSent |= !pRequest->aForceNewList.empty();
|
|
|
|
// Special command line args to create an empty document for a given module
|
|
|
|
// #i18338# (lo)
|
|
// we only do this if no document was specified on the command line,
|
|
// since this would be inconsistent with the behaviour of
|
|
// the first process, see OpenClients() (call to OpenDefault()) in app.cxx
|
|
if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
|
|
{
|
|
SvtModuleOptions aOpt;
|
|
SvtModuleOptions::EFactory eFactory = SvtModuleOptions::EFactory::WRITER;
|
|
if ( aCmdLineArgs->IsWriter() )
|
|
eFactory = SvtModuleOptions::EFactory::WRITER;
|
|
else if ( aCmdLineArgs->IsCalc() )
|
|
eFactory = SvtModuleOptions::EFactory::CALC;
|
|
else if ( aCmdLineArgs->IsDraw() )
|
|
eFactory = SvtModuleOptions::EFactory::DRAW;
|
|
else if ( aCmdLineArgs->IsImpress() )
|
|
eFactory = SvtModuleOptions::EFactory::IMPRESS;
|
|
else if ( aCmdLineArgs->IsBase() )
|
|
eFactory = SvtModuleOptions::EFactory::DATABASE;
|
|
else if ( aCmdLineArgs->IsMath() )
|
|
eFactory = SvtModuleOptions::EFactory::MATH;
|
|
else if ( aCmdLineArgs->IsGlobal() )
|
|
eFactory = SvtModuleOptions::EFactory::WRITERGLOBAL;
|
|
else if ( aCmdLineArgs->IsWeb() )
|
|
eFactory = SvtModuleOptions::EFactory::WRITERWEB;
|
|
|
|
if ( !pRequest->aOpenList.empty() )
|
|
pRequest->aModule = aOpt.GetFactoryName( eFactory );
|
|
else
|
|
pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
|
|
bDocRequestSent = true;
|
|
}
|
|
}
|
|
|
|
if ( !aCmdLineArgs->IsQuickstart() ) {
|
|
bool bShowHelp = false;
|
|
OUStringBuffer aHelpURLBuffer;
|
|
if (aCmdLineArgs->IsHelpWriter()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
|
|
} else if (aCmdLineArgs->IsHelpCalc()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
|
|
} else if (aCmdLineArgs->IsHelpDraw()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
|
|
} else if (aCmdLineArgs->IsHelpImpress()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
|
|
} else if (aCmdLineArgs->IsHelpBase()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
|
|
} else if (aCmdLineArgs->IsHelpBasic()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
|
|
} else if (aCmdLineArgs->IsHelpMath()) {
|
|
bShowHelp = true;
|
|
aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
|
|
}
|
|
if (bShowHelp) {
|
|
aHelpURLBuffer.append("?Language="
|
|
+ utl::ConfigManager::getUILocale()
|
|
#if defined UNX
|
|
+ "&System=UNX");
|
|
#elif defined _WIN32
|
|
+ "&System=WIN");
|
|
#endif
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::Type::OpenHelpUrl,
|
|
aHelpURLBuffer.makeStringAndClear());
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
}
|
|
|
|
if ( bDocRequestSent )
|
|
{
|
|
// Send requests to dispatch watcher if we have at least one. The receiver
|
|
// is responsible to delete the request after processing it.
|
|
if ( aCmdLineArgs->HasModuleParam() )
|
|
{
|
|
SvtModuleOptions aOpt;
|
|
|
|
// Support command line parameters to start a module (as preselection)
|
|
if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
|
|
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
|
|
else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
|
|
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
|
|
else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
|
|
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
|
|
else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
|
|
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
|
|
}
|
|
|
|
ImplPostProcessDocumentsEvent( std::move(pRequest) );
|
|
}
|
|
else
|
|
{
|
|
// delete not used request again
|
|
pRequest.reset();
|
|
}
|
|
if (aCmdLineArgs->IsEmpty())
|
|
{
|
|
// no document was sent, just bring Office to front
|
|
ApplicationEvent* pAppEvent =
|
|
new ApplicationEvent(ApplicationEvent::Type::Appear);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
}
|
|
*waitProcessed = bDocRequestSent;
|
|
return true;
|
|
}
|
|
|
|
void PipeIpcThread::execute()
|
|
{
|
|
assert(m_handler != nullptr);
|
|
do
|
|
{
|
|
osl::StreamPipe aStreamPipe;
|
|
oslPipeError nError = pipe_.accept( aStreamPipe );
|
|
|
|
|
|
if( nError == osl_Pipe_E_None )
|
|
{
|
|
// if we receive a request while the office is displaying some dialog or error during
|
|
// bootstrap, that dialogs event loop might get events that are dispatched by this thread
|
|
// we have to wait for cReady to be set by the real main loop.
|
|
// only requests that don't dispatch events may be processed before cReady is set.
|
|
m_handler->cReady.wait();
|
|
|
|
// we might have decided to shutdown while we were sleeping
|
|
if (!RequestHandler::pGlobal.is()) return;
|
|
|
|
// only lock the mutex when processing starts, otherwise we deadlock when the office goes
|
|
// down during wait
|
|
osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
|
|
|
|
if (m_handler->mState == RequestHandler::State::Downing)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// notify client we're ready to process its args:
|
|
SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
|
|
std::size_t n = aStreamPipe.write(
|
|
SEND_ARGUMENTS, std::size(SEND_ARGUMENTS));
|
|
// incl. terminating NUL
|
|
if (n != std::size(SEND_ARGUMENTS)) {
|
|
SAL_WARN("desktop.app", "short write: " << n);
|
|
continue;
|
|
}
|
|
|
|
OString aArguments = readStringFromPipe(aStreamPipe);
|
|
|
|
// Is this a lookup message from another application? if so, ignore
|
|
if (aArguments.isEmpty())
|
|
continue;
|
|
|
|
bool waitProcessed = false;
|
|
if (!process(aArguments, &waitProcessed)) {
|
|
continue;
|
|
}
|
|
|
|
// we don't need the mutex any longer...
|
|
aGuard.clear();
|
|
bool bSuccess = true;
|
|
// wait for processing to finish
|
|
if (waitProcessed)
|
|
{
|
|
m_handler->cProcessed.wait();
|
|
bSuccess = m_handler->mbSuccess;
|
|
}
|
|
if (bSuccess)
|
|
{
|
|
// processing finished, inform the requesting end:
|
|
SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
|
|
n = aStreamPipe.write(PROCESSING_DONE, std::size(PROCESSING_DONE));
|
|
// incl. terminating NUL
|
|
if (n != std::size(PROCESSING_DONE))
|
|
{
|
|
SAL_WARN("desktop.app", "short write: " << n);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
{
|
|
osl::MutexGuard aGuard( RequestHandler::GetMutex() );
|
|
if (m_handler->mState == RequestHandler::State::Downing)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
|
|
std::this_thread::sleep_for( std::chrono::seconds(1) );
|
|
}
|
|
} while( schedule() );
|
|
}
|
|
|
|
static void AddToDispatchList(
|
|
std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
|
|
std::optional< OUString > const & cwdUrl,
|
|
std::vector< OUString > const & aRequestList,
|
|
DispatchWatcher::RequestType nType,
|
|
const OUString& aParam,
|
|
const OUString& aFactory )
|
|
{
|
|
for (auto const& request : aRequestList)
|
|
{
|
|
rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
|
|
}
|
|
}
|
|
|
|
static void AddConversionsToDispatchList(
|
|
std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
|
|
std::optional< OUString > const & cwdUrl,
|
|
std::vector< OUString > const & rRequestList,
|
|
const OUString& rParam,
|
|
const OUString& rPrinterName,
|
|
const OUString& rFactory,
|
|
const OUString& rParamOut,
|
|
std::u16string_view rImgOut,
|
|
const bool isTextCat,
|
|
const bool isScriptCat )
|
|
{
|
|
DispatchWatcher::RequestType nType;
|
|
OUString aParam( rParam );
|
|
|
|
if( !rParam.isEmpty() )
|
|
{
|
|
if ( isTextCat )
|
|
nType = DispatchWatcher::REQUEST_CAT;
|
|
else
|
|
nType = DispatchWatcher::REQUEST_CONVERSION;
|
|
}
|
|
else
|
|
{
|
|
if ( isScriptCat )
|
|
nType = DispatchWatcher::REQUEST_SCRIPT_CAT;
|
|
else
|
|
{
|
|
nType = DispatchWatcher::REQUEST_BATCHPRINT;
|
|
aParam = rPrinterName;
|
|
}
|
|
}
|
|
|
|
OUString aPWD;
|
|
if (cwdUrl)
|
|
{
|
|
aPWD = *cwdUrl;
|
|
}
|
|
else
|
|
{
|
|
utl::Bootstrap::getProcessWorkingDir( aPWD );
|
|
}
|
|
|
|
if (OUString aOutDir(rParamOut.trim()); !aOutDir.isEmpty())
|
|
{
|
|
if (osl::FileBase::getAbsoluteFileURL(aPWD, rParamOut, aOutDir) == osl::FileBase::E_None)
|
|
osl::FileBase::getSystemPathFromFileURL(aOutDir, aOutDir);
|
|
aParam += ";" + aOutDir;
|
|
}
|
|
else
|
|
{
|
|
::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
|
|
aParam += ";" + aPWD;
|
|
}
|
|
|
|
if( !rImgOut.empty() )
|
|
aParam += OUString::Concat("|") + o3tl::trim(rImgOut);
|
|
|
|
for (auto const& request : rRequestList)
|
|
{
|
|
rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct ConditionSetGuard
|
|
{
|
|
osl::Condition* m_pCondition;
|
|
ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
|
|
~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
|
|
};
|
|
|
|
}
|
|
|
|
bool RequestHandler::ExecuteCmdLineRequests(
|
|
ProcessDocumentsRequest& aRequest, bool noTerminate)
|
|
{
|
|
// protect the dispatch list
|
|
osl::ClearableMutexGuard aGuard( GetMutex() );
|
|
|
|
// ensure that Processed flag (if exists) is signaled in any outcome
|
|
ConditionSetGuard aSetGuard(aRequest.pcProcessed);
|
|
|
|
static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
|
|
|
|
// Create dispatch list for dispatch watcher
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, u""_ustr, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, u""_ustr, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, u""_ustr, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, aRequest.aStartListParams, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, u""_ustr, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, u""_ustr, aRequest.aModule );
|
|
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, u""_ustr, aRequest.aModule );
|
|
AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
|
|
bool bShutdown( false );
|
|
|
|
if ( pGlobal.is() )
|
|
{
|
|
if( ! pGlobal->AreRequestsEnabled() )
|
|
{
|
|
// Either starting, or downing - do not process the request, just try to bring Office to front
|
|
ApplicationEvent* pAppEvent =
|
|
new ApplicationEvent(ApplicationEvent::Type::Appear);
|
|
ImplPostForeignAppEvent(pAppEvent);
|
|
return bShutdown;
|
|
}
|
|
|
|
pGlobal->mnPendingRequests += aDispatchList.size();
|
|
if ( !pGlobal->mpDispatchWatcher.is() )
|
|
{
|
|
pGlobal->mpDispatchWatcher = new DispatchWatcher;
|
|
}
|
|
rtl::Reference<DispatchWatcher> dispatchWatcher(
|
|
pGlobal->mpDispatchWatcher);
|
|
|
|
// copy for execute
|
|
std::vector<DispatchWatcher::DispatchRequest> aTempList;
|
|
aTempList.swap( aDispatchList );
|
|
|
|
aGuard.clear();
|
|
|
|
// Execute dispatch requests
|
|
bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
|
|
if (aRequest.mpbSuccess)
|
|
*aRequest.mpbSuccess = true; // signal that we have actually succeeded
|
|
}
|
|
|
|
return bShutdown;
|
|
}
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|