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