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(WNT)
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  {
357  OUString aMsg = "Invalid hash found.\nExpected: " + rExpectedHash + ";\nReceived: " + rReceivedHash;
358  maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
359  }
360 
361  const char* what() const noexcept override
362  {
363  return maMessage.getStr();
364  }
365 };
366 
367 class invalid_size : public std::exception
368 {
369  OString maMessage;
370 public:
371 
372  invalid_size(const size_t nExpectedSize, const size_t nReceivedSize)
373  {
374  OUString aMsg = "Invalid file size found.\nExpected: " + OUString::number(nExpectedSize) + ";\nReceived: " + OUString::number(nReceivedSize);
375  maMessage = OUStringToOString(aMsg, RTL_TEXTENCODING_UTF8);
376  }
377 
378  const char* what() const noexcept override
379  {
380  return maMessage.getStr();
381  }
382 };
383 
384 OUString toOUString(const std::string& rStr)
385 {
386  return OUString::fromUtf8(rStr.c_str());
387 }
388 
389 update_file parse_update_file(orcus::json::node& rNode)
390 {
391  if (rNode.type() != orcus::json::node_t::object)
392  {
393  SAL_WARN("desktop.updater", "invalid update or language file entry");
394  throw invalid_update_info();
395  }
396 
397  if (rNode.child_count() < 4)
398  {
399  SAL_WARN("desktop.updater", "invalid update or language file entry");
400  throw invalid_update_info();
401  }
402 
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");
407 
408  if (aHashTypeNode.string_value() != "sha512")
409  {
410  SAL_WARN("desktop.updater", "invalid hash type");
411  throw invalid_update_info();
412  }
413 
414  update_file aUpdateFile;
415  aUpdateFile.aURL = toOUString(aURLNode.string_value().str());
416 
417  if (aUpdateFile.aURL.isEmpty())
418  throw invalid_update_info();
419 
420  aUpdateFile.aHash = toOUString(aHashNode.string_value().str());
421  aUpdateFile.nSize = static_cast<sal_uInt32>(aSizeNode.numeric_value());
422  return aUpdateFile;
423 }
424 
425 update_info parse_response(const std::string& rResponse)
426 {
427  orcus::json::document_tree aJsonDoc;
428  orcus::json_config aConfig;
429  aJsonDoc.load(rResponse, aConfig);
430 
431  auto aDocumentRoot = aJsonDoc.get_document_root();
432  if (aDocumentRoot.type() != orcus::json::node_t::object)
433  {
434  SAL_WARN("desktop.updater", "invalid root entries: " << rResponse);
435  throw invalid_update_info();
436  }
437 
438  auto aRootKeys = aDocumentRoot.keys();
439  if (std::find(aRootKeys.begin(), aRootKeys.end(), "error") != aRootKeys.end())
440  {
441  throw invalid_update_info();
442  }
443  else if (std::find(aRootKeys.begin(), aRootKeys.end(), "response") != aRootKeys.end())
444  {
445  update_info aUpdateInfo;
446  auto aMsgNode = aDocumentRoot.child("response");
447  aUpdateInfo.aMessage = toOUString(aMsgNode.string_value().str());
448  return aUpdateInfo;
449  }
450 
451  orcus::json::node aFromNode = aDocumentRoot.child("from");
452  if (aFromNode.type() != orcus::json::node_t::string)
453  {
454  throw invalid_update_info();
455  }
456 
457  orcus::json::node aSeeAlsoNode = aDocumentRoot.child("see also");
458  if (aSeeAlsoNode.type() != orcus::json::node_t::string)
459  {
460  throw invalid_update_info();
461  }
462 
463  orcus::json::node aUpdateNode = aDocumentRoot.child("update");
464  if (aUpdateNode.type() != orcus::json::node_t::object)
465  {
466  throw invalid_update_info();
467  }
468 
469  orcus::json::node aLanguageNode = aDocumentRoot.child("languages");
470  if (aUpdateNode.type() != orcus::json::node_t::object)
471  {
472  throw invalid_update_info();
473  }
474 
475  update_info aUpdateInfo;
476  aUpdateInfo.aFromBuildID = toOUString(aFromNode.string_value().str());
477  aUpdateInfo.aSeeAlsoURL = toOUString(aSeeAlsoNode.string_value().str());
478 
479  aUpdateInfo.aUpdateFile = parse_update_file(aUpdateNode);
480 
481  std::vector<orcus::pstring> aLanguages = aLanguageNode.keys();
482  for (auto const& language : aLanguages)
483  {
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);
489  }
490 
491  return aUpdateInfo;
492 }
493 
494 struct WriteDataFile
495 {
496  comphelper::Hash maHash;
497  SvStream* mpStream;
498 
499  WriteDataFile(SvStream* pStream):
500  maHash(comphelper::HashType::SHA512),
501  mpStream(pStream)
502  {
503  }
504 
505  OUString getHash()
506  {
507  auto final_hash = maHash.finalize();
508  std::stringstream aStrm;
509  for (auto& i: final_hash)
510  {
511  aStrm << std::setw(2) << std::setfill('0') << std::hex << (int)i;
512  }
513 
514  return toOUString(aStrm.str());
515  }
516 };
517 
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)
521 {
522  if (!userp)
523  return 0;
524 
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;
530 }
531 
532 std::string download_content(const OString& rURL, bool bFile, OUString& rHash)
533 {
534  Updater::log("Download: " + rURL);
535  CURL* curl = curl_easy_init();
536 
537  if (!curl)
538  return std::string();
539 
540  curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
541  curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
542  bool bUseProxy = false;
543  if (bUseProxy)
544  {
545  /*
546  curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
547  curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
548  */
549  }
550 
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);
558 
559  std::string response_body;
560  utl::TempFile aTempFile;
561  WriteDataFile aFile(aTempFile.GetStream(StreamMode::WRITE));
562  if (!bFile)
563  {
564  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
565  curl_easy_setopt(curl, CURLOPT_WRITEDATA,
566  static_cast<void *>(&response_body));
567 
568  aTempFile.EnableKillingFile(true);
569  }
570  else
571  {
572  OUString aTempFileURL = aTempFile.GetURL();
573  OString aTempFileURLOString = OUStringToOString(aTempFileURL, RTL_TEXTENCODING_UTF8);
574  response_body.append(aTempFileURLOString.getStr(), aTempFileURLOString.getLength());
575 
576  aTempFile.EnableKillingFile(false);
577 
578  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
579  curl_easy_setopt(curl, CURLOPT_WRITEDATA,
580  static_cast<void *>(&aFile));
581  }
582 
583  // Fail if 400+ is returned from the web server.
584  curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
585 
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)
590  {
591  SAL_WARN("desktop.updater", "download did not succeed. Error code: " << http_code);
592  throw error_updater("download did not succeed");
593  }
594 
595  if (cc != CURLE_OK)
596  {
597  SAL_WARN("desktop.updater", "curl error: " << cc);
598  throw error_updater("curl error");
599  }
600 
601  if (bFile)
602  rHash = aFile.getHash();
603 
604  return response_body;
605 }
606 
607 void handle_file_error(osl::FileBase::RC eError, const OUString& rMsg)
608 {
609  switch (eError)
610  {
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));
616  }
617 }
618 
619 void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash, const OUString& aFileName)
620 {
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");
627 
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);
633 
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)
638  {
639  SAL_WARN("desktop.updater", "File sizes don't match. File might be corrupted.");
640  throw invalid_size(nFileSize, nSize);
641  }
642 
643  if (aHash != rHash)
644  {
645  SAL_WARN("desktop.updater", "File hash don't match. File might be corrupted.");
646  throw invalid_hash(rHash, aHash);
647  }
648 
649  OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
650  rtl::Bootstrap::expandMacros(aPatchDirURL);
651  osl::Directory::create(aPatchDirURL);
652 
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);
658 }
659 
660 }
661 
663 {
664  OUString aBrandBaseDir("${BRAND_BASE_DIR}");
665  rtl::Bootstrap::expandMacros(aBrandBaseDir);
666  bool bUserWritable = isUserWritable(aBrandBaseDir);
667  if (!bUserWritable)
668  {
669  Updater::log("Can't update as the update location is not user writable");
670  return;
671  }
672 
673  OUString aDownloadCheckBaseURL = officecfg::Office::Update::Update::URL::get();
674  static const char* pDownloadCheckBaseURLEnv = std::getenv("LIBO_UPDATER_URL");
675  if (pDownloadCheckBaseURLEnv)
676  {
677  aDownloadCheckBaseURL = OUString::createFromAscii(pDownloadCheckBaseURLEnv);
678  }
679 
680  OUString aProductName = utl::ConfigManager::getProductName();
681  OUString aBuildID = Updater::getBuildID();
682 
683  static const char* pBuildIdEnv = std::getenv("LIBO_UPDATER_BUILD");
684  if (pBuildIdEnv)
685  {
686  aBuildID = OUString::createFromAscii(pBuildIdEnv);
687  }
688 
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)
694  {
695  aChannel = OUString::createFromAscii(pUpdateChannelEnv);
696  }
697 
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);
702 
703  try
704  {
705  OUString aHash;
706  std::string response_body = download_content(aURL, false, aHash);
707  if (!response_body.empty())
708  {
709 
710  update_info aUpdateInfo = parse_response(response_body);
711  if (aUpdateInfo.aUpdateFile.aURL.isEmpty())
712  {
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);
717  }
718  else
719  {
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)
724  {
725  // only download the language packs for installed languages
726  if (aInstalledLanguageSet.find(lang_update.aLangCode) != aInstalledLanguageSet.end())
727  {
728  OUString aFileName = "update_" + lang_update.aLangCode + ".mar";
729  download_file(lang_update.aUpdateFile.aURL, lang_update.aUpdateFile.nSize, lang_update.aUpdateFile.aHash, aFileName);
730  }
731  }
732  OUString aSeeAlsoURL = aUpdateInfo.aSeeAlsoURL;
733  std::shared_ptr< comphelper::ConfigurationChanges > batch(
735  officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
736  batch->commit();
737  }
738  }
739  }
740  catch (const invalid_update_info&)
741  {
742  SAL_WARN("desktop.updater", "invalid update information");
743  Updater::log(OString("warning: invalid update info"));
744  }
745  catch (const error_updater& e)
746  {
747  SAL_WARN("desktop.updater", "error during the update check: " << e.what());
748  Updater::log(OString("warning: error by the updater") + e.what());
749  }
750  catch (const invalid_size& e)
751  {
752  SAL_WARN("desktop.updater", e.what());
753  Updater::log(OString("warning: invalid size"));
754  }
755  catch (const invalid_hash& e)
756  {
757  SAL_WARN("desktop.updater", e.what());
758  Updater::log(OString("warning: invalid hash"));
759  }
760  catch (...)
761  {
762  SAL_WARN("desktop.updater", "unknown error during the update check");
763  Updater::log(OString("warning: unknown exception"));
764  }
765 }
766 
768 {
769  OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
770  rtl::Bootstrap::expandMacros(aUpdateInfoURL);
771 
772  return aUpdateInfoURL;
773 }
774 
776 {
777  OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
778  rtl::Bootstrap::expandMacros(aPatchDirURL);
779 
780  return aPatchDirURL;
781 }
782 
784 {
785  return getPatchDirURL() + "update.mar";
786 }
787 
789 {
790  OUString aInstallDir( "$BRAND_BASE_DIR/");
791  rtl::Bootstrap::expandMacros(aInstallDir);
792 
793  return getPathFromURL(aInstallDir);
794 }
795 
797 {
798  OUString aExeDir( "$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/" );
799  rtl::Bootstrap::expandMacros(aExeDir);
800 
801  return aExeDir;
802 }
803 
804 void Updater::log(const OUString& rMessage)
805 {
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));
811 }
812 
813 void Updater::log(const OString& 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(rMessage);
820 }
821 
822 void Updater::log(const char* pMessage)
823 {
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);
829 }
830 
832 {
833  OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
834  rtl::Bootstrap::expandMacros(aBuildID);
835 
836  return aBuildID;
837 }
838 
840 {
841  OUString aUpdateChannel("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateChannel}");
842  rtl::Bootstrap::expandMacros(aUpdateChannel);
843 
844  return aUpdateChannel;
845 }
846 
848 {
849  Updater::log("Removing: " + getUpdateFileURL());
850  osl::File::remove(getUpdateFileURL());
851 
852  OUString aPatchDirURL = getPatchDirURL();
853  osl::Directory aDir(aPatchDirURL);
854  aDir.open();
855 
856  osl::FileBase::RC eRC;
857  do
858  {
859  osl::DirectoryItem aItem;
860  eRC = aDir.getNextItem(aItem);
861  if (eRC == osl::FileBase::E_None)
862  {
863  osl::FileStatus aStatus(osl_FileStatus_Mask_All);
864  if (aItem.getFileStatus(aStatus) != osl::FileBase::E_None)
865  continue;
866 
867  if (!aStatus.isRegular())
868  continue;
869 
870  OUString aURL = aStatus.getFileURL();
871  if (!aURL.endsWith(".mar"))
872  continue;
873 
874  Updater::log("Removing. " + aURL);
875  osl::File::remove(aURL);
876  }
877  }
878  while (eRC == osl::FileBase::E_None);
879 }
880 
881 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static OUString getUpdateInfoLog()
Definition: updater.cxx:767
static size_t WriteCallback(void const *ptr, size_t size, size_t nmemb, void *userp)
Definition: minidump.cxx:45
bool update()
Definition: updater.cxx:284
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
sal_uInt64 Seek(sal_uInt64 nPos)
static const char kUserAgent[]
Definition: minidump.cxx:20
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:783
static void log(const OUString &rMessage)
Definition: updater.cxx:804
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:847
static OUString getPatchDirURL()
Definition: updater.cxx:775
static OUString getInstallationPath()
Definition: updater.cxx:788
#define SAL_CONFIGFILE(name)
static OUString getProductName()
bool WriteLine(const OString &rStr)
HashType
OUString const & GetURL() const
int i
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:662
#define SAL_INFO(area, stream)
static OUString getBuildID()
Definition: updater.cxx:831
sal_uInt64 Tell() const
#define SAL_WARN(area, stream)
static OUString getUpdateChannel()
Definition: updater.cxx:839
#define tstrncpy
Definition: updater.cxx:138
SvStream * GetStream(StreamMode eMode)
std::vector< unsigned char > finalize()
static OUString getExecutableDirURL()
Definition: updater.cxx:796
aStr
void EnableKillingFile(bool bEnable=true)