diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index f41fce8ed..9568cd001 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -34,6 +34,13 @@ #include "metapropertyt.h" #include "llsd.h" +///---------------------------------------------------------------------------- +/// Class LFSimFeatureHandlerInterface +///---------------------------------------------------------------------------- + +//static +LFSimFeatureHandlerInterface* LFSimFeatureHandlerInterface::sInstance; + ///---------------------------------------------------------------------------- /// Class LLPermissions ///---------------------------------------------------------------------------- @@ -475,6 +482,14 @@ BOOL LLPermissions::setNextOwnerBits(const LLUUID& agent, const LLUUID& group, B bool LLPermissions::allowOperationBy(PermissionBit op, const LLUUID& requester, const LLUUID& group) const { + // Singu extension: Make this function work for PERM_EXPORT operation (also on grids not supporting it). + if (op == PERM_EXPORT) + { + // Requester must always be the owner; on grids supporting PERM_EXPORT, that bit + // must be set in the mMaskEveryone mask, otherwise the requester must be the creator. + return (!mIsGroupOwned && (mOwner == requester) && + (mCreator == requester || (LFSimFeatureHandlerInterface::ifInstance()->simSupportsExport() && (mMaskEveryone & PERM_EXPORT)))); + } if(requester.isNull()) { // ...system making request diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index 7d3a68d35..5986e0dd9 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -40,6 +40,23 @@ extern void mask_to_string(U32 mask, char* str); extern std::string mask_to_string(U32 mask); template class LLMetaClassT; +// Interface of LFSimFeatureHandler. +class LFSimFeatureHandlerInterface +{ +private: + // LFSimFeatureHandler is a singleton. + static LFSimFeatureHandlerInterface* sInstance; + +protected: + LFSimFeatureHandlerInterface(void) { sInstance = this; } + virtual ~LFSimFeatureHandlerInterface() { } + +public: + virtual bool simSupportsExport() const = 0; + + static LFSimFeatureHandlerInterface* ifInstance(void) { return sInstance; } +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLPermissions // @@ -274,6 +291,7 @@ public: inline bool allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const; inline bool allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const; inline bool allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const; + inline bool allowExportBy(const LLUUID &agent_id) const; // Singu extension. // This somewhat specialized function is meant for testing if the // current owner is allowed to transfer to the specified agent id. @@ -373,6 +391,11 @@ bool LLPermissions::allowTransferTo(const LLUUID &agent_id) const } } +bool LLPermissions::allowExportBy(const LLUUID& agent) const +{ + return allowOperationBy(PERM_EXPORT, agent); +} + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLAggregatePermissions // diff --git a/indra/newview/awavefront.cpp b/indra/newview/awavefront.cpp index 3f4d2c994..816895e91 100644 --- a/indra/newview/awavefront.cpp +++ b/indra/newview/awavefront.cpp @@ -30,7 +30,6 @@ #include "llnotificationsutil.h" // newview includes -#include "lfsimfeaturehandler.h" #include "llavatarappearancedefines.h" #include "llface.h" #include "llvoavatar.h" @@ -220,17 +219,6 @@ void WavefrontSaver::Add(const LLViewerObject* some_vo) } namespace { - bool can_export_node(const LLSelectNode* node) - { - if (const LLPermissions* perms = node->mPermissions) - { - if (gAgentID == perms->getCreator() || (LFSimFeatureHandler::instance().simSupportsExport() && gAgentID == perms->getOwner() && perms->getMaskEveryone() & PERM_EXPORT)) - { - return true; - } - } - return false; - } class LFSaveSelectedObjects : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -245,7 +233,7 @@ namespace { total++; LLSelectNode* node = *iter; - if (!can_export_node(node)) continue; + if (!node->mPermissions->allowExportBy(gAgentID)) continue; included++; wfsaver->Add(node->getObject()); } @@ -328,7 +316,7 @@ void WavefrontSaver::Add(const LLVOAvatar* av_vo) //adds attachments, too! if (!c) continue; if (const LLSelectNode* n = LLSelectMgr::getInstance()->getSelection()->findNode(const_cast(c))) { - if (!can_export_node(n)) continue; + if (!n->mPermissions->allowExportBy(gAgentID)) continue; } else continue; const LLVolume* vol = c->getVolume(); diff --git a/indra/newview/daeexport.cpp b/indra/newview/daeexport.cpp index 586dc8cf5..145261d19 100644 --- a/indra/newview/daeexport.cpp +++ b/indra/newview/daeexport.cpp @@ -65,7 +65,6 @@ #include "boost/date_time/posix_time/posix_time.hpp" // newview includes -#include "lfsimfeaturehandler.h" #include "llface.h" #include "llvovolume.h" @@ -117,18 +116,6 @@ namespace DAEExportUtil } } - bool canExportNode(const LLSelectNode* node) - { - if (const LLPermissions* perms = node->mPermissions) - { - if (gAgentID == perms->getCreator() || (LFSimFeatureHandler::instance().simSupportsExport() && gAgentID == perms->getOwner() && perms->getMaskEveryone() & PERM_EXPORT)) - { - return true; - } - } - return false; - } - void saveSelectedObject() { static const std::string file_ext = ".dae"; @@ -144,7 +131,7 @@ namespace DAEExportUtil { total++; LLSelectNode* node = *iter; - if (!canExportNode(node) || !node->getObject()->getVolume()) continue; + if (!node->mPermissions->allowExportBy(gAgentID) || !node->getObject()->getVolume()) continue; included++; daesaver->Add(node->getObject(), node->mName); } diff --git a/indra/newview/lfsimfeaturehandler.h b/indra/newview/lfsimfeaturehandler.h index 070d6ba1e..062a55898 100644 --- a/indra/newview/lfsimfeaturehandler.h +++ b/indra/newview/lfsimfeaturehandler.h @@ -19,6 +19,7 @@ #define LFSIMFEATUREHANDLER_H #include "llsingleton.h" +#include "llpermissions.h" template class SignaledType @@ -45,7 +46,7 @@ private: Type mValue; }; -class LFSimFeatureHandler : public LLSingleton +class LFSimFeatureHandler : public LFSimFeatureHandlerInterface, public LLSingleton { protected: friend class LLSingleton; @@ -59,7 +60,7 @@ public: boost::signals2::connection setSupportsExportCallback(const boost::signals2::signal::slot_type& slot); // Accessors - bool simSupportsExport() const { return mSupportsExport; } + /*virtual*/ bool simSupportsExport() const { return mSupportsExport; } private: // SignaledTypes diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 50c8bc5cc..924164a26 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3165,7 +3165,8 @@ BOOL LLAgent::allowOperation(PermissionBit op, U8 god_minimum) { // Check god level. - if (getGodLevel() >= god_minimum) return TRUE; + // Singu note: We should never get here for PERM_EXPORT really, but if we do - then Gods are not above the law. + if (getGodLevel() >= god_minimum && PERM_EXPORT != op) return TRUE; if (!perm.isOwned()) return FALSE; @@ -3187,7 +3188,8 @@ BOOL LLAgent::allowOperation(PermissionBit op, else { // Check for granted mod permissions. - if ((PERM_OWNER != op) && isGrantedProxy(perm)) + // Singu note: do not allow proxy powers for exporting. + if ((PERM_OWNER != op && PERM_EXPORT != op) && isGrantedProxy(perm)) { agent_proxy = owner_id; } diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index c55ff49d4..a1c56811f 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -325,35 +325,96 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLNotificationsUtil::add("AIXMLImportParseError", args); return; } - LLXmlTreeNode* root = xml.getRoot(); - if (!root) - { - llwarns << "No root node found in wearable import file: " << filename << llendl; + LLXmlTreeNode* root = xml.getRoot(); + if (!root) + { + llwarns << "No root node found in wearable import file: " << filename << llendl; LLNotificationsUtil::add("AIXMLImportParseError", args); - return; - } + return; + } - //------------------------------------------------------------------------- - // (root) - //------------------------------------------------------------------------- - if (!root->hasName("linden_genepool")) - { - llwarns << "Invalid wearable import file (missing linden_genepool header): " << filename << llendl; + //------------------------------------------------------------------------- + // (root) + //------------------------------------------------------------------------- + if (!root->hasName("linden_genepool")) + { + llwarns << "Invalid wearable import file (missing linden_genepool header): " << filename << llendl; LLNotificationsUtil::add("AIXMLImportRootTypeError", args); return; - } + } static LLStdStringHandle const version_string = LLXmlTree::addAttributeString("version"); std::string version; if (!root->getFastAttributeString(version_string, version) || (version != "1.0")) { - llwarns << "Invalid linden_genepool version: " << version << " in file: " << filename << llendl; + llwarns << "Invalid or incompatible linden_genepool version: " << version << " in file: " << filename << llendl; + args["TAG"] = "version"; + args["VERSIONMAJOR"] = "1"; + LLNotificationsUtil::add("AIXMLImportRootVersionError", args); + return; + } + static LLStdStringHandle const metaversion_string = LLXmlTree::addAttributeString("metaversion"); + std::string metaversion; + U32 metaversion_major; + if (!root->getFastAttributeString(metaversion_string, metaversion)) + { + llwarns << "Invalid linden_genepool metaversion: " << metaversion << " in file: " << filename << llendl; + metaversion_major = 0; + } + else if (!LLStringUtil::convertToU32(metaversion, metaversion_major) || metaversion_major > 1) + { + llwarns << "Invalid or incompatible linden_genepool metaversion: " << metaversion << " in file: " << filename << llendl; + args["TAG"] = "metaversion"; + args["VERSIONMAJOR"] = "1"; LLNotificationsUtil::add("AIXMLImportRootVersionError", args); return; } - //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + // + //------------------------------------------------------------------------- + std::string gridnick; + LLDate date; + bool different_grid = false; // By default assume it was exported on the same grid as we're on now. + bool mixed_grids = false; // Set to true if two different grids (might) share UUIDs. Currently only "secondlife" and "secondlife_beta". + if (metaversion_major >= 1) + { + static LLStdStringHandle const gridnick_string = LLXmlTree::addAttributeString("gridnick"); + static LLStdStringHandle const date_string = LLXmlTree::addAttributeString("date"); + std::string date_s; + bool invalid = true; + LLXmlTreeNode* meta_node = root->getChildByName("meta"); + if (!meta_node) + { + llwarns << "No meta (1) in wearable import file: " << filename << llendl; + } + else if (!meta_node->getFastAttributeString(gridnick_string, gridnick)) + { + llwarns << "meta tag in file: " << filename << " is missing the 'gridnick' parameter." << llendl; + } + else if (!meta_node->getFastAttributeString(date_string, date_s) || !date.fromString(date_s)) + { + llwarns << "meta tag in file: " << filename << " is missing or invalid 'date' parameter." << llendl; + } + else + { + invalid = false; + std::string current_gridnick = gHippoGridManager->getConnectedGrid()->getGridNick(); + different_grid = gridnick != current_gridnick; + mixed_grids = (gridnick == "secondlife" && current_gridnick == "secondlife_beta") || + (gridnick == "secondlife_beta" && current_gridnick == "secondlife"); + } + if (invalid) + { + LLNotificationsUtil::add("AIXMLImportInvalidError", args); + return; + } + } + + static LLStdStringHandle const name_string = LLXmlTree::addAttributeString("name"); + + //------------------------------------------------------------------------- // - //------------------------------------------------------------------------- + //------------------------------------------------------------------------- LLXmlTreeNode* archetype_node = root->getChildByName("archetype"); if (!archetype_node) { @@ -361,13 +422,62 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) LLNotificationsUtil::add("AIXMLImportInvalidError", args); return; } + // Legacy that name="" exists. Using it as human (only) readable type label of contents. Don't use it for anything else because it might not be set. + std::string label = "???"; + if (metaversion_major >= 1) + { + if (!archetype_node->getFastAttributeString(name_string, label)) + { + llwarns << "archetype tag in file: " << filename << " is missing the 'name' parameter." << llendl; + } + } + + //------------------------------------------------------------------------- + // + //------------------------------------------------------------------------- + std::string path; + std::string wearable_name; + std::string wearable_description; + if (metaversion_major >= 1) + { + static LLStdStringHandle const path_string = LLXmlTree::addAttributeString("path"); + static LLStdStringHandle const description_string = LLXmlTree::addAttributeString("description"); + bool invalid = true; + LLXmlTreeNode* meta_node = archetype_node->getChildByName("meta"); + if (!meta_node) + { + llwarns << "No meta (2) in wearable import file: " << filename << llendl; + } + else if (!meta_node->getFastAttributeString(path_string, path)) + { + llwarns << "meta tag in file: " << filename << " is missing the 'path' parameter." << llendl; + } + else if (!meta_node->getFastAttributeString(name_string, wearable_name)) + { + llwarns << "meta tag in file: " << filename << " is missing the 'name' parameter." << llendl; + } + else if (!meta_node->getFastAttributeString(description_string, wearable_description)) + { + llwarns << "meta tag in file: " << filename << " is missing the 'description' parameter." << llendl; + } + else + { + invalid = false; + } + if (invalid) + { + LLNotificationsUtil::add("AIXMLImportInvalidError", args); + return; + } + } // Parse the XML content. static LLStdStringHandle const id_string = LLXmlTree::addAttributeString("id"); static LLStdStringHandle const value_string = LLXmlTree::addAttributeString("value"); static LLStdStringHandle const te_string = LLXmlTree::addAttributeString("te"); static LLStdStringHandle const uuid_string = LLXmlTree::addAttributeString("uuid"); - bool found = false; + bool found_param = false; + bool found_texture = false; for(LLXmlTreeNode* child = archetype_node->getFirstChild(); child; child = archetype_node->getNextChild()) { if (child->hasName("param")) @@ -379,13 +489,13 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) if (!child->getFastAttributeString(id_string, id_s) || !LLStringUtil::convertToU32(id_s, id) || !child->getFastAttributeString(value_string, value_s) || !LLStringUtil::convertToF32(value_s, value)) { - llwarns << "Possible syntax error or corruption for node in " << filename << llendl; - continue; + llwarns << "Possible syntax error or corruption for node in " << filename << llendl; + continue; } LLVisualParam* visual_param = edit_wearable->getVisualParam(id); if (visual_param) { - found = true; + found_param = true; visual_param->setWeight(value, FALSE); } } @@ -398,27 +508,44 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) if (!child->getFastAttributeString(te_string, te_s) || !LLStringUtil::convertToS32(te_s, te) || te < 0 || te >= TEX_NUM_INDICES || !child->getFastAttributeString(uuid_string, uuid_s) || !uuid.set(uuid_s, TRUE)) { - llwarns << "Possible syntax error or corruption for node in " << filename << llendl; - continue; + llwarns << "Possible syntax error or corruption for node in " << filename << llendl; + continue; } ETextureIndex te_index = (ETextureIndex)te; LLWearableType::EType te_wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(te_index); if (te_wearable_type == edit_wearable->getType()) { - found = true; - panel_edit_wearable->setNewImageID(te_index, uuid); + found_texture = true; + if (!different_grid || mixed_grids) + { + panel_edit_wearable->setNewImageID(te_index, uuid); + } } } } - if (found) + if (found_param || found_texture) { edit_wearable->writeToAvatar(gAgentAvatarp); gAgentAvatarp->updateVisualParams(); panel_edit_wearable->updateScrollingPanelUI(); + if (found_texture && different_grid) + { + args["EXPORTGRID"] = gridnick; + args["CURRENTGRID"] = gHippoGridManager->getConnectedGrid()->getGridNick(); + if (mixed_grids) + { + LLNotificationsUtil::add("AIXMLImportMixedGrid", args); + } + else + { + LLNotificationsUtil::add("AIXMLImportDifferentGrid", args); + } + } } else { args["TYPE"] = panel_edit_wearable->LLPanel::getLabel(); + args["ARCHETYPENAME"] = label; LLNotificationsUtil::add("AIXMLImportWearableTypeMismatch", args); } } diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index c2d3c549b..aef365e7b 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1422,11 +1422,7 @@ bool LLPanelEditWearable::updatePermissions() // Exporting (of slider values) is allowed when the wearable is full perm, and owned by and created by the user. // Of course, only modifiable is enough for the user to write down the values and enter them else where... but why make it easy for them to break the ToS. - if (is_complete && - gAgent.getID() == item->getPermissions().getOwner() && - gAgent.getID() == item->getPermissions().getCreator() && - (PERM_ITEM_UNRESTRICTED & - perm_mask) == PERM_ITEM_UNRESTRICTED) + if (is_complete && item->getPermissions().allowExportBy(gAgent.getID())) { can_export = true; } diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 9f3414448..fa7f0565c 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -1177,7 +1177,7 @@ void LLPanelPermissions::onCommitEveryoneCopy(LLUICtrl *ctrl, void *data) void LLPanelPermissions::onCommitExport(const LLSD& param) { - LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_EVERYONE, param, PERM_EXPORT); + LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_EVERYONE, param.asBoolean(), PERM_EXPORT); } // static diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 7bd68ad4a..7d84af13a 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5926,6 +5926,12 @@ BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) } } + if (PERM_EXPORT == op) + { + // No proxy or god powers allowed. + return mPermissions->allowExportBy(gAgent.getID()); + } + // Calculate proxy_agent_id and group_id to use for permissions checks. // proxy_agent_id may be set to the object owner through group powers. // group_id can only be set to the object's group, if the agent is in that group. diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 21d4226a6..e384b8bdb 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2895,15 +2895,7 @@ class LLObjectEnableExport : public view_listener_t const LLSD& userdata; virtual bool apply(LLSelectNode* node) { - // Note: the actual permission checking algorithm depends on the grid TOS and must be - // performed for each prim and texture. This is done later in llviewerobjectbackup.cpp. - // This means that even if the item is enabled in the menu, the export may fail should - // the permissions not be met for each exported asset. The permissions check below - // therefore only corresponds to the minimal permissions requirement common to all grids. - LLPermissions *item_permissions = node->mPermissions; - return (gAgent.getID() == item_permissions->getOwner() && - (gAgent.getID() == item_permissions->getCreator() || - (item_permissions->getMaskOwner() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)); + return node->mPermissions->allowExportBy(gAgent.getID()); } }; ff * the_ff = new ff(userdata); diff --git a/indra/newview/llviewerobjectbackup.cpp b/indra/newview/llviewerobjectbackup.cpp index 09783ed21..932dde3a6 100644 --- a/indra/newview/llviewerobjectbackup.cpp +++ b/indra/newview/llviewerobjectbackup.cpp @@ -398,18 +398,7 @@ void LLObjectBackup::exportObject_continued(AIFilePicker* filepicker) bool LLObjectBackup::validatePerms(const LLPermissions *item_permissions) { - if (gHippoGridManager->getConnectedGrid()->isSecondLife()) - { - // In Second Life, you must be the creator to be permitted to export the asset. - return (gAgent.getID() == item_permissions->getOwner() && - gAgent.getID() == item_permissions->getCreator()); - } - else - { - // Out of Second Life, simply check that the asset is full perms. - return (gAgent.getID() == item_permissions->getOwner() && - (item_permissions->getMaskOwner() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED); - } + return item_permissions->allowExportBy(gAgent.getID()); } // So far, only Second Life forces TPVs to verify the creator for textures... diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 5445fc06f..6eb27cb20 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -165,7 +165,7 @@ Import Failure: the file "[FILE]" is not a linden_genepool XML file. icon="alertmodal.tga" name="AIXMLImportRootVersionError" type="alertmodal"> -Import Failure: the file "[FILE]" contains linden_genepool XML data of the wrong version. Version 1.0 is required. +Import Failure: the file "[FILE]" contains linden_genepool XML data with the wrong [TAG]. Version "[VERSIONMAJOR].0" or less is required. + +Import was successful but note that the wearable was exported on grid [EXPORTGRID] (and the current grid is [CURRENTGRID]). Texture UUIDs have NOT been applied! + + + +Import was successful but note that the wearable was exported on grid [EXPORTGRID] (and the current grid is [CURRENTGRID]). Texture UUIDs have been applied but might not exist here! + + Import Warning: the file "[FILE]" does not contain a wearable of type [TYPE]. -Please select the correct type before importing. +It contains an archetype of type [ARCHETYPENAME]. Please select the correct type before importing.