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 
639 {
640  static ::osl::Mutex theRequestHandlerMutex;
641  return theRequestHandlerMutex;
642 }
643 
645 {
646  // We have the order to block all incoming requests. Framework
647  // wants to shutdown and we have to make sure that no loading/printing
648  // requests are executed anymore.
649  ::osl::MutexGuard aGuard( GetMutex() );
650 
651  if ( pGlobal.is() )
652  pGlobal->mState = State::Downing;
653 }
654 
656 {
657  // switch between just queueing the requests and executing them
658  ::osl::MutexGuard aGuard( GetMutex() );
659 
660  if ( pGlobal.is() )
661  {
662  if (pGlobal->mState != State::Downing) {
664  }
665  // hit the compiler over the head - this avoids GCC -Werror=maybe-uninitialized
666  std::optional<OUString> tmp;
667  ProcessDocumentsRequest aEmptyReq(tmp);
668  // trigger already queued requests
670  }
671 }
672 
674 {
675  // Give info about pending requests
676  ::osl::MutexGuard aGuard( GetMutex() );
677  if ( pGlobal.is() )
678  return ( pGlobal->mnPendingRequests > 0 );
679  else
680  return false;
681 }
682 
684 {
685  // Remove nCount pending requests from our internal counter
686  ::osl::MutexGuard aGuard( GetMutex() );
687  if ( pGlobal.is() )
688  {
689  if ( pGlobal->mnPendingRequests > 0 )
690  pGlobal->mnPendingRequests --;
691  }
692 }
693 
695 {
696  ::osl::MutexGuard aGuard( GetMutex() );
697 
698  if( pGlobal.is() )
699  return IPC_STATUS_OK;
700 
701 #if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX
702  ipc = false;
703 #endif
704 
705  if (!ipc) {
706  pGlobal = new RequestHandler;
707  return IPC_STATUS_OK;
708  }
709 
710  enum class Kind { Pipe, Dbus };
711  Kind kind;
712 #if ENABLE_DBUS
713  kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
714 #else
715  kind = Kind::Pipe;
716 #endif
718  Status stat = Status(); // silence bogus potentially-uninitialized warnings
719  switch (kind) {
720  case Kind::Pipe:
721  stat = PipeIpcThread::enable(&thread);
722  break;
723  case Kind::Dbus:
724 #if ENABLE_DBUS
725  stat = DbusIpcThread::enable(&thread);
726  break;
727 #endif
728  default:
729  assert(false);
730  }
731  assert(thread.is() == (stat == IPC_STATUS_OK));
732  if (stat == IPC_STATUS_OK) {
733  pGlobal = new RequestHandler;
734  pGlobal->mIpcThread = thread;
735  pGlobal->mIpcThread->start(pGlobal.get());
736  }
737  return stat;
738 }
739 
741 {
742  assert(thread != nullptr);
743 
744  // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
745  // this information from a unotools implementation.
746  OUString aUserInstallPath;
748  if (aLocateResult != utl::Bootstrap::PATH_EXISTS
749  && aLocateResult != utl::Bootstrap::PATH_VALID)
750  {
752  }
753 
754  // Try to determine if we are the first office or not! This should prevent multiple
755  // access to the user directory !
756  // First we try to create our pipe if this fails we try to connect. We have to do this
757  // in a loop because the other office can crash or shutdown between createPipe
758  // and connectPipe!!
759  auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
760 
761  // Check result to create a hash code from the user install path
762  if ( aUserInstallPathHashCode.isEmpty() )
763  return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
764 
765  osl::Pipe pipe;
766  enum PipeMode
767  {
768  PIPEMODE_DONTKNOW,
769  PIPEMODE_CREATED,
770  PIPEMODE_CONNECTED
771  };
772  PipeMode nPipeMode = PIPEMODE_DONTKNOW;
773 
774  OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
775  do
776  {
777  osl::Security security;
778 
779  // Try to create pipe
780  if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
781  {
782  // Pipe created
783  nPipeMode = PIPEMODE_CREATED;
784  }
785  else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
786  {
787  osl::StreamPipe aStreamPipe(pipe.getHandle());
788  if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
789  {
790  // Pipe connected to first office
791  nPipeMode = PIPEMODE_CONNECTED;
792  }
793  else
794  {
795  // Pipe connection failed (other office exited or crashed)
796  TimeValue tval;
797  tval.Seconds = 0;
798  tval.Nanosec = 500000000;
799  salhelper::Thread::wait( tval );
800  }
801  }
802  else
803  {
804  oslPipeError eReason = pipe.getError();
805  if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
807 
808  // Wait for second office to be ready
809  TimeValue aTimeValue;
810  aTimeValue.Seconds = 0;
811  aTimeValue.Nanosec = 10000000; // 10ms
812  salhelper::Thread::wait( aTimeValue );
813  }
814 
815  } while ( nPipeMode == PIPEMODE_DONTKNOW );
816 
817  if ( nPipeMode == PIPEMODE_CREATED )
818  {
819  // Seems we are the one and only, so create listening thread
820  *thread = new PipeIpcThread(pipe);
822  }
823  else
824  {
825  // Seems another office is running. Pipe arguments to it and self terminate
826  osl::StreamPipe aStreamPipe(pipe.getHandle());
827 
828  OStringBuffer aArguments(ARGUMENT_PREFIX);
829  OUString cwdUrl;
830  if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
831  addArgument(aArguments, '1', cwdUrl)))
832  {
833  aArguments.append('0');
834  }
835  sal_uInt32 nCount = rtl_getAppCommandArgCount();
836  for( sal_uInt32 i=0; i < nCount; i++ )
837  {
838  rtl_getAppCommandArg( i, &aUserInstallPath.pData );
839  if (!addArgument(aArguments, ',', aUserInstallPath)) {
841  }
842  }
843  aArguments.append('\0');
844  // finally, write the string onto the pipe
845  SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
846  sal_Int32 n = aStreamPipe.write(
847  aArguments.getStr(), aArguments.getLength());
848  if (n != aArguments.getLength()) {
849  SAL_INFO("desktop.app", "short write: " << n);
851  }
852 
853  if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
854  {
855  // something went wrong
857  }
858 
860  }
861 }
862 
864 {
865  osl::ClearableMutexGuard aMutex( GetMutex() );
866 
867  if( !pGlobal.is() )
868  return;
869 
871  pGlobal.clear();
872 
873  handler->mState = State::Downing;
874  if (handler->mIpcThread.is()) {
875  handler->mIpcThread->close();
876  }
877 
878  // release mutex to avoid deadlocks
879  aMutex.clear();
880 
881  handler->cReady.set();
882 
883  // exit gracefully and join
884  if (handler->mIpcThread.is())
885  {
886  handler->mIpcThread->join();
887  handler->mIpcThread.clear();
888  }
889 
890  handler->cReady.reset();
891 }
892 
894  mState( State::Starting ),
895  mnPendingRequests( 0 )
896 {
897 }
898 
900 {
901  assert(!mIpcThread.is());
902 }
903 
904 void RequestHandler::SetReady(bool bIsReady)
905 {
906  osl::MutexGuard g(GetMutex());
907  if (pGlobal.is())
908  {
909  if (bIsReady)
910  pGlobal->cReady.set();
911  else
912  pGlobal->cReady.reset();
913  }
914 }
915 
917 {
919  {
920  osl::MutexGuard g(GetMutex());
921  t = pGlobal;
922  }
923  if (t.is())
924  {
925  t->cReady.wait();
926  }
927 }
928 
929 bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
930  assert(waitProcessed != nullptr);
931 
932  std::unique_ptr< CommandLineArgs > aCmdLineArgs;
933  try
934  {
935  Parser p(arguments);
936  aCmdLineArgs.reset( new CommandLineArgs( p ) );
937  }
938  catch ( const CommandLineArgs::Supplier::Exception & )
939  {
940  SAL_WARN("desktop.app", "Error in received command line arguments");
941  return false;
942  }
943 
944  bool bDocRequestSent = false;
945 
946  OUString aUnknown( aCmdLineArgs->GetUnknown() );
947  if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
948  {
949  const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
950 
951  if ( aCmdLineArgs->IsQuickstart() )
952  {
953  // we have to use application event, because we have to start quickstart service in main thread!!
954  ApplicationEvent* pAppEvent =
956  ImplPostForeignAppEvent( pAppEvent );
957  }
958 
959  // handle request for acceptor
960  std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
961  for (auto const& elem : accept)
962  {
963  ApplicationEvent* pAppEvent = new ApplicationEvent(
965  ImplPostForeignAppEvent( pAppEvent );
966  }
967  // handle acceptor removal
968  std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
969  for (auto const& elem : unaccept)
970  {
971  ApplicationEvent* pAppEvent = new ApplicationEvent(
973  ImplPostForeignAppEvent( pAppEvent );
974  }
975 
976  std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
977  aCmdLineArgs->getCwdUrl()));
978  m_handler->cProcessed.reset();
979  pRequest->pcProcessed = &m_handler->cProcessed;
980  m_handler->mbSuccess = false;
981  pRequest->mpbSuccess = &m_handler->mbSuccess;
982 
983  // Print requests are not dependent on the --invisible cmdline argument as they are
984  // loaded with the "hidden" flag! So they are always checked.
985  pRequest->aPrintList = aCmdLineArgs->GetPrintList();
986  bDocRequestSent |= !pRequest->aPrintList.empty();
987  pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
988  pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
989  bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
990  pRequest->aConversionList = aCmdLineArgs->GetConversionList();
991  pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
992  pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
993  pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
994  pRequest->aInFilter = aCmdLineArgs->GetInFilter();
995  pRequest->bTextCat = aCmdLineArgs->IsTextCat();
996  pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
997  bDocRequestSent |= !pRequest->aConversionList.empty();
998 
999  if ( !rCurrentCmdLineArgs.IsInvisible() )
1000  {
1001  // Read cmdline args that can open/create documents. As they would open a window
1002  // they are only allowed if the "--invisible" is currently not used!
1003  pRequest->aOpenList = aCmdLineArgs->GetOpenList();
1004  bDocRequestSent |= !pRequest->aOpenList.empty();
1005  pRequest->aViewList = aCmdLineArgs->GetViewList();
1006  bDocRequestSent |= !pRequest->aViewList.empty();
1007  pRequest->aStartList = aCmdLineArgs->GetStartList();
1008  bDocRequestSent |= !pRequest->aStartList.empty();
1009  pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
1010  bDocRequestSent |= !pRequest->aForceOpenList.empty();
1011  pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
1012  bDocRequestSent |= !pRequest->aForceNewList.empty();
1013 
1014  // Special command line args to create an empty document for a given module
1015 
1016  // #i18338# (lo)
1017  // we only do this if no document was specified on the command line,
1018  // since this would be inconsistent with the behaviour of
1019  // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
1020  if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
1021  {
1022  SvtModuleOptions aOpt;
1024  if ( aCmdLineArgs->IsWriter() )
1026  else if ( aCmdLineArgs->IsCalc() )
1028  else if ( aCmdLineArgs->IsDraw() )
1030  else if ( aCmdLineArgs->IsImpress() )
1032  else if ( aCmdLineArgs->IsBase() )
1034  else if ( aCmdLineArgs->IsMath() )
1036  else if ( aCmdLineArgs->IsGlobal() )
1038  else if ( aCmdLineArgs->IsWeb() )
1040 
1041  if ( !pRequest->aOpenList.empty() )
1042  pRequest->aModule = aOpt.GetFactoryName( eFactory );
1043  else
1044  pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
1045  bDocRequestSent = true;
1046  }
1047  }
1048 
1049  if ( !aCmdLineArgs->IsQuickstart() ) {
1050  bool bShowHelp = false;
1051  OUStringBuffer aHelpURLBuffer;
1052  if (aCmdLineArgs->IsHelpWriter()) {
1053  bShowHelp = true;
1054  aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
1055  } else if (aCmdLineArgs->IsHelpCalc()) {
1056  bShowHelp = true;
1057  aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
1058  } else if (aCmdLineArgs->IsHelpDraw()) {
1059  bShowHelp = true;
1060  aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
1061  } else if (aCmdLineArgs->IsHelpImpress()) {
1062  bShowHelp = true;
1063  aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
1064  } else if (aCmdLineArgs->IsHelpBase()) {
1065  bShowHelp = true;
1066  aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
1067  } else if (aCmdLineArgs->IsHelpBasic()) {
1068  bShowHelp = true;
1069  aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
1070  } else if (aCmdLineArgs->IsHelpMath()) {
1071  bShowHelp = true;
1072  aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
1073  }
1074  if (bShowHelp) {
1075  aHelpURLBuffer.append("?Language=");
1076  aHelpURLBuffer.append(utl::ConfigManager::getUILocale());
1077 #if defined UNX
1078  aHelpURLBuffer.append("&System=UNX");
1079 #elif defined _WIN32
1080  aHelpURLBuffer.append("&System=WIN");
1081 #endif
1082  ApplicationEvent* pAppEvent = new ApplicationEvent(
1084  aHelpURLBuffer.makeStringAndClear());
1085  ImplPostForeignAppEvent( pAppEvent );
1086  }
1087  }
1088 
1089  if ( bDocRequestSent )
1090  {
1091  // Send requests to dispatch watcher if we have at least one. The receiver
1092  // is responsible to delete the request after processing it.
1093  if ( aCmdLineArgs->HasModuleParam() )
1094  {
1095  SvtModuleOptions aOpt;
1096 
1097  // Support command line parameters to start a module (as preselection)
1098  if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
1099  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
1100  else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
1101  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
1102  else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
1103  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
1104  else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
1105  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
1106  }
1107 
1108  ImplPostProcessDocumentsEvent( std::move(pRequest) );
1109  }
1110  else
1111  {
1112  // delete not used request again
1113  pRequest.reset();
1114  }
1115  if (aCmdLineArgs->IsEmpty())
1116  {
1117  // no document was sent, just bring Office to front
1118  ApplicationEvent* pAppEvent =
1120  ImplPostForeignAppEvent( pAppEvent );
1121  }
1122  }
1123  *waitProcessed = bDocRequestSent;
1124  return true;
1125 }
1126 
1128 {
1129  assert(m_handler != nullptr);
1130  do
1131  {
1132  osl::StreamPipe aStreamPipe;
1133  oslPipeError nError = pipe_.accept( aStreamPipe );
1134 
1135 
1136  if( nError == osl_Pipe_E_None )
1137  {
1138  // if we receive a request while the office is displaying some dialog or error during
1139  // bootstrap, that dialogs event loop might get events that are dispatched by this thread
1140  // we have to wait for cReady to be set by the real main loop.
1141  // only requests that don't dispatch events may be processed before cReady is set.
1142  m_handler->cReady.wait();
1143 
1144  // we might have decided to shutdown while we were sleeping
1145  if (!RequestHandler::pGlobal.is()) return;
1146 
1147  // only lock the mutex when processing starts, otherwise we deadlock when the office goes
1148  // down during wait
1149  osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
1150 
1152  {
1153  break;
1154  }
1155 
1156  // notify client we're ready to process its args:
1157  SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
1158  sal_Int32 n = aStreamPipe.write(
1159  SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
1160  // incl. terminating NUL
1161  if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
1162  SAL_WARN("desktop.app", "short write: " << n);
1163  continue;
1164  }
1165 
1166  OString aArguments = readStringFromPipe(aStreamPipe);
1167 
1168  // Is this a lookup message from another application? if so, ignore
1169  if (aArguments.isEmpty())
1170  continue;
1171 
1172  bool waitProcessed = false;
1173  if (!process(aArguments, &waitProcessed)) {
1174  continue;
1175  }
1176 
1177  // we don't need the mutex any longer...
1178  aGuard.clear();
1179  bool bSuccess = true;
1180  // wait for processing to finish
1181  if (waitProcessed)
1182  {
1183  m_handler->cProcessed.wait();
1184  bSuccess = m_handler->mbSuccess;
1185  }
1186  if (bSuccess)
1187  {
1188  // processing finished, inform the requesting end:
1189  SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
1190  n = aStreamPipe.write(PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
1191  // incl. terminating NUL
1192  if (n != SAL_N_ELEMENTS(PROCESSING_DONE))
1193  {
1194  SAL_WARN("desktop.app", "short write: " << n);
1195  continue;
1196  }
1197  }
1198  }
1199  else
1200  {
1201  {
1202  osl::MutexGuard aGuard( RequestHandler::GetMutex() );
1204  {
1205  break;
1206  }
1207  }
1208 
1209  SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
1210  TimeValue tval;
1211  tval.Seconds = 1;
1212  tval.Nanosec = 0;
1213  salhelper::Thread::wait( tval );
1214  }
1215  } while( schedule() );
1216 }
1217 
1218 static void AddToDispatchList(
1219  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1220  std::optional< OUString > const & cwdUrl,
1221  std::vector< OUString > const & aRequestList,
1223  const OUString& aParam,
1224  const OUString& aFactory )
1225 {
1226  for (auto const& request : aRequestList)
1227  {
1228  rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
1229  }
1230 }
1231 
1233  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1234  std::optional< OUString > const & cwdUrl,
1235  std::vector< OUString > const & rRequestList,
1236  const OUString& rParam,
1237  const OUString& rPrinterName,
1238  const OUString& rFactory,
1239  const OUString& rParamOut,
1240  const OUString& rImgOut,
1241  const bool isTextCat,
1242  const bool isScriptCat )
1243 {
1245  OUString aParam( rParam );
1246 
1247  if( !rParam.isEmpty() )
1248  {
1249  if ( isTextCat )
1251  else
1253  aParam = rParam;
1254  }
1255  else
1256  {
1257  if ( isScriptCat )
1259  else
1260  {
1262  aParam = rPrinterName;
1263  }
1264  }
1265 
1266  OUString aOutDir( rParamOut.trim() );
1267  OUString aImgOut( rImgOut.trim() );
1268  OUString aPWD;
1269  if (cwdUrl)
1270  {
1271  aPWD = *cwdUrl;
1272  }
1273  else
1274  {
1276  }
1277 
1278  if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
1279  ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
1280 
1281  if( !rParamOut.trim().isEmpty() )
1282  {
1283  aParam += ";" + aOutDir;
1284  }
1285  else
1286  {
1287  ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1288  aParam += ";" + aPWD;
1289  }
1290 
1291  if( !rImgOut.trim().isEmpty() )
1292  aParam += "|" + aImgOut;
1293 
1294  for (auto const& request : rRequestList)
1295  {
1296  rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
1297  }
1298 }
1299 
1300 namespace {
1301 
1302 struct ConditionSetGuard
1303 {
1304  osl::Condition* m_pCondition;
1305  ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
1306  ~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
1307 };
1308 
1309 }
1310 
1312  ProcessDocumentsRequest& aRequest, bool noTerminate)
1313 {
1314  // protect the dispatch list
1315  osl::ClearableMutexGuard aGuard( GetMutex() );
1316 
1317  // ensure that Processed flag (if exists) is signaled in any outcome
1318  ConditionSetGuard aSetGuard(aRequest.pcProcessed);
1319 
1320  static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
1321 
1322  // Create dispatch list for dispatch watcher
1323  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, "", aRequest.aModule );
1324  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, "", aRequest.aModule );
1325  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, "", aRequest.aModule );
1326  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, "", aRequest.aModule );
1327  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, "", aRequest.aModule );
1328  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1329  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, "", aRequest.aModule );
1330  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, "", aRequest.aModule );
1331  AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
1332  bool bShutdown( false );
1333 
1334  if ( pGlobal.is() )
1335  {
1336  if( ! pGlobal->AreRequestsEnabled() )
1337  {
1338  // Either starting, or downing - do not process the request, just try to bring Office to front
1339  ApplicationEvent* pAppEvent =
1341  ImplPostForeignAppEvent(pAppEvent);
1342  return bShutdown;
1343  }
1344 
1345  pGlobal->mnPendingRequests += aDispatchList.size();
1346  if ( !pGlobal->mpDispatchWatcher.is() )
1347  {
1348  pGlobal->mpDispatchWatcher = new DispatchWatcher;
1349  }
1350  rtl::Reference<DispatchWatcher> dispatchWatcher(
1351  pGlobal->mpDispatchWatcher);
1352 
1353  // copy for execute
1354  std::vector<DispatchWatcher::DispatchRequest> aTempList;
1355  aTempList.swap( aDispatchList );
1356 
1357  aGuard.clear();
1358 
1359  // Execute dispatch requests
1360  bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
1361  if (aRequest.mpbSuccess)
1362  *aRequest.mpbSuccess = true; // signal that we have actually succeeded
1363  }
1364 
1365  return bShutdown;
1366 }
1367 
1368 }
1369 
1370 /* 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:164
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:1722
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)
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
const OUString & GetFactoryName(EFactory eFactory) const
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:388
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:2251
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)