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