The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #ifndef JUCER_AUTOUPDATER_H_INCLUDED
  18. #define JUCER_AUTOUPDATER_H_INCLUDED
  19. //==============================================================================
  20. class LatestVersionChecker : private Thread,
  21. private Timer
  22. {
  23. public:
  24. LatestVersionChecker() : Thread ("Updater"),
  25. hasAttemptedToReadWebsite (false)
  26. {
  27. startTimer (2000);
  28. }
  29. ~LatestVersionChecker()
  30. {
  31. stopThread (20000);
  32. }
  33. static URL getLatestVersionURL()
  34. {
  35. return URL ("http://www.juce.com/juce/updates/updatelist.php");
  36. }
  37. void checkForNewVersion()
  38. {
  39. hasAttemptedToReadWebsite = true;
  40. {
  41. const ScopedPointer<InputStream> in (getLatestVersionURL().createInputStream (false));
  42. if (in == nullptr || threadShouldExit())
  43. return; // can't connect: fail silently.
  44. jsonReply = JSON::parse (in->readEntireStreamAsString());
  45. }
  46. if (threadShouldExit())
  47. return;
  48. if (jsonReply.isArray() || jsonReply.isObject())
  49. startTimer (100);
  50. }
  51. void processResult (var reply)
  52. {
  53. if (reply.isArray())
  54. {
  55. askUserAboutNewVersion (VersionInfo (reply[0]));
  56. }
  57. else if (reply.isObject())
  58. {
  59. // In the far-distant future, this may be contacting a defunct
  60. // URL, so hopefully the website will contain a helpful message
  61. // for the user..
  62. String message = reply.getProperty ("message", var()).toString();
  63. if (message.isNotEmpty())
  64. {
  65. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  66. TRANS("JUCE Updater"),
  67. message);
  68. }
  69. }
  70. }
  71. struct VersionInfo
  72. {
  73. VersionInfo (var v)
  74. {
  75. version = v.getProperty ("version", var()).toString().trim();
  76. url = v.getProperty (
  77. #if JUCE_MAC
  78. "url_osx",
  79. #elif JUCE_WINDOWS
  80. "url_win",
  81. #elif JUCE_LINUX
  82. "url_linux",
  83. #endif
  84. var()).toString();
  85. }
  86. bool isDifferentVersionToCurrent() const
  87. {
  88. JUCE_COMPILER_WARNING("testing")
  89. return true;
  90. return version != JUCE_STRINGIFY(JUCE_MAJOR_VERSION)
  91. "." JUCE_STRINGIFY(JUCE_MINOR_VERSION)
  92. "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)
  93. && version.containsChar ('.')
  94. && version.length() > 2;
  95. }
  96. String version;
  97. URL url;
  98. };
  99. void askUserAboutNewVersion (const VersionInfo& info)
  100. {
  101. if (info.isDifferentVersionToCurrent())
  102. {
  103. if (isRunningFromZipFolder())
  104. {
  105. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
  106. TRANS("Download JUCE version 123?").replace ("123", info.version),
  107. TRANS("A new version of JUCE is available - would you like to overwrite the folder:\n\n"
  108. "xfldrx\n\n"
  109. " ..with the latest version from juce.com?\n\n"
  110. "(Please note that this will overwrite everything in that folder!)")
  111. .replace ("xfldrx", getZipFolder().getFullPathName())))
  112. {
  113. DownloadNewVersionThread::performDownload (info.url, getZipFolder());
  114. }
  115. }
  116. else
  117. {
  118. JUCE_COMPILER_WARNING("todo")
  119. File targetFolder;
  120. // FileChooser f;
  121. DownloadNewVersionThread::performDownload (info.url, targetFolder);
  122. }
  123. }
  124. }
  125. static bool isZipFolder (const File& f)
  126. {
  127. JUCE_COMPILER_WARNING("testing")
  128. return true;
  129. return f.getChildFile ("modules").isDirectory()
  130. && f.getChildFile ("extras").isDirectory()
  131. && f.getChildFile ("examples").isDirectory()
  132. && ! f.getChildFile (".git").isDirectory();
  133. }
  134. static File getZipFolder()
  135. {
  136. File appParentFolder (File::getSpecialLocation (File::currentApplicationFile).getParentDirectory());
  137. return isZipFolder (appParentFolder) ? appParentFolder : File::nonexistent;
  138. }
  139. static bool isRunningFromZipFolder()
  140. {
  141. return getZipFolder() != File::nonexistent;
  142. }
  143. private:
  144. void timerCallback() override
  145. {
  146. stopTimer();
  147. if (hasAttemptedToReadWebsite)
  148. processResult (jsonReply);
  149. else
  150. startThread (3);
  151. }
  152. void run() override
  153. {
  154. checkForNewVersion();
  155. }
  156. var jsonReply;
  157. bool hasAttemptedToReadWebsite;
  158. URL newVersionToDownload;
  159. //==============================================================================
  160. class DownloadNewVersionThread : public ThreadWithProgressWindow
  161. {
  162. public:
  163. DownloadNewVersionThread (URL u, File target)
  164. : ThreadWithProgressWindow ("Downloading New Version", true, true),
  165. result (Result::ok()),
  166. url (u), targetFolder (target)
  167. {
  168. }
  169. static void performDownload (URL u, File targetFolder)
  170. {
  171. DownloadNewVersionThread d (u, targetFolder);
  172. if (d.runThread())
  173. {
  174. if (d.result.failed())
  175. {
  176. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  177. "Installation Failed",
  178. d.result.getErrorMessage());
  179. }
  180. else
  181. {
  182. JUCE_COMPILER_WARNING("todo")
  183. }
  184. }
  185. }
  186. void run() override
  187. {
  188. setProgress (-1.0);
  189. MemoryBlock zipData;
  190. result = download (zipData);
  191. if (result.wasOk() && ! threadShouldExit())
  192. result = unzip (zipData);
  193. }
  194. Result download (MemoryBlock& dest)
  195. {
  196. setStatusMessage ("Downloading...");
  197. const ScopedPointer<InputStream> in (url.createInputStream (false, nullptr, nullptr, String::empty, 10000));
  198. if (in != nullptr)
  199. {
  200. int64 total = 0;
  201. MemoryOutputStream mo (dest, true);
  202. for (;;)
  203. {
  204. if (threadShouldExit())
  205. return Result::fail ("cancel");
  206. size_t written = mo.writeFromInputStream (*in, 8192);
  207. if (written == 0)
  208. break;
  209. total += written;
  210. setStatusMessage (String (TRANS ("Downloading... (123)"))
  211. .replace ("123", File::descriptionOfSizeInBytes (total)));
  212. }
  213. return Result::ok();
  214. }
  215. return Result::fail ("Failed to download from: " + url.toString (false));
  216. }
  217. Result unzip (const MemoryBlock& data)
  218. {
  219. setStatusMessage ("Installing...");
  220. File tempUnzipped;
  221. {
  222. MemoryInputStream input (data, false);
  223. ZipFile zip (input);
  224. if (zip.getNumEntries() == 0)
  225. return Result::fail ("The downloaded file wasn't a valid JUCE file!");
  226. tempUnzipped = targetFolder.getNonexistentSibling();
  227. if (! tempUnzipped.createDirectory())
  228. return Result::fail ("Couldn't create a folder to unzip the new version!");
  229. Result r (zip.uncompressTo (tempUnzipped));
  230. if (r.failed())
  231. {
  232. tempUnzipped.deleteRecursively();
  233. return r;
  234. }
  235. }
  236. File oldFolder (targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling());
  237. if (! targetFolder.moveFileTo (targetFolder.getNonexistentSibling()))
  238. {
  239. tempUnzipped.deleteRecursively();
  240. return Result::fail ("Could not remove the existing folder!");
  241. }
  242. if (! tempUnzipped.moveFileTo (targetFolder))
  243. {
  244. tempUnzipped.deleteRecursively();
  245. return Result::fail ("Could not overwrite the existing folder!");
  246. }
  247. return Result::ok();
  248. }
  249. Result result;
  250. URL url;
  251. File targetFolder;
  252. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadNewVersionThread)
  253. };
  254. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatestVersionChecker)
  255. };
  256. #endif // JUCER_AUTOUPDATER_H_INCLUDED