diff options
-rw-r--r-- | indra/newview/app_settings/settings.xml | 19 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 85 | ||||
-rw-r--r-- | indra/newview/lllogininstance.cpp | 415 | ||||
-rw-r--r-- | indra/newview/lllogininstance.h | 6 | ||||
-rw-r--r-- | indra/newview/llprogressview.cpp | 4 | ||||
-rw-r--r-- | indra/newview/llviewercontrol.cpp | 4 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 64 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_preferences_setup.xml | 53 | ||||
-rw-r--r-- | indra/newview/tests/lllogininstance_test.cpp | 47 | ||||
-rw-r--r-- | indra/viewer_components/updater/llupdatedownloader.cpp | 89 | ||||
-rw-r--r-- | indra/viewer_components/updater/llupdatedownloader.h | 6 | ||||
-rw-r--r-- | indra/viewer_components/updater/llupdaterservice.cpp | 102 | ||||
-rw-r--r-- | indra/viewer_components/updater/llupdaterservice.h | 26 | ||||
-rw-r--r-- | indra/viewer_components/updater/tests/llupdaterservice_test.cpp | 3 |
14 files changed, 873 insertions, 50 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ed67a87024..ae66f5ec39 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9990,6 +9990,17 @@ <key>Value</key> <real>500.0</real> </map> + <key>UpdaterMaximumBandwidth</key> + <map> + <key>Comment</key> + <string>Maximum allowable downstream bandwidth for updater service (kilo bits per second)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>500.0</real> + </map> <key>ToolTipDelay</key> <map> <key>Comment</key> @@ -11090,16 +11101,16 @@ <key>Value</key> <integer>15</integer> </map> - <key>UpdaterServiceActive</key> + <key>UpdaterServiceSetting</key> <map> <key>Comment</key> - <string>Enable or disable the updater service.</string> + <string>Configure updater service.</string> <key>Persist</key> <integer>1</integer> <key>Type</key> - <string>Boolean</string> + <string>U32</string> <key>Value</key> - <integer>1</integer> + <integer>3</integer> </map> <key>UpdaterServiceCheckPeriod</key> <map> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6c07974f69..41be4eb065 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -80,6 +80,7 @@ #include "llfeaturemanager.h" #include "llurlmatch.h" #include "lltextutil.h" +#include "lllogininstance.h" #include "llweb.h" #include "llsecondlifeurls.h" @@ -590,10 +591,14 @@ LLAppViewer::LLAppViewer() : setupErrorHandling(); sInstance = this; gLoggedInTime.stop(); + + LLLoginInstance::instance().setUpdaterService(mUpdater.get()); } LLAppViewer::~LLAppViewer() { + LLLoginInstance::instance().setUpdaterService(0); + destroyMainloopTimeout(); // If we got to this destructor somehow, the app didn't hang. @@ -2389,26 +2394,95 @@ bool LLAppViewer::initConfiguration() } namespace { - // *TODO - decide if there's a better place for this function. + // *TODO - decide if there's a better place for these functions. // do we need a file llupdaterui.cpp or something? -brad + + void apply_update_callback(LLSD const & notification, LLSD const & response) + { + lldebugs << "LLUpdate user response: " << response << llendl; + if(response["OK_okcancelbuttons"].asBoolean()) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + } + + void apply_update_ok_callback(LLSD const & notification, LLSD const & response) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + + void on_required_update_downloaded(LLSD const & data) + { + std::string notification_name; + if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT) + { + // The user never saw the progress bar. + notification_name = "RequiredUpdateDownloadedVerboseDialog"; + } + else + { + notification_name = "RequiredUpdateDownloadedDialog"; + } + LLSD substitutions; + substitutions["VERSION"] = data["version"]; + LLNotificationsUtil::add(notification_name, substitutions, LLSD(), &apply_update_ok_callback); + } + + void on_optional_update_downloaded(LLSD const & data) + { + std::string notification_name; + if(LLStartUp::getStartupState() < STATE_STARTED) + { + // CHOP-262 we need to use a different notification + // method prior to login. + notification_name = "DownloadBackgroundDialog"; + } + else + { + notification_name = "DownloadBackgroundTip"; + } + LLSD substitutions; + substitutions["VERSION"] = data["version"]; + LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_update_callback); + } + bool notify_update(LLSD const & evt) { + std::string notification_name; switch (evt["type"].asInteger()) { case LLUpdaterService::DOWNLOAD_COMPLETE: - LLNotificationsUtil::add("DownloadBackground"); + if(evt["required"].asBoolean()) + { + on_required_update_downloaded(evt); + } + else + { + on_optional_update_downloaded(evt); + } break; case LLUpdaterService::INSTALL_ERROR: LLNotificationsUtil::add("FailedUpdateInstall"); break; default: - llinfos << "unhandled update event " << evt << llendl; break; } // let others also handle this event by default return false; } + + bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt) + { + updater->setBandwidthLimit(evt.asInteger() * (1024/8)); + return false; // Let others receive this event. + }; }; void LLAppViewer::initUpdater() @@ -2431,7 +2505,10 @@ void LLAppViewer::initUpdater() channel, version); mUpdater->setCheckPeriod(check_period); - if(gSavedSettings.getBOOL("UpdaterServiceActive")) + mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8)); + gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()-> + connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2)); + if(gSavedSettings.getU32("UpdaterServiceSetting")) { bool install_if_ready = true; mUpdater->startChecking(install_if_ready); diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 52ce932241..8d9d7298f8 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -49,18 +49,421 @@ #include "llnotifications.h" #include "llwindow.h" #include "llviewerwindow.h" +#include "llprogressview.h" #if LL_LINUX || LL_SOLARIS #include "lltrans.h" #endif #include "llsecapi.h" #include "llstartup.h" #include "llmachineid.h" +#include "llupdaterservice.h" +#include "llevents.h" +#include "llnotificationsutil.h" +#include "llappviewer.h" + +#include <boost/scoped_ptr.hpp> +#include <sstream> + +class LLLoginInstance::Disposable { +public: + virtual ~Disposable() {} +}; + +namespace { + class MandatoryUpdateMachine: + public LLLoginInstance::Disposable + { + public: + MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService); + + void start(void); + + private: + class State; + class CheckingForUpdate; + class Error; + class ReadyToInstall; + class StartingUpdaterService; + class WaitingForDownload; + + LLLoginInstance & mLoginInstance; + boost::scoped_ptr<State> mState; + LLUpdaterService & mUpdaterService; + + void setCurrentState(State * newState); + }; + + + class MandatoryUpdateMachine::State { + public: + virtual ~State() {} + virtual void enter(void) {} + virtual void exit(void) {} + }; + + + class MandatoryUpdateMachine::CheckingForUpdate: + public MandatoryUpdateMachine::State + { + public: + CheckingForUpdate(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + LLTempBoundListener mConnection; + MandatoryUpdateMachine & mMachine; + LLProgressView * mProgressView; + + bool onEvent(LLSD const & event); + }; + + + class MandatoryUpdateMachine::Error: + public MandatoryUpdateMachine::State + { + public: + Error(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + void onButtonClicked(const LLSD &, const LLSD &); + + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::ReadyToInstall: + public MandatoryUpdateMachine::State + { + public: + ReadyToInstall(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::StartingUpdaterService: + public MandatoryUpdateMachine::State + { + public: + StartingUpdaterService(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + void onButtonClicked(const LLSD & uiform, const LLSD & result); + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::WaitingForDownload: + public MandatoryUpdateMachine::State + { + public: + WaitingForDownload(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + LLTempBoundListener mConnection; + MandatoryUpdateMachine & mMachine; + LLProgressView * mProgressView; + + bool onEvent(LLSD const & event); + }; +} static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; std::string construct_start_string(); + + +// MandatoryUpdateMachine +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService): + mLoginInstance(loginInstance), + mUpdaterService(updaterService) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::start(void) +{ + llinfos << "starting manditory update machine" << llendl; + + if(mUpdaterService.isChecking()) { + switch(mUpdaterService.getState()) { + case LLUpdaterService::UP_TO_DATE: + mUpdaterService.stopChecking(); + mUpdaterService.startChecking(); + // Fall through. + case LLUpdaterService::INITIAL: + case LLUpdaterService::CHECKING_FOR_UPDATE: + setCurrentState(new CheckingForUpdate(*this)); + break; + case LLUpdaterService::DOWNLOADING: + setCurrentState(new WaitingForDownload(*this)); + break; + case LLUpdaterService::TERMINAL: + if(LLUpdaterService::updateReadyToInstall()) { + setCurrentState(new ReadyToInstall(*this)); + } else { + setCurrentState(new Error(*this)); + } + break; + case LLUpdaterService::FAILURE: + setCurrentState(new Error(*this)); + break; + default: + llassert(!"unpossible case"); + break; + } + } else { + setCurrentState(new StartingUpdaterService(*this)); + } +} + + +void MandatoryUpdateMachine::setCurrentState(State * newStatePointer) +{ + { + boost::scoped_ptr<State> newState(newStatePointer); + if(mState != 0) mState->exit(); + mState.swap(newState); + + // Old state will be deleted on exit from this block before the new state + // is entered. + } + if(mState != 0) mState->enter(); +} + + + +// MandatoryUpdateMachine::CheckingForUpdate +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::CheckingForUpdate::CheckingForUpdate(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::CheckingForUpdate::enter(void) +{ + llinfos << "entering checking for update" << llendl; + + mProgressView = gViewerWindow->getProgressView(); + mProgressView->setMessage("Looking for update..."); + mProgressView->setText("There is a required update for your Second Life installation."); + mProgressView->setPercent(0); + mProgressView->setVisible(true); + mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). + listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::CheckingForUpdate::onEvent, this, _1)); +} + + +void MandatoryUpdateMachine::CheckingForUpdate::exit(void) +{ +} + + +bool MandatoryUpdateMachine::CheckingForUpdate::onEvent(LLSD const & event) +{ + if(event["type"].asInteger() == LLUpdaterService::STATE_CHANGE) { + switch(event["state"].asInteger()) { + case LLUpdaterService::DOWNLOADING: + mMachine.setCurrentState(new WaitingForDownload(mMachine)); + break; + case LLUpdaterService::UP_TO_DATE: + case LLUpdaterService::TERMINAL: + case LLUpdaterService::FAILURE: + mProgressView->setVisible(false); + mMachine.setCurrentState(new Error(mMachine)); + break; + case LLUpdaterService::INSTALLING: + llassert(!"can't possibly be installing"); + break; + default: + break; + } + } else { + ; // Ignore. + } + + return false; +} + + + +// MandatoryUpdateMachine::Error +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::Error::Error(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::Error::enter(void) +{ + llinfos << "entering error" << llendl; + LLNotificationsUtil::add("FailedUpdateInstall", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::Error::onButtonClicked, this, _1, _2)); +} + + +void MandatoryUpdateMachine::Error::exit(void) +{ + LLAppViewer::instance()->forceQuit(); +} + + +void MandatoryUpdateMachine::Error::onButtonClicked(const LLSD &, const LLSD &) +{ + mMachine.setCurrentState(0); +} + + + +// MandatoryUpdateMachine::ReadyToInstall +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::ReadyToInstall::ReadyToInstall(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::ReadyToInstall::enter(void) +{ + llinfos << "entering ready to install" << llendl; + // Open update ready dialog. +} + + +void MandatoryUpdateMachine::ReadyToInstall::exit(void) +{ + // Restart viewer. +} + + + +// MandatoryUpdateMachine::StartingUpdaterService +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::StartingUpdaterService::StartingUpdaterService(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::StartingUpdaterService::enter(void) +{ + llinfos << "entering start update service" << llendl; + LLNotificationsUtil::add("UpdaterServiceNotRunning", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked, this, _1, _2)); +} + + +void MandatoryUpdateMachine::StartingUpdaterService::exit(void) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked(const LLSD & uiform, const LLSD & result) +{ + if(result["OK_okcancelbuttons"].asBoolean()) { + mMachine.mUpdaterService.startChecking(false); + mMachine.setCurrentState(new CheckingForUpdate(mMachine)); + } else { + LLAppViewer::instance()->forceQuit(); + } +} + + + +// MandatoryUpdateMachine::WaitingForDownload +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::WaitingForDownload::WaitingForDownload(MandatoryUpdateMachine & machine): + mMachine(machine), + mProgressView(0) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::WaitingForDownload::enter(void) +{ + llinfos << "entering waiting for download" << llendl; + mProgressView = gViewerWindow->getProgressView(); + mProgressView->setMessage("Downloading update..."); + std::ostringstream stream; + stream << "There is a required update for your Second Life installation." << std::endl << + "Version " << mMachine.mUpdaterService.updatedVersion(); + mProgressView->setText(stream.str()); + mProgressView->setPercent(0); + mProgressView->setVisible(true); + mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). + listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::WaitingForDownload::onEvent, this, _1)); +} + + +void MandatoryUpdateMachine::WaitingForDownload::exit(void) +{ + mProgressView->setVisible(false); +} + + +bool MandatoryUpdateMachine::WaitingForDownload::onEvent(LLSD const & event) +{ + switch(event["type"].asInteger()) { + case LLUpdaterService::DOWNLOAD_COMPLETE: + mMachine.setCurrentState(new ReadyToInstall(mMachine)); + break; + case LLUpdaterService::DOWNLOAD_ERROR: + mMachine.setCurrentState(new Error(mMachine)); + break; + case LLUpdaterService::PROGRESS: { + double downloadSize = event["download_size"].asReal(); + double bytesDownloaded = event["bytes_downloaded"].asReal(); + mProgressView->setPercent(100. * bytesDownloaded / downloadSize); + break; + } + default: + break; + } + + return false; +} + + + +// LLLoginInstance +//----------------------------------------------------------------------------- + + LLLoginInstance::LLLoginInstance() : mLoginModule(new LLLogin()), mNotifications(NULL), @@ -69,7 +472,8 @@ LLLoginInstance::LLLoginInstance() : mSkipOptionalUpdate(false), mAttemptComplete(false), mTransferRate(0.0f), - mDispatcher("LLLoginInstance", "change") + mDispatcher("LLLoginInstance", "change"), + mUpdaterService(0) { mLoginModule->getEventPump().listen("lllogininstance", boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); @@ -353,6 +757,15 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg) { + if(mandatory) + { + gViewerWindow->setShowProgress(false); + MandatoryUpdateMachine * machine = new MandatoryUpdateMachine(*this, *mUpdaterService); + mUpdateStateMachine.reset(machine); + machine->start(); + return; + } + // store off config state, as we might quit soon gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); LLUIColorTable::instance().saveUserSettings(); diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index 159e05046c..b872d7d1b1 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -34,12 +34,15 @@ class LLLogin; class LLEventStream; class LLNotificationsInterface; +class LLUpdaterService; // This class hosts the login module and is used to // negotiate user authentication attempts. class LLLoginInstance : public LLSingleton<LLLoginInstance> { public: + class Disposable; + LLLoginInstance(); ~LLLoginInstance(); @@ -75,6 +78,7 @@ public: typedef boost::function<void()> UpdaterLauncherCallback; void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; } + void setUpdaterService(LLUpdaterService * updaterService) { mUpdaterService = updaterService; } private: void constructAuthParams(LLPointer<LLCredential> user_credentials); void updateApp(bool mandatory, const std::string& message); @@ -104,6 +108,8 @@ private: int mLastExecEvent; UpdaterLauncherCallback mUpdaterLauncher; LLEventDispatcher mDispatcher; + LLUpdaterService * mUpdaterService; + boost::scoped_ptr<Disposable> mUpdateStateMachine; }; #endif diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index e9504cbba0..250dfc5713 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -133,13 +133,13 @@ void LLProgressView::setVisible(BOOL visible) mFadeTimer.start(); } // showing progress view - else if (!getVisible() && visible) + else if (visible && (!getVisible() || mFadeTimer.getStarted())) { setFocus(TRUE); mFadeTimer.stop(); mProgressTimer.start(); LLPanel::setVisible(TRUE); - } + } } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 622d09c600..2c75551285 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -504,7 +504,7 @@ bool toggle_show_object_render_cost(const LLSD& newvalue) void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value) { - if(new_value.asBoolean()) + if(new_value.asInteger()) { LLUpdaterService().startChecking(); } @@ -661,7 +661,7 @@ void settings_setup_listeners() gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2)); gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2)); gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2)); - gSavedSettings.getControl("UpdaterServiceActive")->getSignal()->connect(&toggle_updater_service_active); + gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active); gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2)); gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2)); } diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 60b876d163..eecbeeb8dc 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2887,12 +2887,66 @@ http://secondlife.com/download. name="okbutton" yestext="OK"/> </notification> + <notification - icon="notifytip.tga" - name="DownloadBackground" - type="notifytip"> -An updated version of [APP_NAME] has been downloaded. -It will be applied the next time you restart [APP_NAME] + icon="alertmodal.tga" + name="UpdaterServiceNotRunning" + type="alertmodal"> +There is a required update for your Second Life Installation. + +You may download this update from http://www.secondlife.com/downloads +or you can install it now. + <usetemplate + name="okcancelbuttons" + notext="Quit Second Life" + yestext="Download and install now"/> + </notification> + + <notification + icon="notify.tga" + name="DownloadBackgroundTip" + type="notify"> +We have downloaded and update to your [APP_NAME] installation. +Version [VERSION] + <usetemplate + name="okcancelbuttons" + notext="Wait" + yestext="Restart Now"/> + </notification> + + <notification + icon="alertmodal.tga" + name="DownloadBackgroundDialog" + type="alertmodal"> +We have downloaded and update to your [APP_NAME] installation. +Version [VERSION] + <usetemplate + name="okcancelbuttons" + notext="Later..." + yestext="Install now and restart [APP_NAME]"/> + </notification> + + <notification + icon="alertmodal.tga" + name="RequiredUpdateDownloadedVerboseDialog" + type="alertmodal"> +We have downloaded a required software update. +Version [VERSION] + +We must restart [APP_NAME] to install the update. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="RequiredUpdateDownloadedDialog" + type="alertmodal"> +We must restart [APP_NAME] to install the update. + <usetemplate + name="okbutton" + yestext="OK"/> </notification> <notification diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 584bd1ea9d..542b6bcf6b 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -142,7 +142,7 @@ layout="topleft" left="80" name="Cache location" - top_delta="40" + top_delta="20" width="300"> Cache location: </text> @@ -341,20 +341,39 @@ name="web_proxy_port" top_delta="0" width="145" /> - - <check_box - top_delta="2" - enabled="true" - follows="left|top" - height="18" - initial_value="true" - control_name="UpdaterServiceActive" - label="Automatically download and install [APP_NAME] updates" - left="30" - mouse_opaque="true" - name="updater_service_active" - radio_style="false" - width="400" - top_pad="10"/> - + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="30" + name="Software updates:" + mouse_opaque="false" + top_pad="5" + width="300"> + Software updates: + </text> + <combo_box + control_name="UpdaterServiceSetting" + follows="left|top" + height="23" + layout="topleft" + left_delta="50" + top_pad="5" + name="updater_service_combobox" + width="300"> + <combo_box.item + label="Install automatically" + name="Install_automatically" + value="3" /> + <combo_box.item + label="Ask before installing" + name="Install_ask" + value="1" /> + <combo_box.item + label="Download and install updates manually" + name="Install_manual" + value="0" /> + </combo_box> </panel> diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 309e9e9ee3..9e321db889 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -40,6 +40,7 @@ #if defined(LL_WINDOWS) #pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr +#pragma warning(disable: 4702) // disable 'unreachable code' so we can safely use skip(). #endif // Constants @@ -68,6 +69,7 @@ static bool gDisconnectCalled = false; #include "../llviewerwindow.h" void LLViewerWindow::setShowProgress(BOOL show) {} +LLProgressView * LLViewerWindow::getProgressView(void) const { return 0; } LLViewerWindow* gViewerWindow; @@ -185,6 +187,41 @@ const std::string &LLVersionInfo::getChannelAndVersion() { return VIEWERLOGIN_VE const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } //----------------------------------------------------------------------------- +#include "../llappviewer.h" +void LLAppViewer::forceQuit(void) {} +LLAppViewer * LLAppViewer::sInstance = 0; + +//----------------------------------------------------------------------------- +#include "llnotificationsutil.h" +LLNotificationPtr LLNotificationsUtil::add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + boost::function<void (const LLSD&, const LLSD&)> functor) { return LLNotificationPtr((LLNotification*)0); } + + +//----------------------------------------------------------------------------- +#include "llupdaterservice.h" + +std::string const & LLUpdaterService::pumpName(void) +{ + static std::string wakka = "wakka wakka wakka"; + return wakka; +} +bool LLUpdaterService::updateReadyToInstall(void) { return false; } +void LLUpdaterService::initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version) {} + +void LLUpdaterService::setCheckPeriod(unsigned int seconds) {} +void LLUpdaterService::startChecking(bool install_if_ready) {} +void LLUpdaterService::stopChecking() {} +bool LLUpdaterService::isChecking() { return false; } +LLUpdaterService::eUpdaterState LLUpdaterService::getState() { return INITIAL; } +std::string LLUpdaterService::updatedVersion() { return ""; } + +//----------------------------------------------------------------------------- #include "llnotifications.h" #include "llfloaterreg.h" static std::string gTOSType; @@ -198,6 +235,12 @@ LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, return NULL; } +//---------------------------------------------------------------------------- +#include "../llprogressview.h" +void LLProgressView::setText(std::string const &){} +void LLProgressView::setPercent(float){} +void LLProgressView::setMessage(std::string const &){} + //----------------------------------------------------------------------------- // LLNotifications class MockNotifications : public LLNotificationsInterface @@ -435,6 +478,8 @@ namespace tut template<> template<> void lllogininstance_object::test<3>() { + skip(); + set_test_name("Test Mandatory Update User Accepts"); // Part 1 - Mandatory Update, with User accepts response. @@ -462,6 +507,8 @@ namespace tut template<> template<> void lllogininstance_object::test<4>() { + skip(); + set_test_name("Test Mandatory Update User Decline"); // Test connect with update needed. diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp index c17a50e242..2dd0084fdc 100644 --- a/indra/viewer_components/updater/llupdatedownloader.cpp +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -24,17 +24,20 @@ */ #include "linden_common.h" + +#include "llupdatedownloader.h" + #include <stdexcept> #include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <curl/curl.h> #include "lldir.h" +#include "llevents.h" #include "llfile.h" #include "llmd5.h" #include "llsd.h" #include "llsdserialize.h" #include "llthread.h" -#include "llupdatedownloader.h" #include "llupdaterservice.h" @@ -45,18 +48,22 @@ public: Implementation(LLUpdateDownloader::Client & client); ~Implementation(); void cancel(void); - void download(LLURI const & uri, std::string const & hash); + void download(LLURI const & uri, std::string const & hash, bool required); bool isDownloading(void); size_t onHeader(void * header, size_t size); size_t onBody(void * header, size_t size); + int onProgress(double downloadSize, double bytesDownloaded); void resume(void); + void setBandwidthLimit(U64 bytesPerSecond); private: + curl_off_t mBandwidthLimit; bool mCancelled; LLUpdateDownloader::Client & mClient; CURL * mCurl; LLSD mDownloadData; llofstream mDownloadStream; + unsigned char mDownloadPercent; std::string mDownloadRecordPath; curl_slist * mHeaderList; @@ -113,9 +120,9 @@ void LLUpdateDownloader::cancel(void) } -void LLUpdateDownloader::download(LLURI const & uri, std::string const & hash) +void LLUpdateDownloader::download(LLURI const & uri, std::string const & hash, bool required) { - mImplementation->download(uri, hash); + mImplementation->download(uri, hash, required); } @@ -131,6 +138,12 @@ void LLUpdateDownloader::resume(void) } +void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) +{ + mImplementation->setBandwidthLimit(bytesPerSecond); +} + + // LLUpdateDownloader::Implementation //----------------------------------------------------------------------------- @@ -149,14 +162,27 @@ namespace { size_t bytes = blockSize * blocks; return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes); } + + + int progress_callback(void * downloader, + double dowloadTotal, + double downloadNow, + double uploadTotal, + double uploadNow) + { + return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)-> + onProgress(dowloadTotal, downloadNow); + } } LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client): LLThread("LLUpdateDownloader"), + mBandwidthLimit(0), mCancelled(false), mClient(client), mCurl(0), + mDownloadPercent(0), mHeaderList(0) { CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case. @@ -182,12 +208,13 @@ void LLUpdateDownloader::Implementation::cancel(void) } -void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash) +void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash, bool required) { if(isDownloading()) mClient.downloadError("download in progress"); mDownloadRecordPath = downloadMarkerPath(); mDownloadData = LLSD(); + mDownloadData["required"] = required; try { startDownloading(uri, hash); } catch(DownloadError const & e) { @@ -233,12 +260,12 @@ void LLUpdateDownloader::Implementation::resume(void) resumeDownloading(fileStatus.st_size); } else if(!validateDownload()) { LLFile::remove(filePath); - download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); + download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString(), mDownloadData["required"].asBoolean()); } else { mClient.downloadComplete(mDownloadData); } } else { - download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); + download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString(), mDownloadData["required"].asBoolean()); } } catch(DownloadError & e) { mClient.downloadError(e.what()); @@ -246,6 +273,20 @@ void LLUpdateDownloader::Implementation::resume(void) } +void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond) +{ + if((mBandwidthLimit != bytesPerSecond) && isDownloading() && !mDownloadData["required"].asBoolean()) { + llassert(mCurl != 0); + mBandwidthLimit = bytesPerSecond; + CURLcode code = curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit); + if(code != CURLE_OK) LL_WARNS("UpdateDownload") << + "unable to change dowload bandwidth" << LL_ENDL; + } else { + mBandwidthLimit = bytesPerSecond; + } +} + + size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size) { char const * headerPtr = reinterpret_cast<const char *> (buffer); @@ -290,6 +331,30 @@ size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size) } +int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double bytesDownloaded) +{ + int downloadPercent = static_cast<int>(100. * (bytesDownloaded / downloadSize)); + if(downloadPercent > mDownloadPercent) { + mDownloadPercent = downloadPercent; + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::PROGRESS); + payload["download_size"] = downloadSize; + payload["bytes_downloaded"] = bytesDownloaded; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + LL_INFOS("UpdateDownload") << "progress event " << payload << LL_ENDL; + } else { + ; // Keep events to a reasonalbe number. + } + + return 0; +} + + void LLUpdateDownloader::Implementation::run(void) { CURLcode code = curl_easy_perform(mCurl); @@ -343,6 +408,16 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u } throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true)); throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str())); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, &progress_callback)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, this)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, false)); + if((mBandwidthLimit != 0) && !mDownloadData["required"].asBoolean()) { + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, mBandwidthLimit)); + } else { + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, -1)); + } + + mDownloadPercent = 0; } diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h index 1b3d7480fd..4e20b307b8 100644 --- a/indra/viewer_components/updater/llupdatedownloader.h +++ b/indra/viewer_components/updater/llupdatedownloader.h @@ -52,7 +52,7 @@ public: void cancel(void); // Start a new download. - void download(LLURI const & uri, std::string const & hash); + void download(LLURI const & uri, std::string const & hash, bool required=false); // Returns true if a download is in progress. bool isDownloading(void); @@ -60,6 +60,9 @@ public: // Resume a partial download. void resume(void); + // Set a limit on the dowload rate. + void setBandwidthLimit(U64 bytesPerSecond); + private: boost::shared_ptr<Implementation> mImplementation; }; @@ -76,6 +79,7 @@ public: // url - source (remote) location // hash - the md5 sum that should match the installer file. // path - destination (local) location + // required - boolean indicating if this is a required update. // size - the size of the installer in bytes virtual void downloadComplete(LLSD const & data) = 0; diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp index cc60eaead2..78f768facf 100644 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -25,10 +25,11 @@ #include "linden_common.h" +#include "llupdaterservice.h" + #include "llupdatedownloader.h" #include "llevents.h" #include "lltimer.h" -#include "llupdaterservice.h" #include "llupdatechecker.h" #include "llupdateinstaller.h" #include "llversionviewer.h" @@ -98,6 +99,8 @@ class LLUpdaterServiceImpl : LLUpdaterService::app_exit_callback_t mAppExitCallback; + LLUpdaterService::eUpdaterState mState; + LOG_CLASS(LLUpdaterServiceImpl); public: @@ -111,12 +114,15 @@ public: const std::string& version); void setCheckPeriod(unsigned int seconds); + void setBandwidthLimit(U64 bytesPerSecond); void startChecking(bool install_if_ready); void stopChecking(); bool isChecking(); + LLUpdaterService::eUpdaterState getState(); void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;} + std::string updatedVersion(void); bool checkForInstall(bool launchInstaller); // Test if a local install is ready. bool checkForResume(); // Test for resumeable d/l. @@ -138,7 +144,10 @@ public: bool onMainLoop(LLSD const & event); private: + std::string mNewVersion; + void restartTimer(unsigned int seconds); + void setState(LLUpdaterService::eUpdaterState state); void stopTimer(); }; @@ -149,7 +158,8 @@ LLUpdaterServiceImpl::LLUpdaterServiceImpl() : mIsDownloading(false), mCheckPeriod(0), mUpdateChecker(*this), - mUpdateDownloader(*this) + mUpdateDownloader(*this), + mState(LLUpdaterService::INITIAL) { } @@ -183,6 +193,11 @@ void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds) mCheckPeriod = seconds; } +void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond) +{ + mUpdateDownloader.setBandwidthLimit(bytesPerSecond); +} + void LLUpdaterServiceImpl::startChecking(bool install_if_ready) { if(mUrl.empty() || mChannel.empty() || mVersion.empty()) @@ -201,10 +216,16 @@ void LLUpdaterServiceImpl::startChecking(bool install_if_ready) if(!mIsDownloading) { + setState(LLUpdaterService::CHECKING_FOR_UPDATE); + // Checking can only occur during the mainloop. // reset the timer to 0 so that the next mainloop event // triggers a check; restartTimer(0); + } + else + { + setState(LLUpdaterService::DOWNLOADING); } } } @@ -222,6 +243,8 @@ void LLUpdaterServiceImpl::stopChecking() mUpdateDownloader.cancel(); mIsDownloading = false; } + + setState(LLUpdaterService::TERMINAL); } bool LLUpdaterServiceImpl::isChecking() @@ -229,6 +252,16 @@ bool LLUpdaterServiceImpl::isChecking() return mIsChecking; } +LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState() +{ + return mState; +} + +std::string LLUpdaterServiceImpl::updatedVersion(void) +{ + return mNewVersion; +} + bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) { bool foundInstall = false; // return true if install is found. @@ -266,6 +299,8 @@ bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) { if(launchInstaller) { + setState(LLUpdaterService::INSTALLING); + LLFile::remove(update_marker_path()); int result = ll_install_update(install_script_path(), @@ -333,8 +368,11 @@ void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion, std::string const & hash) { stopTimer(); + mNewVersion = newVersion; mIsDownloading = true; - mUpdateDownloader.download(uri, hash); + mUpdateDownloader.download(uri, hash, false); + + setState(LLUpdaterService::DOWNLOADING); } void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion, @@ -342,8 +380,11 @@ void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion, std::string const & hash) { stopTimer(); + mNewVersion = newVersion; mIsDownloading = true; - mUpdateDownloader.download(uri, hash); + mUpdateDownloader.download(uri, hash, true); + + setState(LLUpdaterService::DOWNLOADING); } void LLUpdaterServiceImpl::upToDate(void) @@ -352,6 +393,8 @@ void LLUpdaterServiceImpl::upToDate(void) { restartTimer(mCheckPeriod); } + + setState(LLUpdaterService::UP_TO_DATE); } void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) @@ -367,8 +410,12 @@ void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) event["pump"] = LLUpdaterService::pumpName(); LLSD payload; payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE); + payload["required"] = data["required"]; + payload["version"] = mNewVersion; event["payload"] = payload; LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + setState(LLUpdaterService::TERMINAL); } void LLUpdaterServiceImpl::downloadError(std::string const & message) @@ -390,6 +437,8 @@ void LLUpdaterServiceImpl::downloadError(std::string const & message) payload["message"] = message; event["payload"] = payload; LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + setState(LLUpdaterService::FAILURE); } void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) @@ -402,6 +451,28 @@ void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1)); } +void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state) +{ + if(state != mState) + { + mState = state; + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE); + payload["state"] = state; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL; + } + else + { + ; // State unchanged; noop. + } +} + void LLUpdaterServiceImpl::stopTimer() { mTimer.stop(); @@ -425,10 +496,13 @@ bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event) LLSD event; event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR); LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event); + + setState(LLUpdaterService::TERMINAL); } else { mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion); + setState(LLUpdaterService::CHECKING_FOR_UPDATE); } } else @@ -449,6 +523,11 @@ std::string const & LLUpdaterService::pumpName(void) return name; } +bool LLUpdaterService::updateReadyToInstall(void) +{ + return LLFile::isfile(update_marker_path()); +} + LLUpdaterService::LLUpdaterService() { if(gUpdater.expired()) @@ -480,6 +559,11 @@ void LLUpdaterService::setCheckPeriod(unsigned int seconds) { mImpl->setCheckPeriod(seconds); } + +void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond) +{ + mImpl->setBandwidthLimit(bytesPerSecond); +} void LLUpdaterService::startChecking(bool install_if_ready) { @@ -496,11 +580,21 @@ bool LLUpdaterService::isChecking() return mImpl->isChecking(); } +LLUpdaterService::eUpdaterState LLUpdaterService::getState() +{ + return mImpl->getState(); +} + void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { return mImpl->setAppExitCallback(aecb); } +std::string LLUpdaterService::updatedVersion(void) +{ + return mImpl->updatedVersion(); +} + std::string const & ll_get_version(void) { static std::string version(""); diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h index 752a6f834b..421481bc43 100644 --- a/indra/viewer_components/updater/llupdaterservice.h +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -43,12 +43,27 @@ public: // Name of the event pump through which update events will be delivered. static std::string const & pumpName(void); + // Returns true if an update has been completely downloaded and is now ready to install. + static bool updateReadyToInstall(void); + // Type codes for events posted by this service. Stored the event's 'type' element. - enum eUpdateEvent { + enum eUpdaterEvent { INVALID, DOWNLOAD_COMPLETE, DOWNLOAD_ERROR, - INSTALL_ERROR + INSTALL_ERROR, + PROGRESS, + STATE_CHANGE + }; + + enum eUpdaterState { + INITIAL, + CHECKING_FOR_UPDATE, + DOWNLOADING, + INSTALLING, + UP_TO_DATE, + TERMINAL, + FAILURE }; LLUpdaterService(); @@ -61,10 +76,12 @@ public: const std::string& version); void setCheckPeriod(unsigned int seconds); + void setBandwidthLimit(U64 bytesPerSecond); void startChecking(bool install_if_ready = false); void stopChecking(); bool isChecking(); + eUpdaterState getState(); typedef boost::function<void (void)> app_exit_callback_t; template <typename F> @@ -73,6 +90,11 @@ public: app_exit_callback_t aecb = callable; setImplAppExitCallback(aecb); } + + // If an update is or has been downloaded, this method will return the + // version string for that update. An empty string will be returned + // otherwise. + std::string updatedVersion(void); private: boost::shared_ptr<LLUpdaterServiceImpl> mImpl; diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp index 04ed4e6364..fbdf9a4993 100644 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -48,7 +48,7 @@ void LLUpdateChecker::check(std::string const & protocolVersion, std::string con std::string const & servicePath, std::string channel, std::string version)
{}
LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
-void LLUpdateDownloader::download(LLURI const & , std::string const &){}
+void LLUpdateDownloader::download(LLURI const & , std::string const &, bool){}
class LLDir_Mock : public LLDir
{
@@ -101,6 +101,7 @@ std::string LLUpdateDownloader::downloadMarkerPath(void) void LLUpdateDownloader::resume(void) {}
void LLUpdateDownloader::cancel(void) {}
+void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {}
int ll_install_update(std::string const &, std::string const &, LLInstallScriptMode)
{
|