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>
30 #include <unotools/configmgr.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 
50 namespace {
51 
52 class error_updater : public std::exception
53 {
54  OString maStr;
55 public:
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
69 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (Linux)";
70 #else
71 static const char kUserAgent[] = "LibreOffice UpdateChecker/1.0 (unknown platform)";
72 #endif
73 
74 #ifdef UNX
75 const char* const pUpdaterName = "updater";
76 const char* const pSofficeExeName = "soffice";
77 #elif defined(_WIN32)
78 const char* pUpdaterName = "updater.exe";
79 const char* pSofficeExeName = "soffice.exe";
80 #else
81 #error "Need implementation"
82 #endif
83 
84 OUString 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 
111 void 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 
124 OUString getPathFromURL(const OUString& rURL)
125 {
126  OUString aPath;
127  osl::FileBase::getSystemPathFromFileURL(rURL, aPath);
128 
129  return normalizePath(aPath);
130 }
131 
132 void CopyUpdaterToTempDir(const OUString& rInstallDirURL, const OUString& rTempDirURL)
133 {
134  OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
135  CopyFileToDir(rTempDirURL, aUpdaterName, rInstallDirURL);
136 }
137 
138 #ifdef UNX
139 typedef char CharT;
140 #define tstrncpy std::strncpy
141 #elif defined(_WIN32)
142 typedef wchar_t CharT;
143 #define tstrncpy std::wcsncpy
144 #else
145 #error "Need an implementation"
146 #endif
147 
148 void 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 
163 CharT** 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 
232 struct update_file
233 {
234  OUString aURL;
235  OUString aHash;
236  size_t nSize;
237 };
238 
239 struct language_file
240 {
241  update_file aUpdateFile;
242  OUString aLangCode;
243 };
244 
245 struct 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 
255 bool 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 
286 bool update()
287 {
288  utl::TempFile 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 
331 namespace {
332 
333 // Callback to get the response data from server.
334 size_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 
348 class invalid_update_info : public std::exception
349 {
350 };
351 
352 class invalid_hash : public std::exception
353 {
354  OString maMessage;
355 public:
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 
372 class invalid_size : public std::exception
373 {
374  OString maMessage;
375 public:
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 
392 OUString toOUString(const std::string& rStr)
393 {
394  return OUString::fromUtf8(rStr.c_str());
395 }
396 
397 update_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 
433 update_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 
502 struct 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.
527 size_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 
540 std::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 http:// and https://
566  curl_easy_setopt(curl.get(), CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
567 
568  std::string response_body;
569  utl::TempFile aTempFile;
570  WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
571  if (!bFile)
572  {
573  curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
574  curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
575  static_cast<void *>(&response_body));
576 
577  aTempFile.EnableKillingFile(true);
578  }
579  else
580  {
581  OUString aTempFileURL = aTempFile.GetURL();
582  OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
583  response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
584 
585  aTempFile.EnableKillingFile(false);
586 
587  curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallbackFile);
588  curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA,
589  static_cast<void *>(&aFile));
590  }
591 
592  // Fail if 400+ is returned from the web server.
593  curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1);
594 
595  CURLcode cc = curl_easy_perform(curl.get());
596  long http_code = 0;
597  curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
598  if (http_code != 200)
599  {
600  SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
601  throw error_updater("download did not succeed");
602  }
603 
604  if (cc != CURLE_OK)
605  {
606  SAL_WARN("desktop.updater", "curl error: " << cc);
607  throw error_updater("curl error");
608  }
609 
610  if (bFile)
611  rHash = aFile.getHash();
612 
613  return response_body;
614 }
615 
616 void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
617 {
618  switch (eError)
619  {
620  case osl::FileBase::E_None:
621  break;
622  default:
623  SAL_WARN("desktop.updater", "file error code: " << eError << ", " << rMsg);
624  throw error_updater(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8));
625  }
626 }
627 
628 void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
629 {
630  Updater::log("Download File: " + rURL + "; FileName: " + aFileName);
631  OString aURL = OUStringToOString(rURL, RTL_TEXTENCODING_UTF8);
632  OUString aHash;
633  std::string temp_file = download_content(aURL, true, aHash);
634  if (temp_file.empty())
635  throw error_updater("empty temp file string");
636 
637  OUString aTempFile = OUString::fromUtf8(temp_file.c_str());
638  Updater::log("TempFile: " + aTempFile);
639  osl::File aDownloadedFile(aTempFile);
640  osl::FileBase::RC eError = aDownloadedFile.open(1);
641  handle_file_error(eError, "Could not open the download file: " + aTempFile);
642 
643  sal_uInt64 nSize = 0;
644  eError = aDownloadedFile.getSize(nSize);
645  handle_file_error(eError, "Could not get the file size of the downloaded file: " + aTempFile);
646  if (nSize != nFileSize)
647  {
648  SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
649  throw invalid_size(nFileSize, nSize);
650  }
651 
652  if (aHash != rHash)
653  {
654  SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
655  throw invalid_hash(rHash, aHash);
656  }
657 
658  OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
659  rtl::Bootstrap::expandMacros(aPatchDirURL);
660  osl::Directory::create(aPatchDirURL);
661 
662  OUString aDestFile = aPatchDirURL + aFileName;
663  Updater::log("Destination File: " + aDestFile);
664  aDownloadedFile.close();
665  eError = osl::File::move(aTempFile, aDestFile);
666  handle_file_error(eError, "Could not move the file from the Temp directory to the user config: TempFile: " + aTempFile + "; DestFile: " + aDestFile);
667 }
668 
669 }
670 
672 {
673  OUString aBrandBaseDir("${BRAND_BASE_DIR}");
674  rtl::Bootstrap::expandMacros(aBrandBaseDir);
675  bool bUserWritable = isUserWritable(aBrandBaseDir);
676  if (!bUserWritable)
677  {
678  Updater::log("Can't update as the update location is not user writable");
679  return;
680  }
681 
682  OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
683  static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
684  if (pDownloadCheckBaseURLEnv)
685  {
686  aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
687  }
688 
689  OUString aProductName = utl::ConfigManager::getProductName();
690  OUString aBuildID = Updater::getBuildID();
691 
692  static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
693  if (pBuildIdEnv)
694  {
695  aBuildID = OUString::createFromAscii(pBuildIdEnv);
696  }
697 
698  OUString aBuildTarget = "${_OS}_${_ARCH}";
699  rtl::Bootstrap::expandMacros(aBuildTarget);
700  OUString aChannel = Updater::getUpdateChannel();
701  static const char* pUpdateChannelEnv = std::getenv("LIBO_UPDATER_CHANNEL");
702  if (pUpdateChannelEnv)
703  {
704  aChannel = OUString::createFromAscii(pUpdateChannelEnv);
705  }
706 
707  OUString aDownloadCheckURL = aDownloadCheckBaseURL + "update/check/1/" + aProductName +
708  "/" + aBuildID + "/" + aBuildTarget + "/" + aChannel;
709  OString aURL = OUStringToOString(aDownloadCheckURL, RTL_TEXTENCODING_UTF8);
710  Updater::log("Update check: " + aURL);
711 
712  try
713  {
714  OUString aHash;
715  std::string response_body = download_content(aURL, false, aHash);
716  if (!response_body.empty())
717  {
718 
719  update_info aUpdateInfo = parse_response(response_body);
720  if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
721  {
722  // No update currently available
723  // add entry to updating.log with the message
724  SAL_WARN("desktop.updater", "Message received from the updater: " << aUpdateInfo.aMessage);
725  Updater::log("Server response: " + aUpdateInfo.aMessage);
726  }
727  else
728  {
729  css::uno::Sequence<OUString> aInstalledLanguages(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
730  std::set<OUString> aInstalledLanguageSet(std::begin(aInstalledLanguages), std::end(aInstalledLanguages));
731  download_file(aUpdateInfo.aUpdateFile.aURL, aUpdateInfo.aUpdateFile.nSize, aUpdateInfo.aUpdateFile.aHash, "update.mar");
732  for (auto& lang_update : aUpdateInfo.aLanguageFiles)
733  {
734  // only download the language packs for installed languages
735  if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
736  {
737  OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
738  download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
739  }
740  }
741  OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
742  std::shared_ptr< comphelper::ConfigurationChanges > batch(
744  officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
745  batch->commit();
746  }
747  }
748  }
749  catch (const invalid_update_info&)
750  {
751  SAL_WARN("desktop.updater", "invalid update information");
752  Updater::log(OString("warning: invalid update info"));
753  }
754  catch (const error_updater& e)
755  {
756  SAL_WARN("desktop.updater", "error during the update check: " << e.what());
757  Updater::log(OString("warning: error by the updater") + e.what());
758  }
759  catch (const invalid_size& e)
760  {
761  SAL_WARN("desktop.updater", e.what());
762  Updater::log(OString("warning: invalid size"));
763  }
764  catch (const invalid_hash& e)
765  {
766  SAL_WARN("desktop.updater", e.what());
767  Updater::log(OString("warning: invalid hash"));
768  }
769  catch (...)
770  {
771  SAL_WARN("desktop.updater", "unknown error during the update check");
772  Updater::log(OString("warning: unknown exception"));
773  }
774 }
775 
777 {
778  OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
779  rtl::Bootstrap::expandMacros(aUpdateInfoURL);
780 
781  return aUpdateInfoURL;
782 }
783 
785 {
786  OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
787  rtl::Bootstrap::expandMacros(aPatchDirURL);
788 
789  return aPatchDirURL;
790 }
791 
793 {
794  return getPatchDirURL() + "update.mar";
795 }
796 
798 {
799  OUString aInstallDir( "$BRAND_BASE_DIR/");
800  rtl::Bootstrap::expandMacros(aInstallDir);
801 
802  return getPathFromURL(aInstallDir);
803 }
804 
806 {
807  OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
808  rtl::Bootstrap::expandMacros(aExeDir);
809 
810  return aExeDir;
811 }
812 
813 void Updater::log(const OUString& rMessage)
814 {
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(OUStringToOString(rMessage, RTL_TEXTENCODING_UTF8));
820 }
821 
822 void Updater::log(const OString& rMessage)
823 {
824  SAL_INFO("desktop.updater", rMessage);
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.WriteLine(rMessage);
829 }
830 
831 void Updater::log(const char* pMessage)
832 {
833  SAL_INFO("desktop.updater", pMessage);
834  OUString aUpdateLog = getUpdateInfoLog();
835  SvFileStream aLog(aUpdateLog, StreamMode::STD_READWRITE);
836  aLog.Seek(aLog.Tell() + aLog.remainingSize()); // make sure we are at the end
837  aLog.WriteCharPtr(pMessage);
838 }
839 
841 {
842  OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
843  rtl::Bootstrap::expandMacros(aBuildID);
844 
845  return aBuildID;
846 }
847 
849 {
850  OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
851  rtl::Bootstrap::expandMacros(aUpdateChannel);
852 
853  return aUpdateChannel;
854 }
855 
857 {
858  Updater::log("Removing: " + getUpdateFileURL());
859  osl::File::remove(getUpdateFileURL());
860 
861  OUString aPatchDirURL = getPatchDirURL();
862  osl::Directory aDir(aPatchDirURL);
863  aDir.open();
864 
865  osl::FileBase::RC eRC;
866  do
867  {
868  osl::DirectoryItem aItem;
869  eRC = aDir.getNextItem(aItem);
870  if (eRC == osl::FileBase::E_None)
871  {
872  osl::FileStatus aStatus(osl_FileStatus_Mask_All);
873  if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
874  continue;
875 
876  if (!aStatus.isRegular())
877  continue;
878 
879  OUString aURL = aStatus.getFileURL();
880  if (!aURL.endsWith(".mar"))
881  continue;
882 
883  Updater::log("Removing. " + aURL);
884  osl::File::remove(aURL);
885  }
886  }
887  while (eRC == osl::FileBase::E_None);
888 }
889 
890 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
URL aURL
sal_Int32 nIndex
static OUString getUpdateInfoLog()
Definition: updater.cxx:776
static size_t WriteCallback(void const *ptr, size_t size, size_t nmemb, void *userp)
Definition: minidump.cxx:49
bool update()
Definition: updater.cxx:286
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
sal_uInt64 Seek(sal_uInt64 nPos)
bool WriteLine(std::string_view rStr)
bool bReadOnly
const char kUserAgent[]
Definition: minidump.cxx:24
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)
SvStream & WriteCharPtr(const char *pBuf)
static OUString getUpdateFileURL()
Definition: updater.cxx:792
static void log(const OUString &rMessage)
Definition: updater.cxx:813
static std::shared_ptr< ConfigurationChanges > create(css::uno::Reference< css::uno::XComponentContext > const &context=comphelper::getProcessComponentContext())
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
sal_uInt64 remainingSize()
static void removeUpdateFiles()
Definition: updater.cxx:856
static OUString getPatchDirURL()
Definition: updater.cxx:784
static OUString getInstallationPath()
Definition: updater.cxx:797
#define SAL_CONFIGFILE(name)
int i
static OUString getProductName()
HashType
OUString const & GetURL() const
BOOL WinLaunchChild(const wchar_t *exePath, int argc, wchar_t **argv, HANDLE userToken, HANDLE *hProcess)
void update_checker()
Definition: updater.cxx:671
#define SAL_INFO(area, stream)
static OUString getBuildID()
Definition: updater.cxx:840
sal_uInt64 Tell() const
#define SAL_WARN(area, stream)
static OUString getUpdateChannel()
Definition: updater.cxx:848
#define tstrncpy
Definition: updater.cxx:140
SvStream * GetStream(StreamMode eMode)
std::vector< unsigned char > finalize()
static OUString getExecutableDirURL()
Definition: updater.cxx:805
aStr
void EnableKillingFile(bool bEnable=true)
OUString toOUString(const QString &s)