From 2d19b297a98d8be87c487f6b93e098dce9ca908c Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Fri, 13 May 2011 18:23:11 +0300 Subject: STORM-1202 FIXED Time spin control Implemented time spin control which is like spin control, but shows and allows to edit time string in "hh:mm PM/AM" format. Implemented according to the WLRS spec. --- indra/llui/CMakeLists.txt | 2 + indra/llui/lllineeditor.cpp | 106 +++++++++--- indra/llui/lllineeditor.h | 4 + indra/llui/lltextvalidate.cpp | 33 ++++ indra/llui/lltextvalidate.h | 1 + indra/llui/lltimectrl.cpp | 390 ++++++++++++++++++++++++++++++++++++++++++ indra/llui/lltimectrl.h | 125 ++++++++++++++ 7 files changed, 640 insertions(+), 21 deletions(-) create mode 100644 indra/llui/lltimectrl.cpp create mode 100644 indra/llui/lltimectrl.h (limited to 'indra/llui') diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 33ab2e93b5..72329c70fe 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -93,6 +93,7 @@ set(llui_SOURCE_FILES lltextparser.cpp lltextutil.cpp lltextvalidate.cpp + lltimectrl.cpp lltransutil.cpp lltoggleablemenu.cpp lltooltip.cpp @@ -191,6 +192,7 @@ set(llui_HEADER_FILES lltextparser.h lltextutil.h lltextvalidate.h + lltimectrl.h lltoggleablemenu.h lltooltip.h lltransutil.h diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index d99ee5a545..66c607e988 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -81,6 +81,7 @@ LLLineEditor::Params::Params() : max_length(""), keystroke_callback("keystroke_callback"), prevalidate_callback("prevalidate_callback"), + prevalidate_input_callback("prevalidate_input_callback"), background_image("background_image"), background_image_disabled("background_image_disabled"), background_image_focused("background_image_focused"), @@ -173,6 +174,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) updateTextPadding(); setCursor(mText.length()); + setPrevalidate(p.prevalidate_input_callback()); setPrevalidate(p.prevalidate_callback()); LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile @@ -401,23 +403,16 @@ void LLLineEditor::setText(const LLStringExplicit &new_text) // Picks a new cursor position based on the actual screen size of text being drawn. void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) { - const llwchar* wtext = mText.getWString().c_str(); - LLWString asterix_text; - if (mDrawAsterixes) - { - for (S32 i = 0; i < mText.length(); i++) - { - asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); - } - wtext = asterix_text.c_str(); - } + S32 cursor_pos = calcCursorPos(local_mouse_x); + + S32 left_pos = llmin( mSelectionStart, cursor_pos ); + S32 selection_length = llabs( mSelectionStart - cursor_pos ); + const LLWString& text = mText.getWString(); + const LLWString& substr = text.substr(left_pos, selection_length); + + if (mPrevalidateInputFunc && mIsSelecting && !mPrevalidateInputFunc(substr)) + return; - S32 cursor_pos = - mScrollHPos + - mGLFont->charFromPixelOffset( - wtext, mScrollHPos, - (F32)(local_mouse_x - mTextLeftEdge), - (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive setCursor(cursor_pos); } @@ -501,6 +496,9 @@ BOOL LLLineEditor::canSelectAll() const void LLLineEditor::selectAll() { + if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString())) + return; + mSelectionStart = mText.length(); mSelectionEnd = 0; setCursor(mSelectionEnd); @@ -586,6 +584,9 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mask & MASK_SHIFT) { + // assume we're starting a drag select + mIsSelecting = TRUE; + // Handle selection extension S32 old_cursor_pos = getCursor(); setCursorAtLocalPos(x); @@ -620,8 +621,6 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) mSelectionStart = old_cursor_pos; mSelectionEnd = getCursor(); } - // assume we're starting a drag select - mIsSelecting = TRUE; } else { @@ -792,6 +791,9 @@ void LLLineEditor::removeChar() { if( getCursor() > 0 ) { + if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor()-1, 1))) + return; + mText.erase(getCursor() - 1, 1); setCursor(getCursor() - 1); @@ -812,6 +814,9 @@ void LLLineEditor::addChar(const llwchar uni_char) } else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { + if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor(), 1))) + return; + mText.erase(getCursor(), 1); } @@ -860,6 +865,13 @@ void LLLineEditor::extendSelection( S32 new_cursor_pos ) startSelection(); } + S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); + S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); + const LLWString& selection = mText.getWString(); + + if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length))) + return; + setCursor(new_cursor_pos); mSelectionEnd = getCursor(); } @@ -992,6 +1004,10 @@ void LLLineEditor::deleteSelection() { S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 selection_length = llabs( mSelectionStart - mSelectionEnd ); + const LLWString& selection = mText.getWString(); + + if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length))) + return; mText.erase(left_pos, selection_length); deselect(); @@ -1009,12 +1025,16 @@ void LLLineEditor::cut() { if( canCut() ) { + S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); + S32 length = llabs( mSelectionStart - mSelectionEnd ); + const LLWString& selection = mText.getWString(); + + if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, length))) + return; + // Prepare for possible rollback LLLineEditorRollback rollback( this ); - - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); gClipboard.copyFromSubstring( mText.getWString(), left_pos, length ); deleteSelection(); @@ -1094,6 +1114,9 @@ void LLLineEditor::pasteHelper(bool is_primary) if (!paste.empty()) { + if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(paste) ) + return; + // Prepare for possible rollback LLLineEditorRollback rollback(this); @@ -1441,6 +1464,11 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) LLLineEditorRollback rollback( this ); + LLWString u_char; + u_char.assign(1, uni_char); + if (mPrevalidateInputFunc && !mPrevalidateInputFunc(u_char)) + return handled; + addChar(uni_char); mKeystrokeTimer.reset(); @@ -1492,6 +1520,15 @@ void LLLineEditor::doDelete() } else if ( getCursor() < mText.length()) { + const LLWString& selection = mText.getWString(); + + if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(getCursor(), 1))) + { + if( mKeystrokeCallback ) + mKeystrokeCallback( this ); + + return; + } setCursor(getCursor() + 1); removeChar(); } @@ -1839,6 +1876,27 @@ S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const return result; } +S32 LLLineEditor::calcCursorPos(S32 mouse_x) +{ + const llwchar* wtext = mText.getWString().c_str(); + LLWString asterix_text; + if (mDrawAsterixes) + { + for (S32 i = 0; i < mText.length(); i++) + { + asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK); + } + wtext = asterix_text.c_str(); + } + + S32 cur_pos = mScrollHPos + + mGLFont->charFromPixelOffset( + wtext, mScrollHPos, + (F32)(mouse_x - mTextLeftEdge), + (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive + + return cur_pos; +} //virtual void LLLineEditor::clear() { @@ -1932,6 +1990,12 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func) updateAllowingLanguageInput(); } +void LLLineEditor::setPrevalidateInputText(LLTextValidate::validate_func_t func) +{ + mPrevalidateInputFunc = func; + updateAllowingLanguageInput(); +} + // static BOOL LLLineEditor::postvalidateFloat(const std::string &str) { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 7b5fa218f2..1e29fd0dbf 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -75,6 +75,7 @@ public: Optional keystroke_callback; Optional prevalidate_callback; + Optional prevalidate_input_callback; Optional border; @@ -231,6 +232,7 @@ public: // Prevalidation controls which keystrokes can affect the editor void setPrevalidate( LLTextValidate::validate_func_t func ); + void setPrevalidateInputText( LLTextValidate::validate_func_t func ); static BOOL postvalidateFloat(const std::string &str); // line history support: @@ -250,6 +252,7 @@ private: void addChar(const llwchar c); void setCursorAtLocalPos(S32 local_mouse_x); S32 findPixelNearestPos(S32 cursor_offset = 0) const; + S32 calcCursorPos(S32 mouse_x); BOOL handleSpecialKey(KEY key, MASK mask); BOOL handleSelectionKey(KEY key, MASK mask); BOOL handleControlKey(KEY key, MASK mask); @@ -311,6 +314,7 @@ protected: S32 mLastSelectionEnd; LLTextValidate::validate_func_t mPrevalidateFunc; + LLTextValidate::validate_func_t mPrevalidateInputFunc; LLFrameTimer mKeystrokeTimer; LLTimer mTripleClickTimer; diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp index 4b9faa0560..234e600ccd 100644 --- a/indra/llui/lltextvalidate.cpp +++ b/indra/llui/lltextvalidate.cpp @@ -188,6 +188,39 @@ namespace LLTextValidate return success; } + bool validateNonNegativeS32NoSpace(const LLWString &str) + { + LLLocale locale(LLLocale::USER_LOCALE); + + LLWString test_str = str; + S32 len = test_str.length(); + bool success = TRUE; + if(0 < len) + { + if('-' == test_str[0]) + { + success = FALSE; + } + S32 i = 0; + while(success && (i < len)) + { + if(!LLStringOps::isDigit(test_str[i]) || LLStringOps::isSpace(test_str[i++])) + { + success = FALSE; + } + } + } + if (success) + { + S32 val = strtol(wstring_to_utf8str(test_str).c_str(), NULL, 10); + if (val < 0) + { + success = FALSE; + } + } + return success; + } + bool validateAlphaNum(const LLWString &str) { LLLocale locale(LLLocale::USER_LOCALE); diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h index 84644be30c..5c830d7db3 100644 --- a/indra/llui/lltextvalidate.h +++ b/indra/llui/lltextvalidate.h @@ -46,6 +46,7 @@ namespace LLTextValidate bool validateInt(const LLWString &str ); bool validatePositiveS32(const LLWString &str); bool validateNonNegativeS32(const LLWString &str); + bool validateNonNegativeS32NoSpace(const LLWString &str); bool validateAlphaNum(const LLWString &str ); bool validateAlphaNumSpace(const LLWString &str ); bool validateASCIIPrintableNoPipe(const LLWString &str); diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp new file mode 100644 index 0000000000..33e8db432f --- /dev/null +++ b/indra/llui/lltimectrl.cpp @@ -0,0 +1,390 @@ +/** + * @file lltimectrl.cpp + * @brief LLTimeCtrl base class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lltimectrl.h" + +#include "llui.h" +#include "lluiconstants.h" + +#include "llbutton.h" +#include "llfontgl.h" +#include "lllineeditor.h" +#include "llkeyboard.h" +#include "llstring.h" +#include "lltextbox.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register time_r("time"); + +const U32 AMPM_LEN = 3; +const U32 MINUTES_MIN = 0; +const U32 MINUTES_MAX = 59; +const U32 HOURS_MIN = 1; +const U32 HOURS_MAX = 12; + +LLTimeCtrl::Params::Params() +: label_width("label_width"), + allow_text_entry("allow_text_entry", true), + text_enabled_color("text_enabled_color"), + text_disabled_color("text_disabled_color"), + up_button("up_button"), + down_button("down_button") +{} + +LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) +: LLUICtrl(p), + mLabelBox(NULL), + mTextEnabledColor(p.text_enabled_color()), + mTextDisabledColor(p.text_disabled_color()), + mHours(HOURS_MIN), + mMinutes(MINUTES_MIN) +{ + static LLUICachedControl spinctrl_spacing ("UISpinctrlSpacing", 0); + static LLUICachedControl spinctrl_btn_width ("UISpinctrlBtnWidth", 0); + static LLUICachedControl spinctrl_btn_height ("UISpinctrlBtnHeight", 0); + S32 centered_top = getRect().getHeight(); + S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height; + S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40)); + S32 editor_left = label_width + spinctrl_spacing; + + //================= Label =================// + if( !p.label().empty() ) + { + LLRect label_rect( 0, centered_top, label_width, centered_bottom ); + LLTextBox::Params params; + params.name("TimeCtrl Label"); + params.rect(label_rect); + params.initial_value(p.label()); + if (p.font.isProvided()) + { + params.font(p.font); + } + mLabelBox = LLUICtrlFactory::create (params); + addChild(mLabelBox); + + editor_left = label_rect.mRight + spinctrl_spacing; + } + + S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing; + + //================= Editor ================// + LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom ); + LLLineEditor::Params params; + params.name("SpinCtrl Editor"); + params.rect(editor_rect); + if (p.font.isProvided()) + { + params.font(p.font); + } + + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + params.max_length.chars(8); + params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1)); + mEditor = LLUICtrlFactory::create (params); + mEditor->setPrevalidateInputText(LLTextValidate::validateNonNegativeS32NoSpace); + mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1)); + mEditor->setText(LLStringExplicit("0:00 AM")); + addChild(mEditor); + + //================= Spin Buttons ==========// + LLButton::Params up_button_params(p.up_button); + up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height); + + up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); + up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this)); + mUpBtn = LLUICtrlFactory::create(up_button_params); + addChild(mUpBtn); + + LLButton::Params down_button_params(p.down_button); + down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height); + down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); + down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this)); + mDownBtn = LLUICtrlFactory::create(down_button_params); + addChild(mDownBtn); + + setUseBoundingRect( TRUE ); +} + +BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask) +{ + if (mEditor->hasFocus()) + { + if(key == KEY_UP) + { + onUpBtn(); + return TRUE; + } + if(key == KEY_DOWN) + { + onDownBtn(); + return TRUE; + } + } + return FALSE; +} + +void LLTimeCtrl::onUpBtn() +{ + switch(getEditingPart()) + { + case HOURS: + increaseHours(); + break; + case MINUTES: + increaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + buildTimeString(); + mEditor->setText(mTimeString); +} + +void LLTimeCtrl::onDownBtn() +{ + switch(getEditingPart()) + { + case HOURS: + decreaseHours(); + break; + case MINUTES: + decreaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + buildTimeString(); + mEditor->setText(mTimeString); +} + +void LLTimeCtrl::onFocusLost() +{ + buildTimeString(); + mEditor->setText(mTimeString); + + LLUICtrl::onFocusLost(); +} + +void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor) +{ + LLWString time_str = line_editor->getWText(); + switch(getEditingPart()) + { + case HOURS: + validateHours(getHoursWString(time_str)); + break; + case MINUTES: + validateMinutes(getMinutesWString(time_str)); + break; + default: + break; + } +} + +bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr) +{ + if (!isHoursStringValid(getHoursWString(wstr)) || !isMinutesStringValid(getMinutesWString(wstr)) || !isPMAMStringValid(wstr)) + return false; + + return true; +} + +bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr) +{ + U32 hours; + if ((!LLWStringUtil::convertToU32(wstr, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3) + return true; + + return false; +} + +bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr) +{ + U32 minutes; + if (!LLWStringUtil::convertToU32(wstr, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3) + return true; + + return false; +} + +void LLTimeCtrl::validateHours(const LLWString& wstr) +{ + U32 hours; + if (LLWStringUtil::convertToU32(wstr, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) + { + mHours = hours; + } + else + { + mHours = HOURS_MIN; + } +} + +void LLTimeCtrl::validateMinutes(const LLWString& wstr) +{ + U32 minutes; + if (LLWStringUtil::convertToU32(wstr, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX)) + { + mMinutes = minutes; + } + else + { + mMinutes = MINUTES_MIN; + } +} + +bool LLTimeCtrl::isPMAMStringValid(const LLWString &wstr) +{ + S32 len = wstr.length(); + + bool valid = (wstr[--len] == 'M') && (wstr[--len] == 'P' || wstr[len] == 'A'); + + return valid; +} + +LLWString LLTimeCtrl::getHoursWString(const LLWString& wstr) +{ + size_t colon_index = wstr.find_first_of(':'); + LLWString hours_str = wstr.substr(0, colon_index); + + return hours_str; +} + +LLWString LLTimeCtrl::getMinutesWString(const LLWString& wstr) +{ + size_t colon_index = wstr.find_first_of(':'); + ++colon_index; + + int minutes_len = wstr.length() - colon_index - AMPM_LEN; + LLWString minutes_str = wstr.substr(colon_index, minutes_len); + + return minutes_str; +} + +void LLTimeCtrl::increaseMinutes() +{ + if (++mMinutes > MINUTES_MAX) + { + mMinutes = MINUTES_MIN; + } +} + +void LLTimeCtrl::increaseHours() +{ + if (++mHours > HOURS_MAX) + { + mHours = HOURS_MIN; + } +} + +void LLTimeCtrl::decreaseMinutes() +{ + if (mMinutes-- == MINUTES_MIN) + { + mMinutes = MINUTES_MAX; + } +} + +void LLTimeCtrl::decreaseHours() +{ + if (mHours-- == HOURS_MIN) + { + mHours = HOURS_MAX; + switchDayPeriod(); + } +} + +void LLTimeCtrl::switchDayPeriod() +{ + switch (mCurrentDayPeriod) + { + case AM: + mCurrentDayPeriod = PM; + break; + case PM: + mCurrentDayPeriod = AM; + break; + } +} + +void LLTimeCtrl::buildTimeString() +{ + std::stringstream time_buf; + time_buf << mHours << ":"; + + if (mMinutes < 10) + { + time_buf << "0"; + } + + time_buf << mMinutes; + time_buf << " "; + + switch (mCurrentDayPeriod) + { + case AM: + time_buf << "AM"; + break; + case PM: + time_buf << "PM"; + break; + } + + mTimeString = time_buf.str(); +} + +LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() +{ + S32 cur_pos = mEditor->getCursor(); + LLWString time_str = mEditor->getWText(); + + size_t colon_index = time_str.find_first_of(':'); + + if (cur_pos <= colon_index) + { + return HOURS; + } + else if (cur_pos > colon_index && cur_pos <= (time_str.length() - AMPM_LEN)) + { + return MINUTES; + } + else if (cur_pos > (time_str.length() - AMPM_LEN)) + { + return DAYPART; + } + + return NONE; +} diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h new file mode 100644 index 0000000000..81d4477da4 --- /dev/null +++ b/indra/llui/lltimectrl.h @@ -0,0 +1,125 @@ +/** + * @file lltimectrl.h + * @brief Time control + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLTIMECTRL_H_ +#define LLTIMECTRL_H_ + +#include "stdtypes.h" +#include "llbutton.h" +#include "v4color.h" +#include "llrect.h" + +class LLLineEditor; + +class LLTimeCtrl +: public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional label_width; + Optional allow_text_entry; + + Optional text_enabled_color; + Optional text_disabled_color; + + Optional up_button; + Optional down_button; + + Params(); + }; +protected: + LLTimeCtrl(const Params&); + friend class LLUICtrlFactory; + + U32 getHours() const { return mHours; } + U32 getMinutes() const { return mMinutes; } + +private: + + enum EDayPeriod + { + AM, + PM + }; + + enum EEditingPart + { + HOURS, + MINUTES, + DAYPART, + NONE + }; + + virtual void onFocusLost(); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + void onUpBtn(); + void onDownBtn(); + + void onTextEntry(LLLineEditor* line_editor); + + void validateHours(const LLWString& wstr); + void validateMinutes(const LLWString& wstr); + bool isTimeStringValid(const LLWString& wstr); + + bool isPMAMStringValid(const LLWString& wstr); + bool isHoursStringValid(const LLWString& wstr); + bool isMinutesStringValid(const LLWString& wstr); + + LLWString getHoursWString(const LLWString& wstr); + LLWString getMinutesWString(const LLWString& wstr); + + void increaseMinutes(); + void increaseHours(); + + void decreaseMinutes(); + void decreaseHours(); + + void switchDayPeriod(); + + void buildTimeString(); + + EEditingPart getEditingPart(); + + class LLTextBox* mLabelBox; + + class LLLineEditor* mEditor; + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + class LLButton* mUpBtn; + class LLButton* mDownBtn; + + U32 mHours; + U32 mMinutes; + EDayPeriod mCurrentDayPeriod; + + std::string mTimeString; + + BOOL mAllowEdit; +}; +#endif /* LLTIMECTRL_H_ */ -- cgit v1.3 From 5daeefd35e1e105f403f99184ad866e8823767d4 Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Mon, 16 May 2011 17:59:05 +0300 Subject: STORM-1202 Code cleanup for LLLineEditor - Removed code duplication - Renamed prevalidateInputText to prevalidateInput --- indra/llui/lllineeditor.cpp | 65 +++++++++++++++++++++++++++------------------ indra/llui/lllineeditor.h | 8 +++++- indra/llui/lltimectrl.cpp | 2 +- 3 files changed, 47 insertions(+), 28 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 66c607e988..2527a608bf 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -174,7 +174,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) updateTextPadding(); setCursor(mText.length()); - setPrevalidate(p.prevalidate_input_callback()); + setPrevalidateInput(p.prevalidate_input_callback()); setPrevalidate(p.prevalidate_callback()); LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile @@ -406,11 +406,10 @@ void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x ) S32 cursor_pos = calcCursorPos(local_mouse_x); S32 left_pos = llmin( mSelectionStart, cursor_pos ); - S32 selection_length = llabs( mSelectionStart - cursor_pos ); - const LLWString& text = mText.getWString(); - const LLWString& substr = text.substr(left_pos, selection_length); + S32 length = llabs( mSelectionStart - cursor_pos ); + const LLWString& substr = mText.getWString().substr(left_pos, length); - if (mPrevalidateInputFunc && mIsSelecting && !mPrevalidateInputFunc(substr)) + if (mIsSelecting && !prevalidateInput(substr)) return; setCursor(cursor_pos); @@ -496,8 +495,10 @@ BOOL LLLineEditor::canSelectAll() const void LLLineEditor::selectAll() { - if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString())) + if (!prevalidateInput(mText.getWString())) + { return; + } mSelectionStart = mText.length(); mSelectionEnd = 0; @@ -791,7 +792,7 @@ void LLLineEditor::removeChar() { if( getCursor() > 0 ) { - if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor()-1, 1))) + if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) return; mText.erase(getCursor() - 1, 1); @@ -814,7 +815,7 @@ void LLLineEditor::addChar(const llwchar uni_char) } else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { - if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor(), 1))) + if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) return; mText.erase(getCursor(), 1); @@ -867,9 +868,9 @@ void LLLineEditor::extendSelection( S32 new_cursor_pos ) S32 left_pos = llmin( mSelectionStart, new_cursor_pos ); S32 selection_length = llabs( mSelectionStart - new_cursor_pos ); - const LLWString& selection = mText.getWString(); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length))) + if (!prevalidateInput(selection)) return; setCursor(new_cursor_pos); @@ -1002,11 +1003,11 @@ void LLLineEditor::deleteSelection() { if( !mReadOnly && hasSelection() ) { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_length = llabs( mSelectionStart - mSelectionEnd ); - const LLWString& selection = mText.getWString(); + S32 left_pos, selection_length; + getSelectionRange(&left_pos, &selection_length); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); - if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length))) + if (!prevalidateInput(selection)) return; mText.erase(left_pos, selection_length); @@ -1025,11 +1026,11 @@ void LLLineEditor::cut() { if( canCut() ) { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 length = llabs( mSelectionStart - mSelectionEnd ); - const LLWString& selection = mText.getWString(); + S32 left_pos, length; + getSelectionRange(&left_pos, &length); + const LLWString& selection = mText.getWString().substr(left_pos, length); - if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, length))) + if (!prevalidateInput(selection)) return; // Prepare for possible rollback @@ -1114,7 +1115,7 @@ void LLLineEditor::pasteHelper(bool is_primary) if (!paste.empty()) { - if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(paste) ) + if (!prevalidateInput(paste)) return; // Prepare for possible rollback @@ -1464,10 +1465,12 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) LLLineEditorRollback rollback( this ); - LLWString u_char; - u_char.assign(1, uni_char); - if (mPrevalidateInputFunc && !mPrevalidateInputFunc(u_char)) - return handled; + { + LLWString u_char; + u_char.assign(1, uni_char); + if (!prevalidateInput(u_char)) + return handled; + } addChar(uni_char); @@ -1520,9 +1523,9 @@ void LLLineEditor::doDelete() } else if ( getCursor() < mText.length()) { - const LLWString& selection = mText.getWString(); + const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); - if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(getCursor(), 1))) + if (!prevalidateInput(text_to_delete)) { if( mKeystrokeCallback ) mKeystrokeCallback( this ); @@ -1990,12 +1993,22 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func) updateAllowingLanguageInput(); } -void LLLineEditor::setPrevalidateInputText(LLTextValidate::validate_func_t func) +void LLLineEditor::setPrevalidateInput(LLTextValidate::validate_func_t func) { mPrevalidateInputFunc = func; updateAllowingLanguageInput(); } +bool LLLineEditor::prevalidateInput(const LLWString& wstr) +{ + if (mPrevalidateInputFunc && !mPrevalidateInputFunc(wstr)) + { + return false; + } + + return true; +} + // static BOOL LLLineEditor::postvalidateFloat(const std::string &str) { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 1e29fd0dbf..1588f59b46 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -232,9 +232,15 @@ public: // Prevalidation controls which keystrokes can affect the editor void setPrevalidate( LLTextValidate::validate_func_t func ); - void setPrevalidateInputText( LLTextValidate::validate_func_t func ); + // This method sets callback that prevents from: + // - deleting, selecting, typing, cutting, pasting characters that are not valid. + // Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed + // symbols, before existing text is modified, but setPrevalidate validates line after it was modified. + void setPrevalidateInput(LLTextValidate::validate_func_t func); static BOOL postvalidateFloat(const std::string &str); + bool prevalidateInput(const LLWString& wstr); + // line history support: void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off void updateHistory(); // stores current line in history diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index 33e8db432f..08d24a29a8 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -106,7 +106,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) params.max_length.chars(8); params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1)); mEditor = LLUICtrlFactory::create (params); - mEditor->setPrevalidateInputText(LLTextValidate::validateNonNegativeS32NoSpace); + mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1)); mEditor->setText(LLStringExplicit("0:00 AM")); addChild(mEditor); -- cgit v1.3 From 03dca7cb7e578d20ec07298cecc47e0ae67aaaf5 Mon Sep 17 00:00:00 2001 From: Leyla Farazha Date: Tue, 17 May 2011 16:38:51 -0700 Subject: fixing llaccordian crash with multiple child panels --- indra/llui/llaccordionctrltab.cpp | 2 +- .../skins/default/xui/en/sidepanel_inventory.xml | 35 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 9e4849c58b..6afe276379 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -1022,7 +1022,7 @@ void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect ) S32 panel_width = child_rect.getWidth(); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - if(mScrollbar->getVisible() != false) + if(mScrollbar && mScrollbar->getVisible() != false) { panel_top+=mScrollbar->getDocPos(); panel_width-=scrollbar_size; diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index faf5e28bb8..41b9283edc 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -75,12 +75,35 @@ min_height="150" fit_panel="false" title="MARKETPLACE INBOX"> - + + Just a panel + + + Just a panel 2 + Date: Wed, 18 May 2011 16:07:36 +0300 Subject: STORM-1202 ADDITIONAL_FIX Fixing Windows build. --- indra/llui/lltimectrl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index 08d24a29a8..4b49c45006 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -371,17 +371,17 @@ LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() S32 cur_pos = mEditor->getCursor(); LLWString time_str = mEditor->getWText(); - size_t colon_index = time_str.find_first_of(':'); + S32 colon_index = time_str.find_first_of(':'); if (cur_pos <= colon_index) { return HOURS; } - else if (cur_pos > colon_index && cur_pos <= (time_str.length() - AMPM_LEN)) + else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN)) { return MINUTES; } - else if (cur_pos > (time_str.length() - AMPM_LEN)) + else if (cur_pos > (S32)(time_str.length() - AMPM_LEN)) { return DAYPART; } -- cgit v1.3 From f02d6e7095d526bc7abc75cc796dc4f07c9b18c7 Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Wed, 18 May 2011 21:07:06 +0300 Subject: STORM-1202 ADDITIONAL_FIX Workaround for another Windows build issue. --- indra/llui/lltimectrl.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index 4b49c45006..a77842a392 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -225,7 +225,8 @@ bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr) bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr) { U32 hours; - if ((!LLWStringUtil::convertToU32(wstr, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3) + std::string utf8time = wstring_to_utf8str(wstr); + if ((!LLStringUtil::convertToU32(utf8time, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3) return true; return false; @@ -234,7 +235,8 @@ bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr) bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr) { U32 minutes; - if (!LLWStringUtil::convertToU32(wstr, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3) + std::string utf8time = wstring_to_utf8str(wstr); + if (!LLStringUtil::convertToU32(utf8time, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3) return true; return false; @@ -243,7 +245,8 @@ bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr) void LLTimeCtrl::validateHours(const LLWString& wstr) { U32 hours; - if (LLWStringUtil::convertToU32(wstr, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) + std::string utf8time = wstring_to_utf8str(wstr); + if (LLStringUtil::convertToU32(utf8time, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) { mHours = hours; } @@ -256,7 +259,8 @@ void LLTimeCtrl::validateHours(const LLWString& wstr) void LLTimeCtrl::validateMinutes(const LLWString& wstr) { U32 minutes; - if (LLWStringUtil::convertToU32(wstr, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX)) + std::string utf8time = wstring_to_utf8str(wstr); + if (LLStringUtil::convertToU32(utf8time, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX)) { mMinutes = minutes; } -- cgit v1.3 From 406195e4f20e0bea341c8a7a979b56a8259b8536 Mon Sep 17 00:00:00 2001 From: Leslie Linden Date: Tue, 24 May 2011 09:38:49 -0700 Subject: Added badges version 0.1 to buttons. Reviewed by Richard --- indra/llcommon/lldate.h | 2 + indra/llui/CMakeLists.txt | 2 + indra/llui/llbadge.cpp | 263 +++++++++++++++++++++ indra/llui/llbadge.h | 152 ++++++++++++ indra/llui/llbutton.cpp | 94 +++++++- indra/llui/llbutton.h | 12 +- indra/llxuixml/llinitparam.h | 2 + indra/newview/skins/default/colors.xml | 6 + indra/newview/skins/default/textures/textures.xml | 2 + .../skins/default/textures/widgets/Badge.png | Bin 0 -> 1716 bytes .../newview/skins/default/xui/en/widgets/badge.xml | 14 ++ 11 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 indra/llui/llbadge.cpp create mode 100644 indra/llui/llbadge.h create mode 100644 indra/newview/skins/default/textures/widgets/Badge.png create mode 100644 indra/newview/skins/default/xui/en/widgets/badge.xml (limited to 'indra/llui') diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index 7ff8b550ad..f2bf2366ac 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -34,6 +34,8 @@ #include "stdtypes.h" +#include "llpreprocessor.h" + /** * @class LLDate * @brief This class represents a particular point in time in UTC. diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 33ab2e93b5..fb585e062c 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -28,6 +28,7 @@ include_directories( set(llui_SOURCE_FILES llaccordionctrl.cpp llaccordionctrltab.cpp + llbadge.cpp llbutton.cpp llcheckboxctrl.cpp llclipboard.cpp @@ -119,6 +120,7 @@ set(llui_HEADER_FILES llaccordionctrl.h llaccordionctrltab.h + llbadge.h llbutton.h llcallbackmap.h llcheckboxctrl.h diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp new file mode 100644 index 0000000000..ea934aa93b --- /dev/null +++ b/indra/llui/llbadge.cpp @@ -0,0 +1,263 @@ +/** + * @file llbadge.cpp + * @brief Implementation for badges + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#define LLBADGE_CPP +#include "llbadge.h" + +#include "lluictrlfactory.h" + + +static LLDefaultChildRegistry::Register r("badge"); + +// Compiler optimization, generate extern template +template class LLBadge* LLView::getChild(const std::string& name, BOOL recurse) const; + + +LLBadge::Params::Params() + : image("image") + , image_color("image_color") + , label("label") + , label_color("label_color") + , location("location", LLRelPos::TOP_LEFT) + , location_percent_hcenter("location_percent_hcenter") + , location_percent_vcenter("location_percent_vcenter") + , padding_horiz("padding_horiz") + , padding_vert("padding_vert") +{ + // We set a name here so the name isn't necessary in any xml files that use badges + name = "badge"; +} + +bool LLBadge::Params::equals(const Params& a) const +{ + bool comp = true; + + // skip owner in comparison on purpose + + comp &= (image() == a.image()); + comp &= (image_color() == a.image_color()); + comp &= (label() == a.label()); + comp &= (label_color() == a.label_color()); + comp &= (location() == a.location()); + comp &= (location_percent_hcenter() == a.location_percent_hcenter()); + comp &= (location_percent_vcenter() == a.location_percent_vcenter()); + comp &= (padding_horiz() == a.padding_horiz()); + comp &= (padding_vert() == a.padding_vert()); + + return comp; +} + +LLBadge::LLBadge(const LLBadge::Params& p) + : LLUICtrl(p) + , mOwner(p.owner) + , mGLFont(p.font) + , mImage(p.image) + , mImageColor(p.image_color) + , mLabel(p.label) + , mLabelColor(p.label_color) + , mLocation(p.location) + , mLocationPercentHCenter(0.5f) + , mLocationPercentVCenter(0.5f) + , mPaddingHoriz(p.padding_horiz) + , mPaddingVert(p.padding_vert) +{ + if (mImage.isNull()) + { + llwarns << "Badge: " << getName() << " with no image!" << llendl; + } + + // + // The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter + // based on the Location enum and our horizontal and vertical location percentages. The + // draw code then uses this on the owner rectangle to compute the screen location for + // the badge. + // + + if (!LLRelPos::IsCenter(mLocation)) + { + F32 h_center = p.location_percent_hcenter * 0.01f; + F32 v_center = p.location_percent_vcenter * 0.01f; + + if (LLRelPos::IsRight(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f + h_center); + } + else if (LLRelPos::IsLeft(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f - h_center); + } + + if (LLRelPos::IsTop(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f + v_center); + } + else if (LLRelPos::IsBottom(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f - v_center); + } + } +} + +LLBadge::~LLBadge() +{ +} + +void LLBadge::setLabel(const LLStringExplicit& label) +{ + mLabel = label; +} + +// +// This is a fallback function to render a rectangle for badges without a valid image +// +void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color) +{ + gGL.pushUIMatrix(); + gGL.loadUIIdentity(); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.color4ubv(color.mV); + gGL.texCoord2i(0, 0); + + F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f; + F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f; + + LLRectf screen_rect(llround(x), + llround(y), + llround(x) + width, + llround(y) + height); + + LLVector3 vertices[4]; + vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f); + vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f); + vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f); + vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f); + + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, 4); + } + gGL.end(); + + gGL.popUIMatrix(); +} + + +// virtual +void LLBadge::draw() +{ + if (!mLabel.empty()) + { + LLUICtrl* owner_ctrl = mOwner.get(); + + if (owner_ctrl) + { + // + // Calculate badge position based on owner + // + + LLRect owner_rect; + owner_ctrl->localRectToOtherView(owner_ctrl->getLocalRect(), & owner_rect, this); + + F32 badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter; + F32 badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter; + + // + // Calculate badge size based on label text + // + + LLWString badge_label_wstring = mLabel; + + S32 badge_label_begin_offset = 0; + S32 badge_char_length = S32_MAX; + S32 badge_pixel_length = S32_MAX; + F32 *right_position_out = NULL; + BOOL do_not_use_ellipses = false; + + F32 badge_width = (2.0f * mPaddingHoriz) + + mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length); + + F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight(); + + // + // Draw button image, if available. + // Otherwise draw basic rectangular button. + // + + F32 alpha = getDrawContext().mAlpha; + + if (!mImage.isNull()) + { + F32 badge_x = badge_center_x - badge_width * 0.5f; + F32 badge_y = badge_center_y - badge_height * 0.5f; + + mImage->drawSolid(badge_x, badge_y, badge_width, badge_height, mImageColor % alpha); + } + else + { + lldebugs << "No image for badge " << getName() << " on owner " << owner_ctrl->getName() << llendl; + + renderBadgeBackground(badge_center_x, badge_center_y, + badge_width, badge_height, + mImageColor % alpha); + } + + // + // Draw the label + // + + mGLFont->render(badge_label_wstring, badge_label_begin_offset, + badge_center_x, badge_center_y, + mLabelColor % alpha, + LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position + LLFontGL::NORMAL, // normal text (not bold, italics, etc.) + LLFontGL::DROP_SHADOW_SOFT, + badge_char_length, badge_pixel_length, + right_position_out, do_not_use_ellipses); + } + } +} + + +namespace LLInitParam +{ + void TypeValues::declareValues() + { + declare("bottom", LLRelPos::BOTTOM); + declare("bottom_left", LLRelPos::BOTTOM_LEFT); + declare("bottom_right", LLRelPos::BOTTOM_RIGHT); + declare("center", LLRelPos::CENTER); + declare("left", LLRelPos::LEFT); + declare("right", LLRelPos::RIGHT); + declare("top", LLRelPos::TOP); + declare("top_left", LLRelPos::TOP_LEFT); + declare("top_right", LLRelPos::TOP_RIGHT); + } +} + + +// eof diff --git a/indra/llui/llbadge.h b/indra/llui/llbadge.h new file mode 100644 index 0000000000..c2e0a763b2 --- /dev/null +++ b/indra/llui/llbadge.h @@ -0,0 +1,152 @@ +/** + * @file llbadge.h + * @brief Header for badges + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLBADGE_H +#define LL_LLBADGE_H + +#include + +#include "lluicolor.h" +#include "lluictrl.h" +#include "llstring.h" +#include "lluiimage.h" + +// +// Declarations +// + +class LLUICtrlFactory; +class LLFontGL; + +// +// Relative Position Alignment +// + +namespace LLRelPos +{ + enum Location + { + CENTER = 0, + + LEFT = (1 << 0), + RIGHT = (1 << 1), + + TOP = (1 << 2), + BOTTOM = (1 << 3), + + BOTTOM_LEFT = (BOTTOM | LEFT), + BOTTOM_RIGHT = (BOTTOM | RIGHT), + + TOP_LEFT = (TOP | LEFT), + TOP_RIGHT = (TOP | RIGHT), + }; + + inline bool IsBottom(Location relPos) { return (relPos & BOTTOM) == BOTTOM; } + inline bool IsCenter(Location relPos) { return (relPos == CENTER); } + inline bool IsLeft(Location relPos) { return (relPos & LEFT) == LEFT; } + inline bool IsRight(Location relPos) { return (relPos & RIGHT) == RIGHT; } + inline bool IsTop(Location relPos) { return (relPos & TOP) == TOP; } +} + +// NOTE: This needs to occur before Optional declaration for proper compilation. +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; +} + +// +// Classes +// + +class LLBadge +: public LLUICtrl +{ +public: + struct Params + : public LLInitParam::Block + { + Optional< LLHandle > owner; // Mandatory in code but not in xml + + Optional< LLUIImage* > image; + Optional< LLUIColor > image_color; + + Optional< std::string > label; + Optional< LLUIColor > label_color; + + Optional< LLRelPos::Location > location; + Optional< U32 > location_percent_hcenter; + Optional< U32 > location_percent_vcenter; + + Optional< F32 > padding_horiz; + Optional< F32 > padding_vert; + + Params(); + + bool equals(const Params&) const; + }; + +protected: + friend class LLUICtrlFactory; + LLBadge(const Params& p); + +public: + + ~LLBadge(); + + virtual void draw(); + + const std::string getLabel() const { return wstring_to_utf8str(mLabel); } + void setLabel( const LLStringExplicit& label); + +private: + const LLFontGL* mGLFont; + + LLPointer< LLUIImage > mImage; + LLUIColor mImageColor; + + LLUIString mLabel; + LLUIColor mLabelColor; + + LLRelPos::Location mLocation; + F32 mLocationPercentHCenter; + F32 mLocationPercentVCenter; + + LLHandle< LLUICtrl > mOwner; + + F32 mPaddingHoriz; + F32 mPaddingVert; +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLBADGE_CPP +extern template class LLBadge* LLView::getChild(const std::string& name, BOOL recurse) const; +#endif + +#endif // LL_LLBADGE_H diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 45ceaff696..a63281b3c1 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -56,6 +56,9 @@ static LLDefaultChildRegistry::Register r("button"); template class LLButton* LLView::getChild( const std::string& name, BOOL recurse) const; + + + // globals loaded from settings.xml S32 LLBUTTON_H_PAD = 0; S32 BTN_HEIGHT_SMALL= 0; @@ -99,7 +102,8 @@ LLButton::Params::Params() scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), commit_on_return("commit_on_return", true), - use_draw_context_alpha("use_draw_context_alpha", true) + use_draw_context_alpha("use_draw_context_alpha", true), + badge("badge") { addSynonym(is_toggle, "toggle"); held_down_delay.seconds = 0.5f; @@ -160,8 +164,8 @@ LLButton::LLButton(const LLButton::Params& p) mMouseDownSignal(NULL), mMouseUpSignal(NULL), mHeldDownSignal(NULL), - mUseDrawContextAlpha(p.use_draw_context_alpha) - + mUseDrawContextAlpha(p.use_draw_context_alpha), + mBadge(NULL) { static LLUICachedControl llbutton_orig_h_pad ("UIButtonOrigHPad", 0); static Params default_params(LLUICtrlFactory::getDefaultParams()); @@ -244,6 +248,17 @@ LLButton::LLButton(const LLButton::Params& p) { setHeldDownCallback(initCommitCallback(p.mouse_held_callback)); } + + // Only create a badge here if a non-default one was provided. + if (p.badge.isProvided()) + { + if (!p.badge().equals(LLUICtrlFactory::getDefaultParams())) + { + LLBadge::Params badge_params(p.badge()); + badge_params.owner = getUICtrlHandle(); + mBadge = LLUICtrlFactory::create(badge_params); + } + } } LLButton::~LLButton() @@ -327,6 +342,13 @@ boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, BOOL LLButton::postBuild() { autoResize(); + + // Attach the badge to the appropriate parent panel + if (mBadge) + { + addBadgeToParentPanel(); + } + return TRUE; } BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) @@ -867,7 +889,7 @@ void LLButton::draw() S32_MAX, text_width, NULL, mUseEllipses); } - + LLUICtrl::draw(); } @@ -1063,6 +1085,70 @@ void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignmen } } +void LLButton::addBadgeToParentPanel() +{ + if (mBadge) + { + // Find the appropriate parent panel for the badge + + LLPanel * parentPanel = NULL; + LLUICtrl * parent = getParentUICtrl(); + + while (parent) + { + parentPanel = dynamic_cast(parent); + + if (parentPanel != NULL) + { + break; + } + + parent = parent->getParentUICtrl(); + } + + if (parentPanel) + { + parentPanel->addChild(mBadge); + } + else + { + llwarns << "Unable to find parent panel for badge " << mBadge->getName() << " on button " << getName() << llendl; + } + } + else + { + llwarns << "Unable to add NULL badge to button " << getName() << llendl; + } +} + +void LLButton::setBadgeLabel(const LLStringExplicit& label) +{ + if (mBadge == NULL) + { + LLBadge::Params badge_params(LLUICtrlFactory::getDefaultParams()); + badge_params.owner = getUICtrlHandle(); + mBadge = LLUICtrlFactory::create(badge_params); + + addBadgeToParentPanel(); + } + + if (mBadge) + { + mBadge->setLabel(label); + + // + // Push the badge to the front so it renders last + // + + LLUICtrl * parent = mBadge->getParentUICtrl(); + + if (parent) + { + parent->sendChildToFront(mBadge); + } + } +} + void LLButton::onMouseCaptureLost() { resetMouseDownTimer(); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 0cfc393e05..232ab81e0d 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -27,6 +27,8 @@ #ifndef LL_LLBUTTON_H #define LL_LLBUTTON_H +#include "lluuid.h" +#include "llbadge.h" #include "llcontrol.h" #include "lluictrl.h" #include "v4color.h" @@ -52,8 +54,6 @@ S32 round_up(S32 grid, S32 value); class LLUICtrlFactory; -class LLUIImage; -class LLUUID; // // Classes @@ -126,6 +126,8 @@ public: Optional held_down_delay; Optional use_draw_context_alpha; + + Optional badge; Params(); }; @@ -249,6 +251,8 @@ public: void setImageDisabledSelected(LLPointer image); void setImageFlash(LLPointer image); void setImagePressed(LLPointer image); + + void setBadgeLabel(const LLStringExplicit& label); void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } BOOL getCommitOnReturn() const { return mCommitOnReturn; } @@ -267,6 +271,8 @@ protected: LLPointer getImageUnselected() const { return mImageUnselected; } LLPointer getImageSelected() const { return mImageSelected; } + void addBadgeToParentPanel(); + LLFrameTimer mMouseDownTimer; private: @@ -357,6 +363,8 @@ private: bool mForcePressedState; LLFrameTimer mFlashingTimer; + + LLBadge* mBadge; }; // Build time optimization, generate once in .cpp file diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h index a853999e94..ce7cb5b3e0 100644 --- a/indra/llxuixml/llinitparam.h +++ b/indra/llxuixml/llinitparam.h @@ -34,6 +34,8 @@ #include #include +#include "llerror.h" + namespace LLInitParam { template const T& defaultValue() { static T value; return value; } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index a19eccf748..0795e38366 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -132,6 +132,12 @@ + + diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 4f19dd9a6d..2a78337f65 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -72,6 +72,8 @@ with the same filename but different name + + diff --git a/indra/newview/skins/default/textures/widgets/Badge.png b/indra/newview/skins/default/textures/widgets/Badge.png new file mode 100644 index 0000000000..afda6b66b9 Binary files /dev/null and b/indra/newview/skins/default/textures/widgets/Badge.png differ diff --git a/indra/newview/skins/default/xui/en/widgets/badge.xml b/indra/newview/skins/default/xui/en/widgets/badge.xml new file mode 100644 index 0000000000..4eb633ac0c --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/badge.xml @@ -0,0 +1,14 @@ + + + + -- cgit v1.3 From 33e4a2c7b1ab3a06f1adb5edd9850523c897ea77 Mon Sep 17 00:00:00 2001 From: Leslie Linden Date: Wed, 25 May 2011 10:39:34 -0700 Subject: Updated Marketplace Inbox & Outbox panel to maintain size when one is expanded vs the other --- indra/llui/lllayoutstack.cpp | 18 +- indra/llui/lllayoutstack.h | 4 + indra/newview/llsidepanelinventory.cpp | 49 +-- .../skins/default/xui/en/sidepanel_inventory.xml | 403 +++++++++++---------- 4 files changed, 248 insertions(+), 226 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 9b6830a816..8a92796942 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -78,20 +78,20 @@ LLLayoutPanel::~LLLayoutPanel() delete mResizeBar; mResizeBar = NULL; } - + F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation) { if (orientation == LLLayoutStack::HORIZONTAL) { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); return mVisibleAmt * collapse_amt; } else { - F32 collapse_amt = + F32 collapse_amt = clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight()))); - return mVisibleAmt * collapse_amt; + return mVisibleAmt * collapse_amt; } } @@ -182,14 +182,14 @@ BOOL LLLayoutStack::postBuild() } bool LLLayoutStack::addChild(LLView* child, S32 tab_group) - { +{ LLLayoutPanel* panelp = dynamic_cast(child); - if (panelp) - { + if (panelp) + { mPanels.push_back(panelp); - } + } return LLView::addChild(child, tab_group); - } +} S32 LLLayoutStack::getDefaultHeight(S32 cur_height) diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 4ac8ef0ee9..2fc2cf3eb4 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -177,6 +177,10 @@ public: ~LLLayoutPanel(); void initFromParams(const Params& p); + + S32 getMinDim() const { return mMinDim; } + S32 getMaxDim() const { return mMaxDim; } + void setMinDim(S32 value) { mMinDim = value; } void setMaxDim(S32 value) { mMaxDim = value; } diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 7f5a2e64bf..9247611257 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -134,38 +134,45 @@ BOOL LLSidepanelInventory::postBuild() } -void LLSidepanelInventory::onToggleInboxBtn() +void manageInboxOutboxPanels(LLLayoutStack * stack, + LLButton * pressedButton, LLLayoutPanel * pressedPanel, + LLButton * otherButton, LLLayoutPanel * otherPanel) { - LLLayoutStack* stack = getChild("inventory_layout_stack"); - bool collapse = !getChild("inbox_btn")->getToggleState(); + bool expand = pressedButton->getToggleState(); + bool otherExpanded = otherButton->getToggleState(); - if (stack) - { - stack->collapsePanel(getChild("inbox_layout_panel"), collapse); - } - if (!collapse) + if (expand && otherExpanded) { - stack->collapsePanel(getChild("outbox_layout_panel"), true); - getChild("outbox_btn")->setToggleState(false); + // Reshape pressedPanel to the otherPanel's height so we preserve the marketplace panel size + pressedPanel->reshape(pressedPanel->getRect().getWidth(), otherPanel->getRect().getHeight()); + + stack->collapsePanel(otherPanel, true); + otherButton->setToggleState(false); } + + stack->collapsePanel(pressedPanel, !expand); } -void LLSidepanelInventory::onToggleOutboxBtn() +void LLSidepanelInventory::onToggleInboxBtn() { LLLayoutStack* stack = getChild("inventory_layout_stack"); - bool collapse = !getChild("outbox_btn")->getToggleState(); + LLButton* pressedButton = getChild("inbox_btn"); + LLLayoutPanel* pressedPanel = getChild("inbox_layout_panel"); + LLButton* otherButton = getChild("outbox_btn"); + LLLayoutPanel* otherPanel = getChild("outbox_layout_panel"); - if (stack) - { - stack->collapsePanel(getChild("outbox_layout_panel"), collapse); - } + manageInboxOutboxPanels(stack, pressedButton, pressedPanel, otherButton, otherPanel); +} - if (!collapse) - { - stack->collapsePanel(getChild("inbox_layout_panel"), true); - getChild("inbox_btn")->setToggleState(false); - } +void LLSidepanelInventory::onToggleOutboxBtn() +{ + LLLayoutStack* stack = getChild("inventory_layout_stack"); + LLButton* pressedButton = getChild("outbox_btn"); + LLLayoutPanel* pressedPanel = getChild("outbox_layout_panel"); + LLButton* otherButton = getChild("inbox_btn"); + LLLayoutPanel* otherPanel = getChild("inbox_layout_panel"); + manageInboxOutboxPanels(stack, pressedButton, pressedPanel, otherButton, otherPanel); } diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index a0caf80256..ce5cf4c622 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -9,170 +9,181 @@ min_width="240" name="objects panel" width="333"> - - - - - - - - - - Just a panel - - - - - - - - + + + + + + + -- cgit v1.3 From 4267014b146798cabe96568b2091c6bb2dd2b294 Mon Sep 17 00:00:00 2001 From: Leslie Linden Date: Wed, 22 Jun 2011 17:35:25 -0400 Subject: EXP-890 FIX -- Resizing inbox or outbox to minimum size while open does not show present items when hte box is next opened EXP-897 FIX -- Black box shown in UI when shrinking Inbox down in size * Layout panel now supports "expanded_min_dim" parameter which allows a different min size to be specified when the layout panel is collapsed vs expanded. * Inbox and Outbox expanded_min_dim attributes are now set to be larger than min dim so they can not be collapsed all the way when they are expanded. --- indra/llui/lllayoutstack.cpp | 97 +++++++++++----------- indra/llui/lllayoutstack.h | 34 ++++++-- .../skins/default/xui/en/sidepanel_inventory.xml | 4 +- 3 files changed, 80 insertions(+), 55 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 8a92796942..d87ee428aa 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -49,6 +49,8 @@ void LLLayoutStack::OrientationNames::declareValues() // LLLayoutPanel::LLLayoutPanel(const Params& p) : LLPanel(p), + mExpandedMinDimSpecified(false), + mExpandedMinDim(p.min_dim), mMinDim(p.min_dim), mMaxDim(p.max_dim), mAutoResize(p.auto_resize), @@ -58,6 +60,13 @@ LLLayoutPanel::LLLayoutPanel(const Params& p) mVisibleAmt(1.f), // default to fully visible mResizeBar(NULL) { + // Set the expanded min dim if it is provided, otherwise it gets the p.min_dim value + if (p.expanded_min_dim.isProvided()) + { + mExpandedMinDimSpecified = true; + mExpandedMinDim = p.expanded_min_dim(); + } + // panels initialized as hidden should not start out partially visible if (!getVisible()) { @@ -84,13 +93,13 @@ F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientati if (orientation == LLLayoutStack::HORIZONTAL) { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, getRelevantMinDim() / (F32)llmax(1, getRect().getWidth())); return mVisibleAmt * collapse_amt; } else { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight()))); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, getRelevantMinDim() / (F32)llmax(1, getRect().getHeight()))); return mVisibleAmt * collapse_amt; } } @@ -281,9 +290,9 @@ bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_dimp { LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name); - if (panel) + if (panel && min_dimp) { - if (min_dimp) *min_dimp = panel->mMinDim; + *min_dimp = panel->getRelevantMinDim(); } return NULL != panel; @@ -316,7 +325,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); if (panelp->getVisible()) { if (mAnimate) @@ -366,9 +375,9 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if (mOrientation == HORIZONTAL) { // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->mMinDim) + if (panelp->getRect().getWidth() < (*panel_it)->getRelevantMinDim()) { - panelp->reshape((*panel_it)->mMinDim, panelp->getRect().getHeight()); + panelp->reshape((*panel_it)->getRelevantMinDim(), panelp->getRect().getHeight()); } total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor(mOrientation)); // want n-1 panel gaps for n panels @@ -380,9 +389,9 @@ void LLLayoutStack::updateLayout(BOOL force_resize) else //VERTICAL { // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->mMinDim) + if (panelp->getRect().getHeight() < (*panel_it)->getRelevantMinDim()) { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinDim); + panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->getRelevantMinDim()); } total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor(mOrientation)); if (panel_it != mPanels.begin()) @@ -409,28 +418,20 @@ void LLLayoutStack::updateLayout(BOOL force_resize) || (!(*panel_it)->mAutoResize && !force_resize)) { - if (mOrientation == HORIZONTAL) - { - shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - } - else //VERTICAL - { - shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - } + S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); + F32 relevant_min = ((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); + + shrink_headroom_total += relevant_dimension - relevant_min; } else { num_resizable_panels++; - if (mOrientation == HORIZONTAL) - { - shrink_headroom_available += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - } - else //VERTICAL - { - shrink_headroom_available += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - } + + S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); + F32 relevant_min = ((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); + + shrink_headroom_available += relevant_dimension - relevant_min; + shrink_headroom_total += relevant_dimension - relevant_min; } } @@ -452,27 +453,28 @@ void LLLayoutStack::updateLayout(BOOL force_resize) for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); S32 cur_width = panelp->getRect().getWidth(); S32 cur_height = panelp->getRect().getHeight(); S32 new_width = cur_width; - S32 new_height = cur_height; + S32 new_height = cur_height; + S32 relevant_min = (S32) panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { - new_width = llmax((*panel_it)->mMinDim, new_width); + new_width = llmax(relevant_min, new_width); } else { - new_height = llmax((*panel_it)->mMinDim, new_height); + new_height = llmax(relevant_min, new_height); } S32 delta_size = 0; // if panel can automatically resize (not animating, and resize flag set)... - if ((*panel_it)->getCollapseFactor(mOrientation) == 1.f - && (force_resize || (*panel_it)->mAutoResize) - && !(*panel_it)->mResizeBar->hasMouseCapture()) + if (panelp->getCollapseFactor(mOrientation) == 1.f + && (force_resize || panelp->mAutoResize) + && !panelp->mResizeBar->hasMouseCapture()) { if (mOrientation == HORIZONTAL) { @@ -481,8 +483,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { // shrink proportionally to amount over minimum // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_width - (*panel_it)->mMinDim); + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - panelp->getRelevantMinDim()) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_width - relevant_min); } else { @@ -491,7 +493,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) num_resizable_panels--; } pixels_to_distribute -= delta_size; - new_width = llmax((*panel_it)->mMinDim, cur_width + delta_size); + new_width = llmax(relevant_min, cur_width + delta_size); } else { @@ -504,8 +506,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { // shrink proportionally to amount over minimum // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_height - (*panel_it)->mMinDim); + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - relevant_min) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_height - relevant_min); } else { @@ -513,7 +515,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) num_resizable_panels--; } pixels_to_distribute -= delta_size; - new_height = llmax((*panel_it)->mMinDim, cur_height + delta_size); + new_height = llmax(relevant_min, cur_height + delta_size); } else { @@ -566,19 +568,20 @@ void LLLayoutStack::updateLayout(BOOL force_resize) LLLayoutPanel* last_resizeable_panel = NULL; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); + F32 relevant_min = panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinDim, - (*panel_it)->mMinDim + shrink_headroom_total); + relevant_min, + relevant_min + shrink_headroom_total); } else //VERTICAL { (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinDim, - (*panel_it)->mMinDim + shrink_headroom_total); + relevant_min, + relevant_min + shrink_headroom_total); } // toggle resize bars based on panel visibility, resizability, etc @@ -658,7 +661,7 @@ void LLLayoutStack::calcMinExtents() { if (mOrientation == HORIZONTAL) { - mMinWidth += (*panel_it)->mMinDim; + mMinWidth += (*panel_it)->getRelevantMinDim(); if (panel_it != mPanels.begin()) { mMinWidth += mPanelSpacing; @@ -666,7 +669,7 @@ void LLLayoutStack::calcMinExtents() } else //VERTICAL { - mMinHeight += (*panel_it)->mMinDim; + mMinHeight += (*panel_it)->getRelevantMinDim(); if (panel_it != mPanels.begin()) { mMinHeight += mPanelSpacing; diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 2fc2cf3eb4..d8ef0aeaca 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -30,10 +30,10 @@ #include "llpanel.h" -class LLPanel; class LLLayoutPanel; + class LLLayoutStack : public LLView, public LLInstanceTracker { public: @@ -149,6 +149,7 @@ private: F32 mCloseTimeConstant; }; // end class LLLayoutStack + class LLLayoutPanel : public LLPanel { friend class LLLayoutStack; @@ -156,13 +157,15 @@ friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block { - Optional min_dim, + Optional expanded_min_dim, + min_dim, max_dim; Optional user_resize, auto_resize; Params() - : min_dim("min_dim", 0), + : expanded_min_dim("expanded_min_dim", 0), + min_dim("min_dim", 0), max_dim("max_dim", 0), user_resize("user_resize", true), auto_resize("auto_resize", true) @@ -179,17 +182,34 @@ public: void initFromParams(const Params& p); S32 getMinDim() const { return mMinDim; } - S32 getMaxDim() const { return mMaxDim; } + void setMinDim(S32 value) { mMinDim = value; if (!mExpandedMinDimSpecified) mExpandedMinDim = value; } - void setMinDim(S32 value) { mMinDim = value; } + S32 getMaxDim() const { return mMaxDim; } void setMaxDim(S32 value) { mMaxDim = value; } -protected: - LLLayoutPanel(const Params& p) ; + S32 getExpandedMinDim() const { return mExpandedMinDim; } + void setExpandedMinDim(S32 value) { mExpandedMinDim = value; mExpandedMinDimSpecified = true; } + + S32 getRelevantMinDim() const + { + S32 min_dim = mMinDim; + + if (!mCollapsed) + { + min_dim = mExpandedMinDim; + } + + return min_dim; + } +protected: + LLLayoutPanel(const Params& p); F32 getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation); + bool mExpandedMinDimSpecified; + S32 mExpandedMinDim; + S32 mMinDim; S32 mMaxDim; BOOL mAutoResize; diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index 1fedd6aab8..00f3135035 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -52,9 +52,10 @@ auto_resize="true" user_resize="false" follows="bottom|left|right" - min_dim="35" name="inbox_layout_panel" + min_dim="35" max_dim="200" + expanded_min_dim="90" height="200"> Date: Thu, 23 Jun 2011 12:02:59 -0700 Subject: fix for gcc build --- indra/llui/lllayoutstack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index d87ee428aa..c34acd3db6 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -419,7 +419,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) && !force_resize)) { S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); - F32 relevant_min = ((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); + S32 relevant_min = llround((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); shrink_headroom_total += relevant_dimension - relevant_min; } @@ -428,7 +428,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) num_resizable_panels++; S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); - F32 relevant_min = ((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); + S32 relevant_min = llround((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); shrink_headroom_available += relevant_dimension - relevant_min; shrink_headroom_total += relevant_dimension - relevant_min; -- cgit v1.3 From 842b6d2d142abcaa11a825a96537b0ceaef33e24 Mon Sep 17 00:00:00 2001 From: Leslie Linden Date: Thu, 23 Jun 2011 16:34:54 -0400 Subject: Build fix after merge. --- indra/llui/lllayoutstack.cpp | 47 +++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index c34acd3db6..7e0c5542ea 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -332,16 +332,16 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); - if ((*panel_it)->mVisibleAmt > 0.99f) + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); + if (panelp->mVisibleAmt > 0.99f) { - (*panel_it)->mVisibleAmt = 1.f; + panelp->mVisibleAmt = 1.f; } } } else { - (*panel_it)->mVisibleAmt = 1.f; + panelp->mVisibleAmt = 1.f; } } else // not visible @@ -350,36 +350,36 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); - if ((*panel_it)->mVisibleAmt < 0.001f) + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + if (panelp->mVisibleAmt < 0.001f) { - (*panel_it)->mVisibleAmt = 0.f; + panelp->mVisibleAmt = 0.f; } } } else { - (*panel_it)->mVisibleAmt = 0.f; + panelp->mVisibleAmt = 0.f; } } - if ((*panel_it)->mCollapsed) + if (panelp->mCollapsed) { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } else { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } if (mOrientation == HORIZONTAL) { // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->getRelevantMinDim()) + if (panelp->getRect().getWidth() < panelp->getRelevantMinDim()) { - panelp->reshape((*panel_it)->getRelevantMinDim(), panelp->getRect().getHeight()); + panelp->reshape(panelp->getRelevantMinDim(), panelp->getRect().getHeight()); } - total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor(mOrientation)); + total_width += llround(panelp->getRect().getWidth() * panelp->getCollapseFactor(mOrientation)); // want n-1 panel gaps for n panels if (panel_it != mPanels.begin()) { @@ -389,11 +389,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) else //VERTICAL { // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->getRelevantMinDim()) + if (panelp->getRect().getHeight() < panelp->getRelevantMinDim()) { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->getRelevantMinDim()); + panelp->reshape(panelp->getRect().getWidth(), panelp->getRelevantMinDim()); } - total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor(mOrientation)); + total_height += llround(panelp->getRect().getHeight() * panelp->getCollapseFactor(mOrientation)); if (panel_it != mPanels.begin()) { total_height += mPanelSpacing; @@ -412,23 +412,20 @@ void LLLayoutStack::updateLayout(BOOL force_resize) continue; } + S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); + S32 relevant_min = (*panel_it)->getRelevantMinDim(); + // if currently resizing a panel or the panel is flagged as not automatically resizing // only track total available headroom, but don't use it for automatic resize logic if ((*panel_it)->mResizeBar->hasMouseCapture() || (!(*panel_it)->mAutoResize && !force_resize)) { - S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); - S32 relevant_min = llround((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); - shrink_headroom_total += relevant_dimension - relevant_min; } else { num_resizable_panels++; - - S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); - S32 relevant_min = llround((*panel_it)->mCollapsed ? (*panel_it)->getRelevantMinDim() : (*panel_it)->mExpandedMinDim); shrink_headroom_available += relevant_dimension - relevant_min; shrink_headroom_total += relevant_dimension - relevant_min; @@ -483,7 +480,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { // shrink proportionally to amount over minimum // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - panelp->getRelevantMinDim()) / (F32)shrink_headroom_available)) : 0; + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - relevant_min) / (F32)shrink_headroom_available)) : 0; shrink_headroom_available -= (cur_width - relevant_min); } else @@ -691,7 +688,7 @@ void LLLayoutStack::createResizeBars() LLResizeBar::Params resize_params; resize_params.name("resize"); resize_params.resizing_view(lp); - resize_params.min_size(lp->mMinDim); + resize_params.min_size(lp->getRelevantMinDim()); resize_params.side(side); resize_params.snapping_enabled(false); LLResizeBar* resize_bar = LLUICtrlFactory::create(resize_params); -- cgit v1.3 From c6d9a6b8321807523926fe7055b0b80572d29f44 Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Thu, 23 Jun 2011 15:35:23 -0700 Subject: another gcc fix --- indra/llui/lllayoutstack.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 7e0c5542ea..6a91ec56e4 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -93,13 +93,13 @@ F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientati if (orientation == LLLayoutStack::HORIZONTAL) { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, getRelevantMinDim() / (F32)llmax(1, getRect().getWidth())); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)getRelevantMinDim() / (F32)llmax(1, getRect().getWidth())); return mVisibleAmt * collapse_amt; } else { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, getRelevantMinDim() / (F32)llmax(1, getRect().getHeight()))); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)getRelevantMinDim() / (F32)llmax(1, getRect().getHeight()))); return mVisibleAmt * collapse_amt; } } @@ -456,7 +456,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 cur_height = panelp->getRect().getHeight(); S32 new_width = cur_width; S32 new_height = cur_height; - S32 relevant_min = (S32) panelp->getRelevantMinDim(); + S32 relevant_min = panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { @@ -566,7 +566,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { LLLayoutPanel* panelp = (*panel_it); - F32 relevant_min = panelp->getRelevantMinDim(); + S32 relevant_min = panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { -- cgit v1.3 From 7717b6f647feb250c0b94d038f72a640a7888915 Mon Sep 17 00:00:00 2001 From: Logan Dethrow Date: Tue, 28 Jun 2011 19:54:53 -0400 Subject: STORM-1112 More cleanup of SOCKS 5 proxy code. Renamed llsocks5.cpp to llproxy.cpp. --- indra/llmessage/CMakeLists.txt | 4 +- indra/llmessage/llcurl.cpp | 38 +-- indra/llmessage/lliosocket.h | 2 +- indra/llmessage/llpacketring.cpp | 40 ++- indra/llmessage/llproxy.cpp | 294 +++++++++++++++++++++ indra/llmessage/llproxy.h | 244 +++++++++++++++++ indra/llmessage/llsocks5.cpp | 285 -------------------- indra/llmessage/llsocks5.h | 241 ----------------- indra/llmessage/net.cpp | 2 +- indra/llui/llfunctorregistry.h | 4 +- indra/newview/llfloaterpreference.cpp | 47 +++- indra/newview/llloginhandler.cpp | 6 +- indra/newview/llpanellogin.h | 3 - indra/newview/llsecapi.h | 4 +- indra/newview/llstartup.cpp | 80 +++--- indra/newview/llstartup.h | 2 +- indra/newview/llxmlrpctransaction.cpp | 14 +- .../default/xui/en/floater_preferences_proxy.xml | 181 +++++++------ .../newview/skins/default/xui/en/notifications.xml | 21 +- .../skins/default/xui/en/panel_cof_wearables.xml | 2 +- .../default/xui/en/panel_preferences_privacy.xml | 2 +- .../default/xui/en/panel_preferences_setup.xml | 2 +- 22 files changed, 773 insertions(+), 745 deletions(-) create mode 100644 indra/llmessage/llproxy.cpp create mode 100644 indra/llmessage/llproxy.h delete mode 100644 indra/llmessage/llsocks5.cpp delete mode 100644 indra/llmessage/llsocks5.h (limited to 'indra/llui') diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 4b679ef6a5..0f40a670fa 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -65,6 +65,7 @@ set(llmessage_SOURCE_FILES llpacketbuffer.cpp llpacketring.cpp llpartdata.cpp + llproxy.cpp llpumpio.cpp llregionpresenceverifier.cpp llsdappservices.cpp @@ -76,7 +77,6 @@ set(llmessage_SOURCE_FILES llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp - llsocks5.cpp llstoredmessage.cpp lltemplatemessagebuilder.cpp lltemplatemessagedispatcher.cpp @@ -162,6 +162,7 @@ set(llmessage_HEADER_FILES llpacketring.h llpartdata.h llpumpio.h + llproxy.h llqueryflags.h llregionflags.h llregionhandle.h @@ -175,7 +176,6 @@ set(llmessage_HEADER_FILES llsdrpcserver.h llservice.h llservicebuilder.h - llsocks5.h llstoredmessage.h lltaskname.h llteleportflags.h diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 32dd438e68..0b368196d2 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -46,12 +46,12 @@ #endif #include "llbufferstream.h" -#include "llstl.h" #include "llsdserialize.h" +#include "llproxy.h" +#include "llstl.h" #include "llthread.h" #include "lltimer.h" -#include "llsocks5.h" ////////////////////////////////////////////////////////////////////////////// /* @@ -357,27 +357,6 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); - - //Set the CURL options for either Socks or HTTP proxy - if (LLSocks::getInstance()->isHTTPProxyEnabled()) - { - std::string address = LLSocks::getInstance()->getHTTPProxy().getIPString(); - U16 port = LLSocks::getInstance()->getHTTPProxy().getPort(); - curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXY,address.c_str()); - curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYPORT,port); - if (LLSocks::getInstance()->getHTTPProxyType() == LLPROXY_SOCKS) - { - curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - if(LLSocks::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) - { - curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYUSERPWD,LLSocks::getInstance()->getProxyUserPwd().c_str()); - } - } - else - { - curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } - } ++gCurlEasyCount; return easy; @@ -557,18 +536,19 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // usefull for debugging setopt(CURLOPT_NOSIGNAL, 1); - if (LLSocks::getInstance()->isHTTPProxyEnabled()) + // Set the CURL options for either Socks or HTTP proxy + if (LLProxy::getInstance()->isHTTPProxyEnabled()) { - std::string address = LLSocks::getInstance()->getHTTPProxy().getIPString(); - U16 port = LLSocks::getInstance()->getHTTPProxy().getPort(); + std::string address = LLProxy::getInstance()->getHTTPProxy().getIPString(); + U16 port = LLProxy::getInstance()->getHTTPProxy().getPort(); setoptString(CURLOPT_PROXY, address.c_str()); setopt(CURLOPT_PROXYPORT, port); - if (LLSocks::getInstance()->getHTTPProxyType() == LLPROXY_SOCKS) + if (LLProxy::getInstance()->getHTTPProxyType() == LLPROXY_SOCKS) { setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - if(LLSocks::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) + if(LLProxy::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) { - setoptString(CURLOPT_PROXYUSERPWD,LLSocks::getInstance()->getProxyUserPwd()); + setoptString(CURLOPT_PROXYUSERPWD, LLProxy::getInstance()->getProxyUserPwdCURL()); } } else diff --git a/indra/llmessage/lliosocket.h b/indra/llmessage/lliosocket.h index 6806e5084a..ec60470459 100644 --- a/indra/llmessage/lliosocket.h +++ b/indra/llmessage/lliosocket.h @@ -159,7 +159,7 @@ protected: public: /** - * @brief Do not call this directly. + * @brief Do not call this directly. Use LLSocket::ptr_t.reset() instead. */ ~LLSocket(); diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp index 62aaca0672..91ab1df149 100644 --- a/indra/llmessage/llpacketring.cpp +++ b/indra/llmessage/llpacketring.cpp @@ -37,19 +37,13 @@ // linden library includes #include "llerror.h" -#include "message.h" -#include "llsocks5.h" #include "lltimer.h" -#include "timing.h" +#include "llproxy.h" #include "llrand.h" +#include "message.h" +#include "timing.h" #include "u64.h" - - - - - - /////////////////////////////////////////////////////////// LLPacketRing::LLPacketRing () : mUseInThrottle(FALSE), @@ -231,28 +225,28 @@ S32 LLPacketRing::receivePacket (S32 socket, char *datap) else { // no delay, pull straight from net - if (LLSocks::isEnabled()) + if (LLProxy::isEnabled()) { - U8 buffer[MAX_BUFFER_SIZE]; - packet_size = receive_packet(socket, (char*)buffer); + U8 buffer[NET_BUFFER_SIZE]; + packet_size = receive_packet(socket, reinterpret_cast(buffer)); if (packet_size > 10) { - memcpy(datap,buffer+10,packet_size-10); + // *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6) + memcpy(datap, buffer + 10, packet_size - 10); + proxywrap_t * header = reinterpret_cast(buffer); + mLastSender.setAddress(header->addr); + mLastSender.setPort(ntohs(header->port)); } else { - packet_size=0; + packet_size = 0; } - - proxywrap_t * header = (proxywrap_t *)buffer; - mLastSender.setAddress(header->addr); - mLastSender.setPort(ntohs(header->port)); } else { - packet_size = receive_packet(socket, datap); - mLastSender = ::get_sender(); + packet_size = receive_packet(socket, datap); + mLastSender = ::get_sender(); } mLastReceivingIF = ::get_receiving_interface(); @@ -352,7 +346,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL BOOL LLPacketRing::doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host) { - if (!LLSocks::isEnabled()) + if (!LLProxy::isEnabled()) { return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort()); } @@ -364,7 +358,7 @@ BOOL LLPacketRing::doSendPacket(int h_socket, const char * send_buffer, S32 buf_ socks_header->atype = ADDRESS_IPV4; socks_header->frag = 0; - memcpy(mProxyWrappedSendBuffer+10, send_buffer, buf_size); + memcpy(mProxyWrappedSendBuffer + 10, send_buffer, buf_size); - return send_packet(h_socket,(const char*) mProxyWrappedSendBuffer, buf_size+10, LLSocks::getInstance()->getUDPProxy().getAddress(), LLSocks::getInstance()->getUDPProxy().getPort()); + return send_packet(h_socket,(const char*) mProxyWrappedSendBuffer, buf_size + 10, LLProxy::getInstance()->getUDPProxy().getAddress(), LLProxy::getInstance()->getUDPProxy().getPort()); } diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp new file mode 100644 index 0000000000..6bc9e8b62b --- /dev/null +++ b/indra/llmessage/llproxy.cpp @@ -0,0 +1,294 @@ +/** + * @file llsocks5.cpp + * @brief SOCKS 5 implementation + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llproxy.h" + +#include + +#include "llapr.h" +#include "llhost.h" +#include "message.h" +#include "net.h" + +// Static class variable instances + +// We want this to be static to avoid excessive indirection on every +// incoming packet just to do a simple bool test. The getter for this +// member is also static +bool LLProxy::sUDPProxyEnabled = false; +bool LLProxy::sHTTPProxyEnabled = false; + +// Some helpful TCP functions +static LLSocket::ptr_t tcp_open_channel(apr_pool_t* pool, LLHost host); // Open a TCP channel to a given host +static void tcp_close_channel(LLSocket::ptr_t handle); // Close an open TCP channel +static int tcp_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen); // Do a TCP data handshake + + +LLProxy::LLProxy(): + mProxyType(LLPROXY_SOCKS), + mUDPProxy(), + mTCPProxy(), + mHTTPProxy(), + mAuthMethodSelected(METHOD_NOAUTH), + mSocksUsername(), + mSocksPassword(), + mPool(gAPRPoolp) +{ +} + +LLProxy::~LLProxy() +{ + tcp_close_channel(mProxyControlChannel); + sUDPProxyEnabled = false; + sHTTPProxyEnabled = false; +} + +// Perform a SOCKS 5 authentication and UDP association to the proxy +// specified by proxy, and associate UDP port message_port +int LLProxy::proxyHandshake(LLHost proxy, U32 message_port) +{ + int result; + + /* SOCKS 5 Auth request */ + socks_auth_request_t socks_auth_request; + socks_auth_response_t socks_auth_response; + + socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5 + socks_auth_request.num_methods = 1; // Sending 1 method. + socks_auth_request.methods = mAuthMethodSelected; // Send only the selected method. + + result = tcp_handshake(mProxyControlChannel, (char*)&socks_auth_request, sizeof(socks_auth_request), (char*)&socks_auth_response, sizeof(socks_auth_response)); + if (result != 0) + { + llwarns << "SOCKS authentication request failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (socks_auth_response.method == AUTH_NOT_ACCEPTABLE) + { + llwarns << "SOCKS 5 server refused all our authentication methods" << llendl; + stopProxy(); + return SOCKS_NOT_ACCEPTABLE; + } + + // SOCKS 5 USERNAME/PASSWORD authentication + if (socks_auth_response.method == METHOD_PASSWORD) + { + // The server has requested a username/password combination + U32 request_size = mSocksUsername.size() + mSocksPassword.size() + 3; + char * password_auth = new char[request_size]; + password_auth[0] = 0x01; + password_auth[1] = mSocksUsername.size(); + memcpy(&password_auth[2], mSocksUsername.c_str(), mSocksUsername.size()); + password_auth[mSocksUsername.size() + 2] = mSocksPassword.size(); + memcpy(&password_auth[mSocksUsername.size()+3], mSocksPassword.c_str(), mSocksPassword.size()); + + authmethod_password_reply_t password_reply; + + result = tcp_handshake(mProxyControlChannel, password_auth, request_size, (char*)&password_reply, sizeof(authmethod_password_reply_t)); + delete[] password_auth; + + if (result != 0) + { + llwarns << "SOCKS authentication failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (password_reply.status != AUTH_SUCCESS) + { + llwarns << "SOCKS authentication failed" << llendl; + stopProxy(); + return SOCKS_AUTH_FAIL; + } + } + + /* SOCKS5 connect request */ + + socks_command_request_t connect_request; + socks_command_response_t connect_reply; + + connect_request.version = SOCKS_VERSION; // SOCKS V5 + connect_request.command = COMMAND_UDP_ASSOCIATE; // Associate UDP + connect_request.reserved = FIELD_RESERVED; + connect_request.atype = ADDRESS_IPV4; + connect_request.address = htonl(0); // 0.0.0.0 + connect_request.port = htons(0); // 0 + // "If the client is not in possession of the information at the time of the UDP ASSOCIATE, + // the client MUST use a port number and address of all zeros. RFC 1928" + + result = tcp_handshake(mProxyControlChannel, (char*)&connect_request, sizeof(socks_command_request_t), (char*)&connect_reply, sizeof(socks_command_response_t)); + if (result != 0) + { + llwarns << "SOCKS connect request failed, error on TCP control channel : " << result << llendl; + stopProxy(); + return SOCKS_CONNECT_ERROR; + } + + if (connect_reply.reply != REPLY_REQUEST_GRANTED) + { + //Something went wrong + llwarns << "Connection to SOCKS 5 server failed, UDP forward request not granted" << llendl; + stopProxy(); + return SOCKS_UDP_FWD_NOT_GRANTED; + } + + mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order + mUDPProxy.setAddress(proxy.getAddress()); + // All good now we have been given the UDP port to send requests that need forwarding. + llinfos << "SOCKS 5 UDP proxy connected on " << mUDPProxy << llendl; + return SOCKS_OK; +} + +int LLProxy::startProxy(std::string host, U32 port) +{ + mTCPProxy.setHostByName(host); + mTCPProxy.setPort(port); + + int status; + + if (mProxyControlChannel) + { + tcp_close_channel(mProxyControlChannel); + } + + mProxyControlChannel = tcp_open_channel(mPool, mTCPProxy); + if (!mProxyControlChannel) + { + return SOCKS_HOST_CONNECT_FAILED; + } + status = proxyHandshake(mTCPProxy, (U32)gMessageSystem->mPort); + if (status == SOCKS_OK) + { + sUDPProxyEnabled = true; + } + else + { + stopProxy(); + } + return status; + +} + +void LLProxy::stopProxy() +{ + sUDPProxyEnabled = false; + + // If the SOCKS proxy is requested to stop and we are using that for http as well + // then we must shut down any http proxy operations. But it is allowable if web + // proxy is being used to continue proxying http. + + if(LLPROXY_SOCKS == mProxyType) + { + sHTTPProxyEnabled = false; + } + + if (mProxyControlChannel) + { + tcp_close_channel(mProxyControlChannel); + } +} + +void LLProxy::setAuthNone() +{ + mAuthMethodSelected = METHOD_NOAUTH; +} + +void LLProxy::setAuthPassword(const std::string &username, const std::string &password) +{ + mAuthMethodSelected = METHOD_PASSWORD; + mSocksUsername = username; + mSocksPassword = password; +} + +void LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type) +{ + sHTTPProxyEnabled = true; + mHTTPProxy = httpHost; + mProxyType = type; +} + +static int tcp_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen) +{ + apr_socket_t* apr_socket = handle->getSocket(); + apr_status_t rv; + + apr_size_t expected_len = outlen; + + apr_socket_opt_set(apr_socket, APR_SO_NONBLOCK, -5); // Blocking connection, 5 second timeout + apr_socket_timeout_set(apr_socket, (APR_USEC_PER_SEC * 5)); + + rv = apr_socket_send(apr_socket, dataout, &outlen); + if (rv != APR_SUCCESS || expected_len != outlen) + { + llwarns << "Error sending data to proxy control channel" << llendl; + ll_apr_warn_status(rv); + return -1; + } + + expected_len = maxinlen; + do + { + rv = apr_socket_recv(apr_socket, datain, &maxinlen); + llinfos << "Receiving packets." << llendl; + llwarns << "Proxy control channel status: " << rv << llendl; + } while (APR_STATUS_IS_EAGAIN(rv)); + + if (rv != APR_SUCCESS) + { + llwarns << "Error receiving data from proxy control channel, status: " << rv << llendl; + llwarns << "Received " << maxinlen << " bytes." << llendl; + ll_apr_warn_status(rv); + return rv; + } + else if (expected_len != maxinlen) + { + llwarns << "Incorrect data received length in proxy control channel" << llendl; + return -1; + } + + return 0; +} + +static LLSocket::ptr_t tcp_open_channel(apr_pool_t* pool, LLHost host) +{ + LLSocket::ptr_t socket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); + bool connected = socket->blockingConnect(host); + if (!connected) + { + tcp_close_channel(socket); + } + + return socket; +} + +static void tcp_close_channel(LLSocket::ptr_t handle) +{ + handle.reset(); +} diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h new file mode 100644 index 0000000000..979514a7e0 --- /dev/null +++ b/indra/llmessage/llproxy.h @@ -0,0 +1,244 @@ +/** + * @file llsocks5.h + * @brief Socks 5 implementation + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_PROXY_H +#define LL_PROXY_H + +#include "llhost.h" +#include "lliosocket.h" +#include "llmemory.h" +#include "llsingleton.h" +#include + +// Error codes returned from the StartProxy method + +#define SOCKS_OK 0 +#define SOCKS_CONNECT_ERROR (-1) +#define SOCKS_NOT_PERMITTED (-2) +#define SOCKS_NOT_ACCEPTABLE (-3) +#define SOCKS_AUTH_FAIL (-4) +#define SOCKS_UDP_FWD_NOT_GRANTED (-5) +#define SOCKS_HOST_CONNECT_FAILED (-6) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN (255 + 1) /* socks5: 255, +1 for len. */ +#endif + +#define SOCKS_VERSION 0x05 // we are using SOCKS 5 + +// SOCKS 5 address/hostname types +#define ADDRESS_IPV4 0x01 +#define ADDRESS_HOSTNAME 0x03 +#define ADDRESS_IPV6 0x04 + +// Lets just use our own ipv4 struct rather than dragging in system +// specific headers +union ipv4_address_t { + U8 octets[4]; + U32 addr32; +}; + +// SOCKS 5 control channel commands +#define COMMAND_TCP_STREAM 0x01 +#define COMMAND_TCP_BIND 0x02 +#define COMMAND_UDP_ASSOCIATE 0x03 + +// SOCKS 5 command replies +#define REPLY_REQUEST_GRANTED 0x00 +#define REPLY_GENERAL_FAIL 0x01 +#define REPLY_RULESET_FAIL 0x02 +#define REPLY_NETWORK_UNREACHABLE 0x03 +#define REPLY_HOST_UNREACHABLE 0x04 +#define REPLY_CONNECTION_REFUSED 0x05 +#define REPLY_TTL_EXPIRED 0x06 +#define REPLY_PROTOCOL_ERROR 0x07 +#define REPLY_TYPE_NOT_SUPPORTED 0x08 + +#define FIELD_RESERVED 0x00 + +// The standard SOCKS 5 request packet +// Push current alignment to stack and set alignment to 1 byte boundary +// This enabled us to use structs directly to set up and receive network packets +// into the correct fields, without fear of boundary alignment causing issues +#pragma pack(push,1) + +// SOCKS 5 command packet +struct socks_command_request_t { + U8 version; + U8 command; + U8 reserved; + U8 atype; + U32 address; + U16 port; +}; + +// Standard SOCKS 5 reply packet +struct socks_command_response_t { + U8 version; + U8 reply; + U8 reserved; + U8 atype; + U8 add_bytes[4]; + U16 port; +}; + +#define AUTH_NOT_ACCEPTABLE 0xFF // reply if preferred methods are not available +#define AUTH_SUCCESS 0x00 // reply if authentication successful + +// SOCKS 5 authentication request, stating which methods the client supports +struct socks_auth_request_t { + U8 version; + U8 num_methods; + U8 methods; // We are only using a single method currently +}; + +// SOCKS 5 authentication response packet, stating server preferred method +struct socks_auth_response_t { + U8 version; + U8 method; +}; + +// SOCKS 5 password reply packet +struct authmethod_password_reply_t { + U8 version; + U8 status; +}; + +// SOCKS 5 UDP packet header +struct proxywrap_t { + U16 rsv; + U8 frag; + U8 atype; + U32 addr; + U16 port; +}; + +#pragma pack(pop) /* restore original alignment from stack */ + + +// Currently selected http proxy type +enum LLHttpProxyType +{ + LLPROXY_SOCKS = 0, + LLPROXY_HTTP = 1 +}; + +// Auth types +enum LLSocks5AuthType +{ + METHOD_NOAUTH = 0x00, // Client supports no auth + METHOD_GSSAPI = 0x01, // Client supports GSSAPI (Not currently supported) + METHOD_PASSWORD = 0x02 // Client supports username/password +}; + +class LLProxy: public LLSingleton +{ +public: + LLProxy(); + ~LLProxy(); + + // Start a connection to the SOCKS 5 proxy + int startProxy(std::string host, U32 port); + + // Disconnect and clean up any connection to the SOCKS 5 proxy + void stopProxy(); + + // Set up to use Password auth when connecting to the SOCKS proxy + void setAuthPassword(const std::string &username, const std::string &password); + + // Set up to use No Auth when connecting to the SOCKS proxy + void setAuthNone(); + + // get the currently selected auth method + LLSocks5AuthType getSelectedAuthMethod() const { return mAuthMethodSelected; } + + // static check for enabled status for UDP packets + static bool isEnabled() { return sUDPProxyEnabled; } + + // static check for enabled status for http packets + static bool isHTTPProxyEnabled() { return sHTTPProxyEnabled; } + + // Proxy HTTP packets via httpHost, which can be a SOCKS 5 or a HTTP proxy + // as specified in type + void enableHTTPProxy(LLHost httpHost, LLHttpProxyType type); + + // Stop proxying HTTP packets + void disableHTTPProxy() { sHTTPProxyEnabled = false; }; + + // Get the UDP proxy address and port + LLHost getUDPProxy() const { return mUDPProxy; } + + // Get the SOCKS 5 TCP control channel address and port + LLHost getTCPProxy() const { return mTCPProxy; } + + // Get the HTTP proxy address and port + LLHost getHTTPProxy() const { return mHTTPProxy; } + + // Get the currently selected HTTP proxy type + LLHttpProxyType getHTTPProxyType() const { return mProxyType; } + + // Get the username password in a curl compatible format + std::string getProxyUserPwdCURL() const { return (mSocksUsername + ":" + mSocksPassword); } + + std::string getSocksPwd() const { return mSocksPassword; } + std::string getSocksUser() const { return mSocksUsername; } + +private: + + // Open a communication channel to the SOCKS 5 proxy proxy, at port messagePort + int proxyHandshake(LLHost proxy, U32 messagePort); + + // socket handle to proxy TCP control channel + LLSocket::ptr_t mProxyControlChannel; + + // is the UDP proxy enabled? + static bool sUDPProxyEnabled; + // is the http proxy enabled? + static bool sHTTPProxyEnabled; + + // currently selected http proxy type + LLHttpProxyType mProxyType; + + // UDP proxy address and port + LLHost mUDPProxy; + // TCP Proxy control channel address and port + LLHost mTCPProxy; + // HTTP proxy address and port + LLHost mHTTPProxy; + + // SOCKS 5 auth method selected + LLSocks5AuthType mAuthMethodSelected; + + // SOCKS 5 username + std::string mSocksUsername; + // SOCKS 5 password + std::string mSocksPassword; + + // APR pool for the socket + apr_pool_t* mPool; +}; + +#endif diff --git a/indra/llmessage/llsocks5.cpp b/indra/llmessage/llsocks5.cpp deleted file mode 100644 index 278350bf25..0000000000 --- a/indra/llmessage/llsocks5.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/** - * @file llsocks5.cpp - * @brief SOCKS 5 implementation - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llsocks5.h" - -#include - -#include "llapr.h" -#include "llhost.h" -#include "message.h" -#include "net.h" - -// Static class variable instances - -// We want this to be static to avoid excessive indirection on every -// incoming packet just to do a simple bool test. The getter for this -// member is also static -bool LLSocks::sUDPProxyEnabled; -bool LLSocks::sHTTPProxyEnabled; - -// Some helpful TCP functions -static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host -static void tcp_close_channel(LLSocket::ptr_t handle); // Close an open TCP channel -static int tcp_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen); // Do a TCP data handshake - - -LLSocks::LLSocks() -{ - sUDPProxyEnabled = false; - sHTTPProxyEnabled = false; - mProxyControlChannel.reset(); - mProxyType = LLPROXY_SOCKS; -} - -// Perform a SOCKS 5 authentication and UDP association to the proxy -// specified by proxy, and associate UDP port message_port -int LLSocks::proxyHandshake(LLHost proxy, U32 message_port) -{ - int result; - - /* SOCKS 5 Auth request */ - socks_auth_request_t socks_auth_request; - socks_auth_response_t socks_auth_response; - - socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5 - socks_auth_request.num_methods = 1; // Sending 1 method. - socks_auth_request.methods = mAuthMethodSelected; // Send only the selected method. - - result = tcp_handshake(mProxyControlChannel, (char*)&socks_auth_request, sizeof(socks_auth_request_t), (char*)&socks_auth_response, sizeof(socks_auth_response_t)); - if (result != 0) - { - llwarns << "SOCKS authentication request failed, error on TCP control channel : " << result << llendl; - stopProxy(); - return SOCKS_CONNECT_ERROR; - } - - if (socks_auth_response.method == AUTH_NOT_ACCEPTABLE) - { - llwarns << "SOCKS 5 server refused all our authentication methods" << llendl; - stopProxy(); - return SOCKS_NOT_ACCEPTABLE; - } - - // SOCKS 5 USERNAME/PASSWORD authentication - if (socks_auth_response.method == METHOD_PASSWORD) - { - // The server has requested a username/password combination - U32 request_size = mSocksUsername.size() + mSocksPassword.size() + 3; - char * password_auth = new char[request_size]; - password_auth[0] = 0x01; - password_auth[1] = mSocksUsername.size(); - memcpy(&password_auth[2], mSocksUsername.c_str(), mSocksUsername.size()); - password_auth[mSocksUsername.size()+2] = mSocksPassword.size(); - memcpy(&password_auth[mSocksUsername.size()+3], mSocksPassword.c_str(), mSocksPassword.size()); - - authmethod_password_reply_t password_reply; - - result = tcp_handshake(mProxyControlChannel, password_auth, request_size, (char*)&password_reply, sizeof(authmethod_password_reply_t)); - delete[] password_auth; - - if (result != 0) - { - llwarns << "SOCKS authentication failed, error on TCP control channel : " << result << llendl; - stopProxy(); - return SOCKS_CONNECT_ERROR; - } - - if (password_reply.status != AUTH_SUCCESS) - { - llwarns << "SOCKS authentication failed" << llendl; - stopProxy(); - return SOCKS_AUTH_FAIL; - } - } - - /* SOCKS5 connect request */ - - socks_command_request_t connect_request; - socks_command_response_t connect_reply; - - connect_request.version = SOCKS_VERSION; // SOCKS V5 - connect_request.command = COMMAND_UDP_ASSOCIATE; // Associate UDP - connect_request.reserved = FIELD_RESERVED; - connect_request.atype = ADDRESS_IPV4; - connect_request.address = htonl(0); // 0.0.0.0 - connect_request.port = htons(0); // 0 - // "If the client is not in possesion of the information at the time of the UDP ASSOCIATE, - // the client MUST use a port number and address of all zeros. RFC 1928" - - result = tcp_handshake(mProxyControlChannel, (char*)&connect_request, sizeof(socks_command_request_t), (char*)&connect_reply, sizeof(socks_command_response_t)); - if (result != 0) - { - llwarns << "SOCKS connect request failed, error on TCP control channel : " << result << llendl; - stopProxy(); - return SOCKS_CONNECT_ERROR; - } - - if (connect_reply.reply != REPLY_REQUEST_GRANTED) - { - //Something went wrong - llwarns << "Connection to SOCKS 5 server failed, UDP forward request not granted" << llendl; - stopProxy(); - return SOCKS_UDP_FWD_NOT_GRANTED; - } - - mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order - mUDPProxy.setAddress(proxy.getAddress()); - // All good now we have been given the UDP port to send requests that need forwarding. - llinfos << "SOCKS 5 UDP proxy connected on " << mUDPProxy << llendl; - return SOCKS_OK; -} - -int LLSocks::startProxy(LLHost proxy, U32 message_port) -{ - int status; - - mTCPProxy = proxy; - - if (mProxyControlChannel) - { - tcp_close_channel(mProxyControlChannel); - } - - mProxyControlChannel = tcp_open_channel(mTCPProxy); - if (!mProxyControlChannel) - { - return SOCKS_HOST_CONNECT_FAILED; - } - status = proxyHandshake(proxy, message_port); - if (status == SOCKS_OK) - { - sUDPProxyEnabled = true; - } - return status; -} - -int LLSocks::startProxy(std::string host, U32 port) -{ - mTCPProxy.setHostByName(host); - mTCPProxy.setPort(port); - return startProxy(mTCPProxy, (U32)gMessageSystem->mPort); -} - -void LLSocks::stopProxy() -{ - sUDPProxyEnabled = false; - - // If the SOCKS proxy is requested to stop and we are using that for http as well - // then we must shut down any http proxy operations. But it is allowable if web - // proxy is being used to continue proxying http. - - if(LLPROXY_SOCKS == mProxyType) - { - sHTTPProxyEnabled = false; - } - - if (mProxyControlChannel) - { - tcp_close_channel(mProxyControlChannel); - } -} - -void LLSocks::setAuthNone() -{ - mAuthMethodSelected = METHOD_NOAUTH; -} - -void LLSocks::setAuthPassword(std::string username, std::string password) -{ - mAuthMethodSelected = METHOD_PASSWORD; - mSocksUsername = username; - mSocksPassword = password; -} - -void LLSocks::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type) -{ - sHTTPProxyEnabled = true; - mHTTPProxy = httpHost; - mProxyType = type; -} - -static int tcp_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen) -{ - apr_socket_t* apr_socket = handle->getSocket(); - apr_status_t rv; - - apr_size_t expected_len = outlen; - - apr_socket_opt_set(apr_socket, APR_SO_NONBLOCK, -5); // Blocking connection, 5 second timeout - apr_socket_timeout_set(apr_socket, (APR_USEC_PER_SEC * 5)); - - rv = apr_socket_send(apr_socket, dataout, &outlen); - if (rv != APR_SUCCESS || expected_len != outlen) - { - llwarns << "Error sending data to proxy control channel" << llendl; - ll_apr_warn_status(rv); - return -1; - } - - expected_len = maxinlen; - do - { - rv = apr_socket_recv(apr_socket, datain, &maxinlen); - llinfos << "Receiving packets." << llendl; - llwarns << "Proxy control channel status: " << rv << llendl; - } while (APR_STATUS_IS_EAGAIN(rv)); - - if (rv != APR_SUCCESS) - { - llwarns << "Error receiving data from proxy control channel, status: " << rv << llendl; - llwarns << "Received " << maxinlen << " bytes." << llendl; - ll_apr_warn_status(rv); - return rv; - } - else if (expected_len != maxinlen) - { - llwarns << "Incorrect data received length in proxy control channel" << llendl; - return -1; - } - - return 0; -} - -static LLSocket::ptr_t tcp_open_channel(LLHost host) -{ - LLSocket::ptr_t socket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); - bool connected = socket->blockingConnect(host); - if (!connected) - { - tcp_close_channel(socket); - } - - return socket; -} - -static void tcp_close_channel(LLSocket::ptr_t handle) -{ - handle.reset(); -} - diff --git a/indra/llmessage/llsocks5.h b/indra/llmessage/llsocks5.h deleted file mode 100644 index 3c10f661de..0000000000 --- a/indra/llmessage/llsocks5.h +++ /dev/null @@ -1,241 +0,0 @@ -/** - * @file llsocks5.h - * @brief Socks 5 implementation - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_SOCKS5_H -#define LL_SOCKS5_H - -#include "llhost.h" -#include "lliosocket.h" -#include "llmemory.h" -#include "llsingleton.h" -#include - -// Error codes returned from the StartProxy method - -#define SOCKS_OK 0 -#define SOCKS_CONNECT_ERROR (-1) -#define SOCKS_NOT_PERMITTED (-2) -#define SOCKS_NOT_ACCEPTABLE (-3) -#define SOCKS_AUTH_FAIL (-4) -#define SOCKS_UDP_FWD_NOT_GRANTED (-5) -#define SOCKS_HOST_CONNECT_FAILED (-6) - -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN (255 + 1) /* socks5: 255, +1 for len. */ -#endif - -#define SOCKS_VERSION 0x05 // we are using SOCKS 5 - -// SOCKS 5 address/hostname types -#define ADDRESS_IPV4 0x01 -#define ADDRESS_HOSTNAME 0x03 -#define ADDRESS_IPV6 0x04 - -// Lets just use our own ipv4 struct rather than dragging in system -// specific headers -union ipv4_address_t { - U8 octets[4]; - U32 addr32; -}; - -// SOCKS 5 control channel commands -#define COMMAND_TCP_STREAM 0x01 -#define COMMAND_TCP_BIND 0x02 -#define COMMAND_UDP_ASSOCIATE 0x03 - -// SOCKS 5 command replies -#define REPLY_REQUEST_GRANTED 0x00 -#define REPLY_GENERAL_FAIL 0x01 -#define REPLY_RULESET_FAIL 0x02 -#define REPLY_NETWORK_UNREACHABLE 0x03 -#define REPLY_HOST_UNREACHABLE 0x04 -#define REPLY_CONNECTION_REFUSED 0x05 -#define REPLY_TTL_EXPIRED 0x06 -#define REPLY_PROTOCOL_ERROR 0x07 -#define REPLY_TYPE_NOT_SUPPORTED 0x08 - -#define FIELD_RESERVED 0x00 - -// The standard SOCKS 5 request packet -// Push current alignment to stack and set alignment to 1 byte boundary -// This enabled us to use structs directly to set up and receive network packets -// into the correct fields, without fear of boundary alignment causing issues -#pragma pack(push,1) - -// SOCKS 5 command packet -struct socks_command_request_t { - U8 version; - U8 command; - U8 reserved; - U8 atype; - U32 address; - U16 port; -}; - -// Standard SOCKS 5 reply packet -struct socks_command_response_t { - U8 version; - U8 reply; - U8 reserved; - U8 atype; - U8 add_bytes[4]; - U16 port; -}; - -#define AUTH_NOT_ACCEPTABLE 0xFF // reply if preferred methods are not available -#define AUTH_SUCCESS 0x00 // reply if authentication successful - -// SOCKS 5 authentication request, stating which methods the client supports -struct socks_auth_request_t { - U8 version; - U8 num_methods; - U8 methods; // We are only using a single method currently -}; - -// SOCKS 5 authentication response packet, stating server preferred method -struct socks_auth_response_t { - U8 version; - U8 method; -}; - -// SOCKS 5 password reply packet -struct authmethod_password_reply_t { - U8 version; - U8 status; -}; - -// SOCKS 5 UDP packet header -struct proxywrap_t { - U16 rsv; - U8 frag; - U8 atype; - U32 addr; - U16 port; -}; - -#pragma pack(pop) /* restore original alignment from stack */ - - -// Currently selected http proxy type -enum LLHttpProxyType -{ - LLPROXY_SOCKS = 0, - LLPROXY_HTTP = 1 -}; - -// Auth types -enum LLSocks5AuthType -{ - METHOD_NOAUTH = 0x00, // Client supports no auth - METHOD_GSSAPI = 0x01, // Client supports GSSAPI (Not currently supported) - METHOD_PASSWORD = 0x02 // Client supports username/password -}; - -class LLSocks: public LLSingleton -{ -public: - LLSocks(); - - // Start a connection to the SOCKS 5 proxy - int startProxy(std::string host, U32 port); - int startProxy(LLHost proxy, U32 messagePort); - - // Disconnect and clean up any connection to the SOCKS 5 proxy - void stopProxy(); - - // Set up to use Password auth when connecting to the SOCKS proxy - void setAuthPassword(std::string username, std::string password); - - // Set up to use No Auth when connecting to the SOCKS proxy - void setAuthNone(); - - // get the currently selected auth method - LLSocks5AuthType getSelectedAuthMethod() const { return mAuthMethodSelected; } - - // static check for enabled status for UDP packets - static bool isEnabled() { return sUDPProxyEnabled; } - - // static check for enabled status for http packets - static bool isHTTPProxyEnabled() { return sHTTPProxyEnabled; } - - // Proxy HTTP packets via httpHost, which can be a SOCKS 5 or a HTTP proxy - // as specified in type - void enableHTTPProxy(LLHost httpHost, LLHttpProxyType type); - - // Stop proxying HTTP packets - void disableHTTPProxy() { sHTTPProxyEnabled = false; }; - - // Get the UDP proxy address and port - LLHost getUDPProxy() const { return mUDPProxy; } - - // Get the SOCKS 5 TCP control channel address and port - LLHost getTCPProxy() const { return mTCPProxy; } - - // Get the HTTP proxy address and port - LLHost getHTTPProxy() const { return mHTTPProxy; } - - // Get the currently selected HTTP proxy type - LLHttpProxyType getHTTPProxyType() const { return mProxyType; } - - // Get the username password in a curl compatible format - std::string getProxyUserPwd() const { return (mSocksUsername + ":" + mSocksPassword); } - -private: - - // Open a communication channel to the SOCKS 5 proxy proxy, at port messagePort - int proxyHandshake(LLHost proxy, U32 messagePort); - - // socket handle to proxy TCP control channel - LLSocket::ptr_t mProxyControlChannel; - - // is the UDP proxy enabled? - static bool sUDPProxyEnabled; - // is the http proxy enabled? - static bool sHTTPProxyEnabled; - - // currently selected http proxy type - LLHttpProxyType mProxyType; - - // UDP proxy address and port - LLHost mUDPProxy; - // TCP Proxy control channel address and port - LLHost mTCPProxy; - // HTTP proxy address and port - LLHost mHTTPProxy; - - // SOCKS 5 auth method selected - LLSocks5AuthType mAuthMethodSelected; - - // SOCKS 5 username - std::string mSocksUsername; - // SOCKS 5 password - std::string mSocksPassword; - - // APR pool for the socket - apr_pool_t* mPool; -}; - -#endif diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp index e2d185b959..f8ab55143c 100644 --- a/indra/llmessage/net.cpp +++ b/indra/llmessage/net.cpp @@ -50,7 +50,7 @@ #include "lltimer.h" #include "indra_constants.h" -#include "llsocks5.h" +#include "llproxy.h" // Globals #if LL_WINDOWS diff --git a/indra/llui/llfunctorregistry.h b/indra/llui/llfunctorregistry.h index 752c7df7ee..899cc3a326 100644 --- a/indra/llui/llfunctorregistry.h +++ b/indra/llui/llfunctorregistry.h @@ -103,7 +103,7 @@ public: } else { - llwarns << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << llendl; + lldebugs << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << llendl; return mMap[LOGFUNCTOR]; } } @@ -115,7 +115,7 @@ private: static void log_functor(const LLSD& notification, const LLSD& payload) { - llwarns << "log_functor called with payload: " << payload << llendl; + lldebugs << "log_functor called with payload: " << payload << llendl; } static void do_nothing(const LLSD& notification, const LLSD& payload) diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index c97f0779a1..ebdef8e38f 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -105,7 +105,7 @@ #include "llviewermedia.h" #include "llpluginclassmedia.h" #include "llteleporthistorystorage.h" -#include "llsocks5.h" +#include "llproxy.h" #include "lllogininstance.h" // to check if logged in yet #include "llsdserialize.h" @@ -1940,15 +1940,19 @@ LLFloaterPreferenceProxy::~LLFloaterPreferenceProxy() BOOL LLFloaterPreferenceProxy::postBuild() { - LLLineEditor* edit = getChild("socks_password_editor"); - if (edit) edit->setDrawAsterixes(TRUE); - LLRadioGroup* socksAuth = getChild("socks5_auth_type"); if(socksAuth->getSelectedValue().asString() == "None") { getChild("socks5_username")->setEnabled(false); getChild("socks5_password")->setEnabled(false); } + else + { + // Populate the SOCKS 5 credential fields with protected values. + LLPointer socks_cred = gSecAPIHandler->loadCredential("SOCKS5"); + getChild("socks5_username")->setValue(socks_cred->getIdentifier()["username"].asString()); + getChild("socks5_password")->setValue(socks_cred->getAuthenticator()["creds"].asString()); + } center(); return TRUE; @@ -1968,11 +1972,8 @@ void LLFloaterPreferenceProxy::onClose(bool app_quitting) // it will not be updated until next restart. if(LLStartUp::getStartupState()>STATE_LOGIN_WAIT) { - if(this->mSocksSettingsDirty == true ) - { - LLNotifications::instance().add("ChangeSocks5Settings",LLSD(),LLSD()); - mSocksSettingsDirty = false; // we have notified the user now be quiet again - } + LLNotifications::instance().add("ChangeSocks5Settings", LLSD(), LLSD()); + mSocksSettingsDirty = false; // we have notified the user now be quiet again } } } @@ -2006,7 +2007,6 @@ void LLFloaterPreferenceProxy::saveSettings() view_stack.push_back(*iter); } } - } void LLFloaterPreferenceProxy::onBtnOk() @@ -2020,6 +2020,29 @@ void LLFloaterPreferenceProxy::onBtnOk() cur_focus->onCommit(); } } + + // Save SOCKS proxy credentials securely if password auth is enabled + LLRadioGroup* socksAuth = getChild("socks5_auth_type"); + if(socksAuth->getSelectedValue().asString() == "UserPass") + { + LLSD socks_id = LLSD::emptyMap(); + socks_id["type"] = "SOCKS5"; + socks_id["username"] = getChild("socks5_username")->getValue().asString(); + + LLSD socks_authenticator = LLSD::emptyMap(); + socks_authenticator["type"] = "SOCKS5"; + socks_authenticator["creds"] = getChild("socks5_password")->getValue().asString(); + + LLPointer socks_cred = gSecAPIHandler->createCredential("SOCKS5", socks_id, socks_authenticator); + gSecAPIHandler->saveCredential(socks_cred, true); + } + else + { + // Clear SOCKS5 credentials since they are no longer needed. + LLPointer socks_cred = new LLCredential("SOCKS5"); + gSecAPIHandler->deleteCredential(socks_cred); + } + closeFloater(false); } @@ -2036,8 +2059,8 @@ void LLFloaterPreferenceProxy::onBtnCancel() } cancel(); - } + void LLFloaterPreferenceProxy::cancel() { @@ -2068,7 +2091,7 @@ void LLFloaterPreferenceProxy::onChangeSocksSettings() getChild("socks5_password")->setEnabled(true); } - //Check for invalid states for the other http proxy radio + // Check for invalid states for the other HTTP proxy radio LLRadioGroup* otherHttpProxy = getChild("other_http_proxy_selection"); if( (otherHttpProxy->getSelectedValue().asString() == "Socks" && getChild("socks_proxy_enabled")->get() == FALSE )||( diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 48be251611..9b4f146332 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -30,13 +30,13 @@ // viewer includes #include "llsecapi.h" -#include "lllogininstance.h" // to check if logged in yet -#include "llpanellogin.h" // save_password_to_disk() +#include "lllogininstance.h" // to check if logged in yet +#include "llpanellogin.h" #include "llstartup.h" // getStartupState() #include "llslurl.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo -#include "llviewerwindow.h" // getWindow() +#include "llviewerwindow.h" // getWindow() // library includes #include "llmd5.h" diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 11273453ba..b1390a483a 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -115,7 +115,4 @@ private: static BOOL sCapslockDidNotification; }; -std::string load_password_from_disk(void); -void save_password_to_disk(const char* hashed_password); - #endif diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index b65cf37e7f..812a539324 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -286,8 +286,8 @@ bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVe #define CRED_AUTHENTICATOR_TYPE_HASH "hash" // // LLCredential - interface for credentials providing the following functionality: -// * persistance of credential information based on grid (for saving username/password) -// * serialization to an OGP identifier/authenticator pair +// * Persistence of credential information based on grid (for saving username/password) +// * Serialization to an OGP identifier/authenticator pair // class LLCredential : public LLRefCount { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index c2f0ca164b..7f14e403b0 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -125,6 +125,7 @@ #include "llpanelgroupnotices.h" #include "llpreview.h" #include "llpreviewscript.h" +#include "llproxy.h" #include "llproductinforequest.h" #include "llsecondlifeurls.h" #include "llselectmgr.h" @@ -191,8 +192,6 @@ #include "llevents.h" #include "llstartuplistener.h" -#include "llsocks5.h" - #if LL_WINDOWS #include "lldxhardware.h" #endif @@ -392,7 +391,7 @@ bool idle_startup() gSavedSettings.setS32("LastGPUClass", LLFeatureManager::getInstance()->getGPUClass()); // load dynamic GPU/feature tables from website (S3) - //LLFeatureManager::getInstance()->fetchHTTPTables(); + LLFeatureManager::getInstance()->fetchHTTPTables(); std::string xml_file = LLUI::locateSkin("xui_version.xml"); LLXMLNodePtr root; @@ -595,13 +594,13 @@ bool idle_startup() LL_INFOS("AppInit") << "Message System Initialized." << LL_ENDL; //------------------------------------------------- - // Init the socks 5 proxy and open the control TCP - // connection if the user is using SOCKS5 - // We need to do this early incase the user is using - // socks for http so we get the login screen via socks + // Init the SOCKS 5 proxy and open the control TCP + // connection if the user is using SOCKS 5 + // We need to do this early in case the user is using + // socks for HTTP so we get the login screen via SOCKS //------------------------------------------------- - LLStartUp::handleSocksProxy(false); + LLStartUp::handleSocksProxy(); //------------------------------------------------- // Init audio, which may be needed for prefs dialog @@ -823,7 +822,7 @@ bool idle_startup() // past this point may require the proxy to be up. if ( gSavedSettings.getBOOL("Socks5ProxyEnabled") ) { - if (!LLStartUp::handleSocksProxy(true)) + if (!LLStartUp::handleSocksProxy()) { // Proxy start up failed, we should now bail the state machine // HandleSocksProxy() will have reported an error to the user @@ -835,7 +834,7 @@ bool idle_startup() } else { - LLSocks::getInstance()->stopProxy(); + LLProxy::getInstance()->stopProxy(); } @@ -2760,54 +2759,70 @@ void LLStartUp::setStartSLURL(const LLSLURL& slurl) } } -bool LLStartUp::handleSocksProxy(bool reportOK) +bool LLStartUp::handleSocksProxy() { std::string httpProxyType = gSavedSettings.getString("Socks5HttpProxyType"); - // Determine the http proxy type (if any) + // Determine the HTTP proxy type (if any) if ((httpProxyType.compare("Web") == 0) && gSavedSettings.getBOOL("BrowserProxyEnabled")) { LLHost httpHost; httpHost.setHostByName(gSavedSettings.getString("BrowserProxyAddress")); httpHost.setPort(gSavedSettings.getS32("BrowserProxyPort")); - LLSocks::getInstance()->enableHTTPProxy(httpHost,LLPROXY_HTTP); + LLProxy::getInstance()->enableHTTPProxy(httpHost, LLPROXY_HTTP); } else if ((httpProxyType.compare("Socks") == 0) && gSavedSettings.getBOOL("Socks5ProxyEnabled")) { LLHost httpHost; httpHost.setHostByName(gSavedSettings.getString("Socks5ProxyHost")); httpHost.setPort(gSavedSettings.getU32("Socks5ProxyPort")); - LLSocks::getInstance()->enableHTTPProxy(httpHost,LLPROXY_SOCKS); + LLProxy::getInstance()->enableHTTPProxy(httpHost, LLPROXY_SOCKS); } else { - LLSocks::getInstance()->disableHTTPProxy(); + LLProxy::getInstance()->disableHTTPProxy(); } bool use_socks_proxy = gSavedSettings.getBOOL("Socks5ProxyEnabled"); if (use_socks_proxy) { - // Determine and update LLSocks with the saved authentication system + // Determine and update LLProxy with the saved authentication system std::string auth_type = gSavedSettings.getString("Socks5AuthType"); - - if (auth_type.compare("None") == 0) - { - LLSocks::getInstance()->setAuthNone(); - } if (auth_type.compare("UserPass") == 0) { - LLSocks::getInstance()->setAuthPassword(gSavedSettings.getString("Socks5Username"),gSavedSettings.getString("Socks5Password")); + LLPointer socks_cred = gSecAPIHandler->loadCredential("SOCKS5"); + std::string socks_user = socks_cred->getIdentifier()["username"].asString(); + std::string socks_password = socks_cred->getAuthenticator()["creds"].asString(); + LLProxy::getInstance()->setAuthPassword(socks_user, socks_password); + } + else if (auth_type.compare("None") == 0) + { + LLProxy::getInstance()->setAuthNone(); + } + else + { + // Unknown or missing setting. + gSavedSettings.setString("Socks5AuthType", "None"); + + // Clear the SOCKS credentials. + LLPointer socks_cred = new LLCredential("SOCKS5"); + gSecAPIHandler->deleteCredential(socks_cred); + + LLProxy::getInstance()->setAuthNone(); } // Start the proxy and check for errors - int status = LLSocks::getInstance()->startProxy(gSavedSettings.getString("Socks5ProxyHost"), gSavedSettings.getU32("Socks5ProxyPort")); + // If status != SOCKS_OK, stopProxy() will already have been called when startProxy() returns. + int status = LLProxy::getInstance()->startProxy(gSavedSettings.getString("Socks5ProxyHost"), gSavedSettings.getU32("Socks5ProxyPort")); LLSD subs; LLSD payload; subs["HOST"] = gSavedSettings.getString("Socks5ProxyHost"); subs["PORT"] = (S32)gSavedSettings.getU32("Socks5ProxyPort"); + std::string error_string; + switch(status) { case SOCKS_OK: @@ -2815,35 +2830,36 @@ bool LLStartUp::handleSocksProxy(bool reportOK) break; case SOCKS_CONNECT_ERROR: // TCP Fail - LLNotifications::instance().add("SOCKS_CONNECT_ERROR", subs,payload); + error_string = "SOCKS_CONNECT_ERROR"; break; - case SOCKS_NOT_PERMITTED: // Socks5 server rule set refused connection - LLNotifications::instance().add("SOCKS_NOT_PERMITTED", subs,payload); + case SOCKS_NOT_PERMITTED: // SOCKS 5 server rule set refused connection + error_string = "SOCKS_NOT_PERMITTED"; break; case SOCKS_NOT_ACCEPTABLE: // Selected authentication is not acceptable to server - LLNotifications::instance().add("SOCKS_NOT_ACCEPTABLE", subs,payload); + error_string = "SOCKS_NOT_ACCEPTABLE"; break; case SOCKS_AUTH_FAIL: // Authentication failed - LLNotifications::instance().add("SOCKS_AUTH_FAIL", subs,payload); + error_string = "SOCKS_AUTH_FAIL"; break; case SOCKS_UDP_FWD_NOT_GRANTED: // UDP forward request failed - LLNotifications::instance().add("SOCKS_UDP_FWD_NOT_GRANTED", subs,payload); + error_string = "SOCKS_UDP_FWD_NOT_GRANTED"; break; case SOCKS_HOST_CONNECT_FAILED: // Failed to open a TCP channel to the socks server - LLNotifications::instance().add("SOCKS_HOST_CONNECT_FAILED", subs,payload); - break; + error_string = "SOCKS_HOST_CONNECT_FAILED"; + break; } + LLNotificationsUtil::add(error_string, subs); return false; } else { - LLSocks::getInstance()->stopProxy(); // ensure no UDP proxy is running and it's all cleaned up + LLProxy::getInstance()->stopProxy(); // ensure no UDP proxy is running and it's all cleaned up } return true; diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index a512ec7bff..7292e4d68c 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -113,7 +113,7 @@ public: static void setStartSLURL(const LLSLURL& slurl); static LLSLURL& getStartSLURL() { return sStartSLURL; } - static bool handleSocksProxy(bool reportOK); //handle kicking the socks 5 proxy code at startup time + static bool handleSocksProxy(); // Initialize the SOCKS 5 proxy private: static LLSLURL sStartSLURL; diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index b9ce7d9fae..ef6763a5d1 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -41,7 +41,7 @@ #include "llappviewer.h" #include "lltrans.h" -#include "llsocks5.h" +#include "llproxy.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 @@ -309,18 +309,18 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) } mErrorCert = NULL; - if (LLSocks::getInstance()->isHTTPProxyEnabled()) + if (LLProxy::getInstance()->isHTTPProxyEnabled()) { - std::string address = LLSocks::getInstance()->getHTTPProxy().getIPString(); - U16 port = LLSocks::getInstance()->getHTTPProxy().getPort(); + std::string address = LLProxy::getInstance()->getHTTPProxy().getIPString(); + U16 port = LLProxy::getInstance()->getHTTPProxy().getPort(); mCurlRequest->setoptString(CURLOPT_PROXY, address.c_str()); mCurlRequest->setopt(CURLOPT_PROXYPORT, port); - if (LLSocks::getInstance()->getHTTPProxyType() == LLPROXY_SOCKS) + if (LLProxy::getInstance()->getHTTPProxyType() == LLPROXY_SOCKS) { mCurlRequest->setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - if(LLSocks::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) + if(LLProxy::getInstance()->getSelectedAuthMethod()==METHOD_PASSWORD) { - mCurlRequest->setoptString(CURLOPT_PROXYUSERPWD,LLSocks::getInstance()->getProxyUserPwd()); + mCurlRequest->setoptString(CURLOPT_PROXYUSERPWD,LLProxy::getInstance()->getProxyUserPwdCURL()); } } else diff --git a/indra/newview/skins/default/xui/en/floater_preferences_proxy.xml b/indra/newview/skins/default/xui/en/floater_preferences_proxy.xml index 9baa9a0e02..53060b0326 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_proxy.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_proxy.xml @@ -3,82 +3,82 @@ legacy_header_height="18" height="490" layout="topleft" - name="Socks5 Advanced Settings Floater" + name="Proxy Settings Floater" help_topic="hardware_settings_floater" title="Socks5 proxy advanced settings" width="385"> - - - - Other Http traffic proxy: - - - - - - - + + + Other Http traffic proxy: + + + + + + + + tool_tip="The port of the SOCKS 5 proxy you would like to use." + commit_callback.function="Proxy.Change" /> + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="16" + name="Proxy location" + top_delta="35" + width="300"> Authentication: - + tool_tip="Socks5 proxy requires no authentication." width="120" /> + tool_tip="Socks5 proxy requires username/password authentication." width="120" />