/** * @file llassetuploadresponders.cpp * @brief Processes responses received for asset upload requests. * * $LicenseInfo:firstyear=2007&license=viewergpl$ * Second Life Viewer Source Code * Copyright (c) 2007-2009, Linden Research, Inc. * * 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 "llassetuploadresponders.h" // viewer includes #include "llagent.h" #include "llagentbenefits.h" #include "llcompilequeue.h" #include "llfloaterbuycurrency.h" #include "statemachine/aifilepicker.h" #include "llinventorydefines.h" #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llpanelmaininventory.h" #include "llpermissionsflags.h" #include "llpreviewnotecard.h" #include "llpreviewscript.h" #include "llpreviewgesture.h" #include "llgesturemgr.h" #include "llstatusbar.h" // sendMoneyBalanceRequest() #include "llsdserialize.h" #include "lluploaddialog.h" #include "llviewerobject.h" #include "llviewercontrol.h" #include "llviewerobjectlist.h" #include "llviewertexlayer.h" #include "llviewerwindow.h" #include "lltrans.h" // library includes #include "lldir.h" #include "llfocusmgr.h" #include "llnotificationsutil.h" #include "llscrolllistctrl.h" #include "llsdserialize.h" #include "llsdutil.h" #include "llvfs.h" // When uploading multiple files, don't display any of them when uploading more than this number. static const S32 FILE_COUNT_DISPLAY_THRESHOLD = 5; void dialog_refresh_all(); void on_new_single_inventory_upload_complete( LLAssetType::EType asset_type, LLInventoryType::EType inventory_type, const std::string inventory_type_string, const LLUUID& item_folder_id, const std::string& item_name, const std::string& item_description, const LLSD& server_response, S32 upload_price) { if (upload_price > 0) { // this upload costed us L$, update our balance // and display something saying that it cost L$ LLStatusBar::sendMoneyBalanceRequest(); LLSD args; args["AMOUNT"] = llformat("%d", upload_price); LLNotificationsUtil::add("UploadPayment", args); } if (item_folder_id.notNull()) { U32 everyone_perms = PERM_NONE; U32 group_perms = PERM_NONE; U32 next_owner_perms = PERM_ALL; if (server_response.has("new_next_owner_mask")) { // The server provided creation perms so use them. // Do not assume we got the perms we asked for // since the server may not have granted them all. everyone_perms = server_response["new_everyone_mask"].asInteger(); group_perms = server_response["new_group_mask"].asInteger(); next_owner_perms = server_response["new_next_owner_mask"].asInteger(); } else { // The server doesn't provide creation perms // so use old assumption-based perms. if (inventory_type_string != "snapshot") { next_owner_perms = PERM_MOVE | PERM_TRANSFER; } } LLPermissions new_perms; new_perms.init( gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); new_perms.initMasks( PERM_ALL, PERM_ALL, everyone_perms, group_perms, next_owner_perms); U32 inventory_item_flags = 0; if (server_response.has("inventory_flags")) { inventory_item_flags = (U32) server_response["inventory_flags"].asInteger(); if (inventory_item_flags != 0) { LL_INFOS() << "inventory_item_flags " << inventory_item_flags << LL_ENDL; } } S32 creation_date_now = time_corrected(); LLPointer item = new LLViewerInventoryItem( server_response["new_inventory_item"].asUUID(), item_folder_id, new_perms, server_response["new_asset"].asUUID(), asset_type, inventory_type, item_name, item_description, LLSaleInfo::DEFAULT, inventory_item_flags, creation_date_now); gInventory.updateItem(item); gInventory.notifyObservers(); // Show the preview panel for textures and sounds to let // user know that the image (or snapshot) arrived intact. LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel(); if ( panel ) { LLFocusableElement* focus = gFocusMgr.getKeyboardFocus(); panel->setSelection( server_response["new_inventory_item"].asUUID(), TAKE_FOCUS_NO); if ((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type) /* FIXME: && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD */) { panel->openSelected(); } // restore keyboard focus gFocusMgr.setKeyboardFocus(focus); } } else { LL_WARNS() << "Can't find a folder to put it in" << LL_ENDL; } // remove the "Uploading..." message LLUploadDialog::modalUploadFinished(); } LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type) : mPostData(post_data), mVFileID(vfile_id), mAssetType(asset_type) { if (!gVFS->getExists(vfile_id, asset_type)) { LL_WARNS() << "LLAssetUploadResponder called with nonexistant vfile_id" << LL_ENDL; mVFileID.setNull(); mAssetType = LLAssetType::AT_NONE; return; } } LLAssetUploadResponder::LLAssetUploadResponder( const LLSD &post_data, const std::string& file_name, LLAssetType::EType asset_type) : mPostData(post_data), mFileName(file_name), mAssetType(asset_type) { } LLAssetUploadResponder::~LLAssetUploadResponder() { if (!mFileName.empty()) { // Delete temp file LLFile::remove(mFileName); } } void on_failure(const LLAssetType::EType& mAssetType, const LLSD& mPostData, const LLSD& args) { switch (mAssetType) { case LLAssetType::AT_NOTECARD: { if (LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::find(mPostData["item_id"])) nc->setEnabled(true); break; } case LLAssetType::AT_SCRIPT: case LLAssetType::AT_LSL_TEXT: case LLAssetType::AT_LSL_BYTECODE: { if (LLPreviewLSL* lsl = (LLPreviewLSL*)LLPreview::find(mPostData["item_id"])) lsl->callbackLSLCompileFailed(LLSD().with(0, "Upload Failure:").with(1, args["REASON"])); break; } default: break; } } // virtual void LLAssetUploadResponder::httpFailure() { LL_INFOS() << "LLAssetUploadResponder::error " << mStatus << " reason: " << mReason << LL_ENDL; LLSD args; switch(mStatus) { case 400: args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = "Error in upload request. Please visit " "http://secondlife.com/support for help fixing this problem."; LLNotificationsUtil::add("CannotUploadReason", args); break; case 500: default: args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = "The server is experiencing unexpected " "difficulties."; LLNotificationsUtil::add("CannotUploadReason", args); break; } on_failure(mAssetType, mPostData, args); LLUploadDialog::modalUploadFinished(); } //virtual void LLAssetUploadResponder::httpSuccess() { const LLSD& content = getContent(); if (!content.isMap()) { failureResult(HTTP_INTERNAL_ERROR_OTHER, "Malformed response contents", content); return; } LL_DEBUGS() << "LLAssetUploadResponder::result from capabilities" << LL_ENDL; const std::string& state = content["state"].asString(); if (state == "upload") { uploadUpload(content); } else if (state == "complete") { // rename file in VFS with new asset id if (mFileName.empty()) { // rename the file in the VFS to the actual asset id // LL_INFOS() << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << LL_ENDL; gVFS->renameFile(mVFileID, mAssetType, content["new_asset"].asUUID(), mAssetType); } uploadComplete(content); } else { uploadFailure(content); } } void LLAssetUploadResponder::uploadUpload(const LLSD& content) { const std::string& uploader = content["uploader"].asString(); if (mFileName.empty()) { LLHTTPClient::postFile(uploader, mVFileID, mAssetType, this); } else { LLHTTPClient::postFile(uploader, mFileName, this); } } void LLAssetUploadResponder::uploadFailure(const LLSD& content) { // remove the "Uploading..." message LLUploadDialog::modalUploadFinished(); const std::string& reason = content["state"].asString(); // deal with L$ errors if (reason == "insufficient funds") { S32 price; if (content.has("upload_price")) price = content["upload_price"]; else LLAgentBenefitsMgr::current().findUploadCost(mAssetType, price); LLFloaterBuyCurrency::buyCurrency("Uploading costs", price); } else { LLSD args; args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName); args["REASON"] = content["message"].asString(); LLNotificationsUtil::add("CannotUploadReason", args); on_failure(mAssetType, mPostData, args); } } void LLAssetUploadResponder::uploadComplete(const LLSD& content) { } LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( const LLSD& post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, vfile_id, asset_type) { } LLNewAgentInventoryResponder::LLNewAgentInventoryResponder( const LLSD& post_data, const std::string& file_name, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, file_name, asset_type) { } // virtual void LLNewAgentInventoryResponder::httpFailure() { LLAssetUploadResponder::httpFailure(); //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, LLUUID(), FALSE); } //virtual void LLNewAgentInventoryResponder::uploadFailure(const LLSD& content) { LLAssetUploadResponder::uploadFailure(content); //LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], FALSE); } //virtual void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) { LL_DEBUGS() << "LLNewAgentInventoryResponder::result from capabilities" << LL_ENDL; //std::ostringstream llsdxml; //LLSDSerialize::toXML(content, llsdxml); //LL_INFOS() << "upload complete content:\n " << llsdxml.str() << LL_ENDL; LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString()); LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString()); S32 expected_upload_cost = 0; // Update L$ and ownership credit information // since it probably changed on the server if (content.has("upload_price")) expected_upload_cost = content["upload_price"]; else if (asset_type == LLAssetType::AT_TEXTURE || asset_type == LLAssetType::AT_SOUND || asset_type == LLAssetType::AT_ANIMATION || asset_type == LLAssetType::AT_MESH) { LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost); } LL_INFOS() << "Adding " << content["new_inventory_item"].asUUID() << " " << content["new_asset"].asUUID() << " to inventory." << LL_ENDL; on_new_single_inventory_upload_complete( asset_type, inventory_type, mPostData["asset_type"].asString(), mPostData["folder_id"].asUUID(), mPostData["name"], mPostData["description"], content, expected_upload_cost); // continue uploading for bulk uploads /* Singu Note: sUploadQueue was never getting populated, anywhere! Therefore, this entire block never was reached. ** I have condensed it to here in the hopes it may one day see use. Apparently, it came in with Siana's prep work ** for mesh upload (697dd7e9298282590f8cf858a58335f70302532b), but we never needed it. static std::deque sUploadQueue; if (!sUploadQueue.empty()) { std::string next_file = sUploadQueue.front(); sUploadQueue.pop_front(); if (next_file.empty()) return; std::string name = gDirUtilp->getBaseFileName(next_file, true); std::string asset_name = name; LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); LLStringUtil::replaceChar(asset_name, '|', '?'); LLStringUtil::stripNonprintable(asset_name); LLStringUtil::trim(asset_name); // We need to extract the originally requested permissions data, if any, // and use them for each next file to be uploaded. Note the requested // perms are not the same as the granted ones found in the given // "content" structure but can still be found in mPostData. -MG U32 everyone_perms = mPostData.has("everyone_mask") ? mPostData.get("everyone_mask").asInteger() : PERM_NONE; U32 group_perms = mPostData.has("group_mask") ? mPostData.get("group_mask").asInteger() : PERM_NONE; U32 next_owner_perms = mPostData.has("next_owner_mask") ? mPostData.get("next_owner_mask").asInteger() : PERM_NONE; std::string display_name = LLStringUtil::null; LLAssetStorage::LLStoreAssetCallback callback = NULL; void *userdata = NULL; upload_new_resource( next_file, asset_name, asset_name, 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, next_owner_perms, group_perms, everyone_perms, display_name, callback, expected_upload_cost, userdata); } */ } LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( const LLSD& post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, vfile_id, asset_type) { } LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder( const LLSD& post_data, const std::string& file_name, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, file_name, asset_type) { } //virtual void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content) { LL_INFOS() << "LLUpdateAgentInventoryResponder::result from capabilities" << LL_ENDL; LLUUID item_id = mPostData["item_id"]; LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(item_id); if(!item) { LL_WARNS() << "Inventory item for " << mVFileID << " is no longer in agent inventory." << LL_ENDL; return; } // Update viewer inventory item LLPointer new_item = new LLViewerInventoryItem(item); new_item->setAssetUUID(content["new_asset"].asUUID()); gInventory.updateItem(new_item); gInventory.notifyObservers(); LL_INFOS() << "Inventory item " << item->getName() << " saved into " << content["new_asset"].asString() << LL_ENDL; LLInventoryType::EType inventory_type = new_item->getInventoryType(); switch(inventory_type) { case LLInventoryType::IT_NOTECARD: { // Update the UI with the new asset. LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::find(new_item->getUUID()); if (nc) { // *HACK: we have to delete the asset in the VFS so // that the viewer will redownload it. This is only // really necessary if the asset had to be modified by // the uploader, so this can be optimized away in some // cases. A better design is to have a new uuid if the // script actually changed the asset. if(nc->hasEmbeddedInventory()) { gVFS->removeFile(content["new_asset"].asUUID(), LLAssetType::AT_NOTECARD); } nc->refreshFromInventory(); } break; } case LLInventoryType::IT_LSL: { // Find our window and close it if requested. LLPreviewLSL* preview = (LLPreviewLSL*)LLPreview::find(item_id); if (preview) { // Bytecode save completed if (content["compiled"]) { preview->callbackLSLCompileSucceeded(); } else { preview->callbackLSLCompileFailed(content["errors"]); } } break; } case LLInventoryType::IT_GESTURE: { // If this gesture is active, then we need to update the in-memory // active map with the new pointer. if (LLGestureMgr::instance().isGestureActive(item_id)) { LLUUID asset_id = new_item->getAssetUUID(); LLGestureMgr::instance().replaceGesture(item_id, asset_id); gInventory.notifyObservers(); } //gesture will have a new asset_id LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(item_id); if (previewp) { previewp->onUpdateSucceeded(); } break; } case LLInventoryType::IT_WEARABLE: default: break; } } LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, vfile_id, asset_type) { } LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, const std::string& file_name, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, file_name, asset_type) { } LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data, const std::string& file_name, const LLUUID& queue_id, LLAssetType::EType asset_type) : LLAssetUploadResponder(post_data, file_name, asset_type), mQueueId(queue_id) { } //virtual void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content) { LL_INFOS() << "LLUpdateTaskInventoryResponder::result from capabilities" << LL_ENDL; LLUUID item_id = mPostData["item_id"]; LLUUID task_id = mPostData["task_id"]; dialog_refresh_all(); switch(mAssetType) { case LLAssetType::AT_NOTECARD: { // Update the UI with the new asset. LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::find(item_id); if (nc) { // *HACK: we have to delete the asset in the VFS so // that the viewer will redownload it. This is only // really necessary if the asset had to be modified by // the uploader, so this can be optimized away in some // cases. A better design is to have a new uuid if the // script actually changed the asset. if (nc->hasEmbeddedInventory()) { gVFS->removeFile(content["new_asset"].asUUID(), LLAssetType::AT_NOTECARD); } nc->setAssetId(content["new_asset"].asUUID()); nc->refreshFromInventory(); } break; } case LLAssetType::AT_LSL_TEXT: { if (mQueueId.notNull()) { LLFloaterCompileQueue* queue = (LLFloaterCompileQueue*) LLFloaterScriptQueue::findInstance(mQueueId); if (NULL != queue) { queue->removeItemByItemID(item_id); } } else { LLLiveLSLEditor* preview = static_cast(LLPreview::find(item_id)); if (preview) { // Bytecode save completed if (content["compiled"]) { preview->callbackLSLCompileSucceeded(task_id, item_id, mPostData["is_script_running"]); } else { preview->callbackLSLCompileFailed(content["errors"]); } } } break; } default: break; } } ///////////////////////////////////////////////////// // LLNewAgentInventoryVariablePriceResponder::Impl // ///////////////////////////////////////////////////// class LLNewAgentInventoryVariablePriceResponder::Impl { public: Impl( const LLUUID& vfile_id, LLAssetType::EType asset_type, const LLSD& inventory_data) : mVFileID(vfile_id), mAssetType(asset_type), mInventoryData(inventory_data), mFileName("") { if (!gVFS->getExists(vfile_id, asset_type)) { LL_WARNS() << "LLAssetUploadResponder called with nonexistant " << "vfile_id " << vfile_id << LL_ENDL; mVFileID.setNull(); mAssetType = LLAssetType::AT_NONE; } } Impl( const std::string& file_name, LLAssetType::EType asset_type, const LLSD& inventory_data) : mFileName(file_name), mAssetType(asset_type), mInventoryData(inventory_data) { mVFileID.setNull(); } std::string getFilenameOrIDString() const { return (mFileName.empty() ? mVFileID.asString() : mFileName); } LLUUID getVFileID() const { return mVFileID; } std::string getFilename() const { return mFileName; } LLAssetType::EType getAssetType() const { return mAssetType; } LLInventoryType::EType getInventoryType() const { return LLInventoryType::lookup( mInventoryData["inventory_type"].asString()); } std::string getInventoryTypeString() const { return mInventoryData["inventory_type"].asString(); } LLUUID getFolderID() const { return mInventoryData["folder_id"].asUUID(); } std::string getItemName() const { return mInventoryData["name"].asString(); } std::string getItemDescription() const { return mInventoryData["description"].asString(); } void displayCannotUploadReason(const std::string& reason) { LLSD args; args["FILE"] = getFilenameOrIDString(); args["REASON"] = reason; LLNotificationsUtil::add("CannotUploadReason", args); LLUploadDialog::modalUploadFinished(); } void onApplicationLevelError(const LLSD& error) { static const std::string _IDENTIFIER = "identifier"; static const std::string _INSUFFICIENT_FUNDS = "NewAgentInventory_InsufficientLindenDollarBalance"; static const std::string _MISSING_REQUIRED_PARAMETER = "NewAgentInventory_MissingRequiredParamater"; static const std::string _INVALID_REQUEST_BODY = "NewAgentInventory_InvalidRequestBody"; static const std::string _RESOURCE_COST_DIFFERS = "NewAgentInventory_ResourceCostDiffers"; static const std::string _MISSING_PARAMETER = "missing_parameter"; static const std::string _INVALID_PARAMETER = "invalid_parameter"; static const std::string _MISSING_RESOURCE = "missing_resource"; static const std::string _INVALID_RESOURCE = "invalid_resource"; // TODO* Add the other error_identifiers std::string error_identifier = error[_IDENTIFIER].asString(); // TODO*: Pull these user visible strings from an xml file // to be localized if (_INSUFFICIENT_FUNDS == error_identifier) { displayCannotUploadReason("You do not have a sufficient L$ balance to complete this upload."); } else if (_MISSING_REQUIRED_PARAMETER == error_identifier) { // Missing parameters if (error.has(_MISSING_PARAMETER) ) { std::string message = "Upload request was missing required parameter '[P]'"; LLStringUtil::replaceString( message, "[P]", error[_MISSING_PARAMETER].asString()); displayCannotUploadReason(message); } else { std::string message = "Upload request was missing a required parameter"; displayCannotUploadReason(message); } } else if ( _INVALID_REQUEST_BODY == error_identifier ) { // Invalid request body, check to see if // a particular parameter was invalid if ( error.has(_INVALID_PARAMETER) ) { std::string message = "Upload parameter '[P]' is invalid."; LLStringUtil::replaceString( message, "[P]", error[_INVALID_PARAMETER].asString()); // See if the server also responds with what resource // is missing. if ( error.has(_MISSING_RESOURCE) ) { message += "\nMissing resource '[R]'."; LLStringUtil::replaceString( message, "[R]", error[_MISSING_RESOURCE].asString()); } else if ( error.has(_INVALID_RESOURCE) ) { message += "\nInvalid resource '[R]'."; LLStringUtil::replaceString( message, "[R]", error[_INVALID_RESOURCE].asString()); } displayCannotUploadReason(message); } else { std::string message = "Upload request was malformed"; displayCannotUploadReason(message); } } else if (_RESOURCE_COST_DIFFERS == error_identifier) { displayCannotUploadReason("The resource cost associated with this upload is not consistent with the server."); } else { displayCannotUploadReason("Unknown Error"); } } void onTransportError() { displayCannotUploadReason( "The server is experiencing unexpected difficulties."); } void onTransportError(const LLSD& error) { static const std::string _IDENTIFIER = "identifier"; static const std::string _SERVER_ERROR_AFTER_CHARGE = "NewAgentInventory_ServerErrorAfterCharge"; std::string error_identifier = error[_IDENTIFIER].asString(); // TODO*: Pull the user visible strings from an xml file // to be localized if ( _SERVER_ERROR_AFTER_CHARGE == error_identifier ) { displayCannotUploadReason( "The server is experiencing unexpected difficulties. You may have been charged for the upload."); } else { displayCannotUploadReason( "The server is experiencing unexpected difficulties."); } } bool uploadConfirmationCallback( const LLSD& notification, const LLSD& response, boost::intrusive_ptr responder) { S32 option; std::string confirmation_url; option = LLNotificationsUtil::getSelectedOption( notification, response); confirmation_url = notification["payload"]["confirmation_url"].asString(); // Yay! We are confirming or cancelling our upload switch(option) { case 0: { confirmUpload(confirmation_url, responder); } break; case 1: default: break; } return false; } void confirmUpload( const std::string& confirmation_url, boost::intrusive_ptr responder) { if ( getFilename().empty() ) { // we have no filename, use virtual file ID instead LLHTTPClient::postFile( confirmation_url, getVFileID(), getAssetType(), responder); } else { LLHTTPClient::postFile( confirmation_url, getFilename(), responder); } } private: std::string mFileName; LLSD mInventoryData; LLAssetType::EType mAssetType; LLUUID mVFileID; }; /////////////////////////////////////////////// // LLNewAgentInventoryVariablePriceResponder // /////////////////////////////////////////////// LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( const LLUUID& vfile_id, LLAssetType::EType asset_type, const LLSD& inventory_info) { mImpl = new Impl( vfile_id, asset_type, inventory_info); } LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder( const std::string& file_name, LLAssetType::EType asset_type, const LLSD& inventory_info) { mImpl = new Impl( file_name, asset_type, inventory_info); } LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResponder() { delete mImpl; } void LLNewAgentInventoryVariablePriceResponder::httpFailure() { const LLSD& content = getContent(); LL_WARNS("Upload") << dumpResponse() << LL_ENDL; static const std::string _ERROR = "error"; if ( content.has(_ERROR) ) { mImpl->onTransportError(content[_ERROR]); } else { mImpl->onTransportError(); } } void LLNewAgentInventoryVariablePriceResponder::httpSuccess() { const LLSD& content = getContent(); if (!content.isMap()) { failureResult(HTTP_INTERNAL_ERROR_OTHER, "Malformed response contents", content); return; } // Parse out application level errors and the appropriate // responses for them static const std::string _ERROR = "error"; static const std::string _STATE = "state"; static const std::string _COMPLETE = "complete"; static const std::string _CONFIRM_UPLOAD = "confirm_upload"; static const std::string _UPLOAD_PRICE = "upload_price"; static const std::string _RESOURCE_COST = "resource_cost"; static const std::string _RSVP = "rsvp"; // Check for application level errors if ( content.has(_ERROR) ) { LL_WARNS("Upload") << dumpResponse() << LL_ENDL; onApplicationLevelError(content[_ERROR]); return; } std::string state = content[_STATE]; LLAssetType::EType asset_type = mImpl->getAssetType(); if (_COMPLETE == state) { // rename file in VFS with new asset id if (mImpl->getFilename().empty()) { // rename the file in the VFS to the actual asset id // LL_INFOS() << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << LL_ENDL; gVFS->renameFile( mImpl->getVFileID(), asset_type, content["new_asset"].asUUID(), asset_type); } on_new_single_inventory_upload_complete( asset_type, mImpl->getInventoryType(), mImpl->getInventoryTypeString(), mImpl->getFolderID(), mImpl->getItemName(), mImpl->getItemDescription(), content, content[_UPLOAD_PRICE].asInteger()); // TODO* Add bulk (serial) uploading or add // a super class of this that does so } else if ( _CONFIRM_UPLOAD == state ) { showConfirmationDialog( content[_UPLOAD_PRICE].asInteger(), content[_RESOURCE_COST].asInteger(), content[_RSVP].asString()); } else { LL_WARNS("Upload") << dumpResponse() << LL_ENDL; onApplicationLevelError(""); } } void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError( const LLSD& error) { mImpl->onApplicationLevelError(error); } void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog( S32 upload_price, S32 resource_cost, const std::string& confirmation_url) { if (0 == upload_price) { // don't show confirmation dialog for free uploads, I mean, // they're free! // The creating of a new instrusive_ptr(this) // creates a new boost::intrusive_ptr // which is a copy of this. This code is required because // 'this' is always of type Class* and not the intrusive_ptr, // and thus, a reference to 'this' is not registered // by using just plain 'this'. // Since LLNewAgentInventoryVariablePriceResponder is a // reference counted class, it is possible (since the // reference to a plain 'this' would be missed here) that, // when using plain ol' 'this', that this object // would be deleted before the callback is triggered // and cause sadness. mImpl->confirmUpload( confirmation_url, boost::intrusive_ptr(this)); } else { LLSD substitutions; LLSD payload; substitutions["PRICE"] = upload_price; payload["confirmation_url"] = confirmation_url; // The creating of a new instrusive_ptr(this) // creates a new boost::intrusive_ptr // which is a copy of this. This code is required because // 'this' is always of type Class* and not the intrusive_ptr, // and thus, a reference to 'this' is not registered // by using just plain 'this'. // Since LLNewAgentInventoryVariablePriceResponder is a // reference counted class, it is possible (since the // reference to a plain 'this' would be missed here) that, // when using plain ol' 'this', that this object // would be deleted before the callback is triggered // and cause sadness. LLNotificationsUtil::add( "UploadCostConfirmation", substitutions, payload, boost::bind( &LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback, mImpl, _1, _2, boost::intrusive_ptr(this))); } }