LibreOffice Module desktop (master)  1
officeipcthread.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <config_dbus.h>
23 #include <config_features.h>
24 #include <config_feature_desktop.h>
25 
26 #include <app.hxx>
27 #include "officeipcthread.hxx"
28 #include "cmdlineargs.hxx"
29 #include "dispatchwatcher.hxx"
30 #include <com/sun/star/frame/TerminationVetoException.hpp>
31 #include <salhelper/thread.hxx>
32 #include <sal/log.hxx>
33 #include <unotools/bootstrap.hxx>
34 #include <vcl/svapp.hxx>
35 #include <unotools/configmgr.hxx>
36 #include <osl/pipe.hxx>
37 #include <rtl/digest.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <rtl/instance.hxx>
40 #include <osl/conditn.hxx>
42 #include <rtl/strbuf.hxx>
44 #include <osl/file.hxx>
45 #include <rtl/process.h>
46 
47 #include <cassert>
48 #include <cstdlib>
49 #include <memory>
50 
51 #if ENABLE_DBUS
52 #include <dbus/dbus.h>
53 #include <sys/socket.h>
54 #endif
55 
56 using namespace desktop;
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::frame;
60 
61 namespace {
62 
63 char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
64 char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
65 char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
66 
67 // Receives packets from the pipe until a packet ends in a NUL character (that
68 // will not be included in the returned string) or it cannot read anything (due
69 // to error or closed pipe, in which case an empty string will be returned to
70 // signal failure):
71 OString readStringFromPipe(osl::StreamPipe const & pipe) {
72  for (OStringBuffer str;;) {
73  char buf[1024];
74  sal_Int32 n = pipe.recv(buf, SAL_N_ELEMENTS(buf));
75  if (n <= 0) {
76  SAL_INFO("desktop.app", "read empty string");
77  return "";
78  }
79  bool end = false;
80  if (buf[n - 1] == '\0') {
81  end = true;
82  --n;
83  }
84  str.append(buf, n);
85  //TODO: how does OStringBuffer.append handle overflow?
86  if (end) {
87  auto s = str.makeStringAndClear();
88  SAL_INFO("desktop.app", "read <" << s << ">");
89  return s;
90  }
91  }
92 }
93 
94 }
95 
96 namespace desktop
97 {
98 
99 namespace {
100 
101 class Parser: public CommandLineArgs::Supplier {
102 public:
103  explicit Parser(OString const & input): m_input(input) {
104  if (!m_input.match(ARGUMENT_PREFIX) ||
105  m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
106  {
108  }
109  m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
110  switch (m_input[m_index++]) {
111  case '0':
112  break;
113  case '1':
114  {
115  OUString url;
116  if (!next(&url, false)) {
118  }
119  m_cwdUrl = url;
120  break;
121  }
122  case '2':
123  {
124  OUString path;
125  if (!next(&path, false)) {
127  }
128  OUString url;
129  if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
130  osl::FileBase::E_None)
131  {
132  m_cwdUrl = url;
133  }
134  break;
135  }
136  default:
138  }
139  }
140 
141  virtual std::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
142 
143  virtual bool next(OUString * argument) override { return next(argument, true); }
144 
145 private:
146  bool next(OUString * argument, bool prefix) {
147  OSL_ASSERT(argument != nullptr);
148  if (m_index < m_input.getLength()) {
149  if (prefix) {
150  if (m_input[m_index] != ',') {
152  }
153  ++m_index;
154  }
155  OStringBuffer b;
156  while (m_index < m_input.getLength()) {
157  char c = m_input[m_index];
158  if (c == ',') {
159  break;
160  }
161  ++m_index;
162  if (c == '\\') {
163  if (m_index >= m_input.getLength())
165  c = m_input[m_index++];
166  switch (c) {
167  case '0':
168  c = '\0';
169  break;
170  case ',':
171  case '\\':
172  break;
173  default:
175  }
176  }
177  b.append(c);
178  }
179  OString b2(b.makeStringAndClear());
180  if (!rtl_convertStringToUString(
181  &argument->pData, b2.getStr(), b2.getLength(),
182  RTL_TEXTENCODING_UTF8,
183  (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
184  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
185  RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
186  {
188  }
189  return true;
190  } else {
191  return false;
192  }
193  }
194 
195  std::optional< OUString > m_cwdUrl;
196  OString m_input;
197  sal_Int32 m_index;
198 };
199 
200 bool addArgument(OStringBuffer &rArguments, char prefix,
201  const OUString &rArgument)
202 {
203  OString utf8;
204  if (!rArgument.convertToString(
205  &utf8, RTL_TEXTENCODING_UTF8,
206  (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
207  RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
208  {
209  return false;
210  }
211  rArguments.append(prefix);
212  for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
213  char c = utf8[i];
214  switch (c) {
215  case '\0':
216  rArguments.append("\\0");
217  break;
218  case ',':
219  rArguments.append("\\,");
220  break;
221  case '\\':
222  rArguments.append("\\\\");
223  break;
224  default:
225  rArguments.append(c);
226  break;
227  }
228  }
229  return true;
230 }
231 
232 }
233 
235 
236 // Turns a string in aMsg such as file:///home/foo/.libreoffice/3
237 // Into a hex string of well known length ff132a86...
238 static OUString CreateMD5FromString( const OUString& aMsg )
239 {
240  SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'");
241 
242  rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
243  if ( handle )
244  {
245  const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr());
246  sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode );
247  sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
248  std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
249 
250  rtl_digest_init( handle, pData, nSize );
251  rtl_digest_update( handle, pData, nSize );
252  rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
253  rtl_digest_destroy( handle );
254 
255  // Create hex-value string from the MD5 value to keep the string size minimal
256  OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
257  for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
258  aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 );
259 
260  return aBuffer.makeStringAndClear();
261  }
262 
263  return OUString();
264 }
265 
266 namespace {
267 
268 class ProcessEventsClass_Impl
269 {
270 public:
271  DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, void );
272  DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, void );
273 };
274 
275 }
276 
277 IMPL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, pEvent, void )
278 {
279  // Application events are processed by the Desktop::HandleAppEvent implementation.
280  Desktop::HandleAppEvent( *static_cast<ApplicationEvent*>(pEvent) );
281  delete static_cast<ApplicationEvent*>(pEvent);
282 }
283 
284 IMPL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent, void )
285 {
286  // Documents requests are processed by the RequestHandler implementation
287  ProcessDocumentsRequest* pDocsRequest = static_cast<ProcessDocumentsRequest*>(pEvent);
288  RequestHandler::ExecuteCmdLineRequests(*pDocsRequest, false);
289  delete pDocsRequest;
290 }
291 
293 {
294  Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, CallEvent ), pEvent );
295 }
296 
297 static void ImplPostProcessDocumentsEvent( std::unique_ptr<ProcessDocumentsRequest> pEvent )
298 {
299  Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent.release() );
300 }
301 
302 oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void* /*pData*/, oslSignalInfo* pInfo)
303 {
304  if( pInfo->Signal == osl_Signal_Terminate )
306  return osl_Signal_ActCallNextHdl;
307 }
308 
309 
310 // The RequestHandlerController implementation is a bookkeeper for all pending requests
311 // that were created by the RequestHandler. The requests are waiting to be processed by
312 // our framework loadComponentFromURL function (e.g. open/print request).
313 // During shutdown the framework is asking RequestHandlerController about pending requests.
314 // If there are pending requests framework has to stop the shutdown process. It is waiting
315 // for these requests because framework is not able to handle shutdown and open a document
316 // concurrently.
317 
318 
319 // XServiceInfo
321 {
322  return "com.sun.star.comp.RequestHandlerController";
323 }
324 
326  OUString const & ServiceName)
327 {
328  return cppu::supportsService(this, ServiceName);
329 }
330 
332 {
333  return { };
334 }
335 
336 // XEventListener
337 void SAL_CALL RequestHandlerController::disposing( const EventObject& )
338 {
339 }
340 
341 // XTerminateListener
342 void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
343 {
344  // Desktop ask about pending request through our office ipc pipe. We have to
345  // be sure that no pending request is waiting because framework is not able to
346  // handle shutdown and open a document concurrently.
347 
349  throw TerminationVetoException();
351 }
352 
353 void SAL_CALL RequestHandlerController::notifyTermination( const EventObject& )
354 {
355 }
356 
358 public:
359  void start(RequestHandler * handler) {
360  m_handler = handler;
361  launch();
362  }
363 
364  virtual void close() = 0;
365 
366 protected:
367  explicit IpcThread(char const * name): Thread(name), m_handler(nullptr) {}
368 
369  virtual ~IpcThread() override {}
370 
371  bool process(OString const & arguments, bool * waitProcessed);
372 
374 };
375 
376 class PipeIpcThread: public IpcThread {
377 public:
379 
380 private:
381  explicit PipeIpcThread(osl::Pipe const & pipe):
382  IpcThread("PipeIPC"), pipe_(pipe)
383  {}
384 
385  virtual ~PipeIpcThread() override {}
386 
387  void execute() override;
388 
389  void close() override { pipe_.close(); }
390 
391  osl::Pipe pipe_;
392 };
393 
394 #if ENABLE_DBUS
395 
396 namespace {
397 
398 struct DbusConnectionHolder {
399  explicit DbusConnectionHolder(DBusConnection * theConnection):
400  connection(theConnection)
401  {}
402 
403  DbusConnectionHolder(DbusConnectionHolder && other): connection(nullptr)
404  { std::swap(connection, other.connection); }
405 
406  ~DbusConnectionHolder() {
407  if (connection != nullptr) {
408  dbus_connection_close(connection);
409  dbus_connection_unref(connection);
410  }
411  }
412 
413  DBusConnection * connection;
414 };
415 
416 struct DbusMessageHolder {
417  explicit DbusMessageHolder(DBusMessage * theMessage): message(theMessage) {}
418 
419  ~DbusMessageHolder() { clear(); }
420 
421  void clear() {
422  if (message != nullptr) {
423  dbus_message_unref(message);
424  }
425  message = nullptr;
426  }
427 
428  DBusMessage * message;
429 
430 private:
431  DbusMessageHolder(DbusMessageHolder const &) = delete;
432  DbusMessageHolder& operator =(DbusMessageHolder const &) = delete;
433 };
434 
435 }
436 
437 class DbusIpcThread: public IpcThread {
438 public:
439  static RequestHandler::Status enable(rtl::Reference<IpcThread> * thread);
440 
441 private:
442  explicit DbusIpcThread(DbusConnectionHolder && connection):
443  IpcThread("DbusIPC"), connection_(std::move(connection))
444  {}
445 
446  virtual ~DbusIpcThread() override {}
447 
448  void execute() override;
449 
450  void close() override;
451 
452  DbusConnectionHolder connection_;
453 };
454 
455 RequestHandler::Status DbusIpcThread::enable(rtl::Reference<IpcThread> * thread)
456 {
457  assert(thread != nullptr);
458  if (!dbus_threads_init_default()) {
459  SAL_WARN("desktop.app", "dbus_threads_init_default failed");
461  }
462  DBusError e;
463  dbus_error_init(&e);
464  DbusConnectionHolder con(dbus_bus_get_private(DBUS_BUS_SESSION, &e));
465  assert((con.connection == nullptr) == bool(dbus_error_is_set(&e)));
466  if (con.connection == nullptr) {
467  SAL_WARN(
468  "desktop.app",
469  "dbus_bus_get_private failed with: " << e.name << ": "
470  << e.message);
471  dbus_error_free(&e);
473  }
474  for (;;) {
475  int n = dbus_bus_request_name(
476  con.connection, "org.libreoffice.LibreOfficeIpc0",
477  DBUS_NAME_FLAG_DO_NOT_QUEUE, &e);
478  assert((n == -1) == bool(dbus_error_is_set(&e)));
479  switch (n) {
480  case -1:
481  SAL_WARN(
482  "desktop.app",
483  "dbus_bus_request_name failed with: " << e.name << ": "
484  << e.message);
485  dbus_error_free(&e);
487  case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
488  *thread = new DbusIpcThread(std::move(con));
490  case DBUS_REQUEST_NAME_REPLY_EXISTS:
491  {
492  OStringBuffer buf(ARGUMENT_PREFIX);
493  OUString arg;
495  && addArgument(buf, '1', arg)))
496  {
497  buf.append('0');
498  }
499  sal_uInt32 narg = rtl_getAppCommandArgCount();
500  for (sal_uInt32 i = 0; i != narg; ++i) {
501  rtl_getAppCommandArg(i, &arg.pData);
502  if (!addArgument(buf, ',', arg)) {
504  }
505  }
506  char const * argstr = buf.getStr();
507  DbusMessageHolder msg(
508  dbus_message_new_method_call(
509  "org.libreoffice.LibreOfficeIpc0",
510  "/org/libreoffice/LibreOfficeIpc0",
511  "org.libreoffice.LibreOfficeIpcIfc0", "Execute"));
512  if (msg.message == nullptr) {
513  SAL_WARN(
514  "desktop.app", "dbus_message_new_method_call failed");
516  }
517  DBusMessageIter it;
518  dbus_message_iter_init_append(msg.message, &it);
519  if (!dbus_message_iter_append_basic(
520  &it, DBUS_TYPE_STRING, &argstr))
521  {
522  SAL_WARN(
523  "desktop.app", "dbus_message_iter_append_basic failed");
525  }
526  DbusMessageHolder repl(
527  dbus_connection_send_with_reply_and_block(
528  con.connection, msg.message, 0x7FFFFFFF, &e));
529  assert(
530  (repl.message == nullptr) == bool(dbus_error_is_set(&e)));
531  if (repl.message == nullptr) {
532  SAL_INFO(
533  "desktop.app",
534  "dbus_connection_send_with_reply_and_block failed"
535  " with: " << e.name << ": " << e.message);
536  dbus_error_free(&e);
537  break;
538  }
540  }
541  case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
542  case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
543  SAL_WARN(
544  "desktop.app",
545  "dbus_bus_request_name failed with unexpected " << +n);
547  default:
548  for (;;) std::abort();
549  }
550  }
551 }
552 
553 void DbusIpcThread::execute()
554 {
555  assert(m_handler != nullptr);
556  m_handler->cReady.wait();
557  for (;;) {
558  {
559  osl::MutexGuard g(RequestHandler::GetMutex());
560  if (m_handler->mState == RequestHandler::State::Downing) {
561  break;
562  }
563  }
564  if (!dbus_connection_read_write(connection_.connection, -1)) {
565  break;
566  }
567  for (;;) {
568  DbusMessageHolder msg(
569  dbus_connection_pop_message(connection_.connection));
570  if (msg.message == nullptr) {
571  break;
572  }
573  if (!dbus_message_is_method_call(
574  msg.message, "org.libreoffice.LibreOfficeIpcIfc0",
575  "Execute"))
576  {
577  SAL_INFO("desktop.app", "unknown DBus message ignored");
578  continue;
579  }
580  DBusMessageIter it;
581  if (!dbus_message_iter_init(msg.message, &it)) {
582  SAL_WARN(
583  "desktop.app", "DBus message without argument ignored");
584  continue;
585  }
586  if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
587  SAL_WARN(
588  "desktop.app",
589  "DBus message with non-string argument ignored");
590  continue;
591  }
592  char const * argstr;
593  dbus_message_iter_get_basic(&it, &argstr);
594  bool waitProcessed = false;
595  {
596  osl::MutexGuard g(RequestHandler::GetMutex());
597  if (!process(argstr, &waitProcessed)) {
598  continue;
599  }
600  }
601  if (waitProcessed) {
602  m_handler->cProcessed.wait();
603  }
604  DbusMessageHolder repl(dbus_message_new_method_return(msg.message));
605  if (repl.message == nullptr) {
606  SAL_WARN(
607  "desktop.app", "dbus_message_new_method_return failed");
608  continue;
609  }
610  dbus_uint32_t serial = 0;
611  if (!dbus_connection_send(
612  connection_.connection, repl.message, &serial)) {
613  SAL_WARN("desktop.app", "dbus_connection_send failed");
614  continue;
615  }
616  dbus_connection_flush(connection_.connection);
617  }
618  }
619 }
620 
621 void DbusIpcThread::close() {
622  assert(connection_.connection != nullptr);
623  // Make dbus_connection_read_write fall out of internal poll call blocking
624  // on POLLIN:
625  int fd;
626  if (!dbus_connection_get_socket(connection_.connection, &fd)) {
627  SAL_WARN("desktop.app", "dbus_connection_get_socket failed");
628  return;
629  }
630  if (shutdown(fd, SHUT_RD) == -1) {
631  auto const e = errno;
632  SAL_WARN("desktop.app", "shutdown failed with errno " << e);
633  }
634 }
635 
636 #endif
637 
638 namespace
639 {
640  class theRequestHandlerMutex
641  : public rtl::Static<osl::Mutex, theRequestHandlerMutex> {};
642 }
643 
645 {
646  return theRequestHandlerMutex::get();
647 }
648 
650 {
651  // We have the order to block all incoming requests. Framework
652  // wants to shutdown and we have to make sure that no loading/printing
653  // requests are executed anymore.
654  ::osl::MutexGuard aGuard( GetMutex() );
655 
656  if ( pGlobal.is() )
657  pGlobal->mState = State::Downing;
658 }
659 
661 {
662  // switch between just queueing the requests and executing them
663  ::osl::MutexGuard aGuard( GetMutex() );
664 
665  if ( pGlobal.is() )
666  {
667  if (pGlobal->mState != State::Downing) {
669  }
670  // hit the compiler over the head - this avoids GCC -Werror=maybe-uninitialized
671  std::optional<OUString> tmp;
672  ProcessDocumentsRequest aEmptyReq(tmp);
673  // trigger already queued requests
675  }
676 }
677 
679 {
680  // Give info about pending requests
681  ::osl::MutexGuard aGuard( GetMutex() );
682  if ( pGlobal.is() )
683  return ( pGlobal->mnPendingRequests > 0 );
684  else
685  return false;
686 }
687 
689 {
690  // Remove nCount pending requests from our internal counter
691  ::osl::MutexGuard aGuard( GetMutex() );
692  if ( pGlobal.is() )
693  {
694  if ( pGlobal->mnPendingRequests > 0 )
695  pGlobal->mnPendingRequests --;
696  }
697 }
698 
700 {
701  ::osl::MutexGuard aGuard( GetMutex() );
702 
703  if( pGlobal.is() )
704  return IPC_STATUS_OK;
705 
706 #if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX
707  ipc = false;
708 #endif
709 
710  if (!ipc) {
711  pGlobal = new RequestHandler;
712  return IPC_STATUS_OK;
713  }
714 
715  enum class Kind { Pipe, Dbus };
716  Kind kind;
717 #if ENABLE_DBUS
718  kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
719 #else
720  kind = Kind::Pipe;
721 #endif
723  Status stat = Status(); // silence bogus potentially-uninitialized warnings
724  switch (kind) {
725  case Kind::Pipe:
726  stat = PipeIpcThread::enable(&thread);
727  break;
728  case Kind::Dbus:
729 #if ENABLE_DBUS
730  stat = DbusIpcThread::enable(&thread);
731  break;
732 #endif
733  default:
734  assert(false);
735  }
736  assert(thread.is() == (stat == IPC_STATUS_OK));
737  if (stat == IPC_STATUS_OK) {
738  pGlobal = new RequestHandler;
739  pGlobal->mIpcThread = thread;
740  pGlobal->mIpcThread->start(pGlobal.get());
741  }
742  return stat;
743 }
744 
746 {
747  assert(thread != nullptr);
748 
749  // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
750  // this information from a unotools implementation.
751  OUString aUserInstallPath;
753  if (aLocateResult != utl::Bootstrap::PATH_EXISTS
754  && aLocateResult != utl::Bootstrap::PATH_VALID)
755  {
757  }
758 
759  // Try to determine if we are the first office or not! This should prevent multiple
760  // access to the user directory !
761  // First we try to create our pipe if this fails we try to connect. We have to do this
762  // in a loop because the other office can crash or shutdown between createPipe
763  // and connectPipe!!
764  auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
765 
766  // Check result to create a hash code from the user install path
767  if ( aUserInstallPathHashCode.isEmpty() )
768  return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
769 
770  osl::Pipe pipe;
771  enum PipeMode
772  {
773  PIPEMODE_DONTKNOW,
774  PIPEMODE_CREATED,
775  PIPEMODE_CONNECTED
776  };
777  PipeMode nPipeMode = PIPEMODE_DONTKNOW;
778 
779  OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
780  do
781  {
782  osl::Security security;
783 
784  // Try to create pipe
785  if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
786  {
787  // Pipe created
788  nPipeMode = PIPEMODE_CREATED;
789  }
790  else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
791  {
792  osl::StreamPipe aStreamPipe(pipe.getHandle());
793  if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
794  {
795  // Pipe connected to first office
796  nPipeMode = PIPEMODE_CONNECTED;
797  }
798  else
799  {
800  // Pipe connection failed (other office exited or crashed)
801  TimeValue tval;
802  tval.Seconds = 0;
803  tval.Nanosec = 500000000;
804  salhelper::Thread::wait( tval );
805  }
806  }
807  else
808  {
809  oslPipeError eReason = pipe.getError();
810  if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
812 
813  // Wait for second office to be ready
814  TimeValue aTimeValue;
815  aTimeValue.Seconds = 0;
816  aTimeValue.Nanosec = 10000000; // 10ms
817  salhelper::Thread::wait( aTimeValue );
818  }
819 
820  } while ( nPipeMode == PIPEMODE_DONTKNOW );
821 
822  if ( nPipeMode == PIPEMODE_CREATED )
823  {
824  // Seems we are the one and only, so create listening thread
825  *thread = new PipeIpcThread(pipe);
827  }
828  else
829  {
830  // Seems another office is running. Pipe arguments to it and self terminate
831  osl::StreamPipe aStreamPipe(pipe.getHandle());
832 
833  OStringBuffer aArguments(ARGUMENT_PREFIX);
834  OUString cwdUrl;
835  if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
836  addArgument(aArguments, '1', cwdUrl)))
837  {
838  aArguments.append('0');
839  }
840  sal_uInt32 nCount = rtl_getAppCommandArgCount();
841  for( sal_uInt32 i=0; i < nCount; i++ )
842  {
843  rtl_getAppCommandArg( i, &aUserInstallPath.pData );
844  if (!addArgument(aArguments, ',', aUserInstallPath)) {
846  }
847  }
848  aArguments.append('\0');
849  // finally, write the string onto the pipe
850  SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
851  sal_Int32 n = aStreamPipe.write(
852  aArguments.getStr(), aArguments.getLength());
853  if (n != aArguments.getLength()) {
854  SAL_INFO("desktop.app", "short write: " << n);
856  }
857 
858  if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
859  {
860  // something went wrong
862  }
863 
865  }
866 }
867 
869 {
870  osl::ClearableMutexGuard aMutex( GetMutex() );
871 
872  if( !pGlobal.is() )
873  return;
874 
876  pGlobal.clear();
877 
878  handler->mState = State::Downing;
879  if (handler->mIpcThread.is()) {
880  handler->mIpcThread->close();
881  }
882 
883  // release mutex to avoid deadlocks
884  aMutex.clear();
885 
886  handler->cReady.set();
887 
888  // exit gracefully and join
889  if (handler->mIpcThread.is())
890  {
891  handler->mIpcThread->join();
892  handler->mIpcThread.clear();
893  }
894 
895  handler->cReady.reset();
896 }
897 
899  mState( State::Starting ),
900  mnPendingRequests( 0 )
901 {
902 }
903 
905 {
906  assert(!mIpcThread.is());
907 }
908 
909 void RequestHandler::SetReady(bool bIsReady)
910 {
911  osl::MutexGuard g(GetMutex());
912  if (pGlobal.is())
913  {
914  if (bIsReady)
915  pGlobal->cReady.set();
916  else
917  pGlobal->cReady.reset();
918  }
919 }
920 
922 {
924  {
925  osl::MutexGuard g(GetMutex());
926  t = pGlobal;
927  }
928  if (t.is())
929  {
930  t->cReady.wait();
931  }
932 }
933 
934 bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
935  assert(waitProcessed != nullptr);
936 
937  std::unique_ptr< CommandLineArgs > aCmdLineArgs;
938  try
939  {
940  Parser p(arguments);
941  aCmdLineArgs.reset( new CommandLineArgs( p ) );
942  }
943  catch ( const CommandLineArgs::Supplier::Exception & )
944  {
945  SAL_WARN("desktop.app", "Error in received command line arguments");
946  return false;
947  }
948 
949  bool bDocRequestSent = false;
950 
951  OUString aUnknown( aCmdLineArgs->GetUnknown() );
952  if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
953  {
954  const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
955 
956  if ( aCmdLineArgs->IsQuickstart() )
957  {
958  // we have to use application event, because we have to start quickstart service in main thread!!
959  ApplicationEvent* pAppEvent =
961  ImplPostForeignAppEvent( pAppEvent );
962  }
963 
964  // handle request for acceptor
965  std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
966  for (auto const& elem : accept)
967  {
968  ApplicationEvent* pAppEvent = new ApplicationEvent(
970  ImplPostForeignAppEvent( pAppEvent );
971  }
972  // handle acceptor removal
973  std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
974  for (auto const& elem : unaccept)
975  {
976  ApplicationEvent* pAppEvent = new ApplicationEvent(
978  ImplPostForeignAppEvent( pAppEvent );
979  }
980 
981  std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
982  aCmdLineArgs->getCwdUrl()));
983  m_handler->cProcessed.reset();
984  pRequest->pcProcessed = &m_handler->cProcessed;
985  m_handler->mbSuccess = false;
986  pRequest->mpbSuccess = &m_handler->mbSuccess;
987 
988  // Print requests are not dependent on the --invisible cmdline argument as they are
989  // loaded with the "hidden" flag! So they are always checked.
990  pRequest->aPrintList = aCmdLineArgs->GetPrintList();
991  bDocRequestSent |= !pRequest->aPrintList.empty();
992  pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
993  pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
994  bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
995  pRequest->aConversionList = aCmdLineArgs->GetConversionList();
996  pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
997  pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
998  pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
999  pRequest->aInFilter = aCmdLineArgs->GetInFilter();
1000  pRequest->bTextCat = aCmdLineArgs->IsTextCat();
1001  pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
1002  bDocRequestSent |= !pRequest->aConversionList.empty();
1003 
1004  if ( !rCurrentCmdLineArgs.IsInvisible() )
1005  {
1006  // Read cmdline args that can open/create documents. As they would open a window
1007  // they are only allowed if the "--invisible" is currently not used!
1008  pRequest->aOpenList = aCmdLineArgs->GetOpenList();
1009  bDocRequestSent |= !pRequest->aOpenList.empty();
1010  pRequest->aViewList = aCmdLineArgs->GetViewList();
1011  bDocRequestSent |= !pRequest->aViewList.empty();
1012  pRequest->aStartList = aCmdLineArgs->GetStartList();
1013  bDocRequestSent |= !pRequest->aStartList.empty();
1014  pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
1015  bDocRequestSent |= !pRequest->aForceOpenList.empty();
1016  pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
1017  bDocRequestSent |= !pRequest->aForceNewList.empty();
1018 
1019  // Special command line args to create an empty document for a given module
1020 
1021  // #i18338# (lo)
1022  // we only do this if no document was specified on the command line,
1023  // since this would be inconsistent with the behaviour of
1024  // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
1025  if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
1026  {
1027  SvtModuleOptions aOpt;
1029  if ( aCmdLineArgs->IsWriter() )
1031  else if ( aCmdLineArgs->IsCalc() )
1033  else if ( aCmdLineArgs->IsDraw() )
1035  else if ( aCmdLineArgs->IsImpress() )
1037  else if ( aCmdLineArgs->IsBase() )
1039  else if ( aCmdLineArgs->IsMath() )
1041  else if ( aCmdLineArgs->IsGlobal() )
1043  else if ( aCmdLineArgs->IsWeb() )
1045 
1046  if ( !pRequest->aOpenList.empty() )
1047  pRequest->aModule = aOpt.GetFactoryName( eFactory );
1048  else
1049  pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
1050  bDocRequestSent = true;
1051  }
1052  }
1053 
1054  if ( !aCmdLineArgs->IsQuickstart() ) {
1055  bool bShowHelp = false;
1056  OUStringBuffer aHelpURLBuffer;
1057  if (aCmdLineArgs->IsHelpWriter()) {
1058  bShowHelp = true;
1059  aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
1060  } else if (aCmdLineArgs->IsHelpCalc()) {
1061  bShowHelp = true;
1062  aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
1063  } else if (aCmdLineArgs->IsHelpDraw()) {
1064  bShowHelp = true;
1065  aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
1066  } else if (aCmdLineArgs->IsHelpImpress()) {
1067  bShowHelp = true;
1068  aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
1069  } else if (aCmdLineArgs->IsHelpBase()) {
1070  bShowHelp = true;
1071  aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
1072  } else if (aCmdLineArgs->IsHelpBasic()) {
1073  bShowHelp = true;
1074  aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
1075  } else if (aCmdLineArgs->IsHelpMath()) {
1076  bShowHelp = true;
1077  aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
1078  }
1079  if (bShowHelp) {
1080  aHelpURLBuffer.append("?Language=");
1081  aHelpURLBuffer.append(utl::ConfigManager::getUILocale());
1082 #if defined UNX
1083  aHelpURLBuffer.append("&System=UNX");
1084 #elif defined _WIN32
1085  aHelpURLBuffer.append("&System=WIN");
1086 #endif
1087  ApplicationEvent* pAppEvent = new ApplicationEvent(
1089  aHelpURLBuffer.makeStringAndClear());
1090  ImplPostForeignAppEvent( pAppEvent );
1091  }
1092  }
1093 
1094  if ( bDocRequestSent )
1095  {
1096  // Send requests to dispatch watcher if we have at least one. The receiver
1097  // is responsible to delete the request after processing it.
1098  if ( aCmdLineArgs->HasModuleParam() )
1099  {
1100  SvtModuleOptions aOpt;
1101 
1102  // Support command line parameters to start a module (as preselection)
1103  if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
1104  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
1105  else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
1106  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
1107  else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
1108  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
1109  else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
1110  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
1111  }
1112 
1113  ImplPostProcessDocumentsEvent( std::move(pRequest) );
1114  }
1115  else
1116  {
1117  // delete not used request again
1118  pRequest.reset();
1119  }
1120  if (aCmdLineArgs->IsEmpty())
1121  {
1122  // no document was sent, just bring Office to front
1123  ApplicationEvent* pAppEvent =
1125  ImplPostForeignAppEvent( pAppEvent );
1126  }
1127  }
1128  *waitProcessed = bDocRequestSent;
1129  return true;
1130 }
1131 
1133 {
1134  assert(m_handler != nullptr);
1135  do
1136  {
1137  osl::StreamPipe aStreamPipe;
1138  oslPipeError nError = pipe_.accept( aStreamPipe );
1139 
1140 
1141  if( nError == osl_Pipe_E_None )
1142  {
1143  // if we receive a request while the office is displaying some dialog or error during
1144  // bootstrap, that dialogs event loop might get events that are dispatched by this thread
1145  // we have to wait for cReady to be set by the real main loop.
1146  // only requests that don't dispatch events may be processed before cReady is set.
1147  m_handler->cReady.wait();
1148 
1149  // we might have decided to shutdown while we were sleeping
1150  if (!RequestHandler::pGlobal.is()) return;
1151 
1152  // only lock the mutex when processing starts, otherwise we deadlock when the office goes
1153  // down during wait
1154  osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
1155 
1157  {
1158  break;
1159  }
1160 
1161  // notify client we're ready to process its args:
1162  SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
1163  sal_Int32 n = aStreamPipe.write(
1164  SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
1165  // incl. terminating NUL
1166  if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
1167  SAL_WARN("desktop.app", "short write: " << n);
1168  continue;
1169  }
1170 
1171  OString aArguments = readStringFromPipe(aStreamPipe);
1172 
1173  // Is this a lookup message from another application? if so, ignore
1174  if (aArguments.isEmpty())
1175  continue;
1176 
1177  bool waitProcessed = false;
1178  if (!process(aArguments, &waitProcessed)) {
1179  continue;
1180  }
1181 
1182  // we don't need the mutex any longer...
1183  aGuard.clear();
1184  bool bSuccess = true;
1185  // wait for processing to finish
1186  if (waitProcessed)
1187  {
1188  m_handler->cProcessed.wait();
1189  bSuccess = m_handler->mbSuccess;
1190  }
1191  if (bSuccess)
1192  {
1193  // processing finished, inform the requesting end:
1194  SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
1195  n = aStreamPipe.write(PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
1196  // incl. terminating NUL
1197  if (n != SAL_N_ELEMENTS(PROCESSING_DONE))
1198  {
1199  SAL_WARN("desktop.app", "short write: " << n);
1200  continue;
1201  }
1202  }
1203  }
1204  else
1205  {
1206  {
1207  osl::MutexGuard aGuard( RequestHandler::GetMutex() );
1209  {
1210  break;
1211  }
1212  }
1213 
1214  SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
1215  TimeValue tval;
1216  tval.Seconds = 1;
1217  tval.Nanosec = 0;
1218  salhelper::Thread::wait( tval );
1219  }
1220  } while( schedule() );
1221 }
1222 
1223 static void AddToDispatchList(
1224  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1225  std::optional< OUString > const & cwdUrl,
1226  std::vector< OUString > const & aRequestList,
1228  const OUString& aParam,
1229  const OUString& aFactory )
1230 {
1231  for (auto const& request : aRequestList)
1232  {
1233  rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
1234  }
1235 }
1236 
1238  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1239  std::optional< OUString > const & cwdUrl,
1240  std::vector< OUString > const & rRequestList,
1241  const OUString& rParam,
1242  const OUString& rPrinterName,
1243  const OUString& rFactory,
1244  const OUString& rParamOut,
1245  const OUString& rImgOut,
1246  const bool isTextCat,
1247  const bool isScriptCat )
1248 {
1250  OUString aParam( rParam );
1251 
1252  if( !rParam.isEmpty() )
1253  {
1254  if ( isTextCat )
1256  else
1258  aParam = rParam;
1259  }
1260  else
1261  {
1262  if ( isScriptCat )
1264  else
1265  {
1267  aParam = rPrinterName;
1268  }
1269  }
1270 
1271  OUString aOutDir( rParamOut.trim() );
1272  OUString aImgOut( rImgOut.trim() );
1273  OUString aPWD;
1274  if (cwdUrl)
1275  {
1276  aPWD = *cwdUrl;
1277  }
1278  else
1279  {
1281  }
1282 
1283  if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
1284  ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
1285 
1286  if( !rParamOut.trim().isEmpty() )
1287  {
1288  aParam += ";" + aOutDir;
1289  }
1290  else
1291  {
1292  ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1293  aParam += ";" + aPWD;
1294  }
1295 
1296  if( !rImgOut.trim().isEmpty() )
1297  aParam += "|" + aImgOut;
1298 
1299  for (auto const& request : rRequestList)
1300  {
1301  rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
1302  }
1303 }
1304 
1305 namespace {
1306 
1307 struct ConditionSetGuard
1308 {
1309  osl::Condition* m_pCondition;
1310  ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
1311  ~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
1312 };
1313 
1314 }
1315 
1317  ProcessDocumentsRequest& aRequest, bool noTerminate)
1318 {
1319  // protect the dispatch list
1320  osl::ClearableMutexGuard aGuard( GetMutex() );
1321 
1322  // ensure that Processed flag (if exists) is signaled in any outcome
1323  ConditionSetGuard aSetGuard(aRequest.pcProcessed);
1324 
1325  static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
1326 
1327  // Create dispatch list for dispatch watcher
1328  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, "", aRequest.aModule );
1329  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, "", aRequest.aModule );
1330  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, "", aRequest.aModule );
1331  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, "", aRequest.aModule );
1332  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, "", aRequest.aModule );
1333  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1334  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, "", aRequest.aModule );
1335  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, "", aRequest.aModule );
1336  AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
1337  bool bShutdown( false );
1338 
1339  if ( pGlobal.is() )
1340  {
1341  if( ! pGlobal->AreRequestsEnabled() )
1342  {
1343  // Either starting, or downing - do not process the request, just try to bring Office to front
1344  ApplicationEvent* pAppEvent =
1346  ImplPostForeignAppEvent(pAppEvent);
1347  return bShutdown;
1348  }
1349 
1350  pGlobal->mnPendingRequests += aDispatchList.size();
1351  if ( !pGlobal->mpDispatchWatcher.is() )
1352  {
1353  pGlobal->mpDispatchWatcher = new DispatchWatcher;
1354  }
1355  rtl::Reference<DispatchWatcher> dispatchWatcher(
1356  pGlobal->mpDispatchWatcher);
1357 
1358  // copy for execute
1359  std::vector<DispatchWatcher::DispatchRequest> aTempList;
1360  aTempList.swap( aDispatchList );
1361 
1362  aGuard.clear();
1363 
1364  // Execute dispatch requests
1365  bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
1366  if (aRequest.mpbSuccess)
1367  *aRequest.mpbSuccess = true; // signal that we have actually succeeded
1368  }
1369 
1370  return bShutdown;
1371 }
1372 
1373 }
1374 
1375 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
rtl::Reference< IpcThread > mIpcThread
std::vector< OUString > aOpenList
PipeIpcThread(osl::Pipe const &pipe)
static PathStatus locateUserInstallation(OUString &_rURL)
static bool ExecuteCmdLineRequests(ProcessDocumentsRequest &, bool noTerminate)
static void ImplPostProcessDocumentsEvent(std::unique_ptr< ProcessDocumentsRequest > pEvent)
std::unique_ptr< ContentProperties > pData
bool IsModuleInstalled(EModule eModule) const
static bool getProcessWorkingDir(OUString &rUrl)
std::vector< OUString > aForceNewList
sal_Int64 n
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)
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
Definition: app.cxx:159
std::vector< OUString > aViewList
static rtl::Reference< RequestHandler > pGlobal
virtual void SAL_CALL queryTermination(const css::lang::EventObject &aEvent) override
IMPL_STATIC_LINK(Desktop, ImplInitFilterHdl,::ConvertData &, rData, bool)
Definition: app.cxx:1709
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
sal_uInt16 sal_Unicode
Sequence< PropertyValue > aArguments
virtual OUString SAL_CALL getImplementationName() override
Thread(char const *name)
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
std::optional< OUString > m_cwdUrl
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
int nCount
void start(RequestHandler *handler)
virtual void SAL_CALL notifyTermination(const css::lang::EventObject &aEvent) override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Mutex aMutex
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
exports com.sun.star. connection
virtual ~IpcThread() override
#define SAL_N_ELEMENTS(arr)
std::vector< OUString > aForceOpenList
RequestHandler * m_handler
std::vector< OUString > aPrintToList
static bool AreRequestsPending()
int i
sal_Int32 m_index
static void SetReady(bool bIsReady)
virtual void close()=0
tuple con
unsigned char sal_Bool
static CommandLineArgs & GetCommandLineArgs()
Definition: app.cxx:375
OUString GetFactoryName(EFactory eFactory) const
OUString GetFactoryEmptyDocumentURL(EFactory eFactory) const
virtual ~RequestHandler() override
std::vector< OUString > aConversionList
static OUString getUILocale()
enumrange< T >::Iterator end(enumrange< T >)
bool IsInvisible() const
Definition: cmdlineargs.hxx:62
std::vector< OUString > aStartList
XPropertyListType t
bool close
static RequestHandler::Status enable(rtl::Reference< IpcThread > *thread)
std::optional< OUString > aCwdUrl
osl::Condition * m_pCondition
static void HandleAppEvent(const ApplicationEvent &rAppEvent)
Definition: app.cxx:2227
static Status Enable(bool ipc)
std::unique_ptr< char[]> aBuffer
bool process(OString const &arguments, bool *waitProcessed)
virtual ~PipeIpcThread() override
std::vector< OUString > aPrintList
unsigned char sal_uInt8
#define SAL_INFO(area, stream)
::osl::Condition cProcessed
void * p
QPRO_FUNC_TYPE nType
std::vector< OUString > aInFilter
oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void *, oslSignalInfo *pInfo)
#define SAL_WARN(area, stream)
static void AddConversionsToDispatchList(std::vector< DispatchWatcher::DispatchRequest > &rDispatchList, std::optional< OUString > const &cwdUrl, std::vector< OUString > const &rRequestList, const OUString &rParam, const OUString &rPrinterName, const OUString &rFactory, const OUString &rParamOut, const OUString &rImgOut, const bool isTextCat, const bool isScriptCat)
OString m_input
exports com.sun.star. security
IpcThread(char const *name)
static OUString CreateMD5FromString(const OUString &aMsg)
static void ImplPostForeignAppEvent(ApplicationEvent *pEvent)