diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 864a79c38a02ca6b6fcfb94af73a600ff74bb7b4..52e987dac90e37aed7966ef0b778896484ec2322 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11417,5 +11417,16 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
+    <key>OutfitOperationsTimeout</key>
+    <map>
+      <key>Comment</key>
+      <string>Timeout for outfit related operations.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>180</integer>
+    </map>
 </map>
 </llsd>
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index a899926938ce2266c87f7d2843c55ee7f2bcb658..135629bb8df18bf945becf1768cae4ea593f2156 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -38,11 +38,13 @@
 #include "llagentwearables.h"
 #include "llappearancemgr.h"
 #include "llcommandhandler.h"
+#include "lleventtimer.h"
 #include "llgesturemgr.h"
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
 #include "llinventoryobserver.h"
 #include "llnotificationsutil.h"
+#include "lloutfitobserver.h"
 #include "llpaneloutfitsinventory.h"
 #include "llselectmgr.h"
 #include "llsidepanelappearance.h"
@@ -55,6 +57,34 @@
 
 char ORDER_NUMBER_SEPARATOR('@');
 
+class LLOutfitUnLockTimer: public LLEventTimer
+{
+public:
+	LLOutfitUnLockTimer(F32 period) : LLEventTimer(period)
+	{
+		// restart timer on BOF changed event
+		LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(
+				&LLOutfitUnLockTimer::reset, this));
+		stop();
+	}
+
+	/*virtual*/
+	BOOL tick()
+	{
+		if(mEventTimer.hasExpired())
+		{
+			LLAppearanceMgr::instance().setOutfitLocked(false);
+		}
+		return FALSE;
+	}
+	void stop() { mEventTimer.stop(); }
+	void start() { mEventTimer.start(); }
+	void reset() { mEventTimer.reset(); }
+	BOOL getStarted() { return mEventTimer.getStarted(); }
+
+	LLTimer&  getEventTimer() { return mEventTimer;}
+};
+
 // support for secondlife:///app/appearance SLapps
 class LLAppearanceHandler : public LLCommandHandler
 {
@@ -762,6 +792,27 @@ void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& respo
 	}
 }
 
+void LLAppearanceMgr::setOutfitLocked(bool locked)
+{
+	if (mOutfitLocked == locked)
+	{
+		return;
+	}
+
+	mOutfitLocked = locked;
+	if (locked)
+	{
+		mUnlockOutfitTimer->reset();
+		mUnlockOutfitTimer->start();
+	}
+	else
+	{
+		mUnlockOutfitTimer->stop();
+	}
+
+	LLOutfitObserver::instance().notifyOutfitLockChanged();
+}
+
 void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
 {
 	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
@@ -1810,6 +1861,14 @@ void LLAppearanceMgr::onFirstFullyVisible()
 
 bool LLAppearanceMgr::updateBaseOutfit()
 {
+	if (isOutfitLocked())
+	{
+		// don't allow modify locked outfit
+		llassert(!isOutfitLocked());
+		return false;
+	}
+	setOutfitLocked(true);
+
 	const LLUUID base_outfit_id = getBaseOutfitUUID();
 	if (base_outfit_id.isNull()) return false;
 
@@ -2138,6 +2197,14 @@ LLAppearanceMgr::LLAppearanceMgr():
 	mAttachmentInvLinkEnabled(false),
 	mOutfitIsDirty(false)
 {
+	LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();
+
+	// unlock outfit on save operation completed
+	outfit_observer.addCOFSavedCallback(boost::bind(
+			&LLAppearanceMgr::setOutfitLocked, this, false));
+
+	mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32(
+			"OutfitOperationsTimeout")));
 }
 
 LLAppearanceMgr::~LLAppearanceMgr()
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index f1beef5857fd732fb5dab31f0c03cf725e0c440d..2227a43cd88d4a6ef8890e2665217b06a89faf4c 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -42,10 +42,12 @@
 class LLWearable;
 class LLWearableHoldingPattern;
 class LLInventoryCallback;
+class LLOutfitUnLockTimer;
 
 class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 {
 	friend class LLSingleton<LLAppearanceMgr>;
+	friend class LLOutfitUnLockTimer;
 	
 public:
 	typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
@@ -162,6 +164,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	// COF is processed if cat_id is not specified
 	void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null);
 
+	bool isOutfitLocked() { return mOutfitLocked; }
+
 protected:
 	LLAppearanceMgr();
 	~LLAppearanceMgr();
@@ -186,10 +190,20 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 
 	static void onOutfitRename(const LLSD& notification, const LLSD& response);
 
+	void setOutfitLocked(bool locked);
+
 	std::set<LLUUID> mRegisteredAttachments;
 	bool mAttachmentInvLinkEnabled;
 	bool mOutfitIsDirty;
 
+	/**
+	 * Lock for blocking operations on outfit until server reply or timeout exceed
+	 * to avoid unsynchronized outfit state or performing duplicate operations.
+	 */
+	bool mOutfitLocked;
+
+	std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer;
+
 	//////////////////////////////////////////////////////////////////////////////////
 	// Item-specific convenience functions 
 public:
diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp
index 848b59561322b7c42e4d7a5ddea5fac3c076096f..5652a98981f5772f04a64705ad378d2f0fd3c309 100644
--- a/indra/newview/lloutfitobserver.cpp
+++ b/indra/newview/lloutfitobserver.cpp
@@ -56,9 +56,9 @@ void LLOutfitObserver::changed(U32 mask)
 	if (!gInventory.isInventoryUsable())
 		return;
 
-	bool panel_updated = checkCOF();
+	bool COF_changed = checkCOF();
 
-	if (!panel_updated)
+	if (!COF_changed)
 	{
 		checkBaseOutfit();
 	}
@@ -87,6 +87,7 @@ bool LLOutfitObserver::checkCOF()
 
 	mCOFLastVersion = cof_version;
 
+	// dirtiness state should be updated before sending signal
 	LLAppearanceMgr::getInstance()->updateIsDirty();
 	mCOFChanged();
 
@@ -120,6 +121,16 @@ void LLOutfitObserver::checkBaseOutfit()
 	}
 
 	LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance();
+	// dirtiness state should be updated before sending signal
 	app_mgr.updateIsDirty();
 	mBOFChanged();
+
+	if (mLastOutfitDirtiness != app_mgr.isOutfitDirty())
+	{
+		if(!app_mgr.isOutfitDirty())
+		{
+			mCOFSaved();
+		}
+		mLastOutfitDirtiness = app_mgr.isOutfitDirty();
+	}
 }
diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h
index 4cb40ead15544a6ad771306ceb5428c94035494b..a4b5fbe04acf1c66d0122959dc0d11056f0619ba 100644
--- a/indra/newview/lloutfitobserver.h
+++ b/indra/newview/lloutfitobserver.h
@@ -48,6 +48,8 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	virtual void changed(U32 mask);
 
+	void notifyOutfitLockChanged() { mOutfitLockChanged();  }
+
 	typedef boost::signals2::signal<void (void)> signal_t;
 
 	void addBOFReplacedCallback(const signal_t::slot_type& cb) { mBOFReplaced.connect(cb); }
@@ -56,6 +58,10 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	void addCOFChangedCallback(const signal_t::slot_type& cb) { mCOFChanged.connect(cb); }
 
+	void addCOFSavedCallback(const signal_t::slot_type& cb) { mCOFSaved.connect(cb); }
+
+	void addOutfitLockChangedCallback(const signal_t::slot_type& cb) { mOutfitLockChanged.connect(cb); }
+
 protected:
 	LLOutfitObserver();
 
@@ -73,10 +79,18 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	S32 mBaseOutfitLastVersion;
 
+	bool mLastOutfitDirtiness;
+
 private:
 	signal_t mBOFReplaced;
 	signal_t mBOFChanged;
 	signal_t mCOFChanged;
+	signal_t mCOFSaved;
+
+	/**
+	 * Signal for changing state of outfit lock.
+	 */
+	signal_t mOutfitLockChanged;
 };
 
 #endif /* LL_OUTFITOBSERVER_H */
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index ce382541c671335d3627523a0112237058f8d928..f3f67f2b0319b50547caf6abac8bfe64f12b50c8 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -198,6 +198,7 @@ LLPanelOutfitEdit::LLPanelOutfitEdit()
 	LLOutfitObserver& observer = LLOutfitObserver::instance();
 	observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this));
 	observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
+	observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
 	observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::update, this));
 	
 	mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES);
@@ -666,12 +667,13 @@ void LLPanelOutfitEdit::updateCurrentOutfitName()
 void LLPanelOutfitEdit::updateVerbs()
 {
 	bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty();
+	bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked();
 	bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull();
 
-	mSaveComboBtn->setSaveBtnEnabled(outfit_is_dirty);
+	mSaveComboBtn->setSaveBtnEnabled(!outfit_locked && outfit_is_dirty);
 	childSetEnabled(REVERT_BTN, outfit_is_dirty && has_baseoutfit);
 
-	mSaveComboBtn->setMenuItemEnabled("save_outfit", outfit_is_dirty);
+	mSaveComboBtn->setMenuItemEnabled("save_outfit", !outfit_locked && outfit_is_dirty);
 
 	mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing"));
 
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 8836672f91e39d3e0f496ae682c7769d2e77568d..8b451c156c3da71f20ddff0235be301dcc1ec1a5 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -209,6 +209,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() :
 	LLOutfitObserver& observer = LLOutfitObserver::instance();
 	observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
 	observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
+	observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
 }
 
 LLPanelOutfitsInventory::~LLPanelOutfitsInventory()
@@ -522,7 +523,7 @@ void LLPanelOutfitsInventory::updateListCommands()
 {
 	bool trash_enabled = isActionEnabled("delete");
 	bool wear_enabled = isActionEnabled("wear");
-	bool make_outfit_enabled = isActionEnabled("make_outfit");
+	bool make_outfit_enabled = isActionEnabled("save_outfit");
 
 	mListCommands->childSetEnabled("trash_btn", trash_enabled);
 	mListCommands->childSetEnabled("wear_btn", wear_enabled);
@@ -668,9 +669,12 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata)
 			return FALSE;
 		}
 	}
-	if (command_name == "make_outfit")
+	if (command_name == "save_outfit")
 	{
-		return LLAppearanceMgr::getInstance()->isOutfitDirty();
+		bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked();
+		bool outfit_dirty =LLAppearanceMgr::getInstance()->isOutfitDirty();
+		// allow save only if outfit isn't locked and is dirty
+		return !outfit_locked && outfit_dirty;
 	}
    
 	if (command_name == "edit" ||