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