diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 8d4969a49ea7100e8bf6e35df302d7437067cc60..d01e1869b6983dfbb1116499330bdbb783504e54 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -22,7 +22,10 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
 include(Variables)
 
 if (DARWIN)
-  cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR)
+  # 2.6.4 fixes a Mac bug in get_target_property(... "SLPlugin" LOCATION):
+  # before that version it returns "pathname/SLPlugin", whereas the correct
+  # answer is "pathname/SLPlugin.app/Contents/MacOS/SLPlugin".
+  cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR)
 endif (DARWIN)
 
 if (NOT CMAKE_BUILD_TYPE)
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index a114d6e7783497b8ad194ee5eb9263617ca1c560..db2cdb5ff8fa3f41e8f41621d3067f0797d80ca0 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -38,10 +38,10 @@ if (WINDOWS)
   set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /MDd /MP"
       CACHE STRING "C++ compiler debug options" FORCE)
   set(CMAKE_CXX_FLAGS_RELWITHDEBINFO 
-      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP"
+      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP /Ob2"
       CACHE STRING "C++ compiler release-with-debug options" FORCE)
   set(CMAKE_CXX_FLAGS_RELEASE
-      "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP"
+      "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2"
       CACHE STRING "C++ compiler release options" FORCE)
 
   set(CMAKE_CXX_STANDARD_LIBRARIES "")
diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake
index 79c3bb7da25b3d3d4417f44335aa6a30e5d1b695..29e2492551cfd3f0b81c8708d860da14416ccf0c 100644
--- a/indra/cmake/LLAddBuildTest.cmake
+++ b/indra/cmake/LLAddBuildTest.cmake
@@ -1,265 +1,273 @@
-# -*- cmake -*-
-include(LLTestCommand)
-include(GoogleMock)
-
-MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
-  # Given a project name and a list of sourcefiles (with optional properties on each),
-  # add targets to build and run the tests specified.
-  # ASSUMPTIONS:
-  # * this macro is being executed in the project file that is passed in
-  # * current working SOURCE dir is that project dir
-  # * there is a subfolder tests/ with test code corresponding to the filenames passed in
-  # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT)
-  #
-  # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code
-  #
-  # WARNING: do NOT modify this code without working with poppy -
-  # there is another branch that will conflict heavily with any changes here.
-INCLUDE(GoogleMock)
-
-
-  IF(LL_TEST_VERBOSE)
-    MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}")
-  ENDIF(LL_TEST_VERBOSE)
-
-  # Start with the header and project-wide setup before making targets
-  #project(UNITTEST_PROJECT_${project})
-  # Setup includes, paths, etc
-  SET(alltest_SOURCE_FILES
-    ${CMAKE_SOURCE_DIR}/test/test.cpp
-    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
-    )
-  SET(alltest_DEP_TARGETS
-    # needed by the test harness itself
-    ${APRUTIL_LIBRARIES}
-    ${APR_LIBRARIES}
-    llcommon
-    )
-  IF(NOT "${project}" STREQUAL "llmath")
-    # add llmath as a dep unless the tested module *is* llmath!
-    LIST(APPEND alltest_DEP_TARGETS
-      llmath
-      )
-  ENDIF(NOT "${project}" STREQUAL "llmath")
-  SET(alltest_INCLUDE_DIRS
-    ${LLMATH_INCLUDE_DIRS}
-    ${LLCOMMON_INCLUDE_DIRS}
-    ${LIBS_OPEN_DIR}/test
-    ${GOOGLEMOCK_INCLUDE_DIRS}
-    )
-  SET(alltest_LIBRARIES
-    ${GOOGLEMOCK_LIBRARIES}
-    ${PTHREAD_LIBRARY}
-    ${WINDOWS_LIBRARIES}
-    )
-  # Headers, for convenience in targets.
-  SET(alltest_HEADER_FILES
-    ${CMAKE_SOURCE_DIR}/test/test.h
-    )
-
-  # Use the default flags
-  if (LINUX)
-    SET(CMAKE_EXE_LINKER_FLAGS "")
-  endif (LINUX)
-
-  # start the source test executable definitions
-  SET(${project}_TEST_OUTPUT "")
-  FOREACH (source ${sources})
-    STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} )
-    STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} )
-    IF(LL_TEST_VERBOSE)
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})")
-    ENDIF(LL_TEST_VERBOSE)
-
-    #
-    # Per-codefile additional / external source, header, and include dir property extraction
-    #
-    # Source
-    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES)
-    IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
-      SET(${name}_test_additional_SOURCE_FILES "")
-    ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
-    SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} )
-    IF(LL_TEST_VERBOSE)
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}")
-    ENDIF(LL_TEST_VERBOSE)
-    # Headers
-    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES)
-    IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
-      SET(${name}_test_additional_HEADER_FILES "")
-    ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
-    SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES})
-    set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
-    LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES})
-    IF(LL_TEST_VERBOSE)
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}")
-    ENDIF(LL_TEST_VERBOSE)
-    # Include dirs
-    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS)
-    IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
-      SET(${name}_test_additional_INCLUDE_DIRS "")
-    ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
-    INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS )
-    IF(LL_TEST_VERBOSE)
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}")
-    ENDIF(LL_TEST_VERBOSE)
-
-
-    # Setup target
-    ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES})
-    SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
-
-    #
-    # Per-codefile additional / external project dep and lib dep property extraction
-    #
-    # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19
-    # Projects
-    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS)
-    IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
-      SET(${name}_test_additional_PROJECTS "")
-    ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
-    # Libraries
-    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES)
-    IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
-      SET(${name}_test_additional_LIBRARIES "")
-    ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
-    IF(LL_TEST_VERBOSE)
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}")
-      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}")
-    ENDIF(LL_TEST_VERBOSE)
-    # Add to project
-    TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} )
-    
-    #
-    # Setup test targets
-    #
-    GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION)
-    SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt)
-    SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR})
-
-    # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19
-    IF(LL_TEST_VERBOSE)
-      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd  = ${TEST_CMD}")
-    ENDIF(LL_TEST_VERBOSE)
-
-    SET_TEST_PATH(LD_LIBRARY_PATH)
-    LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD})
-    IF(LL_TEST_VERBOSE)
-      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script  = ${TEST_SCRIPT_CMD}")
-    ENDIF(LL_TEST_VERBOSE)
-    # Add test 
-    ADD_CUSTOM_COMMAND(
-        OUTPUT ${TEST_OUTPUT}
-        COMMAND ${TEST_SCRIPT_CMD}
-        DEPENDS PROJECT_${project}_TEST_${name}
-        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-        )
-    # Why not add custom target and add POST_BUILD command?
-    # Slightly less uncertain behavior
-    # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19
-    # > I did not use a post build step as I could not make it notify of a 
-    # > failure after the first time you build and fail a test. - daveh 2009-04-20
-    LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT})
-  ENDFOREACH (source)
-
-  # Add the test runner target per-project
-  # (replaces old _test_ok targets all over the place)
-  ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT})
-  ADD_DEPENDENCIES(${project} ${project}_tests)
-ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS)
-
-FUNCTION(LL_ADD_INTEGRATION_TEST 
-    testname
-    additional_source_files
-    library_dependencies
-# variable args
-    )
-  if(TEST_DEBUG)
-    message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on")
-  endif(TEST_DEBUG)
-  
-  SET(source_files
-    tests/${testname}_test.cpp
-    ${CMAKE_SOURCE_DIR}/test/test.cpp
-    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
-    ${additional_source_files}
-    )
-
-  SET(libraries
-    ${library_dependencies}
-    ${GOOGLEMOCK_LIBRARIES}
-    ${PTHREAD_LIBRARY}
-    )
-
-  # Add test executable build target
-  if(TEST_DEBUG)
-    message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})")
-  endif(TEST_DEBUG)
-  ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})
-  SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
-
-  # Add link deps to the executable
-  if(TEST_DEBUG)
-    message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})")
-  endif(TEST_DEBUG)
-  TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})
-
-  # Create the test running command
-  SET(test_command ${ARGN})
-  GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION)
-  LIST(FIND test_command "{}" test_exe_pos)
-  IF(test_exe_pos LESS 0)
-    # The {} marker means "the full pathname of the test executable."
-    # test_exe_pos -1 means we didn't find it -- so append the test executable
-    # name to $ARGN, the variable part of the arg list. This is convenient
-    # shorthand for both straightforward execution of the test program (empty
-    # $ARGN) and for running a "wrapper" program of some kind accepting the
-    # pathname of the test program as the last of its args. You need specify
-    # {} only if the test program's pathname isn't the last argument in the
-    # desired command line.
-    LIST(APPEND test_command "${TEST_EXE}")
-  ELSE (test_exe_pos LESS 0)
-    # Found {} marker at test_exe_pos. Remove the {}...
-    LIST(REMOVE_AT test_command test_exe_pos)
-    # ...and replace it with the actual name of the test executable.
-    LIST(INSERT test_command test_exe_pos "${TEST_EXE}")
-  ENDIF (test_exe_pos LESS 0)
-
-  SET_TEST_PATH(LD_LIBRARY_PATH)
-  LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command})
-
-  if(TEST_DEBUG)
-    message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}")
-  endif(TEST_DEBUG)
-
-  ADD_CUSTOM_COMMAND(
-    TARGET INTEGRATION_TEST_${testname}
-    POST_BUILD
-    COMMAND ${TEST_SCRIPT_CMD}
-    )
-
-  # Use CTEST? Not sure how to yet...
-  # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD})
-
-ENDFUNCTION(LL_ADD_INTEGRATION_TEST)
-
-MACRO(SET_TEST_PATH LISTVAR)
-  IF(WINDOWS)
-    # We typically build/package only Release variants of third-party
-    # libraries, so append the Release staging dir in case the library being
-    # sought doesn't have a debug variant.
-    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release)
-  ELSEIF(DARWIN)
-    # We typically build/package only Release variants of third-party
-    # libraries, so append the Release staging dir in case the library being
-    # sought doesn't have a debug variant.
-    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib)
-  ELSE(WINDOWS)
-    # Linux uses a single staging directory anyway.
-    IF (STANDALONE)
-      set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib)
-    ELSE (STANDALONE)
-      set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib)
-    ENDIF (STANDALONE)
-  ENDIF(WINDOWS)
-ENDMACRO(SET_TEST_PATH)
+# -*- cmake -*-
+include(LLTestCommand)
+include(GoogleMock)
+
+MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
+  # Given a project name and a list of sourcefiles (with optional properties on each),
+  # add targets to build and run the tests specified.
+  # ASSUMPTIONS:
+  # * this macro is being executed in the project file that is passed in
+  # * current working SOURCE dir is that project dir
+  # * there is a subfolder tests/ with test code corresponding to the filenames passed in
+  # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT)
+  #
+  # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code
+  #
+  # WARNING: do NOT modify this code without working with poppy -
+  # there is another branch that will conflict heavily with any changes here.
+INCLUDE(GoogleMock)
+
+
+  IF(LL_TEST_VERBOSE)
+    MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}")
+  ENDIF(LL_TEST_VERBOSE)
+
+  # Start with the header and project-wide setup before making targets
+  #project(UNITTEST_PROJECT_${project})
+  # Setup includes, paths, etc
+  SET(alltest_SOURCE_FILES
+    ${CMAKE_SOURCE_DIR}/test/test.cpp
+    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+    )
+  SET(alltest_DEP_TARGETS
+    # needed by the test harness itself
+    ${APRUTIL_LIBRARIES}
+    ${APR_LIBRARIES}
+    llcommon
+    )
+  IF(NOT "${project}" STREQUAL "llmath")
+    # add llmath as a dep unless the tested module *is* llmath!
+    LIST(APPEND alltest_DEP_TARGETS
+      llmath
+      )
+  ENDIF(NOT "${project}" STREQUAL "llmath")
+  SET(alltest_INCLUDE_DIRS
+    ${LLMATH_INCLUDE_DIRS}
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LIBS_OPEN_DIR}/test
+    ${GOOGLEMOCK_INCLUDE_DIRS}
+    )
+  SET(alltest_LIBRARIES
+    ${GOOGLEMOCK_LIBRARIES}
+    ${PTHREAD_LIBRARY}
+    ${WINDOWS_LIBRARIES}
+    )
+  # Headers, for convenience in targets.
+  SET(alltest_HEADER_FILES
+    ${CMAKE_SOURCE_DIR}/test/test.h
+    )
+
+  # Use the default flags
+  if (LINUX)
+    SET(CMAKE_EXE_LINKER_FLAGS "")
+  endif (LINUX)
+
+  # start the source test executable definitions
+  SET(${project}_TEST_OUTPUT "")
+  FOREACH (source ${sources})
+    STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} )
+    STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} )
+    IF(LL_TEST_VERBOSE)
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})")
+    ENDIF(LL_TEST_VERBOSE)
+
+    #
+    # Per-codefile additional / external source, header, and include dir property extraction
+    #
+    # Source
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES)
+    IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
+      SET(${name}_test_additional_SOURCE_FILES "")
+    ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
+    SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} )
+    IF(LL_TEST_VERBOSE)
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}")
+    ENDIF(LL_TEST_VERBOSE)
+    # Headers
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES)
+    IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
+      SET(${name}_test_additional_HEADER_FILES "")
+    ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
+    SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES})
+    set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
+    LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES})
+    IF(LL_TEST_VERBOSE)
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}")
+    ENDIF(LL_TEST_VERBOSE)
+    # Include dirs
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS)
+    IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
+      SET(${name}_test_additional_INCLUDE_DIRS "")
+    ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
+    INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS )
+    IF(LL_TEST_VERBOSE)
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}")
+    ENDIF(LL_TEST_VERBOSE)
+
+
+    # Setup target
+    ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES})
+    SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
+
+    #
+    # Per-codefile additional / external project dep and lib dep property extraction
+    #
+    # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19
+    # Projects
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS)
+    IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
+      SET(${name}_test_additional_PROJECTS "")
+    ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
+    # Libraries
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES)
+    IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
+      SET(${name}_test_additional_LIBRARIES "")
+    ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
+    IF(LL_TEST_VERBOSE)
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}")
+      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}")
+    ENDIF(LL_TEST_VERBOSE)
+    # Add to project
+    TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} )
+    # Compile-time Definitions
+    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_CFLAGS ${source} LL_TEST_ADDITIONAL_CFLAGS)
+     IF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
+       SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES COMPILE_FLAGS ${${name}_test_additional_CFLAGS} )
+       IF(LL_TEST_VERBOSE)
+         MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}")
+       ENDIF(LL_TEST_VERBOSE)
+     ENDIF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
+     
+    #
+    # Setup test targets
+    #
+    GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION)
+    SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt)
+    SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR})
+
+    # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19
+    IF(LL_TEST_VERBOSE)
+      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd  = ${TEST_CMD}")
+    ENDIF(LL_TEST_VERBOSE)
+
+    SET_TEST_PATH(LD_LIBRARY_PATH)
+    LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD})
+    IF(LL_TEST_VERBOSE)
+      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script  = ${TEST_SCRIPT_CMD}")
+    ENDIF(LL_TEST_VERBOSE)
+    # Add test 
+    ADD_CUSTOM_COMMAND(
+        OUTPUT ${TEST_OUTPUT}
+        COMMAND ${TEST_SCRIPT_CMD}
+        DEPENDS PROJECT_${project}_TEST_${name}
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+        )
+    # Why not add custom target and add POST_BUILD command?
+    # Slightly less uncertain behavior
+    # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19
+    # > I did not use a post build step as I could not make it notify of a 
+    # > failure after the first time you build and fail a test. - daveh 2009-04-20
+    LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT})
+  ENDFOREACH (source)
+
+  # Add the test runner target per-project
+  # (replaces old _test_ok targets all over the place)
+  ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT})
+  ADD_DEPENDENCIES(${project} ${project}_tests)
+ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS)
+
+FUNCTION(LL_ADD_INTEGRATION_TEST 
+    testname
+    additional_source_files
+    library_dependencies
+# variable args
+    )
+  if(TEST_DEBUG)
+    message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on")
+  endif(TEST_DEBUG)
+  
+  SET(source_files
+    tests/${testname}_test.cpp
+    ${CMAKE_SOURCE_DIR}/test/test.cpp
+    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+    ${additional_source_files}
+    )
+
+  SET(libraries
+    ${library_dependencies}
+    ${GOOGLEMOCK_LIBRARIES}
+    ${PTHREAD_LIBRARY}
+    )
+
+  # Add test executable build target
+  if(TEST_DEBUG)
+    message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})")
+  endif(TEST_DEBUG)
+  ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})
+  SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
+
+  # Add link deps to the executable
+  if(TEST_DEBUG)
+    message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})")
+  endif(TEST_DEBUG)
+  TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})
+
+  # Create the test running command
+  SET(test_command ${ARGN})
+  GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION)
+  LIST(FIND test_command "{}" test_exe_pos)
+  IF(test_exe_pos LESS 0)
+    # The {} marker means "the full pathname of the test executable."
+    # test_exe_pos -1 means we didn't find it -- so append the test executable
+    # name to $ARGN, the variable part of the arg list. This is convenient
+    # shorthand for both straightforward execution of the test program (empty
+    # $ARGN) and for running a "wrapper" program of some kind accepting the
+    # pathname of the test program as the last of its args. You need specify
+    # {} only if the test program's pathname isn't the last argument in the
+    # desired command line.
+    LIST(APPEND test_command "${TEST_EXE}")
+  ELSE (test_exe_pos LESS 0)
+    # Found {} marker at test_exe_pos. Remove the {}...
+    LIST(REMOVE_AT test_command test_exe_pos)
+    # ...and replace it with the actual name of the test executable.
+    LIST(INSERT test_command test_exe_pos "${TEST_EXE}")
+  ENDIF (test_exe_pos LESS 0)
+
+  SET_TEST_PATH(LD_LIBRARY_PATH)
+  LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command})
+
+  if(TEST_DEBUG)
+    message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}")
+  endif(TEST_DEBUG)
+
+  ADD_CUSTOM_COMMAND(
+    TARGET INTEGRATION_TEST_${testname}
+    POST_BUILD
+    COMMAND ${TEST_SCRIPT_CMD}
+    )
+
+  # Use CTEST? Not sure how to yet...
+  # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD})
+
+ENDFUNCTION(LL_ADD_INTEGRATION_TEST)
+
+MACRO(SET_TEST_PATH LISTVAR)
+  IF(WINDOWS)
+    # We typically build/package only Release variants of third-party
+    # libraries, so append the Release staging dir in case the library being
+    # sought doesn't have a debug variant.
+    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release)
+  ELSEIF(DARWIN)
+    # We typically build/package only Release variants of third-party
+    # libraries, so append the Release staging dir in case the library being
+    # sought doesn't have a debug variant.
+    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib)
+  ELSE(WINDOWS)
+    # Linux uses a single staging directory anyway.
+    IF (STANDALONE)
+      set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib)
+    ELSE (STANDALONE)
+      set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib)
+    ENDIF (STANDALONE)
+  ENDIF(WINDOWS)
+ENDMACRO(SET_TEST_PATH)
diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp
index 23c34e52e799deae245a6b1c803f5ef5aa89262e..d909516bf8fbce6872458eec049e0b4c836df6ae 100644
--- a/indra/linux_updater/linux_updater.cpp
+++ b/indra/linux_updater/linux_updater.cpp
@@ -49,6 +49,7 @@ const guint ROTATE_IMAGE_TIMEOUT = 8000;
 typedef struct _updater_app_state {
 	std::string app_name;
 	std::string url;
+	std::string file;
 	std::string image_dir;
 	std::string dest_dir;
 	std::string strings_dirs;
@@ -113,7 +114,7 @@ BOOL install_package(std::string package_file, std::string destination);
 BOOL spawn_viewer(UpdaterAppState *app_state);
 
 extern "C" {
-	void on_window_closed(GtkWidget *sender, gpointer state);
+	void on_window_closed(GtkWidget *sender, GdkEvent *event, gpointer state);
 	gpointer worker_thread_cb(gpointer *data);
 	int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);
 	gboolean rotate_image_cb(gpointer data);
@@ -220,7 +221,7 @@ std::string next_image_filename(std::string& image_path)
 	return image_path + "/" + image_filename;
 }
 
-void on_window_closed(GtkWidget *sender, gpointer data)
+void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data)
 {
 	UpdaterAppState *app_state;
 
@@ -266,85 +267,95 @@ gpointer worker_thread_cb(gpointer data)
 	CURLcode result;
 	FILE *package_file;
 	GError *error = NULL;
-	char *tmp_filename = NULL;
 	int fd;
 
 	//g_return_val_if_fail (data != NULL, NULL);
 	app_state = (UpdaterAppState *) data;
 
 	try {
-		// create temporary file to store the package.
-		fd = g_file_open_tmp
-			("secondlife-update-XXXXXX", &tmp_filename, &error);
-		if (error != NULL)
-		{
-			llerrs << "Unable to create temporary file: "
-			       << error->message
-			       << llendl;
-
-			g_error_free(error);
-			throw 0;
-		}
 
-		package_file = fdopen(fd, "wb");
-		if (package_file == NULL)
+		if(!app_state->url.empty())
 		{
-			llerrs << "Failed to create temporary file: "
-			       << tmp_filename
-			       << llendl;
+			char* tmp_local_filename = NULL;
+			// create temporary file to store the package.
+			fd = g_file_open_tmp
+				("secondlife-update-XXXXXX", &tmp_local_filename, &error);
+			if (error != NULL)
+			{
+				llerrs << "Unable to create temporary file: "
+					   << error->message
+					   << llendl;
 
-			gdk_threads_enter();
-			display_error(app_state->window,
-				      LLTrans::getString("UpdaterFailDownloadTitle"),
-				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
-			gdk_threads_leave();
-			throw 0;
-		}
+				g_error_free(error);
+				throw 0;
+			}
+			
+			if(tmp_local_filename != NULL)
+			{
+				app_state->file = tmp_local_filename;
+				g_free(tmp_local_filename);
+			}
 
-		// initialize curl and start downloading the package
-		llinfos << "Downloading package: " << app_state->url << llendl;
+			package_file = fdopen(fd, "wb");
+			if (package_file == NULL)
+			{
+				llerrs << "Failed to create temporary file: "
+					   << app_state->file.c_str()
+					   << llendl;
+
+				gdk_threads_enter();
+				display_error(app_state->window,
+							  LLTrans::getString("UpdaterFailDownloadTitle"),
+							  LLTrans::getString("UpdaterFailUpdateDescriptive"));
+				gdk_threads_leave();
+				throw 0;
+			}
 
-		curl = curl_easy_init();
-		if (curl == NULL)
-		{
-			llerrs << "Failed to initialize libcurl" << llendl;
+			// initialize curl and start downloading the package
+			llinfos << "Downloading package: " << app_state->url << llendl;
 
-			gdk_threads_enter();
-			display_error(app_state->window,
-				      LLTrans::getString("UpdaterFailDownloadTitle"),
-				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
-			gdk_threads_leave();
-			throw 0;
-		}
+			curl = curl_easy_init();
+			if (curl == NULL)
+			{
+				llerrs << "Failed to initialize libcurl" << llendl;
+
+				gdk_threads_enter();
+				display_error(app_state->window,
+							  LLTrans::getString("UpdaterFailDownloadTitle"),
+							  LLTrans::getString("UpdaterFailUpdateDescriptive"));
+				gdk_threads_leave();
+				throw 0;
+			}
 
-		curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
-		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
-		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
-		curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
-		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
-		curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, 
-				 &download_progress_cb);
-		curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
+			curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
+			curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
+			curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
+			curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
+			curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
+			curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, 
+							 &download_progress_cb);
+			curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
 
-		result = curl_easy_perform(curl);
-		fclose(package_file);
-		curl_easy_cleanup(curl);
+			result = curl_easy_perform(curl);
+			fclose(package_file);
+			curl_easy_cleanup(curl);
 
-		if (result)
-		{
-			llerrs << "Failed to download update: " 
-			       << app_state->url 
-			       << llendl;
+			if (result)
+			{
+				llerrs << "Failed to download update: " 
+					   << app_state->url 
+					   << llendl;
 
-			gdk_threads_enter();
-			display_error(app_state->window,
-				      LLTrans::getString("UpdaterFailDownloadTitle"),
-				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
-			gdk_threads_leave();
+				gdk_threads_enter();
+				display_error(app_state->window,
+							  LLTrans::getString("UpdaterFailDownloadTitle"),
+							  LLTrans::getString("UpdaterFailUpdateDescriptive"));
+				gdk_threads_leave();
 
-			throw 0;
+				throw 0;
+			}
 		}
-
+		
 		// now pulse the progres bar back and forth while the package is
 		// being unpacked
 		gdk_threads_enter();
@@ -357,7 +368,7 @@ gpointer worker_thread_cb(gpointer data)
 
 		// *TODO: if the destination is not writable, terminate this
 		// thread and show file chooser?
-		if (!install_package(tmp_filename, app_state->dest_dir))
+		if (!install_package(app_state->file.c_str(), app_state->dest_dir))
 		{
 			llwarns << "Failed to install package to destination: "
 				<< app_state->dest_dir
@@ -392,15 +403,6 @@ gpointer worker_thread_cb(gpointer data)
 		app_state->failure = TRUE;
 	}
 
-	// FIXME: delete package file also if delete-event is raised on window
-	if (tmp_filename != NULL)
-	{
-		if (gDirUtilp->fileExists(tmp_filename))
-		{
-			LLFile::remove(tmp_filename);
-		}
-	}
-
 	gdk_threads_enter();
 	updater_app_quit(app_state);
 	gdk_threads_leave();
@@ -712,7 +714,7 @@ BOOL spawn_viewer(UpdaterAppState *app_state)
 
 void show_usage_and_exit()
 {
-	std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
+	std::cout << "Usage: linux-updater <--url URL | --file FILE> --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
 		  << "[--image-dir PATH]"
 		  << std::endl;
 	exit(1);
@@ -728,6 +730,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
 		{
 			app_state->url = argv[i];
 		}
+		else if ((!strcmp(argv[i], "--file")) && (++i < argc))
+		{
+			app_state->file = argv[i];
+		}
 		else if ((!strcmp(argv[i], "--name")) && (++i < argc))
 		{
 			app_state->app_name = argv[i];
@@ -756,7 +762,7 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
 	}
 
 	if (app_state->app_name.empty() 
-	    || app_state->url.empty() 
+	    || (app_state->url.empty() && app_state->file.empty())  
 	    || app_state->dest_dir.empty())
 	{
 		show_usage_and_exit();
@@ -771,10 +777,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
 
 int main(int argc, char **argv)
 {
-	UpdaterAppState app_state;
+	UpdaterAppState* app_state = new UpdaterAppState;
 	GThread *worker_thread;
 
-	parse_args_and_init(argc, argv, &app_state);
+	parse_args_and_init(argc, argv, app_state);
 
 	// Initialize logger, and rename old log file
 	gDirUtilp->initAppDirs("SecondLife");
@@ -797,17 +803,29 @@ int main(int argc, char **argv)
 	gtk_init(&argc, &argv);
 
 	// create UI
-	updater_app_ui_init(&app_state);
+	updater_app_ui_init(app_state);
 
 	//llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;
 
 	// create download thread
 	worker_thread = g_thread_create
-		(GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL);
+		(GThreadFunc(worker_thread_cb), app_state, FALSE, NULL);
 
 	gdk_threads_enter();
 	gtk_main();
 	gdk_threads_leave();
 
-	return (app_state.failure == FALSE) ? 0 : 1;
+	// Delete the file only if created from url download.
+	if(!app_state->url.empty() && !app_state->file.empty())
+	{
+		if (gDirUtilp->fileExists(app_state->file))
+		{
+			LLFile::remove(app_state->file);
+		}
+	}
+
+	bool success = !app_state->failure;
+	delete app_state;
+	return success ? 0 : 1;
 }
+
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 478f2fedbd1912b8807e8730ffc645cb33ab2604..9342a22d46f9d0a3a0b0017544af3aa6a19383bd 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -93,6 +93,7 @@ set(llcommon_SOURCE_FILES
     llstringtable.cpp
     llsys.cpp
     llthread.cpp
+    llthreadsafequeue.cpp
     lltimer.cpp
     lluri.cpp
     lluuid.cpp
@@ -225,6 +226,7 @@ set(llcommon_HEADER_FILES
     llstringtable.h
     llsys.h
     llthread.h
+    llthreadsafequeue.h
     lltimer.h
     lltreeiterators.h
     lluri.h
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 289ce0bc2cfda4b52a63975e7d1905e47d03210b..8f02391e75a7dcbcf6f2d1b8734aca1550d7969f 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -318,7 +318,12 @@ void llofstream::close()
 	if(is_open())
 	{
 		if (_Filebuffer->close() == 0)
+		{
 			_Myios::setstate(ios_base::failbit);	/*Flawfinder: ignore*/
+		}
+		delete _Filebuffer;
+		_Filebuffer = NULL;
+		_ShouldClose = false;
 	}
 }
 
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index d7b7c3699ccdca91002c21d34bcd25fba57a28d0..148aaf8aed7cc31b5eea35f4deef0ab0d6668d4a 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -147,16 +147,20 @@ void LLThread::shutdown()
 		{
 			// This thread just wouldn't stop, even though we gave it time
 			llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+			// Put a stake in its heart.
+			apr_thread_exit(mAPRThreadp, -1);
 			return;
 		}
 		mAPRThreadp = NULL;
 	}
 
 	delete mRunCondition;
+	mRunCondition = 0;
 	
-	if (mIsLocalPool)
+	if (mIsLocalPool && mAPRPoolp)
 	{
 		apr_pool_destroy(mAPRPoolp);
+		mAPRPoolp = 0;
 	}
 }
 
diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a73e632a9ada2249b4353f88828d5d66fcfe00d
--- /dev/null
+++ b/indra/llcommon/llthreadsafequeue.cpp
@@ -0,0 +1,109 @@
+/** 
+ * @file llthread.cpp
+ *
+ * $LicenseInfo:firstyear=2004&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$
+ */
+
+#include "linden_common.h"
+#include <apr_pools.h>
+#include <apr_queue.h>
+#include "llthreadsafequeue.h"
+
+
+
+// LLThreadSafeQueueImplementation
+//-----------------------------------------------------------------------------
+
+
+LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity):
+	mOwnsPool(pool == 0),
+	mPool(pool),
+	mQueue(0)
+{
+	if(mOwnsPool) {
+		apr_status_t status = apr_pool_create(&mPool, 0);
+		if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool");
+	} else {
+		; // No op.
+	}
+	
+	apr_status_t status = apr_queue_create(&mQueue, capacity, mPool);
+	if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue");
+}
+
+
+LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation()
+{
+	if(mQueue != 0) {
+		if(apr_queue_size(mQueue) != 0) llwarns << 
+			"terminating queue which still contains " << apr_queue_size(mQueue) <<
+			" elements;" << "memory will be leaked" << LL_ENDL;
+		apr_queue_term(mQueue);
+	}
+	if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool);
+}
+
+
+void LLThreadSafeQueueImplementation::pushFront(void * element)
+{
+	apr_status_t status = apr_queue_push(mQueue, element);
+	
+	if(status == APR_EINTR) {
+		throw LLThreadSafeQueueInterrupt();
+	} else if(status != APR_SUCCESS) {
+		throw LLThreadSafeQueueError("push failed");
+	} else {
+		; // Success.
+	}
+}
+
+
+bool LLThreadSafeQueueImplementation::tryPushFront(void * element){
+	return apr_queue_trypush(mQueue, element) == APR_SUCCESS;
+}
+
+
+void * LLThreadSafeQueueImplementation::popBack(void)
+{
+	void * element;
+	apr_status_t status = apr_queue_pop(mQueue, &element);
+
+	if(status == APR_EINTR) {
+		throw LLThreadSafeQueueInterrupt();
+	} else if(status != APR_SUCCESS) {
+		throw LLThreadSafeQueueError("pop failed");
+	} else {
+		return element;
+	}
+}
+
+
+bool LLThreadSafeQueueImplementation::tryPopBack(void *& element)
+{
+	return apr_queue_trypop(mQueue, &element) == APR_SUCCESS;
+}
+
+
+size_t LLThreadSafeQueueImplementation::size()
+{
+	return apr_queue_size(mQueue);
+}
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
new file mode 100644
index 0000000000000000000000000000000000000000..58cac38769cd2d3801fc9f01b1d1cc2a010ae0c6
--- /dev/null
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -0,0 +1,205 @@
+/** 
+ * @file llthreadsafequeue.h
+ * @brief Base classes for thread, mutex and condition handling.
+ *
+ * $LicenseInfo:firstyear=2004&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_LLTHREADSAFEQUEUE_H
+#define LL_LLTHREADSAFEQUEUE_H
+
+
+#include <string>
+#include <stdexcept>
+
+
+struct apr_pool_t; // From apr_pools.h
+class LLThreadSafeQueueImplementation; // See below.
+
+
+//
+// A general queue exception.
+//
+class LL_COMMON_API LLThreadSafeQueueError:
+public std::runtime_error
+{
+public:
+	LLThreadSafeQueueError(std::string const & message):
+	std::runtime_error(message)
+	{
+		; // No op.
+	}
+};
+
+
+//
+// An exception raised when blocking operations are interrupted.
+//
+class LL_COMMON_API LLThreadSafeQueueInterrupt:
+	public LLThreadSafeQueueError
+{
+public:
+	LLThreadSafeQueueInterrupt(void):
+		LLThreadSafeQueueError("queue operation interrupted")
+	{
+		; // No op.
+	}
+};
+
+
+struct apr_queue_t; // From apr_queue.h
+
+
+//
+// Implementation details. 
+//
+class LL_COMMON_API LLThreadSafeQueueImplementation
+{
+public:
+	LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity);
+	~LLThreadSafeQueueImplementation();
+	void pushFront(void * element);
+	bool tryPushFront(void * element);
+	void * popBack(void);
+	bool tryPopBack(void *& element);
+	size_t size();
+	
+private:
+	bool mOwnsPool;
+	apr_pool_t * mPool;
+	apr_queue_t * mQueue;
+};
+
+
+//
+// Implements a thread safe FIFO.
+//
+template<typename ElementT>
+class LLThreadSafeQueue
+{
+public:
+	typedef ElementT value_type;
+	
+	// If the pool is set to NULL one will be allocated and managed by this
+	// queue.
+	LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024);
+	
+	// Add an element to the front of queue (will block if the queue has
+	// reached capacity).
+	//
+	// This call will raise an interrupt error if the queue is deleted while
+	// the caller is blocked.
+	void pushFront(ElementT const & element);
+	
+	// Try to add an element to the front ofqueue without blocking. Returns
+	// true only if the element was actually added.
+	bool tryPushFront(ElementT const & element);
+	
+	// Pop the element at the end of the queue (will block if the queue is
+	// empty).
+	//
+	// This call will raise an interrupt error if the queue is deleted while
+	// the caller is blocked.
+	ElementT popBack(void);
+	
+	// Pop an element from the end of the queue if there is one available.
+	// Returns true only if an element was popped.
+	bool tryPopBack(ElementT & element);
+	
+	// Returns the size of the queue.
+	size_t size();
+
+private:
+	LLThreadSafeQueueImplementation mImplementation;
+};
+
+
+
+// LLThreadSafeQueue
+//-----------------------------------------------------------------------------
+
+
+template<typename ElementT>
+LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity):
+	mImplementation(pool, capacity)
+{
+	; // No op.
+}
+
+
+template<typename ElementT>
+void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
+{
+	ElementT * elementCopy = new ElementT(element);
+	try {
+		mImplementation.pushFront(elementCopy);
+	} catch (LLThreadSafeQueueInterrupt) {
+		delete elementCopy;
+		throw;
+	}
+}
+
+
+template<typename ElementT>
+bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element)
+{
+	ElementT * elementCopy = new ElementT(element);
+	bool result = mImplementation.tryPushFront(elementCopy);
+	if(!result) delete elementCopy;
+	return result;
+}
+
+
+template<typename ElementT>
+ElementT LLThreadSafeQueue<ElementT>::popBack(void)
+{
+	ElementT * element = reinterpret_cast<ElementT *> (mImplementation.popBack());
+	ElementT result(*element);
+	delete element;
+	return result;
+}
+
+
+template<typename ElementT>
+bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element)
+{
+	void * storedElement;
+	bool result = mImplementation.tryPopBack(storedElement);
+	if(result) {
+		ElementT * elementPtr = reinterpret_cast<ElementT *>(storedElement); 
+		element = *elementPtr;
+		delete elementPtr;
+	} else {
+		; // No op.
+	}
+	return result;
+}
+
+
+template<typename ElementT>
+size_t LLThreadSafeQueue<ElementT>::size(void)
+{
+	return mImplementation.size();
+}
+
+
+#endif
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index ff9af21e545eb762ec7a33874532a2d4c7b590b0..19c42bf61a2e1de48ddd96c13dcea36e32b94dd5 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -1620,7 +1620,10 @@ void LLUI::initClass(const settings_map_t& settings,
 
 void LLUI::cleanupClass()
 {
-	sImageProvider->cleanUp();
+	if(sImageProvider)
+	{
+		sImageProvider->cleanUp();
+	}
 }
 
 void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup,  const clear_popups_t& clear_popups)
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index f9a39826f56203dab893ca78815704df497f2164..27c694dde97fef4294ccb2dee95b3b1a285ee7e3 100644
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -1107,7 +1107,7 @@ bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::strin
 		return sd.asBoolean();
 	else
 	{
-		CONTROL_ERRS << "Invalid BOOL value" << llendl;
+		CONTROL_ERRS << "Invalid BOOL value for " << control_name << ": " << sd << llendl;
 		return FALSE;
 	}
 }
@@ -1119,7 +1119,7 @@ S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string&
 		return sd.asInteger();
 	else
 	{
-		CONTROL_ERRS << "Invalid S32 value" << llendl;
+		CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << sd << llendl;
 		return 0;
 	}
 }
@@ -1131,7 +1131,7 @@ U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string&
 		return sd.asInteger();
 	else
 	{
-		CONTROL_ERRS << "Invalid U32 value" << llendl;
+		CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << sd << llendl;
 		return 0;
 	}
 }
@@ -1143,7 +1143,7 @@ F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string&
 		return (F32) sd.asReal();
 	else
 	{
-		CONTROL_ERRS << "Invalid F32 value" << llendl;
+		CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << sd << llendl;
 		return 0.0f;
 	}
 }
@@ -1155,7 +1155,7 @@ std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, co
 		return sd.asString();
 	else
 	{
-		CONTROL_ERRS << "Invalid string value" << llendl;
+		CONTROL_ERRS << "Invalid string value for " << control_name << ": " << sd << llendl;
 		return LLStringUtil::null;
 	}
 }
@@ -1173,7 +1173,7 @@ LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const
 		return (LLVector3)sd;
 	else
 	{
-		CONTROL_ERRS << "Invalid LLVector3 value" << llendl;
+		CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << sd << llendl;
 		return LLVector3::zero;
 	}
 }
@@ -1185,7 +1185,7 @@ LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, cons
 		return (LLVector3d)sd;
 	else
 	{
-		CONTROL_ERRS << "Invalid LLVector3d value" << llendl;
+		CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << sd << llendl;
 		return LLVector3d::zero;
 	}
 }
@@ -1197,7 +1197,7 @@ LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::s
 		return LLRect(sd);
 	else
 	{
-		CONTROL_ERRS << "Invalid rect value" << llendl;
+		CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << sd << llendl;
 		return LLRect::null;
 	}
 }
@@ -1211,19 +1211,19 @@ LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const st
 		LLColor4 color(sd);
 		if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f)
 		{
-			llwarns << "Color " << control_name << " value out of range " << llendl;
+			llwarns << "Color " << control_name << " red value out of range: " << color << llendl;
 		}
 		else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f)
 		{
-			llwarns << "Color " << control_name << " value out of range " << llendl;
+			llwarns << "Color " << control_name << " green value out of range: " << color << llendl;
 		}
 		else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f)
 		{
-			llwarns << "Color " << control_name << " value out of range " << llendl;
+			llwarns << "Color " << control_name << " blue value out of range: " << color << llendl;
 		}
 		else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f)
 		{
-			llwarns << "Color " << control_name << " value out of range " << llendl;
+			llwarns << "Color " << control_name << " alpha value out of range: " << color << llendl;
 		}
 
 		return LLColor4(sd);
@@ -1242,7 +1242,7 @@ LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const st
 		return sd;
 	else
 	{
-		CONTROL_ERRS << "Invalid LLColor3 value" << llendl;
+		CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << sd << llendl;
 		return LLColor3::white;
 	}
 }
diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp
index 23980ffac22a7954a0a6327369b71e5b2129ef38..5d19e8a8899b0eb3ca3bd36d558cf6ca5d5a7b4c 100644
--- a/indra/mac_updater/mac_updater.cpp
+++ b/indra/mac_updater/mac_updater.cpp
@@ -26,6 +26,9 @@
 
 #include "linden_common.h"
 
+#include <boost/format.hpp>
+
+#include <libgen.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -62,6 +65,8 @@ Boolean gCancelled = false;
 const char *gUpdateURL;
 const char *gProductName;
 const char *gBundleID;
+const char *gDmgFile;
+const char *gMarkerPath;
 
 void *updatethreadproc(void*);
 
@@ -334,6 +339,14 @@ int parse_args(int argc, char **argv)
 		{
 			gBundleID = argv[j];
 		}
+		else if ((!strcmp(argv[j], "-dmg")) && (++j < argc)) 
+		{
+			gDmgFile = argv[j];
+		}
+		else if ((!strcmp(argv[j], "-marker")) && (++j < argc)) 
+		{
+			gMarkerPath = argv[j];;
+		}
 	}
 
 	return 0;
@@ -361,10 +374,12 @@ int main(int argc, char **argv)
 	gUpdateURL  = NULL;
 	gProductName = NULL;
 	gBundleID = NULL;
+	gDmgFile = NULL;
+	gMarkerPath = NULL;
 	parse_args(argc, argv);
-	if (!gUpdateURL)
+	if ((gUpdateURL == NULL) && (gDmgFile == NULL))
 	{
-		llinfos << "Usage: mac_updater -url <url> [-name <product_name>] [-program <program_name>]" << llendl;
+		llinfos << "Usage: mac_updater -url <url> | -dmg <dmg file> [-name <product_name>] [-program <program_name>]" << llendl;
 		exit(1);
 	}
 	else
@@ -488,11 +503,18 @@ int main(int argc, char **argv)
 					NULL,
 					&retval_mac);
 		}
-
+		
+		if(gMarkerPath != 0)
+		{
+			// Create a install fail marker that can be used by the viewer to
+			// detect install problems.
+			std::ofstream stream(gMarkerPath);
+			if(stream) stream << -1;
+		}
+		exit(-1);
+	} else {
+		exit(0);
 	}
-	
-	// Don't dispose of things, just exit.  This keeps the update thread from potentially getting hosed.
-	exit(0);
 
 	if(gWindow != NULL)
 	{
@@ -700,17 +722,26 @@ static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app)
 						// Looks promising.  Check to see if it has the right bundle identifier.
 						if(isFSRefViewerBundle(&ref))
 						{
+							llinfos << name << " is the one" << llendl;
 							// This is the one.  Return it.
 							*app = ref;
 							found = true;
+							break;
+						} else {
+							llinfos << name << " is not the bundle we are looking for; move along" << llendl;
 						}
+
 					}
 				}
 			}
 		}
-		while(!err && !found);
+		while(!err);
+		
+		llinfos << "closing the iterator" << llendl;
 		
 		FSCloseIterator(iterator);
+		
+		llinfos << "closed" << llendl;
 	}
 	
 	if(!err && !found)
@@ -921,6 +952,22 @@ void *updatethreadproc(void*)
 
 #endif // 0 *HACK for DEV-11935
 		
+		// Skip downloading the file if the dmg was passed on the command line.
+		std::string dmgName;
+		if(gDmgFile != NULL) {
+			dmgName = basename((char *)gDmgFile);
+			char * dmgDir = dirname((char *)gDmgFile);
+			strncpy(tempDir, dmgDir, sizeof(tempDir));
+			err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
+			if(err != noErr) throw 0;
+			chdir(tempDir);
+			goto begin_install;
+		} else {
+			// Continue on to download file.
+			dmgName = "SecondLife.dmg";
+		}
+
+		
 		strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);
 		if(mkdtemp(temp) == NULL)
 		{
@@ -979,14 +1026,17 @@ void *updatethreadproc(void*)
 			fclose(downloadFile);
 			downloadFile = NULL;
 		}
-		
+
+	begin_install:
 		sendProgress(0, 0, CFSTR("Mounting image..."));
 		LLFile::mkdir("mnt", 0700);
 		
 		// NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
 		//		but if our cleanup fails, this makes it much harder for the user to unmount the image.
 		std::string mountOutput;
-		FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");		/* Flawfinder: ignore */
+		boost::format cmdFormat("hdiutil attach %s -mountpoint mnt");
+		cmdFormat % dmgName;
+		FILE* mounter = popen(cmdFormat.str().c_str(), "r");		/* Flawfinder: ignore */
 		
 		if(mounter == NULL)
 		{
@@ -1052,12 +1102,19 @@ void *updatethreadproc(void*)
 			throw 0;
 		}
 
+		sendProgress(0, 0, CFSTR("Searching for the app bundle..."));
 		err = findAppBundleOnDiskImage(&mountRef, &sourceRef);
 		if(err != noErr)
 		{
 			llinfos << "Couldn't find application bundle on mounted disk image." << llendl;
 			throw 0;
 		}
+		else
+		{
+			llinfos << "found the bundle." << llendl;
+		}
+
+		sendProgress(0, 0, CFSTR("Preparing to copy files..."));
 		
 		FSRef asideRef;
 		char aside[MAX_PATH];		/* Flawfinder: ignore */
@@ -1077,7 +1134,11 @@ void *updatethreadproc(void*)
 			// Move aside old version (into work directory)
 			err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
 			if(err != noErr)
+			{
+				llwarns << "failed to move aside old version (error code " << 
+					err << ")" << llendl;
 				throw 0;
+			}
 
 			// Grab the path for later use.
 			err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
@@ -1175,6 +1236,10 @@ void *updatethreadproc(void*)
 		llinfos << "Moving work directory to the trash." << llendl;
 
 		err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
+		if(err != noErr) {
+			llwarns << "failed to move files to trash, (error code " <<
+				err << ")" << llendl;
+		}
 
 //		snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
 //		printf("%s\n", temp);
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 5647418619fa9f5213af9691992836f00c85e664..fa49c1ac4c420e19dbf04a36c21b84f5b8be3c16 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -64,6 +64,7 @@ include_directories(
     ${LSCRIPT_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}/lscript_compile
     ${LLLOGIN_INCLUDE_DIRS}
+    ${UPDATER_INCLUDE_DIRS}
     )
 
 set(viewer_SOURCE_FILES
@@ -284,6 +285,7 @@ set(viewer_SOURCE_FILES
     llloginhandler.cpp
     lllogininstance.cpp
     llmachineid.cpp
+    llmainlooprepeater.cpp
     llmanip.cpp
     llmaniprotate.cpp
     llmanipscale.cpp
@@ -818,6 +820,7 @@ set(viewer_HEADER_FILES
     llloginhandler.h
     lllogininstance.h
     llmachineid.h
+    llmainlooprepeater.h
     llmanip.h
     llmaniprotate.h
     llmanipscale.h
@@ -1646,7 +1649,14 @@ if (WINDOWS)
     endif (PACKAGE)
 endif (WINDOWS)
 
+# *NOTE - this list is very sensitive to ordering, test carefully on all
+# platforms if you change the releative order of the entries here.
+# In particular, cmake 2.6.4 (when buidling with linux/makefile generators)
+# appears to sometimes de-duplicate redundantly listed dependencies improperly.
+# To work around this, higher level modules should be listed before the modules
+# that they depend upon. -brad
 target_link_libraries(${VIEWER_BINARY_NAME}
+    ${UPDATER_LIBRARIES}
     ${LLAUDIO_LIBRARIES}
     ${LLCHARACTER_LIBRARIES}
     ${LLIMAGE_LIBRARIES}
@@ -1833,10 +1843,18 @@ if (PACKAGE)
     set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
   endif (LINUX)
 
+  if(CMAKE_CFG_INTDIR STREQUAL ".")
+      set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE})
+  else(CMAKE_CFG_INTDIR STREQUAL ".")
+      # set LLBUILD_CONFIG to be a shell variable evaluated at build time
+      # reflecting the configuration we are currently building.
+      set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR})
+  endif(CMAKE_CFG_INTDIR STREQUAL ".")
   add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
     COMMAND "${PYTHON_EXECUTABLE}"
     ARGS
       "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
+      "${LLBUILD_CONFIG}"
       "${VIEWER_DIST_DIR}"
       "${VIEWER_EXE_GLOBS}"
       "${VIEWER_LIB_GLOB}"
@@ -1845,7 +1863,7 @@ if (PACKAGE)
     DEPENDS generate_breakpad_symbols.py
     VERBATIM
   )
-  add_custom_target(generate_breakpad_symbols ALL DEPENDS "${VIEWER_SYMBOL_FILE}")
+  add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}")
   add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")
   add_dependencies(package generate_breakpad_symbols)
 endif (PACKAGE)
@@ -1860,6 +1878,7 @@ if (LL_TESTS)
     llmediadataclient.cpp
     lllogininstance.cpp
     llviewerhelputil.cpp
+    llversioninfo.cpp
   )
 
   ##################################################
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 0562cf5480e901c8d090674e14f53aee5ab961ee..1b8393330d92dade02d5417fd6d5ec222b023537 100644
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -363,8 +363,7 @@
     <map>
       <key>count</key>
       <integer>1</integer>
-      <key>map-to</key>
-      <string>VersionChannelName</string>
+      <!-- Special case. Not mapped to a setting. -->
     </map>
 
     <key>loginpage</key>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e5845bc9474f6c07a64b65e22cc0f9999e94c0b0..0f946b0f0b2deb47e6be125f95056e300510c4ea 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11090,7 +11090,62 @@
       <key>Value</key>
       <integer>15</integer>
     </map>
-	<key>UploadBakedTexOld</key>
+    <key>UpdaterServiceActive</key>
+    <map>
+      <key>Comment</key>
+      <string>Enable or disable the updater service.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
+    <key>UpdaterServiceCheckPeriod</key>
+    <map>
+      <key>Comment</key>
+      <string>Default period between update checking.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>U32</string>
+      <key>Value</key>
+      <integer>3600</integer>
+    </map>
+    <key>UpdaterServiceURL</key>
+    <map>
+      <key>Comment</key>
+      <string>Default location for the updater service.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>http://update.secondlife.com</string>
+    </map>
+    <key>UpdaterServicePath</key>
+    <map>
+      <key>Comment</key>
+      <string>Path on the update server host.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>update</string>
+    </map>
+    <key>UpdaterServiceProtocolVersion</key>
+    <map>
+      <key>Comment</key>
+      <string>The update protocol version to use.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>v1.0</string>
+    </map>
+    <key>UploadBakedTexOld</key>
     <map>
       <key>Comment</key>
       <string>Forces the baked texture pipeline to upload using the old method.</string>
@@ -11443,17 +11498,6 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
-    <key>VersionChannelName</key>
-    <map>
-      <key>Comment</key>
-      <string>Versioning Channel Name.</string>
-      <key>Persist</key>
-      <integer>0</integer>
-      <key>Type</key>
-      <string>String</string>
-      <key>Value</key>
-      <string>Second Life Release</string>
-    </map>
     <key>VertexShaderEnable</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/generate_breakpad_symbols.py b/indra/newview/generate_breakpad_symbols.py
index 8f2dfd2348909bec0991d12992956d4c5bcd91c7..4fd04d780eb0a8b926797d7623d26b1b04ab611e 100644
--- a/indra/newview/generate_breakpad_symbols.py
+++ b/indra/newview/generate_breakpad_symbols.py
@@ -31,6 +31,7 @@
 import itertools
 import operator
 import os
+import re
 import sys
 import shlex
 import subprocess
@@ -45,8 +46,12 @@ def __init__(self, modules):
         Exception.__init__(self, "Failed to find required modules: %r" % modules)
         self.modules = modules
 
-def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file):
-    print "generate_breakpad_symbols run with args: %s" % str((viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file))
+def main(configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file):
+    print "generate_breakpad_symbols run with args: %s" % str((configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file))
+
+    if not re.match("release", configuration, re.IGNORECASE):
+        print "skipping breakpad symbol generation for non-release build."
+        return 0
 
     # split up list of viewer_exes
     # "'Second Life' SLPlugin" becomes ['Second Life', 'SLPlugin']
@@ -122,7 +127,7 @@ def match_module_basename(m):
     return 0
 
 if __name__ == "__main__":
-    if len(sys.argv) != 6:
+    if len(sys.argv) != 7:
         usage()
         sys.exit(1)
     sys.exit(main(*sys.argv[1:]))
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index d5712f80cfffe15745b26700903103c2d90778ce..4e8ed807eeef65541d288055ff4dbb948fd91b9b 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -85,6 +85,8 @@ AutoCloseWindow true					; after all files install, close window
 InstallDir "$PROGRAMFILES\${INSTNAME}"
 InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" ""
 DirText $(DirectoryChooseTitle) $(DirectoryChooseSetup)
+Page directory dirPre
+Page instfiles
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Variables
@@ -95,6 +97,8 @@ Var INSTFLAGS
 Var INSTSHORTCUT
 Var COMMANDLINE         ; command line passed to this installer, set in .onInit
 Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer
+Var SKIP_DIALOGS        ; set from command line in  .onInit. autoinstall 
+                        ; GUI and the defaults.
 
 ;;; Function definitions should go before file includes, because calls to
 ;;; DLLs like LangDLL trigger an implicit file include, so if that call is at
@@ -110,6 +114,9 @@ Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 Function .onInstSuccess
     Push $R0	# Option value, unused
+
+    StrCmp $SKIP_DIALOGS "true" label_launch 
+
     ${GetOptions} $COMMANDLINE "/AUTOSTART" $R0
     # If parameter was there (no error) just launch
     # Otherwise ask
@@ -128,6 +135,13 @@ label_no_launch:
 	Pop $R0
 FunctionEnd
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Pre-directory page callback
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Function dirPre
+    StrCmp $SKIP_DIALOGS "true" 0 +2
+	Abort
+FunctionEnd    
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Make sure we're not on Windows 98 / ME
@@ -145,7 +159,8 @@ Function CheckWindowsVersion
 	StrCmp $R0 "NT" win_ver_bad
 	Return
 win_ver_bad:
-	MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort
+	StrCmp $SKIP_DIALOGS "true" +2 ; If skip_dialogs is set just install
+            MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort
 	Return
 win_ver_abort:
 	Quit
@@ -184,13 +199,13 @@ FunctionEnd
 ; If it has, allow user to bail out of install process.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 Function CheckIfAlreadyCurrent
-  Push $0
-	ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version"
-    StrCmp $0 ${VERSION_LONG} 0 DONE
-	MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK DONE
+    Push $0
+    ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version"
+    StrCmp $0 ${VERSION_LONG} 0 continue_install
+    StrCmp $SKIP_DIALOGS "true" continue_install
+    MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK continue_install
     Quit
-
-  DONE:
+continue_install:
     Pop $0
     Return
 FunctionEnd
@@ -203,7 +218,9 @@ Function CloseSecondLife
   Push $0
   FindWindow $0 "Second Life" ""
   IntCmp $0 0 DONE
-  MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL
+  
+  StrCmp $SKIP_DIALOGS "true" CLOSE
+    MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL
 
   CANCEL_INSTALL:
     Quit
@@ -659,23 +676,29 @@ FunctionEnd
 Function .onInit
     Push $0
     ${GetParameters} $COMMANDLINE              ; get our command line
+
+    ${GetOptions} $COMMANDLINE "/SKIP_DIALOGS" $0   
+    IfErrors +2 0 ; If error jump past setting SKIP_DIALOGS
+        StrCpy $SKIP_DIALOGS "true"
+
     ${GetOptions} $COMMANDLINE "/LANGID=" $0   ; /LANGID=1033 implies US English
     ; If no language (error), then proceed
-    IfErrors lbl_check_silent
+    IfErrors lbl_configure_default_lang
     ; No error means we got a language, so use it
     StrCpy $LANGUAGE $0
     Goto lbl_return
 
-lbl_check_silent:
-    ; For silent installs, no language prompt, use default
-    IfSilent lbl_return
-    
-	; If we currently have a version of SL installed, default to the language of that install
+lbl_configure_default_lang:
+    ; If we currently have a version of SL installed, default to the language of that install
     ; Otherwise don't change $LANGUAGE and it will default to the OS UI language.
-	ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage"
-    IfErrors lbl_build_menu
+    ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage"
+    IfErrors +2 0 ; If error skip the copy instruction 
 	StrCpy $LANGUAGE $0
 
+    ; For silent installs, no language prompt, use default
+    IfSilent lbl_return
+    StrCmp $SKIP_DIALOGS "true" lbl_return
+  
 lbl_build_menu:
 	Push ""
     # Use separate file so labels can be UTF-16 but we can still merge changes
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f66663891da2eca6d0bd8450f1b994a022f742d6..b6f52e3e15c2dc146e5b4fd03cd746840e4f3cd6 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -83,6 +83,7 @@
 
 #include "llweb.h"
 #include "llsecondlifeurls.h"
+#include "llupdaterservice.h"
 
 // Linden library includes
 #include "llavatarnamecache.h"
@@ -196,6 +197,8 @@
 #include "llsecapi.h"
 #include "llmachineid.h"
 
+#include "llmainlooprepeater.h"
+
 // *FIX: These extern globals should be cleaned up.
 // The globals either represent state/config/resource-storage of either 
 // this app, or another 'component' of the viewer. App globals should be 
@@ -576,7 +579,8 @@ LLAppViewer::LLAppViewer() :
 	mAgentRegionLastAlive(false),
 	mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", FALSE)),
 	mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)),
-	mFastTimerLogThread(NULL)
+	mFastTimerLogThread(NULL),
+	mUpdater(new LLUpdaterService())
 {
 	if(NULL != sInstance)
 	{
@@ -652,10 +656,13 @@ bool LLAppViewer::init()
     initThreads();
     writeSystemInfo();
 
-	// Build a string representing the current version number.
-    gCurrentVersion = llformat("%s %s", 
-							   gSavedSettings.getString("VersionChannelName").c_str(),
-							   LLVersionInfo::getVersion().c_str());
+	// Initialize updater service (now that we have an io pump)
+	initUpdater();
+	if(isQuitting())
+	{
+		// Early out here because updater set the quitting flag.
+		return true;
+	}
 
 	//////////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////////
@@ -799,6 +806,9 @@ bool LLAppViewer::init()
 		return 1;
 	}
 	
+	// Initialize the repeater service.
+	LLMainLoopRepeater::instance().start();
+	
 	//
 	// Initialize the window
 	//
@@ -895,7 +905,8 @@ bool LLAppViewer::init()
 	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();
 
 	// Save the current version to the prefs file
-	gSavedSettings.setString("LastRunVersion", gCurrentVersion);
+	gSavedSettings.setString("LastRunVersion", 
+							 LLVersionInfo::getVersionAndChannel());
 
 	gSimLastTime = gRenderStartTime.getElapsedTimeF32();
 	gSimFrames = (F32)gFrameCount;
@@ -970,7 +981,7 @@ bool LLAppViewer::mainLoop()
 	gServicePump = new LLPumpIO(gAPRPoolp);
 	LLHTTPClient::setPump(*gServicePump);
 	LLCurl::setCAFile(gDirUtilp->getCAFile());
-
+	
 	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
 
 	LLVoiceChannel::initClass();
@@ -1353,11 +1364,14 @@ bool LLAppViewer::cleanup()
 	llinfos << "Cleaning Up" << llendflush;
 
 	// Must clean up texture references before viewer window is destroyed.
-	LLHUDManager::getInstance()->updateEffects();
-	LLHUDObject::updateAll();
-	LLHUDManager::getInstance()->cleanupEffects();
-	LLHUDObject::cleanupHUDObjects();
-	llinfos << "HUD Objects cleaned up" << llendflush;
+	if(LLHUDManager::instanceExists())
+	{
+		LLHUDManager::getInstance()->updateEffects();
+		LLHUDObject::updateAll();
+		LLHUDManager::getInstance()->cleanupEffects();
+		LLHUDObject::cleanupHUDObjects();
+		llinfos << "HUD Objects cleaned up" << llendflush;
+	}
 
 	LLKeyframeDataCache::clear();
 	
@@ -1369,8 +1383,10 @@ bool LLAppViewer::cleanup()
 	// Note: this is where gWorldMap used to be deleted.
 
 	// Note: this is where gHUDManager used to be deleted.
-	LLHUDManager::getInstance()->shutdownClass();
-	
+	if(LLHUDManager::instanceExists())
+	{
+		LLHUDManager::getInstance()->shutdownClass();
+	}
 
 	delete gAssetStorage;
 	gAssetStorage = NULL;
@@ -1666,7 +1682,10 @@ bool LLAppViewer::cleanup()
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
 	llinfos << "Auditing VFS" << llendl;
-	gVFS->audit();
+	if(gVFS)
+	{
+		gVFS->audit();
+	}
 #endif
 
 	llinfos << "Misc Cleanup" << llendflush;
@@ -1707,6 +1726,8 @@ bool LLAppViewer::cleanup()
 		llinfos << "File launched." << llendflush;
 	}
 
+	LLMainLoopRepeater::instance().stop();
+
 	ll_close_fail_log();
 
     llinfos << "Goodbye!" << llendflush;
@@ -1953,8 +1974,6 @@ bool LLAppViewer::initConfiguration()
 	gSavedSettings.setString("ClientSettingsFile", 
         gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
 
-	gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel());
-
 #ifndef	LL_RELEASE_FOR_DOWNLOAD
 	// provide developer build only overrides for these control variables that are not
 	// persisted to settings.xml
@@ -2095,6 +2114,11 @@ bool LLAppViewer::initConfiguration()
         }
     }
 
+    if(clp.hasOption("channel"))
+    {
+		LLVersionInfo::resetChannel(clp.getOption("channel")[0]);
+	}
+	
 
 	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time
 	if(clp.hasOption("crashonstartup"))
@@ -2364,6 +2388,58 @@ bool LLAppViewer::initConfiguration()
 	return true; // Config was successful.
 }
 
+namespace {
+    // *TODO - decide if there's a better place for this function.
+    // do we need a file llupdaterui.cpp or something? -brad
+    bool notify_update(LLSD const & evt)
+    {
+		switch (evt["type"].asInteger())
+		{
+			case LLUpdaterService::DOWNLOAD_COMPLETE:
+				LLNotificationsUtil::add("DownloadBackground");
+				break;
+			case LLUpdaterService::INSTALL_ERROR:
+				LLNotificationsUtil::add("FailedUpdateInstall");
+				break;
+			default:
+				llinfos << "unhandled update event " << evt << llendl;
+				break;
+		}
+
+		// let others also handle this event by default
+        return false;
+    }
+};
+
+void LLAppViewer::initUpdater()
+{
+	// Initialize the updater service.
+	// Generate URL to the udpater service
+	// Get Channel
+	// Get Version
+	std::string url = gSavedSettings.getString("UpdaterServiceURL");
+	std::string channel = LLVersionInfo::getChannel();
+	std::string version = LLVersionInfo::getVersion();
+	std::string protocol_version = gSavedSettings.getString("UpdaterServiceProtocolVersion");
+	std::string service_path = gSavedSettings.getString("UpdaterServicePath");
+	U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod");
+
+	mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this));
+	mUpdater->initialize(protocol_version, 
+						 url, 
+						 service_path, 
+						 channel, 
+						 version);
+ 	mUpdater->setCheckPeriod(check_period);
+	if(gSavedSettings.getBOOL("UpdaterServiceActive"))
+	{
+		bool install_if_ready = true;
+		mUpdater->startChecking(install_if_ready);
+	}
+
+    LLEventPump & updater_pump = LLEventPumps::instance().obtain(LLUpdaterService::pumpName());
+    updater_pump.listen("notify_update", &notify_update);
+}
 
 void LLAppViewer::checkForCrash(void)
 {
@@ -2524,15 +2600,18 @@ void LLAppViewer::cleanupSavedSettings()
 
 	// save window position if not maximized
 	// as we don't track it in callbacks
-	BOOL maximized = gViewerWindow->mWindow->getMaximized();
-	if (!maximized)
+	if(NULL != gViewerWindow)
 	{
-		LLCoordScreen window_pos;
-
-		if (gViewerWindow->mWindow->getPosition(&window_pos))
+		BOOL maximized = gViewerWindow->mWindow->getMaximized();
+		if (!maximized)
 		{
-			gSavedSettings.setS32("WindowX", window_pos.mX);
-			gSavedSettings.setS32("WindowY", window_pos.mY);
+			LLCoordScreen window_pos;
+
+			if (gViewerWindow->mWindow->getPosition(&window_pos))
+			{
+				gSavedSettings.setS32("WindowX", window_pos.mX);
+				gSavedSettings.setS32("WindowY", window_pos.mY);
+			}
 		}
 	}
 
@@ -2555,7 +2634,7 @@ void LLAppViewer::writeSystemInfo()
 {
 	gDebugInfo["SLLog"] = LLError::logFileName();
 
-	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName");
+	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();
 	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();
 	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor();
 	gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch();
@@ -2658,7 +2737,7 @@ void LLAppViewer::handleViewerCrash()
 	
 	//We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version
 	//to check against no matter what
-	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName");
+	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();
 
 	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();
 	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor();
@@ -4271,7 +4350,10 @@ void LLAppViewer::disconnectViewer()
 
 	// This is where we used to call gObjectList.destroy() and then delete gWorldp.
 	// Now we just ask the LLWorld singleton to cleanly shut down.
-	LLWorld::getInstance()->destroyClass();
+	if(LLWorld::instanceExists())
+	{
+		LLWorld::getInstance()->destroyClass();
+	}
 
 	// call all self-registered classes
 	LLDestroyClassList::instance().fireCallbacks();
@@ -4385,7 +4467,7 @@ void LLAppViewer::handleLoginComplete()
 	initMainloopTimeout("Mainloop Init");
 
 	// Store some data to DebugInfo in case of a freeze.
-	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName");
+	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();
 
 	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();
 	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor();
@@ -4491,7 +4573,7 @@ void LLAppViewer::launchUpdater()
 	// *TODO change userserver to be grid on both viewer and sim, since
 	// userserver no longer exists.
 	query_map["userserver"] = LLGridManager::getInstance()->getGridLabel();
-	query_map["channel"] = gSavedSettings.getString("VersionChannelName");
+	query_map["channel"] = LLVersionInfo::getChannel();
 	// *TODO constantize this guy
 	// *NOTE: This URL is also used in win_setup/lldownloader.cpp
 	LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map);
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index a14ab4362f104922abe0602321ba7211c83b4765..aa4256a2bde478debe60e235d99fb1536dc02cdf 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -39,7 +39,7 @@ class LLTextureCache;
 class LLImageDecodeThread;
 class LLTextureFetch;
 class LLWatchdogTimeout;
-class LLCommandLineParser;
+class LLUpdaterService;
 
 struct apr_dso_handle_t;
 
@@ -186,7 +186,7 @@ class LLAppViewer : public LLApp
 
 	bool initThreads(); // Initialize viewer threads, return false on failure.
 	bool initConfiguration(); // Initialize settings from the command line/config file.
-
+	void initUpdater(); // Initialize the updater service.
 	bool initCache(); // Initialize local client cache.
 
 
@@ -264,7 +264,14 @@ class LLAppViewer : public LLApp
 
 	U32 mAvailPhysicalMemInKB ;
 	U32 mAvailVirtualMemInKB ;
+	
+	boost::scoped_ptr<LLUpdaterService> mUpdater;
+
+	//---------------------------------------------
+	//*NOTE: Mani - legacy updater stuff
+	// Still useable?
 public:
+
 	//some information for updater
 	typedef struct
 	{
@@ -274,6 +281,7 @@ class LLAppViewer : public LLApp
 	static LLUpdaterInfo *sUpdaterInfo ;
 
 	void launchUpdater();
+	//---------------------------------------------
 };
 
 // consts from viewer.h
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 8f385160e9e50c084a03a3f5b62515a4117a468c..885d5535247e86c3de6449e2d06fd110bd85b21e 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -1092,9 +1092,11 @@ LLChicletPanel::LLChicletPanel(const Params&p)
 
 LLChicletPanel::~LLChicletPanel()
 {
-	LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton);
-	LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton);
-
+	if(LLTransientFloaterMgr::instanceExists())
+	{
+		LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton);
+		LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton);
+	}
 }
 
 void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){
diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp
index 2b92b228b39c2274981a3bae9df64905fb92abf8..b4a1457f47f9d302982bbfecc489ebb9f8cd0b35 100644
--- a/indra/newview/llcurrencyuimanager.cpp
+++ b/indra/newview/llcurrencyuimanager.cpp
@@ -166,7 +166,7 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
 		gAgent.getSecureSessionID().asString());
 	keywordArgs.appendString("language", LLUI::getLanguage());
 	keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
-	keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName"));
+	keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel());
 	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor());
 	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor());
 	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch());
@@ -241,7 +241,7 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
 	{
 		keywordArgs.appendString("password", password);
 	}
-	keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName"));
+	keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel());
 	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor());
 	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor());
 	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch());
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
index 135137069c5a4735a5b39cf0dc1ca803e475352c..8ae3ccbae3146d824c83a63b492c06b46ebf1060 100644
--- a/indra/newview/llfloaterabout.cpp
+++ b/indra/newview/llfloaterabout.cpp
@@ -213,7 +213,7 @@ LLSD LLFloaterAbout::getInfo()
 	info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion();
 	info["BUILD_DATE"] = __DATE__;
 	info["BUILD_TIME"] = __TIME__;
-	info["CHANNEL"] = gSavedSettings.getString("VersionChannelName");
+	info["CHANNEL"] = LLVersionInfo::getChannel();
 
 	info["VIEWER_RELEASE_NOTES_URL"] = get_viewer_release_notes_url();
 
@@ -291,7 +291,7 @@ static std::string get_viewer_release_notes_url()
 	std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL");
 	if (! LLStringUtil::endsWith(url, "/"))
 		url += "/";
-	url += gSavedSettings.getString("VersionChannelName") + "/";
+	url += LLVersionInfo::getChannel() + "/";
 	url += LLVersionInfo::getShortVersion();
 	return LLWeb::escapeURL(url);
 }
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 029e700c4ccd7678ad8a1df48b9393ccf48cd0da..fe84aca1471e3d52e6efe7536c2f078683fc142f 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -42,6 +42,7 @@
 // newview
 #include "llviewernetwork.h"
 #include "llviewercontrol.h"
+#include "llversioninfo.h"
 #include "llslurl.h"
 #include "llstartup.h"
 #include "llfloaterreg.h"
@@ -181,8 +182,8 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia
 	request_params["read_critical"] = false; // handleTOSResponse
 	request_params["last_exec_event"] = mLastExecEvent;
 	request_params["mac"] = hashed_unique_id_string;
-	request_params["version"] = gCurrentVersion; // Includes channel name
-	request_params["channel"] = gSavedSettings.getString("VersionChannelName");
+	request_params["version"] = LLVersionInfo::getVersionAndChannel(); // Includes channel name
+	request_params["channel"] = LLVersionInfo::getChannel();
 	request_params["id0"] = mSerialNumber;
 
 	mRequestData.clear();
diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c020e6d982eb7060b857a800f36b4d050f87f25
--- /dev/null
+++ b/indra/newview/llmainlooprepeater.cpp
@@ -0,0 +1,88 @@
+/** 
+ * @file llmachineid.cpp
+ * @brief retrieves unique machine ids
+ *
+ * $LicenseInfo:firstyear=2009&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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llapr.h"
+#include "llevents.h"
+#include "llmainlooprepeater.h"
+
+
+
+// LLMainLoopRepeater
+//-----------------------------------------------------------------------------
+
+
+LLMainLoopRepeater::LLMainLoopRepeater(void):
+	mQueue(0)
+{
+	; // No op.
+}
+
+
+void LLMainLoopRepeater::start(void)
+{
+	if(mQueue != 0) return;
+
+	mQueue = new LLThreadSafeQueue<LLSD>(gAPRPoolp, 1024);
+	mMainLoopConnection = LLEventPumps::instance().
+		obtain("mainloop").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMainLoop, this, _1));
+	mRepeaterConnection = LLEventPumps::instance().
+		obtain("mainlooprepeater").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMessage, this, _1));
+}
+
+
+void LLMainLoopRepeater::stop(void)
+{
+	mMainLoopConnection.release();
+	mRepeaterConnection.release();
+
+	delete mQueue;
+	mQueue = 0;
+}
+
+
+bool LLMainLoopRepeater::onMainLoop(LLSD const &)
+{
+	LLSD message;
+	while(mQueue->tryPopBack(message)) {
+		std::string pump = message["pump"].asString();
+		if(pump.length() == 0 ) continue; // No pump.
+		LLEventPumps::instance().obtain(pump).post(message["payload"]);
+	}
+	return false;
+}
+
+
+bool LLMainLoopRepeater::onMessage(LLSD const & event)
+{
+	try {
+		mQueue->pushFront(event);
+	} catch(LLThreadSafeQueueError & e) {
+		llwarns << "could not repeat message (" << e.what() << ")" << 
+			event.asString() << LL_ENDL;
+	}
+	return false;
+}
diff --git a/indra/newview/llmainlooprepeater.h b/indra/newview/llmainlooprepeater.h
new file mode 100644
index 0000000000000000000000000000000000000000..f84c0ca94c2d1b4c812533d655e689a357c275a8
--- /dev/null
+++ b/indra/newview/llmainlooprepeater.h
@@ -0,0 +1,65 @@
+/** 
+ * @file llmainlooprepeater.h
+ * @brief a service for repeating messages on the main loop.
+ *
+ * $LicenseInfo:firstyear=2010&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_LLMAINLOOPREPEATER_H
+#define LL_LLMAINLOOPREPEATER_H
+
+
+#include "llsd.h"
+#include "llthreadsafequeue.h"
+
+
+//
+// A service which creates the pump 'mainlooprepeater' to which any thread can
+// post a message that will be re-posted on the main loop.
+//
+// The posted message should contain two map elements: pump and payload.  The
+// pump value is a string naming the pump to which the message should be
+// re-posted.  The payload value is what will be posted to the designated pump.
+//
+class LLMainLoopRepeater:
+	public LLSingleton<LLMainLoopRepeater>
+{
+public:
+	LLMainLoopRepeater(void);
+	
+	// Start the repeater service.
+	void start(void);
+	
+	// Stop the repeater service.
+	void stop(void);
+	
+private:
+	LLTempBoundListener mMainLoopConnection;
+	LLTempBoundListener mRepeaterConnection;
+	LLThreadSafeQueue<LLSD> * mQueue;
+	
+	bool onMainLoop(LLSD const &);
+	bool onMessage(LLSD const & event);
+};
+
+
+#endif
diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp
index 6658e1d7e89b6eefeae012e711ad50ed616a4b6d..d38bb5aa4a45a240d006d7cb6c4229c16649a5aa 100644
--- a/indra/newview/llmoveview.cpp
+++ b/indra/newview/llmoveview.cpp
@@ -552,7 +552,7 @@ LLPanelStandStopFlying::LLPanelStandStopFlying() :
 }
 
 // static
-inline LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance()
+LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance()
 {
 	static LLPanelStandStopFlying* panel = getStandStopFlyingPanel();
 	return panel;
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 467aefc60f44edae403d945663775025d1aa2e69..cf567fb208f89d97899cbc195fb4ef9d10b2c285 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -230,7 +230,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 
 	getChild<LLPanel>("login")->setDefaultBtn("connect_btn");
 
-	std::string channel = gSavedSettings.getString("VersionChannelName");
+	std::string channel = LLVersionInfo::getChannel();
 	std::string version = llformat("%s (%d)",
 								   LLVersionInfo::getShortVersion().c_str(),
 								   LLVersionInfo::getBuild());
@@ -817,7 +817,7 @@ void LLPanelLogin::loadLoginPage()
 								   LLVersionInfo::getShortVersion().c_str(),
 								   LLVersionInfo::getBuild());
 
-	char* curl_channel = curl_escape(gSavedSettings.getString("VersionChannelName").c_str(), 0);
+	char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0);
 	char* curl_version = curl_escape(version.c_str(), 0);
 
 	oStr << "&channel=" << curl_channel;
diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp
index 3dce66f394d49fef75b17a0e77188d710c656052..c76ecae4a2b513d613ae616999d6f489269a9ddc 100644
--- a/indra/newview/llspeakbutton.cpp
+++ b/indra/newview/llspeakbutton.cpp
@@ -134,8 +134,11 @@ LLSpeakButton::LLSpeakButton(const Params& p)
 
 LLSpeakButton::~LLSpeakButton()
 {
-	LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn);
-	LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn);
+	if(LLTransientFloaterMgr::instanceExists())
+	{
+		LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn);
+		LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn);
+	}
 }
 
 void LLSpeakButton::setSpeakToolTip(const std::string& msg)
diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
index ede1d6bebee7c02795af170fd95b01dffd54f34c..9b38bf22ffe03ec7cea5ca373633c7f807ecb5f2 100644
--- a/indra/newview/llspeakingindicatormanager.cpp
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -308,7 +308,10 @@ void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker
 
 void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
 {
-	SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator);
+	if(SpeakingIndicatorManager::instanceExists())
+	{
+		SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator);
+	}
 }
 
 // EOF
diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp
index 78dd602f39bbde39b9bdd3f514882fedde3d20fe..6deab96b454602c1a0dccb6431c7c6f91a5f96ad 100644
--- a/indra/newview/lltransientfloatermgr.cpp
+++ b/indra/newview/lltransientfloatermgr.cpp
@@ -36,8 +36,11 @@
 
 LLTransientFloaterMgr::LLTransientFloaterMgr()
 {
-	gViewerWindow->getRootView()->addMouseDownCallback(boost::bind(
+	if(gViewerWindow)
+	{
+		gViewerWindow->getRootView()->addMouseDownCallback(boost::bind(
 			&LLTransientFloaterMgr::leftMouseClickCallback, this, _1, _2, _3));
+	}
 
 	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(GLOBAL, std::set<LLView*>()));
 	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(DOCKED, std::set<LLView*>()));
diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp
index 050e34ade9ab137afb56fc864a8e437591998e7c..8ccfdb071b663a0c5d73955c0c3dafef5f13976b 100644
--- a/indra/newview/lltranslate.cpp
+++ b/indra/newview/lltranslate.cpp
@@ -36,7 +36,7 @@
 
 #include "llbufferstream.h"
 #include "llui.h"
-#include "llversionviewer.h"
+#include "llversioninfo.h"
 #include "llviewercontrol.h"
 
 #include "jsoncpp/reader.h"
@@ -64,11 +64,11 @@ void LLTranslate::translateMessage(LLHTTPClient::ResponderPtr &result, const std
 	getTranslateUrl(url, from_lang, to_lang, mesg);
 
     std::string user_agent = llformat("%s %d.%d.%d (%d)",
-		LL_CHANNEL,
-		LL_VERSION_MAJOR,
-		LL_VERSION_MINOR,
-		LL_VERSION_PATCH,
-		LL_VERSION_BUILD );
+		LLVersionInfo::getChannel().c_str(),
+		LLVersionInfo::getMajor(),
+		LLVersionInfo::getMinor(),
+		LLVersionInfo::getPatch(),
+		LLVersionInfo::getBuild());
 
 	if (!m_Header.size())
 	{
diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp
index 733d05834a13d89d34462cbc2c3c3023a78f8481..53994c68f2ca86b90877a4bf687b69d99572f322 100644
--- a/indra/newview/llversioninfo.cpp
+++ b/indra/newview/llversioninfo.cpp
@@ -95,9 +95,42 @@ const std::string &LLVersionInfo::getShortVersion()
 	return version;
 }
 
+namespace
+{
+	/// Storage of the channel name the viewer is using.
+	//  The channel name is set by hardcoded constant, 
+	//  or by calling LLVersionInfo::resetChannel()
+	std::string sWorkingChannelName(LL_CHANNEL);
+
+	// Storage for the "version and channel" string.
+	// This will get reset too.
+	std::string sVersionChannel("");
+}
+
+//static
+const std::string &LLVersionInfo::getVersionAndChannel()
+{
+	if (sVersionChannel.empty())
+	{
+		// cache the version string
+		std::ostringstream stream;
+		stream << LLVersionInfo::getVersion() 
+			   << " "
+			   << LLVersionInfo::getChannel();
+		sVersionChannel = stream.str();
+	}
+
+	return sVersionChannel;
+}
+
 //static
 const std::string &LLVersionInfo::getChannel()
 {
-	static std::string name(LL_CHANNEL);
-	return name;
+	return sWorkingChannelName;
+}
+
+void LLVersionInfo::resetChannel(const std::string& channel)
+{
+	sWorkingChannelName = channel;
+	sVersionChannel.clear(); // Reset version and channel string til next use.
 }
diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h
index e468b6ae4ec9707068adff45176018af16edfc12..36defbcd68f467e4257ef059f0e04137971111ac 100644
--- a/indra/newview/llversioninfo.h
+++ b/indra/newview/llversioninfo.h
@@ -58,8 +58,15 @@ class LLVersionInfo
 	/// return the viewer version as a string like "2.0.0"
 	static const std::string &getShortVersion();
 
+	/// return the viewer version and channel as a string
+	/// like "2.0.0.200030 Second Life Release"
+	static const std::string &getVersionAndChannel();
+
 	/// return the channel name, e.g. "Second Life"
 	static const std::string &getChannel();
+	
+	/// reset the channel name used by the viewer.
+	static void resetChannel(const std::string& channel);
 };
 
 #endif
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 117e49d67faa1a136156ef3c313c8b67996e246c..622d09c60015b0639258bf9a96b05de66c173668 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -70,6 +70,7 @@
 #include "llpaneloutfitsinventory.h"
 #include "llpanellogin.h"
 #include "llpaneltopinfobar.h"
+#include "llupdaterservice.h"
 
 #ifdef TOGGLE_HACKED_GODLIKE_VIEWER
 BOOL 				gHackGodmode = FALSE;
@@ -82,7 +83,6 @@ LLControlGroup gCrashSettings("CrashSettings");	// saved at end of session
 LLControlGroup gWarningSettings("Warnings"); // persists ignored dialogs/warnings
 
 std::string gLastRunVersion;
-std::string gCurrentVersion;
 
 extern BOOL gResizeScreenTexture;
 extern BOOL gDebugGL;
@@ -502,6 +502,18 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)
 	return true;
 }
 
+void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value)
+{
+    if(new_value.asBoolean())
+    {
+        LLUpdaterService().startChecking();
+    }
+    else
+    {
+        LLUpdaterService().stopChecking();
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 void settings_setup_listeners()
@@ -649,6 +661,7 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2));
 	gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2));
 	gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2));
+	gSavedSettings.getControl("UpdaterServiceActive")->getSignal()->connect(&toggle_updater_service_active);
 	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
 	gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
 }
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
index 22b48f8906b1ec26ebb2e88bea9ff134b2d767f0..d7191f5c8d5d8703ec7d76b80fdb8ab9a2c49259 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -57,7 +57,5 @@ extern LLControlGroup gCrashSettings;
 
 // Set after settings loaded
 extern std::string gLastRunVersion;
-extern std::string gCurrentVersion;
-
 
 #endif // LL_LLVIEWERCONTROL_H
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 31cf0acdd761b88225f2c1df53494f05f43f8d73..fae4eb3c056029e2ea5085f299dbb7e8a14a9e1e 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -492,7 +492,7 @@ std::string LLViewerMedia::getCurrentUserAgent()
 
 	// Just in case we need to check browser differences in A/B test
 	// builds.
-	std::string channel = gSavedSettings.getString("VersionChannelName");
+	std::string channel = LLVersionInfo::getChannel();
 
 	// append our magic version number string to the browser user agent id
 	// See the HTTP 1.0 and 1.1 specifications for allowed formats:
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 46c78e2bb4210c87a55bc605cfd6b9e2b61e467d..402f00c5e73b3d310911f707a3dceb6e53edc79b 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -48,6 +48,7 @@
 #include "llagent.h"
 #include "llagentcamera.h"
 #include "llviewercontrol.h"
+#include "llversioninfo.h"
 #include "llfloatertools.h"
 #include "lldebugview.h"
 #include "llfasttimerview.h"
@@ -749,7 +750,7 @@ void send_stats()
 
 	// send fps only for time app spends in foreground
 	agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32();
-	agent["version"] = gCurrentVersion;
+	agent["version"] = LLVersionInfo::getVersionAndChannel();
 	std::string language = LLUI::getLanguage();
 	agent["language"] = language;
 	
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 1247e4724e443b141f81208a429f9ce6060b3f73..9536bf2cf70c8db5fa98f796dcc9ec84d6fa56cf 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -2869,6 +2869,25 @@ Download to your Applications folder?
      yestext="Download"/>
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="FailedUpdateInstall"
+   type="alertmodal">
+An error occurred installing the viewer update.
+Please download and install the latest viewer from
+http://secondlife.com/download.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+  <notification
+   icon="notifytip.tga"
+   name="DownloadBackground"
+   type="notifytip">
+An updated version of [APP_NAME] has been downloaded.
+It will be applied the next time you restart [APP_NAME]
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="DeedObjectToGroup"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
index 14aa38c5d36f03dc27e29fe8870d3a3227a72b51..584bd1ea9d88430cd70301cf557b9570287739c0 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
@@ -341,4 +341,20 @@
    name="web_proxy_port"
    top_delta="0"
    width="145" />
+
+  <check_box
+    top_delta="2"
+    enabled="true"
+    follows="left|top"
+    height="18"
+    initial_value="true"
+    control_name="UpdaterServiceActive"
+    label="Automatically download and install [APP_NAME] updates"
+    left="30"
+    mouse_opaque="true"
+    name="updater_service_active"
+    radio_style="false"
+    width="400"
+    top_pad="10"/>
+
 </panel>
diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp
index db50b896204988faff21ca5d606e97d086bcc715..b902c7ab09a0cb211efd432a321b09b7b885849a 100644
--- a/indra/newview/tests/lllogininstance_test.cpp
+++ b/indra/newview/tests/lllogininstance_test.cpp
@@ -48,6 +48,9 @@ const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid");
 
 const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno");
 
+const std::string VIEWERLOGIN_CHANNEL("invalid_channel");
+const std::string VIEWERLOGIN_VERSION_CHANNEL("invalid_version");
+
 // Link seams.
 
 //-----------------------------------------------------------------------------
@@ -160,7 +163,6 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name)
 //-----------------------------------------------------------------------------
 #include "../llviewercontrol.h"
 LLControlGroup gSavedSettings("Global");
-std::string gCurrentVersion = "invalid_version";
 
 LLControlGroup::LLControlGroup(const std::string& name) :
 	LLInstanceTracker<LLControlGroup, std::string>(name){}
@@ -177,6 +179,10 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i
 #include "lluicolortable.h"
 void LLUIColorTable::saveUserSettings(void)const {}
 
+//-----------------------------------------------------------------------------
+#include "../llversioninfo.h"
+const std::string &LLVersionInfo::getVersionAndChannel() { return VIEWERLOGIN_VERSION_CHANNEL; }
+const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; }
 
 //-----------------------------------------------------------------------------
 #include "llnotifications.h"
@@ -290,7 +296,6 @@ namespace tut
 			gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", FALSE);
 			gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", FALSE);
 			gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", FALSE);
-			gSavedSettings.declareString("VersionChannelName", "test_version_string", "", FALSE);
 			gSavedSettings.declareString("NextLoginLocation", "", "", FALSE);
 			gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE);
 
diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8855a24eadefde9b98dc685503bd2013493357ab
--- /dev/null
+++ b/indra/newview/tests/llversioninfo_test.cpp
@@ -0,0 +1,114 @@
+/** 
+ * @file llversioninfo_test.cpp
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+#include "linden_common.h"
+
+#include "../test/lltut.h"
+
+#include "../llversioninfo.h"
+#include "llversionviewer.h"
+
+namespace tut
+{
+    struct versioninfo
+    {
+		versioninfo()
+			: mResetChannel("Reset Channel")
+		{
+			std::ostringstream stream;
+			stream << LL_VERSION_MAJOR << "."
+				   << LL_VERSION_MINOR << "."
+				   << LL_VERSION_PATCH << "."
+				   << LL_VERSION_BUILD;
+			mVersion = stream.str();
+			stream.str("");
+
+			stream << LL_VERSION_MAJOR << "."
+				   << LL_VERSION_MINOR << "."
+				   << LL_VERSION_PATCH;
+			mShortVersion = stream.str();
+			stream.str("");
+
+			stream << mVersion
+				   << " "
+				   << LL_CHANNEL;
+			mVersionAndChannel = stream.str();
+			stream.str("");
+
+			stream << mVersion
+				   << " "
+				   << mResetChannel;
+			mResetVersionAndChannel = stream.str();
+		}
+		std::string mResetChannel;
+		std::string mVersion;
+		std::string mShortVersion;
+		std::string mVersionAndChannel;
+		std::string mResetVersionAndChannel;
+    };
+    
+	typedef test_group<versioninfo> versioninfo_t;
+	typedef versioninfo_t::object versioninfo_object_t;
+	tut::versioninfo_t tut_versioninfo("LLVersionInfo");
+
+	template<> template<>
+	void versioninfo_object_t::test<1>()
+	{
+		ensure_equals("Major version", 
+					  LLVersionInfo::getMajor(), 
+					  LL_VERSION_MAJOR);
+		ensure_equals("Minor version", 
+					  LLVersionInfo::getMinor(), 
+					  LL_VERSION_MINOR);
+		ensure_equals("Patch version", 
+					  LLVersionInfo::getPatch(), 
+					  LL_VERSION_PATCH);
+		ensure_equals("Build version", 
+					  LLVersionInfo::getBuild(), 
+					  LL_VERSION_BUILD);
+		ensure_equals("Channel version", 
+					  LLVersionInfo::getChannel(), 
+					  LL_CHANNEL);
+
+		ensure_equals("Version String", 
+					  LLVersionInfo::getVersion(), 
+					  mVersion);
+		ensure_equals("Short Version String", 
+					  LLVersionInfo::getShortVersion(), 
+					  mShortVersion);
+		ensure_equals("Version and channel String", 
+					  LLVersionInfo::getVersionAndChannel(), 
+					  mVersionAndChannel);
+
+		LLVersionInfo::resetChannel(mResetChannel);
+		ensure_equals("Reset channel version", 
+					  LLVersionInfo::getChannel(), 
+					  mResetChannel);
+
+		ensure_equals("Reset Version and channel String", 
+					  LLVersionInfo::getVersionAndChannel(), 
+					  mResetVersionAndChannel);
+	}
+}
diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp
index a0f1d1c3c34499998e1cc6a0f1abd3679dd6fe67..b425b50c8bd36a2be38d959b586c880bdb867ceb 100644
--- a/indra/newview/tests/llviewerhelputil_test.cpp
+++ b/indra/newview/tests/llviewerhelputil_test.cpp
@@ -72,16 +72,13 @@ static void substitute_string(std::string &input, const std::string &search, con
 	}
 }
 
-class LLAgent
-{
-public:
-	LLAgent() {}
-	~LLAgent() {}
-#ifdef __GNUC__
-	__attribute__ ((noinline))
-#endif
-	bool isGodlike() const { return FALSE; }
-};
+#include "../llagent.h"
+LLAgent::LLAgent() : mAgentAccess(gSavedSettings) { }
+LLAgent::~LLAgent() { }
+bool LLAgent::isGodlike() const { return FALSE; }
+LLAgentAccess::LLAgentAccess(LLControlGroup& settings) : mSavedSettings(settings) { }
+LLUIColor::LLUIColor() {}
+
 LLAgent gAgent;
 
 std::string LLWeb::expandURLSubstitutions(const std::string &url,
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 6861f02bfbad6876bd36d6fb3c2286a2c1bffc0d..4e5d6271df254465d27ae58568fe61bf21a1305b 100644
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -247,10 +247,13 @@ def construct(self):
         
         self.disable_manifest_check()
 
+        self.path("../viewer_components/updater/scripts/windows/update_install.bat")
+
         # Get shared libs from the shared libs staging directory
         if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),
                        dst=""):
 
+
             self.enable_crt_manifest_check()
 
             # Get kdu dll, continue if missing.
@@ -572,6 +575,8 @@ def construct(self):
             # copy additional libs in <bundle>/Contents/MacOS/
             self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
 
+            self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install")
+
             # most everything goes in the Resources directory
             if self.prefix(src="", dst="Resources"):
                 super(DarwinManifest, self).construct()
@@ -743,6 +748,11 @@ def package_finish(self):
             devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()
             volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
 
+            if devfile != '/dev/disk1':
+                # adding more debugging info based upon nat's hunches to the
+                # logs to help track down 'SetFile -a V' failures -brad
+                print "WARNING: 'SetFile -a V' command below is probably gonna fail"
+
             # Copy everything in to the mounted .dmg
 
             if self.default_channel() and not self.default_grid():
@@ -842,6 +852,8 @@ def construct(self):
             # recurse
             self.end_prefix("res-sdl")
 
+        self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install")
+
         # plugins
         if self.prefix(src="", dst="bin/llplugin"):
             self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
@@ -870,7 +882,7 @@ def package_finish(self):
 
         if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
             print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
-            self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
+            self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
 
         # Fix access permissions
         self.run_command("""
@@ -897,6 +909,9 @@ def package_finish(self):
                         'dir': self.get_build_prefix(),
                         'inst_name': installer_name,
                         'inst_path':self.build_path_of(installer_name)})
+            else:
+                print "Skipping %s.tar.bz2 for non-Release build (%s)" % \
+                      (installer_name, self.args['buildtype'])
         finally:
             self.run_command("mv %(inst)s %(dst)s" % {
                 'dst': self.get_dst_prefix(),
diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt
index 0993b64b14781415b4de7a1b20488a7182487231..74c9b4568d97b9d46cbef7a481b8c25e04632c43 100644
--- a/indra/viewer_components/CMakeLists.txt
+++ b/indra/viewer_components/CMakeLists.txt
@@ -1,4 +1,4 @@
 # -*- cmake -*-
 
 add_subdirectory(login)
-
+add_subdirectory(updater)
diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0e288bb4963a52f91620cea48a01e7310659f92b
--- /dev/null
+++ b/indra/viewer_components/updater/CMakeLists.txt
@@ -0,0 +1,82 @@
+# -*- cmake -*-
+
+project(updater_service)
+
+include(00-Common)
+if(LL_TESTS)
+  include(LLAddBuildTest)
+endif(LL_TESTS)
+include(CMakeCopyIfDifferent)
+include(CURL)
+include(LLCommon)
+include(LLMessage)
+include(LLPlugin)
+include(LLVFS)
+
+include_directories(
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LLMESSAGE_INCLUDE_DIRS}
+    ${LLPLUGIN_INCLUDE_DIRS}
+	${LLVFS_INCLUDE_DIRS}
+	${CURL_INCLUDE_DIRS}
+    )
+
+set(updater_service_SOURCE_FILES
+    llupdaterservice.cpp
+    llupdatechecker.cpp
+    llupdatedownloader.cpp
+    llupdateinstaller.cpp
+    )
+
+set(updater_service_HEADER_FILES
+    llupdaterservice.h
+    llupdatechecker.h
+    llupdatedownloader.h
+    llupdateinstaller.h
+    )
+
+set_source_files_properties(${updater_service_HEADER_FILES}
+                            PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND 
+    updater_service_SOURCE_FILES 
+    ${updater_service_HEADER_FILES} 
+    )
+
+add_library(llupdaterservice
+            ${updater_service_SOURCE_FILES}
+            )
+
+target_link_libraries(llupdaterservice
+    ${LLCOMMON_LIBRARIES}
+    ${LLMESSAGE_LIBRARIES}
+    ${LLPLUGIN_LIBRARIES}
+	${LLVFS_LIBRARIES}
+    )
+
+if(LL_TESTS)
+  SET(llupdater_service_TEST_SOURCE_FILES
+      llupdaterservice.cpp
+      )
+
+# *NOTE:Mani - I was trying to use the preprocessor seam to mock out
+#              llifstream (and other) llcommon classes. I didn't work
+#              because of the windows declspec(dllimport)attribute.
+#set_source_files_properties(
+#    llupdaterservice.cpp
+#    PROPERTIES
+#      LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream"
+#    )
+
+  LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}")
+endif(LL_TESTS)
+
+set(UPDATER_INCLUDE_DIRS 
+  ${LIBS_OPEN_DIR}/viewer_components/updater 
+  CACHE INTERNAL ""
+)
+
+set(UPDATER_LIBRARIES 
+  llupdaterservice
+  CACHE INTERNAL ""
+)
diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6aa9b0f110d1422e4c665c7e6532cc4bc91684d
--- /dev/null
+++ b/indra/viewer_components/updater/llupdatechecker.cpp
@@ -0,0 +1,194 @@
+/** 
+ * @file llupdaterservice.cpp
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+#include "linden_common.h"
+#include <stdexcept>
+#include <boost/format.hpp>
+#include "llhttpclient.h"
+#include "llsd.h"
+#include "llupdatechecker.h"
+#include "lluri.h"
+
+
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+
+class LLUpdateChecker::CheckError:
+	public std::runtime_error
+{
+public:
+	CheckError(const char * message):
+		std::runtime_error(message)
+	{
+		; // No op.
+	}
+};
+
+
+class LLUpdateChecker::Implementation:
+	public LLHTTPClient::Responder
+{
+public:
+	Implementation(Client & client);
+	~Implementation();
+	void check(std::string const & protocolVersion, std::string const & hostUrl, 
+			   std::string const & servicePath, std::string channel, std::string version);
+	
+	// Responder:
+	virtual void completed(U32 status,
+						   const std::string & reason,
+						   const LLSD& content);
+	virtual void error(U32 status, const std::string & reason);
+	
+private:	
+	static const char * sProtocolVersion;
+	
+	Client & mClient;
+	LLHTTPClient mHttpClient;
+	bool mInProgress;
+	std::string mVersion;
+	
+	std::string buildUrl(std::string const & protocolVersion, std::string const & hostUrl, 
+						 std::string const & servicePath, std::string channel, std::string version);
+
+	LOG_CLASS(LLUpdateChecker::Implementation);
+};
+
+
+
+// LLUpdateChecker
+//-----------------------------------------------------------------------------
+
+
+LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client):
+	mImplementation(new LLUpdateChecker::Implementation(client))
+{
+	; // No op.
+}
+
+
+void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, 
+							std::string const & servicePath, std::string channel, std::string version)
+{
+	mImplementation->check(protocolVersion, hostUrl, servicePath, channel, version);
+}
+
+
+
+// LLUpdateChecker::Implementation
+//-----------------------------------------------------------------------------
+
+
+const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.0";
+
+
+LLUpdateChecker::Implementation::Implementation(LLUpdateChecker::Client & client):
+	mClient(client),
+	mInProgress(false)
+{
+	; // No op.
+}
+
+
+LLUpdateChecker::Implementation::~Implementation()
+{
+	; // No op.
+}
+
+
+void LLUpdateChecker::Implementation::check(std::string const & protocolVersion, std::string const & hostUrl, 
+											std::string const & servicePath, std::string channel, std::string version)
+{
+	llassert(!mInProgress);
+	
+	if(protocolVersion != sProtocolVersion) throw CheckError("unsupported protocol");
+		
+	mInProgress = true;
+	mVersion = version;
+	std::string checkUrl = buildUrl(protocolVersion, hostUrl, servicePath, channel, version);
+	LL_INFOS("UpdateCheck") << "checking for updates at " << checkUrl << llendl;
+	
+	// The HTTP client will wrap a raw pointer in a boost::intrusive_ptr causing the
+	// passed object to be silently and automatically deleted.  We pass a self-
+	// referential intrusive pointer to which we add a reference to keep the
+	// client from deleting the update checker implementation instance.
+	LLHTTPClient::ResponderPtr temporaryPtr(this);
+	boost::intrusive_ptr_add_ref(temporaryPtr.get());
+	mHttpClient.get(checkUrl, temporaryPtr);
+}
+
+void LLUpdateChecker::Implementation::completed(U32 status,
+							  const std::string & reason,
+							  const LLSD & content)
+{
+	mInProgress = false;	
+	
+	if(status != 200) {
+		LL_WARNS("UpdateCheck") << "html error " << status << " (" << reason << ")" << llendl;
+		mClient.error(reason);
+	} else if(!content.asBoolean()) {
+		LL_INFOS("UpdateCheck") << "up to date" << llendl;
+		mClient.upToDate();
+	} else if(content["required"].asBoolean()) {
+		LL_INFOS("UpdateCheck") << "version invalid" << llendl;
+		LLURI uri(content["url"].asString());
+		mClient.requiredUpdate(content["version"].asString(), uri, content["hash"].asString());
+	} else {
+		LL_INFOS("UpdateCheck") << "newer version " << content["version"].asString() << " available" << llendl;
+		LLURI uri(content["url"].asString());
+		mClient.optionalUpdate(content["version"].asString(), uri, content["hash"].asString());
+	}
+}
+
+
+void LLUpdateChecker::Implementation::error(U32 status, const std::string & reason)
+{
+	mInProgress = false;
+	LL_WARNS("UpdateCheck") << "update check failed; " << reason << llendl;
+	mClient.error(reason);
+}
+
+
+std::string LLUpdateChecker::Implementation::buildUrl(std::string const & protocolVersion, std::string const & hostUrl, 
+													  std::string const & servicePath, std::string channel, std::string version)
+{	
+#ifdef LL_WINDOWS
+	static const char * platform = "win";
+#elif LL_DARWIN
+	static const char * platform = "mac";
+#else
+	static const char * platform = "lnx";
+#endif
+	
+	LLSD path;
+	path.append(servicePath);
+	path.append(protocolVersion);
+	path.append(channel);
+	path.append(version);
+	path.append(platform);
+	return LLURI::buildHTTP(hostUrl, path).asString();
+}
diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h
new file mode 100644
index 0000000000000000000000000000000000000000..cea1f13647dc2275182691c6ac022ab89d075b7d
--- /dev/null
+++ b/indra/viewer_components/updater/llupdatechecker.h
@@ -0,0 +1,82 @@
+/** 
+ * @file llupdatechecker.h
+ *
+ * $LicenseInfo:firstyear=2010&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_UPDATERCHECKER_H
+#define LL_UPDATERCHECKER_H
+
+
+#include <boost/shared_ptr.hpp>
+
+
+//
+// Implements asynchronous checking for updates.
+//
+class LLUpdateChecker {
+public:
+	class Client;
+	class Implementation;
+	
+	// An exception that may be raised on check errors.
+	class CheckError;
+	
+	LLUpdateChecker(Client & client);
+	
+	// Check status of current app on the given host for the channel and version provided.
+	void check(std::string const & protocolVersion, std::string const & hostUrl, 
+			   std::string const & servicePath, std::string channel, std::string version);
+	
+private:
+	boost::shared_ptr<Implementation> mImplementation;
+};
+
+
+class LLURI; // From lluri.h
+
+
+//
+// The client interface implemented by a requestor checking for an update.
+//
+class LLUpdateChecker::Client
+{
+public:
+	// An error occurred while checking for an update.
+	virtual void error(std::string const & message) = 0;
+	
+	// A newer version is available, but the current version may still be used.
+	virtual void optionalUpdate(std::string const & newVersion,
+								LLURI const & uri,
+								std::string const & hash) = 0;
+	
+	// A newer version is available, and the current version is no longer valid. 
+	virtual void requiredUpdate(std::string const & newVersion,
+								LLURI const & uri,
+								std::string const & hash) = 0;
+	
+	// The checked version is up to date; no newer version exists.
+	virtual void upToDate(void) = 0;
+};
+
+
+#endif
diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c17a50e24245aeb3d5846ab5fb569696b744dd7d
--- /dev/null
+++ b/indra/viewer_components/updater/llupdatedownloader.cpp
@@ -0,0 +1,428 @@
+/** 
+ * @file llupdatedownloader.cpp
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+#include "linden_common.h"
+#include <stdexcept>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <curl/curl.h>
+#include "lldir.h"
+#include "llfile.h"
+#include "llmd5.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llthread.h"
+#include "llupdatedownloader.h"
+#include "llupdaterservice.h"
+
+
+class LLUpdateDownloader::Implementation:
+	public LLThread
+{
+public:
+	Implementation(LLUpdateDownloader::Client & client);
+	~Implementation();
+	void cancel(void);
+	void download(LLURI const & uri, std::string const & hash);
+	bool isDownloading(void);
+	size_t onHeader(void * header, size_t size);
+	size_t onBody(void * header, size_t size);
+	void resume(void);
+	
+private:
+	bool mCancelled;
+	LLUpdateDownloader::Client & mClient;
+	CURL * mCurl;
+	LLSD mDownloadData;
+	llofstream mDownloadStream;
+	std::string mDownloadRecordPath;
+	curl_slist * mHeaderList;
+	
+	void initializeCurlGet(std::string const & url, bool processHeader);
+	void resumeDownloading(size_t startByte);
+	void run(void);
+	void startDownloading(LLURI const & uri, std::string const & hash);
+	void throwOnCurlError(CURLcode code);
+	bool validateDownload(void);
+
+	LOG_CLASS(LLUpdateDownloader::Implementation);
+};
+
+
+namespace {
+	class DownloadError:
+		public std::runtime_error
+	{
+	public:
+		DownloadError(const char * message):
+			std::runtime_error(message)
+		{
+			; // No op.
+		}
+	};
+
+		
+	const char * gSecondLifeUpdateRecord = "SecondLifeUpdateDownload.xml";
+};
+
+
+
+// LLUpdateDownloader
+//-----------------------------------------------------------------------------
+
+
+
+std::string LLUpdateDownloader::downloadMarkerPath(void)
+{
+	return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, gSecondLifeUpdateRecord);
+}
+
+
+LLUpdateDownloader::LLUpdateDownloader(Client & client):
+	mImplementation(new LLUpdateDownloader::Implementation(client))
+{
+	; // No op.
+}
+
+
+void LLUpdateDownloader::cancel(void)
+{
+	mImplementation->cancel();
+}
+
+
+void LLUpdateDownloader::download(LLURI const & uri, std::string const & hash)
+{
+	mImplementation->download(uri, hash);
+}
+
+
+bool LLUpdateDownloader::isDownloading(void)
+{
+	return mImplementation->isDownloading();
+}
+
+
+void LLUpdateDownloader::resume(void)
+{
+	mImplementation->resume();
+}
+
+
+
+// LLUpdateDownloader::Implementation
+//-----------------------------------------------------------------------------
+
+
+namespace {
+	size_t write_function(void * data, size_t blockSize, size_t blocks, void * downloader)
+	{
+		size_t bytes = blockSize * blocks;
+		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onBody(data, bytes);
+	}
+
+
+	size_t header_function(void * data, size_t blockSize, size_t blocks, void * downloader)
+	{
+		size_t bytes = blockSize * blocks;
+		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes);
+	}
+}
+
+
+LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client):
+	LLThread("LLUpdateDownloader"),
+	mCancelled(false),
+	mClient(client),
+	mCurl(0),
+	mHeaderList(0)
+{
+	CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case.
+	llverify(code == CURLE_OK); // TODO: real error handling here. 
+}
+
+
+LLUpdateDownloader::Implementation::~Implementation()
+{
+	if(isDownloading()) {
+		cancel();
+		shutdown();
+	} else {
+		; // No op.
+	}
+	if(mCurl) curl_easy_cleanup(mCurl);
+}
+
+
+void LLUpdateDownloader::Implementation::cancel(void)
+{
+	mCancelled = true;
+}
+	
+
+void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash)
+{
+	if(isDownloading()) mClient.downloadError("download in progress");
+
+	mDownloadRecordPath = downloadMarkerPath();
+	mDownloadData = LLSD();
+	try {
+		startDownloading(uri, hash);
+	} catch(DownloadError const & e) {
+		mClient.downloadError(e.what());
+	}
+}
+
+
+bool LLUpdateDownloader::Implementation::isDownloading(void)
+{
+	return !isStopped();
+}
+
+
+void LLUpdateDownloader::Implementation::resume(void)
+{
+	mCancelled = false;
+
+	if(isDownloading()) {
+		mClient.downloadError("download in progress");
+	}
+
+	mDownloadRecordPath = downloadMarkerPath();
+	llifstream dataStream(mDownloadRecordPath);
+	if(!dataStream) {
+		mClient.downloadError("no download marker");
+		return;
+	}
+	
+	LLSDSerialize::fromXMLDocument(mDownloadData, dataStream);
+	
+	if(!mDownloadData.asBoolean()) {
+		mClient.downloadError("no download information in marker");
+		return;
+	}
+	
+	std::string filePath = mDownloadData["path"].asString();
+	try {
+		if(LLFile::isfile(filePath)) {		
+			llstat fileStatus;
+			LLFile::stat(filePath, &fileStatus);
+			if(fileStatus.st_size != mDownloadData["size"].asInteger()) {
+				resumeDownloading(fileStatus.st_size);
+			} else if(!validateDownload()) {
+				LLFile::remove(filePath);
+				download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString());
+			} else {
+				mClient.downloadComplete(mDownloadData);
+			}
+		} else {
+			download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString());
+		}
+	} catch(DownloadError & e) {
+		mClient.downloadError(e.what());
+	}
+}
+
+
+size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
+{
+	char const * headerPtr = reinterpret_cast<const char *> (buffer);
+	std::string header(headerPtr, headerPtr + size);
+	size_t colonPosition = header.find(':');
+	if(colonPosition == std::string::npos) return size; // HTML response; ignore.
+	
+	if(header.substr(0, colonPosition) == "Content-Length") {
+		try {
+			size_t firstDigitPos = header.find_first_of("0123456789", colonPosition);
+			size_t lastDigitPos = header.find_last_of("0123456789");
+			std::string contentLength = header.substr(firstDigitPos, lastDigitPos - firstDigitPos + 1);
+			size_t size = boost::lexical_cast<size_t>(contentLength);
+			LL_INFOS("UpdateDownload") << "download size is " << size << LL_ENDL;
+			
+			mDownloadData["size"] = LLSD(LLSD::Integer(size));
+			llofstream odataStream(mDownloadRecordPath);
+			LLSDSerialize::toPrettyXML(mDownloadData, odataStream);
+		} catch (std::exception const & e) {
+			LL_WARNS("UpdateDownload") << "unable to read content length (" 
+				<< e.what() << ")" << LL_ENDL;
+		}
+	} else {
+		; // No op.
+	}
+	
+	return size;
+}
+
+
+size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size)
+{
+	if(mCancelled) return 0; // Forces a write error which will halt curl thread.
+	if((size == 0) || (buffer == 0)) return 0; 
+	
+	mDownloadStream.write(reinterpret_cast<const char *>(buffer), size);
+	if(mDownloadStream.bad()) {
+		return 0;
+	} else {
+		return size;
+	}
+}
+
+
+void LLUpdateDownloader::Implementation::run(void)
+{
+	CURLcode code = curl_easy_perform(mCurl);
+	mDownloadStream.close();
+	if(code == CURLE_OK) {
+		LLFile::remove(mDownloadRecordPath);
+		if(validateDownload()) {
+			LL_INFOS("UpdateDownload") << "download successful" << LL_ENDL;
+			mClient.downloadComplete(mDownloadData);
+		} else {
+			LL_INFOS("UpdateDownload") << "download failed hash check" << LL_ENDL;
+			std::string filePath = mDownloadData["path"].asString();
+			if(filePath.size() != 0) LLFile::remove(filePath);
+			mClient.downloadError("failed hash check");
+		}
+	} else if(mCancelled && (code == CURLE_WRITE_ERROR)) {
+		LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL;
+		// Do not call back client.
+	} else {
+		LL_WARNS("UpdateDownload") << "download failed with error '" << 
+			curl_easy_strerror(code) << "'" << LL_ENDL;
+		LLFile::remove(mDownloadRecordPath);
+		if(mDownloadData.has("path")) LLFile::remove(mDownloadData["path"].asString());
+		mClient.downloadError("curl error");
+	}
+	
+	if(mHeaderList) {
+		curl_slist_free_all(mHeaderList);
+		mHeaderList = 0;
+	}
+}
+
+
+void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader)
+{
+	if(mCurl == 0) {
+		mCurl = curl_easy_init();
+	} else {
+		curl_easy_reset(mCurl);
+	}
+	
+	if(mCurl == 0) throw DownloadError("failed to initialize curl");
+	
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this));
+	if(processHeader) {
+	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, &header_function));
+	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, this));
+	}
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str()));
+}
+
+
+void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte)
+{
+	LL_INFOS("UpdateDownload") << "resuming download from " << mDownloadData["url"].asString()
+		<< " at byte " << startByte << LL_ENDL;
+
+	initializeCurlGet(mDownloadData["url"].asString(), false);
+	
+	// The header 'Range: bytes n-' will request the bytes remaining in the
+	// source begining with byte n and ending with the last byte.
+	boost::format rangeHeaderFormat("Range: bytes=%u-");
+	rangeHeaderFormat % startByte;
+	mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str());
+	if(mHeaderList == 0) throw DownloadError("cannot add Range header");
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaderList));
+	
+	mDownloadStream.open(mDownloadData["path"].asString(),
+						 std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	start();
+}
+
+
+void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std::string const & hash)
+{
+	mDownloadData["url"] = uri.asString();
+	mDownloadData["hash"] = hash;
+	mDownloadData["current_version"] = ll_get_version();
+	LLSD path = uri.pathArray();
+	if(path.size() == 0) throw DownloadError("no file path");
+	std::string fileName = path[path.size() - 1].asString();
+	std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName);
+	mDownloadData["path"] = filePath;
+
+	LL_INFOS("UpdateDownload") << "downloading " << filePath
+		<< " from " << uri.asString() << LL_ENDL;
+	LL_INFOS("UpdateDownload") << "hash of file is " << hash << LL_ENDL;
+		
+	llofstream dataStream(mDownloadRecordPath);
+	LLSDSerialize::toPrettyXML(mDownloadData, dataStream);
+	
+	mDownloadStream.open(filePath, std::ios_base::out | std::ios_base::binary);
+	initializeCurlGet(uri.asString(), true);
+	start();
+}
+
+
+void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code)
+{
+	if(code != CURLE_OK) {
+		const char * errorString = curl_easy_strerror(code);
+		if(errorString != 0) {
+			throw DownloadError(curl_easy_strerror(code));
+		} else {
+			throw DownloadError("unknown curl error");
+		}
+	} else {
+		; // No op.
+	}
+}
+
+
+bool LLUpdateDownloader::Implementation::validateDownload(void)
+{
+	std::string filePath = mDownloadData["path"].asString();
+	llifstream fileStream(filePath, std::ios_base::in | std::ios_base::binary);
+	if(!fileStream) return false;
+
+	std::string hash = mDownloadData["hash"].asString();
+	if(hash.size() != 0) {
+		LL_INFOS("UpdateDownload") << "checking hash..." << LL_ENDL;
+		char digest[33];
+		LLMD5(fileStream).hex_digest(digest);
+		if(hash != digest) {
+			LL_WARNS("UpdateDownload") << "download hash mismatch; expeted " << hash <<
+				" but download is " << digest << LL_ENDL;
+		}
+		return hash == digest;
+	} else {
+		return true; // No hash check provided.
+	}
+}
diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b3d7480fd1785747b5d5f6e5509667a03f791ac
--- /dev/null
+++ b/indra/viewer_components/updater/llupdatedownloader.h
@@ -0,0 +1,87 @@
+/** 
+ * @file llupdatedownloader.h
+ *
+ * $LicenseInfo:firstyear=2010&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_UPDATE_DOWNLOADER_H
+#define LL_UPDATE_DOWNLOADER_H
+
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "lluri.h"
+
+
+//
+// An asynchronous download service for fetching updates.
+//
+class LLUpdateDownloader
+{
+public:
+	class Client;
+	class Implementation;
+	
+	// Returns the path to the download marker file containing details of the
+	// latest download.
+	static std::string downloadMarkerPath(void);
+	
+	LLUpdateDownloader(Client & client);
+	
+	// Cancel any in progress download; a no op if none is in progress.  The
+	// client will not receive a complete or error callback.
+	void cancel(void);
+	
+	// Start a new download.
+	void download(LLURI const & uri, std::string const & hash);
+	
+	// Returns true if a download is in progress.
+	bool isDownloading(void);
+	
+	// Resume a partial download.
+	void resume(void);
+	
+private:
+	boost::shared_ptr<Implementation> mImplementation;
+};
+
+
+//
+// An interface to be implemented by clients initiating a update download.
+//
+class LLUpdateDownloader::Client {
+public:
+	
+	// The download has completed successfully.
+	// data is a map containing the following items:
+	// url - source (remote) location
+	// hash - the md5 sum that should match the installer file.
+	// path - destination (local) location
+	// size - the size of the installer in bytes
+	virtual void downloadComplete(LLSD const & data) = 0;
+	
+	// The download failed.
+	virtual void downloadError(std::string const & message) = 0;
+};
+
+
+#endif
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e69bcf28b8b9d201b701709855dd1e431162b73
--- /dev/null
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -0,0 +1,90 @@
+/** 
+ * @file llupdateinstaller.cpp
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+#include "linden_common.h"
+#include <apr_file_io.h>
+#include "llapr.h"
+#include "llprocesslauncher.h"
+#include "llupdateinstaller.h"
+#include "lldir.h"
+
+
+namespace {
+	class RelocateError {};
+	
+	
+	std::string copy_to_temp(std::string const & path)
+	{
+		std::string scriptFile = gDirUtilp->getBaseFileName(path);
+		std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile);
+		apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp);
+		if(status != APR_SUCCESS) throw RelocateError();
+		
+		return newPath;
+	}
+}
+
+
+int ll_install_update(std::string const & script, std::string const & updatePath, LLInstallScriptMode mode)
+{
+	std::string actualScriptPath;
+	switch(mode) {
+		case LL_COPY_INSTALL_SCRIPT_TO_TEMP:
+			try {
+				actualScriptPath = copy_to_temp(script);
+			}
+			catch (RelocateError &) {
+				return -1;
+			}
+			break;
+		case LL_RUN_INSTALL_SCRIPT_IN_PLACE:
+			actualScriptPath = script;
+			break;
+		default:
+			llassert(!"unpossible copy mode");
+	}
+	
+	llinfos << "UpdateInstaller: installing " << updatePath << " using " <<
+		actualScriptPath << LL_ENDL;
+	
+	LLProcessLauncher launcher;
+	launcher.setExecutable(actualScriptPath);
+	launcher.addArgument(updatePath);
+	launcher.addArgument(ll_install_failed_marker_path().c_str());
+	int result = launcher.launch();
+	launcher.orphan();
+	
+	return result;
+}
+
+
+std::string const & ll_install_failed_marker_path(void)
+{
+	static std::string path;
+	if(path.empty()) {
+		path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeInstallFailed.marker");
+	}
+	return path;
+}
diff --git a/indra/viewer_components/updater/llupdateinstaller.h b/indra/viewer_components/updater/llupdateinstaller.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ce08ce6fa51ece998fc77469b473c2fd3f3742d
--- /dev/null
+++ b/indra/viewer_components/updater/llupdateinstaller.h
@@ -0,0 +1,57 @@
+/** 
+ * @file llupdateinstaller.h
+ *
+ * $LicenseInfo:firstyear=2010&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_UPDATE_INSTALLER_H
+#define LL_UPDATE_INSTALLER_H
+
+
+#include <string>
+
+
+enum LLInstallScriptMode {
+	LL_RUN_INSTALL_SCRIPT_IN_PLACE,
+	LL_COPY_INSTALL_SCRIPT_TO_TEMP
+};
+
+//
+// Launch the installation script.
+// 
+// The updater will overwrite the current installation, so it is highly recommended
+// that the current application terminate once this function is called.
+//
+int ll_install_update(
+					   std::string const & script, // Script to execute.
+					   std::string const & updatePath, // Path to update file.
+					   LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp?
+
+
+//
+// Returns the path which points to the failed install marker file, should it
+// exist.
+//
+std::string const & ll_install_failed_marker_path(void);
+
+
+#endif
diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cc60eaead27a9ba49e5804bce09f4e415c277fb5
--- /dev/null
+++ b/indra/viewer_components/updater/llupdaterservice.cpp
@@ -0,0 +1,519 @@
+/** 
+ * @file llupdaterservice.cpp
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+#include "linden_common.h"
+
+#include "llupdatedownloader.h"
+#include "llevents.h"
+#include "lltimer.h"
+#include "llupdaterservice.h"
+#include "llupdatechecker.h"
+#include "llupdateinstaller.h"
+#include "llversionviewer.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "llfile.h"
+
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+
+namespace 
+{
+	boost::weak_ptr<LLUpdaterServiceImpl> gUpdater;
+
+	const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml");
+	std::string update_marker_path()
+	{
+		return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, 
+											  UPDATE_MARKER_FILENAME);
+	}
+	
+	std::string install_script_path(void)
+	{
+#ifdef LL_WINDOWS
+		std::string scriptFile = "update_install.bat";
+#else
+		std::string scriptFile = "update_install";
+#endif
+		return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile);
+	}
+	
+	LLInstallScriptMode install_script_mode(void) 
+	{
+#ifdef LL_WINDOWS
+		return LL_COPY_INSTALL_SCRIPT_TO_TEMP;
+#else
+		return LL_RUN_INSTALL_SCRIPT_IN_PLACE;
+#endif
+	};
+	
+}
+
+class LLUpdaterServiceImpl : 
+	public LLUpdateChecker::Client,
+	public LLUpdateDownloader::Client
+{
+	static const std::string sListenerName;
+	
+	std::string mProtocolVersion;
+	std::string mUrl;
+	std::string mPath;
+	std::string mChannel;
+	std::string mVersion;
+	
+	unsigned int mCheckPeriod;
+	bool mIsChecking;
+	bool mIsDownloading;
+	
+	LLUpdateChecker mUpdateChecker;
+	LLUpdateDownloader mUpdateDownloader;
+	LLTimer mTimer;
+
+	LLUpdaterService::app_exit_callback_t mAppExitCallback;
+	
+	LOG_CLASS(LLUpdaterServiceImpl);
+	
+public:
+	LLUpdaterServiceImpl();
+	virtual ~LLUpdaterServiceImpl();
+
+	void initialize(const std::string& protocol_version,
+				   const std::string& url, 
+				   const std::string& path,
+				   const std::string& channel,
+				   const std::string& version);
+	
+	void setCheckPeriod(unsigned int seconds);
+
+	void startChecking(bool install_if_ready);
+	void stopChecking();
+	bool isChecking();
+	
+	void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;}
+
+	bool checkForInstall(bool launchInstaller); // Test if a local install is ready.
+	bool checkForResume(); // Test for resumeable d/l.
+
+	// LLUpdateChecker::Client:
+	virtual void error(std::string const & message);
+	virtual void optionalUpdate(std::string const & newVersion,
+								LLURI const & uri,
+								std::string const & hash);
+	virtual void requiredUpdate(std::string const & newVersion,
+								LLURI const & uri,
+								std::string const & hash);
+	virtual void upToDate(void);
+	
+	// LLUpdateDownloader::Client
+	void downloadComplete(LLSD const & data);
+	void downloadError(std::string const & message);
+
+	bool onMainLoop(LLSD const & event);
+
+private:
+	void restartTimer(unsigned int seconds);
+	void stopTimer();
+};
+
+const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl";
+
+LLUpdaterServiceImpl::LLUpdaterServiceImpl() :
+	mIsChecking(false),
+	mIsDownloading(false),
+	mCheckPeriod(0),
+	mUpdateChecker(*this),
+	mUpdateDownloader(*this)
+{
+}
+
+LLUpdaterServiceImpl::~LLUpdaterServiceImpl()
+{
+	LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL;
+	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
+}
+
+void LLUpdaterServiceImpl::initialize(const std::string& protocol_version,
+									  const std::string& url, 
+									  const std::string& path,
+									  const std::string& channel,
+									  const std::string& version)
+{
+	if(mIsChecking || mIsDownloading)
+	{
+		throw LLUpdaterService::UsageError("LLUpdaterService::initialize call "
+										   "while updater is running.");
+	}
+		
+	mProtocolVersion = protocol_version;
+	mUrl = url;
+	mPath = path;
+	mChannel = channel;
+	mVersion = version;
+}
+
+void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds)
+{
+	mCheckPeriod = seconds;
+}
+
+void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
+{
+	if(mUrl.empty() || mChannel.empty() || mVersion.empty())
+	{
+		throw LLUpdaterService::UsageError("Set params before call to "
+			"LLUpdaterService::startCheck().");
+	}
+
+	mIsChecking = true;
+
+    // Check to see if an install is ready.
+	bool has_install = checkForInstall(install_if_ready);
+	if(!has_install)
+	{
+		checkForResume(); // will set mIsDownloading to true if resuming
+
+		if(!mIsDownloading)
+		{
+			// Checking can only occur during the mainloop.
+			// reset the timer to 0 so that the next mainloop event 
+			// triggers a check;
+			restartTimer(0); 
+		}
+	}
+}
+
+void LLUpdaterServiceImpl::stopChecking()
+{
+	if(mIsChecking)
+	{
+		mIsChecking = false;
+		stopTimer();
+	}
+
+    if(mIsDownloading)
+    {
+        mUpdateDownloader.cancel();
+		mIsDownloading = false;
+    }
+}
+
+bool LLUpdaterServiceImpl::isChecking()
+{
+	return mIsChecking;
+}
+
+bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller)
+{
+	bool foundInstall = false; // return true if install is found.
+
+	llifstream update_marker(update_marker_path(), 
+							 std::ios::in | std::ios::binary);
+
+	if(update_marker.is_open())
+	{
+		// Found an update info - now lets see if its valid.
+		LLSD update_info;
+		LLSDSerialize::fromXMLDocument(update_info, update_marker);
+		update_marker.close();
+
+		// Get the path to the installer file.
+		LLSD path = update_info.get("path");
+		if(update_info["current_version"].asString() != ll_get_version())
+		{
+			// This viewer is not the same version as the one that downloaded
+			// the update.  Do not install this update.
+			if(!path.asString().empty())
+			{
+				llinfos << "ignoring update dowloaded by different client version" << llendl;
+				LLFile::remove(path.asString());
+				LLFile::remove(update_marker_path());
+			}
+			else
+			{
+				; // Nothing to clean up.
+			}
+			
+			foundInstall = false;
+		} 
+		else if(path.isDefined() && !path.asString().empty())
+		{
+			if(launchInstaller)
+			{
+				LLFile::remove(update_marker_path());
+
+				int result = ll_install_update(install_script_path(),
+											   update_info["path"].asString(),
+											   install_script_mode());	
+				
+				if((result == 0) && mAppExitCallback)
+				{
+					mAppExitCallback();
+				} else if(result != 0) {
+					llwarns << "failed to run update install script" << LL_ENDL;
+				} else {
+					; // No op.
+				}
+			}
+			
+			foundInstall = true;
+		}
+	}
+	return foundInstall;
+}
+
+bool LLUpdaterServiceImpl::checkForResume()
+{
+	bool result = false;
+	std::string download_marker_path = mUpdateDownloader.downloadMarkerPath();
+	if(LLFile::isfile(download_marker_path))
+	{
+		llifstream download_marker_stream(download_marker_path, 
+								 std::ios::in | std::ios::binary);
+		if(download_marker_stream.is_open())
+		{
+			LLSD download_info;
+			LLSDSerialize::fromXMLDocument(download_info, download_marker_stream);
+			download_marker_stream.close();
+			if(download_info["current_version"].asString() == ll_get_version())
+			{
+				mIsDownloading = true;
+				mUpdateDownloader.resume();
+				result = true;
+			}
+			else 
+			{
+				// The viewer that started this download is not the same as this viewer; ignore.
+				llinfos << "ignoring partial download from different viewer version" << llendl;
+				std::string path = download_info["path"].asString();
+				if(!path.empty()) LLFile::remove(path);
+				LLFile::remove(download_marker_path);
+			}
+		} 
+	}
+	return result;
+}
+
+void LLUpdaterServiceImpl::error(std::string const & message)
+{
+	if(mIsChecking)
+	{
+		restartTimer(mCheckPeriod);
+	}
+}
+
+void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion,
+										  LLURI const & uri,
+										  std::string const & hash)
+{
+	stopTimer();
+	mIsDownloading = true;
+	mUpdateDownloader.download(uri, hash);
+}
+
+void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
+										  LLURI const & uri,
+										  std::string const & hash)
+{
+	stopTimer();
+	mIsDownloading = true;
+	mUpdateDownloader.download(uri, hash);
+}
+
+void LLUpdaterServiceImpl::upToDate(void)
+{
+	if(mIsChecking)
+	{
+		restartTimer(mCheckPeriod);
+	}
+}
+
+void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) 
+{ 
+	mIsDownloading = false;
+
+	// Save out the download data to the SecondLifeUpdateReady
+	// marker file. 
+	llofstream update_marker(update_marker_path());
+	LLSDSerialize::toPrettyXML(data, update_marker);
+	
+	LLSD event;
+	event["pump"] = LLUpdaterService::pumpName();
+	LLSD payload;
+	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE);
+	event["payload"] = payload;
+	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
+}
+
+void LLUpdaterServiceImpl::downloadError(std::string const & message) 
+{ 
+	LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL;
+
+	mIsDownloading = false;
+
+	// Restart the timer on error
+	if(mIsChecking)
+	{
+		restartTimer(mCheckPeriod); 
+	}
+
+	LLSD event;
+	event["pump"] = LLUpdaterService::pumpName();
+	LLSD payload;
+	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR);
+	payload["message"] = message;
+	event["payload"] = payload;
+	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
+}
+
+void LLUpdaterServiceImpl::restartTimer(unsigned int seconds)
+{
+	LL_INFOS("UpdaterService") << "will check for update again in " << 
+	seconds << " seconds" << LL_ENDL; 
+	mTimer.start();
+	mTimer.setTimerExpirySec(seconds);
+	LLEventPumps::instance().obtain("mainloop").listen(
+		sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1));
+}
+
+void LLUpdaterServiceImpl::stopTimer()
+{
+	mTimer.stop();
+	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
+}
+
+bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event)
+{
+	if(mTimer.getStarted() && mTimer.hasExpired())
+	{
+		stopTimer();
+
+		// Check for failed install.
+		if(LLFile::isfile(ll_install_failed_marker_path()))
+		{
+			// TODO: notify the user.
+			llinfos << "found marker " << ll_install_failed_marker_path() << llendl;
+			llinfos << "last install attempt failed" << llendl;
+			LLFile::remove(ll_install_failed_marker_path());
+			
+			LLSD event;
+			event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR);
+			LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event);
+		}
+		else
+		{
+			mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion);
+		}
+	} 
+	else 
+	{
+		// Keep on waiting...
+	}
+	
+	return false;
+}
+
+
+//-----------------------------------------------------------------------
+// Facade interface
+
+std::string const & LLUpdaterService::pumpName(void)
+{
+	static std::string name("updater_service");
+	return name;
+}
+
+LLUpdaterService::LLUpdaterService()
+{
+	if(gUpdater.expired())
+	{
+		mImpl = 
+			boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl());
+		gUpdater = mImpl;
+	}
+	else
+	{
+		mImpl = gUpdater.lock();
+	}
+}
+
+LLUpdaterService::~LLUpdaterService()
+{
+}
+
+void LLUpdaterService::initialize(const std::string& protocol_version,
+								 const std::string& url, 
+								 const std::string& path,
+								 const std::string& channel,
+								 const std::string& version)
+{
+	mImpl->initialize(protocol_version, url, path, channel, version);
+}
+
+void LLUpdaterService::setCheckPeriod(unsigned int seconds)
+{
+	mImpl->setCheckPeriod(seconds);
+}
+	
+void LLUpdaterService::startChecking(bool install_if_ready)
+{
+	mImpl->startChecking(install_if_ready);
+}
+
+void LLUpdaterService::stopChecking()
+{
+	mImpl->stopChecking();
+}
+
+bool LLUpdaterService::isChecking()
+{
+	return mImpl->isChecking();
+}
+
+void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb)
+{
+	return mImpl->setAppExitCallback(aecb);
+}
+
+
+std::string const & ll_get_version(void) {
+	static std::string version("");
+	
+	if (version.empty()) {
+		std::ostringstream stream;
+		stream << LL_VERSION_MAJOR << "."
+		<< LL_VERSION_MINOR << "."
+		<< LL_VERSION_PATCH << "."
+		<< LL_VERSION_BUILD;
+		version = stream.str();
+	}
+	
+	return version;
+}
+
diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h
new file mode 100644
index 0000000000000000000000000000000000000000..752a6f834b4f93a7204a9e8573d30812f9d7de90
--- /dev/null
+++ b/indra/viewer_components/updater/llupdaterservice.h
@@ -0,0 +1,85 @@
+/** 
+ * @file llupdaterservice.h
+ *
+ * $LicenseInfo:firstyear=2010&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_UPDATERSERVICE_H
+#define LL_UPDATERSERVICE_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+class LLUpdaterServiceImpl;
+
+class LLUpdaterService
+{
+public:
+	class UsageError: public std::runtime_error
+	{
+	public:
+		UsageError(const std::string& msg) : std::runtime_error(msg) {}
+	};
+	
+	// Name of the event pump through which update events will be delivered.
+	static std::string const & pumpName(void);
+	
+	// Type codes for events posted by this service.  Stored the event's 'type' element.
+	enum eUpdateEvent {
+		INVALID,
+		DOWNLOAD_COMPLETE,
+		DOWNLOAD_ERROR,
+		INSTALL_ERROR
+	};
+
+	LLUpdaterService();
+	~LLUpdaterService();
+
+	void initialize(const std::string& protocol_version,
+				    const std::string& url, 
+				    const std::string& path,
+				    const std::string& channel,
+				    const std::string& version);
+
+	void setCheckPeriod(unsigned int seconds);
+	
+	void startChecking(bool install_if_ready = false);
+	void stopChecking();
+	bool isChecking();
+
+	typedef boost::function<void (void)> app_exit_callback_t;
+	template <typename F>
+	void setAppExitCallback(F const &callable) 
+	{ 
+		app_exit_callback_t aecb = callable;
+		setImplAppExitCallback(aecb);
+	}
+
+private:
+	boost::shared_ptr<LLUpdaterServiceImpl> mImpl;
+	void setImplAppExitCallback(app_exit_callback_t aecb);
+};
+
+// Returns the full version as a string.
+std::string const & ll_get_version(void);
+
+#endif // LL_UPDATERSERVICE_H
diff --git a/indra/viewer_components/updater/scripts/darwin/update_install b/indra/viewer_components/updater/scripts/darwin/update_install
new file mode 100644
index 0000000000000000000000000000000000000000..b174b3570a2bf4854c11d021d4523db931449a59
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/darwin/update_install
@@ -0,0 +1,9 @@
+#! /bin/bash
+
+#
+# The first argument contains the path to the installer app.  The second a path
+# to a marker file which should be created if the installer fails.q
+#
+
+open ../Resources/mac-updater.app --args -dmg "$1" -name "Second Life Viewer 2" -marker "$2"
+exit 0
diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install
new file mode 100644
index 0000000000000000000000000000000000000000..fef5ef7d09a95393d8e9bf8edf1a21fedd2e8416
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/linux/update_install
@@ -0,0 +1,10 @@
+#! /bin/bash
+INSTALL_DIR=$(cd "$(dirname $0)/.." ; pwd)
+export LD_LIBRARY_PATH=$INSTALL_DIR/lib
+bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer 2" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml"
+
+if [ $? -ne 0 ]
+   then touch $2
+fi
+
+rm -f $1
diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat
new file mode 100644
index 0000000000000000000000000000000000000000..42e148a707e11433798ac33e3d2e56e86acac394
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/windows/update_install.bat
@@ -0,0 +1,3 @@
+start /WAIT %1 /SKIP_DIALOGS
+IF ERRORLEVEL 1 ECHO %ERRORLEVEL% > %2
+DEL %1
diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..04ed4e63640b24aab81ef74db2960cfe5dc7cbdb
--- /dev/null
+++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp
@@ -0,0 +1,199 @@
+/**
+ * @file   llupdaterservice_test.cpp
+ * @brief  Tests of llupdaterservice.cpp.
+ * 
+ * $LicenseInfo:firstyear=2010&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$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "../llupdaterservice.h"
+#include "../llupdatechecker.h"
+#include "../llupdatedownloader.h"
+#include "../llupdateinstaller.h"
+
+#include "../../../test/lltut.h"
+//#define DEBUG_ON
+#include "../../../test/debug.h"
+
+#include "llevents.h"
+#include "lldir.h"
+
+/*****************************************************************************
+*   MOCK'd
+*****************************************************************************/
+LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client)
+{}
+void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, 
+								  std::string const & servicePath, std::string channel, std::string version)
+{}
+LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
+void LLUpdateDownloader::download(LLURI const & , std::string const &){}
+
+class LLDir_Mock : public LLDir
+{
+	void initAppDirs(const std::string &app_name, 
+		   			 const std::string& app_read_only_data_dir = "") {}
+	U32 countFilesInDir(const std::string &dirname, const std::string &mask) 
+	{
+		return 0;
+	}
+
+	BOOL getNextFileInDir(const std::string &dirname, 
+						  const std::string &mask, 
+						  std::string &fname) 
+	{
+		return false;
+	}
+	void getRandomFileInDir(const std::string &dirname, 
+							const std::string &mask, 
+							std::string &fname) {}
+	std::string getCurPath() { return ""; }
+	BOOL fileExists(const std::string &filename) const { return false; }
+	std::string getLLPluginLauncher() { return ""; }
+	std::string getLLPluginFilename(std::string base_name) { return ""; }
+
+} gDirUtil;
+LLDir* gDirUtilp = &gDirUtil;
+LLDir::LLDir() {}
+LLDir::~LLDir() {}
+S32 LLDir::deleteFilesInDir(const std::string &dirname, 
+							const std::string &mask)
+{ return 0; }
+
+void LLDir::setChatLogsDir(const std::string &path){}		
+void LLDir::setPerAccountChatLogsDir(const std::string &username){}
+void LLDir::setLindenUserDir(const std::string &username){}		
+void LLDir::setSkinFolder(const std::string &skin_folder){}
+bool LLDir::setCacheDir(const std::string &path){ return true; }
+void LLDir::dumpCurrentDirectories() {}
+
+std::string LLDir::getExpandedFilename(ELLPath location, 
+									   const std::string &filename) const 
+{
+	return "";
+}
+
+std::string LLUpdateDownloader::downloadMarkerPath(void)
+{
+	return "";
+}
+
+void LLUpdateDownloader::resume(void) {}
+void LLUpdateDownloader::cancel(void) {}
+
+int ll_install_update(std::string const &, std::string const &, LLInstallScriptMode)
+{
+	return 0;
+}
+
+std::string const & ll_install_failed_marker_path()
+{
+	static std::string wubba;
+	return wubba;
+}
+
+/*
+#pragma warning(disable: 4273)
+llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename,
+										   ios_base::openmode _Mode,
+										   int _Prot) :
+	std::basic_istream<char,std::char_traits< char > >(NULL,true)
+{}
+
+llus_mock_llifstream::~llus_mock_llifstream() {}
+bool llus_mock_llifstream::is_open() const {return true;}
+void llus_mock_llifstream::close() {}
+*/
+
+/*****************************************************************************
+*   TUT
+*****************************************************************************/
+namespace tut
+{
+    struct llupdaterservice_data
+    {
+		llupdaterservice_data() :
+            pumps(LLEventPumps::instance()),
+			test_url("dummy_url"),
+			test_channel("dummy_channel"),
+			test_version("dummy_version")
+		{}
+		LLEventPumps& pumps;
+		std::string test_url;
+		std::string test_channel;
+		std::string test_version;
+	};
+
+    typedef test_group<llupdaterservice_data> llupdaterservice_group;
+    typedef llupdaterservice_group::object llupdaterservice_object;
+    llupdaterservice_group llupdaterservicegrp("LLUpdaterService");
+
+    template<> template<>
+    void llupdaterservice_object::test<1>()
+    {
+        DEBUG;
+		LLUpdaterService updater;
+		bool got_usage_error = false;
+		try
+		{
+			updater.startChecking();
+		}
+		catch(LLUpdaterService::UsageError)
+		{
+			got_usage_error = true;
+		}
+		ensure("Caught start before params", got_usage_error);
+	}
+
+    template<> template<>
+    void llupdaterservice_object::test<2>()
+    {
+        DEBUG;
+		LLUpdaterService updater;
+		bool got_usage_error = false;
+		try
+		{
+			updater.initialize("1.0",test_url, "update" ,test_channel, test_version);
+			updater.startChecking();
+			updater.initialize("1.0", "other_url", "update", test_channel, test_version);
+		}
+		catch(LLUpdaterService::UsageError)
+		{
+			got_usage_error = true;
+		}
+		ensure("Caught params while running", got_usage_error);
+	}
+
+    template<> template<>
+    void llupdaterservice_object::test<3>()
+    {
+        DEBUG;
+		LLUpdaterService updater;
+		updater.initialize("1.0", test_url, "update", test_channel, test_version);
+		updater.startChecking();
+		ensure(updater.isChecking());
+		updater.stopChecking();
+		ensure(!updater.isChecking());
+	}
+}