1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_desktop.hxx"
26 
27 
28 #include "dp_misc.h"
29 #include "dp_version.hxx"
30 #include "dp_interact.h"
31 #include "rtl/uri.hxx"
32 #include "rtl/digest.h"
33 #include "rtl/random.h"
34 #include "rtl/bootstrap.hxx"
35 #include "unotools/bootstrap.hxx"
36 #include "osl/file.hxx"
37 #include "osl/pipe.hxx"
38 #include "osl/security.hxx"
39 #include "osl/thread.hxx"
40 #include "osl/mutex.hxx"
41 #include "com/sun/star/ucb/CommandAbortedException.hpp"
42 #include "com/sun/star/task/XInteractionHandler.hpp"
43 #include "com/sun/star/bridge/UnoUrlResolver.hpp"
44 #include "com/sun/star/bridge/XUnoUrlResolver.hpp"
45 #include "com/sun/star/deployment/ExtensionManager.hpp"
46 #include "com/sun/star/task/XRestartManager.hpp"
47 #include "boost/scoped_array.hpp"
48 #include "boost/shared_ptr.hpp"
49 #include <comphelper/processfactory.hxx>
50 
51 #ifdef WNT
52 //#include "tools/prewin.h"
53 #define UNICODE
54 #define _UNICODE
55 #define WIN32_LEAN_AND_MEAN
56 #include <Windows.h>
57 //#include "tools/postwin.h"
58 #endif
59 
60 using namespace ::com::sun::star;
61 using namespace ::com::sun::star::uno;
62 using ::rtl::OUString;
63 using ::rtl::OString;
64 
65 
66 #define SOFFICE1 "soffice.exe"
67 #define SOFFICE2 "soffice.bin"
68 #define SBASE "sbase.exe"
69 #define SCALC "scalc.exe"
70 #define SDRAW "sdraw.exe"
71 #define SIMPRESS "simpress.exe"
72 #define SWRITER "swriter.exe"
73 
74 namespace dp_misc {
75 namespace {
76 
77 struct UnoRc : public rtl::StaticWithInit<
78     const boost::shared_ptr<rtl::Bootstrap>, UnoRc> {
79     const boost::shared_ptr<rtl::Bootstrap> operator () () {
80         OUString unorc( RTL_CONSTASCII_USTRINGPARAM(
81                             "$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")) );
82         ::rtl::Bootstrap::expandMacros( unorc );
83         ::boost::shared_ptr< ::rtl::Bootstrap > ret(
84             new ::rtl::Bootstrap( unorc ) );
85         OSL_ASSERT( ret->getHandle() != 0 );
86         return ret;
87     }
88 };
89 
90 struct OfficePipeId : public rtl::StaticWithInit<const OUString, OfficePipeId> {
91     const OUString operator () ();
92 };
93 
94 const OUString OfficePipeId::operator () ()
95 {
96     OUString userPath;
97 	::utl::Bootstrap::PathStatus aLocateResult =
98 	::utl::Bootstrap::locateUserInstallation( userPath );
99 	if (!(aLocateResult == ::utl::Bootstrap::PATH_EXISTS ||
100 		aLocateResult == ::utl::Bootstrap::PATH_VALID))
101 	{
102 		throw Exception(OUSTR("Extension Manager: Could not obtain path for UserInstallation."), 0);
103 	}
104 
105     rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
106     if (digest <= 0) {
107         throw RuntimeException(
108             OUSTR("cannot get digest rtl_Digest_AlgorithmMD5!"), 0 );
109     }
110 
111     sal_uInt8 const * data =
112         reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
113     sal_Size size = (userPath.getLength() * sizeof (sal_Unicode));
114     sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
115     ::boost::scoped_array<sal_uInt8> md5_buf( new sal_uInt8 [ md5_key_len ] );
116 
117     rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
118     rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
119     rtl_digest_get( digest, md5_buf.get(), md5_key_len );
120     rtl_digest_destroy( digest );
121 
122     // create hex-value string from the MD5 value to keep
123     // the string size minimal
124     ::rtl::OUStringBuffer buf;
125     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("SingleOfficeIPC_") );
126     for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
127         buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
128     }
129     return buf.makeStringAndClear();
130 }
131 
132 bool existsOfficePipe()
133 {
134     OUString const & pipeId = OfficePipeId::get();
135     if (pipeId.getLength() == 0)
136         return false;
137     ::osl::Security sec;
138     ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
139     return pipe.is();
140 }
141 
142 
143 //Returns true if the Folder was more recently modified then
144 //the lastsynchronized file. That is the repository needs to
145 //be synchronized.
146 bool compareExtensionFolderWithLastSynchronizedFile(
147     OUString const & folderURL, OUString const & fileURL)
148 {
149     bool bNeedsSync = false;
150     ::osl::DirectoryItem itemExtFolder;
151     ::osl::File::RC err1 =
152           ::osl::DirectoryItem::get(folderURL, itemExtFolder);
153     //If it does not exist, then there is nothing to be done
154     if (err1 == ::osl::File::E_NOENT)
155     {
156         return false;
157     }
158     else if (err1 != ::osl::File::E_None)
159     {
160         OSL_ENSURE(0, "Cannot access extension folder");
161         return true; //sync just in case
162     }
163 
164     //If last synchronized does not exist, then OOo is started for the first time
165     ::osl::DirectoryItem itemFile;
166     ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
167     if (err2 == ::osl::File::E_NOENT)
168     {
169         return true;
170 
171     }
172     else if (err2 != ::osl::File::E_None)
173     {
174         OSL_ENSURE(0, "Cannot access file lastsynchronized");
175         return true; //sync just in case
176     }
177 
178     //compare the modification time of the extension folder and the last
179     //modified file
180     ::osl::FileStatus statFolder(FileStatusMask_ModifyTime);
181     ::osl::FileStatus statFile(FileStatusMask_ModifyTime);
182     if (itemExtFolder.getFileStatus(statFolder) == ::osl::File::E_None)
183     {
184         if (itemFile.getFileStatus(statFile) == ::osl::File::E_None)
185         {
186             TimeValue timeFolder = statFolder.getModifyTime();
187             TimeValue timeFile = statFile.getModifyTime();
188 
189             if (timeFile.Seconds < timeFolder.Seconds)
190                 bNeedsSync = true;
191         }
192         else
193         {
194             OSL_ASSERT(0);
195             bNeedsSync = true;
196         }
197     }
198     else
199     {
200         OSL_ASSERT(0);
201         bNeedsSync = true;
202     }
203     return bNeedsSync;
204 }
205 
206 bool needToSyncRepostitory(OUString const & name)
207 {
208     OUString folder;
209     OUString file;
210     if (name.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("bundled"))))
211     {
212         folder = OUString(
213             RTL_CONSTASCII_USTRINGPARAM("$BUNDLED_EXTENSIONS"));
214         file = OUString (
215             RTL_CONSTASCII_USTRINGPARAM(
216                 "$BUNDLED_EXTENSIONS_USER/lastsynchronized"));
217     }
218     else if (name.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("shared"))))
219     {
220         folder = OUString(
221             RTL_CONSTASCII_USTRINGPARAM(
222                 "$UNO_SHARED_PACKAGES_CACHE/uno_packages"));
223         file = OUString (
224             RTL_CONSTASCII_USTRINGPARAM(
225                 "$SHARED_EXTENSIONS_USER/lastsynchronized"));
226     }
227     else
228     {
229         OSL_ASSERT(0);
230         return true;
231     }
232     ::rtl::Bootstrap::expandMacros(folder);
233     ::rtl::Bootstrap::expandMacros(file);
234     return compareExtensionFolderWithLastSynchronizedFile(
235         folder, file);
236 }
237 
238 
239 } // anon namespace
240 
241 //==============================================================================
242 
243 namespace {
244 inline OUString encodeForRcFile( OUString const & str )
245 {
246     // escape $\{} (=> rtl bootstrap files)
247     ::rtl::OUStringBuffer buf;
248     sal_Int32 pos = 0;
249     const sal_Int32 len = str.getLength();
250     for ( ; pos < len; ++pos ) {
251         sal_Unicode c = str[ pos ];
252         switch (c) {
253         case '$':
254         case '\\':
255         case '{':
256         case '}':
257             buf.append( static_cast<sal_Unicode>('\\') );
258             break;
259         }
260         buf.append( c );
261     }
262     return buf.makeStringAndClear();
263 }
264 }
265 
266 //==============================================================================
267 OUString makeURL( OUString const & baseURL, OUString const & relPath_ )
268 {
269     ::rtl::OUStringBuffer buf;
270     if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/')
271         buf.append( baseURL.copy( 0, baseURL.getLength() - 1 ) );
272     else
273         buf.append( baseURL );
274     OUString relPath(relPath_);
275     if (relPath.getLength() > 0 && relPath[ 0 ] == '/')
276         relPath = relPath.copy( 1 );
277     if (relPath.getLength() > 0)
278     {
279         buf.append( static_cast<sal_Unicode>('/') );
280         if (baseURL.matchAsciiL(
281                 RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) {
282             // encode for macro expansion: relPath is supposed to have no
283             // macros, so encode $, {} \ (bootstrap mimic)
284             relPath = encodeForRcFile(relPath);
285 
286             // encode once more for vnd.sun.star.expand schema:
287             // vnd.sun.star.expand:$UNO_...
288             // will expand to file-url
289             relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
290                                           rtl_UriEncodeIgnoreEscapes,
291                                           RTL_TEXTENCODING_UTF8 );
292         }
293         buf.append( relPath );
294     }
295     return buf.makeStringAndClear();
296 }
297 
298 OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & relPath_ )
299 {
300     OUString segment = relPath_;
301     OSL_ASSERT(segment.indexOf(static_cast<sal_Unicode>('/')) == -1);
302 
303     ::rtl::Uri::encode(
304         segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
305         RTL_TEXTENCODING_UTF8);
306     return makeURL(baseURL, segment);
307 }
308 
309 
310 
311 //==============================================================================
312 OUString expandUnoRcTerm( OUString const & term_ )
313 {
314     OUString term(term_);
315     UnoRc::get()->expandMacrosFrom( term );
316     return term;
317 }
318 
319 OUString makeRcTerm( OUString const & url )
320 {
321     OSL_ASSERT( url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM(
322                                      "vnd.sun.star.expand:") ) );
323     if (url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) {
324         // cut protocol:
325         OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
326         // decode uric class chars:
327         rcterm = ::rtl::Uri::decode(
328             rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
329         return rcterm;
330     }
331     else
332         return url;
333 }
334 
335 //==============================================================================
336 OUString expandUnoRcUrl( OUString const & url )
337 {
338     if (url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:") )) {
339         // cut protocol:
340         OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
341         // decode uric class chars:
342         rcurl = ::rtl::Uri::decode(
343             rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
344         // expand macro string:
345         UnoRc::get()->expandMacrosFrom( rcurl );
346         return rcurl;
347     }
348     else {
349         return url;
350     }
351 }
352 
353 //==============================================================================
354 bool office_is_running()
355 {
356     //We need to check if we run within the office process. Then we must not use the pipe, because
357     //this could cause a deadlock. This is actually a workaround for i82778
358     OUString sFile;
359     oslProcessError err = osl_getExecutableFile(& sFile.pData);
360     bool ret = false;
361     if (osl_Process_E_None == err)
362     {
363         sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
364         if (
365 #if defined UNIX
366             sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE2)))
367 #elif defined WNT || defined OS2
368             //osl_getExecutableFile should deliver "soffice.bin" on windows
369             //even if swriter.exe, scalc.exe etc. was started. This is a bug
370             //in osl_getExecutableFile
371             sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE1)))
372             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SOFFICE2)))
373             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SBASE)))
374             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SCALC)))
375             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SDRAW)))
376             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SIMPRESS)))
377             || sFile.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(SWRITER)))
378 #else
379 #error "Unsupported platform"
380 #endif
381 
382             )
383             ret = true;
384         else
385             ret = existsOfficePipe();
386     }
387     else
388     {
389         OSL_ENSURE(0, "NOT osl_Process_E_None ");
390         //if osl_getExecutable file than we take the risk of creating a pipe
391         ret =  existsOfficePipe();
392     }
393     return ret;
394 }
395 
396 //==============================================================================
397 oslProcess raiseProcess(
398     OUString const & appURL, Sequence<OUString> const & args )
399 {
400     ::osl::Security sec;
401     oslProcess hProcess = 0;
402     oslProcessError rc = osl_executeProcess(
403         appURL.pData,
404         reinterpret_cast<rtl_uString **>(
405             const_cast<OUString *>(args.getConstArray()) ),
406         args.getLength(),
407         osl_Process_DETACHED,
408         sec.getHandle(),
409         0, // => current working dir
410         0, 0, // => no env vars
411         &hProcess );
412 
413     switch (rc) {
414     case osl_Process_E_None:
415         break;
416     case osl_Process_E_NotFound:
417         throw RuntimeException( OUSTR("image not found!"), 0 );
418     case osl_Process_E_TimedOut:
419         throw RuntimeException( OUSTR("timout occurred!"), 0 );
420     case osl_Process_E_NoPermission:
421         throw RuntimeException( OUSTR("permission denied!"), 0 );
422     case osl_Process_E_Unknown:
423         throw RuntimeException( OUSTR("unknown error!"), 0 );
424     case osl_Process_E_InvalidError:
425     default:
426         throw RuntimeException( OUSTR("unmapped error!"), 0 );
427     }
428 
429     return hProcess;
430 }
431 
432 //==============================================================================
433 OUString generateRandomPipeId()
434 {
435     // compute some good pipe id:
436     static rtlRandomPool s_hPool = rtl_random_createPool();
437     if (s_hPool == 0)
438         throw RuntimeException( OUSTR("cannot create random pool!?"), 0 );
439     sal_uInt8 bytes[ 32 ];
440     if (rtl_random_getBytes(
441             s_hPool, bytes, ARLEN(bytes) ) != rtl_Random_E_None) {
442         throw RuntimeException( OUSTR("random pool error!?"), 0 );
443     }
444     ::rtl::OUStringBuffer buf;
445     for ( sal_uInt32 i = 0; i < ARLEN(bytes); ++i ) {
446         buf.append( static_cast<sal_Int32>(bytes[ i ]), 0x10 );
447     }
448     return buf.makeStringAndClear();
449 }
450 
451 //==============================================================================
452 Reference<XInterface> resolveUnoURL(
453     OUString const & connectString,
454     Reference<XComponentContext> const & xLocalContext,
455     AbortChannel * abortChannel )
456 {
457     Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
458         bridge::UnoUrlResolver::create( xLocalContext ) );
459 
460     for (;;)
461     {
462         if (abortChannel != 0 && abortChannel->isAborted()) {
463             throw ucb::CommandAbortedException(
464                 OUSTR("abort!"), Reference<XInterface>() );
465         }
466         try {
467             return xUnoUrlResolver->resolve( connectString );
468         }
469         catch (connection::NoConnectException &) {
470             TimeValue tv = { 0 /* secs */, 500000000 /* nanosecs */ };
471             ::osl::Thread::wait( tv );
472         }
473     }
474 }
475 
476 #ifdef WNT
477 void writeConsoleWithStream(::rtl::OUString const & sText, HANDLE stream)
478 {
479     DWORD nWrittenChars = 0;
480     WriteFile(stream, sText.getStr(),
481         sText.getLength() * 2, &nWrittenChars, NULL);
482 }
483 #else
484 void writeConsoleWithStream(::rtl::OUString const & sText, FILE * stream)
485 {
486     OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
487     fprintf(stream, "%s", s.getStr());
488     fflush(stream);
489 }
490 #endif
491 
492 #ifdef WNT
493 void writeConsoleWithStream(::rtl::OString const & sText, HANDLE stream)
494 {
495     writeConsoleWithStream(OStringToOUString(
496         sText, RTL_TEXTENCODING_UTF8), stream);
497 }
498 #else
499 void writeConsoleWithStream(::rtl::OString const & sText, FILE * stream)
500 {
501     fprintf(stream, "%s", sText.getStr());
502     fflush(stream);
503 }
504 #endif
505 
506 void writeConsole(::rtl::OUString const & sText)
507 {
508 #ifdef WNT
509     writeConsoleWithStream(sText, GetStdHandle(STD_OUTPUT_HANDLE));
510 #else
511     writeConsoleWithStream(sText, stdout);
512 #endif
513 }
514 
515 void writeConsole(::rtl::OString const & sText)
516 {
517 #ifdef WNT
518     writeConsoleWithStream(sText, GetStdHandle(STD_OUTPUT_HANDLE));
519 #else
520     writeConsoleWithStream(sText, stdout);
521 #endif
522 }
523 
524 void writeConsoleError(::rtl::OUString const & sText)
525 {
526 #ifdef WNT
527     writeConsoleWithStream(sText, GetStdHandle(STD_ERROR_HANDLE));
528 #else
529     writeConsoleWithStream(sText, stderr);
530 #endif
531 }
532 
533 
534 void writeConsoleError(::rtl::OString const & sText)
535 {
536 #ifdef WNT
537     writeConsoleWithStream(sText, GetStdHandle(STD_ERROR_HANDLE));
538 #else
539     writeConsoleWithStream(sText, stderr);
540 #endif
541 }
542 
543 
544 
545 OUString readConsole()
546 {
547 #ifdef WNT
548     sal_Unicode aBuffer[1024];
549 	DWORD	dwRead = 0;
550     //unopkg.com feeds unopkg.exe with wchar_t|s
551     if (ReadFile( GetStdHandle(STD_INPUT_HANDLE), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
552 	{
553         OSL_ASSERT((dwRead % 2) == 0);
554         OUString value( aBuffer, dwRead / 2);
555         return value.trim();
556 	}
557 #else
558 	char buf[1024];
559 	rtl_zeroMemory(buf, 1024);
560 	// read one char less so that the last char in buf is always zero
561 	if (fgets(buf, 1024, stdin) != NULL)
562     {
563         OUString value = ::rtl::OStringToOUString(::rtl::OString(buf), osl_getThreadTextEncoding());
564         return value.trim();
565     }
566 #endif
567     return OUString();
568 }
569 
570 void TRACE(::rtl::OUString const & sText)
571 {
572     (void) sText;
573 #if OSL_DEBUG_LEVEL > 1
574     writeConsole(sText);
575 #endif
576 }
577 
578 void TRACE(::rtl::OString const & sText)
579 {
580     (void) sText;
581 #if OSL_DEBUG_LEVEL > 1
582     writeConsole(sText);
583 #endif
584 }
585 
586 void syncRepositories(Reference<ucb::XCommandEnvironment> const & xCmdEnv)
587 {
588     OUString sDisable;
589     ::rtl::Bootstrap::get( OUSTR( "DISABLE_EXTENSION_SYNCHRONIZATION" ), sDisable, OUString() );
590     if (sDisable.getLength() > 0)
591         return;
592 
593     Reference<deployment::XExtensionManager> xExtensionManager;
594     //synchronize shared before bundled otherewise there are
595     //more revoke and registration calls.
596     sal_Bool bModified = false;
597     if (needToSyncRepostitory(OUString(RTL_CONSTASCII_USTRINGPARAM("shared")))
598         || needToSyncRepostitory(OUString(RTL_CONSTASCII_USTRINGPARAM("bundled"))))
599     {
600         xExtensionManager =
601             deployment::ExtensionManager::get(
602                 comphelper_getProcessComponentContext());
603 
604         if (xExtensionManager.is())
605         {
606             bModified = xExtensionManager->synchronize(
607                 Reference<task::XAbortChannel>(), xCmdEnv);
608         }
609     }
610 
611     if (bModified)
612     {
613         Reference<task::XRestartManager> restarter(
614             comphelper_getProcessComponentContext()->getValueByName(
615                 OUSTR( "/singletons/com.sun.star.task.OfficeRestartManager") ), UNO_QUERY );
616         if (restarter.is())
617         {
618             restarter->requestRestart(xCmdEnv.is() == sal_True ? xCmdEnv->getInteractionHandler() :
619                                       Reference<task::XInteractionHandler>());
620         }
621      }
622 }
623 
624 
625 
626 }
627