LibreOffice Module desktop (master) 1
updater.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include "updater.hxx"
11
12#if UNX
13#include <unistd.h>
14#include <errno.h>
15
16#endif
17
18#ifdef _WIN32
20#endif
21
22#include <fstream>
23#include <config_folders.h>
24#include <rtl/bootstrap.hxx>
25
26#include <officecfg/Office/Update.hxx>
27
28#include <rtl/ustring.hxx>
29#include <unotools/tempfile.hxx>
31#include <osl/file.hxx>
32#include <rtl/process.h>
33#include <sal/log.hxx>
34
35#include <curl/curl.h>
36
37#include <orcus/json_document_tree.hpp>
38#include <orcus/config.hpp>
39#include <orcus/pstring.hpp>
40#include <comphelper/hash.hxx>
41
42#include <com/sun/star/container/XNameAccess.hpp>
43
44#include <officecfg/Setup.hxx>
45
46#include <functional>
47#include <memory>
48#include <set>
49
50namespace {
51
52class error_updater : public std::exception
53{
54 OString maStr;
55public:
56
57 error_updater(const OString& rStr):
58 maStr(rStr)
59 {
60 }
61
62 virtual const char* what() const throw() override
63 {
64 return maStr.getStr();
65 }
66};
67
68#ifdef UNX
69static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
70#else
71static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
72#endif
73
74#ifdef UNX
75const char* const pUpdaterName = "updater";
76const char* const pSofficeExeName = "soffice";
77#elif defined(_WIN32)
78const char* pUpdaterName = "updater.exe";
79const char* pSofficeExeName = "soffice.exe";
80#else
81#error "Need implementation"
82#endif
83
84OUString normalizePath(const OUString& rPath)
85{
86 OUString aPath = rPath.replaceAll("//", "/");
87
88 // remove final /
89 if (aPath.endsWith("/"))
90 {
91 aPath = aPath.copy(0, aPath.getLength() - 1);
92 }
93
94 while (aPath.indexOf("/..") != -1)
95 {
96 sal_Int32 nIndex = aPath.indexOf("/..");
97 sal_Int32 i = nIndex - 1;
98 for (; i > 0; --i)
99 {
100 if (aPath[i] == '/')
101 break;
102 }
103
104 OUString aTempPath = aPath;
105 aPath = aTempPath.copy(0, i) + aPath.copy(nIndex + 3);
106 }
107
108 return aPath.replaceAll("\\", "/");
109}
110
111void CopyFileToDir(const OUString& rTempDirURL, const OUString & rFileName, const OUString& rOldDir)
112{
113 OUString aSourceURL = rOldDir + "/" + rFileName;
114 OUString aDestURL = rTempDirURL + "/" + rFileName;
115
116 osl::File::RC eError = osl::File::copy(aSourceURL, aDestURL);
117 if (eError != osl::File::E_None)
118 {
119 SAL_WARN("desktop.updater", "could not copy the file to a temp directory: " << rFileName);
120 throw std::exception();
121 }
122}
123
124OUString getPathFromURL(const OUString& rURL)
125{
126 OUString aPath;
127 osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
128
129 return normalizePath(aPath);
130}
131
132void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
133{
134 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
135 CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
136}
137
138#ifdef UNX
139typedef char CharT;
140#define tstrncpy std::strncpy
141#elif defined(_WIN32)
142typedef wchar_t CharT;
143#define tstrncpy std::wcsncpy
144#else
145#error "Need an implementation"
146#endif
147
148void createStr(const OUString& rStr, CharT** pArgs, size_t i)
149{
150#ifdef UNX
151 OString aStr = OUStringToOString(rStr, RTL_TEXTENCODING_UTF8);
152#elif defined(_WIN32)
153 OUString aStr = rStr;
154#else
155#error "Need an implementation"
156#endif
157 CharT* pStr = new CharT[aStr.getLength() + 1];
158 tstrncpy(pStr, (CharT*)aStr.getStr(), aStr.getLength());
159 pStr[aStr.getLength()] = '\0';
160 pArgs[i] = pStr;
161}
162
163CharT** createCommandLine()
164{
165 OUString aInstallDir = Updater::getInstallationPath();
166
167 size_t nCommandLineArgs = rtl_getAppCommandArgCount();
168 size_t nArgs = 8 + nCommandLineArgs;
169 CharT** pArgs = new CharT*[nArgs];
170 {
171 OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
172 createStr(aUpdaterName, pArgs, 0);
173 }
174 {
175 // directory with the patch log
176 OUString aPatchDir = Updater::getPatchDirURL();
177 rtl::Bootstrap::expandMacros(aPatchDir);
178 OUString aTempDirPath = getPathFromURL(aPatchDir);
179 Updater::log("Patch Dir: " + aTempDirPath);
180 createStr(aTempDirPath, pArgs, 1);
181 }
182 {
183 // the actual update directory
184 Updater::log("Install Dir: " + aInstallDir);
185 createStr(aInstallDir, pArgs, 2);
186 }
187 {
188 // the temporary updated build
189 Updater::log("Working Dir: " + aInstallDir);
190 createStr(aInstallDir, pArgs, 3);
191 }
192 {
193#ifdef UNX
194 OUString aPID("0");
195#elif defined(_WIN32)
196 oslProcessInfo aInfo;
197 aInfo.Size = sizeof(oslProcessInfo);
198 osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aInfo);
199 OUString aPID = OUString::number(aInfo.Ident);
200#else
201#error "Need an implementation"
202#endif
203 createStr(aPID, pArgs, 4);
204 }
205 {
206 OUString aExeDir = Updater::getExecutableDirURL();
207 OUString aSofficePath = getPathFromURL(aExeDir);
208 Updater::log("soffice Path: " + aSofficePath);
209 createStr(aSofficePath, pArgs, 5);
210 }
211 {
212 // the executable to start after the successful update
213 OUString aExeDir = Updater::getExecutableDirURL();
214 OUString aSofficePathURL = aExeDir + OUString::fromUtf8(pSofficeExeName);
215 OUString aSofficePath = getPathFromURL(aSofficePathURL);
216 createStr(aSofficePath, pArgs, 6);
217 }
218
219 // add the command line arguments from the soffice list
220 for (size_t i = 0; i < nCommandLineArgs; ++i)
221 {
222 OUString aCommandLineArg;
223 rtl_getAppCommandArg(i, &aCommandLineArg.pData);
224 createStr(aCommandLineArg, pArgs, 7 + i);
225 }
226
227 pArgs[nArgs - 1] = nullptr;
228
229 return pArgs;
230}
231
232struct update_file
233{
234 OUString aURL;
235 OUString aHash;
236 size_t nSize;
237};
238
239struct language_file
240{
241 update_file aUpdateFile;
242 OUString aLangCode;
243};
244
245struct update_info
246{
247 OUString aFromBuildID;
248 OUString aSeeAlsoURL;
249 OUString aMessage;
250
251 update_file aUpdateFile;
252 std::vector<language_file> aLanguageFiles;
253};
254
255bool isUserWritable(const OUString& rFileURL)
256{
257 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
258 osl::DirectoryItem aDirectoryItem;
259
260 osl::FileBase::RC eRes = osl::DirectoryItem::get(rFileURL, aDirectoryItem);
261 if (eRes != osl::FileBase::E_None)
262 {
263 Updater::log("Could not get the directory item for: " + rFileURL);
264 return false;
265 }
266
267 osl::FileBase::RC eResult = aDirectoryItem.getFileStatus(aStatus);
268 if (eResult != osl::FileBase::E_None)
269 {
270 Updater::log("Could not get the file status for: " + rFileURL);
271 return false;
272 }
273
274 bool bReadOnly = (aStatus.getAttributes() & static_cast<sal_uInt64>(osl_File_Attribute_ReadOnly)) != 0;
275 if (bReadOnly)
276 {
277 Updater::log("Update location as determined by: " + rFileURL + " is read-only.");
278 return false;
279 }
280
281 return true;
282}
283
284}
285
286bool update()
287{
288 utl::TempFileNamed aTempDir(nullptr, true);
289 OUString aTempDirURL = aTempDir.GetURL();
290 CopyUpdaterToTempDir(Updater::getExecutableDirURL(), aTempDirURL);
291
292 OUString aUpdaterPath = getPathFromURL(aTempDirURL + "/" + OUString::fromUtf8(pUpdaterName));
293
294 Updater::log("Calling the updater with parameters: ");
295 CharT** pArgs = createCommandLine();
296
297 bool bSuccess = true;
298 const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
299 if (!pUpdaterTestReplace)
300 {
301#if UNX
302 OString aPath = OUStringToOString(aUpdaterPath, RTL_TEXTENCODING_UTF8);
303 if (execv(aPath.getStr(), pArgs))
304 {
305 printf("execv failed with error %d %s\n",errno,strerror(errno));
306 bSuccess = false;
307 }
308#elif defined(_WIN32)
309 bSuccess = WinLaunchChild((wchar_t*)aUpdaterPath.getStr(), 8, pArgs);
310#endif
311 }
312 else
313 {
314 SAL_WARN("desktop.updater", "Updater executable path: " << aUpdaterPath);
315 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
316 {
317 SAL_WARN("desktop.updater", pArgs[i]);
318 }
319 bSuccess = false;
320 }
321
322 for (size_t i = 0; i < 8 + rtl_getAppCommandArgCount(); ++i)
323 {
324 delete[] pArgs[i];
325 }
326 delete[] pArgs;
327
328 return bSuccess;
329}
330
331namespace {
332
333// Callback to get the response data from server.
334size_t WriteCallback(void *ptr, size_t size,
335 size_t nmemb, void *userp)
336{
337 if (!userp)
338 return 0;
339
340 std::string* response = static_cast<std::string *>(userp);
341 size_t real_size = size * nmemb;
342 response->append(static_cast<char *>(ptr), real_size);
343 return real_size;
344}
345
346
347
348class invalid_update_info : public std::exception
349{
350};
351
352class invalid_hash : public std::exception
353{
354 OString maMessage;
355public:
356
357 invalid_hash(const OUString& rExpectedHash, const OUString& rReceivedHash)
358 : maMessage(
360 OUString("Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash),
361 RTL_TEXTENCODING_UTF8)
362 )
363 {
364 }
365
366 const char* what() const noexcept override
367 {
368 return maMessage.getStr();
369 }
370};
371
372class invalid_size : public std::exception
373{
374 OString maMessage;
375public:
376
377 invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
378 : maMessage(
380 OUString("Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize)),
381 RTL_TEXTENCODING_UTF8)
382 )
383 {
384 }
385
386 const char* what() const noexcept override
387 {
388 return maMessage.getStr();
389 }
390};
391
392OUString toOUString(const std::string& rStr)
393{
394 return OUString::fromUtf8(rStr.c_str());
395}
396
397update_file parse_update_file(orcus::json::node& rNode)
398{
399 if (rNode.type() != orcus::json::node_t::object)
400 {
401 SAL_WARN("desktop.updater", "invalid update or language file entry");
402 throw invalid_update_info();
403 }
404
405 if (rNode.child_count() < 4)
406 {
407 SAL_WARN("desktop.updater", "invalid update or language file entry");
408 throw invalid_update_info();
409 }
410
411 orcus::json::node aURLNode = rNode.child("url");
412 orcus::json::node aHashNode = rNode.child("hash");
413 orcus::json::node aHashTypeNode = rNode.child("hash_function");
414 orcus::json::node aSizeNode = rNode.child("size");
415
416 if (aHashTypeNode.string_value() != "sha512")
417 {
418 SAL_WARN("desktop.updater", "invalid hash type");
419 throw invalid_update_info();
420 }
421
422 update_file aUpdateFile;
423 aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
424
425 if (aUpdateFile.aURL.isEmpty())
426 throw invalid_update_info();
427
428 aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
429 aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
430 return aUpdateFile;
431}
432
433update_info parse_response(const std::string& rResponse)
434{
435 orcus::json::document_tree aJsonDoc;
436 orcus::json_config aConfig;
437 aJsonDoc.load(rResponse, aConfig);
438
439 auto aDocumentRoot = aJsonDoc.get_document_root();
440 if (aDocumentRoot.type() != orcus::json::node_t::object)
441 {
442 SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
443 throw invalid_update_info();
444 }
445
446 auto aRootKeys = aDocumentRoot.keys();
447 if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
448 {
449 throw invalid_update_info();
450 }
451 else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
452 {
453 update_info aUpdateInfo;
454 auto aMsgNode = aDocumentRoot.child("response");
455 aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
456 return aUpdateInfo;
457 }
458
459 orcus::json::node aFromNode = aDocumentRoot.child("from");
460 if (aFromNode.type() != orcus::json::node_t::string)
461 {
462 throw invalid_update_info();
463 }
464
465 orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
466 if (aSeeAlsoNode.type() != orcus::json::node_t::string)
467 {
468 throw invalid_update_info();
469 }
470
471 orcus::json::node aUpdateNode = aDocumentRoot.child("update");
472 if (aUpdateNode.type() != orcus::json::node_t::object)
473 {
474 throw invalid_update_info();
475 }
476
477 orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
478 if (aUpdateNode.type() != orcus::json::node_t::object)
479 {
480 throw invalid_update_info();
481 }
482
483 update_info aUpdateInfo;
484 aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
485 aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
486
487 aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
488
489 std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
490 for (auto const& language : aLanguages)
491 {
492 language_file aLanguageFile;
493 auto aLangEntry = aLanguageNode.child(language);
494 aLanguageFile.aLangCode = toOUString(language.str());
495 aLanguageFile.aUpdateFile = parse_update_file(aLangEntry);
496 aUpdateInfo.aLanguageFiles.push_back(aLanguageFile);
497 }
498
499 return aUpdateInfo;
500}
501
502struct WriteDataFile
503{
504 comphelper::Hash maHash;
505 SvStream* mpStream;
506
507 WriteDataFile(SvStream* pStream):
508 maHash(comphelper::HashType::SHA512),
509 mpStream(pStream)
510 {
511 }
512
513 OUString getHash()
514 {
515 auto final_hash = maHash.finalize();
516 std::stringstream aStrm;
517 for (auto& i: final_hash)
518 {
519 aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
520 }
521
522 return toOUString(aStrm.str());
523 }
524};
525
526// Callback to get the response data from server to a file.
527size_t WriteCallbackFile(void *ptr, size_t size,
528 size_t nmemb, void *userp)
529{
530 if (!userp)
531 return 0;
532
533 WriteDataFile* response = static_cast<WriteDataFile *>(userp);
534 size_t real_size = size * nmemb;
535 response->mpStream->WriteBytes(ptr, real_size);
536 response->maHash.update(static_cast<const unsigned char*>(ptr), real_size);
537 return real_size;
538}
539
540std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
541{
542 Updater::log("Download: " + rURL);
543 std::unique_ptr<CURL, std::function<void(CURL *)>> curl(
544 curl_easy_init(), [](CURL * p) { curl_easy_cleanup(p); });
545
546 if (!curl)
547 return std::string();
548
549 curl_easy_setopt(curl.get(), CURLOPT_URL, rURL.getStr());
550 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, kUserAgent);
551 bool bUseProxy = false;
552 if (bUseProxy)
553 {
554 /*
555 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
556 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
557 */
558 }
559
560 char buf[] = "Expect:";
561 curl_slist* headerlist = nullptr;
562 headerlist = curl_slist_append(headerlist, buf);
563 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headerlist);
564 curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1); // follow redirects
565 // only allow redirect to https://
566#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85)
567 curl_easy_setopt(curl.get(), CURLOPT_REDIR_PROTOCOLS_STR, "https");
568#else
569 curl_easy_setopt(curl.get(), CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
570#endif
571
572 std::string response_body;
573 utl::TempFileNamed aTempFile;
574 WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
575 if (!bFile)
576 {
577 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
578 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
579 static_cast<void *>(&response_body));
580
581 aTempFile.EnableKillingFile(true);
582 }
583 else
584 {
585 OUString aTempFileURL = aTempFile.GetURL();
586 OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
587 response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
588
589 aTempFile.EnableKillingFile(false);
590
591 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallbackFile);
592 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
593 static_cast<void *>(&aFile));
594 }
595
596 // Fail if 400+ is returned from the web server.
597 curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1);
598
599 CURLcode cc = curl_easy_perform(curl.get());
600 long http_code = 0;
601 curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
602 if (http_code != 200)
603 {
604 SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
605 throw error_updater("download did not succeed");
606 }
607
608 if (cc != CURLE_OK)
609 {
610 SAL_WARN("desktop.updater", "curl error: " << cc);
611 throw error_updater("curl error");
612 }
613
614 if (bFile)
615 rHash = aFile.getHash();
616
617 return response_body;
618}
619
620void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
621{
622 switch (eError)
623 {
624 case osl::FileBase::E_None:
625 break;
626 default:
627 SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
628 throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
629 }
630}
631
632void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
633{
634 Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
635 OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
636 OUString aHash;
637 std::string temp_file = download_content(aURL, true, aHash);
638 if (temp_file.empty())
639 throw error_updater("empty temp file string");
640
641 OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
642 Updater::log("TempFile: " + aTempFile);
643 osl::File aDownloadedFile(aTempFile);
644 osl::FileBase::RC eError = aDownloadedFile.open(1);
645 handle_file_error(eError, "Could not open the download file: " + aTempFile);
646
647 sal_uInt64 nSize = 0;
648 eError = aDownloadedFile.getSize(nSize);
649 handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
650 if (nSize != nFileSize)
651 {
652 SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
653 throw invalid_size(nFileSize, nSize);
654 }
655
656 if (aHash != rHash)
657 {
658 SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
659 throw invalid_hash(rHash, aHash);
660 }
661
662 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
663 rtl::Bootstrap::expandMacros(aPatchDirURL);
664 osl::Directory::create(aPatchDirURL);
665
666 OUString aDestFile = aPatchDirURL + aFileName;
667 Updater::log("Destination File: " + aDestFile);
668 aDownloadedFile.close();
669 eError = osl::File::move(aTempFile, aDestFile);
670 handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
671}
672
673}
674
676{
677 OUString aBrandBaseDir("${BRAND_BASE_DIR}");
678 rtl::Bootstrap::expandMacros(aBrandBaseDir);
679 bool bUserWritable = isUserWritable(aBrandBaseDir);
680 if (!bUserWritable)
681 {
682 Updater::log("Can't update as the update location is not user writable");
683 return;
684 }
685
686 OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
687 static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
688 if (pDownloadCheckBaseURLEnv)
689 {
690 aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
691 }
692
693 OUString aProductName = utl::ConfigManager::getProductName();
694 OUString aBuildID = Updater::getBuildID();
695
696 static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
697 if (pBuildIdEnv)
698 {
699 aBuildID = OUString::createFromAscii(pBuildIdEnv);
700 }
701
702 OUString aBuildTarget = "${_OS}_${_ARCH}";
703 rtl::Bootstrap::expandMacros(aBuildTarget);
704 OUString aChannel = Updater::getUpdateChannel();
705 static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
706 if (pUpdateChannelEnv)
707 {
708 aChannel = OUString::createFromAscii(pUpdateChannelEnv);
709 }
710
711 OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
712 "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
713 OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
714 Updater::log("Update check: " + aURL);
715
716 try
717 {
718 OUString aHash;
719 std::string response_body = download_content(aURL, false, aHash);
720 if (!response_body.empty())
721 {
722
723 update_info aUpdateInfo = parse_response(response_body);
724 if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
725 {
726 // No update currently available
727 // add entry to updating.log with the message
728 SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
729 Updater::log("Server response: " + aUpdateInfo.aMessage);
730 }
731 else
732 {
733 css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
734 std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
735 download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
736 for (auto& lang_update : aUpdateInfo.aLanguageFiles)
737 {
738 // only download the language packs for installed languages
739 if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
740 {
741 OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
742 download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
743 }
744 }
745 OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
746 std::shared_ptr< comphelper::ConfigurationChanges > batch(
748 officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
749 batch->commit();
750 }
751 }
752 }
753 catch (const invalid_update_info&)
754 {
755 SAL_WARN("desktop.updater", "invalid update information");
756 Updater::log(OString("warning: invalid update info"));
757 }
758 catch (const error_updater& e)
759 {
760 SAL_WARN("desktop.updater", "error during the update check: " << e.what());
761 Updater::log(OString("warning: error by the updater") + e.what());
762 }
763 catch (const invalid_size& e)
764 {
765 SAL_WARN("desktop.updater", e.what());
766 Updater::log(OString("warning: invalid size"));
767 }
768 catch (const invalid_hash& e)
769 {
770 SAL_WARN("desktop.updater", e.what());
771 Updater::log(OString("warning: invalid hash"));
772 }
773 catch (...)
774 {
775 SAL_WARN("desktop.updater", "unknown error during the update check");
776 Updater::log(OString("warning: unknown exception"));
777 }
778}
779
781{
782 OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
783 rtl::Bootstrap::expandMacros(aUpdateInfoURL);
784
785 return aUpdateInfoURL;
786}
787
789{
790 OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
791 rtl::Bootstrap::expandMacros(aPatchDirURL);
792
793 return aPatchDirURL;
794}
795
797{
798 return getPatchDirURL() + "update.mar";
799}
800
802{
803 OUString aInstallDir( "$BRAND_BASE_DIR/");
804 rtl::Bootstrap::expandMacros(aInstallDir);
805
806 return getPathFromURL(aInstallDir);
807}
808
810{
811 OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
812 rtl::Bootstrap::expandMacros(aExeDir);
813
814 return aExeDir;
815}
816
817void Updater::log(const OUString& rMessage)
818{
819 SAL_INFO("desktop.updater", rMessage);
820 OUString aUpdateLog = getUpdateInfoLog();
821 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
822 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
823 aLog.WriteLine(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
824}
825
826void Updater::log(const OString& rMessage)
827{
828 SAL_INFO("desktop.updater", rMessage);
829 OUString aUpdateLog = getUpdateInfoLog();
830 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
831 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
832 aLog.WriteLine(rMessage);
833}
834
835void Updater::log(const char* pMessage)
836{
837 SAL_INFO("desktop.updater", pMessage);
838 OUString aUpdateLog = getUpdateInfoLog();
839 SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
840 aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
841 aLog.WriteOString(pMessage);
842}
843
845{
846 OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
847 rtl::Bootstrap::expandMacros(aBuildID);
848
849 return aBuildID;
850}
851
853{
854 OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
855 rtl::Bootstrap::expandMacros(aUpdateChannel);
856
857 return aUpdateChannel;
858}
859
861{
862 Updater::log("Removing: " + getUpdateFileURL());
863 osl::File::remove(getUpdateFileURL());
864
865 OUString aPatchDirURL = getPatchDirURL();
866 osl::Directory aDir(aPatchDirURL);
867 aDir.open();
868
869 osl::FileBase::RC eRC;
870 do
871 {
872 osl::DirectoryItem aItem;
873 eRC = aDir.getNextItem(aItem);
874 if (eRC == osl::FileBase::E_None)
875 {
876 osl::FileStatus aStatus(osl_FileStatus_Mask_All);
877 if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
878 continue;
879
880 if (!aStatus.isRegular())
881 continue;
882
883 OUString aURL = aStatus.getFileURL();
884 if (!aURL.endsWith(".mar"))
885 continue;
886
887 Updater::log("Removing. " + aURL);
888 osl::File::remove(aURL);
889 }
890 }
891 while (eRC == osl::FileBase::E_None);
892}
893
894/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt64 Tell() const
SvStream & WriteOString(std::string_view rStr)
bool WriteLine(std::string_view rStr)
sal_uInt64 Seek(sal_uInt64 nPos)
sal_uInt64 remainingSize()
static void removeUpdateFiles()
Definition: updater.cxx:860
static void log(const OUString &rMessage)
Definition: updater.cxx:817
static OUString getUpdateChannel()
Definition: updater.cxx:852
static OUString getExecutableDirURL()
Definition: updater.cxx:809
static OUString getPatchDirURL()
Definition: updater.cxx:788
static OUString getUpdateFileURL()
Definition: updater.cxx:796
static OUString getInstallationPath()
Definition: updater.cxx:801
static OUString getUpdateInfoLog()
Definition: updater.cxx:780
static OUString getBuildID()
Definition: updater.cxx:844
static std::shared_ptr< ConfigurationChanges > create()
std::vector< unsigned char > finalize()
static OUString getProductName()
void EnableKillingFile(bool bEnable=true)
SvStream * GetStream(StreamMode eMode)
OUString const & GetURL() const
#define SAL_CONFIGFILE(name)
URL aURL
bool bReadOnly
sal_Int32 nIndex
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
static size_t WriteCallback(void const *ptr, size_t size, size_t nmemb, void *userp)
Definition: minidump.cxx:50
const char kUserAgent[]
Definition: minidump.cxx:25
size
HashType
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
OUString toOUString(const QString &s)
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
void update_checker()
Definition: updater.cxx:675
#define tstrncpy
Definition: updater.cxx:140
bool update()
Definition: updater.cxx:286
BOOL WinLaunchChild(const wchar_t *exePath, int argc, wchar_t **argv, HANDLE userToken, HANDLE *hProcess)