diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2009-05-11 20:05:46 +0000 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2009-05-11 20:05:46 +0000 |
commit | dc934629919bdcaea72c78e5291263914fb958ec (patch) | |
tree | 0ac81213f607d9a3a133481758fa2274fe986aa3 /indra/newview | |
parent | 3800c0df910c83e987184d541b868168fc2b5bec (diff) |
svn merge -r113003:119136 svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-2 svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-3
Diffstat (limited to 'indra/newview')
26 files changed, 1674 insertions, 285 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e1f545adb5..5d79dfbc3e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -37,6 +37,7 @@ include(UI) include(UnixInstall) include(LLKDU) include(ViewerMiscLibs) +include(LLLogin) if (WINDOWS) include(CopyWinLibs) @@ -61,6 +62,7 @@ include_directories( ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile + ${LLLOGIN_INCLUDE_DIRS} ) set(viewer_SOURCE_FILES @@ -231,6 +233,7 @@ set(viewer_SOURCE_FILES lllocationinputctrl.cpp lllogchat.cpp llloginhandler.cpp + lllogininstance.cpp llmanip.cpp llmaniprotate.cpp llmanipscale.cpp @@ -315,7 +318,6 @@ set(viewer_SOURCE_FILES llslurl.cpp llspatialpartition.cpp llsprite.cpp - llsrv.cpp llstartup.cpp llstatusbar.cpp llstylemap.cpp @@ -353,7 +355,6 @@ set(viewer_SOURCE_FILES llurlhistory.cpp llurlsimstring.cpp llurlwhitelist.cpp - lluserauth.cpp llvectorperfoptions.cpp llviewchildren.cpp llviewerassetstorage.cpp @@ -432,6 +433,7 @@ set(viewer_SOURCE_FILES llworld.cpp llworldmap.cpp llworldmapview.cpp + llxmlrpclistener.cpp llxmlrpctransaction.cpp noise.cpp pipeline.cpp @@ -627,6 +629,7 @@ set(viewer_HEADER_FILES lllocationinputctrl.h lllogchat.h llloginhandler.h + lllogininstance.h llmanip.h llmaniprotate.h llmanipscale.h @@ -712,7 +715,6 @@ set(viewer_HEADER_FILES llslurl.h llspatialpartition.h llsprite.h - llsrv.h llstartup.h llstatusbar.h llstylemap.h @@ -752,7 +754,6 @@ set(viewer_HEADER_FILES llurlhistory.h llurlsimstring.h llurlwhitelist.h - lluserauth.h llvectorperfoptions.h llviewchildren.h llviewerassetstorage.h @@ -832,6 +833,7 @@ set(viewer_HEADER_FILES llworld.h llworldmap.h llworldmapview.h + llxmlrpclistener.h llxmlrpctransaction.h macmain.h noise.h @@ -1266,6 +1268,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${LLLOGIN_LIBRARIES} ) build_version(viewer) @@ -1390,3 +1393,5 @@ endif (INSTALL) ADD_VIEWER_BUILD_TEST(llagentaccess viewer) ADD_VIEWER_COMM_BUILD_TEST(llcapabilitylistener viewer ${CMAKE_CURRENT_SOURCE_DIR}/../llmessage/tests/test_llsdmessage_peer.py) +ADD_VIEWER_COMM_BUILD_TEST(llxmlrpclistener viewer + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llxmlrpc_peer.py) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 073b6b85fc..455e987da0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -142,7 +142,6 @@ #include "llfolderview.h" #include "lltoolbar.h" #include "llagentpilot.h" -#include "llsrv.h" #include "llvovolume.h" #include "llflexibleobject.h" #include "llvosurfacepatch.h" @@ -204,9 +203,6 @@ BOOL gAllowTapTapHoldRun = TRUE; BOOL gShowObjectUpdates = FALSE; BOOL gUseQuickTime = TRUE; -BOOL gAcceptTOS = FALSE; -BOOL gAcceptCriticalMessage = FALSE; - eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; LLSD gDebugInfo; diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 536abfae58..a7f1594d0e 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -258,10 +258,6 @@ extern LLSD gDebugInfo; extern BOOL gAllowTapTapHoldRun; extern BOOL gShowObjectUpdates; -extern BOOL gAcceptTOS; -extern BOOL gAcceptCriticalMessage; - - typedef enum { LAST_EXEC_NORMAL = 0, diff --git a/indra/newview/llclassifiedinfo.cpp b/indra/newview/llclassifiedinfo.cpp index 5cf1579d0e..5fcafbeca6 100644 --- a/indra/newview/llclassifiedinfo.cpp +++ b/indra/newview/llclassifiedinfo.cpp @@ -38,35 +38,19 @@ LLClassifiedInfo::cat_map LLClassifiedInfo::sCategories; // static -void LLClassifiedInfo::loadCategories(LLUserAuth::options_t classified_options) +void LLClassifiedInfo::loadCategories(const LLSD& options) { - LLUserAuth::options_t::iterator resp_it; - for (resp_it = classified_options.begin(); - resp_it != classified_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = options.beginArray(), + end = options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; - - LLUserAuth::response_t::const_iterator option_it; - - S32 cat_id = 0; - option_it = response.find("category_id"); - if (option_it != response.end()) + LLSD name = (*resp_it)["category_name"]; + if(name.isDefined()) { - cat_id = atoi(option_it->second.c_str()); + LLSD id = (*resp_it)["category_id"]; + if(id.isDefined()) + { + LLClassifiedInfo::sCategories[id.asInteger()] = name.asString(); + } } - else - { - continue; - } - - // Add the category id/name pair - option_it = response.find("category_name"); - if (option_it != response.end()) - { - LLClassifiedInfo::sCategories[cat_id] = option_it->second; - } - } - } diff --git a/indra/newview/llclassifiedinfo.h b/indra/newview/llclassifiedinfo.h index cc5a6bf28f..37134c7e5b 100644 --- a/indra/newview/llclassifiedinfo.h +++ b/indra/newview/llclassifiedinfo.h @@ -37,7 +37,6 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lluserauth.h" class LLMessageSystem; @@ -46,7 +45,7 @@ class LLClassifiedInfo public: LLClassifiedInfo() {} - static void loadCategories(LLUserAuth::options_t event_options); + static void loadCategories(const LLSD& options); typedef std::map<U32, std::string> cat_map; static cat_map sCategories; diff --git a/indra/newview/lleventinfo.cpp b/indra/newview/lleventinfo.cpp index d4175b6c84..9be45d18fb 100644 --- a/indra/newview/lleventinfo.cpp +++ b/indra/newview/lleventinfo.cpp @@ -87,35 +87,19 @@ void LLEventInfo::unpack(LLMessageSystem *msg) } // static -void LLEventInfo::loadCategories(LLUserAuth::options_t event_options) +void LLEventInfo::loadCategories(const LLSD& options) { - LLUserAuth::options_t::iterator resp_it; - for (resp_it = event_options.begin(); - resp_it != event_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = options.beginArray(), + end = options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; - - LLUserAuth::response_t::const_iterator option_it; - - S32 cat_id = 0; - option_it = response.find("category_id"); - if (option_it != response.end()) + LLSD name = (*resp_it)["category_name"]; + if(name.isDefined()) { - cat_id = atoi(option_it->second.c_str()); + LLSD id = (*resp_it)["category_id"]; + if(id.isDefined()) + { + LLEventInfo::sCategories[id.asInteger()] = name.asString(); + } } - else - { - continue; - } - - // Add the category id/name pair - option_it = response.find("category_name"); - if (option_it != response.end()) - { - LLEventInfo::sCategories[cat_id] = option_it->second; - } - } - } diff --git a/indra/newview/lleventinfo.h b/indra/newview/lleventinfo.h index 880517a9f4..493c659983 100644 --- a/indra/newview/lleventinfo.h +++ b/indra/newview/lleventinfo.h @@ -37,7 +37,6 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lluserauth.h" class LLMessageSystem; @@ -48,7 +47,7 @@ public: void unpack(LLMessageSystem *msg); - static void loadCategories(LLUserAuth::options_t event_options); + static void loadCategories(const LLSD& options); public: std::string mName; diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp index c0fe327815..e54d78de2e 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -95,18 +95,16 @@ void LLEventNotifier::update() } } -void LLEventNotifier::load(const LLUserAuth::options_t& event_options) +void LLEventNotifier::load(const LLSD& event_options) { - LLUserAuth::options_t::const_iterator resp_it; - for (resp_it = event_options.begin(); - resp_it != event_options.end(); - ++resp_it) + for(LLSD::array_const_iterator resp_it = event_options.beginArray(), + end = event_options.endArray(); resp_it != end; ++resp_it) { - const LLUserAuth::response_t& response = *resp_it; + LLSD response = *resp_it; LLEventNotification *new_enp = new LLEventNotification(); - if (!new_enp->load(response)) + if(!new_enp->load(response)) { delete new_enp; continue; @@ -207,49 +205,46 @@ bool LLEventNotification::handleResponse(const LLSD& notification, const LLSD& r return false; } -BOOL LLEventNotification::load(const LLUserAuth::response_t &response) +BOOL LLEventNotification::load(const LLSD& response) { - - LLUserAuth::response_t::const_iterator option_it; BOOL event_ok = TRUE; - option_it = response.find("event_id"); - if (option_it != response.end()) + LLSD option = response.get("event_id"); + if (option.isDefined()) { - mEventID = atoi(option_it->second.c_str()); + mEventID = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("event_name"); - if (option_it != response.end()) + option = response.get("event_name"); + if (option.isDefined()) { - llinfos << "Event: " << option_it->second << llendl; - mEventName = option_it->second; + llinfos << "Event: " << option.asString() << llendl; + mEventName = option.asString(); } else { event_ok = FALSE; } - - option_it = response.find("event_date"); - if (option_it != response.end()) + option = response.get("event_date"); + if (option.isDefined()) { - llinfos << "EventDate: " << option_it->second << llendl; - mEventDateStr = option_it->second; + llinfos << "EventDate: " << option.asString() << llendl; + mEventDateStr = option.asString(); } else { event_ok = FALSE; } - option_it = response.find("event_date_ut"); - if (option_it != response.end()) + option = response.get("event_date_ut"); + if (option.isDefined()) { - llinfos << "EventDate: " << option_it->second << llendl; - mEventDate = strtoul(option_it->second.c_str(), NULL, 10); + llinfos << "EventDate: " << option.asString() << llendl; + mEventDate = strtoul(option.asString().c_str(), NULL, 10); } else { @@ -261,44 +256,44 @@ BOOL LLEventNotification::load(const LLUserAuth::response_t &response) S32 x_region = 0; S32 y_region = 0; - option_it = response.find("grid_x"); - if (option_it != response.end()) + option = response.get("grid_x"); + if (option.isDefined()) { - llinfos << "GridX: " << option_it->second << llendl; - grid_x= atoi(option_it->second.c_str()); + llinfos << "GridX: " << option.asInteger() << llendl; + grid_x= option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("grid_y"); - if (option_it != response.end()) + option = response.get("grid_y"); + if (option.isDefined()) { - llinfos << "GridY: " << option_it->second << llendl; - grid_y = atoi(option_it->second.c_str()); + llinfos << "GridY: " << option.asInteger() << llendl; + grid_y = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("x_region"); - if (option_it != response.end()) + option = response.get("x_region"); + if (option.isDefined()) { - llinfos << "RegionX: " << option_it->second << llendl; - x_region = atoi(option_it->second.c_str()); + llinfos << "RegionX: " << option.asInteger() << llendl; + x_region = option.asInteger(); } else { event_ok = FALSE; } - option_it = response.find("y_region"); - if (option_it != response.end()) + option = response.get("y_region"); + if (option.isDefined()) { - llinfos << "RegionY: " << option_it->second << llendl; - y_region = atoi(option_it->second.c_str()); + llinfos << "RegionY: " << option.asInteger() << llendl; + y_region = option.asInteger(); } else { diff --git a/indra/newview/lleventnotifier.h b/indra/newview/lleventnotifier.h index feb734948c..6fdde87646 100644 --- a/indra/newview/lleventnotifier.h +++ b/indra/newview/lleventnotifier.h @@ -34,7 +34,6 @@ #define LL_LLEVENTNOTIFIER_H #include "llframetimer.h" -#include "lluserauth.h" #include "v3dmath.h" class LLEventInfo; @@ -49,7 +48,7 @@ public: void update(); // Notify the user of the event if it's coming up - void load(const LLUserAuth::options_t& event_options); // In the format that it comes in from LLUserAuth + void load(const LLSD& event_options); // In the format that it comes in from login void add(LLEventInfo &event_info); // Add a new notification for an event void remove(U32 event_id); @@ -69,7 +68,7 @@ public: LLEventNotification(); virtual ~LLEventNotification(); - BOOL load(const LLUserAuth::response_t &en); // In the format it comes in from LLUserAuth + BOOL load(const LLSD& en); // In the format it comes in from login BOOL load(const LLEventInfo &event_info); // From existing event_info on the viewer. //void setEventID(const U32 event_id); //void setEventName(std::string &event_name); diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index 764a6a3498..c79e96a5e5 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -36,8 +36,6 @@ // viewer includes #include "llagent.h" -#include "llappviewer.h" -#include "llstartup.h" #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerwindow.h" @@ -58,11 +56,13 @@ LLFloaterTOS* LLFloaterTOS::sInstance = NULL; // static -LLFloaterTOS* LLFloaterTOS::show(ETOSType type, const std::string & message) +LLFloaterTOS* LLFloaterTOS::show(ETOSType type, + const std::string & message, + const YesNoCallback& callback) { if( !LLFloaterTOS::sInstance ) { - LLFloaterTOS::sInstance = new LLFloaterTOS(type, message); + LLFloaterTOS::sInstance = new LLFloaterTOS(type, message, callback); } if (type == TOS_TOS) @@ -78,12 +78,15 @@ LLFloaterTOS* LLFloaterTOS::show(ETOSType type, const std::string & message) } -LLFloaterTOS::LLFloaterTOS(ETOSType type, const std::string & message) +LLFloaterTOS::LLFloaterTOS(ETOSType type, + const std::string & message, + const YesNoCallback& callback) : LLModalDialog( std::string(" "), 100, 100 ), mType(type), mMessage(message), mWebBrowserWindowId( 0 ), - mLoadCompleteCount( 0 ) + mLoadCompleteCount( 0 ), + mCallback(callback) { } @@ -235,25 +238,12 @@ void LLFloaterTOS::onContinue( void* userdata ) { LLFloaterTOS* self = (LLFloaterTOS*) userdata; llinfos << "User agrees with TOS." << llendl; - if (self->mType == TOS_TOS) - { - gAcceptTOS = TRUE; - } - else - { - gAcceptCriticalMessage = TRUE; - } - // Testing TOS dialog - #if ! LL_RELEASE_FOR_DOWNLOAD - if ( LLStartUp::getStartupState() == STATE_LOGIN_WAIT ) + if(self->mCallback) { - LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + self->mCallback(true); } - else - #endif - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); // Go back and finish authentication self->closeFloater(); // destroys this object } @@ -262,8 +252,12 @@ void LLFloaterTOS::onCancel( void* userdata ) { LLFloaterTOS* self = (LLFloaterTOS*) userdata; llinfos << "User disagrees with TOS." << llendl; - LLNotifications::instance().add("MustAgreeToLogIn", LLSD(), LLSD(), login_alert_done); - LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + + if(self->mCallback) + { + self->mCallback(false); + } + self->mLoadCompleteCount = 0; // reset counter for next time we come to TOS self->closeFloater(); // destroys this object } diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index dbec3ff8b6..67d2f0ceec 100644 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -36,6 +36,7 @@ #include "llmodaldialog.h" #include "llassetstorage.h" #include "llwebbrowserctrl.h" +#include <boost/function.hpp> class LLButton; class LLRadioGroup; @@ -57,8 +58,12 @@ public: TOS_CRITICAL_MESSAGE = 1 }; + typedef boost::function<void(bool)> YesNoCallback; + // Asset_id is overwritten with LLUUID::null when agree is clicked. - static LLFloaterTOS* show(ETOSType type, const std::string & message); + static LLFloaterTOS* show(ETOSType type, + const std::string & message, + const YesNoCallback& callback); BOOL postBuild(); @@ -74,13 +79,16 @@ public: private: // Asset_id is overwritten with LLUUID::null when agree is clicked. - LLFloaterTOS(ETOSType type, const std::string & message); + LLFloaterTOS(ETOSType type, + const std::string & message, + const YesNoCallback& callback); private: ETOSType mType; std::string mMessage; int mWebBrowserWindowId; int mLoadCompleteCount; + YesNoCallback mCallback; static LLFloaterTOS* sInstance; }; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 1176bf8735..4e2bb3e2e9 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1876,63 +1876,56 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const } bool LLInventoryModel::loadSkeleton( - const LLInventoryModel::options_t& options, + const LLSD& options, const LLUUID& owner_id) { lldebugs << "importing inventory skeleton for " << owner_id << llendl; typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t; cat_set_t temp_cats; + bool rv = true; - update_map_t child_counts; + for(LLSD::array_const_iterator it = options.beginArray(), + end = options.endArray(); it != end; ++it) + { + LLSD name = (*it)["name"]; + LLSD folder_id = (*it)["folder_id"]; + LLSD parent_id = (*it)["parent_id"]; + LLSD version = (*it)["version"]; + if(name.isDefined() + && folder_id.isDefined() + && parent_id.isDefined() + && version.isDefined() + && folder_id.asUUID().notNull() // if an id is null, it locks the viewer. + ) + { + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); + cat->rename(name.asString()); + cat->setUUID(folder_id.asUUID()); + cat->setParent(parent_id.asUUID()); - LLUUID id; - LLAssetType::EType preferred_type; - bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator skel; - skel = (*it).find("name"); - if(skel == no_response) goto clean_cat; - cat->rename(std::string((*skel).second)); - skel = (*it).find("folder_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - // if an id is null, it locks the viewer. - if(id.isNull()) goto clean_cat; - cat->setUUID(id); - skel = (*it).find("parent_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - cat->setParent(id); - skel = (*it).find("type_default"); - if(skel == no_response) - { - preferred_type = LLAssetType::AT_NONE; + LLAssetType::EType preferred_type = LLAssetType::AT_NONE; + LLSD type_default = (*it)["type_default"]; + if(type_default.isDefined()) + { + preferred_type = (LLAssetType::EType)type_default.asInteger(); + } + cat->setPreferredType(preferred_type); + cat->setVersion(version.asInteger()); + temp_cats.insert(cat); } else { - S32 t = atoi((*skel).second.c_str()); - preferred_type = (LLAssetType::EType)t; + llwarns << "Unable to import near " << name.asString() << llendl; + rv = false; } - cat->setPreferredType(preferred_type); - skel = (*it).find("version"); - if(skel == no_response) goto clean_cat; - cat->setVersion(atoi((*skel).second.c_str())); - temp_cats.insert(cat); - continue; - clean_cat: - llwarns << "Unable to import near " << cat->getName() << llendl; - rv = false; - //delete cat; // automatic when cat is reasigned or destroyed } S32 cached_category_count = 0; S32 cached_item_count = 0; if(!temp_cats.empty()) { + update_map_t child_counts; cat_array_t categories; item_array_t items; std::string owner_id_str; @@ -1961,6 +1954,7 @@ bool LLInventoryModel::loadSkeleton( llinfos << "Unable to gunzip " << gzip_filename << llendl; } } + if(loadFromFile(inventory_filename, categories, items)) { // We were able to find a cache of files. So, use what we @@ -2085,85 +2079,84 @@ bool LLInventoryModel::loadSkeleton( return rv; } -bool LLInventoryModel::loadMeat( - const LLInventoryModel::options_t& options, const LLUUID& owner_id) +bool LLInventoryModel::loadMeat(const LLSD& options, const LLUUID& owner_id) { llinfos << "importing inventory for " << owner_id << llendl; - LLPermissions default_perm; - default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); - LLPointer<LLViewerInventoryItem> item; - LLUUID id; - LLAssetType::EType type; - LLInventoryType::EType inv_type; bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - item = new LLViewerInventoryItem; - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator meat; - meat = (*it).find("name"); - if(meat == no_response) goto clean_item; - item->rename(std::string((*meat).second)); - meat = (*it).find("item_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setUUID(id); - meat = (*it).find("parent_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setParent(id); - meat = (*it).find("type"); - if(meat == no_response) goto clean_item; - type = (LLAssetType::EType)atoi((*meat).second.c_str()); - item->setType(type); - meat = (*it).find("inv_type"); - if(meat != no_response) - { - inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str()); - item->setInventoryType(inv_type); - } - meat = (*it).find("data_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - if(LLAssetType::AT_CALLINGCARD == type) - { - LLPermissions perm; - perm.init(id, owner_id, LLUUID::null, LLUUID::null); - item->setPermissions(perm); - } - else + for(LLSD::array_const_iterator it = options.beginArray(), + end = options.endArray(); it != end; ++it) + { + LLSD name = (*it)["name"]; + LLSD item_id = (*it)["item_id"]; + LLSD parent_id = (*it)["parent_id"]; + LLSD asset_type = (*it)["type"]; + LLSD data_id = (*it)["data_id"]; + if(name.isDefined() + && item_id.isDefined() + && parent_id.isDefined() + && asset_type.isDefined() + && data_id.isDefined()) { - meat = (*it).find("perm_mask"); - if(meat != no_response) + LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem; + item->rename(name.asString()); + item->setUUID(item_id.asUUID()); + item->setParent(parent_id.asUUID()); + LLAssetType::EType type = (LLAssetType::EType)asset_type.asInteger(); + item->setType(type); + + LLSD llsd_inv_type = (*it)["inv_type"]; + if(llsd_inv_type.isDefined()) { - PermissionMask perm_mask = atoi((*meat).second.c_str()); - default_perm.initMasks( - perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); + LLInventoryType::EType inv_type = (LLInventoryType::EType)llsd_inv_type.asInteger(); + item->setInventoryType(inv_type); + } + + if(LLAssetType::AT_CALLINGCARD == type) + { + LLPermissions perm; + perm.init(data_id.asUUID(), owner_id, LLUUID::null, LLUUID::null); + item->setPermissions(perm); } else { - default_perm.initMasks( - PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); + LLPermissions default_perm; + default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); + LLSD llsd_perm_mask = (*it)["perm_mask"]; + if(llsd_perm_mask.isDefined()) + { + PermissionMask perm_mask = llsd_perm_mask.asInteger(); + default_perm.initMasks( + perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); + } + else + { + default_perm.initMasks( + PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); + } + item->setPermissions(default_perm); + item->setAssetUUID(data_id.asUUID()); } - item->setPermissions(default_perm); - item->setAssetUUID(id); - } - meat = (*it).find("flags"); - if(meat != no_response) - { - item->setFlags(strtoul((*meat).second.c_str(), NULL, 0)); + + LLSD flags = (*it)["flags"]; + if(flags.isDefined()) + { + // Not sure how well LLSD.asInteger() maps to + // unsigned long - using strtoul() + item->setFlags(strtoul(flags.asString().c_str(), NULL, 0)); + } + + LLSD time = (*it)["time"]; + if(time.isDefined()) + { + item->setCreationDate(time.asInteger()); + } + addItem(item); } - meat = (*it).find("time"); - if(meat != no_response) + else { - item->setCreationDate(atoi((*meat).second.c_str())); + llwarns << "Unable to import near " << name.asString() << llendl; + rv = false; } - addItem(item); - continue; - clean_item: - llwarns << "Unable to import near " << item->getName() << llendl; - rv = false; - //delete item; // automatic when item is reassigned or destroyed } return rv; } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index d73fef7207..fcb3cc737a 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -314,10 +314,8 @@ public: // methods to load up inventory skeleton & meat. These are used // during authentication. return true if everything parsed. - typedef std::map<std::string, std::string> response_t; - typedef std::vector<response_t> options_t; - bool loadSkeleton(const options_t& options, const LLUUID& owner_id); - bool loadMeat(const options_t& options, const LLUUID& owner_id); + bool loadSkeleton(const LLSD& options, const LLUUID& owner_id); + bool loadMeat(const LLSD& options, const LLUUID& owner_id); // This is a brute force method to rebuild the entire parent-child // relations. diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp new file mode 100644 index 0000000000..388bf38d61 --- /dev/null +++ b/indra/newview/lllogininstance.cpp @@ -0,0 +1,532 @@ +/** + * @file lllogininstance.cpp + * @brief Viewer's host for a login connection. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lllogininstance.h" + +// llcommon +#include "llevents.h" +#include "llmd5.h" +#include "stringize.h" + +// llmessage (!) +#include "llfiltersd2xmlrpc.h" // for xml_escape_string() + +// login +#include "lllogin.h" + +// newview +#include "llviewernetwork.h" +#include "llappviewer.h" // Wish I didn't have to, but... +#include "llviewercontrol.h" +#include "llurlsimstring.h" +#include "llfloatertos.h" +#include "llwindow.h" + +std::string construct_start_string(); + +LLLoginInstance::LLLoginInstance() : + mLoginModule(new LLLogin()), + mLoginState("offline"), + mUserInteraction(true), + mSkipOptionalUpdate(false), + mAttemptComplete(false), + mTransferRate(0.0f) +{ + mLoginModule->getEventPump().listen("lllogininstance", + boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); +} + +LLLoginInstance::~LLLoginInstance() +{ +} + + +void LLLoginInstance::connect(const LLSD& credentials) +{ + std::vector<std::string> uris; + LLViewerLogin::getInstance()->getLoginURIs(uris); + connect(uris.front(), credentials); +} + +void LLLoginInstance::connect(const std::string& uri, const LLSD& credentials) +{ + constructAuthParams(credentials); + mLoginModule->connect(uri, mRequestData); +} + +void LLLoginInstance::reconnect() +{ + // Sort of like connect, only using the pre-existing + // request params. + std::vector<std::string> uris; + LLViewerLogin::getInstance()->getLoginURIs(uris); + mLoginModule->connect(uris.front(), mRequestData); +} + +void LLLoginInstance::disconnect() +{ + mRequestData.clear(); + mLoginModule->disconnect(); +} + +LLSD LLLoginInstance::getResponse() +{ + return mResponseData; +} + +void LLLoginInstance::constructAuthParams(const LLSD& credentials) +{ + // Set up auth request options. +//#define LL_MINIMIAL_REQUESTED_OPTIONS + LLSD requested_options; + // *Note: this is where gUserAuth used to be created. + requested_options.append("inventory-root"); + requested_options.append("inventory-skeleton"); + //requested_options.append("inventory-meat"); + //requested_options.append("inventory-skel-targets"); +#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS) + if(FALSE == gSavedSettings.getBOOL("NoInventoryLibrary")) + { + requested_options.append("inventory-lib-root"); + requested_options.append("inventory-lib-owner"); + requested_options.append("inventory-skel-lib"); + // requested_options.append("inventory-meat-lib"); + } + + requested_options.append("initial-outfit"); + requested_options.append("gestures"); + requested_options.append("event_categories"); + requested_options.append("event_notifications"); + requested_options.append("classified_categories"); + //requested_options.append("inventory-targets"); + requested_options.append("buddy-list"); + requested_options.append("ui-config"); +#endif + requested_options.append("tutorial_setting"); + requested_options.append("login-flags"); + requested_options.append("global-textures"); + if(gSavedSettings.getBOOL("ConnectAsGod")) + { + gSavedSettings.setBOOL("UseDebugMenus", TRUE); + requested_options.append("god-connect"); + } + + char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ + LLMD5 hashed_mac; + hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); + hashed_mac.finalize(); + hashed_mac.hex_digest(hashed_mac_string); + + // prepend "$1$" to the password to indicate its the md5'd version. + std::string dpasswd("$1$"); + dpasswd.append(credentials["passwd"].asString()); + + // (re)initialize the request params with creds. + LLSD request_params(credentials); + request_params["passwd"] = dpasswd; + request_params["start"] = construct_start_string(); + request_params["skipoptional"] = mSkipOptionalUpdate; + request_params["agree_to_tos"] = false; // Always false here. Set true in + request_params["read_critical"] = false; // handleTOSResponse + request_params["last_exec_event"] = gLastExecEvent; + request_params["mac"] = hashed_mac_string; + request_params["version"] = gCurrentVersion; // Includes channel name + request_params["channel"] = gSavedSettings.getString("VersionChannelName"); + request_params["id0"] = LLAppViewer::instance()->getSerialNumber(); + + mRequestData["method"] = "login_to_simulator"; + mRequestData["params"] = request_params; + mRequestData["options"] = requested_options; +} + +bool LLLoginInstance::handleLoginEvent(const LLSD& event) +{ + std::cout << "LoginListener called!: \n"; + std::cout << event << "\n"; + + if(!(event.has("state") && event.has("progress"))) + { + llerrs << "Unknown message from LLLogin!" << llendl; + } + + mLoginState = event["state"].asString(); + mResponseData = event["data"]; + + if(event.has("transfer_rate")) + { + mTransferRate = event["transfer_rate"].asReal(); + } + + if(mLoginState == "offline") + { + handleLoginFailure(event); + } + else if(mLoginState == "online") + { + handleLoginSuccess(event); + } + + return false; +} + +bool LLLoginInstance::handleLoginFailure(const LLSD& event) +{ + // Login has failed. + // Figure out why and respond... + LLSD response = event["data"]; + std::string reason_response = response["reason"].asString(); + std::string message_response = response["message"].asString(); + if(mUserInteraction) + { + // For the cases of critical message or TOS agreement, + // start the TOS dialog. The dialog response will be handled + // by the LLLoginInstance::handleTOSResponse() callback. + // The callback intiates the login attempt next step, either + // to reconnect or to end the attempt in failure. + if(reason_response == "tos") + { + LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS, + message_response, + boost::bind(&LLLoginInstance::handleTOSResponse, + this, _1, "agree_to_tos") + ); + tos_dialog->startModal(); + } + else if(reason_response == "critical") + { + LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_CRITICAL_MESSAGE, + message_response, + boost::bind(&LLLoginInstance::handleTOSResponse, + this, _1, "read_critical") + ); + tos_dialog->startModal(); + } + else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate")) + { + gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); + updateApp(true, message_response); + } + else if(reason_response == "optional") + { + updateApp(false, message_response); + } + else + { + attemptComplete(); + } + } + else // no user interaction + { + attemptComplete(); + } + + return false; +} + +bool LLLoginInstance::handleLoginSuccess(const LLSD& event) +{ + LLSD response = event["data"]; + std::string message_response = response["message"].asString(); + if(gSavedSettings.getBOOL("ForceMandatoryUpdate")) + { + // Testing update... + gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); + // Don't confuse startup by leaving login "online". + mLoginModule->disconnect(); + updateApp(true, message_response); + } + else + { + attemptComplete(); + } + return false; +} + +void LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) +{ + if(accepted) + { + // Set the request data to true and retry login. + mRequestData[key] = true; + reconnect(); + } + else + { + attemptComplete(); + } +} + + +void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg) +{ + // store off config state, as we might quit soon + gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); + + std::ostringstream message; + + //*TODO:translate + std::string msg; + if (!auth_msg.empty()) + { + msg = "(" + auth_msg + ") \n"; + } + + LLSD args; + args["MESSAGE"] = msg; + + LLSD payload; + payload["mandatory"] = mandatory; + +/* + We're constructing one of the following 6 strings here: + "DownloadWindowsMandatory" + "DownloadWindowsReleaseForDownload" + "DownloadWindows" + "DownloadMacMandatory" + "DownloadMacReleaseForDownload" + "DownloadMac" + + I've called them out explicitly in this comment so that they can be grepped for. + + Also, we assume that if we're not Windows we're Mac. If we ever intend to support + Linux with autoupdate, this should be an explicit #elif LL_DARWIN, but + we'd rather deliver the wrong message than no message, so until Linux is supported + we'll leave it alone. + */ + std::string notification_name = "Download"; + +#if LL_WINDOWS + notification_name += "Windows"; +#else + notification_name += "Mac"; +#endif + + if (mandatory) + { + notification_name += "Mandatory"; + } + else + { +#if LL_RELEASE_FOR_DOWNLOAD + notification_name += "ReleaseForDownload"; +#endif + } + + LLNotifications::instance().add(notification_name, args, payload, + boost::bind(&LLLoginInstance::updateDialogCallback, this, _1, _2)); +} + +bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + std::string update_exe_path; + bool mandatory = notification["payload"]["mandatory"].asBoolean(); + +#if !LL_RELEASE_FOR_DOWNLOAD + if (option == 2) + { + // This condition attempts to skip the + // update if using a dev build. + // The relog probably won't work if the + // update is mandatory. :) + + // *REMOVE:Mani - Saving for reference... + //LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); + mSkipOptionalUpdate = true; + reconnect(); + return false; + } +#endif + + if (option == 1) + { + // ...user doesn't want to do it + if (mandatory) + { + // Mandatory update, user chose to not to update... + // The login attemp is complete, startup should + // quit when detecting this. + attemptComplete(); + + // *REMOVE:Mani - Saving for reference... + //LLAppViewer::instance()->forceQuit(); + // // Bump them back to the login screen. + // //reset_login(); + } + else + { + // Optional update, user chose to skip + mSkipOptionalUpdate = true; + reconnect(); + } + return false; + } + + LLSD query_map = LLSD::emptyMap(); + // *TODO place os string in a global constant +#if LL_WINDOWS + query_map["os"] = "win"; +#elif LL_DARWIN + query_map["os"] = "mac"; +#elif LL_LINUX + query_map["os"] = "lnx"; +#elif LL_SOLARIS + query_map["os"] = "sol"; +#endif + // *TODO change userserver to be grid on both viewer and sim, since + // userserver no longer exists. + query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); + query_map["channel"] = gSavedSettings.getString("VersionChannelName"); + // *TODO constantize this guy + LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); + + if(LLAppViewer::sUpdaterInfo) + { + delete LLAppViewer::sUpdaterInfo; + } + LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; + +#if LL_WINDOWS + LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); + if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + // We're hosed, bail + LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; + + attemptComplete(); + // *REMOVE:Mani - Saving for reference... + // LLAppViewer::instance()->forceQuit(); + return false; + } + + LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; + + std::string updater_source = gDirUtilp->getAppRODataDir(); + updater_source += gDirUtilp->getDirDelimiter(); + updater_source += "updater.exe"; + + LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source + << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath + << LL_ENDL; + + + if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; + attemptComplete(); + // *REMOVE:Mani - Saving for reference... + // LLAppViewer::instance()->forceQuit(); + return false; + } + + // if a sim name was passed in via command line parameter (typically through a SLURL) + if ( LLURLSimString::sInstance.mSimString.length() ) + { + // record the location to start at next time + gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + }; + + LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; + + //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. + LLAppViewer::instance()->removeMarkerFile(); // In case updater fails + + // *NOTE:Mani The updater is spawned as the last thing before the WinMain exit. + // see LLAppViewerWin32.cpp + +#elif LL_DARWIN + // if a sim name was passed in via command line parameter (typically through a SLURL) + if ( LLURLSimString::sInstance.mSimString.length() ) + { + // record the location to start at next time + gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + }; + + LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; + LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; + + // Run the auto-updater. + system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ + +#elif LL_LINUX || LL_SOLARIS + OSMessageBox("Automatic updating is not yet implemented for Linux.\n" + "Please download the latest version from www.secondlife.com.", + LLStringUtil::null, OSMB_OK); +#endif + + // *REMOVE:Mani - Saving for reference... + // LLAppViewer::instance()->forceQuit(); + + return false; +} + +std::string construct_start_string() +{ + std::string start; + if (LLURLSimString::parse()) + { + // a startup URL was specified + std::string unescaped_start = + STRINGIZE( "uri:" + << LLURLSimString::sInstance.mSimName << "&" + << LLURLSimString::sInstance.mX << "&" + << LLURLSimString::sInstance.mY << "&" + << LLURLSimString::sInstance.mZ); + start = xml_escape_string(unescaped_start); + } + else if (gSavedSettings.getBOOL("LoginLastLocation")) + { + start = "last"; + } + else + { + start = "home"; + } + return start; +} diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h new file mode 100644 index 0000000000..da70fec40e --- /dev/null +++ b/indra/newview/lllogininstance.h @@ -0,0 +1,95 @@ +/** + * @file lllogininstance.h + * @brief A host for the viewer's login connection. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLLOGININSTANCE_H +#define LL_LLLOGININSTANCE_H + +#include <boost/scoped_ptr.hpp> +class LLLogin; + +// This class hosts the login module and is used to +// negotiate user authentication attempts. +class LLLoginInstance : public LLSingleton<LLLoginInstance> +{ +public: + LLLoginInstance(); + ~LLLoginInstance(); + + void connect(const LLSD& credential); // Connect to the current grid choice. + void connect(const std::string& uri, const LLSD& credential); // Connect to the given uri. + void reconnect(); // reconnect using the current credentials. + void disconnect(); + + // Set whether this class will drive user interaction. + // If not, login failures like 'need tos agreement' will + // end the login attempt. + void setUserInteraction(bool state) { mUserInteraction = state; } + bool getUserInteraction() { return mUserInteraction; } + + // Whether to tell login to skip optional update request. + // False by default. + void setSkipOptionalUpdate(bool state) { mSkipOptionalUpdate = state; } + + bool authFailure() { return mAttemptComplete && mLoginState == "offline"; } + bool authSuccess() { return mAttemptComplete && mLoginState == "online"; } + + const std::string& getLoginState() { return mLoginState; } + LLSD getResponse(const std::string& key) { return getResponse()[key]; } + LLSD getResponse(); + + // Only valid when authSuccess == true. + const F64 getLastTransferRateBPS() { return mTransferRate; } + +private: + void constructAuthParams(const LLSD& credentials); + void updateApp(bool mandatory, const std::string& message); + bool updateDialogCallback(const LLSD& notification, const LLSD& response); + + bool handleLoginEvent(const LLSD& event); + bool handleLoginFailure(const LLSD& event); + bool handleLoginSuccess(const LLSD& event); + + void handleTOSResponse(bool v, const std::string& key); + + void attemptComplete() { mAttemptComplete = true; } // In the future an event? + + boost::scoped_ptr<LLLogin> mLoginModule; + std::string mLoginState; + LLSD mRequestData; + LLSD mResponseData; + bool mUserInteraction; + bool mSkipOptionalUpdate; + bool mAttemptComplete; + F64 mTransferRate; +}; + +#endif diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 671d3264bb..06c78a93da 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -434,7 +434,7 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) if ( KEY_F2 == key ) { llinfos << "Spawning floater TOS window" << llendl; - LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,""); + LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"", NULL); tos_dialog->startModal(); return TRUE; } diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 93701800e9..5e89030a01 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -50,11 +50,7 @@ typedef enum { STATE_LOGIN_SHOW, // Show login screen STATE_LOGIN_WAIT, // Wait for user input at login screen STATE_LOGIN_CLEANUP, // Get rid of login screen and start login - STATE_UPDATE_CHECK, // Wait for user at a dialog box (updates, term-of-service, etc) STATE_LOGIN_AUTH_INIT, // Start login to SL servers - STATE_LOGIN_AUTHENTICATE, // Do authentication voodoo - STATE_LOGIN_NO_DATA_YET, // Waiting for authentication replies to start - STATE_LOGIN_DOWNLOADING, // Waiting for authentication replies to download STATE_LOGIN_PROCESS_RESPONSE, // Check authentication reply STATE_WORLD_INIT, // Start building the world STATE_MULTIMEDIA_INIT, // Init the rest of multimedia library @@ -75,8 +71,6 @@ typedef enum { // exported symbols extern bool gAgentMovementCompleted; extern LLPointer<LLImageGL> gStartImageGL; -extern std::string gInitialOutfit; -extern std::string gInitialOutfitGender; // "male" or "female" class LLStartUp { diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index f70e5ad242..5647b6889b 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -177,7 +177,6 @@ #include "lltrans.h" #include "lluictrlfactory.h" #include "lluploaddialog.h" -#include "lluserauth.h" #include "lluuid.h" #include "llviewercamera.h" #include "llviewergenericmessage.h" diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 918b15ef09..801c46035a 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -35,6 +35,8 @@ #include "llviewernetwork.h" #include "llviewercontrol.h" +#include "llevents.h" +#include "lllogin.h" struct LLGridData { @@ -155,6 +157,10 @@ LLViewerLogin::LLViewerLogin() : { } + LLViewerLogin::~LLViewerLogin() + { + } + void LLViewerLogin::setGridChoice(EGridInfo grid) { if(grid < 0 || grid >= GRID_INFO_COUNT) diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 4001ed05c1..edae6dc47b 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -34,7 +34,10 @@ #ifndef LL_LLVIEWERNETWORK_H #define LL_LLVIEWERNETWORK_H +#include <boost/scoped_ptr.hpp> + class LLHost; +class LLLogin; enum EGridInfo { @@ -74,6 +77,7 @@ class LLViewerLogin : public LLSingleton<LLViewerLogin> { public: LLViewerLogin(); + ~LLViewerLogin(); void setGridChoice(EGridInfo grid); void setGridChoice(const std::string& grid_name); diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp new file mode 100644 index 0000000000..2821e6c59f --- /dev/null +++ b/indra/newview/llxmlrpclistener.cpp @@ -0,0 +1,494 @@ +/** + * @file llxmlrpclistener.cpp + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief Implementation for llxmlrpclistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llxmlrpclistener.h" +// STL headers +#include <map> +#include <set> +// std headers +// external library headers +#include <boost/scoped_ptr.hpp> +#include <boost/range.hpp> // boost::begin(), boost::end() +// other Linden headers +#include "llerror.h" +#include "stringize.h" +#include "llxmlrpctransaction.h" + +#include <xmlrpc-epi/xmlrpc.h> + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +template <typename STATUS> +class StatusMapperBase +{ + typedef std::map<STATUS, std::string> MapType; + +public: + StatusMapperBase(const std::string& desc): + mDesc(desc) + {} + + std::string lookup(STATUS status) const + { + typename MapType::const_iterator found = mMap.find(status); + if (found != mMap.end()) + { + return found->second; + } + return STRINGIZE("<unknown " << mDesc << " " << status << ">"); + } + +protected: + std::string mDesc; + MapType mMap; +}; + +class StatusMapper: public StatusMapperBase<LLXMLRPCTransaction::Status> +{ +public: + StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::Status>("Status") + { + mMap[LLXMLRPCTransaction::StatusNotStarted] = "NotStarted"; + mMap[LLXMLRPCTransaction::StatusStarted] = "Started"; + mMap[LLXMLRPCTransaction::StatusDownloading] = "Downloading"; + mMap[LLXMLRPCTransaction::StatusComplete] = "Complete"; + mMap[LLXMLRPCTransaction::StatusCURLError] = "CURLError"; + mMap[LLXMLRPCTransaction::StatusXMLRPCError] = "XMLRPCError"; + mMap[LLXMLRPCTransaction::StatusOtherError] = "OtherError"; + } +}; + +static const StatusMapper sStatusMapper; + +class CURLcodeMapper: public StatusMapperBase<CURLcode> +{ +public: + CURLcodeMapper(): StatusMapperBase<CURLcode>("CURLcode") + { + // from curl.h +// skip the "CURLE_" prefix for each of these strings +#define def(sym) (mMap[sym] = #sym + 6) + def(CURLE_OK); + def(CURLE_UNSUPPORTED_PROTOCOL); /* 1 */ + def(CURLE_FAILED_INIT); /* 2 */ + def(CURLE_URL_MALFORMAT); /* 3 */ + def(CURLE_URL_MALFORMAT_USER); /* 4 - NOT USED */ + def(CURLE_COULDNT_RESOLVE_PROXY); /* 5 */ + def(CURLE_COULDNT_RESOLVE_HOST); /* 6 */ + def(CURLE_COULDNT_CONNECT); /* 7 */ + def(CURLE_FTP_WEIRD_SERVER_REPLY); /* 8 */ + def(CURLE_FTP_ACCESS_DENIED); /* 9 a service was denied by the FTP server + due to lack of access - when login fails + this is not returned. */ + def(CURLE_FTP_USER_PASSWORD_INCORRECT); /* 10 - NOT USED */ + def(CURLE_FTP_WEIRD_PASS_REPLY); /* 11 */ + def(CURLE_FTP_WEIRD_USER_REPLY); /* 12 */ + def(CURLE_FTP_WEIRD_PASV_REPLY); /* 13 */ + def(CURLE_FTP_WEIRD_227_FORMAT); /* 14 */ + def(CURLE_FTP_CANT_GET_HOST); /* 15 */ + def(CURLE_FTP_CANT_RECONNECT); /* 16 */ + def(CURLE_FTP_COULDNT_SET_BINARY); /* 17 */ + def(CURLE_PARTIAL_FILE); /* 18 */ + def(CURLE_FTP_COULDNT_RETR_FILE); /* 19 */ + def(CURLE_FTP_WRITE_ERROR); /* 20 */ + def(CURLE_FTP_QUOTE_ERROR); /* 21 */ + def(CURLE_HTTP_RETURNED_ERROR); /* 22 */ + def(CURLE_WRITE_ERROR); /* 23 */ + def(CURLE_MALFORMAT_USER); /* 24 - NOT USED */ + def(CURLE_UPLOAD_FAILED); /* 25 - failed upload "command" */ + def(CURLE_READ_ERROR); /* 26 - could open/read from file */ + def(CURLE_OUT_OF_MEMORY); /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + def(CURLE_OPERATION_TIMEOUTED); /* 28 - the timeout time was reached */ + def(CURLE_FTP_COULDNT_SET_ASCII); /* 29 - TYPE A failed */ + def(CURLE_FTP_PORT_FAILED); /* 30 - FTP PORT operation failed */ + def(CURLE_FTP_COULDNT_USE_REST); /* 31 - the REST command failed */ + def(CURLE_FTP_COULDNT_GET_SIZE); /* 32 - the SIZE command failed */ + def(CURLE_HTTP_RANGE_ERROR); /* 33 - RANGE "command" didn't work */ + def(CURLE_HTTP_POST_ERROR); /* 34 */ + def(CURLE_SSL_CONNECT_ERROR); /* 35 - wrong when connecting with SSL */ + def(CURLE_BAD_DOWNLOAD_RESUME); /* 36 - couldn't resume download */ + def(CURLE_FILE_COULDNT_READ_FILE); /* 37 */ + def(CURLE_LDAP_CANNOT_BIND); /* 38 */ + def(CURLE_LDAP_SEARCH_FAILED); /* 39 */ + def(CURLE_LIBRARY_NOT_FOUND); /* 40 */ + def(CURLE_FUNCTION_NOT_FOUND); /* 41 */ + def(CURLE_ABORTED_BY_CALLBACK); /* 42 */ + def(CURLE_BAD_FUNCTION_ARGUMENT); /* 43 */ + def(CURLE_BAD_CALLING_ORDER); /* 44 - NOT USED */ + def(CURLE_INTERFACE_FAILED); /* 45 - CURLOPT_INTERFACE failed */ + def(CURLE_BAD_PASSWORD_ENTERED); /* 46 - NOT USED */ + def(CURLE_TOO_MANY_REDIRECTS ); /* 47 - catch endless re-direct loops */ + def(CURLE_UNKNOWN_TELNET_OPTION); /* 48 - User specified an unknown option */ + def(CURLE_TELNET_OPTION_SYNTAX ); /* 49 - Malformed telnet option */ + def(CURLE_OBSOLETE); /* 50 - NOT USED */ + def(CURLE_SSL_PEER_CERTIFICATE); /* 51 - peer's certificate wasn't ok */ + def(CURLE_GOT_NOTHING); /* 52 - when this is a specific error */ + def(CURLE_SSL_ENGINE_NOTFOUND); /* 53 - SSL crypto engine not found */ + def(CURLE_SSL_ENGINE_SETFAILED); /* 54 - can not set SSL crypto engine as + default */ + def(CURLE_SEND_ERROR); /* 55 - failed sending network data */ + def(CURLE_RECV_ERROR); /* 56 - failure in receiving network data */ + def(CURLE_SHARE_IN_USE); /* 57 - share is in use */ + def(CURLE_SSL_CERTPROBLEM); /* 58 - problem with the local certificate */ + def(CURLE_SSL_CIPHER); /* 59 - couldn't use specified cipher */ + def(CURLE_SSL_CACERT); /* 60 - problem with the CA cert (path?) */ + def(CURLE_BAD_CONTENT_ENCODING); /* 61 - Unrecognized transfer encoding */ + def(CURLE_LDAP_INVALID_URL); /* 62 - Invalid LDAP URL */ + def(CURLE_FILESIZE_EXCEEDED); /* 63 - Maximum file size exceeded */ + def(CURLE_FTP_SSL_FAILED); /* 64 - Requested FTP SSL level failed */ + def(CURLE_SEND_FAIL_REWIND); /* 65 - Sending the data requires a rewind + that failed */ + def(CURLE_SSL_ENGINE_INITFAILED); /* 66 - failed to initialise ENGINE */ + def(CURLE_LOGIN_DENIED); /* 67 - user); password or similar was not + accepted and we failed to login */ + def(CURLE_TFTP_NOTFOUND); /* 68 - file not found on server */ + def(CURLE_TFTP_PERM); /* 69 - permission problem on server */ + def(CURLE_TFTP_DISKFULL); /* 70 - out of disk space on server */ + def(CURLE_TFTP_ILLEGAL); /* 71 - Illegal TFTP operation */ + def(CURLE_TFTP_UNKNOWNID); /* 72 - Unknown transfer ID */ + def(CURLE_TFTP_EXISTS); /* 73 - File already exists */ + def(CURLE_TFTP_NOSUCHUSER); /* 74 - No such user */ + def(CURLE_CONV_FAILED); /* 75 - conversion failed */ + def(CURLE_CONV_REQD); /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION); + CURLOPT_CONV_TO_NETWORK_FUNCTION); and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + def(CURLE_SSL_CACERT_BADFILE); /* 77 - could not load CACERT file); missing + or wrong format */ + def(CURLE_REMOTE_FILE_NOT_FOUND); /* 78 - remote file not found */ + def(CURLE_SSH); /* 79 - error from the SSH layer); somewhat + generic so the error message will be of + interest when this has happened */ + + def(CURLE_SSL_SHUTDOWN_FAILED); /* 80 - Failed to shut down the SSL + connection */ +#undef def + } +}; + +static const CURLcodeMapper sCURLcodeMapper; + +LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname): + mBoundListener(LLEventPumps::instance(). + obtain(pumpname). + listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1))) +{ +} + +/** + * Capture an outstanding LLXMLRPCTransaction and poll it periodically until + * done. + * + * The sequence is: + * # Instantiate Poller, which instantiates, populates and initiates an + * LLXMLRPCTransaction. Poller self-registers on the LLEventPump named + * "mainloop". + * # "mainloop" is conventionally pumped once per frame. On each such call, + * Poller checks its LLXMLRPCTransaction for completion. + * # When the LLXMLRPCTransaction completes, Poller collects results (if any) + * and sends notification. + * # The tricky part: Poller frees itself (and thus its LLXMLRPCTransaction) + * when done. The only external reference to it is the connection to the + * "mainloop" LLEventPump. + */ +class Poller +{ +public: + /// Validate the passed request for required fields, then use it to + /// populate an XMLRPC_REQUEST and an associated LLXMLRPCTransaction. Send + /// the request. + Poller(const LLSD& command): + mUri(command["uri"]), + mMethod(command["method"]), + mReplyPump(command["reply"]) + { + // LL_ERRS if any of these are missing + const char* required[] = { "uri", "method", "reply" }; + // optional: "options" (array of string) + // Validate the request + std::set<std::string> missing; + for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri) + { + // If the command does not contain this required entry, add it to 'missing'. + if (! command.has(*ri)) + { + missing.insert(*ri); + } + } + if (! missing.empty()) + { + LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: "; + const char* separator = ""; + for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end()); + mi != mend; ++mi) + { + LL_CONT << separator << *mi; + separator = ", "; + } + LL_CONT << LL_ENDL; + } + + // Build the XMLRPC request. + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + XMLRPC_RequestSetMethodName(request, mMethod.c_str()); + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + LLSD params(command["params"]); + if (params.isMap()) + { + for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap()); + pi != pend; ++pi) + { + std::string name(pi->first); + LLSD param(pi->second); + if (param.isString()) + { + XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0); + } + else if (param.isInteger() || param.isBoolean()) + { + XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger()); + } + else if (param.isReal()) + { + XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal()); + } + else + { + LL_ERRS("LLXMLRPCListener") << mMethod << " request param " + << name << " has unknown type: " << param << LL_ENDL; + } + } + } + LLSD options(command["options"]); + if (options.isArray()) + { + XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array); + for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray()); + oi != oend; ++oi) + { + XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0); + } + XMLRPC_AddValueToVector(xparams, xoptions); + } + XMLRPC_RequestSetData(request, xparams); + + mTransaction.reset(new LLXMLRPCTransaction(mUri, request)); + mPreviousStatus = mTransaction->status(NULL); + + // Free the XMLRPC_REQUEST object and the attached data values. + XMLRPC_RequestFree(request, 1); + + // Now ensure that we get regular callbacks to poll for completion. + mBoundListener = + LLEventPumps::instance(). + obtain("mainloop"). + listen(LLEventPump::inventName(), boost::bind(&Poller::poll, this, _1)); + + LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL; + } + + /// called by "mainloop" LLEventPump + bool poll(const LLSD&) + { + bool done = mTransaction->process(); + + CURLcode curlcode; + LLXMLRPCTransaction::Status status; + { + // LLXMLRPCTransaction::status() is defined to accept int* rather + // than CURLcode*. I don't feel the urge to fix the signature, but + // we want a CURLcode rather than an int. So fetch it as a local + // int, but then assign to a CURLcode for the remainder of this + // method. + int curlint; + status = mTransaction->status(&curlint); + curlcode = CURLcode(curlint); + } + + LLSD data; + data["status"] = sStatusMapper.lookup(status); + data["errorcode"] = sCURLcodeMapper.lookup(curlcode); + data["error"] = ""; + data["transfer_rate"] = 0.0; + LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump)); + if (! done) + { + // Not done yet, carry on. + if (status == LLXMLRPCTransaction::StatusDownloading + && status != mPreviousStatus) + { + // If a response has been received, send the + // 'downloading' status if it hasn't been sent. + replyPump.post(data); + } + + mPreviousStatus = status; + return false; + } + + // Here the transaction is complete. Check status. + data["error"] = mTransaction->statusMessage(); + data["transfer_rate"] = mTransaction->transferRate(); + LL_INFOS("LLXMLRPCListener") << mMethod << " result from " << mUri << ": status " + << data["status"].asString() << ", errorcode " + << data["errorcode"].asString() + << " (" << data["error"].asString() << ")" + << LL_ENDL; + // In addition to CURLE_OK, LLUserAuth distinguishes different error + // values of 'curlcode': + // CURLE_COULDNT_RESOLVE_HOST, + // CURLE_SSL_PEER_CERTIFICATE, + // CURLE_SSL_CACERT, + // CURLE_SSL_CONNECT_ERROR. + // Given 'message', need we care? + if (status == LLXMLRPCTransaction::StatusComplete) + { + // Success! Parse data. + std::string status_string(data["status"]); + data["responses"] = parseResponse(status_string); + data["status"] = status_string; + } + + // whether successful or not, send reply on requested LLEventPump + replyPump.post(data); + + // Because mTransaction is a boost::scoped_ptr, deleting this object + // frees our LLXMLRPCTransaction object. + // Because mBoundListener is an LLTempBoundListener, deleting this + // object disconnects it from "mainloop". + // *** MUST BE LAST *** + delete this; + return false; + } + +private: + /// Derived from LLUserAuth::parseResponse() and parseOptionInto() + LLSD parseResponse(std::string& status_string) + { + // Extract every member into data["responses"] (a map of string + // values). + XMLRPC_REQUEST response = mTransaction->response(); + if (! response) + { + LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL; + return LLSD(); + } + + XMLRPC_VALUE param = XMLRPC_RequestGetData(response); + if (! param) + { + LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL; + return LLSD(); + } + + // Now, parse everything + return parseValues(status_string, "", param); + } + + /** + * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map. + * @param key_pfx Used to describe a given key in log messages. At top + * level, pass "". When parsing an options array, pass the top-level key + * name of the array plus the index of the array entry; to this we'll + * append the subkey of interest. + * @param param XMLRPC_VALUE iterator. At top level, pass + * XMLRPC_RequestGetData(XMLRPC_REQUEST). + */ + LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param) + { + LLSD responses; + for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current; + current = XMLRPC_VectorNext(param)) + { + std::string key(XMLRPC_GetValueID(current)); + LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL; + XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current); + if (xmlrpc_type_string == type) + { + LLSD::String val(XMLRPC_GetValueString(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_int == type) + { + LLSD::Integer val(XMLRPC_GetValueInt(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_double == type) + { + LLSD::Real val(XMLRPC_GetValueDouble(current)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; + responses.insert(key, val); + } + else if (xmlrpc_type_array == type) + { + // We expect this to be an array of submaps. Walk the array, + // recursively parsing each submap and collecting them. + LLSD array; + int i = 0; // for descriptive purposes + for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row; + row = XMLRPC_VectorNext(current), ++i) + { + // Recursive call. For the lower-level key_pfx, if 'key' + // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the + // nested call, a subkey "bar" will then be logged as + // "foo[0]:bar", and so forth. + // Parse the scalar subkey/value pairs from this array + // entry into a temp submap. Collect such submaps in 'array'. + array.append(parseValues(status_string, + STRINGIZE(key_pfx << key << '[' << i << "]:"), + row)); + } + // Having collected an 'array' of 'submap's, insert that whole + // 'array' as the value of this 'key'. + responses.insert(key, array); + } + else + { + // whoops - unrecognized type + LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key " + << key_pfx << key << LL_ENDL; + responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>')); + status_string = "BadType"; + } + } + return responses; + } + + const std::string mUri; + const std::string mMethod; + const std::string mReplyPump; + LLTempBoundListener mBoundListener; + boost::scoped_ptr<LLXMLRPCTransaction> mTransaction; + LLXMLRPCTransaction::Status mPreviousStatus; // To detect state changes. +}; + +bool LLXMLRPCListener::process(const LLSD& command) +{ + // Allocate a new heap Poller, but do not save a pointer to it. Poller + // will check its own status and free itself on completion of the request. + (new Poller(command)); + // conventional event listener return + return false; +} diff --git a/indra/newview/llxmlrpclistener.h b/indra/newview/llxmlrpclistener.h new file mode 100644 index 0000000000..120c2b329b --- /dev/null +++ b/indra/newview/llxmlrpclistener.h @@ -0,0 +1,35 @@ +/** + * @file llxmlrpclistener.h + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief LLEventPump API for LLXMLRPCTransaction. This header doesn't + * actually define the API; the API is defined by the pump name on + * which this class listens, and by the expected content of LLSD it + * receives. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLXMLRPCLISTENER_H) +#define LL_LLXMLRPCLISTENER_H + +#include "llevents.h" + +/// Listen on an LLEventPump with specified name for LLXMLRPCTransaction +/// request events. +class LLXMLRPCListener +{ +public: + /// Specify the pump name on which to listen + LLXMLRPCListener(const std::string& pumpname); + + /// Handle request events on the event pump specified at construction time + bool process(const LLSD& command); + +private: + LLTempBoundListener mBoundListener; +}; + +#endif /* ! defined(LL_LLXMLRPCLISTENER_H) */ diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index a2fd0f0d9c..0e1beb377f 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "llxmlrpctransaction.h" +#include "llxmlrpclistener.h" #include "llcurl.h" #include "llviewercontrol.h" @@ -42,6 +43,13 @@ #include "llappviewer.h" +// Static instance of LLXMLRPCListener declared here so that every time we +// bring in this code, we instantiate a listener. If we put the static +// instance of LLXMLRPCListener into llxmlrpclistener.cpp, the linker would +// simply omit llxmlrpclistener.o, and shouting on the LLEventPump would do +// nothing. +static LLXMLRPCListener listener("LLXMLRPCTransaction"); + LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const { return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id)); @@ -213,6 +221,11 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestSetData(request, params.getValue()); init(request, useGzip); + // DEV-28398: without this XMLRPC_RequestFree() call, it looks as though + // the 'request' object is simply leaked. It's less clear to me whether we + // should also ask to free request value data (second param 1), since the + // data come from 'params'. + XMLRPC_RequestFree(request, 1); } diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp index 3c5f6fad2d..90cc867852 100644 --- a/indra/newview/tests/llcapabilitylistener_test.cpp +++ b/indra/newview/tests/llcapabilitylistener_test.cpp @@ -24,9 +24,9 @@ #include "../test/lltut.h" #include "../llcapabilityprovider.h" #include "lluuid.h" -#include "llerrorcontrol.h" #include "tests/networkio.h" #include "tests/commtest.h" +#include "tests/wrapllerrs.h" #include "stringize.h" #if defined(LL_WINDOWS) @@ -104,28 +104,6 @@ namespace tut typedef llcapears_group::object llcapears_object; llcapears_group llsdmgr("llcapabilitylistener"); - struct CaptureError: public LLError::OverrideFatalFunction - { - CaptureError(): - LLError::OverrideFatalFunction(boost::bind(&CaptureError::operator(), this, _1)) - { - LLError::setPrintLocation(false); - } - - struct FatalException: public std::runtime_error - { - FatalException(const std::string& what): std::runtime_error(what) {} - }; - - void operator()(const std::string& message) - { - error = message; - throw FatalException(message); - } - - std::string error; - }; - template<> template<> void llcapears_object::test<1>() { @@ -137,10 +115,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -184,10 +162,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } @@ -246,10 +224,10 @@ namespace tut std::string threw; try { - CaptureError capture; + WrapLL_ERRS capture; regionPump.post(request); } - catch (const CaptureError::FatalException& e) + catch (const WrapLL_ERRS::FatalException& e) { threw = e.what(); } diff --git a/indra/newview/tests/llxmlrpclistener_test.cpp b/indra/newview/tests/llxmlrpclistener_test.cpp new file mode 100644 index 0000000000..0c1ee42ffc --- /dev/null +++ b/indra/newview/tests/llxmlrpclistener_test.cpp @@ -0,0 +1,230 @@ +/* + * @file llxmlrpclistener_test.cpp + * @author Nat Goodspeed + * @date 2009-03-20 + * @brief Test for llxmlrpclistener. + * + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "../llviewerprecompiledheaders.h" +// associated header +#include "../llxmlrpclistener.h" +// STL headers +#include <iomanip> +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../llxmlrpctransaction.h" +#include "llevents.h" +#include "lleventfilter.h" +#include "llsd.h" +#include "llcontrol.h" +#include "tests/wrapllerrs.h" + +LLControlGroup gSavedSettings; + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct data + { + data(): + pumps(LLEventPumps::instance()), + uri("http://127.0.0.1:8000") + { + // These variables are required by machinery used by + // LLXMLRPCTransaction. The values reflect reality for this test + // executable; hopefully these values are correct. + gSavedSettings.declareBOOL("BrowserProxyEnabled", FALSE, "", FALSE); // don't persist + gSavedSettings.declareBOOL("NoVerifySSLCert", TRUE, "", FALSE); // don't persist + } + + // LLEventPump listener signature + bool captureReply(const LLSD& r) + { + reply = r; + return false; + } + + LLSD reply; + LLEventPumps& pumps; + std::string uri; + }; + typedef test_group<data> llxmlrpclistener_group; + typedef llxmlrpclistener_group::object object; + llxmlrpclistener_group llxmlrpclistenergrp("llxmlrpclistener"); + + template<> template<> + void object::test<1>() + { + set_test_name("request validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "missing params"); + ensure_contains("identified missing", threw, "method"); + ensure_contains("identified missing", threw, "reply"); + } + + template<> template<> + void object::test<2>() + { + set_test_name("param types validation"); + WrapLL_ERRS capture; + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"]["specifically"] = "world"; // LLXMLRPCListener only handles scalar params + std::string threw; + try + { + pumps.obtain("LLXMLRPCTransaction").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("threw exception", threw, "unknown type"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("success case"); + LLSD request; + request["uri"] = uri; + request["method"] = "hello"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["responses"]["hi_there"].asString(), "Hello, world!"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("bogus method"); + LLSD request; + request["uri"] = uri; + request["method"] = "goodbye"; + request["reply"] = "reply"; + LLSD& params(request["params"]); + params["who"] = "world"; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals("XMLRPC error", reply["status"].asString(), "XMLRPCError"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("bad type"); + LLSD request; + request["uri"] = uri; + request["method"] = "getdict"; + request["reply"] = "reply"; + (void)request["params"]; + // Set up a timeout filter so we don't spin forever waiting. + LLEventTimeout watchdog; + // Connect the timeout filter to the reply pump. + LLTempBoundListener temp( + pumps.obtain("reply"). + listen("watchdog", boost::bind(&LLEventTimeout::post, boost::ref(watchdog), _1))); + // Now connect our target listener to the timeout filter. + watchdog.listen("captureReply", boost::bind(&data::captureReply, this, _1)); + // Kick off the request... + reply.clear(); + pumps.obtain("LLXMLRPCTransaction").post(request); + // Set the timer + F32 timeout(10); + watchdog.eventAfter(timeout, LLSD().insert("timeout", 0)); + // and pump "mainloop" until we get something, whether from + // LLXMLRPCListener or from the watchdog filter. + LLTimer timer; + F32 start = timer.getElapsedTimeF32(); + LLEventPump& mainloop(pumps.obtain("mainloop")); + while (reply.isUndefined()) + { + mainloop.post(LLSD()); + } + ensure("timeout works", (timer.getElapsedTimeF32() - start) < (timeout + 1)); + ensure_equals(reply["status"].asString(), "BadType"); + ensure_contains("bad type", reply["responses"]["nested_dict"].asString(), "bad XMLRPC type"); + } +} // namespace tut + +/***************************************************************************** +* Resolve link errors: use real machinery here, since we intend to exchange +* actual XML with a peer process. +*****************************************************************************/ +// Including llxmlrpctransaction.cpp drags in the static LLXMLRPCListener +// instantiated there. That's why it works to post requests to the LLEventPump +// named "LLXMLRPCTransaction". +#include "../llxmlrpctransaction.cpp" +#include "llcontrol.cpp" +#include "llxmltree.cpp" +#include "llxmlparser.cpp" diff --git a/indra/newview/tests/test_llxmlrpc_peer.py b/indra/newview/tests/test_llxmlrpc_peer.py new file mode 100644 index 0000000000..cb8f7d26c4 --- /dev/null +++ b/indra/newview/tests/test_llxmlrpc_peer.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +"""\ +@file test_llxmlrpc_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewergpl$ +Copyright (c) 2008, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys +from threading import Thread +from SimpleXMLRPCServer import SimpleXMLRPCServer + +mydir = os.path.dirname(__file__) # expected to be .../indra/newview/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +sys.path.insert(1, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) +from testrunner import run, debug + +class TestServer(SimpleXMLRPCServer): + def _dispatch(self, method, params): + try: + func = getattr(self, method) + except AttributeError: + raise Exception('method "%s" is not supported' % method) + else: + # LLXMLRPCListener constructs XMLRPC parameters that arrive as a + # 1-tuple containing a dict. + return func(**(params[0])) + + def hello(self, who): + # LLXMLRPCListener expects a dict return. + return {"hi_there": "Hello, %s!" % who} + + def getdict(self): + return dict(nested_dict=dict(a=17, b=5)) + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class ServerRunner(Thread): + def run(self): + server = TestServer(('127.0.0.1', 8000)) + debug("Starting XMLRPC server...\n") + server.serve_forever() + +if __name__ == "__main__": + sys.exit(run(server=ServerRunner(name="xmlrpc"), *sys.argv[1:])) |