Bump version to 6.0-36
[LibreOffice.git] / desktop / source / app / updater.cxx
blobe34cc6a7cce01d4999af631e3abeaf189416753d
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>
34 #include <curl/curl.h>
36 #include <orcus/json_document_tree.hpp>
37 #include <orcus/config.hpp>
38 #include <orcus/pstring.hpp>
39 #include <comphelper/hash.hxx>
41 #include <com/sun/star/container/XNameAccess.hpp>
43 #include <officecfg/Setup.hxx>
45 #include <set>
47 namespace {
49 class error_updater : public std::exception
51 OString maStr;
52 public:
54 error_updater(const OString& rStr):
55 maStr(rStr)
59 virtual const char* what() const throw() override
61 return maStr.getStr();
65 #ifdef UNX
66 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
67 #else
68 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
69 #endif
71 #ifdef UNX
72 const char* const pUpdaterName = "updater";
73 const char* const pSofficeExeName = "soffice";
74 #elif defined(WNT)
75 const char* pUpdaterName = "updater.exe";
76 const char* pSofficeExeName = "soffice.exe";
77 #else
78 #error "Need implementation"
79 #endif
81 OUString normalizePath(const OUString& rPath)
83 OUString aPath = rPath.replaceAll("//", "/");
85 // remove final /
86 if (aPath.endsWith("/"))
88 aPath = aPath.copy(0, aPath.getLength() - 1);
91 while (aPath.indexOf("/..") != -1)
93 sal_Int32 nIndex = aPath.indexOf("/..");
94 sal_Int32 i = nIndex - 1;
95 for (; i > 0; --i)
97 if (aPath[i] == '/')
98 break;
101 OUString aTempPath = aPath;
102 aPath = aTempPath.copy(0, i) + aPath.copy(nIndex + 3);
105 return aPath.replaceAll("\\", "/");
108 void CopyFileToDir(const OUString& rTempDirURL, const OUString & rFileName, const OUString& rOldDir)
110 OUString aSourceURL = rOldDir + "/" + rFileName;
111 OUString aDestURL = rTempDirURL + "/" + rFileName;
113 osl::File::RC eError = osl::File::copy(aSourceURL, aDestURL);
114 if (eError != osl::File::E_None)
116 SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName);
117 throw std::exception();
121 OUString getPathFromURL(const OUString& rURL)
123 OUString aPath;
124 osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
126 return normalizePath(aPath);
129 void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
131 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
132 CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
135 #ifdef UNX
136 typedef char CharT;
137 #define tstrncpy std::strncpy
138 #elif defined(_WIN32)
139 typedef wchar_t CharT;
140 #define tstrncpy std::wcsncpy
141 #else
142 #error "Need an implementation"
143 #endif
145 void createStr(const OUString& rStr, CharT** pArgs, size_t i)
147 #ifdef UNX
148 OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
149 #elif defined(_WIN32)
150 OUString aStr = rStr;
151 #else
152 #error "Need an implementation"
153 #endif
154 CharT* pStr = new CharT[aStr.getLength() + 1];
155 tstrncpy(pStr, aStr.getStr(), aStr.getLength());
156 pStr[aStr.getLength()] = '\0';
157 pArgs[i] = pStr;
160 CharT** createCommandLine()
162 OUString aInstallDir = Updater::getInstallationPath();
164 size_t nCommandLineArgs = rtl_getAppCommandArgCount();
165 size_t nArgs = 8 + nCommandLineArgs;
166 CharT** pArgs = new CharT*[nArgs];
168 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
169 createStr(aUpdaterName, pArgs, 0);
172 // directory with the patch log
173 OUString aPatchDir = Updater::getPatchDirURL();
174 rtl::Bootstrap::expandMacros(aPatchDir);
175 OUString aTempDirPath = getPathFromURL(aPatchDir);
176 Updater::log("Patch Dir: " + aTempDirPath);
177 createStr(aTempDirPath, pArgs, 1);
180 // the actual update directory
181 Updater::log("Install Dir: " + aInstallDir);
182 createStr(aInstallDir, pArgs, 2);
185 // the temporary updated build
186 Updater::log("Working Dir: " + aInstallDir);
187 createStr(aInstallDir, pArgs, 3);
190 #ifdef UNX
191 OUString aPID("0");
192 #elif defined(_WIN32)
193 oslProcessInfo aInfo;
194 aInfo.Size = sizeof(oslProcessInfo);
195 osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aInfo);
196 OUString aPID = OUString::number(aInfo.Ident);
197 #else
198 #error "Need an implementation"
199 #endif
200 createStr(aPID, pArgs, 4);
203 OUString aExeDir = Updater::getExecutableDirURL();
204 OUString aSofficePath = getPathFromURL(aExeDir);
205 Updater::log("soffice Path: " + aSofficePath);
206 createStr(aSofficePath, pArgs, 5);
209 // the executable to start after the successful update
210 OUString aExeDir = Updater::getExecutableDirURL();
211 OUString aSofficePathURL = aExeDir + OUString::fromUtf8(pSofficeExeName);
212 OUString aSofficePath = getPathFromURL(aSofficePathURL);
213 createStr(aSofficePath, pArgs, 6);
216 // add the command line arguments from the soffice list
217 for (size_t i = 0; i < nCommandLineArgs; ++i)
219 OUString aCommandLineArg;
220 rtl_getAppCommandArg(i, &aCommandLineArg.pData);
221 createStr(aCommandLineArg, pArgs, 7 + i);
224 pArgs[nArgs - 1] = nullptr;
226 return pArgs;
229 struct update_file
231 OUString aURL;
232 OUString aHash;
233 size_t nSize;
236 struct language_file
238 update_file aUpdateFile;
239 OUString aLangCode;
242 struct update_info
244 OUString aFromBuildID;
245 OUString aSeeAlsoURL;
246 OUString aMessage;
248 update_file aUpdateFile;
249 std::vector<language_file> aLanguageFiles;
252 bool isUserWritable(const OUString& rFileURL)
254 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
255 osl::DirectoryItem aDirectoryItem;
257 osl::FileBase::RC eRes = osl::DirectoryItem::get(rFileURL, aDirectoryItem);
258 if (eRes != osl::FileBase::E_None)
260 Updater::log("Could not get the directory item for: " + rFileURL);
261 return false;
264 osl::FileBase::RC eResult = aDirectoryItem.getFileStatus(aStatus);
265 if (eResult != osl::FileBase::E_None)
267 Updater::log("Could not get the file status for: " + rFileURL);
268 return false;
271 bool bReadOnly = (aStatus.getAttributes() & static_cast<sal_uInt64>(osl_File_Attribute_ReadOnly)) != 0;
272 if (bReadOnly)
274 Updater::log("Update location as determined by: " + rFileURL + " is read-only.");
275 return false;
278 return true;
283 bool update()
285 utl::TempFile aTempDir(nullptr, true);
286 OUString aTempDirURL = aTempDir.GetURL();
287 CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL);
289 OUString aUpdaterPath = getPathFromURL(aTempDirURL + "/" + OUString::fromUtf8(pUpdaterName));
291 Updater::log("Calling the updater with parameters: ");
292 CharT** pArgs = createCommandLine();
294 bool bSuccess = true;
295 const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
296 if (!pUpdaterTestReplace)
298 #if UNX
299 OString aPath = OUStringToOString(aUpdaterPath, RTL_TEXTENCODING_UTF8);
300 if (execv(aPath.getStr(), pArgs))
302 printf("execv failed with error %d %s\n",errno,strerror(errno));
303 bSuccess = false;
305 #elif defined(_WIN32)
306 bSuccess = WinLaunchChild((wchar_t*)aUpdaterPath.getStr(), 8, pArgs);
307 #endif
309 else
311 SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath);
312 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
314 SAL_WARN("desktop.updater", pArgs[i]);
316 bSuccess = false;
319 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
321 delete[] pArgs[i];
323 delete[] pArgs;
325 return bSuccess;
328 namespace {
330 // Callback to get the response data from server.
331 size_t WriteCallback(void *ptr, size_t size,
332 size_t nmemb, void *userp)
334 if (!userp)
335 return 0;
337 std::string* response = static_cast<std::string *>(userp);
338 size_t real_size = size * nmemb;
339 response->append(static_cast<char *>(ptr), real_size);
340 return real_size;
345 class invalid_update_info : public std::exception
349 class invalid_hash : public std::exception
351 OString maMessage;
352 public:
354 invalid_hash(const OUString& rExpectedHash, const OUString& rReceivedHash)
356 OUString aMsg = "Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash;
357 maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
360 const char* what() const noexcept override
362 return maMessage.getStr();
366 class invalid_size : public std::exception
368 OString maMessage;
369 public:
371 invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
373 OUString aMsg = "Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize);
374 maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
377 const char* what() const noexcept override
379 return maMessage.getStr();
383 OUString toOUString(const std::string& rStr)
385 return OUString::fromUtf8(rStr.c_str());
388 update_file parse_update_file(orcus::json::node& rNode)
390 if (rNode.type() != orcus::json::node_t::object)
392 SAL_WARN("desktop.updater", "invalid update or language file entry");
393 throw invalid_update_info();
396 if (rNode.child_count() < 4)
398 SAL_WARN("desktop.updater", "invalid update or language file entry");
399 throw invalid_update_info();
402 orcus::json::node aURLNode = rNode.child("url");
403 orcus::json::node aHashNode = rNode.child("hash");
404 orcus::json::node aHashTypeNode = rNode.child("hash_function");
405 orcus::json::node aSizeNode = rNode.child("size");
407 if (aHashTypeNode.string_value() != "sha512")
409 SAL_WARN("desktop.updater", "invalid hash type");
410 throw invalid_update_info();
413 update_file aUpdateFile;
414 aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
416 if (aUpdateFile.aURL.isEmpty())
417 throw invalid_update_info();
419 aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
420 aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
421 return aUpdateFile;
424 update_info parse_response(const std::string& rResponse)
426 orcus::json::document_tree aJsonDoc;
427 orcus::json_config aConfig;
428 aJsonDoc.load(rResponse, aConfig);
430 auto aDocumentRoot = aJsonDoc.get_document_root();
431 if (aDocumentRoot.type() != orcus::json::node_t::object)
433 SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
434 throw invalid_update_info();
437 auto aRootKeys = aDocumentRoot.keys();
438 if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
440 throw invalid_update_info();
442 else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
444 update_info aUpdateInfo;
445 auto aMsgNode = aDocumentRoot.child("response");
446 aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
447 return aUpdateInfo;
450 orcus::json::node aFromNode = aDocumentRoot.child("from");
451 if (aFromNode.type() != orcus::json::node_t::string)
453 throw invalid_update_info();
456 orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
457 if (aSeeAlsoNode.type() != orcus::json::node_t::string)
459 throw invalid_update_info();
462 orcus::json::node aUpdateNode = aDocumentRoot.child("update");
463 if (aUpdateNode.type() != orcus::json::node_t::object)
465 throw invalid_update_info();
468 orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
469 if (aUpdateNode.type() != orcus::json::node_t::object)
471 throw invalid_update_info();
474 update_info aUpdateInfo;
475 aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
476 aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
478 aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
480 std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
481 for (auto itr = aLanguages.begin(), itrEnd = aLanguages.end(); itr != itrEnd; ++itr)
483 language_file aLanguageFile;
484 auto aLangEntry = aLanguageNode.child(*itr);
485 aLanguageFile.aLangCode = toOUString(itr->str());
486 aLanguageFile.aUpdateFile = parse_update_file(aLangEntry);
487 aUpdateInfo.aLanguageFiles.push_back(aLanguageFile);
490 return aUpdateInfo;
493 struct WriteDataFile
495 comphelper::Hash maHash;
496 SvStream* mpStream;
498 WriteDataFile(SvStream* pStream):
499 maHash(comphelper::HashType::SHA512),
500 mpStream(pStream)
504 OUString getHash()
506 auto final_hash = maHash.finalize();
507 std::stringstream aStrm;
508 for (auto& i: final_hash)
510 aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
513 return toOUString(aStrm.str());
517 // Callback to get the response data from server to a file.
518 size_t WriteCallbackFile(void *ptr, size_t size,
519 size_t nmemb, void *userp)
521 if (!userp)
522 return 0;
524 WriteDataFile* response = static_cast<WriteDataFile *>(userp);
525 size_t real_size = size * nmemb;
526 response->mpStream->WriteBytes(ptr, real_size);
527 response->maHash.update(static_cast<const unsigned char*>(ptr), real_size);
528 return real_size;
531 std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
533 Updater::log("Download: " + rURL);
534 CURL* curl = curl_easy_init();
536 if (!curl)
537 return std::string();
539 curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
540 curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
541 bool bUseProxy = false;
542 if (bUseProxy)
545 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
546 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
550 char buf[] = "Expect:";
551 curl_slist* headerlist = nullptr;
552 headerlist = curl_slist_append(headerlist, buf);
553 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
554 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // follow redirects
555 // only allow redirect to http:// and https://
556 curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
558 std::string response_body;
559 utl::TempFile aTempFile;
560 WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
561 if (!bFile)
563 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
564 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
565 static_cast<void *>(&response_body));
567 aTempFile.EnableKillingFile(true);
569 else
571 OUString aTempFileURL = aTempFile.GetURL();
572 OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
573 response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
575 aTempFile.EnableKillingFile(false);
577 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
578 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
579 static_cast<void *>(&aFile));
582 // Fail if 400+ is returned from the web server.
583 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
585 CURLcode cc = curl_easy_perform(curl);
586 long http_code = 0;
587 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
588 if (http_code != 200)
590 SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
591 throw error_updater("download did not succeed");
594 if (cc != CURLE_OK)
596 SAL_WARN("desktop.updater", "curl error: " << cc);
597 throw error_updater("curl error");
600 if (bFile)
601 rHash = aFile.getHash();
603 return response_body;
606 void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
608 switch (eError)
610 case osl::FileBase::E_None:
611 break;
612 default:
613 SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
614 throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
618 void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
620 Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
621 OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
622 OUString aHash;
623 std::string temp_file = download_content(aURL, true, aHash);
624 if (temp_file.empty())
625 throw error_updater("empty temp file string");
627 OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
628 Updater::log("TempFile: " + aTempFile);
629 osl::File aDownloadedFile(aTempFile);
630 osl::FileBase::RC eError = aDownloadedFile.open(1);
631 handle_file_error(eError, "Could not open the download file: " + aTempFile);
633 sal_uInt64 nSize = 0;
634 eError = aDownloadedFile.getSize(nSize);
635 handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
636 if (nSize != nFileSize)
638 SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
639 throw invalid_size(nFileSize, nSize);
642 if (aHash != rHash)
644 SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
645 throw invalid_hash(rHash, aHash);
648 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
649 rtl::Bootstrap::expandMacros(aPatchDirURL);
650 osl::Directory::create(aPatchDirURL);
652 OUString aDestFile = aPatchDirURL + aFileName;
653 Updater::log("Destination File: " + aDestFile);
654 aDownloadedFile.close();
655 eError = osl::File::move(aTempFile, aDestFile);
656 handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
661 void update_checker()
663 OUString aBrandBaseDir("${BRAND_BASE_DIR}");
664 rtl::Bootstrap::expandMacros(aBrandBaseDir);
665 bool bUserWritable = isUserWritable(aBrandBaseDir);
666 if (!bUserWritable)
668 Updater::log("Can't update as the update location is not user writable");
669 return;
672 OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
673 static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
674 if (pDownloadCheckBaseURLEnv)
676 aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
679 OUString aProductName = utl::ConfigManager::getProductName();
680 OUString aBuildID = Updater::getBuildID();
682 static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
683 if (pBuildIdEnv)
685 aBuildID = OUString::createFromAscii(pBuildIdEnv);
688 OUString aBuildTarget = "${_OS}_${_ARCH}";
689 rtl::Bootstrap::expandMacros(aBuildTarget);
690 OUString aChannel = Updater::getUpdateChannel();
691 static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
692 if (pUpdateChannelEnv)
694 aChannel = OUString::createFromAscii(pUpdateChannelEnv);
697 OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
698 "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
699 OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
700 Updater::log("Update check: " + aURL);
704 OUString aHash;
705 std::string response_body = download_content(aURL, false, aHash);
706 if (!response_body.empty())
709 update_info aUpdateInfo = parse_response(response_body);
710 if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
712 // No update currently available
713 // add entry to updating.log with the message
714 SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
715 Updater::log("Server response: " + aUpdateInfo.aMessage);
717 else
719 css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
720 std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
721 download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
722 for (auto& lang_update : aUpdateInfo.aLanguageFiles)
724 // only download the language packs for installed languages
725 if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
727 OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
728 download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
731 OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
732 std::shared_ptr< comphelper::ConfigurationChanges > batch(
733 comphelper::ConfigurationChanges::create());
734 officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
735 batch->commit();
739 catch (const invalid_update_info&)
741 SAL_WARN("desktop.updater", "invalid update information");
742 Updater::log(OString("warning: invalid update info"));
744 catch (const error_updater& e)
746 SAL_WARN("desktop.updater", "error during the update check: " << e.what());
747 Updater::log(OString("warning: error by the updater") + e.what());
749 catch (const invalid_size& e)
751 SAL_WARN("desktop.updater", e.what());
752 Updater::log(OString("warning: invalid size"));
754 catch (const invalid_hash& e)
756 SAL_WARN("desktop.updater", e.what());
757 Updater::log(OString("warning: invalid hash"));
759 catch (...)
761 SAL_WARN("desktop.updater", "unknown error during the update check");
762 Updater::log(OString("warning: unknown exception"));
766 OUString Updater::getUpdateInfoLog()
768 OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
769 rtl::Bootstrap::expandMacros(aUpdateInfoURL);
771 return aUpdateInfoURL;
774 OUString Updater::getPatchDirURL()
776 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
777 rtl::Bootstrap::expandMacros(aPatchDirURL);
779 return aPatchDirURL;
782 OUString Updater::getUpdateFileURL()
784 return getPatchDirURL() + "update.mar";
787 OUString Updater::getInstallationPath()
789 OUString aInstallDir( "$BRAND_BASE_DIR/");
790 rtl::Bootstrap::expandMacros(aInstallDir);
792 return getPathFromURL(aInstallDir);
795 OUString Updater::getExecutableDirURL()
797 OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
798 rtl::Bootstrap::expandMacros(aExeDir);
800 return aExeDir;
803 void Updater::log(const OUString& rMessage)
805 SAL_INFO("desktop.updater", rMessage);
806 OUString aUpdateLog = getUpdateInfoLog();
807 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
808 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
809 aLog.WriteLine(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
812 void Updater::log(const OString& rMessage)
814 SAL_INFO("desktop.updater", rMessage);
815 OUString aUpdateLog = getUpdateInfoLog();
816 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
817 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
818 aLog.WriteLine(rMessage);
821 void Updater::log(const char* pMessage)
823 SAL_INFO("desktop.updater", pMessage);
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.WriteCharPtr(pMessage);
830 OUString Updater::getBuildID()
832 OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
833 rtl::Bootstrap::expandMacros(aBuildID);
835 return aBuildID;
838 OUString Updater::getUpdateChannel()
840 OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
841 rtl::Bootstrap::expandMacros(aUpdateChannel);
843 return aUpdateChannel;
846 void Updater::removeUpdateFiles()
848 Updater::log("Removing: " + getUpdateFileURL());
849 osl::File::remove(getUpdateFileURL());
851 OUString aPatchDirURL = getPatchDirURL();
852 osl::Directory aDir(aPatchDirURL);
853 aDir.open();
855 osl::FileBase::RC eRC;
858 osl::DirectoryItem aItem;
859 eRC = aDir.getNextItem(aItem);
860 if (eRC == osl::FileBase::E_None)
862 osl::FileStatus aStatus(osl_FileStatus_Mask_All);
863 if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
864 continue;
866 if (!aStatus.isRegular())
867 continue;
869 OUString aURL = aStatus.getFileURL();
870 if (!aURL.endsWith(".mar"))
871 continue;
873 Updater::log("Removing. " + aURL);
874 osl::File::remove(aURL);
877 while (eRC == osl::FileBase::E_None);
880 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */