Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / desktop / source / app / updater.cxx
blob7ff6234b45555af2326a3828343554374e989645
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "updater.hxx"
12 #if UNX
13 #include <unistd.h>
14 #include <errno.h>
16 #endif
18 #ifdef _WIN32
19 #include <comphelper/windowsStart.hxx>
20 #endif
22 #include <fstream>
23 #include <config_folders.h>
24 #include <rtl/bootstrap.hxx>
26 #include <officecfg/Office/Update.hxx>
28 #include <rtl/ustring.hxx>
29 #include <unotools/tempfile.hxx>
30 #include <unotools/configmgr.hxx>
31 #include <osl/file.hxx>
32 #include <rtl/process.h>
33 #include <sal/log.hxx>
35 #include <curl/curl.h>
37 #include <orcus/json_document_tree.hpp>
38 #include <orcus/config.hpp>
39 #include <orcus/pstring.hpp>
41 #include <curlinit.hxx>
42 #include <comphelper/hash.hxx>
44 #include <com/sun/star/container/XNameAccess.hpp>
46 #include <officecfg/Setup.hxx>
48 #include <functional>
49 #include <memory>
50 #include <set>
52 namespace {
54 class error_updater : public std::exception
56 OString maStr;
57 public:
59 error_updater(const OString& rStr):
60 maStr(rStr)
64 virtual const char* what() const throw() override
66 return maStr.getStr();
70 #ifdef UNX
71 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
72 #else
73 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
74 #endif
76 #ifdef UNX
77 const char* const pUpdaterName = "updater";
78 const char* const pSofficeExeName = "soffice";
79 #elif defined(_WIN32)
80 const char* pUpdaterName = "updater.exe";
81 const char* pSofficeExeName = "soffice.exe";
82 #else
83 #error "Need implementation"
84 #endif
86 OUString normalizePath(const OUString& rPath)
88 OUString aPath = rPath.replaceAll("//", "/");
90 // remove final /
91 if (aPath.endsWith("/"))
93 aPath = aPath.copy(0, aPath.getLength() - 1);
96 while (aPath.indexOf("/..") != -1)
98 sal_Int32 nIndex = aPath.indexOf("/..");
99 sal_Int32 i = nIndex - 1;
100 for (; i > 0; --i)
102 if (aPath[i] == '/')
103 break;
106 OUString aTempPath = aPath;
107 aPath = aTempPath.copy(0, i) + aPath.copy(nIndex + 3);
110 return aPath.replaceAll("\\", "/");
113 void CopyFileToDir(const OUString& rTempDirURL, const OUString & rFileName, const OUString& rOldDir)
115 OUString aSourceURL = rOldDir + "/" + rFileName;
116 OUString aDestURL = rTempDirURL + "/" + rFileName;
118 osl::File::RC eError = osl::File::copy(aSourceURL, aDestURL);
119 if (eError != osl::File::E_None)
121 SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName);
122 throw std::exception();
126 OUString getPathFromURL(const OUString& rURL)
128 OUString aPath;
129 osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
131 return normalizePath(aPath);
134 void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
136 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
137 CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
140 #ifdef UNX
141 typedef char CharT;
142 #define tstrncpy std::strncpy
143 #elif defined(_WIN32)
144 typedef wchar_t CharT;
145 #define tstrncpy std::wcsncpy
146 #else
147 #error "Need an implementation"
148 #endif
150 void createStr(const OUString& rStr, CharT** pArgs, size_t i)
152 #ifdef UNX
153 OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
154 #elif defined(_WIN32)
155 OUString aStr = rStr;
156 #else
157 #error "Need an implementation"
158 #endif
159 CharT* pStr = new CharT[aStr.getLength() + 1];
160 tstrncpy(pStr, (CharT*)aStr.getStr(), aStr.getLength());
161 pStr[aStr.getLength()] = '\0';
162 pArgs[i] = pStr;
165 CharT** createCommandLine()
167 OUString aInstallDir = Updater::getInstallationPath();
169 size_t nCommandLineArgs = rtl_getAppCommandArgCount();
170 size_t nArgs = 8 + nCommandLineArgs;
171 CharT** pArgs = new CharT*[nArgs];
173 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
174 createStr(aUpdaterName, pArgs, 0);
177 // directory with the patch log
178 OUString aPatchDir = Updater::getPatchDirURL();
179 rtl::Bootstrap::expandMacros(aPatchDir);
180 OUString aTempDirPath = getPathFromURL(aPatchDir);
181 Updater::log("Patch Dir: " + aTempDirPath);
182 createStr(aTempDirPath, pArgs, 1);
185 // the actual update directory
186 Updater::log("Install Dir: " + aInstallDir);
187 createStr(aInstallDir, pArgs, 2);
190 // the temporary updated build
191 Updater::log("Working Dir: " + aInstallDir);
192 createStr(aInstallDir, pArgs, 3);
195 #ifdef UNX
196 OUString aPID("0");
197 #elif defined(_WIN32)
198 oslProcessInfo aInfo;
199 aInfo.Size = sizeof(oslProcessInfo);
200 osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aInfo);
201 OUString aPID = OUString::number(aInfo.Ident);
202 #else
203 #error "Need an implementation"
204 #endif
205 createStr(aPID, pArgs, 4);
208 OUString aExeDir = Updater::getExecutableDirURL();
209 OUString aSofficePath = getPathFromURL(aExeDir);
210 Updater::log("soffice Path: " + aSofficePath);
211 createStr(aSofficePath, pArgs, 5);
214 // the executable to start after the successful update
215 OUString aExeDir = Updater::getExecutableDirURL();
216 OUString aSofficePathURL = aExeDir + OUString::fromUtf8(pSofficeExeName);
217 OUString aSofficePath = getPathFromURL(aSofficePathURL);
218 createStr(aSofficePath, pArgs, 6);
221 // add the command line arguments from the soffice list
222 for (size_t i = 0; i < nCommandLineArgs; ++i)
224 OUString aCommandLineArg;
225 rtl_getAppCommandArg(i, &aCommandLineArg.pData);
226 createStr(aCommandLineArg, pArgs, 7 + i);
229 pArgs[nArgs - 1] = nullptr;
231 return pArgs;
234 struct update_file
236 OUString aURL;
237 OUString aHash;
238 size_t nSize;
241 struct language_file
243 update_file aUpdateFile;
244 OUString aLangCode;
247 struct update_info
249 OUString aFromBuildID;
250 OUString aSeeAlsoURL;
251 OUString aMessage;
253 update_file aUpdateFile;
254 std::vector<language_file> aLanguageFiles;
257 bool isUserWritable(const OUString& rFileURL)
259 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
260 osl::DirectoryItem aDirectoryItem;
262 osl::FileBase::RC eRes = osl::DirectoryItem::get(rFileURL, aDirectoryItem);
263 if (eRes != osl::FileBase::E_None)
265 Updater::log("Could not get the directory item for: " + rFileURL);
266 return false;
269 osl::FileBase::RC eResult = aDirectoryItem.getFileStatus(aStatus);
270 if (eResult != osl::FileBase::E_None)
272 Updater::log("Could not get the file status for: " + rFileURL);
273 return false;
276 bool bReadOnly = (aStatus.getAttributes() & static_cast<sal_uInt64>(osl_File_Attribute_ReadOnly)) != 0;
277 if (bReadOnly)
279 Updater::log("Update location as determined by: " + rFileURL + " is read-only.");
280 return false;
283 return true;
288 bool update()
290 utl::TempFileNamed aTempDir(nullptr, true);
291 OUString aTempDirURL = aTempDir.GetURL();
292 CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL);
294 OUString aUpdaterPath = getPathFromURL(aTempDirURL + "/" + OUString::fromUtf8(pUpdaterName));
296 Updater::log("Calling the updater with parameters: ");
297 CharT** pArgs = createCommandLine();
299 bool bSuccess = true;
300 const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
301 if (!pUpdaterTestReplace)
303 #if UNX
304 OString aPath = OUStringToOString(aUpdaterPath, RTL_TEXTENCODING_UTF8);
305 if (execv(aPath.getStr(), pArgs))
307 printf("execv failed with error %d %s\n",errno,strerror(errno));
308 bSuccess = false;
310 #elif defined(_WIN32)
311 bSuccess = WinLaunchChild((wchar_t*)aUpdaterPath.getStr(), 8, pArgs);
312 #endif
314 else
316 SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath);
317 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
319 SAL_WARN("desktop.updater", pArgs[i]);
321 bSuccess = false;
324 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
326 delete[] pArgs[i];
328 delete[] pArgs;
330 return bSuccess;
333 namespace {
335 // Callback to get the response data from server.
336 size_t WriteCallback(void *ptr, size_t size,
337 size_t nmemb, void *userp)
339 if (!userp)
340 return 0;
342 std::string* response = static_cast<std::string *>(userp);
343 size_t real_size = size * nmemb;
344 response->append(static_cast<char *>(ptr), real_size);
345 return real_size;
350 class invalid_update_info : public std::exception
354 class invalid_hash : public std::exception
356 OString maMessage;
357 public:
359 invalid_hash(const OUString& rExpectedHash, const OUString& rReceivedHash)
360 : maMessage(
361 OUStringToOString(
362 OUString("Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash),
363 RTL_TEXTENCODING_UTF8)
368 const char* what() const noexcept override
370 return maMessage.getStr();
374 class invalid_size : public std::exception
376 OString maMessage;
377 public:
379 invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
380 : maMessage(
381 OUStringToOString(
382 OUString("Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize)),
383 RTL_TEXTENCODING_UTF8)
388 const char* what() const noexcept override
390 return maMessage.getStr();
394 OUString toOUString(const std::string& rStr)
396 return OUString::fromUtf8(rStr.c_str());
399 update_file parse_update_file(orcus::json::node& rNode)
401 if (rNode.type() != orcus::json::node_t::object)
403 SAL_WARN("desktop.updater", "invalid update or language file entry");
404 throw invalid_update_info();
407 if (rNode.child_count() < 4)
409 SAL_WARN("desktop.updater", "invalid update or language file entry");
410 throw invalid_update_info();
413 orcus::json::node aURLNode = rNode.child("url");
414 orcus::json::node aHashNode = rNode.child("hash");
415 orcus::json::node aHashTypeNode = rNode.child("hash_function");
416 orcus::json::node aSizeNode = rNode.child("size");
418 if (aHashTypeNode.string_value() != "sha512")
420 SAL_WARN("desktop.updater", "invalid hash type");
421 throw invalid_update_info();
424 update_file aUpdateFile;
425 aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
427 if (aUpdateFile.aURL.isEmpty())
428 throw invalid_update_info();
430 aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
431 aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
432 return aUpdateFile;
435 update_info parse_response(const std::string& rResponse)
437 orcus::json::document_tree aJsonDoc;
438 orcus::json_config aConfig;
439 aJsonDoc.load(rResponse, aConfig);
441 auto aDocumentRoot = aJsonDoc.get_document_root();
442 if (aDocumentRoot.type() != orcus::json::node_t::object)
444 SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
445 throw invalid_update_info();
448 auto aRootKeys = aDocumentRoot.keys();
449 if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
451 throw invalid_update_info();
453 else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
455 update_info aUpdateInfo;
456 auto aMsgNode = aDocumentRoot.child("response");
457 aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
458 return aUpdateInfo;
461 orcus::json::node aFromNode = aDocumentRoot.child("from");
462 if (aFromNode.type() != orcus::json::node_t::string)
464 throw invalid_update_info();
467 orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
468 if (aSeeAlsoNode.type() != orcus::json::node_t::string)
470 throw invalid_update_info();
473 orcus::json::node aUpdateNode = aDocumentRoot.child("update");
474 if (aUpdateNode.type() != orcus::json::node_t::object)
476 throw invalid_update_info();
479 orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
480 if (aUpdateNode.type() != orcus::json::node_t::object)
482 throw invalid_update_info();
485 update_info aUpdateInfo;
486 aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
487 aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
489 aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
491 std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
492 for (auto const& language : aLanguages)
494 language_file aLanguageFile;
495 auto aLangEntry = aLanguageNode.child(language);
496 aLanguageFile.aLangCode = toOUString(language.str());
497 aLanguageFile.aUpdateFile = parse_update_file(aLangEntry);
498 aUpdateInfo.aLanguageFiles.push_back(aLanguageFile);
501 return aUpdateInfo;
504 struct WriteDataFile
506 comphelper::Hash maHash;
507 SvStream* mpStream;
509 WriteDataFile(SvStream* pStream):
510 maHash(comphelper::HashType::SHA512),
511 mpStream(pStream)
515 OUString getHash()
517 auto final_hash = maHash.finalize();
518 std::stringstream aStrm;
519 for (auto& i: final_hash)
521 aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
524 return toOUString(aStrm.str());
528 // Callback to get the response data from server to a file.
529 size_t WriteCallbackFile(void *ptr, size_t size,
530 size_t nmemb, void *userp)
532 if (!userp)
533 return 0;
535 WriteDataFile* response = static_cast<WriteDataFile *>(userp);
536 size_t real_size = size * nmemb;
537 response->mpStream->WriteBytes(ptr, real_size);
538 response->maHash.update(static_cast<const unsigned char*>(ptr), real_size);
539 return real_size;
542 std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
544 Updater::log("Download: " + rURL);
545 std::unique_ptr<CURL, std::function<void(CURL *)>> curl(
546 curl_easy_init(), [](CURL * p) { curl_easy_cleanup(p); });
548 if (!curl)
549 return std::string();
551 ::InitCurl_easy(curl.get());
553 curl_easy_setopt(curl.get(), CURLOPT_URL, rURL.getStr());
554 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, kUserAgent);
555 bool bUseProxy = false;
556 if (bUseProxy)
559 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
560 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
564 char buf[] = "Expect:";
565 curl_slist* headerlist = nullptr;
566 headerlist = curl_slist_append(headerlist, buf);
567 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headerlist);
568 curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1); // follow redirects
569 // only allow redirect to https://
570 #if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85)
571 curl_easy_setopt(curl.get(), CURLOPT_REDIR_PROTOCOLS_STR, "https");
572 #else
573 curl_easy_setopt(curl.get(), CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
574 #endif
576 std::string response_body;
577 utl::TempFileNamed aTempFile;
578 WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
579 if (!bFile)
581 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
582 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
583 static_cast<void *>(&response_body));
585 aTempFile.EnableKillingFile(true);
587 else
589 OUString aTempFileURL = aTempFile.GetURL();
590 OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
591 response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
593 aTempFile.EnableKillingFile(false);
595 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallbackFile);
596 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
597 static_cast<void *>(&aFile));
600 // Fail if 400+ is returned from the web server.
601 curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1);
603 CURLcode cc = curl_easy_perform(curl.get());
604 long http_code = 0;
605 curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
606 if (http_code != 200)
608 SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
609 throw error_updater("download did not succeed");
612 if (cc != CURLE_OK)
614 SAL_WARN("desktop.updater", "curl error: " << cc);
615 throw error_updater("curl error");
618 if (bFile)
619 rHash = aFile.getHash();
621 return response_body;
624 void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
626 switch (eError)
628 case osl::FileBase::E_None:
629 break;
630 default:
631 SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
632 throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
636 void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
638 Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
639 OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
640 OUString aHash;
641 std::string temp_file = download_content(aURL, true, aHash);
642 if (temp_file.empty())
643 throw error_updater("empty temp file string");
645 OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
646 Updater::log("TempFile: " + aTempFile);
647 osl::File aDownloadedFile(aTempFile);
648 osl::FileBase::RC eError = aDownloadedFile.open(1);
649 handle_file_error(eError, "Could not open the download file: " + aTempFile);
651 sal_uInt64 nSize = 0;
652 eError = aDownloadedFile.getSize(nSize);
653 handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
654 if (nSize != nFileSize)
656 SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
657 throw invalid_size(nFileSize, nSize);
660 if (aHash != rHash)
662 SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
663 throw invalid_hash(rHash, aHash);
666 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
667 rtl::Bootstrap::expandMacros(aPatchDirURL);
668 osl::Directory::create(aPatchDirURL);
670 OUString aDestFile = aPatchDirURL + aFileName;
671 Updater::log("Destination File: " + aDestFile);
672 aDownloadedFile.close();
673 eError = osl::File::move(aTempFile, aDestFile);
674 handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
679 void update_checker()
681 OUString aBrandBaseDir("${BRAND_BASE_DIR}");
682 rtl::Bootstrap::expandMacros(aBrandBaseDir);
683 bool bUserWritable = isUserWritable(aBrandBaseDir);
684 if (!bUserWritable)
686 Updater::log("Can't update as the update location is not user writable");
687 return;
690 OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
691 static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
692 if (pDownloadCheckBaseURLEnv)
694 aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
697 OUString aProductName = utl::ConfigManager::getProductName();
698 OUString aBuildID = Updater::getBuildID();
700 static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
701 if (pBuildIdEnv)
703 aBuildID = OUString::createFromAscii(pBuildIdEnv);
706 OUString aBuildTarget = "${_OS}_${_ARCH}";
707 rtl::Bootstrap::expandMacros(aBuildTarget);
708 OUString aChannel = Updater::getUpdateChannel();
709 static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
710 if (pUpdateChannelEnv)
712 aChannel = OUString::createFromAscii(pUpdateChannelEnv);
715 OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
716 "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
717 OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
718 Updater::log("Update check: " + aURL);
722 OUString aHash;
723 std::string response_body = download_content(aURL, false, aHash);
724 if (!response_body.empty())
727 update_info aUpdateInfo = parse_response(response_body);
728 if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
730 // No update currently available
731 // add entry to updating.log with the message
732 SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
733 Updater::log("Server response: " + aUpdateInfo.aMessage);
735 else
737 css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
738 std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
739 download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
740 for (auto& lang_update : aUpdateInfo.aLanguageFiles)
742 // only download the language packs for installed languages
743 if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
745 OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
746 download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
749 OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
750 std::shared_ptr< comphelper::ConfigurationChanges > batch(
751 comphelper::ConfigurationChanges::create());
752 officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
753 batch->commit();
757 catch (const invalid_update_info&)
759 SAL_WARN("desktop.updater", "invalid update information");
760 Updater::log(OString("warning: invalid update info"));
762 catch (const error_updater& e)
764 SAL_WARN("desktop.updater", "error during the update check: " << e.what());
765 Updater::log(OString("warning: error by the updater") + e.what());
767 catch (const invalid_size& e)
769 SAL_WARN("desktop.updater", e.what());
770 Updater::log(OString("warning: invalid size"));
772 catch (const invalid_hash& e)
774 SAL_WARN("desktop.updater", e.what());
775 Updater::log(OString("warning: invalid hash"));
777 catch (...)
779 SAL_WARN("desktop.updater", "unknown error during the update check");
780 Updater::log(OString("warning: unknown exception"));
784 OUString Updater::getUpdateInfoLog()
786 OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
787 rtl::Bootstrap::expandMacros(aUpdateInfoURL);
789 return aUpdateInfoURL;
792 OUString Updater::getPatchDirURL()
794 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
795 rtl::Bootstrap::expandMacros(aPatchDirURL);
797 return aPatchDirURL;
800 OUString Updater::getUpdateFileURL()
802 return getPatchDirURL() + "update.mar";
805 OUString Updater::getInstallationPath()
807 OUString aInstallDir( "$BRAND_BASE_DIR/");
808 rtl::Bootstrap::expandMacros(aInstallDir);
810 return getPathFromURL(aInstallDir);
813 OUString Updater::getExecutableDirURL()
815 OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
816 rtl::Bootstrap::expandMacros(aExeDir);
818 return aExeDir;
821 void Updater::log(const OUString& rMessage)
823 SAL_INFO("desktop.updater", rMessage);
824 OUString aUpdateLog = getUpdateInfoLog();
825 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
826 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
827 aLog.WriteLine(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
830 void Updater::log(const OString& rMessage)
832 SAL_INFO("desktop.updater", rMessage);
833 OUString aUpdateLog = getUpdateInfoLog();
834 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
835 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
836 aLog.WriteLine(rMessage);
839 void Updater::log(const char* pMessage)
841 SAL_INFO("desktop.updater", pMessage);
842 OUString aUpdateLog = getUpdateInfoLog();
843 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
844 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
845 aLog.WriteOString(pMessage);
848 OUString Updater::getBuildID()
850 OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
851 rtl::Bootstrap::expandMacros(aBuildID);
853 return 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);
871 aDir.open();
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)
882 continue;
884 if (!aStatus.isRegular())
885 continue;
887 OUString aURL = aStatus.getFileURL();
888 if (!aURL.endsWith(".mar"))
889 continue;
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: */