Newer
Older
/**
* @file llassetuploadresponders.cpp
* @brief Processes responses received for asset upload requests.
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-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 "llassetuploadresponders.h"
#include "llfloaterbuycurrency.h"
#include "llfilepicker.h"
#include "llpreviewnotecard.h"
#include "llpreviewscript.h"
Don Kjer
committed
#include "llpreviewgesture.h"
#include "llgesturemgr.h"
#include "llstatusbar.h" // sendMoneyBalanceRequest()
Melinda Green
committed
#include "llsdserialize.h"
#include "llviewerobject.h"
Melinda Green
committed
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
Don Kjer
committed
#include "llviewermenufile.h"
#include "lltexlayer.h"
#include "lldir.h"
#include "llfloaterreg.h"
#include "llnotificationsutil.h"
#include "llscrolllistctrl.h"
#include "llsdserialize.h"
#include "llvfs.h"
Melinda Green
committed
// 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();
LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
mVFileID(vfile_id),
mAssetType(asset_type)
{
if (!gVFS->getExists(vfile_id, asset_type))
{
llwarns << "LLAssetUploadResponder called with nonexistant vfile_id" << llendl;
mVFileID.setNull();
mAssetType = LLAssetType::AT_NONE;
return;
}
}
LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
{
}
LLAssetUploadResponder::~LLAssetUploadResponder()
if (!mFileName.empty())
{
// Delete temp file
LLFile::remove(mFileName);
void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason)
llinfos << "LLAssetUploadResponder::error " << statusNum
<< " reason: " << reason << llendl;
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);
args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName);
args["REASON"] = "The server is experiencing unexpected "
Josh Bell
committed
"difficulties.";
LLNotificationsUtil::add("CannotUploadReason", args);
break;
}
LLUploadDialog::modalUploadFinished();
}
//virtual
void LLAssetUploadResponder::result(const LLSD& content)
lldebugs << "LLAssetUploadResponder::result from capabilities" << llendl;
std::string state = content["state"];
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
// llinfos << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << llendl;
gVFS->renameFile(mVFileID, mAssetType, content["new_asset"].asUUID(), mAssetType);
}
uploadComplete(content);
}
else
{
uploadFailure(content);
}
}
void LLAssetUploadResponder::uploadUpload(const LLSD& content)
{
std::string uploader = content["uploader"];
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();
std::string reason = content["state"];
Don Kjer
committed
// deal with L$ errors
if (reason == "insufficient funds")
{
LLFloaterBuyCurrency::buyCurrency("Uploading costs", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload());
}
else
LLSD args;
args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName);
args["REASON"] = content["message"].asString();
LLNotificationsUtil::add("CannotUploadReason", 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::uploadComplete(const LLSD& content)
{
lldebugs << "LLNewAgentInventoryResponder::result from capabilities" << llendl;
Melinda Green
committed
//std::ostringstream llsdxml;
//LLSDSerialize::toXML(content, llsdxml);
//llinfos << "upload complete content:\n " << llsdxml.str() << llendl;
LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString());
LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString());
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
Don Kjer
committed
// Update L$ and ownership credit information
// since it probably changed on the server
if (asset_type == LLAssetType::AT_TEXTURE ||
asset_type == LLAssetType::AT_SOUND ||
asset_type == LLAssetType::AT_ANIMATION)
args["AMOUNT"] = llformat("%d", expected_upload_cost);
LLNotificationsUtil::add("UploadPayment", args);
// Actually add the upload to viewer inventory
llinfos << "Adding " << content["new_inventory_item"].asUUID() << " "
<< content["new_asset"].asUUID() << " to inventory." << llendl;
if(mPostData["folder_id"].asUUID().notNull())
{
Melinda Green
committed
//std::ostringstream out;
//LLSDXMLFormatter *formatter = new LLSDXMLFormatter;
//formatter->format(mPostData, out, LLSDFormatter::OPTIONS_PRETTY);
//llinfos << "Post Data: " << out.str() << llendl;
U32 everyone_perms = PERM_NONE;
U32 group_perms = PERM_NONE;
U32 next_owner_perms = PERM_ALL;
if(content.has("new_next_owner_mask"))
Melinda Green
committed
// This is a new sim that provides creation perms so use them.
// Do not assume we got the perms we asked for in mPostData
// since the sim may not have granted them all.
everyone_perms = content["new_everyone_mask"].asInteger();
group_perms = content["new_group_mask"].asInteger();
next_owner_perms = content["new_next_owner_mask"].asInteger();
Melinda Green
committed
else
Melinda Green
committed
// This old sim doesn't provide creation perms so use old assumption-based perms.
if(mPostData["inventory_type"].asString() != "snapshot")
{
next_owner_perms = PERM_MOVE | PERM_TRANSFER;
}
Melinda Green
committed
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);
S32 creation_date_now = time_corrected();
LLPointer<LLViewerInventoryItem> item
= new LLViewerInventoryItem(content["new_inventory_item"].asUUID(),
mPostData["folder_id"].asUUID(),
Melinda Green
committed
new_perms,
content["new_asset"].asUUID(),
asset_type,
inventory_type,
mPostData["name"].asString(),
mPostData["description"].asString(),
LLSaleInfo::DEFAULT,
LLInventoryItem::II_FLAGS_NONE,
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 *active_panel = LLInventoryPanel::getActiveInventoryPanel();
if (active_panel)
active_panel->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO);
Melinda Green
committed
if((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type)
&& LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD)
active_panel->openSelected();
James Cook
committed
//LLFloaterInventory::dumpSelectionInformation((void*)view);
// restore keyboard focus
LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
}
}
else
{
llwarns << "Can't find a folder to put it in" << llendl;
}
// remove the "Uploading..." message
LLUploadDialog::modalUploadFinished();
// *FIX: This is a pretty big hack. What this does is check the
// file picker if there are any more pending uploads. If so,
// upload that file.
std::string next_file = LLFilePicker::instance().getNextFile();
if(!next_file.empty())
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);
Melinda Green
committed
// Continuing the horrible hack above, 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);
}
}
LLSendTexLayerResponder::LLSendTexLayerResponder(const LLSD& post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type,
LLBakedUploadData * baked_upload_data) :
LLAssetUploadResponder(post_data, vfile_id, asset_type),
mBakedUploadData(baked_upload_data)
{
}
LLSendTexLayerResponder::~LLSendTexLayerResponder()
{
// mBakedUploadData is normally deleted by calls to LLTexLayerSetBuffer::onTextureUploadComplete() below
if (mBakedUploadData)
{ // ...but delete it in the case where uploadComplete() is never called
delete mBakedUploadData;
mBakedUploadData = NULL;
}
}
// Baked texture upload completed
void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
{
LLUUID item_id = mPostData["item_id"];
std::string result = content["state"];
LLUUID new_id = content["new_asset"];
llinfos << "result: " << result << "new_id:" << new_id << llendl;
if (result == "complete"
&& mBakedUploadData != NULL)
{ // Invoke
LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, 0, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
else
{ // Invoke the original callback with an error result
LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, -1, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
}
void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason)
{
llinfos << "status: " << statusNum << " reason: " << reason << llendl;
// Invoke the original callback with an error result
LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
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)
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
{
}
//virtual
void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content)
{
llinfos << "LLUpdateAgentInventoryResponder::result from capabilities" << llendl;
LLUUID item_id = mPostData["item_id"];
LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(item_id);
if(!item)
{
llwarns << "Inventory item for " << mVFileID
<< " is no longer in agent inventory." << llendl;
return;
}
// Update viewer inventory item
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setAssetUUID(content["new_asset"].asUUID());
gInventory.updateItem(new_item);
gInventory.notifyObservers();
llinfos << "Inventory item " << item->getName() << " saved into "
<< content["new_asset"].asString() << llendl;
LLInventoryType::EType inventory_type = new_item->getInventoryType();
switch(inventory_type)
{
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
case LLInventoryType::IT_NOTECARD:
{
// Update the UI with the new asset.
LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(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->refreshFromInventory(new_item->getUUID());
}
break;
}
case LLInventoryType::IT_LSL:
{
// Find our window and close it if requested.
LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(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 (LLGestureManager::instance().isGestureActive(item_id))
{
LLUUID asset_id = new_item->getAssetUUID();
LLGestureManager::instance().replaceGesture(item_id, asset_id);
gInventory.notifyObservers();
}
//gesture will have a new asset_id
LLPreviewGesture* previewp = LLFloaterReg::findTypedInstance<LLPreviewGesture>("preview_gesture", LLSD(item_id));
if(previewp)
{
previewp->onUpdateSucceeded();
}
Don Kjer
committed
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)
{
llinfos << "LLUpdateTaskInventoryResponder::result from capabilities" << llendl;
LLUUID item_id = mPostData["item_id"];
LLUUID task_id = mPostData["task_id"];
dialog_refresh_all();
case LLAssetType::AT_NOTECARD:
{
// Update the UI with the new asset.
LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(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);
}
Brian McGroarty
committed
nc->setAssetId(content["new_asset"].asUUID());
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
nc->refreshFromInventory();
}
break;
}
case LLAssetType::AT_LSL_TEXT:
{
if(mQueueId.notNull())
{
LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", mQueueId);
if(NULL != queue)
{
queue->removeItemByItemID(item_id);
}
}
else
{
LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", LLSD(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: