1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 // Include this early, as it uses the identifier "Yield" which is defined as a macro in Windows
11 // system files indirectly included via some of the later includes here:
12 #include <vcl/svapp.hxx>
14 #include "updater.hxx"
23 #include <comphelper/windowsStart.hxx>
27 #include <config_folders.h>
28 #include <rtl/bootstrap.hxx>
30 #include <officecfg/Office/Update.hxx>
32 #include <rtl/ustring.hxx>
33 #include <unotools/tempfile.hxx>
34 #include <unotools/configmgr.hxx>
35 #include <o3tl/char16_t2wchar_t.hxx>
36 #include <o3tl/runtimetooustring.hxx>
37 #include <osl/file.hxx>
38 #include <osl/thread.h>
39 #include <rtl/process.h>
40 #include <sal/log.hxx>
41 #include <tools/stream.hxx>
43 #include <curl/curl.h>
45 #include <orcus/json_document_tree.hpp>
46 #include <orcus/config.hpp>
48 #include <systools/curlinit.hxx>
49 #include <comphelper/hash.hxx>
51 #include <com/sun/star/container/XNameAccess.hpp>
53 #include <officecfg/Setup.hxx>
58 #include <string_view>
62 class error_updater
: public std::exception
67 error_updater(const OString
& rStr
):
72 virtual const char* what() const throw() override
74 return maStr
.getStr();
79 const char* const pUpdaterName
= "updater";
80 const char* const pSofficeExeName
= "soffice";
82 const char* pUpdaterName
= "updater.exe";
83 const char* pSofficeExeName
= "soffice.exe";
85 #error "Need implementation"
88 OUString
normalizePath(const OUString
& rPath
)
90 OUString aPath
= rPath
;
92 aPath
= aPath
.replace('\\', '/');
95 aPath
= aPath
.replaceAll("//", "/");
98 if (aPath
.endsWith("/"))
100 aPath
= aPath
.copy(0, aPath
.getLength() - 1);
103 while (aPath
.indexOf("/..") != -1)
105 sal_Int32 nIndex
= aPath
.indexOf("/..");
106 sal_Int32 i
= nIndex
- 1;
113 OUString aTempPath
= aPath
;
114 aPath
= aTempPath
.copy(0, i
) + aPath
.copy(nIndex
+ 3);
118 aPath
= aPath
.replace('/', '\\');
123 void CopyFileToDir(const OUString
& rTempDirURL
, const OUString
& rFileName
, const OUString
& rOldDir
)
125 OUString aSourceURL
= rOldDir
+ "/" + rFileName
;
126 OUString aDestURL
= rTempDirURL
+ "/" + rFileName
;
128 osl::File::RC eError
= osl::File::copy(aSourceURL
, aDestURL
);
129 if (eError
!= osl::File::E_None
)
131 SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName
);
132 throw std::exception();
136 OUString
getPathFromURL(const OUString
& rURL
)
139 osl::FileBase::getSystemPathFromFileURL(rURL
, aPath
);
141 return normalizePath(aPath
);
144 void CopyUpdaterToTempDir(const OUString
& rInstallDirURL
, const OUString
& rTempDirURL
)
146 OUString aUpdaterName
= OUString::fromUtf8(pUpdaterName
);
147 CopyFileToDir(rTempDirURL
, aUpdaterName
, rInstallDirURL
);
148 CopyFileToDir(rTempDirURL
, u
"updater.ini"_ustr
, rInstallDirURL
);
153 #define tstrncpy std::strncpy
154 char const * toStream(char const * s
) { return s
; }
155 #elif defined(_WIN32)
156 typedef wchar_t CharT
;
157 #define tstrncpy std::wcsncpy
158 OUString
toStream(wchar_t const * s
) { return OUString(o3tl::toU(s
)); }
160 #error "Need an implementation"
163 void createStr(const OUString
& rStr
, CharT
** pArgs
, size_t i
)
166 OString aStr
= OUStringToOString(rStr
, RTL_TEXTENCODING_UTF8
);
167 #elif defined(_WIN32)
168 OUString aStr
= rStr
;
170 #error "Need an implementation"
172 CharT
* pStr
= new CharT
[aStr
.getLength() + 1];
173 tstrncpy(pStr
, (CharT
*)aStr
.getStr(), aStr
.getLength());
174 pStr
[aStr
.getLength()] = '\0';
178 CharT
** createCommandLine(OUString
const & argv0
)
180 OUString aInstallDir
= Updater::getInstallationPath();
182 size_t nCommandLineArgs
= rtl_getAppCommandArgCount();
183 size_t nArgs
= 8 + nCommandLineArgs
;
184 CharT
** pArgs
= new CharT
*[nArgs
];
185 createStr(argv0
, pArgs
, 0);
187 // directory with the patch log
188 OUString aPatchDir
= Updater::getPatchDirURL();
189 rtl::Bootstrap::expandMacros(aPatchDir
);
190 OUString aTempDirPath
= getPathFromURL(aPatchDir
);
191 Updater::log("Patch Dir: " + aTempDirPath
);
192 createStr(aTempDirPath
, pArgs
, 1);
195 // the actual update directory
196 Updater::log("Install Dir: " + aInstallDir
);
197 createStr(aInstallDir
, pArgs
, 2);
200 // the temporary updated build
201 Updater::log("Working Dir: " + aInstallDir
);
202 createStr(aInstallDir
, pArgs
, 3);
207 #elif defined(_WIN32)
208 oslProcessInfo aInfo
;
209 aInfo
.Size
= sizeof(oslProcessInfo
);
210 osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER
, &aInfo
);
211 OUString aPID
= OUString::number(aInfo
.Ident
);
213 #error "Need an implementation"
215 createStr(aPID
, pArgs
, 4);
218 OUString aExeDir
= Updater::getExecutableDirURL();
219 OUString aSofficePath
= getPathFromURL(aExeDir
);
220 Updater::log("soffice Path: " + aSofficePath
);
221 createStr(aSofficePath
, pArgs
, 5);
224 // the executable to start after the successful update
225 OUString aExeDir
= Updater::getExecutableDirURL();
226 OUString aSofficePathURL
= aExeDir
+ OUString::fromUtf8(pSofficeExeName
);
227 OUString aSofficePath
= getPathFromURL(aSofficePathURL
);
228 createStr(aSofficePath
, pArgs
, 6);
231 // add the command line arguments from the soffice list
232 for (size_t i
= 0; i
< nCommandLineArgs
; ++i
)
234 OUString aCommandLineArg
;
235 rtl_getAppCommandArg(i
, &aCommandLineArg
.pData
);
236 createStr(aCommandLineArg
, pArgs
, 7 + i
);
239 pArgs
[nArgs
- 1] = nullptr;
253 update_file aUpdateFile
;
259 OUString aFromBuildID
;
260 OUString aSeeAlsoURL
;
263 update_file aUpdateFile
;
264 std::vector
<language_file
> aLanguageFiles
;
267 bool isUserWritable(const OUString
& rFileURL
)
269 osl::FileStatus
aStatus(osl_FileStatus_Mask_Attributes
);
270 osl::DirectoryItem aDirectoryItem
;
272 osl::FileBase::RC eRes
= osl::DirectoryItem::get(rFileURL
, aDirectoryItem
);
273 if (eRes
!= osl::FileBase::E_None
)
275 Updater::log("Could not get the directory item for: " + rFileURL
);
279 osl::FileBase::RC eResult
= aDirectoryItem
.getFileStatus(aStatus
);
280 if (eResult
!= osl::FileBase::E_None
)
282 Updater::log("Could not get the file status for: " + rFileURL
);
286 bool bReadOnly
= (aStatus
.getAttributes() & static_cast<sal_uInt64
>(osl_File_Attribute_ReadOnly
)) != 0;
289 Updater::log("Update location as determined by: " + rFileURL
+ " is read-only.");
300 utl::TempFileNamed
aTempDir(nullptr, true);
301 OUString aTempDirURL
= aTempDir
.GetURL();
302 CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL
);
304 OUString aUpdaterPath
= getPathFromURL(aTempDirURL
+ "/" + OUString::fromUtf8(pUpdaterName
));
306 Updater::log("Calling the updater with parameters: ");
307 CharT
** pArgs
= createCommandLine(aUpdaterPath
);
309 bool bSuccess
= true;
310 const char* pUpdaterTestReplace
= std::getenv("LIBO_UPDATER_TEST_REPLACE");
311 if (!pUpdaterTestReplace
)
314 OString aPath
= OUStringToOString(aUpdaterPath
, RTL_TEXTENCODING_UTF8
);
315 if (execv(aPath
.getStr(), pArgs
))
317 printf("execv failed with error %d %s\n",errno
,strerror(errno
));
320 #elif defined(_WIN32)
321 bSuccess
= WinLaunchChild(o3tl::toW(aUpdaterPath
.getStr()), pArgs
);
326 SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath
);
327 for (size_t i
= 0; i
< 8 + rtl_getAppCommandArgCount(); ++i
)
329 SAL_WARN("desktop.updater", toStream(pArgs
[i
]));
334 for (size_t i
= 0; i
< 8 + rtl_getAppCommandArgCount(); ++i
)
345 // Callback to get the response data from server.
346 size_t WriteCallback(void *ptr
, size_t size
,
347 size_t nmemb
, void *userp
)
352 std::string
* response
= static_cast<std::string
*>(userp
);
353 size_t real_size
= size
* nmemb
;
354 response
->append(static_cast<char *>(ptr
), real_size
);
360 class invalid_update_info
: public std::exception
364 class invalid_hash
: public std::exception
369 invalid_hash(const OUString
& rExpectedHash
, const OUString
& rReceivedHash
)
372 OUString("Invalid hash found.\nExpected: " + rExpectedHash
+ ";\nReceived: " + rReceivedHash
),
373 RTL_TEXTENCODING_UTF8
)
378 const char* what() const noexcept override
380 return maMessage
.getStr();
384 class invalid_size
: public std::exception
389 invalid_size(const size_t nExpectedSize
, const size_t nReceivedSize
)
392 OUString("Invalid file size found.\nExpected: " + OUString::number(nExpectedSize
) + ";\nReceived: " + OUString::number(nReceivedSize
)),
393 RTL_TEXTENCODING_UTF8
)
398 const char* what() const noexcept override
400 return maMessage
.getStr();
404 OUString
toOUString(std::string_view str
)
406 return OUString::fromUtf8(str
);
409 update_file
parse_update_file(orcus::json::node
& rNode
)
411 if (rNode
.type() != orcus::json::node_t::object
)
413 SAL_WARN("desktop.updater", "invalid update or language file entry");
414 throw invalid_update_info();
417 if (rNode
.child_count() < 4)
419 SAL_WARN("desktop.updater", "invalid update or language file entry");
420 throw invalid_update_info();
423 orcus::json::node aURLNode
= rNode
.child("url");
424 orcus::json::node aHashNode
= rNode
.child("hash");
425 orcus::json::node aHashTypeNode
= rNode
.child("hash_function");
426 orcus::json::node aSizeNode
= rNode
.child("size");
428 if (aHashTypeNode
.string_value() != "sha512")
430 SAL_WARN("desktop.updater", "invalid hash type");
431 throw invalid_update_info();
434 update_file aUpdateFile
;
435 aUpdateFile
.aURL
= toOUString(aURLNode
.string_value());
437 if (aUpdateFile
.aURL
.isEmpty())
438 throw invalid_update_info();
440 aUpdateFile
.aHash
= toOUString(aHashNode
.string_value());
441 aUpdateFile
.nSize
= static_cast<sal_uInt32
>(aSizeNode
.numeric_value());
445 update_info
parse_response(const std::string
& rResponse
)
447 orcus::json::document_tree aJsonDoc
;
448 orcus::json_config aConfig
;
449 aJsonDoc
.load(rResponse
, aConfig
);
451 auto aDocumentRoot
= aJsonDoc
.get_document_root();
452 if (aDocumentRoot
.type() != orcus::json::node_t::object
)
454 SAL_WARN("desktop.updater", "invalid root entries: " << rResponse
);
455 throw invalid_update_info();
458 auto aRootKeys
= aDocumentRoot
.keys();
459 if (std::find(aRootKeys
.begin(), aRootKeys
.end(), "error") != aRootKeys
.end())
461 throw invalid_update_info();
463 else if (std::find(aRootKeys
.begin(), aRootKeys
.end(), "response") != aRootKeys
.end())
465 update_info aUpdateInfo
;
466 auto aMsgNode
= aDocumentRoot
.child("response");
467 aUpdateInfo
.aMessage
= toOUString(aMsgNode
.string_value());
471 orcus::json::node aFromNode
= aDocumentRoot
.child("from");
472 if (aFromNode
.type() != orcus::json::node_t::string
)
474 throw invalid_update_info();
477 orcus::json::node aSeeAlsoNode
= aDocumentRoot
.child("see also");
478 if (aSeeAlsoNode
.type() != orcus::json::node_t::string
)
480 throw invalid_update_info();
483 orcus::json::node aUpdateNode
= aDocumentRoot
.child("update");
484 if (aUpdateNode
.type() != orcus::json::node_t::object
)
486 throw invalid_update_info();
489 orcus::json::node aLanguageNode
= aDocumentRoot
.child("languages");
490 if (aLanguageNode
.type() != orcus::json::node_t::object
)
492 throw invalid_update_info();
495 update_info aUpdateInfo
;
496 aUpdateInfo
.aFromBuildID
= toOUString(aFromNode
.string_value());
497 aUpdateInfo
.aSeeAlsoURL
= toOUString(aSeeAlsoNode
.string_value());
499 aUpdateInfo
.aUpdateFile
= parse_update_file(aUpdateNode
);
501 std::vector
<std::string_view
> aLanguages
= aLanguageNode
.keys();
502 for (auto const& language
: aLanguages
)
504 language_file aLanguageFile
;
505 auto aLangEntry
= aLanguageNode
.child(language
);
506 aLanguageFile
.aLangCode
= toOUString(language
);
507 aLanguageFile
.aUpdateFile
= parse_update_file(aLangEntry
);
508 aUpdateInfo
.aLanguageFiles
.push_back(aLanguageFile
);
516 comphelper::Hash maHash
;
519 WriteDataFile(SvStream
* pStream
):
520 maHash(comphelper::HashType::SHA512
),
527 auto final_hash
= maHash
.finalize();
528 std::stringstream aStrm
;
529 for (auto& i
: final_hash
)
531 aStrm
<< std::setw(2) << std::setfill('0') << std::hex
<< (int)i
;
534 return toOUString(aStrm
.str());
538 // Callback to get the response data from server to a file.
539 size_t WriteCallbackFile(void *ptr
, size_t size
,
540 size_t nmemb
, void *userp
)
545 WriteDataFile
* response
= static_cast<WriteDataFile
*>(userp
);
546 size_t real_size
= size
* nmemb
;
547 response
->mpStream
->WriteBytes(ptr
, real_size
);
548 response
->maHash
.update(static_cast<const unsigned char*>(ptr
), real_size
);
552 std::string
download_content(const OString
& rURL
, bool bFile
, OUString
& rHash
)
554 Updater::log("Download: " + OStringToOUString(rURL
, osl_getThreadTextEncoding()));
555 std::unique_ptr
<CURL
, std::function
<void(CURL
*)>> curl(
556 curl_easy_init(), [](CURL
* p
) { curl_easy_cleanup(p
); });
559 return std::string();
561 static const OUString kUserAgent
562 = u
"LibreOffice UpdateChecker/1.0 (os_version)"_ustr
.replaceFirst(
563 "os_version", Application::GetOSVersion());
565 ::InitCurl_easy(curl
.get());
567 curl_easy_setopt(curl
.get(), CURLOPT_URL
, rURL
.getStr());
568 curl_easy_setopt(curl
.get(), CURLOPT_USERAGENT
, kUserAgent
.toUtf8().getStr());
569 bool bUseProxy
= false;
573 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
574 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
578 char buf
[] = "Expect:";
579 curl_slist
* headerlist
= nullptr;
580 headerlist
= curl_slist_append(headerlist
, buf
);
581 curl_easy_setopt(curl
.get(), CURLOPT_HTTPHEADER
, headerlist
);
582 curl_easy_setopt(curl
.get(), CURLOPT_FOLLOWLOCATION
, 1); // follow redirects
583 // only allow redirect to https://
584 #if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85)
585 curl_easy_setopt(curl
.get(), CURLOPT_REDIR_PROTOCOLS_STR
, "https");
587 curl_easy_setopt(curl
.get(), CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTPS
);
590 std::string response_body
;
591 utl::TempFileNamed aTempFile
;
592 WriteDataFile
aFile(aTempFile
.GetStream(StreamMode::WRITE
));
595 curl_easy_setopt(curl
.get(), CURLOPT_WRITEFUNCTION
, WriteCallback
);
596 curl_easy_setopt(curl
.get(), CURLOPT_WRITEDATA
,
597 static_cast<void *>(&response_body
));
599 aTempFile
.EnableKillingFile(true);
603 OUString aTempFileURL
= aTempFile
.GetURL();
604 OString aTempFileURLOString
= OUStringToOString(aTempFileURL
, RTL_TEXTENCODING_UTF8
);
605 response_body
.append(aTempFileURLOString
.getStr(), aTempFileURLOString
.getLength());
607 aTempFile
.EnableKillingFile(false);
609 curl_easy_setopt(curl
.get(), CURLOPT_WRITEFUNCTION
, WriteCallbackFile
);
610 curl_easy_setopt(curl
.get(), CURLOPT_WRITEDATA
,
611 static_cast<void *>(&aFile
));
614 // Fail if 400+ is returned from the web server.
615 curl_easy_setopt(curl
.get(), CURLOPT_FAILONERROR
, 1);
617 CURLcode cc
= curl_easy_perform(curl
.get());
619 curl_easy_getinfo(curl
.get(), CURLINFO_RESPONSE_CODE
, &http_code
);
620 if (http_code
!= 200)
622 SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code
);
623 throw error_updater("download did not succeed");
628 SAL_WARN("desktop.updater", "curl error: " << cc
);
629 throw error_updater("curl error");
633 rHash
= aFile
.getHash();
635 return response_body
;
638 void handle_file_error(osl::FileBase::RC eError
, const OUString
& rMsg
)
642 case osl::FileBase::E_None
:
645 SAL_WARN("desktop.updater", "file error code: " << eError
<< ", " << rMsg
);
646 throw error_updater(OUStringToOString(rMsg
, RTL_TEXTENCODING_UTF8
));
650 void download_file(const OUString
& rURL
, size_t nFileSize
, const OUString
& rHash
, const OUString
& aFileName
)
652 Updater::log("Download File: " + rURL
+ "; FileName: " + aFileName
);
653 OString aURL
= OUStringToOString(rURL
, RTL_TEXTENCODING_UTF8
);
655 std::string temp_file
= download_content(aURL
, true, aHash
);
656 if (temp_file
.empty())
657 throw error_updater("empty temp file string");
659 OUString aTempFile
= OUString::fromUtf8(temp_file
.c_str());
660 Updater::log("TempFile: " + aTempFile
);
661 osl::File
aDownloadedFile(aTempFile
);
662 osl::FileBase::RC eError
= aDownloadedFile
.open(1);
663 handle_file_error(eError
, "Could not open the download file: " + aTempFile
);
665 sal_uInt64 nSize
= 0;
666 eError
= aDownloadedFile
.getSize(nSize
);
667 handle_file_error(eError
, "Could not get the file size of the downloaded file: " + aTempFile
);
668 if (nSize
!= nFileSize
)
670 SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
671 throw invalid_size(nFileSize
, nSize
);
676 SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
677 throw invalid_hash(rHash
, aHash
);
680 OUString
aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates/");
681 rtl::Bootstrap::expandMacros(aPatchDirURL
);
682 osl::Directory::create(aPatchDirURL
);
683 aPatchDirURL
+= "0/";
684 osl::Directory::create(aPatchDirURL
);
686 OUString aDestFile
= aPatchDirURL
+ aFileName
;
687 Updater::log("Destination File: " + aDestFile
);
688 aDownloadedFile
.close();
689 eError
= osl::File::move(aTempFile
, aDestFile
);
690 handle_file_error(eError
, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile
+ "; DestFile: " + aDestFile
);
695 void update_checker()
697 OUString
aBrandBaseDir("${BRAND_BASE_DIR}");
698 rtl::Bootstrap::expandMacros(aBrandBaseDir
);
699 bool bUserWritable
= isUserWritable(aBrandBaseDir
);
702 Updater::log("Can't update as the update location is not user writable");
706 OUString aDownloadCheckBaseURL
= officecfg::Office::Update::Update::URL::get();
707 static const char* pDownloadCheckBaseURLEnv
= std::getenv("LIBO_UPDATER_URL");
708 if (pDownloadCheckBaseURLEnv
)
710 aDownloadCheckBaseURL
= OUString::createFromAscii(pDownloadCheckBaseURLEnv
);
713 OUString aProductName
= utl::ConfigManager::getProductName();
714 OUString aBuildID
= Updater::getBuildID();
716 static const char* pBuildIdEnv
= std::getenv("LIBO_UPDATER_BUILD");
719 aBuildID
= OUString::createFromAscii(pBuildIdEnv
);
722 OUString aBuildTarget
= "${_OS}_${_ARCH}";
723 rtl::Bootstrap::expandMacros(aBuildTarget
);
724 OUString aChannel
= Updater::getUpdateChannel();
725 static const char* pUpdateChannelEnv
= std::getenv("LIBO_UPDATER_CHANNEL");
726 if (pUpdateChannelEnv
)
728 aChannel
= OUString::createFromAscii(pUpdateChannelEnv
);
731 OUString aDownloadCheckURL
= aDownloadCheckBaseURL
+ "update/check/1/" + aProductName
+
732 "/" + aBuildID
+ "/" + aBuildTarget
+ "/" + aChannel
;
733 OString aURL
= OUStringToOString(aDownloadCheckURL
, RTL_TEXTENCODING_UTF8
);
734 Updater::log("Update check: " + OStringToOUString(aURL
, osl_getThreadTextEncoding()));
739 std::string response_body
= download_content(aURL
, false, aHash
);
740 if (!response_body
.empty())
743 update_info aUpdateInfo
= parse_response(response_body
);
744 if (aUpdateInfo
.aUpdateFile
.aURL
.isEmpty())
746 // No update currently available
747 // add entry to updating.log with the message
748 SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo
.aMessage
);
749 Updater::log("Server response: " + aUpdateInfo
.aMessage
);
753 css::uno::Sequence
<OUString
> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
754 std::set
<OUString
> aInstalledLanguageSet(std::begin(aInstalledLanguages
), std::end(aInstalledLanguages
));
755 download_file(aUpdateInfo
.aUpdateFile
.aURL
, aUpdateInfo
.aUpdateFile
.nSize
, aUpdateInfo
.aUpdateFile
.aHash
, "update.mar");
756 for (auto& lang_update
: aUpdateInfo
.aLanguageFiles
)
758 // only download the language packs for installed languages
759 if (aInstalledLanguageSet
.find(lang_update
.aLangCode
) != aInstalledLanguageSet
.end())
761 OUString aFileName
= "update_" + lang_update
.aLangCode
+ ".mar";
762 download_file(lang_update
.aUpdateFile
.aURL
, lang_update
.aUpdateFile
.nSize
, lang_update
.aUpdateFile
.aHash
, aFileName
);
765 OUString aSeeAlsoURL
= aUpdateInfo
.aSeeAlsoURL
;
766 std::shared_ptr
< comphelper::ConfigurationChanges
> batch(
767 comphelper::ConfigurationChanges::create());
768 officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL
, batch
);
770 OUString
const statUrl
= Updater::getPatchDirURL() + "update.status";
771 SvFileStream
stat(statUrl
, StreamMode::WRITE
| StreamMode::TRUNC
);
772 stat
.WriteOString("pending-service");
774 if (auto const e
= stat
.GetError()) {
775 Updater::log("Writing <" + statUrl
+ "> failed with " + e
.toString());
781 catch (const invalid_update_info
&)
783 SAL_WARN("desktop.updater", "invalid update information");
784 Updater::log("warning: invalid update info");
786 catch (const error_updater
& e
)
788 SAL_WARN("desktop.updater", "error during the update check: " << e
.what());
789 Updater::log("warning: error by the updater" + o3tl::runtimeToOUString(e
.what()));
791 catch (const invalid_size
& e
)
793 SAL_WARN("desktop.updater", e
.what());
794 Updater::log("warning: invalid size");
796 catch (const invalid_hash
& e
)
798 SAL_WARN("desktop.updater", e
.what());
799 Updater::log("warning: invalid hash");
803 SAL_WARN("desktop.updater", "unknown error during the update check");
804 Updater::log("warning: unknown exception");
808 OUString
Updater::getPatchDirURL()
810 OUString
aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates/0/");
811 rtl::Bootstrap::expandMacros(aPatchDirURL
);
816 OUString
Updater::getUpdateFileURL()
818 return getPatchDirURL() + "update.mar";
821 OUString
Updater::getInstallationPath()
823 OUString
aInstallDir( "$BRAND_BASE_DIR/");
824 rtl::Bootstrap::expandMacros(aInstallDir
);
826 return getPathFromURL(aInstallDir
);
829 OUString
Updater::getExecutableDirURL()
831 OUString
aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER
"/" );
832 rtl::Bootstrap::expandMacros(aExeDir
);
837 void Updater::log(const OUString
& rMessage
)
839 SAL_INFO("desktop.updater", rMessage
);
840 OUString
dir("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates");
841 rtl::Bootstrap::expandMacros(dir
);
842 osl::Directory::create(dir
);
843 SvFileStream
aLog(dir
+ "/updating.log", StreamMode::STD_READWRITE
);
844 aLog
.Seek(aLog
.Tell() + aLog
.remainingSize()); // make sure we are at the end
845 aLog
.WriteLine(OUStringToOString(rMessage
, RTL_TEXTENCODING_UTF8
));
848 OUString
Updater::getBuildID()
850 OUString
aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("version") ":buildid}");
851 rtl::Bootstrap::expandMacros(aBuildID
);
856 OUString
Updater::getUpdateChannel()
858 OUString
aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("version") ":UpdateChannel}");
859 rtl::Bootstrap::expandMacros(aUpdateChannel
);
861 return aUpdateChannel
;
864 void Updater::removeUpdateFiles()
866 Updater::log("Removing: " + getUpdateFileURL());
867 osl::File::remove(getUpdateFileURL());
869 OUString aPatchDirURL
= getPatchDirURL();
870 osl::Directory
aDir(aPatchDirURL
);
873 osl::FileBase::RC eRC
;
876 osl::DirectoryItem aItem
;
877 eRC
= aDir
.getNextItem(aItem
);
878 if (eRC
== osl::FileBase::E_None
)
880 osl::FileStatus
aStatus(osl_FileStatus_Mask_All
);
881 if (aItem
.getFileStatus(aStatus
) != osl::FileBase::E_None
)
884 if (!aStatus
.isRegular())
887 OUString aURL
= aStatus
.getFileURL();
888 if (!aURL
.endsWith(".mar"))
891 Updater::log("Removing. " + aURL
);
892 osl::File::remove(aURL
);
895 while (eRC
== osl::FileBase::E_None
);
898 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */