|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2013 - Raw Material Software Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- #ifndef JUCER_AUTOUPDATER_H_INCLUDED
- #define JUCER_AUTOUPDATER_H_INCLUDED
-
-
-
- //==============================================================================
- class LatestVersionChecker : private Thread,
- private Timer
- {
- public:
- LatestVersionChecker() : Thread ("Updater"),
- hasAttemptedToReadWebsite (false)
- {
- startTimer (2000);
- }
-
- ~LatestVersionChecker()
- {
- stopThread (20000);
- }
-
- static URL getLatestVersionURL()
- {
- return URL ("http://www.juce.com/juce/updates/updatelist.php");
- }
-
- void checkForNewVersion()
- {
- hasAttemptedToReadWebsite = true;
-
- {
- const ScopedPointer<InputStream> in (getLatestVersionURL().createInputStream (false));
-
- if (in == nullptr || threadShouldExit())
- return; // can't connect: fail silently.
-
- jsonReply = JSON::parse (in->readEntireStreamAsString());
- }
-
- if (threadShouldExit())
- return;
-
- if (jsonReply.isArray() || jsonReply.isObject())
- startTimer (100);
- }
-
- void processResult (var reply)
- {
- if (reply.isArray())
- {
- askUserAboutNewVersion (VersionInfo (reply[0]));
- }
- else if (reply.isObject())
- {
- // In the far-distant future, this may be contacting a defunct
- // URL, so hopefully the website will contain a helpful message
- // for the user..
- String message = reply.getProperty ("message", var()).toString();
-
- if (message.isNotEmpty())
- {
- AlertWindow::showMessageBox (AlertWindow::WarningIcon,
- TRANS("JUCE Updater"),
- message);
- }
- }
- }
-
- struct VersionInfo
- {
- VersionInfo (var v)
- {
- version = v.getProperty ("version", var()).toString().trim();
-
- url = v.getProperty (
- #if JUCE_MAC
- "url_osx",
- #elif JUCE_WINDOWS
- "url_win",
- #elif JUCE_LINUX
- "url_linux",
- #endif
- var()).toString();
- }
-
- bool isDifferentVersionToCurrent() const
- {
- return version != JUCE_STRINGIFY(JUCE_MAJOR_VERSION)
- "." JUCE_STRINGIFY(JUCE_MINOR_VERSION)
- "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)
- && version.containsChar ('.')
- && version.length() > 2;
- }
-
- String version;
- URL url;
- };
-
- void askUserAboutNewVersion (const VersionInfo& info)
- {
- if (info.isDifferentVersionToCurrent())
- {
- if (isRunningFromZipFolder())
- {
- if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
- TRANS("Download JUCE version 123?").replace ("123", info.version),
- TRANS("A new version of JUCE is available - would you like to overwrite the folder:\n\n"
- "xfldrx\n\n"
- " ..with the latest version from juce.com?\n\n"
- "(Please note that this will overwrite everything in that folder!)")
- .replace ("xfldrx", getZipFolder().getFullPathName())))
- {
- DownloadNewVersionThread::performDownload (info.url, getZipFolder());
- }
- }
- else
- {
- if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
- TRANS("Download JUCE version 123?").replace ("123", info.version),
- TRANS("A new version of JUCE is available - would you like to download it?")))
- {
- return;
- }
-
- File targetFolder (findDefaultModulesFolder());
-
- if (isJuceModulesFolder (targetFolder))
- targetFolder = targetFolder.getParentDirectory();
-
- FileChooser chooser (TRANS("Please select the location into which you'd like to install the new version"),
- targetFolder);
-
- if (chooser.browseForDirectory())
- {
- targetFolder = chooser.getResult();
-
- if (isJuceModulesFolder (targetFolder))
- targetFolder = targetFolder.getParentDirectory();
-
- if (targetFolder.getChildFile ("JUCE").isDirectory())
- targetFolder = targetFolder.getChildFile ("JUCE");
-
- if (targetFolder.getChildFile (".git").isDirectory())
- {
- AlertWindow::showMessageBox (AlertWindow::WarningIcon,
- TRANS ("Downloading new JUCE version"),
- TRANS ("This folder is a GIT repository!\n\n"
- "You should use a \"git pull\" to update it to the latest version. "
- "Or to use the Introjucer to get an update, you should select an empty "
- "folder into which you'd like to download the new code."));
-
- return;
- }
-
- if (isJuceFolder (targetFolder))
- {
- if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
- TRANS("Overwrite existing JUCE folder?"),
- TRANS("Do you want to overwrite the folder:\n\n"
- "xfldrx\n\n"
- " ..with the latest version from juce.com?\n\n"
- "(Please note that this will overwrite everything in that folder!)")
- .replace ("xfldrx", targetFolder.getFullPathName())))
- {
- return;
- }
- }
- else
- {
- targetFolder = targetFolder.getChildFile ("JUCE").getNonexistentSibling();
- }
-
- DownloadNewVersionThread::performDownload (info.url, targetFolder);
- }
- }
- }
- }
-
- static bool isZipFolder (const File& f)
- {
- return f.getChildFile ("modules").isDirectory()
- && f.getChildFile ("extras").isDirectory()
- && f.getChildFile ("examples").isDirectory()
- && ! f.getChildFile (".git").isDirectory();
- }
-
- static File getZipFolder()
- {
- File appParentFolder (File::getSpecialLocation (File::currentApplicationFile).getParentDirectory());
- return isZipFolder (appParentFolder) ? appParentFolder : File::nonexistent;
- }
-
- static bool isRunningFromZipFolder()
- {
- return getZipFolder() != File::nonexistent;
- }
-
- private:
- void timerCallback() override
- {
- stopTimer();
-
- if (hasAttemptedToReadWebsite)
- processResult (jsonReply);
- else
- startThread (3);
- }
-
- void run() override
- {
- checkForNewVersion();
- }
-
- var jsonReply;
- bool hasAttemptedToReadWebsite;
- URL newVersionToDownload;
-
- //==============================================================================
- class DownloadNewVersionThread : public ThreadWithProgressWindow
- {
- public:
- DownloadNewVersionThread (URL u, File target)
- : ThreadWithProgressWindow ("Downloading New Version", true, true),
- result (Result::ok()),
- url (u), targetFolder (target)
- {
- }
-
- static void performDownload (URL u, File targetFolder)
- {
- DownloadNewVersionThread d (u, targetFolder);
-
- if (d.runThread())
- {
- if (d.result.failed())
- {
- AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
- "Installation Failed",
- d.result.getErrorMessage());
- }
- else
- {
- new RelaunchTimer (targetFolder);
- }
- }
- }
-
- void run() override
- {
- setProgress (-1.0);
-
- MemoryBlock zipData;
- result = download (zipData);
-
- if (result.wasOk() && ! threadShouldExit())
- result = unzip (zipData);
- }
-
- Result download (MemoryBlock& dest)
- {
- setStatusMessage ("Downloading...");
-
- const ScopedPointer<InputStream> in (url.createInputStream (false, nullptr, nullptr, String::empty, 10000));
-
- if (in != nullptr)
- {
- int64 total = 0;
- MemoryOutputStream mo (dest, true);
-
- for (;;)
- {
- if (threadShouldExit())
- return Result::fail ("cancel");
-
- int64 written = mo.writeFromInputStream (*in, 8192);
-
- if (written == 0)
- break;
-
- total += written;
-
- setStatusMessage (String (TRANS ("Downloading... (123)"))
- .replace ("123", File::descriptionOfSizeInBytes (total)));
- }
-
- return Result::ok();
- }
-
- return Result::fail ("Failed to download from: " + url.toString (false));
- }
-
- Result unzip (const MemoryBlock& data)
- {
- setStatusMessage ("Installing...");
-
- File unzipTarget;
- bool isUsingTempFolder = false;
-
- {
- MemoryInputStream input (data, false);
- ZipFile zip (input);
-
- if (zip.getNumEntries() == 0)
- return Result::fail ("The downloaded file wasn't a valid JUCE file!");
-
- unzipTarget = targetFolder;
-
- if (unzipTarget.exists())
- {
- isUsingTempFolder = true;
- unzipTarget = targetFolder.getNonexistentSibling();
-
- if (! unzipTarget.createDirectory())
- return Result::fail ("Couldn't create a folder to unzip the new version!");
- }
-
- Result r (zip.uncompressTo (unzipTarget));
-
- if (r.failed())
- {
- if (isUsingTempFolder)
- unzipTarget.deleteRecursively();
-
- return r;
- }
- }
-
- if (isUsingTempFolder)
- {
- File oldFolder (targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling());
-
- if (! targetFolder.moveFileTo (oldFolder))
- {
- unzipTarget.deleteRecursively();
- return Result::fail ("Could not remove the existing folder!");
- }
-
- if (! unzipTarget.moveFileTo (targetFolder))
- {
- unzipTarget.deleteRecursively();
- return Result::fail ("Could not overwrite the existing folder!");
- }
- }
-
- return Result::ok();
- }
-
- Result result;
- URL url;
- File targetFolder;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadNewVersionThread)
- };
-
- struct RelaunchTimer : private Timer
- {
- RelaunchTimer (const File& f) : parentFolder (f)
- {
- startTimer (1500);
- }
-
- void timerCallback() override
- {
- stopTimer();
-
- File app = parentFolder.getChildFile (
- #if JUCE_MAC
- "Introjucer.app");
- #elif JUCE_WINDOWS
- "Introjucer.exe");
- #elif JUCE_LINUX
- "Introjucer");
- #endif
-
- JUCEApplication::quit();
-
- if (app.exists())
- {
- app.setExecutePermission (true);
-
- #if JUCE_MAC
- app.getChildFile("Contents")
- .getChildFile("MacOS")
- .getChildFile("Introjucer").setExecutePermission (true);
- #endif
-
- app.startAsProcess();
- }
-
- delete this;
- }
-
- File parentFolder;
- };
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatestVersionChecker)
- };
-
-
- #endif // JUCER_AUTOUPDATER_H_INCLUDED
|