Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / app / updater.cxx
blobadcc130751e73a1c09abaaab9ba76a3565ee0808
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>
40 #include <comphelper/hash.hxx>
42 #include <com/sun/star/container/XNameAccess.hpp>
44 #include <officecfg/Setup.hxx>
46 #include <set>
48 namespace {
50 class error_updater : public std::exception
52 OString maStr;
53 public:
55 error_updater(const OString& rStr):
56 maStr(rStr)
60 virtual const char* what() const throw() override
62 return maStr.getStr();
66 #ifdef UNX
67 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
68 #else
69 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
70 #endif
72 #ifdef UNX
73 const char* const pUpdaterName = "updater";
74 const char* const pSofficeExeName = "soffice";
75 #elif defined(WNT)
76 const char* pUpdaterName = "updater.exe";
77 const char* pSofficeExeName = "soffice.exe";
78 #else
79 #error "Need implementation"
80 #endif
82 OUString normalizePath(const OUString& rPath)
84 OUString aPath = rPath.replaceAll("//", "/");
86 // remove final /
87 if (aPath.endsWith("/"))
89 aPath = aPath.copy(0, aPath.getLength() - 1);
92 while (aPath.indexOf("/..") != -1)
94 sal_Int32 nIndex = aPath.indexOf("/..");
95 sal_Int32 i = nIndex - 1;
96 for (; i > 0; --i)
98 if (aPath[i] == '/')
99 break;
102 OUString aTempPath = aPath;
103 aPath = aTempPath.copy(0, i) + aPath.copy(nIndex + 3);
106 return aPath.replaceAll("\\", "/");
109 void CopyFileToDir(const OUString& rTempDirURL, const OUString & rFileName, const OUString& rOldDir)
111 OUString aSourceURL = rOldDir + "/" + rFileName;
112 OUString aDestURL = rTempDirURL + "/" + rFileName;
114 osl::File::RC eError = osl::File::copy(aSourceURL, aDestURL);
115 if (eError != osl::File::E_None)
117 SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName);
118 throw std::exception();
122 OUString getPathFromURL(const OUString& rURL)
124 OUString aPath;
125 osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
127 return normalizePath(aPath);
130 void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
132 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
133 CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
136 #ifdef UNX
137 typedef char CharT;
138 #define tstrncpy std::strncpy
139 #elif defined(_WIN32)
140 typedef wchar_t CharT;
141 #define tstrncpy std::wcsncpy
142 #else
143 #error "Need an implementation"
144 #endif
146 void createStr(const OUString& rStr, CharT** pArgs, size_t i)
148 #ifdef UNX
149 OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
150 #elif defined(_WIN32)
151 OUString aStr = rStr;
152 #else
153 #error "Need an implementation"
154 #endif
155 CharT* pStr = new CharT[aStr.getLength() + 1];
156 tstrncpy(pStr, (CharT*)aStr.getStr(), aStr.getLength());
157 pStr[aStr.getLength()] = '\0';
158 pArgs[i] = pStr;
161 CharT** createCommandLine()
163 OUString aInstallDir = Updater::getInstallationPath();
165 size_t nCommandLineArgs = rtl_getAppCommandArgCount();
166 size_t nArgs = 8 + nCommandLineArgs;
167 CharT** pArgs = new CharT*[nArgs];
169 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
170 createStr(aUpdaterName, pArgs, 0);
173 // directory with the patch log
174 OUString aPatchDir = Updater::getPatchDirURL();
175 rtl::Bootstrap::expandMacros(aPatchDir);
176 OUString aTempDirPath = getPathFromURL(aPatchDir);
177 Updater::log("Patch Dir: " + aTempDirPath);
178 createStr(aTempDirPath, pArgs, 1);
181 // the actual update directory
182 Updater::log("Install Dir: " + aInstallDir);
183 createStr(aInstallDir, pArgs, 2);
186 // the temporary updated build
187 Updater::log("Working Dir: " + aInstallDir);
188 createStr(aInstallDir, pArgs, 3);
191 #ifdef UNX
192 OUString aPID("0");
193 #elif defined(_WIN32)
194 oslProcessInfo aInfo;
195 aInfo.Size = sizeof(oslProcessInfo);
196 osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aInfo);
197 OUString aPID = OUString::number(aInfo.Ident);
198 #else
199 #error "Need an implementation"
200 #endif
201 createStr(aPID, pArgs, 4);
204 OUString aExeDir = Updater::getExecutableDirURL();
205 OUString aSofficePath = getPathFromURL(aExeDir);
206 Updater::log("soffice Path: " + aSofficePath);
207 createStr(aSofficePath, pArgs, 5);
210 // the executable to start after the successful update
211 OUString aExeDir = Updater::getExecutableDirURL();
212 OUString aSofficePathURL = aExeDir + OUString::fromUtf8(pSofficeExeName);
213 OUString aSofficePath = getPathFromURL(aSofficePathURL);
214 createStr(aSofficePath, pArgs, 6);
217 // add the command line arguments from the soffice list
218 for (size_t i = 0; i < nCommandLineArgs; ++i)
220 OUString aCommandLineArg;
221 rtl_getAppCommandArg(i, &aCommandLineArg.pData);
222 createStr(aCommandLineArg, pArgs, 7 + i);
225 pArgs[nArgs - 1] = nullptr;
227 return pArgs;
230 struct update_file
232 OUString aURL;
233 OUString aHash;
234 size_t nSize;
237 struct language_file
239 update_file aUpdateFile;
240 OUString aLangCode;
243 struct update_info
245 OUString aFromBuildID;
246 OUString aSeeAlsoURL;
247 OUString aMessage;
249 update_file aUpdateFile;
250 std::vector<language_file> aLanguageFiles;
253 bool isUserWritable(const OUString& rFileURL)
255 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
256 osl::DirectoryItem aDirectoryItem;
258 osl::FileBase::RC eRes = osl::DirectoryItem::get(rFileURL, aDirectoryItem);
259 if (eRes != osl::FileBase::E_None)
261 Updater::log("Could not get the directory item for: " + rFileURL);
262 return false;
265 osl::FileBase::RC eResult = aDirectoryItem.getFileStatus(aStatus);
266 if (eResult != osl::FileBase::E_None)
268 Updater::log("Could not get the file status for: " + rFileURL);
269 return false;
272 bool bReadOnly = (aStatus.getAttributes() & static_cast<sal_uInt64>(osl_File_Attribute_ReadOnly)) != 0;
273 if (bReadOnly)
275 Updater::log("Update location as determined by: " + rFileURL + " is read-only.");
276 return false;
279 return true;
284 bool update()
286 utl::TempFile aTempDir(nullptr, true);
287 OUString aTempDirURL = aTempDir.GetURL();
288 CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL);
290 OUString aUpdaterPath = getPathFromURL(aTempDirURL + "/" + OUString::fromUtf8(pUpdaterName));
292 Updater::log("Calling the updater with parameters: ");
293 CharT** pArgs = createCommandLine();
295 bool bSuccess = true;
296 const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
297 if (!pUpdaterTestReplace)
299 #if UNX
300 OString aPath = OUStringToOString(aUpdaterPath, RTL_TEXTENCODING_UTF8);
301 if (execv(aPath.getStr(), pArgs))
303 printf("execv failed with error %d %s\n",errno,strerror(errno));
304 bSuccess = false;
306 #elif defined(_WIN32)
307 bSuccess = WinLaunchChild((wchar_t*)aUpdaterPath.getStr(), 8, pArgs);
308 #endif
310 else
312 SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath);
313 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
315 SAL_WARN("desktop.updater", pArgs[i]);
317 bSuccess = false;
320 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
322 delete[] pArgs[i];
324 delete[] pArgs;
326 return bSuccess;
329 namespace {
331 // Callback to get the response data from server.
332 size_t WriteCallback(void *ptr, size_t size,
333 size_t nmemb, void *userp)
335 if (!userp)
336 return 0;
338 std::string* response = static_cast<std::string *>(userp);
339 size_t real_size = size * nmemb;
340 response->append(static_cast<char *>(ptr), real_size);
341 return real_size;
346 class invalid_update_info : public std::exception
350 class invalid_hash : public std::exception
352 OString maMessage;
353 public:
355 invalid_hash(const OUString& rExpectedHash, const OUString& rReceivedHash)
357 OUString aMsg = "Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash;
358 maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
361 const char* what() const noexcept override
363 return maMessage.getStr();
367 class invalid_size : public std::exception
369 OString maMessage;
370 public:
372 invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
374 OUString aMsg = "Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize);
375 maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
378 const char* what() const noexcept override
380 return maMessage.getStr();
384 OUString toOUString(const std::string& rStr)
386 return OUString::fromUtf8(rStr.c_str());
389 update_file parse_update_file(orcus::json::node& rNode)
391 if (rNode.type() != orcus::json::node_t::object)
393 SAL_WARN("desktop.updater", "invalid update or language file entry");
394 throw invalid_update_info();
397 if (rNode.child_count() < 4)
399 SAL_WARN("desktop.updater", "invalid update or language file entry");
400 throw invalid_update_info();
403 orcus::json::node aURLNode = rNode.child("url");
404 orcus::json::node aHashNode = rNode.child("hash");
405 orcus::json::node aHashTypeNode = rNode.child("hash_function");
406 orcus::json::node aSizeNode = rNode.child("size");
408 if (aHashTypeNode.string_value() != "sha512")
410 SAL_WARN("desktop.updater", "invalid hash type");
411 throw invalid_update_info();
414 update_file aUpdateFile;
415 aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
417 if (aUpdateFile.aURL.isEmpty())
418 throw invalid_update_info();
420 aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
421 aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
422 return aUpdateFile;
425 update_info parse_response(const std::string& rResponse)
427 orcus::json::document_tree aJsonDoc;
428 orcus::json_config aConfig;
429 aJsonDoc.load(rResponse, aConfig);
431 auto aDocumentRoot = aJsonDoc.get_document_root();
432 if (aDocumentRoot.type() != orcus::json::node_t::object)
434 SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
435 throw invalid_update_info();
438 auto aRootKeys = aDocumentRoot.keys();
439 if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
441 throw invalid_update_info();
443 else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
445 update_info aUpdateInfo;
446 auto aMsgNode = aDocumentRoot.child("response");
447 aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
448 return aUpdateInfo;
451 orcus::json::node aFromNode = aDocumentRoot.child("from");
452 if (aFromNode.type() != orcus::json::node_t::string)
454 throw invalid_update_info();
457 orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
458 if (aSeeAlsoNode.type() != orcus::json::node_t::string)
460 throw invalid_update_info();
463 orcus::json::node aUpdateNode = aDocumentRoot.child("update");
464 if (aUpdateNode.type() != orcus::json::node_t::object)
466 throw invalid_update_info();
469 orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
470 if (aUpdateNode.type() != orcus::json::node_t::object)
472 throw invalid_update_info();
475 update_info aUpdateInfo;
476 aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
477 aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
479 aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
481 std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
482 for (auto const& language : aLanguages)
484 language_file aLanguageFile;
485 auto aLangEntry = aLanguageNode.child(language);
486 aLanguageFile.aLangCode = toOUString(language.str());
487 aLanguageFile.aUpdateFile = parse_update_file(aLangEntry);
488 aUpdateInfo.aLanguageFiles.push_back(aLanguageFile);
491 return aUpdateInfo;
494 struct WriteDataFile
496 comphelper::Hash maHash;
497 SvStream* mpStream;
499 WriteDataFile(SvStream* pStream):
500 maHash(comphelper::HashType::SHA512),
501 mpStream(pStream)
505 OUString getHash()
507 auto final_hash = maHash.finalize();
508 std::stringstream aStrm;
509 for (auto& i: final_hash)
511 aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
514 return toOUString(aStrm.str());
518 // Callback to get the response data from server to a file.
519 size_t WriteCallbackFile(void *ptr, size_t size,
520 size_t nmemb, void *userp)
522 if (!userp)
523 return 0;
525 WriteDataFile* response = static_cast<WriteDataFile *>(userp);
526 size_t real_size = size * nmemb;
527 response->mpStream->WriteBytes(ptr, real_size);
528 response->maHash.update(static_cast<const unsigned char*>(ptr), real_size);
529 return real_size;
532 std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
534 Updater::log("Download: " + rURL);
535 CURL* curl = curl_easy_init();
537 if (!curl)
538 return std::string();
540 curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
541 curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
542 bool bUseProxy = false;
543 if (bUseProxy)
546 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
547 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
551 char buf[] = "Expect:";
552 curl_slist* headerlist = nullptr;
553 headerlist = curl_slist_append(headerlist, buf);
554 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
555 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // follow redirects
556 // only allow redirect to http:// and https://
557 curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
559 std::string response_body;
560 utl::TempFile aTempFile;
561 WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
562 if (!bFile)
564 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
565 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
566 static_cast<void *>(&response_body));
568 aTempFile.EnableKillingFile(true);
570 else
572 OUString aTempFileURL = aTempFile.GetURL();
573 OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
574 response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
576 aTempFile.EnableKillingFile(false);
578 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
579 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
580 static_cast<void *>(&aFile));
583 // Fail if 400+ is returned from the web server.
584 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
586 CURLcode cc = curl_easy_perform(curl);
587 long http_code = 0;
588 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
589 if (http_code != 200)
591 SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
592 throw error_updater("download did not succeed");
595 if (cc != CURLE_OK)
597 SAL_WARN("desktop.updater", "curl error: " << cc);
598 throw error_updater("curl error");
601 if (bFile)
602 rHash = aFile.getHash();
604 return response_body;
607 void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
609 switch (eError)
611 case osl::FileBase::E_None:
612 break;
613 default:
614 SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
615 throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
619 void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
621 Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
622 OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
623 OUString aHash;
624 std::string temp_file = download_content(aURL, true, aHash);
625 if (temp_file.empty())
626 throw error_updater("empty temp file string");
628 OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
629 Updater::log("TempFile: " + aTempFile);
630 osl::File aDownloadedFile(aTempFile);
631 osl::FileBase::RC eError = aDownloadedFile.open(1);
632 handle_file_error(eError, "Could not open the download file: " + aTempFile);
634 sal_uInt64 nSize = 0;
635 eError = aDownloadedFile.getSize(nSize);
636 handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
637 if (nSize != nFileSize)
639 SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
640 throw invalid_size(nFileSize, nSize);
643 if (aHash != rHash)
645 SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
646 throw invalid_hash(rHash, aHash);
649 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
650 rtl::Bootstrap::expandMacros(aPatchDirURL);
651 osl::Directory::create(aPatchDirURL);
653 OUString aDestFile = aPatchDirURL + aFileName;
654 Updater::log("Destination File: " + aDestFile);
655 aDownloadedFile.close();
656 eError = osl::File::move(aTempFile, aDestFile);
657 handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
662 void update_checker()
664 OUString aBrandBaseDir("${BRAND_BASE_DIR}");
665 rtl::Bootstrap::expandMacros(aBrandBaseDir);
666 bool bUserWritable = isUserWritable(aBrandBaseDir);
667 if (!bUserWritable)
669 Updater::log("Can't update as the update location is not user writable");
670 return;
673 OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
674 static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
675 if (pDownloadCheckBaseURLEnv)
677 aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
680 OUString aProductName = utl::ConfigManager::getProductName();
681 OUString aBuildID = Updater::getBuildID();
683 static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
684 if (pBuildIdEnv)
686 aBuildID = OUString::createFromAscii(pBuildIdEnv);
689 OUString aBuildTarget = "${_OS}_${_ARCH}";
690 rtl::Bootstrap::expandMacros(aBuildTarget);
691 OUString aChannel = Updater::getUpdateChannel();
692 static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
693 if (pUpdateChannelEnv)
695 aChannel = OUString::createFromAscii(pUpdateChannelEnv);
698 OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
699 "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
700 OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
701 Updater::log("Update check: " + aURL);
705 OUString aHash;
706 std::string response_body = download_content(aURL, false, aHash);
707 if (!response_body.empty())
710 update_info aUpdateInfo = parse_response(response_body);
711 if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
713 // No update currently available
714 // add entry to updating.log with the message
715 SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
716 Updater::log("Server response: " + aUpdateInfo.aMessage);
718 else
720 css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
721 std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
722 download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
723 for (auto& lang_update : aUpdateInfo.aLanguageFiles)
725 // only download the language packs for installed languages
726 if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
728 OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
729 download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
732 OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
733 std::shared_ptr< comphelper::ConfigurationChanges > batch(
734 comphelper::ConfigurationChanges::create());
735 officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
736 batch->commit();
740 catch (const invalid_update_info&)
742 SAL_WARN("desktop.updater", "invalid update information");
743 Updater::log(OString("warning: invalid update info"));
745 catch (const error_updater& e)
747 SAL_WARN("desktop.updater", "error during the update check: " << e.what());
748 Updater::log(OString("warning: error by the updater") + e.what());
750 catch (const invalid_size& e)
752 SAL_WARN("desktop.updater", e.what());
753 Updater::log(OString("warning: invalid size"));
755 catch (const invalid_hash& e)
757 SAL_WARN("desktop.updater", e.what());
758 Updater::log(OString("warning: invalid hash"));
760 catch (...)
762 SAL_WARN("desktop.updater", "unknown error during the update check");
763 Updater::log(OString("warning: unknown exception"));
767 OUString Updater::getUpdateInfoLog()
769 OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
770 rtl::Bootstrap::expandMacros(aUpdateInfoURL);
772 return aUpdateInfoURL;
775 OUString Updater::getPatchDirURL()
777 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
778 rtl::Bootstrap::expandMacros(aPatchDirURL);
780 return aPatchDirURL;
783 OUString Updater::getUpdateFileURL()
785 return getPatchDirURL() + "update.mar";
788 OUString Updater::getInstallationPath()
790 OUString aInstallDir( "$BRAND_BASE_DIR/");
791 rtl::Bootstrap::expandMacros(aInstallDir);
793 return getPathFromURL(aInstallDir);
796 OUString Updater::getExecutableDirURL()
798 OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
799 rtl::Bootstrap::expandMacros(aExeDir);
801 return aExeDir;
804 void Updater::log(const OUString& rMessage)
806 SAL_INFO("desktop.updater", rMessage);
807 OUString aUpdateLog = getUpdateInfoLog();
808 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
809 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
810 aLog.WriteLine(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
813 void Updater::log(const OString& rMessage)
815 SAL_INFO("desktop.updater", rMessage);
816 OUString aUpdateLog = getUpdateInfoLog();
817 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
818 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
819 aLog.WriteLine(rMessage);
822 void Updater::log(const char* pMessage)
824 SAL_INFO("desktop.updater", pMessage);
825 OUString aUpdateLog = getUpdateInfoLog();
826 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
827 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
828 aLog.WriteCharPtr(pMessage);
831 OUString Updater::getBuildID()
833 OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
834 rtl::Bootstrap::expandMacros(aBuildID);
836 return aBuildID;
839 OUString Updater::getUpdateChannel()
841 OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
842 rtl::Bootstrap::expandMacros(aUpdateChannel);
844 return aUpdateChannel;
847 void Updater::removeUpdateFiles()
849 Updater::log("Removing: " + getUpdateFileURL());
850 osl::File::remove(getUpdateFileURL());
852 OUString aPatchDirURL = getPatchDirURL();
853 osl::Directory aDir(aPatchDirURL);
854 aDir.open();
856 osl::FileBase::RC eRC;
859 osl::DirectoryItem aItem;
860 eRC = aDir.getNextItem(aItem);
861 if (eRC == osl::FileBase::E_None)
863 osl::FileStatus aStatus(osl_FileStatus_Mask_All);
864 if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
865 continue;
867 if (!aStatus.isRegular())
868 continue;
870 OUString aURL = aStatus.getFileURL();
871 if (!aURL.endsWith(".mar"))
872 continue;
874 Updater::log("Removing. " + aURL);
875 osl::File::remove(aURL);
878 while (eRC == osl::FileBase::E_None);
881 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */