This commit is contained in:
Lirusaito
2012-10-12 12:59:42 -04:00
175 changed files with 11294 additions and 8275 deletions

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@
/LICENSES/
/edited-files.txt
qtcreator-build/
/.pc

View File

@@ -46,7 +46,6 @@ endif(NOT STANDALONE)
add_custom_target(prepare DEPENDS ${prepare_depends})
add_subdirectory(cmake)
add_subdirectory(${LIBS_OPEN_PREFIX}cwdebug)
add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)
add_subdirectory(${LIBS_OPEN_PREFIX}llcommon)

View File

@@ -1,7 +1,7 @@
# -*- cmake -*-
include(Prebuilt)
set(CURL_FIND_QUIETLY ON)
set(CURL_FIND_QUIETLY OFF)
set(CURL_FIND_REQUIRED ON)
if (STANDALONE)

View File

@@ -1 +1,18 @@
set(CWDEBUG_LIBRARIES cwdebug)
include_directories (${CMAKE_SOURCE_DIR}/cwdebug)
set(cwdebug_SOURCE_FILES
${CMAKE_SOURCE_DIR}/cwdebug/debug.cc
)
set(cwdebug_HEADER_FILES
${CMAKE_SOURCE_DIR}/cwdebug/cwdebug.h
${CMAKE_SOURCE_DIR}/cwdebug/sys.h
${CMAKE_SOURCE_DIR}/cwdebug/debug.h
${CMAKE_SOURCE_DIR}/cwdebug/debug_ostream_operators.h
)
set_source_files_properties(${cwdebug_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES})

View File

@@ -1,39 +0,0 @@
# -*- cmake -*-
project(cwdebug)
include(00-Common)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLVFS)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
set(cwdebug_SOURCE_FILES
debug.cc
)
set(cwdebug_HEADER_FILES
CMakeLists.txt
cwdebug.h
sys.h
debug.h
debug_ostream_operators.h
)
set_source_files_properties(${cwdebug_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
if(NOT WORD_SIZE EQUAL 32)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT WORD_SIZE EQUAL 32)
list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES})
add_library (cwdebug ${cwdebug_SOURCE_FILES})

View File

@@ -173,6 +173,8 @@ void stop_recording_backtraces(void)
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
channel_ct curl DDCN("CURL"); //!< This debug channel is used for output related to Curl.
channel_ct curlio DDCN("CURLIO"); //!< This debug channel is used to print debug output of libcurl.
} // namespace dc
} // namespace DEBUGCHANNELS
@@ -411,4 +413,94 @@ void cwdebug_backtrace(int n)
}
#endif
#endif // CWDEBUG
#elif defined(DEBUG_CURLIO)
#include "debug.h"
#include "aithreadid.h"
namespace debug
{
namespace libcwd { libcwd_do_type const libcw_do; }
ll_thread_local int Indent::S_indentation;
Indent::Indent(int indent) : M_indent(indent)
{
S_indentation += M_indent;
}
Indent::~Indent()
{
S_indentation -= M_indent;
}
std::ostream& operator<<(std::ostream& os, Indent::print_nt)
{
if (Indent::S_indentation)
os << std::string(Indent::S_indentation, ' ');
return os;
}
#ifdef DEBUG_CURLIO
std::ostream& operator<<(std::ostream& os, print_thread_id_t)
{
if (!AIThreadID::in_main_thread_inline())
{
os << std::hex << (size_t)AIThreadID::getCurrentThread_inline() << std::dec << ' ';
}
return os;
}
#endif
std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s)
{
static char const c2s_tab[7] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
size_t size = b2s.mSize;
for (char const* p1 = b2s.mBuf; size > 0; --size, ++p1)
{
char c =*p1;
if ((c > 31 && c != 92 && c != 127) || (unsigned char)c > 159)
os.put(c);
else
{
os.put('\\');
if (c > 6 && c < 14)
{
os.put(c2s_tab[c - 7]);
return os;
}
else if (c == 27)
{
os.put('e');
return os;
}
else if (c == '\\')
{
os.put('\\');
return os;
}
std::ostream::char_type old_fill = os.fill('0');
std::ios_base::fmtflags old_flgs = os.flags();
os.width(3);
os << std::oct << (int)((unsigned char)c);
os.setf(old_flgs);
os.fill(old_fill);
}
}
return os;
}
namespace dc
{
fake_channel const warning(1, "WARNING ");
fake_channel const curl(1, "CURL ");
fake_channel const curlio(1, "CURLIO ");
fake_channel const statemachine(1, "STATEMACHINE");
fake_channel const notice(1, "NOTICE ");
} // namespace dc
} // namespace debug
#endif

View File

@@ -27,6 +27,94 @@
#ifndef CWDEBUG
#ifdef DEBUG_CURLIO
// If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace
// some of the cwd macro's with something that generates viewer
// specific debug output. Note that this generates a LOT of
// output and should not normally be defined.
#include <string>
#include "llpreprocessor.h"
namespace debug {
namespace libcwd {
struct buf2str {
buf2str(char const* buf, int size) : mBuf(buf), mSize(size) { }
char const* mBuf;
int mSize;
};
struct libcwd_do_type {
void on() const { }
};
extern LL_COMMON_API libcwd_do_type const libcw_do;
} // namespace libcwd
enum print_thread_id_t { print_thread_id };
inline void init() { }
struct Indent {
int M_indent;
static ll_thread_local int S_indentation;
enum LL_COMMON_API print_nt { print };
LL_COMMON_API Indent(int indent);
LL_COMMON_API ~Indent();
};
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s);
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, Indent::print_nt);
extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, print_thread_id_t);
namespace dc {
struct fake_channel {
int mOn;
char const* mLabel;
fake_channel(int on, char const* label) : mOn(on), mLabel(label) { }
fake_channel(void) : mOn(0) { }
bool is_on() const { return !!mOn; }
bool is_off() const { return !mOn; }
void on() const { }
void off() const { }
};
extern LL_COMMON_API fake_channel const warning;
extern LL_COMMON_API fake_channel const curl;
extern LL_COMMON_API fake_channel const curlio;
extern LL_COMMON_API fake_channel const statemachine;
extern LL_COMMON_API fake_channel const notice;
} // namespace dc
} // namespace debug
#define LIBCWD_DEBUG_CHANNELS debug
#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; using namespace debug::libcwd; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print;
#define LibcwDoutStream llcont
#define LibcwDoutScopeEnd llcont << llendl; } while(0)
#define Debug(x) do { using namespace debug; using namespace debug::libcwd; x; } while(0)
#define Dout(a, b) do { using namespace debug; using namespace debug::libcwd; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0)
#define DoutEntering(a, b) \
int __slviewer_debug_indentation = 2; \
{ \
using namespace debug; \
using namespace debug::libcwd; \
if ((a).mOn) \
llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \
else \
__slviewer_debug_indentation = 0; \
} \
debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation);
#else // !DEBUG_CURLIO
#define Debug(x)
#define Dout(a, b)
#define DoutEntering(a, b)
#endif // !DEBUG_CURLIO
#ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info.
#include <iostream>
@@ -36,9 +124,6 @@
#define AllocTag2(p, desc)
#define AllocTag_dynamic_description(p, x)
#define AllocTag(p, x)
#define Debug(x)
#define Dout(a, b)
#define DoutEntering(a, b)
#define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b)
#define ForAllDebugChannels(STATEMENT)
#define ForAllDebugObjects(STATEMENT)
@@ -118,6 +203,8 @@ extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace;
extern CWD_API channel_ct statemachine;
extern CWD_API channel_ct caps;
extern CWD_API channel_ct curl;
extern CWD_API channel_ct curlio;
#endif

View File

@@ -41,6 +41,7 @@
#include "lldir.h"
#include "llendianswizzle.h"
#include "llassetstorage.h"
#include "llrefcount.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisfile.h"

View File

@@ -16,6 +16,7 @@ include_directories(
set(llcommon_SOURCE_FILES
aiframetimer.cpp
aithreadid.cpp
imageids.cpp
indra_constants.cpp
llallocator.cpp
@@ -104,6 +105,7 @@ set(llcommon_HEADER_FILES
CMakeLists.txt
aiframetimer.h
aithreadid.h
aithreadsafe.h
bitpack.h
ctype_workaround.h
@@ -254,6 +256,7 @@ set(llcommon_HEADER_FILES
set_source_files_properties(${llcommon_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llcommon_SOURCE_FILES ${cwdebug_SOURCE_FILES})
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
@@ -266,15 +269,9 @@ target_link_libraries(
${ZLIB_LIBRARIES}
${WINDOWS_LIBRARIES}
${Boost_REGEX_LIBRARY}
${CWDEBUG_LIBRARIES}
${CORESERVICES_LIBRARY}
)
if (LINUX)
# When linking with llcommon later, we do not want to link with cwdebug.a again.
set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz")
endif (LINUX)
if (DARWIN)
# Don't embed a full path in the library's install name
set_target_properties(

View File

@@ -0,0 +1,78 @@
/**
* @file aithreadid.cpp
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 08/08/2012
* - Initial version, written by Aleric Inglewood @ SL
*/
#include <iostream>
#include <iomanip>
#include "aithreadid.h"
AIThreadID const AIThreadID::sNone(AIThreadID::none);
apr_os_thread_t AIThreadID::sMainThreadID;
apr_os_thread_t const AIThreadID::undefinedID = (apr_os_thread_t)-1;
#ifndef LL_DARWIN
apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread;
#endif
void AIThreadID::set_main_thread_id(void)
{
sMainThreadID = apr_os_thread_current();
}
void AIThreadID::set_current_thread_id(void)
{
#ifndef LL_DARWIN
lCurrentThread = apr_os_thread_current();
#endif
}
#ifndef LL_DARWIN
void AIThreadID::reset(void)
{
mID = lCurrentThread;
}
bool AIThreadID::equals_current_thread(void) const
{
return apr_os_thread_equal(mID, lCurrentThread);
}
bool AIThreadID::in_main_thread(void)
{
return apr_os_thread_equal(lCurrentThread, sMainThreadID);
}
apr_os_thread_t AIThreadID::getCurrentThread(void)
{
return lCurrentThread;
}
#endif
std::ostream& operator<<(std::ostream& os, AIThreadID const& id)
{
return os << id.mID;
}

View File

@@ -0,0 +1,94 @@
/**
* @file aithreadid.h
* @brief Declaration of AIThreadID.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 08/08/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AI_THREAD_ID
#define AI_THREAD_ID
#include <apr_portable.h> // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal().
#include <iosfwd> // std::ostream.
#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS
// Lightweight wrapper around apr_os_thread_t.
// This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety.
class AIThreadID
{
private:
apr_os_thread_t mID;
static LL_COMMON_API apr_os_thread_t sMainThreadID;
static LL_COMMON_API apr_os_thread_t const undefinedID;
#ifndef LL_DARWIN
static ll_thread_local apr_os_thread_t lCurrentThread;
#endif
public:
static LL_COMMON_API AIThreadID const sNone;
enum undefined_thread_t { none };
public:
AIThreadID(void) : mID(apr_os_thread_current()) { }
explicit AIThreadID(undefined_thread_t) : mID(undefinedID) { } // Used for sNone.
AIThreadID(AIThreadID const& id) : mID(id.mID) { }
AIThreadID& operator=(AIThreadID const& id) { mID = id.mID; return *this; }
bool is_main_thread(void) const { return apr_os_thread_equal(mID, sMainThreadID); }
bool is_no_thread(void) const { return apr_os_thread_equal(mID, sNone.mID); }
friend LL_COMMON_API bool operator==(AIThreadID const& id1, AIThreadID const& id2) { return apr_os_thread_equal(id1.mID, id2.mID); }
friend LL_COMMON_API bool operator!=(AIThreadID const& id1, AIThreadID const& id2) { return !apr_os_thread_equal(id1.mID, id2.mID); }
friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id);
static void set_main_thread_id(void); // Called once to set sMainThreadID.
static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread.
#ifndef LL_DARWIN
LL_COMMON_API void reset(void);
LL_COMMON_API bool equals_current_thread(void) const;
LL_COMMON_API static bool in_main_thread(void);
LL_COMMON_API static apr_os_thread_t getCurrentThread(void);
// The *_inline variants cannot be exported because they access a thread-local member.
void reset_inline(void) { mID = lCurrentThread; }
bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, lCurrentThread); }
static bool in_main_thread_inline(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); }
static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; }
#else
// Both variants are inline on OS X.
void reset(void) { mID = apr_os_thread_current(); }
void reset_inline(void) { mID = apr_os_thread_current(); }
bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); }
bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); }
static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); }
static bool in_main_thread_inline(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); }
static apr_os_thread_t getCurrentThread(void) { return apr_os_thread_current(); }
static apr_os_thread_t getCurrentThread_inline(void) { return apr_os_thread_current(); }
#endif
};
// Legacy function.
inline bool is_main_thread(void)
{
return AIThreadID::in_main_thread();
}
#endif // AI_THREAD_ID

View File

@@ -673,18 +673,18 @@ protected:
#ifdef LL_DEBUG
mutable bool mAccessed;
mutable apr_os_thread_t mTheadID;
mutable AIThreadID mTheadID;
void accessed(void) const
{
if (!mAccessed)
{
mAccessed = true;
mTheadID = apr_os_thread_current();
mTheadID.reset();
}
else
{
llassert_always(apr_os_thread_equal(mTheadID, apr_os_thread_current()));
llassert_always(mTheadID.equals_current_thread());
}
}
#endif

View File

@@ -297,6 +297,20 @@ void LLApp::startErrorThread()
}
}
void LLApp::stopErrorThread()
{
LLApp::setStopped(); // Signal error thread that we stopped.
int count = 0;
while (mThreadErrorp && !mThreadErrorp->isStopped() && ++count < 100)
{
ms_sleep(10);
}
if (mThreadErrorp && !mThreadErrorp->isStopped())
{
llwarns << "Failed to stop Error Thread." << llendl;
}
}
void LLApp::setErrorHandler(LLAppErrorHandler handler)
{
LLApp::sErrorHandler = handler;

View File

@@ -264,6 +264,10 @@ protected:
* @ brief This method is called once as soon as logging is initialized.
*/
void startErrorThread();
/**
* @brief This method is called at the end, just prior to deinitializing curl.
*/
void stopErrorThread();
private:
void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)

View File

@@ -60,10 +60,10 @@ void LLAPRPool::create(LLAPRPool& parent)
//
// In other words, it's safe for any thread to create a (sub)pool, independent of who
// owns the parent pool.
mOwner = apr_os_thread_current();
mOwner.reset_inline();
#else
mOwner = mParent->mOwner;
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread_inline());
#endif
apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
llassert_always(apr_pool_create_status == APR_SUCCESS);
@@ -83,7 +83,7 @@ void LLAPRPool::destroy(void)
// of course. Otherwise, if we are a subpool, only the thread that owns
// the parent may destruct us, since that is the pool that is still alive,
// possibly being used by others and being altered here.
llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
llassert(!mParent || mParent->mOwner.equals_current_thread_inline());
#endif
apr_pool_t* pool = mPool;
mPool = NULL; // Mark that we are BEING destructed.

View File

@@ -48,6 +48,7 @@
#include "apr_portable.h"
#include "apr_pools.h"
#include "llerror.h"
#include "aithreadid.h"
extern void ll_init_apr();
@@ -62,22 +63,22 @@ class LL_COMMON_API LLAPRPool
protected:
apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized.
LLAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero.
apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
AIThreadID mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
public:
//! Construct an uninitialized (destructed) pool.
LLAPRPool(void) : mPool(NULL) { }
LLAPRPool(void) : mPool(NULL), mOwner(AIThreadID::none) { }
//! Construct a subpool from an existing pool.
// This is not a copy-constructor, this class doesn't have one!
LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); }
LLAPRPool(LLAPRPool& parent) : mPool(NULL), mOwner(AIThreadID::none) { create(parent); }
//! Destruct the memory pool (free all of it's subpools and allocated memory).
~LLAPRPool() { destroy(); }
protected:
// Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool.
LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
LLAPRPool(int) : mPool(NULL), mParent(NULL)
{
apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
llassert_always(apr_pool_create_status == APR_SUCCESS);
@@ -104,7 +105,7 @@ public:
apr_pool_t* operator()(void) const
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return mPool;
}
@@ -112,7 +113,7 @@ public:
void clear(void)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
apr_pool_clear(mPool);
}
@@ -124,13 +125,13 @@ public:
void* palloc(size_t size)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return apr_palloc(mPool, size);
}
void* pcalloc(size_t size)
{
llassert(mPool);
llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
llassert(mOwner.equals_current_thread());
return apr_pcalloc(mPool, size);
}
#endif

View File

@@ -963,10 +963,14 @@ namespace LLError
settings_w->shouldLogCallCounter += 1;
std::string class_name = className(site.mClassInfo);
std::string function_name = functionName(site.mFunction);
if (site.mClassInfo != typeid(NoClassInfo))
std::string function_name;
if (site.mFunction)
{
function_name = class_name + "::" + function_name;
function_name = functionName(site.mFunction);
if (site.mClassInfo != typeid(NoClassInfo))
{
function_name = class_name + "::" + function_name;
}
}
ELevel compareLevel = settings_w->defaultLevel;
@@ -976,7 +980,7 @@ namespace LLError
// So, in increasing order of importance:
// Default < Broad Tag < File < Class < Function < Narrow Tag
((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)
|| (site.mFunction && checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel))
|| checkLevelMap(settings_w->classLevelMap, class_name, compareLevel)
|| checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false);
@@ -1083,8 +1087,8 @@ namespace LLError
default: prefix << "XXX"; break;
};
bool need_function = true;
if (site.mBroadTag && *site.mBroadTag != '\0')
bool need_function = site.mFunction;
if (need_function && site.mBroadTag && *site.mBroadTag != '\0')
{
prefix << "(\"" << site.mBroadTag << "\")";
#if LL_DEBUG
@@ -1112,7 +1116,7 @@ namespace LLError
#if LL_WINDOWS
// DevStudio: __FUNCTION__ already includes the full class name
#else
if (need_function && site.mClassInfo != typeid(NoClassInfo))
if (site.mClassInfo != typeid(NoClassInfo))
{
prefix << className(site.mClassInfo) << "::";
}

View File

@@ -238,10 +238,10 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
See top of file for common usage.
*/
#define lllog(level, broadTag, narrowTag, once) \
#define lllog(level, broadTag, narrowTag, once, nofunction) \
do { \
static LLError::CallSite _site( \
level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, broadTag, narrowTag, once);\
level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), nofunction ? NULL : __FUNCTION__, broadTag, narrowTag, once);\
if (LL_UNLIKELY(_site.shouldLog())) \
{ \
std::ostringstream* _out = LLError::Log::out(); \
@@ -255,33 +255,39 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
} while(0)
// DEPRECATED: Use the new macros that allow tags and *look* like macros.
#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false)
#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false)
#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false)
#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false)
#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, false)
#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false, false)
#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false, false)
#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false, false)
#define llcont (*_out)
// No function name
#define lldebugs_nf lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, true)
#define llinfos_nf lllog(LLError::LEVEL_INFO, NULL, NULL, false, true)
#define llwarns_nf lllog(LLError::LEVEL_WARN, NULL, NULL, false, true)
#define llerrs_nf lllog(LLError::LEVEL_ERROR, NULL, NULL, false, true)
// NEW Macros for debugging, allow the passing of a string tag
// One Tag
#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false)
#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false)
#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false)
#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false)
#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false, false)
#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false, false)
#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false, false)
#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false, false)
// Two Tags
#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false)
#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false)
#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false)
#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false)
#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false, false)
#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false, false)
#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false, false)
#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false, false)
// Only print the log message once (good for warnings or infos that would otherwise
// spam the log file over and over, such as tighter loops).
#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true)
#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true)
#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true)
#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true)
#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true)
#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true)
#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true, false)
#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true, false)
#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true, false)
#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true, false)
#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true, false)
#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true, false)
#define LL_ENDL llendl
#define LL_CONT (*_out)
@@ -302,8 +308,4 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
Such computation is done iff the message will be logged.
*/
#ifdef SHOW_ASSERT
extern LL_COMMON_API bool is_main_thread();
#endif
#endif // LL_LLERROR_H

View File

@@ -114,7 +114,7 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
: liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\
: std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<<liru_assert_strip<<" line:"<<__LINE__ << llendl;
#define llassert_always(func) do { if (LL_UNLIKELY(!(func))) llerrs << "\nASSERT(" #func ")\nfile:" << liru_assert_strip << " line:" << std::dec << __LINE__ << llendl; } while(0)
#ifdef SHOW_ASSERT
#define llassert(func) llassert_always(func)

View File

@@ -194,17 +194,17 @@ void LLErrorThread::run()
if (LLApp::isError())
{
// The app is in an error state, run the application's error handler.
//llinfos << "thread_error - An error has occurred, running error callback!" << llendl;
Dout(dc::notice, "thread_error - An error has occurred, running error callback!");
// Run the error handling callback
LLApp::runErrorHandler();
}
else
{
// Everything is okay, a clean exit.
//llinfos << "thread_error - Application exited cleanly" << llendl;
Dout(dc::notice, "thread_error - Application exited cleanly");
}
//llinfos << "thread_error - Exiting" << llendl;
Dout(dc::notice, "thread_error - Exiting");
LLApp::sErrorThreadRunning = FALSE;
}

View File

@@ -44,6 +44,9 @@
#include "lltimer.h"
#include "timing.h"
#include <apr_thread_mutex.h>
#ifdef SHOW_ASSERT
#include "aithreadid.h" // is_main_thread()
#endif
class LL_COMMON_API LLFrameTimer
{

View File

@@ -523,7 +523,6 @@ void LLPrivateMemoryPoolTester::operator delete[](void* addr)
//EVENTUALLY REMOVE THESE:
#include "llpointer.h"
#include "llrefcount.h"
#include "llsingleton.h"
#include "llsafehandle.h"

View File

@@ -195,4 +195,13 @@
# define LL_COMMON_API
#endif // LL_COMMON_LINK_SHARED
// Darwin does not support thread-local data.
#ifndef LL_DARWIN
#if LL_WINDOWS
#define ll_thread_local __declspec(thread)
#else // Linux
#define ll_thread_local __thread
#endif
#endif
#endif // not LL_LINDEN_PREPROCESSOR_H

View File

@@ -251,7 +251,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
llassert (handle != nullHandle())
llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
bool done = false;

View File

@@ -111,6 +111,8 @@ public:
return mPriority > second.mPriority;
}
virtual void deleteRequest(); // Only method to delete a request
protected:
status_t setStatus(status_t newstatus)
{
@@ -130,7 +132,6 @@ public:
virtual bool processRequest() = 0; // Return true when request has completed
virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
virtual void deleteRequest(); // Only method to delete a request
void setPriority(U32 pri)
{

View File

@@ -38,14 +38,6 @@ LLRefCount::LLRefCount(const LLRefCount& other)
: mRef(0)
{
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
mMutexp = new LLMutex(gAPRPoolp) ;
}
else
{
mMutexp = NULL ;
}
mCrashAtUnlock = FALSE ;
#endif
}
@@ -60,14 +52,6 @@ LLRefCount::LLRefCount() :
mRef(0)
{
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
mMutexp = new LLMutex(gAPRPoolp) ;
}
else
{
mMutexp = NULL ;
}
mCrashAtUnlock = FALSE ;
#endif
}
@@ -78,29 +62,20 @@ LLRefCount::~LLRefCount()
{
llerrs << "deleting non-zero reference" << llendl;
}
#if LL_REF_COUNT_DEBUG
if(gAPRPoolp)
{
delete mMutexp ;
}
#endif
}
#if LL_REF_COUNT_DEBUG
void LLRefCount::ref() const
{
if(mMutexp)
{
if(mMutexp->isLocked())
if(mMutex.isLocked())
{
mCrashAtUnlock = TRUE ;
llerrs << "the mutex is locked by the thread: " << mLockedThreadID
<< " Current thread: " << LLThread::currentID() << llendl ;
<< " Current thread: " << AIThreadID() << llendl ;
}
mMutexp->lock() ;
mLockedThreadID = LLThread::currentID() ;
mMutex.lock() ;
mLockedThreadID.reset_inline();
mRef++;
@@ -108,27 +83,20 @@ void LLRefCount::ref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
}
else
{
mRef++;
}
mMutex.unlock() ;
}
S32 LLRefCount::unref() const
{
if(mMutexp)
{
if(mMutexp->isLocked())
if(mMutex.isLocked())
{
mCrashAtUnlock = TRUE ;
llerrs << "the mutex is locked by the thread: " << mLockedThreadID
<< " Current thread: " << LLThread::currentID() << llendl ;
<< " Current thread: " << AIThreadID() << llendl ;
}
mMutexp->lock() ;
mLockedThreadID = LLThread::currentID() ;
mMutex.lock() ;
mLockedThreadID.reset_inline();
llassert(mRef >= 1);
if (0 == --mRef)
@@ -137,7 +105,7 @@ S32 LLRefCount::unref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
mMutex.unlock() ;
delete this;
return 0;
@@ -147,18 +115,7 @@ S32 LLRefCount::unref() const
{
while(1); //crash here.
}
mMutexp->unlock() ;
mMutex.unlock() ;
return mRef;
}
else
{
llassert(mRef >= 1);
if (0 == --mRef)
{
delete this;
return 0;
}
return mRef;
}
}
#endif

View File

@@ -30,7 +30,7 @@
#define LL_REF_COUNT_DEBUG 0
#if LL_REF_COUNT_DEBUG
class LLMutex ;
#include "llthread.h" // LLMutexRootPool
#endif
//----------------------------------------------------------------------------
@@ -80,8 +80,8 @@ private:
mutable S32 mRef;
#if LL_REF_COUNT_DEBUG
LLMutex* mMutexp ;
mutable U32 mLockedThreadID ;
mutable LLMutexRootPool mMutex ;
mutable AIThreadID mLockedThreadID ;
mutable BOOL mCrashAtUnlock ;
#endif
};

View File

@@ -29,6 +29,11 @@
* $/LicenseInfo$
*/
#ifdef __GNUC__
// Generate code for inlines from llthread.h (needed for is_main_thread()).
#pragma implementation "llthread.h"
#endif
#include "linden_common.h"
#include "llapr.h"
@@ -62,18 +67,12 @@
//
//----------------------------------------------------------------------------
#if !LL_DARWIN
U32 ll_thread_local local_thread_ID = 0;
#endif
U32 LLThread::sIDIter = 0;
LLAtomicS32 LLThread::sCount = 0;
LLAtomicS32 LLThread::sRunning = 0;
LL_COMMON_API void assert_main_thread()
{
static U32 s_thread_id = LLThread::currentID();
if (LLThread::currentID() != s_thread_id)
if (!AIThreadID::in_main_thread_inline())
{
llerrs << "Illegal execution outside main thread." << llendl;
}
@@ -90,9 +89,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
LLThread *threadp = (LLThread *)datap;
#if !LL_DARWIN
local_thread_ID = threadp->mID;
#endif
// Initialize thread-local cache of current thread ID (if supported).
AIThreadID::set_current_thread_id();
// Create a thread local data.
LLThreadLocalData::create(threadp);
@@ -132,7 +130,6 @@ LLThread::LLThread(std::string const& name) :
mStatus(STOPPED),
mThreadLocalData(NULL)
{
mID = ++sIDIter;
sCount++;
llassert(sCount <= 50);
mRunCondition = new LLCondition;
@@ -276,12 +273,6 @@ void LLThread::setQuitting()
wake();
}
// static
U32 LLThread::currentID()
{
return (U32)apr_os_thread_current();
}
// static
void LLThread::yield()
{
@@ -310,15 +301,19 @@ void LLThread::wakeLocked()
}
}
#ifdef SHOW_ASSERT
// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
static apr_os_thread_t main_thread_id;
LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); }
#endif
// The thread private handle to access the LLThreadLocalData instance.
apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey;
LLThreadLocalData::LLThreadLocalData(char const* name) : mCurlMultiHandle(NULL), mCurlErrorBuffer(NULL), mName(name)
{
}
LLThreadLocalData::~LLThreadLocalData()
{
delete mCurlMultiHandle;
delete [] mCurlErrorBuffer;
}
//static
void LLThreadLocalData::init(void)
{
@@ -328,6 +323,10 @@ void LLThreadLocalData::init(void)
return;
}
// This function is called by the main thread (these values are also needed in the next line).
AIThreadID::set_main_thread_id();
AIThreadID::set_current_thread_id();
apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()());
ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
// total number of keys per process {PTHREAD_KEYS_MAX}
@@ -335,11 +334,6 @@ void LLThreadLocalData::init(void)
// Create the thread-local data for the main thread (this function is called by the main thread).
LLThreadLocalData::create(NULL);
#ifdef SHOW_ASSERT
// This function is called by the main thread.
main_thread_id = apr_os_thread_current();
#endif
}
// This is called once for every thread when the thread is destructed.
@@ -352,7 +346,7 @@ void LLThreadLocalData::destroy(void* thread_local_data)
//static
void LLThreadLocalData::create(LLThread* threadp)
{
LLThreadLocalData* new_tld = new LLThreadLocalData;
LLThreadLocalData* new_tld = new LLThreadLocalData(threadp ? threadp->mName.c_str() : "main thread");
if (threadp)
{
threadp->mThreadLocalData = new_tld;
@@ -406,27 +400,19 @@ void LLCondition::broadcast()
//============================================================================
LLMutexBase::LLMutexBase() :
mLockingThread(NO_THREAD),
mLockingThread(AIThreadID::sNone),
mCount(0)
{
}
bool LLMutexBase::isSelfLocked() const
{
#if LL_DARWIN
return mLockingThread == LLThread::currentID();
#else
return mLockingThread == local_thread_ID;
#endif
return mLockingThread.equals_current_thread_inline();
}
void LLMutexBase::lock()
{
#if LL_DARWIN
if (mLockingThread == LLThread::currentID())
#else
if (mLockingThread == local_thread_ID)
#endif
if (mLockingThread.equals_current_thread_inline())
{ //redundant lock
mCount++;
return;
@@ -434,11 +420,33 @@ void LLMutexBase::lock()
apr_thread_mutex_lock(mAPRMutexp);
#if LL_DARWIN
mLockingThread = LLThread::currentID();
#else
mLockingThread = local_thread_ID;
#endif
mLockingThread.reset_inline();
}
bool LLMutexBase::tryLock()
{
if (mLockingThread.equals_current_thread_inline())
{ //redundant lock
mCount++;
return true;
}
bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp));
if (success)
{
mLockingThread.reset_inline();
}
return success;
}
// non-blocking, but does do a lock/unlock so not free
bool LLMutexBase::isLocked() const
{
if (mLockingThread.equals_current_thread_inline())
return false; // A call to lock() won't block.
if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)))
return true;
apr_thread_mutex_unlock(mAPRMutexp);
return false;
}
void LLMutexBase::unlock()
@@ -448,7 +456,7 @@ void LLMutexBase::unlock()
mCount--;
return;
}
mLockingThread = NO_THREAD;
mLockingThread = AIThreadID::sNone;
apr_thread_mutex_unlock(mAPRMutexp);
}

View File

@@ -33,29 +33,29 @@
#ifndef LL_LLTHREAD_H
#define LL_LLTHREAD_H
#ifdef __GNUC__
// Needed for is_main_thread() when compiling with optimization (relwithdebinfo).
// It doesn't hurt to just always specify it though.
#pragma interface
#endif
#include "llapp.h"
#include "llapr.h"
#include "llmemory.h"
#include "apr_thread_cond.h"
#include "llaprpool.h"
#include "llatomic.h"
#ifdef SHOW_ASSERT
extern LL_COMMON_API bool is_main_thread(void);
#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0)
#else
#define ASSERT_SINGLE_THREAD do { } while(0)
#endif
#include "aithreadid.h"
class LLThread;
class LLMutex;
class LLCondition;
#if LL_WINDOWS
#define ll_thread_local __declspec(thread)
#else
#define ll_thread_local __thread
#endif
class LL_COMMON_API LLThreadLocalDataMember
{
public:
virtual ~LLThreadLocalDataMember() { };
};
class LL_COMMON_API LLThreadLocalData
{
@@ -66,13 +66,23 @@ public:
// Thread-local memory pool.
LLAPRRootPool mRootPool;
LLVolatileAPRPool mVolatileAPRPool;
LLThreadLocalDataMember* mCurlMultiHandle; // Initialized by AICurlMultiHandle::getInstance
char* mCurlErrorBuffer; // NULL, or pointing to a buffer used by libcurl.
std::string mName; // "main thread", or a copy of LLThread::mName.
static void init(void);
static void destroy(void* thread_local_data);
static void create(LLThread* pthread);
static LLThreadLocalData& tldata(void);
private:
LLThreadLocalData(char const* name);
~LLThreadLocalData();
};
// Print to llerrs if the current thread is not the main thread.
LL_COMMON_API void assert_main_thread();
class LL_COMMON_API LLThread
{
private:
@@ -95,7 +105,6 @@ public:
bool isQuitting() const { return (QUITTING == mStatus); }
bool isStopped() const { return (STOPPED == mStatus); }
static U32 currentID(); // Return ID of current thread
static S32 getCount() { return sCount; }
static S32 getRunning() { return sRunning; }
static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
@@ -122,8 +131,6 @@ public:
// Return thread-local data for the current thread.
static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); }
U32 getID() const { return mID; }
private:
bool mPaused;
@@ -136,7 +143,6 @@ protected:
apr_thread_t *mAPRThreadp;
volatile EThreadStatus mStatus;
U32 mID;
friend void LLThreadLocalData::create(LLThread* threadp);
LLThreadLocalData* mThreadLocalData;
@@ -165,6 +171,12 @@ protected:
// mRunCondition->unlock();
};
#ifdef SHOW_ASSERT
#define ASSERT_SINGLE_THREAD do { static AIThreadID first_thread_id; llassert(first_thread_id.equals_current_thread()); } while(0)
#else
#define ASSERT_SINGLE_THREAD do { } while(0)
#endif
//============================================================================
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
@@ -180,32 +192,29 @@ protected:
class LL_COMMON_API LLMutexBase
{
public:
typedef enum
{
NO_THREAD = 0xFFFFFFFF
} e_locking_thread;
LLMutexBase() ;
void lock(); // blocks
void unlock();
// Returns true if lock was obtained successfully.
bool tryLock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); }
bool tryLock();
// non-blocking, but does do a lock/unlock so not free
bool isLocked() { bool is_not_locked = tryLock(); if (is_not_locked) unlock(); return !is_not_locked; }
// Returns true if a call to lock() would block (returns false if self-locked()).
bool isLocked() const;
// Returns true if locked by this thread.
bool isSelfLocked() const;
// get ID of locking thread
U32 lockingThread() const { return mLockingThread; }
protected:
// mAPRMutexp is initialized and uninitialized in the derived class.
apr_thread_mutex_t* mAPRMutexp;
mutable U32 mCount;
mutable U32 mLockingThread;
mutable AIThreadID mLockingThread;
private:
// Disallow copy construction and assignment.
LLMutexBase(LLMutexBase const&);
LLMutexBase& operator=(LLMutexBase const&);
};
class LL_COMMON_API LLMutex : public LLMutexBase
@@ -225,10 +234,6 @@ public:
protected:
LLAPRPool mPool;
private:
// Disable copy construction, as si teh bomb!!! -SG
LLMutex(const LLMutex&);
LLMutex& operator=(const LLMutex&);
};
#if APR_HAS_THREADS
@@ -289,7 +294,7 @@ private:
LLMutexBase* mMutex;
};
class AIRWLock
class LL_COMMON_API AIRWLock
{
public:
AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) :

View File

@@ -371,6 +371,9 @@ void LLCrashLogger::updateApplication(const std::string& message)
bool LLCrashLogger::init()
{
// Initialize curl
AICurlInterface::initCurl();
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");

View File

@@ -22,13 +22,11 @@ include_directories(
)
set(llimage_SOURCE_FILES
aes.cpp
llimagebmp.cpp
llimage.cpp
llimagedxt.cpp
llimagej2c.cpp
llimagejpeg.cpp
llimagemetadatareader.cpp
llimagepng.cpp
llimagetga.cpp
llimageworker.cpp
@@ -37,13 +35,11 @@ set(llimage_SOURCE_FILES
set(llimage_HEADER_FILES
CMakeLists.txt
aes.h
llimage.h
llimagebmp.h
llimagedxt.h
llimagej2c.h
llimagejpeg.h
llimagemetadatareader.h
llimagepng.h
llimagetga.h
llimageworker.h

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +0,0 @@
//Rijndael.h
#ifndef __RIJNDAEL_H__
#define __RIJNDAEL_H__
#include <exception>
#include <string>
#include <cstring>
using namespace std;
//Rijndael (pronounced Reindaal) is a block cipher, designed by Joan Daemen and Vincent Rijmen as a candidate algorithm for the AES.
//The cipher has a variable block length and key length. The authors currently specify how to use keys with a length
//of 128, 192, or 256 bits to encrypt blocks with al length of 128, 192 or 256 bits (all nine combinations of
//key length and block length are possible). Both block length and key length can be extended very easily to
// multiples of 32 bits.
//Rijndael can be implemented very efficiently on a wide range of processors and in hardware.
//This implementation is based on the Java Implementation used with the Cryptix toolkit found at:
//http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael.zip
//Java code authors: Raif S. Naffah, Paulo S. L. M. Barreto
//This Implementation was tested against KAT test published by the authors of the method and the
//results were identical.
class CRijndael
{
public:
//Operation Modes
//The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback Block (CFB) modes
//are implemented.
//In ECB mode if the same block is encrypted twice with the same key, the resulting
//ciphertext blocks are the same.
//In CBC Mode a ciphertext block is obtained by first xoring the
//plaintext block with the previous ciphertext block, and encrypting the resulting value.
//In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block
//and xoring the resulting value with the plaintext.
enum { ECB=0, CBC=1, CFB=2 };
private:
enum { DEFAULT_BLOCK_SIZE=16 };
enum { MAX_BLOCK_SIZE=32, MAX_ROUNDS=14, MAX_KC=8, MAX_BC=8 };
//Auxiliary Functions
//Multiply two elements of GF(2^m)
static int Mul(int a, int b)
{
return (a != 0 && b != 0) ? sm_alog[(sm_log[a & 0xFF] + sm_log[b & 0xFF]) % 255] : 0;
}
//Convenience method used in generating Transposition Boxes
static int Mul4(int a, char b[])
{
if(a == 0)
return 0;
a = sm_log[a & 0xFF];
int a0 = (b[0] != 0) ? sm_alog[(a + sm_log[b[0] & 0xFF]) % 255] & 0xFF : 0;
int a1 = (b[1] != 0) ? sm_alog[(a + sm_log[b[1] & 0xFF]) % 255] & 0xFF : 0;
int a2 = (b[2] != 0) ? sm_alog[(a + sm_log[b[2] & 0xFF]) % 255] & 0xFF : 0;
int a3 = (b[3] != 0) ? sm_alog[(a + sm_log[b[3] & 0xFF]) % 255] & 0xFF : 0;
return a0 << 24 | a1 << 16 | a2 << 8 | a3;
}
public:
//CONSTRUCTOR
CRijndael();
//DESTRUCTOR
virtual ~CRijndael();
//Expand a user-supplied key material into a session key.
// key - The 128/192/256-bit user-key to use.
// chain - initial chain block for CBC and CFB modes.
// keylength - 16, 24 or 32 bytes
// blockSize - The block size in bytes of this Rijndael (16, 24 or 32 bytes).
void MakeKey(char const* key, char const* chain, int keylength=DEFAULT_BLOCK_SIZE, int blockSize=DEFAULT_BLOCK_SIZE);
private:
//Auxiliary Function
void Xor(char* buff, char const* chain)
{
if(false==m_bKeyInit)
throw std::string(sm_szErrorMsg1);
for(int i=0; i<m_blockSize; i++)
*(buff++) ^= *(chain++);
}
//Convenience method to encrypt exactly one block of plaintext, assuming
//Rijndael's default block size (128-bit).
// in - The plaintext
// result - The ciphertext generated from a plaintext using the key
void DefEncryptBlock(char const* in, char* result);
//Convenience method to decrypt exactly one block of plaintext, assuming
//Rijndael's default block size (128-bit).
// in - The ciphertext.
// result - The plaintext generated from a ciphertext using the session key.
void DefDecryptBlock(char const* in, char* result);
public:
//Encrypt exactly one block of plaintext.
// in - The plaintext.
// result - The ciphertext generated from a plaintext using the key.
void EncryptBlock(char const* in, char* result);
//Decrypt exactly one block of ciphertext.
// in - The ciphertext.
// result - The plaintext generated from a ciphertext using the session key.
void DecryptBlock(char const* in, char* result);
void Encrypt(char const* in, char* result, size_t n, int iMode=ECB);
void Decrypt(char const* in, char* result, size_t n, int iMode=ECB);
//Get Key Length
int GetKeyLength()
{
if(false==m_bKeyInit)
throw std::string(sm_szErrorMsg1);
return m_keylength;
}
//Block Size
int GetBlockSize()
{
if(false==m_bKeyInit)
throw std::string(sm_szErrorMsg1);
return m_blockSize;
}
//Number of Rounds
int GetRounds()
{
if(false==m_bKeyInit)
throw std::string(sm_szErrorMsg1);
return m_iROUNDS;
}
void ResetChain()
{
memcpy(m_chain, m_chain0, m_blockSize);
}
public:
//Null chain
static char const* sm_chain0;
private:
static const int sm_alog[256];
static const int sm_log[256];
static const char sm_S[256];
static const char sm_Si[256];
static const int sm_T1[256];
static const int sm_T2[256];
static const int sm_T3[256];
static const int sm_T4[256];
static const int sm_T5[256];
static const int sm_T6[256];
static const int sm_T7[256];
static const int sm_T8[256];
static const int sm_U1[256];
static const int sm_U2[256];
static const int sm_U3[256];
static const int sm_U4[256];
static const char sm_rcon[30];
static const int sm_shifts[3][4][2];
//Error Messages
static char const* sm_szErrorMsg1;
static char const* sm_szErrorMsg2;
//Key Initialization Flag
bool m_bKeyInit;
//Encryption (m_Ke) round key
int m_Ke[MAX_ROUNDS+1][MAX_BC];
//Decryption (m_Kd) round key
int m_Kd[MAX_ROUNDS+1][MAX_BC];
//Key Length
int m_keylength;
//Block Size
int m_blockSize;
//Number of Rounds
int m_iROUNDS;
//Chain Block
char m_chain0[MAX_BLOCK_SIZE];
char m_chain[MAX_BLOCK_SIZE];
//Auxiliary private use buffers
int tk[MAX_KC];
int a[MAX_BC];
int t[MAX_BC];
};
#endif // __RIJNDAEL_H__

View File

@@ -1,213 +0,0 @@
// <edit>
#include "linden_common.h"
#include "llimagemetadatareader.h"
#include "aes.h"
//#include "llapr.h"
//#include "llerror.h"
const unsigned char EMKDU_AES_KEY[] = {0x01,0x00,0x81,0x07,0x63,0x78,0xB6,0xFE,0x6E,0x3F,0xB0,0x12,0xCC,0x65,0x66,0xC1,
0x81,0x96,0xAC,0xC1,0x3B,0x66,0x0B,0xF7};
//#define COMMENT_DEBUGG1ING
LLJ2cParser::LLJ2cParser(U8* data,int data_size)
{
if(data && data_size)
{
mData.resize(data_size);
memcpy(&(mData[0]), data, data_size);
//std::copy(data,data+data_size,mData.begin());
}
mIter = mData.begin();
}
U8 LLJ2cParser::nextChar()
{
U8 rtn = 0x00;
if(mIter != mData.end())
{
rtn = (*mIter);
mIter++;
}
return rtn;
}
std::vector<U8> LLJ2cParser::nextCharArray(int len)
{
std::vector<U8> array;
if(len > 0)
{
array.resize(len);
for(S32 i = 0; i < len; i++)
{
array[i] = nextChar();
}
}
return array;
}
std::vector<U8> LLJ2cParser::GetNextComment()
{
std::vector<U8> content;
while (mIter != mData.end())
{
U8 marker = nextChar();
if (marker == 0xff)
{
U8 marker_type = nextChar();
if (marker_type == 0x4f)
{
continue;
}
if (marker_type == 0x90)
{
//llinfos << "FOUND 0x90" << llendl;
break; //return empty vector
}
if (marker_type == 0x64)
{
//llinfos << "FOUND 0x64 COMMENT SECTION" << llendl;
S32 len = ((S32)nextChar())*256 + (S32)nextChar();
if (len > 3) content = nextCharArray(len - 2);
return content;
}
}
}
content.clear(); //return empty vector by clear anything there
return content;
}
//flow of control in this method is shit, gotta fix this... possibly return a vector or map instead of a string -HG
/*
Notes:
For anyone debugging this method, if a comment is not being decoded properly and you know encryption is being used,
the easiest thing to do is to create an LLAPRFile handle inside this method and write the contents of data to a file.
Normally the comment is going to be up near the header, just have a look at it in a hex editor.
It's generally going to be a string of 130 bytes preceeded by a null.
*/
//static
unsigned int LLImageMetaDataReader::ExtractEncodedComment(U8* data,int data_size, std::string& output)
{
LLJ2cParser parser = LLJ2cParser(data,data_size);
std::string decodedComment;
//not supported yet, but why the hell not?
unsigned int result = ENC_NONE;
while(1)
{
std::vector<U8> comment = parser.GetNextComment();
if (comment.empty()) break; //exit loop
if (comment[1] == 0x00 && comment.size() == 130)
{
bool xorComment = true;
//llinfos << "FOUND PAYLOAD" << llendl;
std::vector<U8> payload(128);
S32 i;
memcpy(&(payload[0]), &(comment[2]), 128);
//std::copy(comment.begin()+2,comment.end(),payload.begin());
//lets check xorComment Cipher first
if (payload[2] == payload[127])
{
// emkdu.dll
for (i = 4; i < 128; i += 4)
{
payload[i] ^= payload[3];
payload[i + 1] ^= payload[1];
payload[i + 2] ^= payload[0];
payload[i + 3] ^= payload[2];
}
result = ENC_EMKDU_V1;
}
else if (payload[3] == payload[127])
{
// emkdu.dll or onyxkdu.dll
for (i = 4; i < 128; i += 4)
{
payload[i] ^= payload[2];
payload[i + 1] ^= payload[0];
payload[i + 2] ^= payload[1];
payload[i + 3] ^= payload[3];
}
result = ENC_ONYXKDU;
}
else
{
xorComment = false;
}
if(!xorComment)
{
//this is terrible i know
std::vector<U8> decrypted(129);
CRijndael aes;
try
{
aes.MakeKey(reinterpret_cast<const char*>(EMKDU_AES_KEY),"", 24, 16);
} catch(std::string error)
{
llinfos << error << llendl;
}
try
{
int numBlocks = 8;
char* datain = (char*)&(payload[0]);
char* dataout = (char*)&(decrypted[0]);
char buffer[64];
memset(buffer,0,sizeof(buffer));
aes.DecryptBlock(datain,dataout); // do first block
for (int pos = 0; pos < 16; ++pos)
*dataout++ ^= buffer[pos];
datain += 16;
numBlocks--;
while (numBlocks)
{
aes.DecryptBlock(datain,dataout); // do next block
for (int pos = 0; pos < 16; ++pos)
*dataout++ ^= *(datain-16+pos);
datain += 16;
--numBlocks;
}
} catch(std::string error)
{
llinfos << error << llendl;
}
//payload.clear();
//memcpy(&(payload[0]),&(dataout[0]),dataout.size());
for (i = 0 ; i < 128; ++i)
{
if (decrypted[i] == 0) break;
}
if(i == 0) continue;
if(decodedComment.length() > 0)
decodedComment.append(", ");
//the way it's being done now, you can only specify the encryption type for the last comment.
//need to switch to a map<std::string, unsigned int> or a vector for output.
result = ENC_EMKDU_V2;
decodedComment.append(decrypted.begin(),decrypted.begin()+i);
}
else
{
for (i = 4 ; i < 128; ++i)
{
if (payload[i] == 0) break;
}
if(i < 4) continue;
if(decodedComment.length() > 0)
decodedComment.append(", ");
decodedComment.append(payload.begin()+4,payload.begin()+i);
}
//llinfos << "FOUND COMMENT: " << result << llendl;
}
}
//end of loop
output = decodedComment;
return result;
}
// </edit>

View File

@@ -1,32 +0,0 @@
// <edit>
#ifndef LL_LLIMAGEMETADATAREADER_H
#define LL_LLIMAGEMETADATAREADER_H
#include "stdtypes.h"
#include <string.h>
#include <vector>
#include <string>
//encryption types
#define ENC_NONE 0
#define ENC_ONYXKDU 1
#define ENC_EMKDU_V1 2
#define ENC_EMKDU_V2 4
class LLJ2cParser
{
public:
LLJ2cParser(U8* data,int data_size);
std::vector<U8> GetNextComment();
std::vector<U8> mData;
private:
U8 nextChar();
std::vector<U8> nextCharArray(int len);
std::vector<U8>::iterator mIter;
};
class LLImageMetaDataReader
{
public:
static unsigned int ExtractEncodedComment(U8* data,int data_size, std::string& output);
};
#endif
// </edit>

View File

@@ -32,7 +32,12 @@
#ifndef LL_LLPNGWRAPPER_H
#define LL_LLPNGWRAPPER_H
#ifdef LL_STANDALONE
#include <png.h>
#else
// Workaround for wrongly packaged prebuilt.
#include "libpng15/png.h"
#endif
#include "llimage.h"
class LLPngWrapper

View File

@@ -12,8 +12,7 @@ include_directories(
set(llmath_SOURCE_FILES
llbbox.cpp
llbboxlocal.cpp
llcalc.cpp
llcalcparser.cpp
llcalc.cpp
llcamera.cpp
llcoordframe.cpp
llline.cpp
@@ -48,8 +47,8 @@ set(llmath_HEADER_FILES
coordframe.h
llbbox.h
llbboxlocal.h
llcalc.h
llcalcparser.h
llcalc.h
llcalcparser.h
llcamera.h
llcoord.h
llcoordframe.h

View File

@@ -1,9 +1,26 @@
/*
* LLCalc.cpp
* SecondLife
*
* Created by Aimee Walton on 28/09/2008.
* Copyright 2008 Aimee Walton.
* Copyright 2008 Aimee Walton.
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2008, 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$
*
*/
@@ -11,59 +28,61 @@
#include "llcalc.h"
#include "llcalcparser.h"
#include "llmath.h"
#include "llcalcparser.h"
// Variable names for use in the build floater
const char* LLCalc::X_POS = "PX";
const char* LLCalc::Y_POS = "PY";
const char* LLCalc::Z_POS = "PZ";
const char* LLCalc::X_SCALE = "SX";
const char* LLCalc::Y_SCALE = "SY";
const char* LLCalc::Z_SCALE = "SZ";
const char* LLCalc::X_ROT = "RX";
const char* LLCalc::Y_ROT = "RY";
const char* LLCalc::Z_ROT = "RZ";
const char* LLCalc::HOLLOW = "HLW";
const char* LLCalc::CUT_BEGIN = "CB";
const char* LLCalc::CUT_END = "CE";
const char* LLCalc::PATH_BEGIN = "PB";
const char* LLCalc::PATH_END = "PE";
const char* LLCalc::TWIST_BEGIN = "TB";
const char* LLCalc::TWIST_END = "TE";
const char* LLCalc::X_SHEAR = "SHX";
const char* LLCalc::Y_SHEAR = "SHY";
const char* LLCalc::X_TAPER = "TPX";
const char* LLCalc::Y_TAPER = "TPY";
const char* LLCalc::RADIUS_OFFSET = "ROF";
const char* LLCalc::REVOLUTIONS = "REV";
const char* LLCalc::SKEW = "SKW";
const char* LLCalc::X_HOLE = "HLX";
const char* LLCalc::Y_HOLE = "HLY";
const char* LLCalc::TEX_U_SCALE = "TSU";
const char* LLCalc::TEX_V_SCALE = "TSV";
const char* LLCalc::TEX_U_OFFSET = "TOU";
const char* LLCalc::TEX_V_OFFSET = "TOV";
const char* LLCalc::TEX_ROTATION = "TROT";
const char* LLCalc::TEX_TRANSPARENCY = "TRNS";
const char* LLCalc::TEX_GLOW = "GLOW";
// must be lower case for parser definition
// case-insensitive for actual parsing
const char* LLCalc::X_POS = "px";
const char* LLCalc::Y_POS = "py";
const char* LLCalc::Z_POS = "pz";
const char* LLCalc::X_SCALE = "sx";
const char* LLCalc::Y_SCALE = "sy";
const char* LLCalc::Z_SCALE = "sz";
const char* LLCalc::X_ROT = "rx";
const char* LLCalc::Y_ROT = "ry";
const char* LLCalc::Z_ROT = "rz";
const char* LLCalc::HOLLOW = "hlw";
const char* LLCalc::CUT_BEGIN = "cb";
const char* LLCalc::CUT_END = "ce";
const char* LLCalc::PATH_BEGIN = "pb";
const char* LLCalc::PATH_END = "pe";
const char* LLCalc::TWIST_BEGIN = "tb";
const char* LLCalc::TWIST_END = "te";
const char* LLCalc::X_SHEAR = "shx";
const char* LLCalc::Y_SHEAR = "shy";
const char* LLCalc::X_TAPER = "tpx";
const char* LLCalc::Y_TAPER = "tpy";
const char* LLCalc::RADIUS_OFFSET = "rof";
const char* LLCalc::REVOLUTIONS = "rev";
const char* LLCalc::SKEW = "skw";
const char* LLCalc::X_HOLE = "hlx";
const char* LLCalc::Y_HOLE = "hly";
const char* LLCalc::TEX_U_SCALE = "tsu";
const char* LLCalc::TEX_V_SCALE = "tsv";
const char* LLCalc::TEX_U_OFFSET = "tou";
const char* LLCalc::TEX_V_OFFSET = "tov";
const char* LLCalc::TEX_ROTATION = "trot";
const char* LLCalc::TEX_TRANSPARENCY = "trns";
const char* LLCalc::TEX_GLOW = "glow";
LLCalc* LLCalc::sInstance = NULL;
//TODO: Make this a static global class
LLCalc::LLCalc() : mLastErrorPos(0)
{
// Init table of constants
mConstants["PI"] = F_PI;
mConstants["TWO_PI"] = F_TWO_PI;
mConstants["PI_BY_TWO"] = F_PI_BY_TWO;
mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI;
mConstants["SQRT2"] = F_SQRT2;
mConstants["SQRT3"] = F_SQRT3;
mConstants["DEG_TO_RAD"] = DEG_TO_RAD;
mConstants["RAD_TO_DEG"] = RAD_TO_DEG;
mConstants["GRAVITY"] = GRAVITY;
/*setVar("PI", F_PI);
setVar("TWO_PI", F_TWO_PI);
setVar("PI_BY_TWO", F_PI_BY_TWO);
setVar("SQRT_TWO_PI", F_SQRT_TWO_PI);
setVar("SQRT2", F_SQRT2);
setVar("SQRT3", F_SQRT3);
setVar("DEG_TO_RAD", DEG_TO_RAD);
setVar("RAD_TO_DEG", RAD_TO_DEG);
setVar("GRAVITY", GRAVITY);*/
}
LLCalc::~LLCalc()
@@ -80,7 +99,7 @@ void LLCalc::cleanUp()
//static
LLCalc* LLCalc::getInstance()
{
if (!sInstance) sInstance = new LLCalc();
if (!sInstance) sInstance = new LLCalc();
return sInstance;
}
@@ -99,47 +118,35 @@ void LLCalc::clearAllVariables()
mVariables.clear();
}
/*
void LLCalc::updateVariables(LLSD& vars)
{
LLSD::map_iterator cIt = vars.beginMap();
for(; cIt != vars.endMap(); cIt++)
{
setVar(cIt->first, (F32)(LLSD::Real)cIt->second);
}
}
*/
bool LLCalc::evalString(const std::string& expression, F32& result)
{
std::string expr_upper = expression;
LLStringUtil::toUpper(expr_upper);
LLCalcParser calc(result, &mConstants, &mVariables);
mLastErrorPos = 0;
std::string::iterator start = expr_upper.begin();
parse_info<std::string::iterator> info;
try
std::string::const_iterator itr = expression.begin();
expression::grammar<F32,std::string::const_iterator> calc;
calc.constant.add
("pi", F_PI)
("two_pi", F_TWO_PI)
("pi_by_two", F_PI_BY_TWO)
("sqrt_two_pi", F_SQRT_TWO_PI)
("sqrt2", F_SQRT2)
("sqrt3", F_SQRT3)
("deg_to_rad", DEG_TO_RAD)
("rad_to_deg", RAD_TO_DEG)
("gravity", GRAVITY)
;
for(calc_map_t::const_iterator iter = mVariables.begin();
iter != mVariables.end();
++iter)
{
info = parse(start, expr_upper.end(), calc, space_p);
lldebugs << "Math expression: " << expression << " = " << result << llendl;
calc.constant.add(iter->first, iter->second);
}
catch(parser_error<std::string, std::string::iterator> &e)
if (!expression::parse<F32,std::string::const_iterator>(itr, expression.end(), calc, result) || itr != expression.end())
{
mLastErrorPos = e.where - expr_upper.begin();
llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl;
return false;
}
if (!info.full)
{
mLastErrorPos = info.stop - expr_upper.begin();
mLastErrorPos = itr - expression.begin();
llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl;
return false;
}
lldebugs << "Math expression: " << expression << " = " << result << llendl;
return true;
}

View File

@@ -1,9 +1,26 @@
/*
* LLCalc.h
* SecondLife
*
* Created by Aimee Walton on 28/09/2008.
* Copyright 2008 Aimee Walton.
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2008, 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$
*
*/
@@ -69,13 +86,8 @@ public:
private:
std::string::size_type mLastErrorPos;
calc_map_t mConstants;
calc_map_t mVariables;
// *TODO: Add support for storing user defined variables, and stored functions.
// Will need UI work, and a means to save them between sessions.
// calc_map_t* mUserVariables;
// "There shall be only one"
static LLCalc* sInstance;
};

View File

@@ -27,165 +27,207 @@
#ifndef LL_CALCPARSER_H
#define LL_CALCPARSER_H
#include <boost/spirit/include/classic_attribute.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_error_handling.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/spirit/include/phoenix1_binders.hpp>
#include <boost/spirit/include/classic_symbols.hpp>
using namespace boost::spirit::classic;
#include <boost/spirit/version.hpp>
#if !defined(SPIRIT_VERSION) || SPIRIT_VERSION < 0x2010
#error "At least Spirit version 2.1 required"
#endif
#include "llcalc.h"
#include "llmath.h"
// Add this in if we want boost math constants.
//#include <boost/math/constants/constants.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
struct LLCalcParser : grammar<LLCalcParser>
namespace expression {
//TODO: If we can find a better way to do this with boost::pheonix::bind lets do it
namespace { // anonymous
template <typename T>
T min_glue(T a, T b)
{
LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) :
mResult(result), mConstants(constants), mVariables(vars) {};
struct value_closure : closure<value_closure, F32>
{
member1 value;
};
template <typename ScannerT>
struct definition
{
// Rule declarations
rule<ScannerT> statement, identifier;
rule<ScannerT, value_closure::context_t> expression, term,
power,
unary_expr,
factor,
unary_func,
binary_func,
group;
return std::min(a, b);
}
// start() should return the starting symbol
rule<ScannerT> const& start() const { return statement; }
definition(LLCalcParser const& self)
template <typename T>
T max_glue(T a, T b)
{
return std::max(a, b);
}
struct lazy_pow_
{
template <typename X, typename Y>
struct result { typedef X type; };
template <typename X, typename Y>
X operator()(X x, Y y) const
{
return std::pow(x, y);
}
};
struct lazy_ufunc_
{
template <typename F, typename A1>
struct result { typedef A1 type; };
template <typename F, typename A1>
A1 operator()(F f, A1 a1) const
{
return f(a1);
}
};
struct lazy_bfunc_
{
template <typename F, typename A1, typename A2>
struct result { typedef A1 type; };
template <typename F, typename A1, typename A2>
A1 operator()(F f, A1 a1, A2 a2) const
{
return f(a1, a2);
}
};
} // end namespace anonymous
template <typename FPT, typename Iterator>
struct grammar
: boost::spirit::qi::grammar<
Iterator, FPT(), boost::spirit::ascii::space_type
>
{
// symbol table for constants
// to be added by the actual calculator
struct constant_
: boost::spirit::qi::symbols<
typename std::iterator_traits<Iterator>::value_type,
FPT
>
{
constant_()
{
using namespace phoenix;
assertion<std::string> assert_domain("Domain error");
// assertion<std::string> assert_symbol("Unknown symbol");
assertion<std::string> assert_syntax("Syntax error");
identifier =
lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')]
;
group =
'(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')'))
;
unary_func =
((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) |
(str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) |
(str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) |
(str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) |
(str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) |
(str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) |
(str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) |
(str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) |
(str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) |
(str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) |
(str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) |
(str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)])
) >> assert_syntax(ch_p(')'))
;
binary_func =
((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) |
(str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) |
(str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)])
) >> assert_syntax(ch_p(')'))
;
// *TODO: Localisation of the decimal point?
// Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate
// for the current locale. However to do that here could clash with using
// the comma as a separator when passing arguments to functions.
factor =
(ureal_p[factor.value = arg1] |
group[factor.value = arg1] |
unary_func[factor.value = arg1] |
binary_func[factor.value = arg1] |
// Lookup throws an Unknown Symbol error if it is unknown, while this works fine,
// would be "neater" to handle symbol lookup from here with an assertive parser.
// constants_p[factor.value = arg1]|
identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)]
) >>
// Detect and throw math errors.
assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value)))
;
unary_expr =
!ch_p('+') >> factor[unary_expr.value = arg1] |
'-' >> factor[unary_expr.value = -arg1]
;
power =
unary_expr[power.value = arg1] >>
*('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)]))
;
term =
power[term.value = arg1] >>
*(('*' >> assert_syntax(power[term.value *= arg1])) |
('/' >> assert_syntax(power[term.value /= arg1])) |
('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)]))
)
;
expression =
assert_syntax(term[expression.value = arg1]) >>
*(('+' >> assert_syntax(term[expression.value += arg1])) |
('-' >> assert_syntax(term[expression.value -= arg1]))
)
;
statement =
!ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p)
}
} constant;
// symbol table for unary functions like "abs"
struct ufunc_
: boost::spirit::qi::symbols<
typename std::iterator_traits<Iterator>::value_type,
FPT (*)(FPT)
>
{
ufunc_()
{
this->add
("abs" , (FPT (*)(FPT)) std::abs )
("acos" , (FPT (*)(FPT)) std::acos )
("asin" , (FPT (*)(FPT)) std::asin )
("atan" , (FPT (*)(FPT)) std::atan )
("ceil" , (FPT (*)(FPT)) std::ceil )
("cos" , (FPT (*)(FPT)) std::cos )
("cosh" , (FPT (*)(FPT)) std::cosh )
("exp" , (FPT (*)(FPT)) std::exp )
("floor" , (FPT (*)(FPT)) std::floor)
("log" , (FPT (*)(FPT)) std::log )
("log10" , (FPT (*)(FPT)) std::log10)
("sin" , (FPT (*)(FPT)) std::sin )
("sinh" , (FPT (*)(FPT)) std::sinh )
("sqrt" , (FPT (*)(FPT)) std::sqrt )
("tan" , (FPT (*)(FPT)) std::tan )
("tanh" , (FPT (*)(FPT)) std::tanh )
;
}
};
private:
// Member functions for semantic actions
F32 lookup(const std::string::iterator&, const std::string::iterator&) const;
F32 _min(const F32& a, const F32& b) const { return llmin(a, b); }
F32 _max(const F32& a, const F32& b) const { return llmax(a, b); }
bool checkNaN(const F32& a) const { return !llisnan(a); }
//FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz
F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); }
F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); }
F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); }
F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); }
F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); }
F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); }
F32 _sqrt(const F32& a) const { return sqrt(a); }
F32 _log(const F32& a) const { return log(a); }
F32 _exp(const F32& a) const { return exp(a); }
F32 _fabs(const F32& a) const { return fabs(a); }
F32 _floor(const F32& a) const { return (F32)llfloor(a); }
F32 _ceil(const F32& a) const { return llceil(a); }
} ufunc;
F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); }
LLCalc::calc_map_t* mConstants;
LLCalc::calc_map_t* mVariables;
// LLCalc::calc_map_t* mUserVariables;
F32& mResult;
// symbol table for binary functions like "pow"
struct bfunc_
: boost::spirit::qi::symbols<
typename std::iterator_traits<Iterator>::value_type,
FPT (*)(FPT, FPT)
>
{
bfunc_()
{
using boost::bind;
this->add
("pow" , (FPT (*)(FPT, FPT)) std::pow )
("atan2", (FPT (*)(FPT, FPT)) std::atan2)
("min" , (FPT (*)(FPT, FPT)) min_glue)
("max" , (FPT (*)(FPT, FPT)) max_glue)
;
}
} bfunc;
boost::spirit::qi::rule<
Iterator, FPT(), boost::spirit::ascii::space_type
> expression, term, factor, primary;
grammar() : grammar::base_type(expression)
{
using boost::spirit::qi::real_parser;
using boost::spirit::qi::real_policies;
real_parser<FPT,real_policies<FPT> > real;
using boost::spirit::qi::_1;
using boost::spirit::qi::_2;
using boost::spirit::qi::_3;
using boost::spirit::qi::no_case;
using boost::spirit::qi::_val;
boost::phoenix::function<lazy_pow_> lazy_pow;
boost::phoenix::function<lazy_ufunc_> lazy_ufunc;
boost::phoenix::function<lazy_bfunc_> lazy_bfunc;
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
)
;
factor =
primary [_val = _1]
>> *( ("**" >> factor [_val = lazy_pow(_val, _1)])
)
;
primary =
real [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> primary [_val = -_1])
| ('+' >> primary [_val = _1])
| (no_case[ufunc] >> '(' >> expression >> ')')
[_val = lazy_ufunc(_1, _2)]
| (no_case[bfunc] >> '(' >> expression >> ','
>> expression >> ')')
[_val = lazy_bfunc(_1, _2, _3)]
| no_case[constant] [_val = _1]
;
}
};
template <typename FPT, typename Iterator>
bool parse(Iterator &iter,
Iterator end,
const grammar<FPT,Iterator> &g,
FPT &result)
{
return boost::spirit::qi::phrase_parse(
iter, end, g, boost::spirit::ascii::space, result);
}
} // end namespace expression
#endif // LL_CALCPARSER_H

View File

@@ -29,7 +29,9 @@ set(llmessage_SOURCE_FILES
llchainio.cpp
llcircuit.cpp
llclassifiedflags.cpp
llcurl.cpp
aicurl.cpp
debug_libcurl.cpp
aicurlthread.cpp
lldatapacker.cpp
lldispatcher.cpp
llfiltersd2xmlrpc.cpp
@@ -66,8 +68,6 @@ set(llmessage_SOURCE_FILES
llsdmessage.cpp
llsdmessagebuilder.cpp
llsdmessagereader.cpp
llsdrpcclient.cpp
llsdrpcserver.cpp
llservicebuilder.cpp
llservice.cpp
llstoredmessage.cpp
@@ -117,6 +117,10 @@ set(llmessage_HEADER_FILES
llcircuit.h
llclassifiedflags.h
llcurl.h
aicurl.h
debug_libcurl.h
aicurlprivate.h
aicurlthread.h
lldatapacker.h
lldbstrings.h
lldispatcher.h
@@ -164,8 +168,6 @@ set(llmessage_HEADER_FILES
llsdmessage.h
llsdmessagebuilder.h
llsdmessagereader.h
llsdrpcclient.h
llsdrpcserver.h
llservice.h
llservicebuilder.h
llstoredmessage.h

1502
indra/llmessage/aicurl.cpp Normal file

File diff suppressed because it is too large Load Diff

343
indra/llmessage/aicurl.h Normal file
View File

@@ -0,0 +1,343 @@
/**
* @file aicurl.h
* @brief Thread safe wrapper for libcurl.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 17/03/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURL_H
#define AICURL_H
#include <string>
#include <vector>
#include <set>
#include <stdexcept>
#include <boost/intrusive_ptr.hpp>
#include <boost/utility.hpp>
#include "llpreprocessor.h"
#include <curl/curl.h> // Needed for files that include this header (also for aicurlprivate.h).
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// Make sure we don't use this option: it is not thread-safe.
#undef CURLOPT_DNS_USE_GLOBAL_CACHE
#define CURLOPT_DNS_USE_GLOBAL_CACHE do_not_use_CURLOPT_DNS_USE_GLOBAL_CACHE
#include "stdtypes.h" // U32
#include "lliopipe.h" // LLIOPipe::buffer_ptr_t
#include "llatomic.h" // LLAtomicU32
#include "aithreadsafe.h"
class LLSD;
//-----------------------------------------------------------------------------
// Exceptions.
//
// A general curl exception.
//
class AICurlError : public std::runtime_error {
public:
AICurlError(std::string const& message) : std::runtime_error(message) { }
};
class AICurlNoEasyHandle : public AICurlError {
public:
AICurlNoEasyHandle(std::string const& message) : AICurlError(message) { }
};
class AICurlNoMultiHandle : public AICurlError {
public:
AICurlNoMultiHandle(std::string const& message) : AICurlError(message) { }
};
// End Exceptions.
//-----------------------------------------------------------------------------
// Things defined in this namespace are called from elsewhere in the viewer code.
namespace AICurlInterface {
// Output parameter of AICurlPrivate::CurlEasyRequest::getResult.
// Only used by LLXMLRPCTransaction::Impl.
struct TransferInfo {
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) { }
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
//-----------------------------------------------------------------------------
// Global functions.
// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)),
// with main purpose to initialize curl.
void initCurl(void (*)(void) = NULL);
// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread.
void startCurlThread(void);
// Called once at end of application (from newview/llappviewer.cpp by main thread),
// with purpose to stop curl threads, free curl resources and deinitialize curl.
void cleanupCurl(void);
// Called from indra/llmessage/llurlrequest.cpp to print debug output regarding
// an error code returned by EasyRequest::getResult.
// Just returns curl_easy_strerror(errorcode).
std::string strerror(CURLcode errorcode);
// Called from indra/newview/llfloaterabout.cpp for the About floater, and
// from newview/llappviewer.cpp in behalf of debug output.
// Just returns curl_version().
std::string getVersionString(void);
// Called from newview/llappviewer.cpp (and llcrashlogger/llcrashlogger.cpp) to set
// the Certificate Authority file used to verify HTTPS certs.
void setCAFile(std::string const& file);
// Not called from anywhere.
// Can be used to set the path to the Certificate Authority file.
void setCAPath(std::string const& file);
//-----------------------------------------------------------------------------
// Global classes.
// Responder - base class for Request::get* and Request::post API.
//
// The life cycle of classes derived from this class is as follows:
// They are allocated with new on the line where get(), getByteRange() or post() is called,
// and the pointer to the allocated object is then put in a reference counting ResponderPtr.
// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its
// member mResponder. Hence, the life time of a Responder is never longer than its
// associated CurlResponderBuffer, however, if everything works correctly, then normally a
// responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting
// mReponder to NULL.
//
// Note that the lifetime of CurlResponderBuffer is (a bit) shorter than the associated
// CurlEasyRequest (because of the order of base classes of ThreadSafeBufferedCurlEasyRequest)
// and the callbacks, as set by prepRequest, only use those two.
// A callback locks the CurlEasyRequest before actually making the callback, and the
// destruction of CurlResponderBuffer also first locks the CurlEasyRequest, and then revokes
// the callbacks. This assures that a Responder is never used when the objects it uses are
// destructed. Also, if any of those are destructed then the Responder is automatically
// destructed too.
//
class Responder {
protected:
Responder(void);
virtual ~Responder();
private:
// Associated URL, used for debug output.
std::string mURL;
public:
// Called to set the URL of the current request for this Responder,
// used only when printing debug output regarding activity of the Responder.
void setURL(std::string const& url);
public:
// Called from LLHTTPClientURLAdaptor::complete():
// Derived classes can override this to get the HTML header that was received, when the message is completed.
// The default does nothing.
virtual void completedHeader(U32 status, std::string const& reason, LLSD const& content);
// Derived classes can override this to get the raw data of the body of the HTML message that was received.
// The default is to interpret the content as LLSD and call completed().
virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer);
// Called from LLHTTPClient request calls, if an error occurs even before we can call one of the above.
// It calls completed() with a fake status U32_MAX, as that is what some derived clients expect (bad design).
// This means that if a derived class overrides completedRaw() it now STILL has to override completed() to catch this error.
void fatalError(std::string const& reason);
// A derived class should return true if curl should follow redirections.
// The default is not to follow redirections.
virtual bool followRedir(void) { return false; }
protected:
// ... or, derived classes can override this to get the LLSD content when the message is completed.
// The default is to call result() (or errorWithContent() in case of a HTML status indicating an error).
virtual void completed(U32 status, std::string const& reason, LLSD const& content);
// ... or, derived classes can override this to received the content of a body upon success.
// The default does nothing.
virtual void result(LLSD const& content);
// Derived classes can override this to get informed when a bad HTML status code is received.
// The default calls error().
virtual void errorWithContent(U32 status, std::string const& reason, LLSD const& content);
// ... or, derived classes can override this to get informed when a bad HTML statis code is received.
// The default prints the error to llinfos.
virtual void error(U32 status, std::string const& reason);
public:
// Called from LLSDMessage::ResponderAdapter::listener.
// LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these functions need to be public.
void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); }
void pubResult(LLSD const& content) { result(content); }
private:
// Used by ResponderPtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(Responder* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<Responder> is made.
friend void intrusive_ptr_release(Responder* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<Responder> is destroyed.
// This function must delete the Responder object when the reference count reaches zero.
};
// A Responder is passed around as ResponderPtr, which causes it to automatically
// destruct when there are no pointers left pointing to it.
typedef boost::intrusive_ptr<Responder> ResponderPtr;
} // namespace AICurlInterface
// Forward declaration (see aicurlprivate.h).
namespace AICurlPrivate {
class CurlEasyRequest;
} // namespace AICurlPrivate
// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type).
// Typical usage is:
// AICurlEasyRequest h1; // Create easy handle.
// AICurlEasyRequest h2(h1); // Make lightweight copies.
// AICurlEasyRequest_wat h2_w(*h2); // Lock and obtain write access to the easy handle.
// Use *h2_w, which is a reference to the locked CurlEasyRequest instance.
// Note: As it is not allowed to use curl easy handles in any way concurrently,
// read access would at most give access to a CURL const*, which will turn out
// to be completely useless; therefore it is sufficient and efficient to use
// an AIThreadSafeSimple and it's unlikely that AICurlEasyRequest_rat will be used.
typedef AIAccessConst<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_rat;
typedef AIAccess<AICurlPrivate::CurlEasyRequest> AICurlEasyRequest_wat;
// Events generated by AICurlPrivate::CurlEasyHandle.
struct AICurlEasyHandleEvents {
// Events.
virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
virtual void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0;
// Avoid compiler warning.
virtual ~AICurlEasyHandleEvents() { }
};
// Pointer to data we're going to POST.
class AIPostField : public LLThreadSafeRefCount {
protected:
char const* mData;
public:
AIPostField(char const* data) : mData(data) { }
char const* data(void) const { return mData; }
};
// The pointer to the data that we have to POST is passed around as AIPostFieldPtr,
// which causes it to automatically clean up when there are no pointers left
// pointing to it.
typedef LLPointer<AIPostField> AIPostFieldPtr;
#include "aicurlprivate.h"
// AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a
// builtin type, but wrapping it in AIThreadSafe is obviously not going to help here.
// Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow
// read accesses on it.
// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle.
class AICurlEasyRequest {
public:
// Initial construction is allowed (thread-safe).
// Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed.
// 'new' never returned however and neither the constructor nor destructor of mCurlEasyRequest is called in this case.
// This might throw AICurlNoEasyHandle.
AICurlEasyRequest(bool buffered) :
mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { }
AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { }
// For the rest, only allow read operations.
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; }
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); }
AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>* get(void) const { return mCurlEasyRequest.get(); }
// Returns true if this object points to the same CurlEasyRequest object.
bool operator==(AICurlEasyRequest const& cer) const { return mCurlEasyRequest == cer.mCurlEasyRequest; }
// Returns true if this object points to a different CurlEasyRequest object.
bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; }
// Queue this request for insertion in the multi session.
void addRequest(void);
// Queue a command to remove this request from the multi session (or cancel a queued command to add it).
void removeRequest(void);
// Returns true when this AICurlEasyRequest wraps a AICurlPrivate::ThreadSafeBufferedCurlEasyRequest.
bool isBuffered(void) const { return mCurlEasyRequest->isBuffered(); }
private:
// The actual pointer to the ThreadSafeCurlEasyRequest instance.
AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest;
private:
// Assignment would not be thread-safe; we may create this object and read from it.
// Note: Destruction is implicitly assumed thread-safe, as it would be a logic error to
// destruct it while another thread still needs it, concurrent or not.
AICurlEasyRequest& operator=(AICurlEasyRequest const&) { return *this; }
public:
// The more exotic member functions of this class, to deal with passing this class
// as CURLOPT_PRIVATE pointer to a curl handle and afterwards restore it.
// For "internal use" only; don't use things from AICurlPrivate yourself.
// It's thread-safe to give read access the underlaying boost::intrusive_ptr.
// It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeCurlEasyRequest* separately.
AICurlPrivate::CurlEasyRequestPtr const& get_ptr(void) const { return mCurlEasyRequest; }
// If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr,
// then it's OK to construct a AICurlEasyRequest from it.
// Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because
// it's not thread-safe in itself.
AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { }
// This one is obviously dangerous. It's for use only in MultiHandle::check_run_count.
// See also the long comment in CurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE.
explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeCurlEasyRequest* ptr) : mCurlEasyRequest(ptr) { }
};
// Write Access Type for the buffer.
struct AICurlResponderBuffer_wat : public AIAccess<AICurlPrivate::CurlResponderBuffer> {
explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) :
AIAccess<AICurlPrivate::CurlResponderBuffer>(lockobj) { }
AICurlResponderBuffer_wat(AIThreadSafeSimple<AICurlPrivate::CurlEasyRequest>& lockobj) :
AIAccess<AICurlPrivate::CurlResponderBuffer>(static_cast<AICurlPrivate::ThreadSafeBufferedCurlEasyRequest&>(lockobj)) { }
};
#define AICurlPrivate DONTUSE_AICurlPrivate
#endif

View File

@@ -0,0 +1,478 @@
/**
* @file aicurlprivate.h
* @brief Thread safe wrapper for libcurl.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 28/04/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLPRIVATE_H
#define AICURLPRIVATE_H
#include <sstream>
#include "llatomic.h"
namespace AICurlPrivate {
namespace curlthread { class MultiHandle; }
struct Stats {
static LLAtomicU32 easy_calls;
static LLAtomicU32 easy_errors;
static LLAtomicU32 easy_init_calls;
static LLAtomicU32 easy_init_errors;
static LLAtomicU32 easy_cleanup_calls;
static LLAtomicU32 multi_calls;
static LLAtomicU32 multi_errors;
static void print(void);
};
void handle_multi_error(CURLMcode code);
inline CURLMcode check_multi_code(CURLMcode code) { Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; }
bool curlThreadIsRunning(void);
void wakeUpCurlThread(void);
void stopCurlThread(void);
class ThreadSafeCurlEasyRequest;
class ThreadSafeBufferedCurlEasyRequest;
#define DECLARE_SETOPT(param_type) \
CURLcode setopt(CURLoption option, param_type parameter)
// This class wraps CURL*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEvents {
public:
CurlEasyHandle(void);
~CurlEasyHandle();
private:
// Disallow assignment.
CurlEasyHandle& operator=(CurlEasyHandle const*);
public:
// Reset all options of a libcurl session handle.
void reset(void) { llassert(!mActiveMultiHandle); curl_easy_reset(mEasyHandle); }
// Set options for a curl easy handle.
DECLARE_SETOPT(long);
DECLARE_SETOPT(long long);
DECLARE_SETOPT(void const*);
DECLARE_SETOPT(curl_debug_callback);
DECLARE_SETOPT(curl_write_callback);
//DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback
DECLARE_SETOPT(curl_ssl_ctx_callback);
DECLARE_SETOPT(curl_conv_callback);
#if 0 // Not used by the viewer.
DECLARE_SETOPT(curl_progress_callback);
DECLARE_SETOPT(curl_seek_callback);
DECLARE_SETOPT(curl_ioctl_callback);
DECLARE_SETOPT(curl_sockopt_callback);
DECLARE_SETOPT(curl_opensocket_callback);
DECLARE_SETOPT(curl_closesocket_callback);
DECLARE_SETOPT(curl_sshkeycallback);
DECLARE_SETOPT(curl_chunk_bgn_callback);
DECLARE_SETOPT(curl_chunk_end_callback);
DECLARE_SETOPT(curl_fnmatch_callback);
#endif
// Automatically cast int types to a long. Note that U32/S32 are int and
// that you can overload int and long even if they have the same size.
CURLcode setopt(CURLoption option, U32 parameter) { return setopt(option, (long)parameter); }
CURLcode setopt(CURLoption option, S32 parameter) { return setopt(option, (long)parameter); }
// Clone a libcurl session handle using all the options previously set.
//CurlEasyHandle(CurlEasyHandle const& orig);
// URL encode/decode the given string.
char* escape(char* url, int length);
char* unescape(char* url, int inlength , int* outlength);
// Extract information from a curl handle.
private:
CURLcode getinfo_priv(CURLINFO info, void* data);
public:
// The rest are inlines to provide some type-safety.
CURLcode getinfo(CURLINFO info, char** data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, curl_slist** data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, double* data) { return getinfo_priv(info, data); }
CURLcode getinfo(CURLINFO info, long* data) { return getinfo_priv(info, data); }
#ifdef __LP64__ // sizeof(long) > sizeof(int) ?
// Overload for integer types that are too small (libcurl demands a long).
CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<S32>(ldata); return res; }
CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast<U32>(ldata); return res; }
#else // sizeof(long) == sizeof(int)
CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, reinterpret_cast<long*>(data)); }
#endif
// Perform a file transfer (blocking).
CURLcode perform(void);
// Pause and unpause a connection.
CURLcode pause(int bitmask);
// Called when a request is queued for removal. In that case a race between the actual removal
// and revoking of the callbacks is harmless (and happens for the raw non-statemachine version).
void remove_queued(void) { mQueuedForRemoval = true; }
// In case it's added after being removed.
void add_queued(void) { mQueuedForRemoval = false; }
private:
CURL* mEasyHandle;
CURLM* mActiveMultiHandle;
char* mErrorBuffer;
AIPostFieldPtr mPostField; // This keeps the POSTFIELD data alive for as long as the easy handle exists.
bool mQueuedForRemoval; // Set if the easy handle is (probably) added to the multi handle, but is queued for removal.
#ifdef SHOW_ASSERT
public:
bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread.
#endif
private:
// This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle.
friend class curlthread::MultiHandle;
CURLMcode add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle);
CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle);
public:
// Returns true if this easy handle was added to a curl multi handle.
bool active(void) const { return mActiveMultiHandle; }
// If there was an error code as result, then this returns a human readable error string.
// Only valid when setErrorBuffer was called and the curl_easy function returned an error.
std::string getErrorString(void) const { return mErrorBuffer ? mErrorBuffer : "(null)"; }
// Returns true when it is expected that the parent will revoke callbacks before the curl
// easy handle is removed from the multi handle; that usually happens when an external
// error demands termination of the request (ie, an expiration).
bool no_warning(void) const { return mQueuedForRemoval || LLApp::isExiting(); }
// Used for debugging purposes.
bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; }
private:
// Call this prior to every curl_easy function whose return value is passed to check_easy_code.
void setErrorBuffer(void);
static void handle_easy_error(CURLcode code);
// Always first call setErrorBuffer()!
static inline CURLcode check_easy_code(CURLcode code)
{
Stats::easy_calls++;
if (code != CURLE_OK)
handle_easy_error(code);
return code;
}
protected:
// Return the underlying curl easy handle.
CURL* getEasyHandle(void) const { return mEasyHandle; }
// Keep POSTFIELD data alive.
void setPostField(AIPostFieldPtr const& post_field_ptr) { mPostField = post_field_ptr; }
private:
// Return, and possibly create, the curl (easy) error buffer used by the current thread.
static char* getTLErrorBuffer(void);
};
// CurlEasyRequest adds a slightly more powerful interface that can be used
// to set the options on a curl easy handle.
//
// Calling sendRequest() will then connect to the given URL and perform
// the data exchange. If an error occurs related to this handle, it can
// be read by calling getErrorString().
//
// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest:
// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest,
// which is only created by creating a AICurlEasyRequest. When the last copy of such
// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted
// and the CurlEasyRequest destructed.
class CurlEasyRequest : public CurlEasyHandle {
private:
void setPost_raw(S32 size, char const* data);
public:
void setPost(S32 size) { setPost_raw(size, NULL); }
void setPost(AIPostFieldPtr const& postdata, S32 size);
void setPost(char const* data, S32 size) { setPost(new AIPostField(data), size); }
void setoptString(CURLoption option, std::string const& value);
void addHeader(char const* str);
private:
// Callback stubs.
static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
static CURLcode SSLCtxCallback(CURL* curl, void* sslctx, void* userdata);
curl_write_callback mHeaderCallback;
void* mHeaderCallbackUserData;
curl_write_callback mWriteCallback;
void* mWriteCallbackUserData;
curl_read_callback mReadCallback;
void* mReadCallbackUserData;
curl_ssl_ctx_callback mSSLCtxCallback;
void* mSSLCtxCallbackUserData;
public:
void setHeaderCallback(curl_write_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
// Call this if the set callbacks are about to be invalidated.
void revokeCallbacks(void);
// Reset everything to the state it was in when this object was just created.
void resetState(void);
private:
// Called from applyDefaultOptions.
void applyProxySettings(void);
// Used in applyProxySettings.
static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm);
public:
// Set default options that we want applied to all curl easy handles.
void applyDefaultOptions(void);
// Prepare the request for adding it to a multi session, or calling perform.
// This actually adds the headers that were collected with addHeader.
void finalizeRequest(std::string const& url);
// Store result code that is returned by getResult.
void store_result(CURLcode result) { mResult = result; }
// Called when the curl easy handle is done.
void done(AICurlEasyRequest_wat& curl_easy_request_w) { finished(curl_easy_request_w); }
// Fill info with the transfer info.
void getTransferInfo(AICurlInterface::TransferInfo* info);
// If result != CURLE_FAILED_INIT then also info was filled.
void getResult(CURLcode* result, AICurlInterface::TransferInfo* info = NULL);
private:
curl_slist* mHeaders;
bool mRequestFinalized;
AICurlEasyHandleEvents* mEventsTarget;
CURLcode mResult;
private:
// This class may only be created by constructing a ThreadSafeCurlEasyRequest.
friend class ThreadSafeCurlEasyRequest;
// Throws AICurlNoEasyHandle.
CurlEasyRequest(void) :
mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT)
{ applyDefaultOptions(); }
public:
~CurlEasyRequest();
public:
// Post-initialization, set the parent to pass the events to.
void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; }
// For debugging purposes
bool is_finalized(void) const { return mRequestFinalized; }
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeCurlEasyRequest* get_lockobj(void);
protected:
// Pass events to parent.
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
};
// Buffers used by the AICurlInterface::Request API.
// Curl callbacks write into and read from these buffers.
// The interface with the rest of the code is through AICurlInterface::Responder.
//
// The lifetime of a CurlResponderBuffer is slightly shorter than its
// associated CurlEasyRequest; this class can only be created as base class
// of ThreadSafeBufferedCurlEasyRequest, and is therefore constructed after
// the construction of the associated CurlEasyRequest and destructed before it.
// Hence, it's safe to use get_lockobj() and through that access the CurlEasyRequest
// object at all times.
//
// A CurlResponderBuffer is thus created when a ThreadSafeBufferedCurlEasyRequest
// is created which only happens by creating a AICurlEasyRequest(true) instance,
// and when the last AICurlEasyRequest is deleted, then the ThreadSafeBufferedCurlEasyRequest
// is deleted and the CurlResponderBuffer destructed.
class CurlResponderBuffer : protected AICurlEasyHandleEvents {
public:
void resetState(AICurlEasyRequest_wat& curl_easy_request_w);
void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, std::vector<std::string> const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0, bool post = false);
LLIOPipe::buffer_ptr_t& getInput(void) { return mInput; }
std::stringstream& getHeaderOutput(void) { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput(void) { return mOutput; }
// Called if libcurl doesn't deliver within CurlRequestTimeOut seconds.
void timed_out(void);
// Called after removed_from_multi_handle was called.
void processOutput(AICurlEasyRequest_wat& curl_easy_request_w);
protected:
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w);
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w);
private:
LLIOPipe::buffer_ptr_t mInput;
U8* mLastRead; // Pointer into mInput where we last stopped reading (or NULL to start at the beginning).
std::stringstream mHeaderOutput;
LLIOPipe::buffer_ptr_t mOutput;
AICurlInterface::ResponderPtr mResponder;
public:
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
private:
// This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest.
friend class ThreadSafeBufferedCurlEasyRequest;
CurlResponderBuffer(void);
public:
~CurlResponderBuffer();
private:
static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data);
static size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data);
static size_t curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data);
public:
// Return pointer to the ThreadSafe (wrapped) version of this object.
ThreadSafeBufferedCurlEasyRequest* get_lockobj(void);
// Return true when prepRequest was already called and the object has not been
// invalidated as a result of calling timed_out().
bool isValid(void) const { return mResponder; }
};
// This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can
// copy it around cheaply and it gets destructed automatically when the last instance is deleted.
// It guarantees that the CURL* handle is never used concurrently, which is not allowed by libcurl.
// As AIThreadSafeSimpleDC contains a mutex, it cannot be copied. Therefore we need a reference counter for this object.
class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple<CurlEasyRequest> {
public:
// Throws AICurlNoEasyHandle.
ThreadSafeCurlEasyRequest(void) : mReferenceCount(0)
{ new (ptr()) CurlEasyRequest;
Dout(dc::curl, "Creating ThreadSafeCurlEasyRequest with this = " << (void*)this); }
virtual ~ThreadSafeCurlEasyRequest()
{ Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); }
// Returns true if this is a base class of ThreadSafeBufferedCurlEasyRequest.
virtual bool isBuffered(void) const { return false; }
private:
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is made.
friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ThreadSafeCurlEasyRequest> is destroyed.
};
// Same as the above but adds a CurlResponderBuffer. The latter has its own locking in order to
// allow casting the underlying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of
// what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest.
// The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently
// as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest.
// Note: the order of these base classes is important: AIThreadSafeSimple<CurlResponderBuffer> is now
// destructed before ThreadSafeCurlEasyRequest is.
class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple<CurlResponderBuffer> {
public:
// Throws AICurlNoEasyHandle.
ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple<CurlResponderBuffer>::ptr()) CurlResponderBuffer; }
/*virtual*/ bool isBuffered(void) const { return true; }
};
// The curl easy request type wrapped in a reference counting pointer.
typedef boost::intrusive_ptr<AICurlPrivate::ThreadSafeCurlEasyRequest> CurlEasyRequestPtr;
// This class wraps CURLM*'s.
// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl.
class CurlMultiHandle : public boost::noncopyable {
public:
CurlMultiHandle(void);
~CurlMultiHandle();
private:
// Disallow assignment.
CurlMultiHandle& operator=(CurlMultiHandle const*);
private:
static LLAtomicU32 sTotalMultiHandles;
protected:
CURLM* mMultiHandle;
public:
// Set options for a curl multi handle.
CURLMcode setopt(CURLMoption option, long parameter);
CURLMcode setopt(CURLMoption option, curl_socket_callback parameter);
CURLMcode setopt(CURLMoption option, curl_multi_timer_callback parameter);
CURLMcode setopt(CURLMoption option, void* parameter);
// Returns total number of existing CURLM* handles (excluding ones created outside this class).
static U32 getTotalMultiHandles(void) { return sTotalMultiHandles; }
};
// Overload the setopt methods in order to enforce the correct types (ie, convert an int to a long).
// curl_multi_setopt may only be passed a long,
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, long parameter)
{
llassert(option == CURLMOPT_MAXCONNECTS || option == CURLMOPT_PIPELINING);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
// ... or a function pointer,
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_socket_callback parameter)
{
llassert(option == CURLMOPT_SOCKETFUNCTION);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_multi_timer_callback parameter)
{
llassert(option == CURLMOPT_TIMERFUNCTION);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
// ... or an object pointer.
inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, void* parameter)
{
llassert(option == CURLMOPT_SOCKETDATA || option == CURLMOPT_TIMERDATA);
return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter));
}
} // namespace AICurlPrivate
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
/**
* @file aicurlthread.h
* @brief Thread safe wrapper for libcurl.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 28/04/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLTHREAD_H
#define AICURLTHREAD_H
#include "aicurl.h"
#include <vector>
#undef AICurlPrivate
namespace AICurlPrivate {
namespace curlthread {
class PollSet;
// For ordering a std::set with AICurlEasyRequest objects.
struct AICurlEasyRequestCompare {
bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); }
};
//-----------------------------------------------------------------------------
// MultiHandle
// This class adds member functions that will only be called from the AICurlThread thread.
// This class guarantees that all added easy handles will be removed from the multi handle
// before the multi handle is cleaned up, as is required by libcurl.
class MultiHandle : public CurlMultiHandle
{
public:
MultiHandle(void);
~MultiHandle();
// Add/remove an easy handle to/from a multi session.
CURLMcode add_easy_request(AICurlEasyRequest const& easy_request);
CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command = false);
// Reads/writes available data from a particular socket (non-blocking).
CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask);
// Set data to association with an internal socket.
CURLMcode assign(curl_socket_t sockfd, void* sockptr);
// Read multi stack informationals.
CURLMsg const* info_read(int* msgs_in_queue) const;
private:
typedef std::set<AICurlEasyRequest, AICurlEasyRequestCompare> addedEasyRequests_type;
addedEasyRequests_type mAddedEasyRequests;
bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count().
int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with.
int mRunningHandles; // The last value returned by curl_multi_socket_action.
long mTimeOut; // The last time out in ms as set by the callback CURLMOPT_TIMERFUNCTION.
private:
static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp);
static int timer_callback(CURLM* multi, long timeout_ms, void* userp);
public:
// Returns the number of active easy handles as reported by the last call to curl_multi_socket_action.
int getRunningHandles(void) const { return mRunningHandles; }
// Returns how long to wait for socket action before calling socket_action(CURL_SOCKET_TIMEOUT, 0), in ms.
int getTimeOut(void) const { return mTimeOut; }
// This is called before sleeping, after calling (one or more times) socket_action.
void check_run_count(void);
public:
//-----------------------------------------------------------------------------
// Curl socket administration:
PollSet* mReadPollSet;
PollSet* mWritePollSet;
};
} // namespace curlthread
} // namespace AICurlPrivate
// Thread safe, noncopyable curl multi handle.
// This class wraps MultiHandle for thread-safety.
// AIThreadSafeSingleThreadDC cannot be copied, but that is OK as we don't need that (or want that);
// this class provides a thread-local singleton (exactly one instance per thread), and because it
// can't be copied, that guarantees that the CURLM* handle is never used concurrently, which is
// not allowed by libcurl.
class AICurlMultiHandle : public AIThreadSafeSingleThreadDC<AICurlPrivate::curlthread::MultiHandle>, public LLThreadLocalDataMember {
public:
static AICurlMultiHandle& getInstance(void);
static void destroyInstance(void);
private:
// Use getInstance().
AICurlMultiHandle(void) { }
};
typedef AISTAccessConst<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_rat;
typedef AISTAccess<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_wat;
#define AICurlPrivate DONTUSE_AICurlPrivate
#endif

View File

@@ -0,0 +1,942 @@
#ifdef DEBUG_CURLIO
#include "sys.h"
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <stdarg.h>
#include <cstring>
#include "llpreprocessor.h"
#include <curl/curl.h>
#define COMPILING_DEBUG_LIBCURL_CC
#include "debug_libcurl.h"
#include "debug.h"
#include "llerror.h"
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#endif
#define CURL_VERSION(major, minor, patch) \
(LIBCURL_VERSION_MAJOR > major || \
(LIBCURL_VERSION_MAJOR == major && \
LIBCURL_VERSION_MINOR > minor || \
(LIBCURL_VERSION_MINOR == minor && \
LIBCURL_VERSION_PATCH >= patch)))
static struct curl_slist unchanged_slist;
std::ostream& operator<<(std::ostream& os, struct curl_slist const& slist)
{
struct curl_slist const* ptr = &slist;
if (ptr == &unchanged_slist)
os << " <unchanged> ";
else
{
os << "(curl_slist)@0x" << std::hex << (size_t)ptr << std::dec << "{";
do
{
os << '"' << ptr->data << '"';
ptr = ptr->next;
if (ptr)
os << ", ";
}
while(ptr);
os << "}";
}
return os;
}
#define CASEPRINT(x) case x: os << #x; break
std::ostream& operator<<(std::ostream& os, CURLINFO info)
{
switch (info)
{
CASEPRINT(CURLINFO_EFFECTIVE_URL);
CASEPRINT(CURLINFO_RESPONSE_CODE);
CASEPRINT(CURLINFO_TOTAL_TIME);
CASEPRINT(CURLINFO_NAMELOOKUP_TIME);
CASEPRINT(CURLINFO_CONNECT_TIME);
CASEPRINT(CURLINFO_PRETRANSFER_TIME);
CASEPRINT(CURLINFO_SIZE_UPLOAD);
CASEPRINT(CURLINFO_SIZE_DOWNLOAD);
CASEPRINT(CURLINFO_SPEED_DOWNLOAD);
CASEPRINT(CURLINFO_SPEED_UPLOAD);
CASEPRINT(CURLINFO_HEADER_SIZE);
CASEPRINT(CURLINFO_REQUEST_SIZE);
CASEPRINT(CURLINFO_SSL_VERIFYRESULT);
CASEPRINT(CURLINFO_FILETIME);
CASEPRINT(CURLINFO_CONTENT_LENGTH_DOWNLOAD);
CASEPRINT(CURLINFO_CONTENT_LENGTH_UPLOAD);
CASEPRINT(CURLINFO_STARTTRANSFER_TIME);
CASEPRINT(CURLINFO_CONTENT_TYPE);
CASEPRINT(CURLINFO_REDIRECT_TIME);
CASEPRINT(CURLINFO_REDIRECT_COUNT);
CASEPRINT(CURLINFO_PRIVATE);
CASEPRINT(CURLINFO_HTTP_CONNECTCODE);
CASEPRINT(CURLINFO_HTTPAUTH_AVAIL);
CASEPRINT(CURLINFO_PROXYAUTH_AVAIL);
CASEPRINT(CURLINFO_OS_ERRNO);
CASEPRINT(CURLINFO_NUM_CONNECTS);
CASEPRINT(CURLINFO_SSL_ENGINES);
CASEPRINT(CURLINFO_COOKIELIST);
CASEPRINT(CURLINFO_LASTSOCKET);
CASEPRINT(CURLINFO_FTP_ENTRY_PATH);
CASEPRINT(CURLINFO_REDIRECT_URL);
CASEPRINT(CURLINFO_PRIMARY_IP);
CASEPRINT(CURLINFO_APPCONNECT_TIME);
CASEPRINT(CURLINFO_CERTINFO);
CASEPRINT(CURLINFO_CONDITION_UNMET);
CASEPRINT(CURLINFO_RTSP_SESSION_ID);
CASEPRINT(CURLINFO_RTSP_CLIENT_CSEQ);
CASEPRINT(CURLINFO_RTSP_SERVER_CSEQ);
CASEPRINT(CURLINFO_RTSP_CSEQ_RECV);
CASEPRINT(CURLINFO_PRIMARY_PORT);
CASEPRINT(CURLINFO_LOCAL_IP);
CASEPRINT(CURLINFO_LOCAL_PORT);
default:
os << "<unknown" << " CURLINFO value (" << (int)info << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLcode code)
{
switch(code)
{
CASEPRINT(CURLE_OK);
CASEPRINT(CURLE_UNSUPPORTED_PROTOCOL);
CASEPRINT(CURLE_FAILED_INIT);
CASEPRINT(CURLE_URL_MALFORMAT);
#if CURL_VERSION(7, 21, 5)
CASEPRINT(CURLE_NOT_BUILT_IN);
#endif
CASEPRINT(CURLE_COULDNT_RESOLVE_PROXY);
CASEPRINT(CURLE_COULDNT_RESOLVE_HOST);
CASEPRINT(CURLE_COULDNT_CONNECT);
CASEPRINT(CURLE_FTP_WEIRD_SERVER_REPLY);
CASEPRINT(CURLE_REMOTE_ACCESS_DENIED);
#if 0
CASEPRINT(CURLE_FTP_ACCEPT_FAILED);
#endif
CASEPRINT(CURLE_FTP_WEIRD_PASS_REPLY);
#if 0
CASEPRINT(CURLE_FTP_ACCEPT_TIMEOUT);
#endif
CASEPRINT(CURLE_FTP_WEIRD_PASV_REPLY);
CASEPRINT(CURLE_FTP_WEIRD_227_FORMAT);
CASEPRINT(CURLE_FTP_CANT_GET_HOST);
CASEPRINT(CURLE_OBSOLETE16);
CASEPRINT(CURLE_FTP_COULDNT_SET_TYPE);
CASEPRINT(CURLE_PARTIAL_FILE);
CASEPRINT(CURLE_FTP_COULDNT_RETR_FILE);
CASEPRINT(CURLE_OBSOLETE20);
CASEPRINT(CURLE_QUOTE_ERROR);
CASEPRINT(CURLE_HTTP_RETURNED_ERROR);
CASEPRINT(CURLE_WRITE_ERROR);
CASEPRINT(CURLE_OBSOLETE24);
CASEPRINT(CURLE_UPLOAD_FAILED);
CASEPRINT(CURLE_READ_ERROR);
CASEPRINT(CURLE_OUT_OF_MEMORY);
CASEPRINT(CURLE_OPERATION_TIMEDOUT);
CASEPRINT(CURLE_OBSOLETE29);
CASEPRINT(CURLE_FTP_PORT_FAILED);
CASEPRINT(CURLE_FTP_COULDNT_USE_REST);
CASEPRINT(CURLE_OBSOLETE32);
CASEPRINT(CURLE_RANGE_ERROR);
CASEPRINT(CURLE_HTTP_POST_ERROR);
CASEPRINT(CURLE_SSL_CONNECT_ERROR);
CASEPRINT(CURLE_BAD_DOWNLOAD_RESUME);
CASEPRINT(CURLE_FILE_COULDNT_READ_FILE);
CASEPRINT(CURLE_LDAP_CANNOT_BIND);
CASEPRINT(CURLE_LDAP_SEARCH_FAILED);
CASEPRINT(CURLE_OBSOLETE40);
CASEPRINT(CURLE_FUNCTION_NOT_FOUND);
CASEPRINT(CURLE_ABORTED_BY_CALLBACK);
CASEPRINT(CURLE_BAD_FUNCTION_ARGUMENT);
CASEPRINT(CURLE_OBSOLETE44);
CASEPRINT(CURLE_INTERFACE_FAILED);
CASEPRINT(CURLE_OBSOLETE46);
CASEPRINT(CURLE_TOO_MANY_REDIRECTS );
#if CURL_VERSION(7, 21, 5)
CASEPRINT(CURLE_UNKNOWN_OPTION);
#else
CASEPRINT(CURLE_UNKNOWN_TELNET_OPTION);
#endif
CASEPRINT(CURLE_TELNET_OPTION_SYNTAX );
CASEPRINT(CURLE_OBSOLETE50);
CASEPRINT(CURLE_PEER_FAILED_VERIFICATION);
CASEPRINT(CURLE_GOT_NOTHING);
CASEPRINT(CURLE_SSL_ENGINE_NOTFOUND);
CASEPRINT(CURLE_SSL_ENGINE_SETFAILED);
CASEPRINT(CURLE_SEND_ERROR);
CASEPRINT(CURLE_RECV_ERROR);
CASEPRINT(CURLE_OBSOLETE57);
CASEPRINT(CURLE_SSL_CERTPROBLEM);
CASEPRINT(CURLE_SSL_CIPHER);
CASEPRINT(CURLE_SSL_CACERT);
CASEPRINT(CURLE_BAD_CONTENT_ENCODING);
CASEPRINT(CURLE_LDAP_INVALID_URL);
CASEPRINT(CURLE_FILESIZE_EXCEEDED);
CASEPRINT(CURLE_USE_SSL_FAILED);
CASEPRINT(CURLE_SEND_FAIL_REWIND);
CASEPRINT(CURLE_SSL_ENGINE_INITFAILED);
CASEPRINT(CURLE_LOGIN_DENIED);
CASEPRINT(CURLE_TFTP_NOTFOUND);
CASEPRINT(CURLE_TFTP_PERM);
CASEPRINT(CURLE_REMOTE_DISK_FULL);
CASEPRINT(CURLE_TFTP_ILLEGAL);
CASEPRINT(CURLE_TFTP_UNKNOWNID);
CASEPRINT(CURLE_REMOTE_FILE_EXISTS);
CASEPRINT(CURLE_TFTP_NOSUCHUSER);
CASEPRINT(CURLE_CONV_FAILED);
CASEPRINT(CURLE_CONV_REQD);
CASEPRINT(CURLE_SSL_CACERT_BADFILE);
CASEPRINT(CURLE_REMOTE_FILE_NOT_FOUND);
CASEPRINT(CURLE_SSH);
CASEPRINT(CURLE_SSL_SHUTDOWN_FAILED);
CASEPRINT(CURLE_AGAIN);
CASEPRINT(CURLE_SSL_CRL_BADFILE);
CASEPRINT(CURLE_SSL_ISSUER_ERROR);
CASEPRINT(CURLE_FTP_PRET_FAILED);
CASEPRINT(CURLE_RTSP_CSEQ_ERROR);
CASEPRINT(CURLE_RTSP_SESSION_ERROR);
CASEPRINT(CURLE_FTP_BAD_FILE_LIST);
CASEPRINT(CURLE_CHUNK_FAILED);
default:
os << (code == CURL_LAST ? "<illegal" : "<unknown") << " CURLcode value (" << (int)code << ")>";
}
return os;
}
struct AICURL;
struct AICURLM;
std::ostream& operator<<(std::ostream& os, AICURL* curl)
{
os << "(CURL*)0x" << std::hex << (size_t)curl << std::dec;
return os;
}
std::ostream& operator<<(std::ostream& os, AICURLM* curl)
{
os << "(CURLM*)0x" << std::hex << (size_t)curl << std::dec;
return os;
}
std::ostream& operator<<(std::ostream& os, CURLoption option)
{
switch(option)
{
CASEPRINT(CURLOPT_WRITEDATA);
CASEPRINT(CURLOPT_URL);
CASEPRINT(CURLOPT_PORT);
CASEPRINT(CURLOPT_PROXY);
CASEPRINT(CURLOPT_USERPWD);
CASEPRINT(CURLOPT_PROXYUSERPWD);
CASEPRINT(CURLOPT_RANGE);
CASEPRINT(CURLOPT_READDATA);
CASEPRINT(CURLOPT_ERRORBUFFER);
CASEPRINT(CURLOPT_WRITEFUNCTION);
CASEPRINT(CURLOPT_READFUNCTION);
CASEPRINT(CURLOPT_TIMEOUT);
CASEPRINT(CURLOPT_INFILESIZE);
CASEPRINT(CURLOPT_POSTFIELDS);
CASEPRINT(CURLOPT_REFERER);
CASEPRINT(CURLOPT_FTPPORT);
CASEPRINT(CURLOPT_USERAGENT);
CASEPRINT(CURLOPT_LOW_SPEED_LIMIT);
CASEPRINT(CURLOPT_LOW_SPEED_TIME);
CASEPRINT(CURLOPT_RESUME_FROM);
CASEPRINT(CURLOPT_COOKIE);
CASEPRINT(CURLOPT_RTSPHEADER);
CASEPRINT(CURLOPT_HTTPPOST);
CASEPRINT(CURLOPT_SSLCERT);
CASEPRINT(CURLOPT_KEYPASSWD);
CASEPRINT(CURLOPT_CRLF);
CASEPRINT(CURLOPT_QUOTE);
CASEPRINT(CURLOPT_HEADERDATA);
CASEPRINT(CURLOPT_COOKIEFILE);
CASEPRINT(CURLOPT_SSLVERSION);
CASEPRINT(CURLOPT_TIMECONDITION);
CASEPRINT(CURLOPT_TIMEVALUE);
CASEPRINT(CURLOPT_CUSTOMREQUEST);
CASEPRINT(CURLOPT_STDERR);
CASEPRINT(CURLOPT_POSTQUOTE);
CASEPRINT(CURLOPT_WRITEINFO);
CASEPRINT(CURLOPT_VERBOSE);
CASEPRINT(CURLOPT_HEADER);
CASEPRINT(CURLOPT_NOPROGRESS);
CASEPRINT(CURLOPT_NOBODY);
CASEPRINT(CURLOPT_FAILONERROR);
CASEPRINT(CURLOPT_UPLOAD);
CASEPRINT(CURLOPT_POST);
CASEPRINT(CURLOPT_DIRLISTONLY);
CASEPRINT(CURLOPT_APPEND);
CASEPRINT(CURLOPT_NETRC);
CASEPRINT(CURLOPT_FOLLOWLOCATION);
CASEPRINT(CURLOPT_TRANSFERTEXT);
CASEPRINT(CURLOPT_PUT);
CASEPRINT(CURLOPT_PROGRESSFUNCTION);
CASEPRINT(CURLOPT_PROGRESSDATA);
CASEPRINT(CURLOPT_AUTOREFERER);
CASEPRINT(CURLOPT_PROXYPORT);
CASEPRINT(CURLOPT_POSTFIELDSIZE);
CASEPRINT(CURLOPT_HTTPPROXYTUNNEL);
CASEPRINT(CURLOPT_INTERFACE);
CASEPRINT(CURLOPT_KRBLEVEL);
CASEPRINT(CURLOPT_SSL_VERIFYPEER);
CASEPRINT(CURLOPT_CAINFO);
CASEPRINT(CURLOPT_MAXREDIRS);
CASEPRINT(CURLOPT_FILETIME);
CASEPRINT(CURLOPT_TELNETOPTIONS);
CASEPRINT(CURLOPT_MAXCONNECTS);
CASEPRINT(CURLOPT_CLOSEPOLICY);
CASEPRINT(CURLOPT_FRESH_CONNECT);
CASEPRINT(CURLOPT_FORBID_REUSE);
CASEPRINT(CURLOPT_RANDOM_FILE);
CASEPRINT(CURLOPT_EGDSOCKET);
CASEPRINT(CURLOPT_CONNECTTIMEOUT);
CASEPRINT(CURLOPT_HEADERFUNCTION);
CASEPRINT(CURLOPT_HTTPGET);
CASEPRINT(CURLOPT_SSL_VERIFYHOST);
CASEPRINT(CURLOPT_COOKIEJAR);
CASEPRINT(CURLOPT_SSL_CIPHER_LIST);
CASEPRINT(CURLOPT_HTTP_VERSION);
CASEPRINT(CURLOPT_FTP_USE_EPSV);
CASEPRINT(CURLOPT_SSLCERTTYPE);
CASEPRINT(CURLOPT_SSLKEY);
CASEPRINT(CURLOPT_SSLKEYTYPE);
CASEPRINT(CURLOPT_SSLENGINE);
CASEPRINT(CURLOPT_SSLENGINE_DEFAULT);
CASEPRINT(CURLOPT_DNS_USE_GLOBAL_CACHE);
CASEPRINT(CURLOPT_DNS_CACHE_TIMEOUT);
CASEPRINT(CURLOPT_PREQUOTE);
CASEPRINT(CURLOPT_DEBUGFUNCTION);
CASEPRINT(CURLOPT_DEBUGDATA);
CASEPRINT(CURLOPT_COOKIESESSION);
CASEPRINT(CURLOPT_CAPATH);
CASEPRINT(CURLOPT_BUFFERSIZE);
CASEPRINT(CURLOPT_NOSIGNAL);
CASEPRINT(CURLOPT_SHARE);
CASEPRINT(CURLOPT_PROXYTYPE);
#if CURL_VERSION(7, 21, 6)
CASEPRINT(CURLOPT_ACCEPT_ENCODING);
#else
CASEPRINT(CURLOPT_ENCODING);
#endif
CASEPRINT(CURLOPT_PRIVATE);
CASEPRINT(CURLOPT_HTTP200ALIASES);
CASEPRINT(CURLOPT_UNRESTRICTED_AUTH);
CASEPRINT(CURLOPT_FTP_USE_EPRT);
CASEPRINT(CURLOPT_HTTPAUTH);
CASEPRINT(CURLOPT_SSL_CTX_FUNCTION);
CASEPRINT(CURLOPT_SSL_CTX_DATA);
CASEPRINT(CURLOPT_FTP_CREATE_MISSING_DIRS);
CASEPRINT(CURLOPT_PROXYAUTH);
CASEPRINT(CURLOPT_FTP_RESPONSE_TIMEOUT);
CASEPRINT(CURLOPT_IPRESOLVE);
CASEPRINT(CURLOPT_MAXFILESIZE);
CASEPRINT(CURLOPT_INFILESIZE_LARGE);
CASEPRINT(CURLOPT_RESUME_FROM_LARGE);
CASEPRINT(CURLOPT_MAXFILESIZE_LARGE);
CASEPRINT(CURLOPT_NETRC_FILE);
CASEPRINT(CURLOPT_USE_SSL);
CASEPRINT(CURLOPT_POSTFIELDSIZE_LARGE);
CASEPRINT(CURLOPT_TCP_NODELAY);
CASEPRINT(CURLOPT_FTPSSLAUTH);
CASEPRINT(CURLOPT_IOCTLFUNCTION);
CASEPRINT(CURLOPT_IOCTLDATA);
CASEPRINT(CURLOPT_FTP_ACCOUNT);
CASEPRINT(CURLOPT_COOKIELIST);
CASEPRINT(CURLOPT_IGNORE_CONTENT_LENGTH);
CASEPRINT(CURLOPT_FTP_SKIP_PASV_IP);
CASEPRINT(CURLOPT_FTP_FILEMETHOD);
CASEPRINT(CURLOPT_LOCALPORT);
CASEPRINT(CURLOPT_LOCALPORTRANGE);
CASEPRINT(CURLOPT_CONNECT_ONLY);
CASEPRINT(CURLOPT_CONV_FROM_NETWORK_FUNCTION);
CASEPRINT(CURLOPT_CONV_TO_NETWORK_FUNCTION);
CASEPRINT(CURLOPT_CONV_FROM_UTF8_FUNCTION);
CASEPRINT(CURLOPT_MAX_SEND_SPEED_LARGE);
CASEPRINT(CURLOPT_MAX_RECV_SPEED_LARGE);
CASEPRINT(CURLOPT_FTP_ALTERNATIVE_TO_USER);
CASEPRINT(CURLOPT_SOCKOPTFUNCTION);
CASEPRINT(CURLOPT_SOCKOPTDATA);
CASEPRINT(CURLOPT_SSL_SESSIONID_CACHE);
CASEPRINT(CURLOPT_SSH_AUTH_TYPES);
CASEPRINT(CURLOPT_SSH_PUBLIC_KEYFILE);
CASEPRINT(CURLOPT_SSH_PRIVATE_KEYFILE);
CASEPRINT(CURLOPT_FTP_SSL_CCC);
CASEPRINT(CURLOPT_TIMEOUT_MS);
CASEPRINT(CURLOPT_CONNECTTIMEOUT_MS);
CASEPRINT(CURLOPT_HTTP_TRANSFER_DECODING);
CASEPRINT(CURLOPT_HTTP_CONTENT_DECODING);
CASEPRINT(CURLOPT_NEW_FILE_PERMS);
CASEPRINT(CURLOPT_NEW_DIRECTORY_PERMS);
CASEPRINT(CURLOPT_POSTREDIR);
CASEPRINT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5);
CASEPRINT(CURLOPT_OPENSOCKETFUNCTION);
CASEPRINT(CURLOPT_OPENSOCKETDATA);
CASEPRINT(CURLOPT_COPYPOSTFIELDS);
CASEPRINT(CURLOPT_PROXY_TRANSFER_MODE);
CASEPRINT(CURLOPT_SEEKFUNCTION);
CASEPRINT(CURLOPT_SEEKDATA);
CASEPRINT(CURLOPT_CRLFILE);
CASEPRINT(CURLOPT_ISSUERCERT);
CASEPRINT(CURLOPT_ADDRESS_SCOPE);
CASEPRINT(CURLOPT_CERTINFO);
CASEPRINT(CURLOPT_USERNAME);
CASEPRINT(CURLOPT_PASSWORD);
CASEPRINT(CURLOPT_PROXYUSERNAME);
CASEPRINT(CURLOPT_PROXYPASSWORD);
CASEPRINT(CURLOPT_NOPROXY);
CASEPRINT(CURLOPT_TFTP_BLKSIZE);
CASEPRINT(CURLOPT_SOCKS5_GSSAPI_SERVICE);
CASEPRINT(CURLOPT_SOCKS5_GSSAPI_NEC);
CASEPRINT(CURLOPT_PROTOCOLS);
CASEPRINT(CURLOPT_REDIR_PROTOCOLS);
CASEPRINT(CURLOPT_SSH_KNOWNHOSTS);
CASEPRINT(CURLOPT_SSH_KEYFUNCTION);
CASEPRINT(CURLOPT_SSH_KEYDATA);
CASEPRINT(CURLOPT_MAIL_FROM);
CASEPRINT(CURLOPT_MAIL_RCPT);
CASEPRINT(CURLOPT_FTP_USE_PRET);
CASEPRINT(CURLOPT_RTSP_REQUEST);
CASEPRINT(CURLOPT_RTSP_SESSION_ID);
CASEPRINT(CURLOPT_RTSP_STREAM_URI);
CASEPRINT(CURLOPT_RTSP_TRANSPORT);
CASEPRINT(CURLOPT_RTSP_CLIENT_CSEQ);
CASEPRINT(CURLOPT_RTSP_SERVER_CSEQ);
CASEPRINT(CURLOPT_INTERLEAVEDATA);
CASEPRINT(CURLOPT_INTERLEAVEFUNCTION);
CASEPRINT(CURLOPT_WILDCARDMATCH);
CASEPRINT(CURLOPT_CHUNK_BGN_FUNCTION);
CASEPRINT(CURLOPT_CHUNK_END_FUNCTION);
CASEPRINT(CURLOPT_FNMATCH_FUNCTION);
CASEPRINT(CURLOPT_CHUNK_DATA);
CASEPRINT(CURLOPT_FNMATCH_DATA);
#if CURL_VERSION(7, 21, 3)
CASEPRINT(CURLOPT_RESOLVE);
#endif
#if CURL_VERSION(7, 21, 4)
CASEPRINT(CURLOPT_TLSAUTH_USERNAME);
CASEPRINT(CURLOPT_TLSAUTH_PASSWORD);
CASEPRINT(CURLOPT_TLSAUTH_TYPE);
#endif
#if CURL_VERSION(7, 21, 6)
CASEPRINT(CURLOPT_TRANSFER_ENCODING);
#endif
#if CURL_VERSION(7, 21, 7)
CASEPRINT(CURLOPT_CLOSESOCKETFUNCTION);
CASEPRINT(CURLOPT_CLOSESOCKETDATA);
#endif
#if CURL_VERSION(7, 22, 0)
CASEPRINT(CURLOPT_GSSAPI_DELEGATION);
#endif
#if CURL_VERSION(7, 24, 0)
CASEPRINT(CURLOPT_DNS_SERVERS);
CASEPRINT(CURLOPT_ACCEPTTIMEOUT_MS);
#endif
#if CURL_VERSION(7, 25, 0)
CASEPRINT(CURLOPT_TCP_KEEPALIVE);
CASEPRINT(CURLOPT_TCP_KEEPIDLE);
CASEPRINT(CURLOPT_TCP_KEEPINTVL);
#endif
#if CURL_VERSION(7, 25, 0)
CASEPRINT(CURLOPT_SSL_OPTIONS);
CASEPRINT(CURLOPT_MAIL_AUTH);
#endif
default:
os << "<unknown CURLoption value (" << (int)option << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLMoption option)
{
switch(option)
{
CASEPRINT(CURLMOPT_SOCKETFUNCTION);
CASEPRINT(CURLMOPT_SOCKETDATA);
CASEPRINT(CURLMOPT_PIPELINING);
CASEPRINT(CURLMOPT_TIMERFUNCTION);
CASEPRINT(CURLMOPT_TIMERDATA);
CASEPRINT(CURLMOPT_MAXCONNECTS);
default:
os << "<unknown CURLMoption value (" << (int)option << ")>";
}
return os;
}
std::ostream& operator<<(std::ostream& os, CURLMsg* msg)
{
if (msg)
{
os << "(CURLMsg*){";
if (msg->msg == CURLMSG_DONE)
os << "CURLMSG_DONE";
else
os << msg->msg;
os << ", " << (AICURL*)msg->easy_handle << ", 0x" << std::hex << (size_t)msg->data.whatever << std::dec << '}';
}
else
os << "(CURLMsg*)NULL";
return os;
}
struct Socket {
curl_socket_t mSocket;
Socket(curl_socket_t sockfd) : mSocket(sockfd) { }
};
std::ostream& operator<<(std::ostream& os, Socket const& sock)
{
if (sock.mSocket == CURL_SOCKET_TIMEOUT)
os << "CURL_SOCKET_TIMEOUT";
else
os << sock.mSocket;
return os;
}
struct EvBitmask {
int mBitmask;
EvBitmask(int mask) : mBitmask(mask) { }
};
std::ostream& operator<<(std::ostream& os, EvBitmask const& bitmask)
{
int m = bitmask.mBitmask;
if (m == 0)
os << '0';
if ((m & CURL_CSELECT_IN))
{
os << "CURL_CSELECT_IN";
if ((m & (CURL_CSELECT_OUT|CURL_CSELECT_ERR)))
os << '|';
}
if ((m & CURL_CSELECT_OUT))
{
os << "CURL_CSELECT_OUT";
if ((m & CURL_CSELECT_ERR))
os << '|';
}
if ((m & CURL_CSELECT_ERR))
{
os << "CURL_CSELECT_ERR";
}
return os;
}
extern "C" {
void debug_curl_easy_cleanup(CURL* handle)
{
curl_easy_cleanup(handle);
Dout(dc::curl, "curl_easy_cleanup(" << (AICURL*)handle << ")");
}
CURL* debug_curl_easy_duphandle(CURL* handle)
{
CURL* ret;
ret = curl_easy_duphandle(handle);
Dout(dc::curl, "curl_easy_duphandle(" << (AICURL*)handle << ") = " << (AICURL*)ret);
return ret;
}
char* debug_curl_easy_escape(CURL* curl, char* url, int length)
{
char* ret;
ret = curl_easy_escape(curl, url, length);
Dout(dc::curl, "curl_easy_escape(" << curl << ", \"" << url << "\", " << length << ") = \"" << ret << '"');
return ret;
}
CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...)
{
CURLcode ret;
va_list ap;
union param_type {
void* some_ptr;
long* long_ptr;
char** char_ptr;
curl_slist** curl_slist_ptr;
double* double_ptr;
} param;
va_start(ap, info);
param.some_ptr = va_arg(ap, void*);
va_end(ap);
ret = curl_easy_getinfo(curl, info, param.some_ptr);
if (info == CURLINFO_PRIVATE)
{
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.some_ptr << std::dec << ") = " << ret);
}
else
{
switch((info & CURLINFO_TYPEMASK))
{
case CURLINFO_STRING:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char**){ \"" << (ret == CURLE_OK ? *param.char_ptr : " <unchanged> ") << "\" }) = " << ret);
break;
case CURLINFO_LONG:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (long*){ " << (ret == CURLE_OK ? *param.long_ptr : 0L) << "L }) = " << ret);
break;
case CURLINFO_DOUBLE:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (double*){" << (ret == CURLE_OK ? *param.double_ptr : 0.) << "}) = " << ret);
break;
case CURLINFO_SLIST:
Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist**){ " << (ret == CURLE_OK ? **param.curl_slist_ptr : unchanged_slist) << " }) = " << ret);
break;
}
}
return ret;
}
CURL* debug_curl_easy_init(void)
{
CURL* ret;
ret = curl_easy_init();
Dout(dc::curl, "curl_easy_init() = " << (AICURL*)ret);
return ret;
}
CURLcode debug_curl_easy_pause(CURL* handle, int bitmask)
{
CURLcode ret;
ret = curl_easy_pause(handle, bitmask);
Dout(dc::curl, "curl_easy_pause(" << (AICURL*)handle << ", 0x" << std::hex << bitmask << std::dec << ") = " << ret);
return ret;
}
CURLcode debug_curl_easy_perform(CURL* handle)
{
CURLcode ret;
ret = curl_easy_perform(handle);
Dout(dc::curl, "curl_easy_perform(" << (AICURL*)handle << ") = " << ret);
return ret;
}
void debug_curl_easy_reset(CURL* handle)
{
curl_easy_reset(handle);
Dout(dc::curl, "curl_easy_reset(" << (AICURL*)handle << ")");
}
CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...)
{
CURLcode ret;
va_list ap;
union param_type {
long along;
void* ptr;
curl_off_t offset;
} param;
unsigned int param_type = (option / 10000) * 10000;
va_start(ap, option);
switch (param_type)
{
case CURLOPTTYPE_LONG:
param.along = va_arg(ap, long);
break;
case CURLOPTTYPE_OBJECTPOINT:
case CURLOPTTYPE_FUNCTIONPOINT:
param.ptr = va_arg(ap, void*);
break;
case CURLOPTTYPE_OFF_T:
param.offset = va_arg(ap, curl_off_t);
break;
default:
std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl;
std::exit(EXIT_FAILURE);
}
va_end(ap);
static long postfieldsize; // Cache. Assumes only one thread sets CURLOPT_POSTFIELDSIZE.
switch (param_type)
{
case CURLOPTTYPE_LONG:
{
ret = curl_easy_setopt(handle, option, param.along);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret);
if (option == CURLOPT_POSTFIELDSIZE)
{
postfieldsize = param.along;
}
break;
}
case CURLOPTTYPE_OBJECTPOINT:
{
ret = curl_easy_setopt(handle, option, param.ptr);
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curl)
LibcwDoutStream << "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", ";
// For a subset of all options that take a char*, print the string passed.
if (option == CURLOPT_PROXY || // Set HTTP proxy to use. The parameter should be a char* to a zero terminated string holding the host name or dotted IP address.
option == CURLOPT_PROXYUSERPWD || // Pass a char* as parameter, which should be [user name]:[password] to use for the connection to the HTTP proxy.
option == CURLOPT_CAINFO || // Pass a char * to a zero terminated string naming a file holding one or more certificates to verify the peer with.
option == CURLOPT_URL || // Pass in a pointer to the actual URL to deal with. The parameter should be a char * to a zero terminated string [...]
option == CURLOPT_COOKIEFILE || // Pass a pointer to a zero terminated string as parameter. It should contain the name of your file holding cookie data to read.
option == CURLOPT_CUSTOMREQUEST || // Pass a pointer to a zero terminated string as parameter. It can be used to specify the request instead of GET or HEAD when performing HTTP based requests
option == CURLOPT_ENCODING || // Sets the contents of the Accept-Encoding: header sent in a HTTP request, and enables decoding of a response when a Content-Encoding: header is received.
option == CURLOPT_POSTFIELDS ||
option == CURLOPT_COPYPOSTFIELDS) // Full data to post in a HTTP POST operation.
{
bool const is_postfield = option == CURLOPT_POSTFIELDS || option == CURLOPT_COPYPOSTFIELDS;
char* str = (char*)param.ptr;
long size;
LibcwDoutStream << "(char*)0x" << std::hex << (size_t)param.ptr << std::dec << ")";
if (is_postfield)
{
size = postfieldsize < 32 ? postfieldsize : 32; // Only print first 32 characters (this was already written to debug output before).
}
else
{
size = strlen(str);
}
LibcwDoutStream << "[";
if (str)
{
LibcwDoutStream << libcwd::buf2str(str, size);
if (is_postfield && postfieldsize > 32)
LibcwDoutStream << "...";
}
else
{
LibcwDoutStream << "NULL";
}
LibcwDoutStream << "](" << (is_postfield ? postfieldsize : size) << " bytes))";
}
else
{
LibcwDoutStream << "(object*)0x" << std::hex << (size_t)param.ptr << std::dec << ")";
}
LibcwDoutStream << " = " << ret;
LibcwDoutScopeEnd;
if (option == CURLOPT_HTTPHEADER && param.ptr)
{
debug::Indent indent(2);
Dout(dc::curl, "HTTP Headers:");
struct curl_slist* list = (struct curl_slist*)param.ptr;
while (list)
{
Dout(dc::curl, '"' << list->data << '"');
list = list->next;
}
}
break;
}
case CURLOPTTYPE_FUNCTIONPOINT:
ret = curl_easy_setopt(handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_OFF_T:
{
ret = curl_easy_setopt(handle, option, param.offset);
Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret);
if (option == CURLOPT_POSTFIELDSIZE_LARGE)
{
postfieldsize = (long)param.offset;
}
break;
}
default:
break;
}
return ret;
}
char const* debug_curl_easy_strerror(CURLcode errornum)
{
char const* ret;
ret = curl_easy_strerror(errornum);
Dout(dc::curl, "curl_easy_strerror(" << errornum << ") = \"" << ret << '"');
return ret;
}
char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength)
{
char* ret;
ret = curl_easy_unescape(curl, url, inlength, outlength);
Dout(dc::curl, "curl_easy_unescape(" << curl << ", \"" << url << "\", " << inlength << ", " << ((ret && outlength) ? *outlength : 1) << ") = \"" << ret << '"');
return ret;
}
void debug_curl_free(char* ptr)
{
curl_free(ptr);
Dout(dc::curl, "curl_free(0x" << std::hex << (size_t)ptr << std::dec << ")");
}
time_t debug_curl_getdate(char const* datestring, time_t* now)
{
time_t ret;
ret = curl_getdate(datestring, now);
Dout(dc::curl, "curl_getdate(\"" << datestring << "\", " << (now == NULL ? "NULL" : "<erroneous non-NULL value for 'now'>") << ") = " << ret);
return ret;
}
void debug_curl_global_cleanup(void)
{
curl_global_cleanup();
Dout(dc::curl, "curl_global_cleanup()");
}
CURLcode debug_curl_global_init(long flags)
{
CURLcode ret;
ret = curl_global_init(flags);
Dout(dc::curl, "curl_global_init(0x" << std::hex << flags << std::dec << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle)
{
CURLMcode ret;
ret = curl_multi_add_handle(multi_handle, easy_handle);
Dout(dc::curl, "curl_multi_add_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr)
{
CURLMcode ret;
ret = curl_multi_assign(multi_handle, sockfd, sockptr);
Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << ", " << sockptr << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle)
{
CURLMcode ret;
ret = curl_multi_cleanup(multi_handle);
Dout(dc::curl, "curl_multi_cleanup(" << (AICURLM*)multi_handle << ") = " << ret);
return ret;
}
CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue)
{
CURLMsg* ret;
ret = curl_multi_info_read(multi_handle, msgs_in_queue);
Dout(dc::curl, "curl_multi_info_read(" << (AICURLM*)multi_handle << ", {" << *msgs_in_queue << "}) = " << ret);
return ret;
}
CURLM* debug_curl_multi_init(void)
{
CURLM* ret;
ret = curl_multi_init();
Dout(dc::curl, "curl_multi_init() = " << (AICURLM*)ret);
return ret;
}
CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle)
{
CURLMcode ret;
ret = curl_multi_remove_handle(multi_handle, easy_handle);
Dout(dc::curl, "curl_multi_remove_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret);
return ret;
}
CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...)
{
CURLMcode ret;
va_list ap;
union param_type {
long along;
void* ptr;
curl_off_t offset;
} param;
unsigned int param_type = (option / 10000) * 10000;
va_start(ap, option);
switch (param_type)
{
case CURLOPTTYPE_LONG:
param.along = va_arg(ap, long);
break;
case CURLOPTTYPE_OBJECTPOINT:
case CURLOPTTYPE_FUNCTIONPOINT:
param.ptr = va_arg(ap, void*);
break;
case CURLOPTTYPE_OFF_T:
param.offset = va_arg(ap, curl_off_t);
break;
default:
std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl;
std::exit(EXIT_FAILURE);
}
va_end(ap);
switch (param_type)
{
case CURLOPTTYPE_LONG:
ret = curl_multi_setopt(multi_handle, option, param.along);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret);
break;
case CURLOPTTYPE_OBJECTPOINT:
ret = curl_multi_setopt(multi_handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_FUNCTIONPOINT:
ret = curl_multi_setopt(multi_handle, option, param.ptr);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret);
break;
case CURLOPTTYPE_OFF_T:
ret = curl_multi_setopt(multi_handle, option, param.offset);
Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret);
break;
default: // Stop compiler complaining about no default.
break;
}
return ret;
}
CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles)
{
CURLMcode ret;
ret = curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles);
Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) <<
", " << EvBitmask(ev_bitmask) << ", {" << (ret == CURLM_OK ? *running_handles : 0) << "}) = " << ret);
return ret;
}
char const* debug_curl_multi_strerror(CURLMcode errornum)
{
char const* ret;
ret = curl_multi_strerror(errornum);
Dout(dc::curl, "curl_multi_strerror(" << errornum << ") = \"" << ret << '"');
return ret;
}
struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string)
{
struct curl_slist* ret;
ret = curl_slist_append(list, string);
Dout(dc::curl, "curl_slist_append((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ", \"" << string << "\") = " << *ret);
return ret;
}
void debug_curl_slist_free_all(struct curl_slist* list)
{
curl_slist_free_all(list);
Dout(dc::curl, "curl_slist_free_all((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ")");
}
char* debug_curl_unescape(char const* url, int length)
{
char* ret;
ret = curl_unescape(url, length);
Dout(dc::curl, "curl_unescape(\"" << url << "\", " << length << ") = \"" << ret << '"');
return ret;
}
char* debug_curl_version(void)
{
char* ret;
ret = curl_version();
Dout(dc::curl, "curl_version() = \"" << ret << '"');
return ret;
}
}
#else // DEBUG_CURLIO
int debug_libcurl_dummy; // I thought some OS didn't like empty source files.
#endif // DEBUG_CURLIO

View File

@@ -0,0 +1,89 @@
#ifndef DEBUG_LIBCURL
#define DEBUG_LIBCURL
#ifndef DEBUG_CURLIO
#error "Don't include debug_libcurl.h unless DEBUG_CURLIO is defined."
#endif
#ifndef CURLINFO_TYPEMASK
#error "<curl/curl.h> must be included before including debug_libcurl.h!"
#endif
#ifndef LLPREPROCESSOR_H
// CURL_STATICLIB is needed on windows namely, which is defined in llpreprocessor.h (but only on windows).
#error "llpreprocessor.h must be included before <curl/curl.h>."
#endif
extern "C" {
extern void debug_curl_easy_cleanup(CURL* handle);
extern CURL* debug_curl_easy_duphandle(CURL* handle);
extern char* debug_curl_easy_escape(CURL* curl, char* url, int length);
extern CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...);
extern CURL* debug_curl_easy_init(void);
extern CURLcode debug_curl_easy_pause(CURL* handle, int bitmask);
extern CURLcode debug_curl_easy_perform(CURL* handle);
extern void debug_curl_easy_reset(CURL* handle);
extern CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...);
extern char const* debug_curl_easy_strerror(CURLcode errornum);
extern char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength);
extern void debug_curl_free(char* ptr);
extern time_t debug_curl_getdate(char const* datestring, time_t* now);
extern void debug_curl_global_cleanup(void);
extern CURLcode debug_curl_global_init(long flags);
extern CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle);
extern CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr);
extern CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle);
extern CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue);
extern CURLM* debug_curl_multi_init(void);
extern CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle);
extern CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...);
extern CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles);
extern char const* debug_curl_multi_strerror(CURLMcode errornum);
extern struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string);
extern void debug_curl_slist_free_all(struct curl_slist* list);
extern char* debug_curl_unescape(char const* url, int length);
extern char* debug_curl_version(void);
}
#ifndef COMPILING_DEBUG_LIBCURL_CC
#ifdef curl_easy_setopt
#undef curl_easy_setopt
#undef curl_easy_getinfo
#undef curl_multi_setopt
#endif
#define curl_easy_cleanup(handle) debug_curl_easy_cleanup(handle)
#define curl_easy_duphandle(handle) debug_curl_easy_duphandle(handle)
#define curl_easy_escape(curl, url, length) debug_curl_easy_escape(curl, url, length)
#define curl_easy_getinfo(curl, info, param) debug_curl_easy_getinfo(curl, info, param)
#define curl_easy_init() debug_curl_easy_init()
#define curl_easy_pause(handle, bitmask) debug_curl_easy_pause(handle, bitmask)
#define curl_easy_perform(handle) debug_curl_easy_perform(handle)
#define curl_easy_reset(handle) debug_curl_easy_reset(handle)
#define curl_easy_setopt(handle, option, param) debug_curl_easy_setopt(handle, option, param)
#define curl_easy_strerror(errornum) debug_curl_easy_strerror(errornum)
#define curl_easy_unescape(curl, url, inlength, outlength) debug_curl_easy_unescape(curl, url, inlength, outlength)
#define curl_free(ptr) debug_curl_free(ptr)
#define curl_getdate(datestring, now) debug_curl_getdate(datestring, now)
#define curl_global_cleanup() debug_curl_global_cleanup()
#define curl_global_init(flags) debug_curl_global_init(flags)
#define curl_multi_add_handle(multi_handle, easy_handle) debug_curl_multi_add_handle(multi_handle, easy_handle)
#define curl_multi_assign(multi_handle, sockfd, sockptr) debug_curl_multi_assign(multi_handle, sockfd, sockptr)
#define curl_multi_cleanup(multi_handle) debug_curl_multi_cleanup(multi_handle)
#define curl_multi_info_read(multi_handle, msgs_in_queue) debug_curl_multi_info_read(multi_handle, msgs_in_queue)
#define curl_multi_init() debug_curl_multi_init()
#define curl_multi_remove_handle(multi_handle, easy_handle) debug_curl_multi_remove_handle(multi_handle, easy_handle)
#define curl_multi_setopt(multi_handle, option, param) debug_curl_multi_setopt(multi_handle, option, param)
#define curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) debug_curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles)
#define curl_multi_strerror(errornum) debug_curl_multi_strerror(errornum)
#define curl_slist_append(list, string) debug_curl_slist_append(list, string)
#define curl_slist_free_all(list) debug_curl_slist_free_all(list)
#define curl_unescape(url, length) debug_curl_unescape(url, length)
#define curl_version() debug_curl_version()
#endif // !COMPILING_DEBUG_LIBCURL_CC
#endif // DEBUG_LIBCURL

View File

@@ -28,7 +28,6 @@
#include "linden_common.h"
#include "llares.h"
#include "llscopedvolatileaprpool.h"
#include <ares_dns.h>
#include <ares_version.h>
@@ -38,6 +37,7 @@
#include "apr_poll.h"
#include "llapr.h"
#include "llaprpool.h"
#include "llareslistener.h"
#if defined(LL_WINDOWS)

View File

@@ -401,7 +401,7 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse
if (user_data)
{
// The *user_data should not be passed without a callback to clean it up.
llassert(callback != NULL)
llassert(callback != NULL);
}
BOOL exists = mStaticVFS->getExists(uuid, type);
@@ -441,7 +441,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL
if (user_data)
{
// The *user_data should not be passed without a callback to clean it up.
llassert(callback != NULL)
llassert(callback != NULL);
}
if (mShutDown)

View File

@@ -56,6 +56,7 @@
#include "llstl.h"
#include "lltransfermanager.h"
#include "llmodularmath.h"
#include "llpacketring.h"
const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked.
const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked.
@@ -346,7 +347,7 @@ S32 LLCircuitData::resendUnackedPackets(const F64 now)
packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend
gMessageSystem->mPacketRing.sendPacket(packetp->mSocket,
gMessageSystem->mPacketRing->sendPacket(packetp->mSocket,
(char *)packetp->mBuffer, packetp->mBufferLength,
packetp->mHost);

File diff suppressed because it is too large Load Diff

View File

@@ -1,383 +1,39 @@
/**
/**
* @file llcurl.h
* @author Zero / Donovan
* @date 2006-10-15
* @brief A wrapper around libcurl.
* @brief Drop in replacement for old llcurl.h.
*
* $LicenseInfo:firstyear=2006&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,
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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$
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 22/06/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef LL_LLCURL_H
#define LL_LLCURL_H
#include "linden_common.h"
#include "aicurl.h"
#include <sstream>
#include <string>
#include <vector>
#include <boost/intrusive_ptr.hpp>
#include <curl/curl.h> // TODO: remove dependency
#include "llbuffer.h"
#include "lliopipe.h"
#include "llsd.h"
#include "llthread.h"
class LLMutex;
// For whatever reason, this is not typedef'd in curl.h
typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
class LLCurl
{
LOG_CLASS(LLCurl);
public:
class Easy;
class Multi;
static bool sMultiThreaded;
struct TransferInfo
{
TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
F64 mSizeDownload;
F64 mTotalTime;
F64 mSpeedDownload;
};
class Responder
{
//LOG_CLASS(Responder);
public:
Responder();
virtual ~Responder();
/**
* @brief return true if the status code indicates success.
*/
static bool isGoodStatus(U32 status)
{
return((200 <= status) && (status < 300));
}
virtual void errorWithContent(
U32 status,
const std::string& reason,
const LLSD& content);
//< called by completed() on bad status
virtual void error(U32 status, const std::string& reason);
//< called by default error(status, reason, content)
virtual void result(const LLSD& content);
//< called by completed for good status codes.
virtual void completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
/**< Override point for clients that may want to use this
class when the response is some other format besides LLSD
*/
virtual void completed(
U32 status,
const std::string& reason,
const LLSD& content);
/**< The default implemetnation calls
either:
* result(), or
* error()
*/
// Override to handle parsing of the header only. Note: this is the only place where the contents
// of the header can be parsed. In the ::completed call above only the body is contained in the LLSD.
virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content);
// Used internally to set the url for debugging later.
void setURL(const std::string& url);
virtual bool followRedir()
{
return false;
}
public: /* but not really -- don't touch this */
U32 mReferenceCount;
private:
std::string mURL;
};
typedef boost::intrusive_ptr<Responder> ResponderPtr;
/**
* @ brief Set certificate authority file used to verify HTTPS certs.
*/
static void setCAFile(const std::string& file);
/**
* @ brief Set certificate authority path used to verify HTTPS certs.
*/
static void setCAPath(const std::string& path);
/**
* @ brief Return human-readable string describing libcurl version.
*/
static std::string getVersionString();
/**
* @ brief Get certificate authority file used to verify HTTPS certs.
*/
static const std::string& getCAFile() { return sCAFile; }
/**
* @ brief Get certificate authority path used to verify HTTPS certs.
*/
static const std::string& getCAPath() { return sCAPath; }
/**
* @ brief Initialize LLCurl class
*/
static void initClass(bool multi_threaded = false);
/**
* @ brief Cleanup LLCurl class
*/
static void cleanupClass();
/**
* @ brief curl error code -> string
*/
static std::string strerror(CURLcode errorcode);
// For OpenSSL callbacks
static std::vector<LLMutex*> sSSLMutex;
// OpenSSL callbacks
static void ssl_locking_callback(int mode, int type, const char *file, int line);
static unsigned long ssl_thread_id(void);
private:
static std::string sCAPath;
static std::string sCAFile;
static const unsigned int MAX_REDIRECTS;
};
class LLCurl::Easy
{
LOG_CLASS(Easy);
private:
Easy();
public:
static Easy* getEasy();
~Easy();
CURL* getCurlHandle() const { return mCurlEasyHandle; }
void setErrorBuffer();
void setCA();
void setopt(CURLoption option, S32 value);
// These assume the setter does not free value!
void setopt(CURLoption option, void* value);
void setopt(CURLoption option, char* value);
// Copies the string so that it is gauranteed to stick around
void setoptString(CURLoption option, const std::string& value);
void slist_append(const char* str);
void setHeaders();
U32 report(CURLcode);
void getTransferInfo(LLCurl::TransferInfo* info);
void prepRequest(const std::string& url, const std::vector<std::string>& headers, ResponderPtr, S32 time_out = 0, bool post = false);
const char* getErrorBuffer();
std::stringstream& getInput() { return mInput; }
std::stringstream& getHeaderOutput() { return mHeaderOutput; }
LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
const LLChannelDescriptors& getChannels() { return mChannels; }
void resetState();
static CURL* allocEasyHandle();
static void releaseEasyHandle(CURL* handle);
private:
friend class LLCurl;
friend class LLCurl::Multi;
CURL* mCurlEasyHandle;
struct curl_slist* mHeaders;
std::stringstream mRequest;
LLChannelDescriptors mChannels;
LLIOPipe::buffer_ptr_t mOutput;
std::stringstream mInput;
std::stringstream mHeaderOutput;
char mErrorBuffer[CURL_ERROR_SIZE];
// Note: char*'s not strings since we pass pointers to curl
std::vector<char*> mStrings;
ResponderPtr mResponder;
static std::set<CURL*> sFreeHandles;
static std::set<CURL*> sActiveHandles;
static LLMutex* sHandleMutex;
static LLMutex* sMultiMutex;
};
class LLCurl::Multi : public LLThread
{
LOG_CLASS(Multi);
public:
typedef enum
{
PERFORM_STATE_READY=0,
PERFORM_STATE_PERFORMING=1,
PERFORM_STATE_COMPLETED=2
} ePerformState;
Multi();
~Multi();
Easy* allocEasy();
bool addEasy(Easy* easy);
void removeEasy(Easy* easy);
S32 process();
void perform();
void doPerform();
virtual void run();
CURLMsg* info_read(S32* msgs_in_queue);
S32 mQueued;
S32 mErrorCount;
S32 mPerformState;
LLCondition* mSignal;
bool mQuitting;
bool mThreaded;
private:
void easyFree(Easy*);
CURLM* mCurlMultiHandle;
typedef std::set<Easy*> easy_active_list_t;
easy_active_list_t mEasyActiveList;
typedef std::map<CURL*, Easy*> easy_active_map_t;
easy_active_map_t mEasyActiveMap;
typedef std::set<Easy*> easy_free_list_t;
easy_free_list_t mEasyFreeList;
};
// DONT UNCOMMENT BREAKS GCC47
//namespace boost
//{
void intrusive_ptr_add_ref(LLCurl::Responder* p);
void intrusive_ptr_release(LLCurl::Responder* p);
//};
class LLCurlRequest
{
public:
typedef std::vector<std::string> headers_t;
LLCurlRequest();
~LLCurlRequest();
void get(const std::string& url, LLCurl::ResponderPtr responder);
bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder);
bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0);
S32 process();
S32 getQueued();
private:
void addMulti();
LLCurl::Easy* allocEasy();
bool addEasy(LLCurl::Easy* easy);
private:
typedef std::set<LLCurl::Multi*> curlmulti_set_t;
curlmulti_set_t mMultiSet;
LLCurl::Multi* mActiveMulti;
S32 mActiveRequestCount;
BOOL mProcessing;
U32 mThreadID; // debug
};
class LLCurlEasyRequest
{
public:
LLCurlEasyRequest();
~LLCurlEasyRequest();
void setopt(CURLoption option, S32 value);
void setoptString(CURLoption option, const std::string& value);
void setPost(char* postdata, S32 size);
void setHeaderCallback(curl_header_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
void slist_append(const char* str);
void sendRequest(const std::string& url);
void requestComplete();
void perform();
bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
std::string getErrorString();
LLCurl::Easy* getEasy() const { return mEasy; }
private:
CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info);
private:
LLCurl::Multi* mMulti;
LLCurl::Easy* mEasy;
bool mRequestSent;
bool mResultReturned;
};
// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.
namespace LLCurlFF
{
void check_easy_code(CURLcode code);
void check_multi_code(CURLMcode code);
}
// Map interface to old LLCurl names so this can be used as a drop-in replacement.
namespace LLCurl = AICurlInterface;
#endif // LL_LLCURL_H

View File

@@ -25,7 +25,6 @@
*/
#include "linden_common.h"
#include "llhttpclient.h"
#include "llassetstorage.h"
@@ -38,10 +37,8 @@
#include "lluri.h"
#include "message.h"
#include <curl/curl.h>
const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
////////////////////////////////////////////////////////////////////////////
// Responder class moved to LLCurl
@@ -122,7 +119,7 @@ namespace
{
public:
RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {}
virtual ~RawInjector() {delete mData;}
virtual ~RawInjector() {delete [] mData;}
const char* contentType() { return "application/octet-stream"; }
@@ -156,9 +153,9 @@ namespace
if(fstream.is_open())
{
fstream.seekg(0, std::ios::end);
U32 fileSize = (U32)fstream.tellg();
U32 fileSize = fstream.tellg();
fstream.seekg(0, std::ios::beg);
std::vector<char> fileBuffer(fileSize); //Mem leak fix'd
std::vector<char> fileBuffer(fileSize);
fstream.read(&fileBuffer[0], fileSize);
ostream.write(&fileBuffer[0], fileSize);
fstream.close();
@@ -209,20 +206,45 @@ static void request(
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS,
const LLSD& headers = LLSD())
{
if (responder)
{
// For possible debug output from within the responder.
responder->setURL(url);
}
if (!LLHTTPClient::hasPump())
{
responder->completed(U32_MAX, "No pump", LLSD());
responder->fatalError("No pump");
return;
}
LLPumpIO::chain_t chain;
LLURLRequest* req = new LLURLRequest(method, url);
LLURLRequest* req;
try
{
req = new LLURLRequest(method, url);
}
catch(AICurlNoEasyHandle& error)
{
llwarns << "Failed to create LLURLRequest: " << error.what() << llendl;
// This is what the old LL code did: no recovery whatsoever (but also no leaks or crash).
return ;
}
req->checkRootCertificate(true);
// Insert custom headers is the caller sent any
if (headers.isMap())
{
lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " "
<< headers << llendl;
// Insert custom headers if the caller sent any
if (headers.isMap())
{
if (headers.has("Cookie"))
{
req->allowCookies();
}
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
@@ -257,11 +279,6 @@ static void request(
}
}
if (responder)
{
responder->setURL(url);
}
req->setCallback(new LLHTTPClientURLAdaptor(responder));
if (method == LLURLRequest::HTTP_POST && gMessageSystem)
@@ -308,7 +325,7 @@ void LLHTTPClient::getByteRange(
std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1);
headers["Range"] = range;
}
request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
}
void LLHTTPClient::head(
@@ -347,12 +364,12 @@ class LLHTTPBuffer
public:
LLHTTPBuffer() { }
static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data)
static size_t curl_write(char* ptr, size_t size, size_t nmemb, void* user_data)
{
LLHTTPBuffer* self = (LLHTTPBuffer*)user_data;
size_t bytes = (size * nmemb);
self->mBuffer.append((char*)ptr,bytes);
self->mBuffer.append(ptr,bytes);
return nmemb;
}
@@ -403,99 +420,82 @@ static LLSD blocking_request(
)
{
lldebugs << "blockingRequest of " << url << llendl;
char curl_error_buffer[CURL_ERROR_SIZE] = "\0";
CURL* curlp = curl_easy_init();
LLHTTPBuffer http_buffer;
std::string body_str;
// other request method checks root cert first, we skip?
// * Set curl handle options
curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts
curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function.
curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);
curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);
curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer);
// * Setup headers (don't forget to free them after the call!)
curl_slist* headers_list = NULL;
if (headers.isMap())
S32 http_status = 499;
LLSD response = LLSD::emptyMap();
try
{
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
for (; iter != end; ++iter)
AICurlEasyRequest easy_request(false);
AICurlEasyRequest_wat curlEasyRequest_w(*easy_request);
LLHTTPBuffer http_buffer;
// * Set curl handle options
curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function.
curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer);
// * Setup headers.
if (headers.isMap())
{
std::ostringstream header;
header << iter->first << ": " << iter->second.asString() ;
lldebugs << "header = " << header.str() << llendl;
headers_list = curl_slist_append(headers_list, header.str().c_str());
LLSD::map_const_iterator iter = headers.beginMap();
LLSD::map_const_iterator end = headers.endMap();
for (; iter != end; ++iter)
{
std::ostringstream header;
header << iter->first << ": " << iter->second.asString() ;
lldebugs << "header = " << header.str() << llendl;
curlEasyRequest_w->addHeader(header.str().c_str());
}
}
// Needs to stay alive until after the call to perform().
std::ostringstream ostr;
// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
if (method == LLURLRequest::HTTP_GET)
{
curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
}
else if (method == LLURLRequest::HTTP_POST)
{
//copied from PHP libs, correct?
curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml");
LLSDSerialize::toXML(body, ostr);
curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length());
}
// * Do the action using curl, handle results
curlEasyRequest_w->addHeader("Accept: application/llsd+xml");
curlEasyRequest_w->finalizeRequest(url);
S32 curl_success = curlEasyRequest_w->perform();
curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &http_status);
// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
{
// We expect 404s, don't spam for them.
llwarns << "CURL REQ URL: " << url << llendl;
llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
llwarns << "CURL REQ BODY: " << ostr.str() << llendl;
llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl;
llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
response["body"] = http_buffer.asString();
}
else
{
response["body"] = http_buffer.asLLSD();
lldebugs << "CURL response: " << http_buffer.asString() << llendl;
}
}
// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy)
if (method == LLURLRequest::HTTP_GET)
catch(AICurlNoEasyHandle const& error)
{
curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1);
}
else if (method == LLURLRequest::HTTP_POST)
{
curl_easy_setopt(curlp, CURLOPT_POST, 1);
//serialize to ostr then copy to str - need to because ostr ptr is unstable :(
std::ostringstream ostr;
LLSDSerialize::toXML(body, ostr);
body_str = ostr.str();
curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str());
//copied from PHP libs, correct?
headers_list = curl_slist_append(headers_list, "Content-Type: application/llsd+xml");
// copied from llurlrequest.cpp
// it appears that apache2.2.3 or django in etch is busted. If
// we do not clear the expect header, we get a 500. May be
// limited to django/mod_wsgi.
headers_list = curl_slist_append(headers_list, "Expect:");
}
// * Do the action using curl, handle results
lldebugs << "HTTP body: " << body_str << llendl;
headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml");
CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list);
if ( curl_result != CURLE_OK )
{
llinfos << "Curl is hosed - can't add headers" << llendl;
response["body"] = error.what();
}
LLSD response = LLSD::emptyMap();
S32 curl_success = curl_easy_perform(curlp);
S32 http_status = 499;
curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status);
response["status"] = http_status;
// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits,
if ( http_status != 404 && (http_status != 200 || curl_success != 0) )
{
// We expect 404s, don't spam for them.
llwarns << "CURL REQ URL: " << url << llendl;
llwarns << "CURL REQ METHOD TYPE: " << method << llendl;
llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl;
llwarns << "CURL REQ BODY: " << body_str << llendl;
llwarns << "CURL HTTP_STATUS: " << http_status << llendl;
llwarns << "CURL ERROR: " << curl_error_buffer << llendl;
llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;
response["body"] = http_buffer.asString();
}
else
{
response["body"] = http_buffer.asLLSD();
lldebugs << "CURL response: " << http_buffer.asString() << llendl;
}
if(headers_list)
{ // free the header list
curl_slist_free_all(headers_list);
}
// * Cleanup
curl_easy_cleanup(curlp);
return response;
}
@@ -595,7 +595,8 @@ bool LLHTTPClient::hasPump()
return theClientPump != NULL;
}
LLPumpIO &LLHTTPClient::getPump()
//static
LLPumpIO& LLHTTPClient::getPump()
{
return *theClientPump;
}

View File

@@ -34,7 +34,6 @@
#include <string>
#include <boost/intrusive_ptr.hpp>
#include "llassettype.h"
#include "llcurl.h"
#include "lliopipe.h"
@@ -56,6 +55,9 @@ public:
typedef LLCurl::Responder Responder;
typedef LLCurl::ResponderPtr ResponderPtr;
// The default actually already ignores responses.
class ResponderIgnore : public Responder { };
/** @name non-blocking API */
//@{
static void head(

View File

@@ -76,7 +76,14 @@ LLIOPipe::~LLIOPipe()
}
//virtual
bool LLIOPipe::isValid()
bool LLIOPipe::hasExpiration(void) const
{
// LLIOPipe::hasNotExpired always returns true.
return false;
}
//virtual
bool LLIOPipe::hasNotExpired(void) const
{
return true ;
}

View File

@@ -149,7 +149,7 @@ public:
// The connection was lost.
STATUS_LOST_CONNECTION = -5,
// The totoal process time has exceeded the timeout.
// The total process time has exceeded the timeout.
STATUS_EXPIRED = -6,
// Keep track of the count of codes here.
@@ -231,7 +231,16 @@ public:
*/
virtual ~LLIOPipe();
virtual bool isValid() ;
/**
* @brief External expiration facility.
*
* If hasExpiration() returns true, then we need to check hasNotExpired()
* to see if the LLIOPipe is still valid. In the legacy LL code the
* latter was called isValid() and was overloaded for two purposes:
* either expiration or failure to initialize.
*/
virtual bool hasExpiration(void) const;
virtual bool hasNotExpired(void) const;
protected:
/**

View File

@@ -32,7 +32,7 @@
#include "llhost.h"
#include "llpacketbuffer.h"
#include "llproxy.h"
//#include "llproxy.h"
#include "llthrottle.h"
#include "net.h"

View File

@@ -29,7 +29,6 @@
#include "llproxy.h"
#include <string>
#include <curl/curl.h>
#include "llapr.h"
#include "llcurl.h"
@@ -47,23 +46,22 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou
static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host
static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel
LLProxy::LLProxy():
mHTTPProxyEnabled(false),
mProxyMutex(),
mUDPProxy(),
mTCPProxy(),
mHTTPProxy(),
ProxyShared::ProxyShared(void):
mProxyType(LLPROXY_SOCKS),
mAuthMethodSelected(METHOD_NOAUTH),
mSocksUsername(),
mSocksPassword()
mAuthMethodSelected(METHOD_NOAUTH)
{
}
LLProxy::LLProxy():
mHTTPProxyEnabled(false)
{
}
LLProxy::~LLProxy()
{
stopSOCKSProxy();
disableHTTPProxy();
Shared_wat shared_w(mShared);
disableHTTPProxy(shared_w);
}
/**
@@ -78,15 +76,18 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
{
S32 result;
Unshared_rat unshared_r(mUnshared);
Shared_rat shared_r(mShared);
/* SOCKS 5 Auth request */
socks_auth_request_t socks_auth_request;
socks_auth_response_t socks_auth_response;
socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5
socks_auth_request.num_methods = 1; // Sending 1 method.
socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method.
socks_auth_request.methods = getSelectedAuthMethod(shared_r); // Send only the selected method.
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
static_cast<char*>(static_cast<void*>(&socks_auth_request)),
sizeof(socks_auth_request),
static_cast<char*>(static_cast<void*>(&socks_auth_response)),
@@ -109,8 +110,8 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
if (socks_auth_response.method == METHOD_PASSWORD)
{
// The server has requested a username/password combination
std::string socks_username(getSocksUser());
std::string socks_password(getSocksPwd());
std::string socks_username(getSocksUser(shared_r));
std::string socks_password(getSocksPwd(shared_r));
U32 request_size = socks_username.size() + socks_password.size() + 3;
char * password_auth = new char[request_size];
password_auth[0] = 0x01;
@@ -121,7 +122,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
authmethod_password_reply_t password_reply;
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
password_auth,
request_size,
static_cast<char*>(static_cast<void*>(&password_reply)),
@@ -157,7 +158,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
// "If the client is not in possession of the information at the time of the UDP ASSOCIATE,
// the client MUST use a port number and address of all zeros. RFC 1928"
result = tcp_blocking_handshake(mProxyControlChannel,
result = tcp_blocking_handshake(unshared_r->mProxyControlChannel,
static_cast<char*>(static_cast<void*>(&connect_request)),
sizeof(connect_request),
static_cast<char*>(static_cast<void*>(&connect_reply)),
@@ -176,10 +177,14 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
return SOCKS_UDP_FWD_NOT_GRANTED;
}
mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order
mUDPProxy.setAddress(proxy.getAddress());
{
// Write access type and read access type are really the same, so unshared_w must be simply a reference.
Unshared_wat& unshared_w = unshared_r;
unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order
unshared_w->mUDPProxy.setAddress(proxy.getAddress());
}
// The connection was successful. We now have the UDP port to send requests that need forwarding to.
LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL;
LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << unshared_r->mUDPProxy << LL_ENDL;
return SOCKS_OK;
}
@@ -197,9 +202,11 @@ S32 LLProxy::proxyHandshake(LLHost proxy)
*/
S32 LLProxy::startSOCKSProxy(LLHost host)
{
Unshared_wat unshared_w(mUnshared);
if (host.isOk())
{
mTCPProxy = host;
unshared_w->mTCPProxy = host;
}
else
{
@@ -209,13 +216,13 @@ S32 LLProxy::startSOCKSProxy(LLHost host)
// Close any running SOCKS connection.
stopSOCKSProxy();
mProxyControlChannel = tcp_open_channel(mTCPProxy);
if (!mProxyControlChannel)
unshared_w->mProxyControlChannel = tcp_open_channel(unshared_w->mTCPProxy);
if (!unshared_w->mProxyControlChannel)
{
return SOCKS_HOST_CONNECT_FAILED;
}
S32 status = proxyHandshake(mTCPProxy);
S32 status = proxyHandshake(unshared_w->mTCPProxy);
if (status != SOCKS_OK)
{
@@ -246,14 +253,16 @@ void LLProxy::stopSOCKSProxy()
// then we must shut down any HTTP proxy operations. But it is allowable if web
// proxy is being used to continue proxying HTTP.
if (LLPROXY_SOCKS == getHTTPProxyType())
Shared_rat shared_r(mShared);
if (LLPROXY_SOCKS == getHTTPProxyType(shared_r))
{
disableHTTPProxy();
Shared_wat shared_w(shared_r);
disableHTTPProxy(shared_w);
}
if (mProxyControlChannel)
Unshared_wat unshared_w(mUnshared);
if (unshared_w->mProxyControlChannel)
{
tcp_close_channel(&mProxyControlChannel);
tcp_close_channel(&unshared_w->mProxyControlChannel);
}
}
@@ -262,9 +271,7 @@ void LLProxy::stopSOCKSProxy()
*/
void LLProxy::setAuthNone()
{
LLMutexLock lock(&mProxyMutex);
mAuthMethodSelected = METHOD_NOAUTH;
Shared_wat(mShared)->mAuthMethodSelected = METHOD_NOAUTH;
}
/**
@@ -288,11 +295,10 @@ bool LLProxy::setAuthPassword(const std::string &username, const std::string &pa
return false;
}
LLMutexLock lock(&mProxyMutex);
mAuthMethodSelected = METHOD_PASSWORD;
mSocksUsername = username;
mSocksPassword = password;
Shared_wat shared_w(mShared);
shared_w->mAuthMethodSelected = METHOD_PASSWORD;
shared_w->mSocksUsername = username;
shared_w->mSocksPassword = password;
return true;
}
@@ -314,12 +320,10 @@ bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type)
return false;
}
LLMutexLock lock(&mProxyMutex);
mHTTPProxy = httpHost;
mProxyType = type;
Shared_wat shared_w(mShared);
mHTTPProxyEnabled = true;
shared_w->mHTTPProxy = httpHost;
shared_w->mProxyType = type;
return true;
}
@@ -335,9 +339,8 @@ bool LLProxy::enableHTTPProxy()
{
bool ok;
LLMutexLock lock(&mProxyMutex);
ok = (mHTTPProxy.isOk());
Shared_rat shared_r(mShared);
ok = (shared_r->mHTTPProxy.isOk());
if (ok)
{
mHTTPProxyEnabled = true;
@@ -346,54 +349,6 @@ bool LLProxy::enableHTTPProxy()
return ok;
}
/**
* @brief Disable the HTTP proxy.
*/
void LLProxy::disableHTTPProxy()
{
LLMutexLock lock(&mProxyMutex);
mHTTPProxyEnabled = false;
}
/**
* @brief Get the currently selected HTTP proxy type
*/
LLHttpProxyType LLProxy::getHTTPProxyType() const
{
LLMutexLock lock(&mProxyMutex);
return mProxyType;
}
/**
* @brief Get the SOCKS 5 password.
*/
std::string LLProxy::getSocksPwd() const
{
LLMutexLock lock(&mProxyMutex);
return mSocksPassword;
}
/**
* @brief Get the SOCKS 5 username.
*/
std::string LLProxy::getSocksUser() const
{
LLMutexLock lock(&mProxyMutex);
return mSocksUsername;
}
/**
* @brief Get the currently selected SOCKS 5 authentication method.
*
* @return Returns either none or password.
*/
LLSocks5AuthType LLProxy::getSelectedAuthMethod() const
{
LLMutexLock lock(&mProxyMutex);
return mAuthMethodSelected;
}
/**
* @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR.
*
@@ -406,57 +361,6 @@ void LLProxy::cleanupClass()
deleteSingleton();
}
void LLProxy::applyProxySettings(LLCurlEasyRequest* handle)
{
applyProxySettings(handle->getEasy());
}
void LLProxy::applyProxySettings(LLCurl::Easy* handle)
{
applyProxySettings(handle->getCurlHandle());
}
/**
* @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled.
*
* This method has been designed to be safe to call from
* any thread in the viewer. This allows requests in the
* texture fetch thread to be aware of the proxy settings.
* When the HTTP proxy is enabled, the proxy mutex will
* be locked every time this method is called.
*
* @param handle A pointer to a valid CURL request, before it has been performed.
*/
void LLProxy::applyProxySettings(CURL* handle)
{
// Do a faster unlocked check to see if we are supposed to proxy.
if (mHTTPProxyEnabled)
{
// We think we should proxy, lock the proxy mutex.
LLMutexLock lock(&mProxyMutex);
// Now test again to verify that the proxy wasn't disabled between the first check and the lock.
if (mHTTPProxyEnabled)
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()));
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()));
if (mProxyType == LLPROXY_SOCKS)
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5));
if (mAuthMethodSelected == METHOD_PASSWORD)
{
std::string auth_string = mSocksUsername + ":" + mSocksPassword;
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()));
}
}
else
{
LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP));
}
}
}
}
/**
* @brief Send one TCP packet and receive one in return.
*

View File

@@ -33,6 +33,7 @@
#include "llmemory.h"
#include "llsingleton.h"
#include "llthread.h"
#include "aithreadsafe.h"
#include <string>
// SOCKS error codes returned from the StartProxy method
@@ -206,41 +207,92 @@ enum LLSocks5AuthType
* The implementation of HTTP proxying is handled by libcurl. LLProxy
* is responsible for managing the HTTP proxy options and provides a
* thread-safe method to apply those options to a curl request
* (LLProxy::applyProxySettings()). This method is overloaded
* to accommodate the various abstraction libcurl layers that exist
* throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL).
*
* If you are working with LLCurl or LLCurlEasyRequest objects,
* the configured proxy settings will be applied in the constructors
* of those request handles. If you are working with CURL objects
* directly, you will need to pass the handle of the request to
* applyProxySettings() before issuing the request.
* (LLProxy::applyProxySettings()).
*
* To ensure thread safety, all LLProxy members that relate to the HTTP
* proxy require the LLProxyMutex to be locked before accessing.
*/
struct ProxyUnshared
{
/*###########################################################################################
MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD.
###########################################################################################*/
// UDP proxy address and port
LLHost mUDPProxy;
// TCP proxy control channel address and port
LLHost mTCPProxy;
// socket handle to proxy TCP control channel
LLSocket::ptr_t mProxyControlChannel;
/*###########################################################################################
END OF UNSHARED MEMBERS
###########################################################################################*/
};
struct ProxyShared
{
ProxyShared(void);
/*###########################################################################################
MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD.
###########################################################################################*/
// HTTP proxy address and port
LLHost mHTTPProxy;
// Currently selected HTTP proxy type. Can be web or SOCKS.
LLHttpProxyType mProxyType;
// SOCKS 5 selected authentication method.
LLSocks5AuthType mAuthMethodSelected;
// SOCKS 5 username
std::string mSocksUsername;
// SOCKS 5 password
std::string mSocksPassword;
/*###########################################################################################
END OF SHARED MEMBERS
###########################################################################################*/
};
class LLProxy: public LLSingleton<LLProxy>
{
LOG_CLASS(LLProxy);
public:
typedef AISTAccessConst<ProxyUnshared> Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access).
typedef AISTAccess<ProxyUnshared> Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all).
typedef AISTAccess<ProxyUnshared> Unshared_wat; // Write Access Type, for Unshared.
typedef AIReadAccessConst<ProxyShared> Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access).
typedef AIReadAccess<ProxyShared> Shared_rat; // Read Access Type for Shared.
typedef AIWriteAccess<ProxyShared> Shared_wat; // Write Access Type for Shared.
/*###########################################################################################
METHODS THAT DO NOT LOCK mProxyMutex!
Public methods that only access variables not shared between threads.
###########################################################################################*/
// Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only.
LLProxy();
// Static check for enabled status for UDP packets. Call from main thread only.
static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; }
// Static check for enabled status for UDP packets. Called from main thread only.
static bool isSOCKSProxyEnabled(void) { llassert(is_main_thread()); return sUDPProxyEnabled; }
// Get the UDP proxy address and port. Call from main thread only.
LLHost getUDPProxy() const { return mUDPProxy; }
// Get the UDP proxy address and port. Called from main thread only.
LLHost getUDPProxy(void) const { return Unshared_crat(mUnshared)->mUDPProxy; }
/*###########################################################################################
END OF NON-LOCKING METHODS
End of methods that only access variables not shared between threads.
###########################################################################################*/
// Return true if there is a good chance that the HTTP proxy is currently enabled.
bool HTTPProxyEnabled(void) const { return mHTTPProxyEnabled; }
/*###########################################################################################
METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED!
Public methods that access variables shared between threads.
###########################################################################################*/
// Destructor, closes open connections. Do not call directly, use cleanupClass().
~LLProxy();
@@ -251,9 +303,7 @@ public:
// Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false.
// Safe to call from any thread.
void applyProxySettings(CURL* handle);
void applyProxySettings(LLCurl::Easy* handle);
void applyProxySettings(LLCurlEasyRequest* handle);
void applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w);
// Start a connection to the SOCKS 5 proxy. Call from main thread only.
S32 startSOCKSProxy(LLHost host);
@@ -273,30 +323,37 @@ public:
bool enableHTTPProxy();
// Stop proxying HTTP packets. Call from main thread only.
void disableHTTPProxy();
// Note that this needs shared_w to be passed because we want the shared members to be locked when this is reset to false.
void disableHTTPProxy(Shared_wat const& shared_w) { mHTTPProxyEnabled = false; }
void disableHTTPProxy(void) { disableHTTPProxy(Shared_wat(mShared)); }
// Get the currently selected HTTP proxy address and port
LLHost const& getHTTPProxy(Shared_crat const& shared_r) const { return shared_r->mHTTPProxy; }
// Get the currently selected HTTP proxy type
LLHttpProxyType getHTTPProxyType(Shared_crat const& shared_r) const { return shared_r->mProxyType; }
// Get the currently selected auth method.
LLSocks5AuthType getSelectedAuthMethod(Shared_crat const& shared_r) const { return shared_r->mAuthMethodSelected; }
// SOCKS 5 username and password accessors.
std::string getSocksUser(Shared_crat const& shared_r) const { return shared_r->mSocksUsername; }
std::string getSocksPwd(Shared_crat const& shared_r) const { return shared_r->mSocksPassword; }
/*###########################################################################################
END OF LOCKING METHODS
End of methods that access variables shared between threads.
###########################################################################################*/
private:
/*###########################################################################################
METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED!
Private methods that access variables shared between threads.
###########################################################################################*/
// Perform a SOCKS 5 authentication and UDP association with the proxy server.
S32 proxyHandshake(LLHost proxy);
// Get the currently selected auth method.
LLSocks5AuthType getSelectedAuthMethod() const;
// Get the currently selected HTTP proxy type
LLHttpProxyType getHTTPProxyType() const;
std::string getSocksPwd() const;
std::string getSocksUser() const;
/*###########################################################################################
END OF LOCKING METHODS
End of methods that access variables shared between threads.
###########################################################################################*/
private:
@@ -304,49 +361,16 @@ private:
// Instead use enableHTTPProxy() and disableHTTPProxy() instead.
mutable LLAtomic32<bool> mHTTPProxyEnabled;
// Mutex to protect shared members in non-main thread calls to applyProxySettings().
mutable LLMutex mProxyMutex;
/*###########################################################################################
MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. DO NOT SHARE!
###########################################################################################*/
// Is the UDP proxy enabled?
static bool sUDPProxyEnabled;
// UDP proxy address and port
LLHost mUDPProxy;
// TCP proxy control channel address and port
LLHost mTCPProxy;
AIThreadSafeSingleThreadDC<ProxyUnshared> mUnshared;
AIThreadSafeDC<ProxyShared> mShared;
// socket handle to proxy TCP control channel
LLSocket::ptr_t mProxyControlChannel;
/*###########################################################################################
END OF UNSHARED MEMBERS
###########################################################################################*/
/*###########################################################################################
MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. ONLY READ OR WRITE AFTER LOCKING mProxyMutex!
###########################################################################################*/
// HTTP proxy address and port
LLHost mHTTPProxy;
// Currently selected HTTP proxy type. Can be web or socks.
LLHttpProxyType mProxyType;
// SOCKS 5 selected authentication method.
LLSocks5AuthType mAuthMethodSelected;
// SOCKS 5 username
std::string mSocksUsername;
// SOCKS 5 password
std::string mSocksPassword;
/*###########################################################################################
END OF SHARED MEMBERS
###########################################################################################*/
public:
// For thread-safe read access. Use the _crat access types with these.
AIThreadSafeSingleThreadDC<ProxyUnshared> const& unshared_lockobj(void) const { return mUnshared; }
AIThreadSafeDC<ProxyShared> const& shared_lockobj(void) const { return mShared; }
};
#endif

View File

@@ -4,31 +4,25 @@
* @date 2004-11-21
* @brief Implementation of the i/o pump and related functions.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* 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.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* 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$
*/
@@ -40,12 +34,11 @@
#include "apr_poll.h"
#include "llapr.h"
#include "llfasttimer.h"
#include "llmemtype.h"
#include "llstl.h"
#include "llstat.h"
#include "llthread.h"
#include "llfasttimer.h"
#include <iterator> //VS2010
// These should not be enabled in production, but they can be
// intensely useful during development for finding certain kinds of
@@ -191,17 +184,28 @@ LLPumpIO::LLPumpIO(void) :
LLPumpIO::~LLPumpIO()
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
cleanup();
#if LL_THREADS_APR
if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
#endif
mChainsMutex = NULL;
mCallbackMutex = NULL;
if(mPollset)
{
// lldebugs << "cleaning up pollset" << llendl;
apr_pollset_destroy(mPollset);
mPollset = NULL;
}
}
bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
bool LLPumpIO::addChain(chain_t const& chain, F32 timeout)
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
if(chain.empty()) return false;
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
chain_t::const_iterator it = chain.begin();
chain_t::const_iterator const end = chain.end();
if (it == end) return false;
LLChainInfo info;
info.setTimeoutSeconds(timeout);
info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
@@ -212,14 +216,17 @@ bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
#else
lldebugs << "LLPumpIO::addChain() " << chain[0] <<llendl;
#endif
chain_t::const_iterator it = chain.begin();
chain_t::const_iterator end = chain.end();
for(; it != end; ++it)
{
info.mHasExpiration = info.mHasExpiration || (*it)->hasExpiration();
link.mPipe = (*it);
link.mChannels = info.mData->nextChannel();
info.mChainLinks.push_back(link);
}
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
mPendingChains.push_back(info);
return true;
}
@@ -236,11 +243,10 @@ bool LLPumpIO::addChain(
// description, we need to have that description matched to a
// particular buffer.
if(!data) return false;
if(links.empty()) return false;
links_t::const_iterator link = links.begin();
links_t::const_iterator const end = links.end();
if (link == end) return false;
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
#if LL_DEBUG_PIPE_TYPE_IN_PUMP
lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '"
<< typeid(*(links[0].mPipe)).name() << "'" << llendl;
@@ -252,6 +258,17 @@ bool LLPumpIO::addChain(
info.mChainLinks = links;
info.mData = data;
info.mContext = context;
for (; link != end; ++link)
{
if (link->mPipe->hasExpiration())
{
info.mHasExpiration = true;
break;
}
}
#if LL_THREADS_APR
LLScopedLock lock(mChainsMutex);
#endif
mPendingChains.push_back(info);
return true;
}
@@ -438,6 +455,15 @@ void LLPumpIO::pump()
static LLFastTimer::DeclareTimer FTM_PUMP_IO("Pump IO");
LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain)
{
std::for_each(
(*run_chain).mDescriptors.begin(),
(*run_chain).mDescriptors.end(),
ll_delete_apr_pollset_fd_client_data());
return mRunningChains.erase(run_chain);
}
//timeout is in microseconds
void LLPumpIO::pump(const S32& poll_timeout)
{
@@ -583,10 +609,16 @@ void LLPumpIO::pump(const S32& poll_timeout)
// << (*run_chain).mChainLinks[0].mPipe
// << " because we reached the end." << llendl;
#endif
run_chain = mRunningChains.erase(run_chain);
run_chain = removeRunningChain(run_chain);
continue;
}
}
else if(isChainExpired(*run_chain))
{
run_chain = removeRunningChain(run_chain);
continue;
}
PUMP_DEBUG;
if((*run_chain).mLock)
{
@@ -694,11 +726,7 @@ void LLPumpIO::pump(const S32& poll_timeout)
PUMP_DEBUG;
// This chain is done. Clean up any allocated memory and
// erase the chain info.
std::for_each(
(*run_chain).mDescriptors.begin(),
(*run_chain).mDescriptors.end(),
ll_delete_apr_pollset_fd_client_data());
run_chain = mRunningChains.erase(run_chain);
run_chain = removeRunningChain(run_chain);
// *NOTE: may not always need to rebuild the pollset.
mRebuildPollset = true;
@@ -833,22 +861,6 @@ void LLPumpIO::initialize(void)
apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
#endif
}
void LLPumpIO::cleanup()
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
#if LL_THREADS_APR
if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
#endif
mChainsMutex = NULL;
mCallbackMutex = NULL;
if(mPollset)
{
// lldebugs << "cleaning up pollset" << llendl;
apr_pollset_destroy(mPollset);
mPollset = NULL;
}
}
void LLPumpIO::rebuildPollset()
{
@@ -1083,10 +1095,30 @@ void LLPumpIO::processChain(LLChainInfo& chain)
PUMP_DEBUG;
}
bool LLPumpIO::isChainExpired(LLChainInfo& chain)
{
if(!chain.mHasExpiration)
{
return false ;
}
for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter)
{
if(!(*iter).mPipe->hasNotExpired())
{
return true ;
}
}
return false ;
}
bool LLPumpIO::handleChainError(
LLChainInfo& chain,
LLIOPipe::EStatus error)
{
DoutEntering(dc::notice, "LLPumpIO::handleChainError(" << (void*)&chain << ", " << LLIOPipe::lookupStatusString(error) << ")");
LLMemType m1(LLMemType::MTYPE_IO_PUMP);
links_t::reverse_iterator rit;
if(chain.mHead == chain.mChainLinks.end())
@@ -1124,6 +1156,9 @@ bool LLPumpIO::handleChainError(
#endif
keep_going = false;
break;
case LLIOPipe::STATUS_EXPIRED:
keep_going = false;
break ;
default:
if(LLIOPipe::isSuccess(error))
{
@@ -1146,6 +1181,7 @@ LLPumpIO::LLChainInfo::LLChainInfo() :
mInit(false),
mLock(0),
mEOS(false),
mHasExpiration(false),
mDescriptorsPool(new LLAPRPool(LLThread::tldata().mRootPool))
{
LLMemType m1(LLMemType::MTYPE_IO_PUMP);

View File

@@ -4,31 +4,25 @@
* @date 2004-11-19
* @brief Declaration of pump class which manages io chains.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* 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.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* 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$
*/
@@ -351,12 +345,13 @@ protected:
// basic member data
bool mInit;
bool mEOS;
bool mHasExpiration;
S32 mLock;
LLFrameTimer mTimer;
links_t::iterator mHead;
links_t mChainLinks;
LLIOPipe::buffer_ptr_t mData;
bool mEOS;
LLIOPipe::buffer_ptr_t mData;
LLSD mContext;
// tracking inside the pump
@@ -397,8 +392,8 @@ protected:
protected:
void initialize();
void cleanup();
current_chain_t removeRunningChain(current_chain_t& chain) ;
/**
* @brief Given the internal state of the chains, rebuild the pollset
* @see setConditional()
@@ -425,6 +420,9 @@ protected:
*/
bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
//if the chain is expired, remove it
bool isChainExpired(LLChainInfo& chain) ;
public:
/**
* @brief Return number of running chains.

View File

@@ -151,11 +151,11 @@ bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
{
if (success)
{
mResponder->result(payload);
mResponder->pubResult(payload);
}
else
{
mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
mResponder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
}
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/

View File

@@ -1,255 +0,0 @@
/**
* @file llsdrpcclient.cpp
* @author Phoenix
* @date 2005-11-05
* @brief Implementation of the llsd client classes.
*
* $LicenseInfo:firstyear=2005&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 "llsdrpcclient.h"
#include "llbufferstream.h"
#include "llfasttimer.h"
#include "llfiltersd2xmlrpc.h"
#include "llmemtype.h"
#include "llpumpio.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llurlrequest.h"
/**
* String constants
*/
static std::string LLSDRPC_RESPONSE_NAME("response");
static std::string LLSDRPC_FAULT_NAME("fault");
/**
* LLSDRPCResponse
*/
LLSDRPCResponse::LLSDRPCResponse() :
mIsError(false),
mIsFault(false)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
// virtual
LLSDRPCResponse::~LLSDRPCResponse()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
bool LLSDRPCResponse::extractResponse(const LLSD& sd)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
bool rv = true;
if(sd.has(LLSDRPC_RESPONSE_NAME))
{
mReturnValue = sd[LLSDRPC_RESPONSE_NAME];
mIsFault = false;
}
else if(sd.has(LLSDRPC_FAULT_NAME))
{
mReturnValue = sd[LLSDRPC_FAULT_NAME];
mIsFault = true;
}
else
{
mReturnValue.clear();
mIsError = true;
rv = false;
}
return rv;
}
static LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response");
// virtual
LLIOPipe::EStatus LLSDRPCResponse::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_SDRPC_RESPONSE);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
if(mIsError)
{
error(pump);
}
else if(mIsFault)
{
fault(pump);
}
else
{
response(pump);
}
PUMP_DEBUG;
return STATUS_DONE;
}
/**
* LLSDRPCClient
*/
LLSDRPCClient::LLSDRPCClient() :
mState(STATE_NONE),
mQueue(EPBQ_PROCESS)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
// virtual
LLSDRPCClient::~LLSDRPCClient()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
}
bool LLSDRPCClient::call(
const std::string& uri,
const std::string& method,
const LLSD& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
//llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")"
// << llendl;
if(method.empty() || !response)
{
return false;
}
mState = STATE_READY;
mURI.assign(uri);
std::stringstream req;
req << LLSDRPC_REQUEST_HEADER_1 << method
<< LLSDRPC_REQUEST_HEADER_2;
LLSDSerialize::toNotation(parameter, req);
req << LLSDRPC_REQUEST_FOOTER;
mRequest = req.str();
mQueue = queue;
mResponse = response;
return true;
}
bool LLSDRPCClient::call(
const std::string& uri,
const std::string& method,
const std::string& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
//llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")"
// << llendl;
if(method.empty() || parameter.empty() || !response)
{
return false;
}
mState = STATE_READY;
mURI.assign(uri);
std::stringstream req;
req << LLSDRPC_REQUEST_HEADER_1 << method
<< LLSDRPC_REQUEST_HEADER_2 << parameter
<< LLSDRPC_REQUEST_FOOTER;
mRequest = req.str();
mQueue = queue;
mResponse = response;
return true;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_CLIENT("SDRPC Client");
// virtual
LLIOPipe::EStatus LLSDRPCClient::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
if((STATE_NONE == mState) || (!pump))
{
// You should have called the call() method already.
return STATUS_PRECONDITION_NOT_MET;
}
EStatus rv = STATUS_DONE;
switch(mState)
{
case STATE_READY:
{
PUMP_DEBUG;
// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl;
buffer->append(
channels.out(),
(U8*)mRequest.c_str(),
mRequest.length());
context[CONTEXT_DEST_URI_SD_LABEL] = mURI;
mState = STATE_WAITING_FOR_RESPONSE;
break;
}
case STATE_WAITING_FOR_RESPONSE:
{
PUMP_DEBUG;
// The input channel has the sd response in it.
//lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE"
// << llendl;
LLBufferStream resp(channels, buffer.get());
LLSD sd;
LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in()));
LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get();
if (!response)
{
mState = STATE_DONE;
break;
}
response->extractResponse(sd);
if(EPBQ_PROCESS == mQueue)
{
LLPumpIO::chain_t chain;
chain.push_back(mResponse);
pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
}
else
{
pump->respond(mResponse.get());
}
mState = STATE_DONE;
break;
}
case STATE_DONE:
default:
PUMP_DEBUG;
llinfos << "invalid state to process" << llendl;
rv = STATUS_ERROR;
break;
}
return rv;
}

View File

@@ -1,323 +0,0 @@
/**
* @file llsdrpcclient.h
* @author Phoenix
* @date 2005-11-05
* @brief Implementation and helpers for structure data RPC clients.
*
* $LicenseInfo:firstyear=2005&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_LLSDRPCCLIENT_H
#define LL_LLSDRPCCLIENT_H
/**
* This file declares classes to encapsulate a basic structured data
* remote procedure client.
*/
#include "llchainio.h"
#include "llfiltersd2xmlrpc.h"
#include "lliopipe.h"
#include "llurlrequest.h"
/**
* @class LLSDRPCClientResponse
* @brief Abstract base class to represent a response from an SD server.
*
* This is used as a base class for callbacks generated from an
* structured data remote procedure call. The
* <code>extractResponse</code> method will deal with the llsdrpc method
* call overhead, and keep track of what to call during the next call
* into <code>process</code>. If you use this as a base class, you
* need to implement <code>response</code>, <code>fault</code>, and
* <code>error</code> to do something useful. When in those methods,
* you can parse and utilize the mReturnValue member data.
*/
class LLSDRPCResponse : public LLIOPipe
{
public:
LLSDRPCResponse();
virtual ~LLSDRPCResponse();
/**
* @brief This method extracts the response out of the sd passed in
*
* Any appropriate data found in the sd passed in will be
* extracted and managed by this object - not copied or cloned. It
* will still be up to the caller to delete the pointer passed in.
* @param sd The raw structured data response from the remote server.
* @return Returns true if this was able to parse the structured data.
*/
bool extractResponse(const LLSD& sd);
protected:
/**
* @brief Method called when the response is ready.
*/
virtual bool response(LLPumpIO* pump) = 0;
/**
* @brief Method called when a fault is generated by the remote server.
*/
virtual bool fault(LLPumpIO* pump) = 0;
/**
* @brief Method called when there was an error
*/
virtual bool error(LLPumpIO* pump) = 0;
protected:
/* @name LLIOPipe virtual implementations
*/
//@{
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
protected:
LLSD mReturnValue;
bool mIsError;
bool mIsFault;
};
/**
* @class LLSDRPCClient
* @brief Client class for a structured data remote procedure call.
*
* This class helps deal with making structured data calls to a remote
* server. You can visualize the calls as:
* <code>
* response = uri.method(parameter)
* </code>
* where you pass in everything to <code>call</code> and this class
* takes care of the rest of the details.
* In typical usage, you will derive a class from this class and
* provide an API more useful for the specific application at
* hand. For example, if you were writing a service to send an instant
* message, you could create an API for it to send the messsage, and
* that class would do the work of translating it into the method and
* parameter, find the destination, and invoke <code>call</call> with
* a useful implementation of LLSDRPCResponse passed in to handle the
* response from the network.
*/
class LLSDRPCClient : public LLIOPipe
{
public:
LLSDRPCClient();
virtual ~LLSDRPCClient();
/**
* @brief Enumeration for tracking which queue to process the
* response.
*/
enum EPassBackQueue
{
EPBQ_PROCESS,
EPBQ_CALLBACK,
};
/**
* @brief Call a method on a remote LLSDRPCServer
*
* @param uri The remote object to call, eg,
* http://localhost/usher. If you are using a factory with a fixed
* url, the uri passed in will probably be ignored.
* @param method The method to call on the remote object
* @param parameter The parameter to pass into the remote
* object. It is up to the caller to delete the value passed in.
* @param response The object which gets the response.
* @param queue Specifies to call the response on the process or
* callback queue.
* @return Returns true if this object will be able to make the RPC call.
*/
bool call(
const std::string& uri,
const std::string& method,
const LLSD& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue);
/**
* @brief Call a method on a remote LLSDRPCServer
*
* @param uri The remote object to call, eg,
* http://localhost/usher. If you are using a factory with a fixed
* url, the uri passed in will probably be ignored.
* @param method The method to call on the remote object
* @param parameter The seriailized parameter to pass into the
* remote object.
* @param response The object which gets the response.
* @param queue Specifies to call the response on the process or
* callback queue.
* @return Returns true if this object will be able to make the RPC call.
*/
bool call(
const std::string& uri,
const std::string& method,
const std::string& parameter,
LLSDRPCResponse* response,
EPassBackQueue queue);
protected:
/**
* @brief Enumeration for tracking client state.
*/
enum EState
{
STATE_NONE,
STATE_READY,
STATE_WAITING_FOR_RESPONSE,
STATE_DONE
};
/* @name LLIOPipe virtual implementations
*/
//@{
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
protected:
EState mState;
std::string mURI;
std::string mRequest;
EPassBackQueue mQueue;
LLIOPipe::ptr_t mResponse;
};
/**
* @class LLSDRPCClientFactory
* @brief Basic implementation for making an SD RPC client factory
*
* This class eases construction of a basic sd rpc client. Here is an
* example of it's use:
* <code>
* class LLUsefulService : public LLService { ... }
* LLService::registerCreator(
* "useful",
* LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>))
* </code>
*/
template<class Client>
class LLSDRPCClientFactory : public LLChainIOFactory
{
public:
LLSDRPCClientFactory() {}
LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLSDRPCClientFactory::build" << llendl;
LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
if(!http->isValid())
{
llwarns << "Creating LLURLRequest failed." << llendl ;
delete http;
return false;
}
LLIOPipe::ptr_t service(new Client);
chain.push_back(service);
LLIOPipe::ptr_t http_pipe(http);
http->addHeader("Content-Type: text/llsd");
if(mURL.empty())
{
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
}
else
{
http->setURL(mURL);
}
chain.push_back(http_pipe);
chain.push_back(service);
return true;
}
protected:
std::string mURL;
};
/**
* @class LLXMLSDRPCClientFactory
* @brief Basic implementation for making an XMLRPC to SD RPC client factory
*
* This class eases construction of a basic sd rpc client which uses
* xmlrpc as a serialization grammar. Here is an example of it's use:
* <code>
* class LLUsefulService : public LLService { ... }
* LLService::registerCreator(
* "useful",
* LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>))
* </code>
*/
template<class Client>
class LLXMLSDRPCClientFactory : public LLChainIOFactory
{
public:
LLXMLSDRPCClientFactory() {}
LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
if(!http->isValid())
{
llwarns << "Creating LLURLRequest failed." << llendl ;
delete http;
return false ;
}
LLIOPipe::ptr_t service(new Client);
chain.push_back(service);
LLIOPipe::ptr_t http_pipe(http);
http->addHeader("Content-Type: text/xml");
if(mURL.empty())
{
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
}
else
{
http->setURL(mURL);
}
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL)));
chain.push_back(http_pipe);
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
chain.push_back(service);
return true;
}
protected:
std::string mURL;
};
#endif // LL_LLSDRPCCLIENT_H

View File

@@ -1,347 +0,0 @@
/**
* @file llsdrpcserver.cpp
* @author Phoenix
* @date 2005-10-11
* @brief Implementation of the LLSDRPCServer and related classes.
*
* $LicenseInfo:firstyear=2005&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 "llsdrpcserver.h"
#include "llbuffer.h"
#include "llbufferstream.h"
#include "llfasttimer.h"
#include "llmemtype.h"
#include "llpumpio.h"
#include "llsdserialize.h"
#include "llstl.h"
static const char FAULT_PART_1[] = "{'fault':{'code':i";
static const char FAULT_PART_2[] = ", 'description':'";
static const char FAULT_PART_3[] = "'}}";
static const char RESPONSE_PART_1[] = "{'response':";
static const char RESPONSE_PART_2[] = "}";
static const S32 FAULT_GENERIC = 1000;
static const S32 FAULT_METHOD_NOT_FOUND = 1001;
static const std::string LLSDRPC_METHOD_SD_NAME("method");
static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter");
/**
* LLSDRPCServer
*/
LLSDRPCServer::LLSDRPCServer() :
mState(LLSDRPCServer::STATE_NONE),
mPump(NULL),
mLock(0)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
}
LLSDRPCServer::~LLSDRPCServer()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
std::for_each(
mMethods.begin(),
mMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
std::for_each(
mCallbackMethods.begin(),
mCallbackMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
}
// virtual
ESDRPCSStatus LLSDRPCServer::deferredResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data) {
// subclass should provide a sane implementation
return ESDRPCS_DONE;
}
void LLSDRPCServer::clearLock()
{
if(mLock && mPump)
{
mPump->clearLock(mLock);
mPump = NULL;
mLock = 0;
}
}
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_SERVER("SDRPC Server");
// virtual
LLIOPipe::EStatus LLSDRPCServer::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_SDRPC_SERVER);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// lldebugs << "LLSDRPCServer::process_impl" << llendl;
// Once we have all the data, We need to read the sd on
// the the in channel, and respond on the out channel
if(!eos) return STATUS_BREAK;
if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
std::string method_name;
LLIOPipe::EStatus status = STATUS_DONE;
switch(mState)
{
case STATE_DEFERRED:
PUMP_DEBUG;
if(ESDRPCS_DONE != deferredResponse(channels, buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"deferred response failed.");
}
mState = STATE_DONE;
return STATUS_DONE;
case STATE_DONE:
// lldebugs << "STATE_DONE" << llendl;
break;
case STATE_CALLBACK:
// lldebugs << "STATE_CALLBACK" << llendl;
PUMP_DEBUG;
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
if(ESDRPCS_DONE != callbackMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Callback method call failed.");
}
}
else
{
// this should never happen, since we should not be in
// this state unless we originally found a method and
// params during the first call to process.
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Invalid LLSDRPC sever state - callback without method.");
}
pump->clearLock(mLock);
mLock = 0;
mState = STATE_DONE;
break;
case STATE_NONE:
// lldebugs << "STATE_NONE" << llendl;
default:
{
// First time we got here - process the SD request, and call
// the method.
PUMP_DEBUG;
LLBufferStream istr(channels, buffer.get());
mRequest.clear();
LLSDSerialize::fromNotation(
mRequest,
istr,
buffer->count(channels.in()));
// { 'method':'...', 'parameter': ... }
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
ESDRPCSStatus rv = callMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get());
switch(rv)
{
case ESDRPCS_DEFERRED:
mPump = pump;
mLock = pump->setLock();
mState = STATE_DEFERRED;
status = STATUS_BREAK;
break;
case ESDRPCS_CALLBACK:
{
mState = STATE_CALLBACK;
LLPumpIO::LLLinkInfo link;
link.mPipe = LLIOPipe::ptr_t(this);
link.mChannels = channels;
LLPumpIO::links_t links;
links.push_back(link);
pump->respond(links, buffer, context);
mLock = pump->setLock();
status = STATUS_BREAK;
break;
}
case ESDRPCS_DONE:
mState = STATE_DONE;
break;
case ESDRPCS_ERROR:
default:
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Method call failed.");
break;
}
}
else
{
// send a fault
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Unable to find method and parameter in request.");
}
break;
}
}
PUMP_DEBUG;
return status;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mMethods.find(method);
if(it != mMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
it = mCallbackMethods.find(method);
if(it == mCallbackMethods.end())
{
// method not found.
std::ostringstream message;
message << "rpc server unable to find method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
else
{
// we found it in the callback methods - tell the process
// to coordinate calling on the pump callback.
return ESDRPCS_CALLBACK;
}
}
return rv;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callbackMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the callback method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mCallbackMethods.find(method);
if(it != mCallbackMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
std::ostringstream message;
message << "pcserver unable to find callback method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
return rv;
}
// static
void LLSDRPCServer::buildFault(
const LLChannelDescriptors& channels,
LLBufferArray* data,
S32 code,
const std::string& msg)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3;
llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl;
}
// static
void LLSDRPCServer::buildResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data,
const LLSD& response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << RESPONSE_PART_1;
LLSDSerialize::toNotation(response, ostr);
ostr << RESPONSE_PART_2;
#if LL_DEBUG
std::ostringstream debug_ostr;
debug_ostr << "LLSDRPCServer::buildResponse: ";
LLSDSerialize::toNotation(response, debug_ostr);
llinfos << debug_ostr.str() << llendl;
#endif
}

View File

@@ -1,360 +0,0 @@
/**
* @file llsdrpcserver.h
* @author Phoenix
* @date 2005-10-11
* @brief Declaration of the structured data remote procedure call server.
*
* $LicenseInfo:firstyear=2005&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_LLSDRPCSERVER_H
#define LL_LLSDRPCSERVER_H
/**
* I've set this up to be pretty easy to use when you want to make a
* structured data rpc server which responds to methods by
* name. Derive a class from the LLSDRPCServer, and during
* construction (or initialization if you have the luxury) map method
* names to pointers to member functions. This will look a lot like:
*
* <code>
* class LLMessageAgents : public LLSDRPCServer {<br>
* public:<br>
* typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
* LLMessageAgents() {<br>
* mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
* mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
* }<br>
* protected:<br>
* rpc_IM(const LLSD& params,
* const LLChannelDescriptors& channels,
* LLBufferArray* data)
* {...}<br>
* rpc_Alert(const LLSD& params,
* const LLChannelDescriptors& channels,
* LLBufferArray* data)
* {...}<br>
* };<br>
* </code>
*
* The params are an array where each element in the array is a single
* parameter in the call.
*
* It is up to you to pack a valid serialized llsd response into the
* data object passed into the method, but you can use the helper
* methods below to help.
*/
#include <map>
#include "lliopipe.h"
#include "lliohttpserver.h"
#include "llfiltersd2xmlrpc.h"
class LLSD;
/**
* @brief Enumeration for specifying server method call status. This
* enumeration controls how the server class will manage the pump
* process/callback mechanism.
*/
enum ESDRPCSStatus
{
// The call went ok, but the response is not yet ready. The
// method will arrange for the clearLock() call to be made at
// a later date, after which, once the chain is being pumped
// again, deferredResponse() will be called to gather the result
ESDRPCS_DEFERRED,
// The LLSDRPCServer would like to handle the method on the
// callback queue of the pump.
ESDRPCS_CALLBACK,
// The method call finished and generated output.
ESDRPCS_DONE,
// Method failed for some unspecified reason - you should avoid
// this. A generic fault will be sent to the output.
ESDRPCS_ERROR,
ESDRPCS_COUNT,
};
/**
* @class LLSDRPCMethodCallBase
* @brief Base class for calling a member function in an sd rpcserver
* implementation.
*/
class LLSDRPCMethodCallBase
{
public:
LLSDRPCMethodCallBase() {}
virtual ~LLSDRPCMethodCallBase() {}
virtual ESDRPCSStatus call(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response) = 0;
protected:
};
/**
* @class LLSDRPCMethodCall
* @brief Class which implements member function calls.
*/
template<class Server>
class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
{
public:
typedef ESDRPCSStatus (Server::*mem_fn)(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
LLSDRPCMethodCall(Server* s, mem_fn fn) :
mServer(s),
mMemFn(fn)
{
}
virtual ~LLSDRPCMethodCall() {}
virtual ESDRPCSStatus call(
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data)
{
return (*mServer.*mMemFn)(params, channels, data);
}
protected:
Server* mServer;
mem_fn mMemFn;
//bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
};
/**
* @class LLSDRPCServer
* @brief Basic implementation of a structure data rpc server
*
* The rpc server is also designed to appropriately straddle the pump
* <code>process()</code> and <code>callback()</code> to specify which
* thread you want to work on when handling a method call. The
* <code>mMethods</code> methods are called from
* <code>process()</code>, while the <code>mCallbackMethods</code> are
* called when a pump is in a <code>callback()</code> cycle.
*/
class LLSDRPCServer : public LLIOPipe
{
public:
LLSDRPCServer();
virtual ~LLSDRPCServer();
/**
* enumeration for generic fault codes
*/
enum
{
FAULT_BAD_REQUEST = 2000,
FAULT_NO_RESPONSE = 2001,
};
/**
* @brief Call this method to return an rpc fault.
*
* @param channel The channel for output on the data buffer
* @param data buffer which will recieve the final output
* @param code The fault code
* @param msg The fault message
*/
static void buildFault(
const LLChannelDescriptors& channels,
LLBufferArray* data,
S32 code,
const std::string& msg);
/**
* @brief Call this method to build an rpc response.
*
* @param channel The channel for output on the data buffer
* @param data buffer which will recieve the final output
* @param response The return value from the method call
*/
static void buildResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data,
const LLSD& response);
protected:
/* @name LLIOPipe virtual implementations
*/
//@{
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
protected:
/**
* @brief Enumeration to track the state of the rpc server instance
*/
enum EState
{
STATE_NONE,
STATE_CALLBACK,
STATE_DEFERRED,
STATE_DONE
};
/**
* @brief This method is called when an http post comes in.
*
* The default behavior is to look at the method name, look up the
* method in the method table, and call it. If the method is not
* found, this function will build a fault response. You can
* implement your own version of this function if you want to hard
* wire some behavior or optimize things a bit.
* @param method The method name being called
* @param params The parameters
* @param channel The channel for output on the data buffer
* @param data The http data
* @return Returns the status of the method call, done/deferred/etc
*/
virtual ESDRPCSStatus callMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
/**
* @brief This method is called when a pump callback is processed.
*
* The default behavior is to look at the method name, look up the
* method in the callback method table, and call it. If the method
* is not found, this function will build a fault response. You
* can implement your own version of this function if you want to
* hard wire some behavior or optimize things a bit.
* @param method The method name being called
* @param params The parameters
* @param channel The channel for output on the data buffer
* @param data The http data
* @return Returns the status of the method call, done/deferred/etc
*/
virtual ESDRPCSStatus callbackMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* data);
/**
* @brief Called after a deferred service is unlocked
*
* If a method returns ESDRPCS_DEFERRED, then the service chain
* will be locked and not processed until some other system calls
* clearLock() on the service instance again. At that point,
* once the pump starts processing the chain again, this method
* will be called so the service can output the final result
* into the buffers.
*/
virtual ESDRPCSStatus deferredResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data);
// donovan put this public here 7/27/06
public:
/**
* @brief unlock a service that as ESDRPCS_DEFERRED
*/
void clearLock();
protected:
EState mState;
LLSD mRequest;
LLPumpIO* mPump;
S32 mLock;
typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
method_map_t mMethods;
method_map_t mCallbackMethods;
};
/**
* @name Helper Templates for making LLHTTPNodes
*
* These templates help in creating nodes for handing a service from
* either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
*
* To use it:
* \code
* class LLUsefulServer : public LLSDRPCServer { ... }
*
* LLHTTPNode& root = LLCreateHTTPWireServer(...);
* root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
* root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
* \endcode
*/
//@{
template<class Server>
class LLSDRPCServerFactory : public LLChainIOFactory
{
public:
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
chain.push_back(LLIOPipe::ptr_t(new Server));
return true;
}
};
template<class Server>
class LLSDRPCNode : public LLHTTPNodeForFactory<
LLSDRPCServerFactory<Server> >
{
};
template<class Server>
class LLXMLRPCServerFactory : public LLChainIOFactory
{
public:
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
{
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
chain.push_back(LLIOPipe::ptr_t(new Server));
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
return true;
}
};
template<class Server>
class LLXMLRPCNode : public LLHTTPNodeForFactory<
LLXMLRPCServerFactory<Server> >
{
};
//@}
#endif // LL_LLSDRPCSERVER_H

View File

@@ -29,6 +29,10 @@
#include "linden_common.h"
#include "llurlrequest.h"
#ifdef CWDEBUG
#include <libcwd/buf2str.h>
#endif
#include <algorithm>
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
@@ -48,13 +52,10 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499;
/**
* String constants
*/
const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
static size_t headerCallback(char* data, size_t size, size_t nmemb, void* user);
/**
* class LLURLRequestDetail
@@ -65,33 +66,26 @@ public:
LLURLRequestDetail();
~LLURLRequestDetail();
std::string mURL;
LLCurlEasyRequest* mCurlRequest;
LLBufferArray* mResponseBuffer;
AICurlEasyRequest mCurlEasyRequest;
LLIOPipe::buffer_ptr_t mResponseBuffer;
LLChannelDescriptors mChannels;
U8* mLastRead;
U32 mBodyLimit;
S32 mByteAccumulator;
bool mIsBodyLimitSet;
LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback;
};
LLURLRequestDetail::LLURLRequestDetail() :
mCurlRequest(NULL),
mResponseBuffer(NULL),
mCurlEasyRequest(false),
mLastRead(NULL),
mBodyLimit(0),
mByteAccumulator(0),
mIsBodyLimitSet(false)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mCurlRequest = new LLCurlEasyRequest();
}
LLURLRequestDetail::~LLURLRequestDetail()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
delete mCurlRequest;
mResponseBuffer = NULL;
mLastRead = NULL;
}
@@ -123,6 +117,7 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
}
@@ -132,6 +127,7 @@ LLURLRequest::LLURLRequest(
mAction(action)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// This might throw AICurlNoEasyHandle.
initialize();
setURL(url);
}
@@ -139,13 +135,16 @@ LLURLRequest::LLURLRequest(
LLURLRequest::~LLURLRequest()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
{
AICurlEasyRequest_wat curl_easy_request_w(*mDetail->mCurlEasyRequest);
curl_easy_request_w->revokeCallbacks();
curl_easy_request_w->send_events_to(NULL);
}
delete mDetail;
mDetail = NULL ;
}
void LLURLRequest::setURL(const std::string& url)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mDetail->mURL = url;
}
@@ -153,19 +152,21 @@ std::string LLURLRequest::getURL() const
{
return mDetail->mURL;
}
void LLURLRequest::addHeader(const char* header)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mDetail->mCurlRequest->slist_append(header);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->addHeader(header);
}
void LLURLRequest::checkRootCertificate(bool check)
{
mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, check ? 1L : 0L);
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
}
void LLURLRequest::setBodyLimit(U32 size)
{
mDetail->mBodyLimit = size;
@@ -176,7 +177,8 @@ void LLURLRequest::setCallback(LLURLRequestComplete* callback)
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mCompletionCallback = callback;
mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setHeaderCallback(&headerCallback, (void*)callback);
}
// Added to mitigate the effect of libcurl looking
@@ -210,26 +212,41 @@ void LLURLRequest::useProxy(bool use_proxy)
}
}
LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL;
LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL;
if (use_proxy && !env_proxy.empty())
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy);
}
else
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, "");
}
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string(""));
}
void LLURLRequest::useProxy(const std::string &proxy)
{
mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy);
}
void LLURLRequest::allowCookies()
{
mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, "");
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, "");
}
//virtual
bool LLURLRequest::hasExpiration(void) const
{
// Currently, this ALWAYS returns false -- because only AICurlEasyRequestStateMachine uses buffered
// AICurlEasyRequest objects, and LLURLRequest uses (unbuffered) AICurlEasyRequest directly, which
// have no expiration facility.
return mDetail->mCurlEasyRequest.isBuffered();
}
//virtual
bool LLURLRequest::hasNotExpired(void) const
{
if (!mDetail->mCurlEasyRequest.isBuffered())
return true;
AICurlEasyRequest_wat buffered_easy_request_w(*mDetail->mCurlEasyRequest);
AICurlResponderBuffer_wat buffer_w(*mDetail->mCurlEasyRequest);
return buffer_w->isValid();
}
// virtual
@@ -237,7 +254,27 @@ LLIOPipe::EStatus LLURLRequest::handleError(
LLIOPipe::EStatus status,
LLPumpIO* pump)
{
DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ")");
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
if (LL_LIKELY(!mDetail->mCurlEasyRequest.isBuffered())) // Currently always true.
{
// The last reference will be deleted when the pump that this chain belongs to
// is removed from the running chains vector, upon returning from this function.
// This keeps the CurlEasyRequest object alive until the curl thread cleanly removed it.
Dout(dc::curl, "Calling mDetail->mCurlEasyRequest.removeRequest()");
mDetail->mCurlEasyRequest.removeRequest();
}
else if (!hasNotExpired())
{
// The buffered version has it's own time out handling, and that already expired,
// so we can ignore the expiration of this timer (currently never happens).
// I left it here because it's what LL did (in the form if (!isValid() ...),
// and it would be relevant if this characteristic of mDetail->mCurlEasyRequest
// would change. --Aleric
return STATUS_EXPIRED ;
}
if(mCompletionCallback && pump)
{
LLURLRequestComplete* complete = NULL;
@@ -252,9 +289,21 @@ LLIOPipe::EStatus LLURLRequest::handleError(
return status;
}
void LLURLRequest::added_to_multi_handle(AICurlEasyRequest_wat&)
{
}
void LLURLRequest::finished(AICurlEasyRequest_wat&)
{
}
void LLURLRequest::removed_from_multi_handle(AICurlEasyRequest_wat&)
{
mRemoved = true;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request");
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result");
static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform");
// virtual
LLIOPipe::EStatus LLURLRequest::process_impl(
@@ -269,9 +318,10 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
//llinfos << "LLURLRequest::process_impl()" << llendl;
if (!buffer) return STATUS_ERROR;
if (!mDetail) return STATUS_ERROR; //Seems to happen on occasion. Need to hunt down why.
// we're still waiting or prcessing, check how many
// we're still waiting or processing, check how many
// bytes we have accumulated.
const S32 MIN_ACCUMULATION = 100000;
if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION))
@@ -309,44 +359,42 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
// *FIX: bit of a hack, but it should work. The configure and
// callback method expect this information to be ready.
mDetail->mResponseBuffer = buffer.get();
mDetail->mResponseBuffer = buffer;
mDetail->mChannels = channels;
if(!configure())
{
return STATUS_ERROR;
}
mRemoved = false;
mState = STATE_WAITING_FOR_RESPONSE;
mDetail->mCurlEasyRequest.addRequest(); // Add easy handle to multi handle.
// *FIX: Maybe we should just go to the next state now...
return STATUS_BREAK;
}
case STATE_WAITING_FOR_RESPONSE:
case STATE_PROCESSING_RESPONSE:
{
PUMP_DEBUG;
LLIOPipe::EStatus status = STATUS_BREAK;
if (!mRemoved) // Not removed from multi handle yet?
{
LLFastTimer t(FTM_URL_PERFORM);
mDetail->mCurlRequest->perform();
// Easy handle is still being processed.
return STATUS_BREAK;
}
// Curl thread finished with this easy handle.
mState = STATE_CURL_FINISHED;
}
case STATE_CURL_FINISHED:
{
PUMP_DEBUG;
LLIOPipe::EStatus status = STATUS_NO_CONNECTION; // Catch-all failure code.
while(1)
// Left braces in order not to change indentation.
{
CURLcode result;
bool newmsg = false;
{
LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT);
newmsg = mDetail->mCurlRequest->getResult(&result);
}
if(!newmsg)
{
// keep processing
break;
}
AICurlEasyRequest_wat(*mDetail->mCurlEasyRequest)->getResult(&result);
mState = STATE_HAVE_RESPONSE;
context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes;
context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes;
@@ -378,6 +426,7 @@ LLIOPipe::EStatus LLURLRequest::process_impl(
}
mCompletionCallback = NULL;
}
status = STATUS_BREAK; // This is what the old code returned. Does it make sense?
break;
case CURLE_FAILED_INIT:
case CURLE_COULDNT_CONNECT:
@@ -419,10 +468,15 @@ void LLURLRequest::initialize()
{
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
mState = STATE_INITIALIZED;
// This might throw AICurlNoEasyHandle.
mDetail = new LLURLRequestDetail;
mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this);
mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this);
{
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
curlEasyRequest_w->setWriteCallback(&downCallback, (void*)this);
curlEasyRequest_w->setReadCallback(&upCallback, (void*)this);
}
mRequestTransferedBytes = 0;
mResponseTransferedBytes = 0;
}
@@ -437,70 +491,66 @@ bool LLURLRequest::configure()
S32 bytes = mDetail->mResponseBuffer->countAfter(
mDetail->mChannels.in(),
NULL);
switch(mAction)
{
case HTTP_HEAD:
mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1);
mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1);
mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
rv = true;
break;
case HTTP_GET:
mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1);
mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest);
switch(mAction)
{
case HTTP_HEAD:
curlEasyRequest_w->setopt(CURLOPT_HEADER, 1);
curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1);
curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
rv = true;
break;
// Set Accept-Encoding to allow response compression
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
case HTTP_GET:
curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1);
curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1);
case HTTP_PUT:
// Disable the expect http 1.1 extension. POST and PUT default
// to turning this on, and I am not too sure what it means.
addHeader("Expect:");
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1);
mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes);
rv = true;
break;
case HTTP_PUT:
// Disable the expect http 1.1 extension. POST and PUT default
// to turning this on, and I am not too sure what it means.
curlEasyRequest_w->addHeader("Expect:");
curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1);
curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes);
rv = true;
break;
case HTTP_POST:
// Disable the expect http 1.1 extension. POST and PUT default
// to turning this on, and I am not too sure what it means.
addHeader("Expect:");
case HTTP_POST:
// Set the handle for an http post
curlEasyRequest_w->setPost(bytes);
// Disable the content type http header.
// *FIX: what should it be?
addHeader("Content-Type:");
// Set Accept-Encoding to allow response compression
curlEasyRequest_w->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
// Set the handle for an http post
mDetail->mCurlRequest->setPost(NULL, bytes);
case HTTP_DELETE:
// Set the handle for an http post
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
rv = true;
break;
// Set Accept-Encoding to allow response compression
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
rv = true;
break;
case HTTP_MOVE:
// Set the handle for an http post
curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
// *NOTE: should we check for the Destination header?
rv = true;
break;
case HTTP_DELETE:
// Set the handle for an http post
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
rv = true;
break;
case HTTP_MOVE:
// Set the handle for an http post
mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
// *NOTE: should we check for the Destination header?
rv = true;
break;
default:
llwarns << "Unhandled URLRequest action: " << mAction << llendl;
break;
}
if(rv)
{
mDetail->mCurlRequest->sendRequest(mDetail->mURL);
default:
llwarns << "Unhandled URLRequest action: " << mAction << llendl;
break;
}
if(rv)
{
curlEasyRequest_w->finalizeRequest(mDetail->mURL);
curlEasyRequest_w->send_events_to(this);
}
}
return rv;
}
@@ -564,9 +614,8 @@ size_t LLURLRequest::upCallback(
return bytes;
}
static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void* user)
{
const char* header_line = (const char*)data;
size_t header_len = size * nmemb;
LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
@@ -632,42 +681,6 @@ static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
return header_len;
}
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor");
/**
* LLContextURLExtractor
*/
// virtual
LLIOPipe::EStatus LLContextURLExtractor::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
// The destination host is in the context.
if(context.isUndefined() || !mRequest)
{
return STATUS_PRECONDITION_NOT_MET;
}
// copy in to out, since this just extract the URL and does not
// actually change the data.
LLChangeChannel change(channels.in(), channels.out());
std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
// find the context url
if(context.has(CONTEXT_DEST_URI_SD_LABEL))
{
mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString());
return STATUS_DONE;
}
return STATUS_ERROR;
}
/**
* LLURLRequestComplete
*/

View File

@@ -41,7 +41,6 @@
#include "llcurl.h"
extern const std::string CONTEXT_REQUEST;
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
extern const std::string CONTEXT_RESPONSE;
extern const std::string CONTEXT_TRANSFERED_BYTES;
@@ -65,7 +64,7 @@ typedef struct x509_store_ctx_st X509_STORE_CTX;
* worth the time and effort to eventually port this to a raw client
* socket.
*/
class LLURLRequest : public LLIOPipe
class LLURLRequest : public LLIOPipe, protected AICurlEasyHandleEvents
{
LOG_CLASS(LLURLRequest);
public:
@@ -188,6 +187,9 @@ public:
*/
void allowCookies();
/*virtual*/ bool hasExpiration(void) const;
/*virtual*/ bool hasNotExpired(void) const;
public:
/**
* @brief Give this pipe a chance to handle a generated error
@@ -212,6 +214,7 @@ protected:
STATE_INITIALIZED,
STATE_WAITING_FOR_RESPONSE,
STATE_PROCESSING_RESPONSE,
STATE_CURL_FINISHED,
STATE_HAVE_RESPONSE,
};
EState mState;
@@ -221,6 +224,14 @@ protected:
S32 mRequestTransferedBytes;
S32 mResponseTransferedBytes;
// mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic.
// Set to false before adding curl request and then only tested.
// Reset in removed_from_multi_handle (by another thread), this is thread-safe.
bool mRemoved;
/*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat&);
/*virtual*/ void finished(AICurlEasyRequest_wat&);
/*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat&);
private:
/**
* @brief Initialize the object. Called during construction.
@@ -259,42 +270,6 @@ private:
LLURLRequest(const LLURLRequest&);
};
/**
* @class LLContextURLExtractor
* @brief This class unpacks the url out of a agent usher service so
* it can be packed into a LLURLRequest object.
* @see LLIOPipe
*
* This class assumes that the context is a map that contains an entry
* named CONTEXT_DEST_URI_SD_LABEL.
*/
class LLContextURLExtractor : public LLIOPipe
{
public:
LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {}
~LLContextURLExtractor() {}
protected:
/* @name LLIOPipe virtual implementations
*/
//@{
/**
* @brief Process the data in buffer
*/
virtual EStatus process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump);
//@}
protected:
LLURLRequest* mRequest;
};
/**
* @class LLURLRequestComplete
* @brief Class which can optionally be used with an LLURLRequest to
@@ -367,11 +342,4 @@ protected:
EStatus mRequestStatus;
};
/**
* External constants
*/
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
#endif // LL_LLURLREQUEST_H

View File

@@ -81,6 +81,7 @@
#include "v4math.h"
#include "lltransfertargetvfile.h"
#include "llmemtype.h"
#include "llpacketring.h"
// Constants
//const char* MESSAGE_LOG_FILENAME = "message.log";
@@ -243,7 +244,8 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port,
bool failure_is_fatal,
const F32 circuit_heartbeat_interval, const F32 circuit_timeout) :
mCircuitInfo(circuit_heartbeat_interval, circuit_timeout),
mLastMessageFromTrustedMessageService(false)
mLastMessageFromTrustedMessageService(false),
mPacketRing(new LLPacketRing)
{
init();
@@ -383,6 +385,9 @@ LLMessageSystem::~LLMessageSystem()
delete mPollInfop;
mPollInfop = NULL;
delete mPacketRing;
mPacketRing = NULL;
mIncomingCompressedSize = 0;
mCurrentRecvPacketID = 0;
}
@@ -548,13 +553,13 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count )
U8* buffer = mTrueReceiveBuffer;
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
mTrueReceiveSize = mPacketRing->receivePacket(mSocket, (char *)mTrueReceiveBuffer);
// If you want to dump all received packets into SecondLife.log, uncomment this
//dumpPacketToLog();
receive_size = mTrueReceiveSize;
mLastSender = mPacketRing.getLastSender();
mLastReceivingIF = mPacketRing.getLastReceivingInterface();
mLastSender = mPacketRing->getLastSender();
mLastReceivingIF = mPacketRing->getLastReceivingInterface();
if (receive_size < (S32) LL_MINIMUM_VALID_PACKET_SIZE)
{
@@ -1129,7 +1134,7 @@ S32 LLMessageSystem::flushReliable(const LLHost &host)
return send_bytes;
}
LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name)
LLFnPtrResponder* LLMessageSystem::createResponder(const std::string& name)
{
if(mSendReliable)
{
@@ -1328,7 +1333,7 @@ S32 LLMessageSystem::sendMessage(const LLHost &host)
}
BOOL success;
success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
success = mPacketRing->sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
if (!success)
{
@@ -3361,7 +3366,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_
void LLMessageSystem::dumpPacketToLog()
{
LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << llendl;
LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing->getLastSender() << llendl;
LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << llendl;
char line_buffer[256]; /* Flawfinder: ignore */
S32 i;

View File

@@ -48,9 +48,9 @@
#include "string_table.h"
#include "llcircuit.h"
#include "lltimer.h"
#include "llpacketring.h"
//#include "llpacketring.h"
#include "llhost.h"
#include "llhttpclient.h"
//#include "llhttpclient.h"
#include "llhttpnode.h"
#include "llpacketack.h"
#include "llsingleton.h"
@@ -61,6 +61,12 @@
#include "llstoredmessage.h"
class LLPacketRing;
namespace
{
class LLFnPtrResponder;
}
const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
@@ -213,7 +219,7 @@ class LLMessageSystem : public LLMessageSenderInterface
LLHost mUntrustedInterface;
public:
LLPacketRing mPacketRing;
LLPacketRing* mPacketRing;
LLReliablePacketParams mReliablePacketParams;
// Set this flag to TRUE when you want *very* verbose logs.
@@ -494,7 +500,7 @@ public:
void (*callback)(void **,S32),
void ** callback_data);
LLHTTPClient::ResponderPtr createResponder(const std::string& name);
LLFnPtrResponder* createResponder(const std::string& name);
S32 sendMessage(const LLHost &host);
S32 sendMessage(const U32 circuit);
private:

View File

@@ -125,7 +125,7 @@ int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_fi
buf[0] = 0;
if (error)
{
strncpy(buf, dlerror(), sizeof(buf));
strncpy(buf, error, sizeof(buf));
}
buf[sizeof(buf) - 1] = 0;
}

View File

@@ -379,6 +379,7 @@ void LLPluginProcessParent::idle(void)
}
else
{
// Set PluginAttachDebuggerToPlugins to TRUE to use this. You might also want to set DebugPluginDisableTimeout to TRUE.
if(mDebug)
{
// If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue.
@@ -400,14 +401,37 @@ void LLPluginProcessParent::idle(void)
mDebugger.launch();
#elif LL_LINUX
// The command we're constructing would look like this on the command line:
// /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345'
// This can be changed by setting the following environment variables, for example:
// export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s"
// /usr/bin/xterm -geometry 160x24-0+0 -e /usr/bin/gdb -n /proc/12345/exe 12345
// Note that most terminals demand that all arguments to the process that is
// started with -e are passed as arguments to the terminal: there are no quotes
// around '/usr/bin/gdb -n /proc/12345/exe 12345'. This is the case for xterm,
// uxterm, konsole etc. The exception might be gnome-terminal.
//
// The constructed command can be changed by setting the following environment
// variables, for example:
//
// export LL_DEBUG_GDB_PATH=/usr/bin/gdb
// export LL_DEBUG_TERMINAL_COMMAND='/usr/bin/gnome-terminal --geometry=165x24-0+0 -e "%s"'
//
// Or, as second example, if you are running the viewer on host 'A', and you want
// to open the gdb terminal on the X display of host 'B', you would run on host B:
// 'ssh -X A' (and then start the viewer, or just leave the terminal open), and
// then use:
//
// export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/uxterm -fs 9 -fa 'DejaVu Sans Mono' -display localhost:10 -geometry 209x31+0-50 -e %s"
//
// which would open the terminal on B (no quotes around the %s, since this uses uxterm!).
// For a list of available strings to pass to the -fa, run in a terminal: fc-list :scalable=true:spacing=mono: family
char const* env;
std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s";
std::string terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s";
char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb";
cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID();
std::string::size_type pos = terminal_command.find("%s");
if (pos != std::string::npos)
{
terminal_command.replace(pos, 2, cmd.str());
}
typedef boost::tokenizer< boost::escaped_list_separator<
char>, std::basic_string<
@@ -429,14 +453,7 @@ void LLPluginProcessParent::idle(void)
mDebugger.setExecutable(*token);
while (++token != tokens.end())
{
if (*token == "%s")
{
mDebugger.addArgument(cmd.str());
}
else
{
mDebugger.addArgument(*token);
}
mDebugger.addArgument(*token);
}
mDebugger.launch();
#endif

View File

@@ -2440,7 +2440,6 @@ BOOL LLLineEditor::evaluateFloat()
{
bool success = false;
std::string expr = getText();
LLStringUtil::toUpper(expr);
// user deleted the contents, nothing to evaluate -- MC
if (expr.empty())

View File

@@ -1416,7 +1416,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
{
found_format = TRUE;
}
else if(cur_format >= num_formats-1)
else if(cur_format >= (S32)num_formats-1)
{
cur_format = 0;
found_format = TRUE;

View File

@@ -42,6 +42,7 @@
#include "v4color.h"
#include "v4coloru.h"
#include "llinstancetracker.h"
#include "llrefcount.h"
#include "llcontrolgroupreader.h"

View File

@@ -133,6 +133,7 @@ set(viewer_SOURCE_FILES
llconfirmationmanager.cpp
llconsole.cpp
llcontainerview.cpp
llcurlrequest.cpp
llcurrencyuimanager.cpp
llcylinder.cpp
lldaycyclemanager.cpp
@@ -634,6 +635,7 @@ set(viewer_HEADER_FILES
llconfirmationmanager.h
llconsole.h
llcontainerview.h
llcurlrequest.h
llcurrencyuimanager.h
llcylinder.h
lldaycyclemanager.h
@@ -1270,8 +1272,6 @@ if (WINDOWS)
if (INTEL_MEMOPS_LIBRARY)
list(APPEND viewer_LIBRARIES ${INTEL_MEMOPS_LIBRARY})
endif (INTEL_MEMOPS_LIBRARY)
use_prebuilt_binary(dbghelp)
endif (WINDOWS)
# Add the xui files. This is handy for searching for xui elements

View File

@@ -4239,17 +4239,6 @@ Found in Advanced->Rendering->Info Displays</string>
<key>Value</key>
<real>120.0</real>
</map>
<key>CurlUseMultipleThreads</key>
<map>
<key>Comment</key>
<string>Use background threads for executing curl_multi_perform (requires restart)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>Cursor3D</key>
<map>
<key>Comment</key>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.8300000429153442</real>
<real>1.8300000429153442</real>
<real>1.8300000429153442</real>
<real>0.61000001430511475</real>
</array>
<key>blue_density</key>
<array>
<real>0.21899415552616119</real>
<real>0.40148928761482239</real>
<real>0.68000000715255737</real>
<real>0.34000000357627869</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0.10838708281517029</real>
<real>0.14000000059604645</real>
<real>0.070000000298023224</real>
</array>
<key>cloud_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.14000000059604645</real>
<real>0.62000000476837158</real>
<real>0.99999999999999767</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.48999997973442078</real>
<real>0.19999998807907104</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.43999999761581421</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.199999853917745</real>
<real>10.010999722693327</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.29999998211860657</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00015999999595806003</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>10.800000190734863</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>0.94999998807907104</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>4.9999999403953552</real>
<real>0.0010000000616982377</real>
<real>-0.47999998693999579</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.94999998807907104</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.25999999046325684</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0</real>
<real>0.96692299842834473</real>
<real>-0.25506836175918579</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>1128</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>28</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.8287147283554077</real>
<key>sunlight_color</key>
<array>
<real>0.65999996662139893</real>
<real>0.78157895803451538</real>
<real>0.89999997615814209</real>
<real>0.29999998211860657</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>blue_density</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.41999998688697815</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>11.539999961853027</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00042999998549930751</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>8.1000003814697266</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.079999998211860657</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0</real>
<real>0.89100658893585205</real>
<real>0.45399042963981628</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>718.70001220703125</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>23</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.0995575189590454</real>
<key>sunlight_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.9499999284744263</real>
<real>1.9499999284744263</real>
<real>1.9499999284744263</real>
<real>0.64999997615814209</real>
</array>
<key>blue_density</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.99999999999999289</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.4199999868869746</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.069999694824219</real>
<real>11.050000190734863</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00089999998454004526</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0.80000001192093606</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>4.7123889923095703</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000474974513</real>
<real>-0.47999998927116394</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.029999999329447746</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0.99144464731216431</real>
<real>0.13052797317504883</real>
<real>-1.182285913614578e-008</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>4000</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>3.0106911659240723</real>
<key>sunlight_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>3</real>
<real>3</real>
<real>3</real>
<real>1</real>
</array>
<key>blue_density</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.99999999999999289</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.4199999868869746</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.069999694824219</real>
<real>11.050000190734863</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00089999998454004526</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0.80000001192093606</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>4.7123889923095703</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000474974513</real>
<real>-0.47999998927116394</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.029999999329447746</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0.99144464731216431</real>
<real>0.13052797317504883</real>
<real>-1.182285913614578e-008</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>4000</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>3.0106911659240723</real>
<key>sunlight_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.3079376220703125</real>
<real>0.31744983792304993</real>
<real>0.4186248779296875</real>
<real>0.1395416259765625</real>
</array>
<key>blue_density</key>
<array>
<real>0.078125</real>
<real>0.078125</real>
<real>0.078125</real>
<real>0.0390625</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.1856689453125</real>
<real>0.2917042076587677</real>
<real>0.3768310546875</real>
<real>0.18841552734375</real>
</array>
<key>cloud_color</key>
<array>
<real>0.2981414794921875</real>
<real>0.0065460205078125</real>
<real>0.081723079085350037</real>
<real>0.2981414794921875</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.68000000715255737</real>
<real>0.5</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.18000000715255737</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.49940013885498</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.47999998927116394</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.25132742524147034</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.0199999809265137</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000474974513</real>
<real>-0.33000001311302185</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.57999998331069946</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.22304299473762512</real>
<real>0.44229033589363098</real>
<real>-0.86869502067565918</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>880</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>21</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.6834418773651123</real>
<key>sunlight_color</key>
<array>
<real>1.56097412109375</real>
<real>1.4059410095214844</real>
<real>1.34527587890625</real>
<real>0.52032470703125</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.21000002324581146</real>
<real>0.21000002324581146</real>
<real>0.21000002324581146</real>
<real>0.070000007748603821</real>
</array>
<key>blue_density</key>
<array>
<real>0.51999998092651367</real>
<real>0.51999998092651367</real>
<real>0.51999998092651367</real>
<real>0.25999999046325684</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.296875</real>
<real>0.296875</real>
<real>0.296875</real>
<real>0.1484375</real>
</array>
<key>cloud_color</key>
<array>
<real>0.69140625</real>
<real>0.69140625</real>
<real>0.69140625</real>
<real>0.69140625</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.57999998331069946</real>
<real>0.44999998807907104</real>
<real>0.91999995708465576</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.41999998688697815</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.649999618530273</real>
<real>10.359999656677246</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.47999998927116394</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00014000000373926014</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>11.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>3.958406925201416</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1.0499999523162842</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.55000001192092896</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.099999994039535522</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0.5154578685760498</real>
<real>0.70710796117782593</real>
<real>0.48404696583747864</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>886</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>2</real>
<key>sun_angle</key>
<real>2.3561928272247314</real>
<key>sunlight_color</key>
<array>
<real>0.87000000476837158</real>
<real>0.87000000476837158</real>
<real>0.87000000476837158</real>
<real>0.28999999165534973</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.291046142578125</real>
<real>0.42025142908096313</real>
<real>0.505828857421875</real>
<real>0.168609619140625</real>
</array>
<key>blue_density</key>
<array>
<real>0.15999999642372131</real>
<real>0.15999999642372131</real>
<real>0.15999999642372131</real>
<real>0.079999998211860657</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.19999998807907104</real>
<real>0.19999998807907104</real>
<real>0.19999998807907104</real>
<real>0.099999994039535522</real>
</array>
<key>cloud_color</key>
<array>
<real>0.61395263671875</real>
<real>0.50241565704345703</real>
<real>0.48760986328125</real>
<real>0.61395263671875</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.52999997138977051</real>
<real>0.45999997854232788</real>
<real>0.070000000298023224</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.039999999105930328</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.15999999642372131</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>20</real>
<real>20</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.68000000715255737</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00015999999595806003</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>12.199999809265137</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>5.026547908782959</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1.2200000286102295</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>0.19999980926513672</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>4</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.17999999225139618</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0.93278193473815918</real>
<real>0.19509218633174896</real>
<real>-0.30307888984680176</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>347</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>18</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.9452412128448486</real>
<key>sunlight_color</key>
<array>
<real>0.91124993562698364</real>
<real>0.61839842796325684</real>
<real>0.52875006198883057</real>
<real>0.30374997854232788</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.6568816900253296</real>
<real>1.5270273685455322</real>
<real>1.4837433099746704</real>
<real>0.55229389667510986</real>
</array>
<key>blue_density</key>
<array>
<real>1.1843417882919312</real>
<real>1.3887529373168945</real>
<real>1.4597588777542114</real>
<real>0.72987943887710571</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.468017578125</real>
<real>0.62104421854019165</real>
<real>0.641357421875</real>
<real>0.3206787109375</real>
</array>
<key>cloud_color</key>
<array>
<real>0.0234222412109375</real>
<real>0.018011331558227539</real>
<real>0.0156402587890625</real>
<real>0.0234222412109375</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.39999997615814209</real>
<real>0.81000000238418579</real>
<real>0.10999999940395355</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.56000000238418579</real>
<real>1</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.08999999612569809</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.6499996185302734</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00017999998817685992</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>2</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>1.2566369771957397</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1.6299999952316284</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000474974513</real>
<real>-0.39999997615814209</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>1.2300000190734863</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.57896542549133301</real>
<real>0.79335421323776245</real>
<real>-0.18811732530593872</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>293</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.2252933979034424</real>
<key>sunlight_color</key>
<array>
<real>1.62725830078125</real>
<real>1.8157768249511719</real>
<real>1.98211669921875</real>
<real>0.66070556640625</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.89040267467498779</real>
<real>1.0580335855484009</real>
<real>1.4399999380111694</real>
<real>0.47999998927116394</real>
</array>
<key>blue_density</key>
<array>
<real>0.43015211820602417</real>
<real>0.44939243793487549</real>
<real>0.51507484912872314</real>
<real>0.25753742456436157</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.15130999684333801</real>
<real>0.30000001192092896</real>
<real>0.35131001472473145</real>
<real>1</real>
</array>
<key>cloud_color</key>
<array>
<real>0.21396400034427643</real>
<real>0.21396400034427643</real>
<real>0.21396400034427643</real>
<real>1</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.099999994039535522</real>
<real>0.09771379828453064</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.079754598438739777</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.41999998688697815</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.644780158996582</real>
<real>10.423800468444824</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.45999997854232788</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00015800200344529003</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000474974513</real>
<real>-0.33000001311302185</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>1.2300000190734863</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.17999999225139618</real>
<real>0.13210900127887726</real>
<real>0.13210900127887726</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0</real>
<real>0.99785882234573364</real>
<real>0.06540437787771225</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>600</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>3</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.5053452253341675</real>
<key>sunlight_color</key>
<array>
<real>3</real>
<real>3</real>
<real>3</real>
<real>1</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.31640625</real>
<real>0.31640625</real>
<real>0.31640625</real>
<real>0.10546875</real>
</array>
<key>blue_density</key>
<array>
<real>0.40625</real>
<real>0.59642577171325684</real>
<real>0.63374996185302734</real>
<real>0.31687498092651367</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.296875</real>
<real>0.296875</real>
<real>0.296875</real>
<real>0.1484375</real>
</array>
<key>cloud_color</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>0</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.57999998331069946</real>
<real>0.44999998807907104</real>
<real>0.91999995708465576</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>1.6884100437164307</real>
<real>0.52609699964523315</real>
<real>0.15999999642372131</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.5899999737739563</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.649999618530273</real>
<real>10.359999656677246</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.32999998331069946</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00016999999934341758</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>31.30000114440918</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>2.4504423141479492</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1.0499999523162842</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>0.19999980926513672</real>
<real>0.0010000000474974513</real>
<real>-2.5</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.59898221492767334</real>
<real>0.34202155470848083</real>
<real>0.72404521703720093</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>827</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.7925252914428711</real>
<key>sunlight_color</key>
<array>
<real>0.77144533395767212</real>
<real>0.86230051517486572</real>
<real>0.9685547947883606</real>
<real>0.32285159826278687</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.7898764610290527</real>
<real>1.7999999523162842</real>
<real>1.7999999523162842</real>
<real>0.59999996423721313</real>
</array>
<key>blue_density</key>
<array>
<real>0.17840505994550426</real>
<real>0.41624023325357484</real>
<real>0.78666704491092787</real>
<real>0.79331876337528229</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.23695440116756572</real>
<real>0.30749256375219147</real>
<real>0.38000917434692383</real>
<real>0.77331733703613281</real>
</array>
<key>cloud_color</key>
<array>
<real>0.28744032449850465</real>
<real>0.28744032449850465</real>
<real>0.28744032449850465</real>
<real>0.80331945419311523</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>1</real>
<real>1</real>
<real>0.92000281969717435</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.29999998211860657</real>
<real>0.29999998211860657</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.4199999912582939</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.399593049491159</real>
<real>10.010999738700775</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.39999997615814209</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.0003666600819003385</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0.93332862854003906</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.0010000000491261485</real>
<real>-0.47999999284769501</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.69999999205299446</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.17000070333489248</real>
<real>0.1991559977192594</real>
<real>0.1991559977192594</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0</real>
<real>0.3070400059223175</real>
<real>-0.95169663429260254</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>910.02454853057861</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.8295114040374756</real>
<key>sunlight_color</key>
<array>
<real>0.29243719577789307</real>
<real>0.29459795355796814</real>
<real>0.29999998211860657</real>
<real>0.099999994039535522</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.73828125</real>
<real>0.63248288631439209</real>
<real>0.61171871423721313</real>
<real>0.24609375</real>
</array>
<key>blue_density</key>
<array>
<real>1.0170860290527344</real>
<real>1.015869140625</real>
<real>1.171630859375</real>
<real>0.5858154296875</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.287109375</real>
<real>0.39328005909919739</real>
<real>0.478515625</real>
<real>0.2392578125</real>
</array>
<key>cloud_color</key>
<array>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.34999999403953552</real>
<real>0.31000000238418579</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.099999994039535522</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.49940013885498</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00026000000070780516</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>6.0999999046325684</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.62831848859786987</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>8.2000017166137695</real>
<real>0.0010000000474974513</real>
<real>-0.19999998807907104</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>1.7799999713897705</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.23999999463558197</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.47401577234268188</real>
<real>0.59131073951721191</real>
<real>-0.65242677927017212</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>720</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>21</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.5089094638824463</real>
<key>sunlight_color</key>
<array>
<real>1.677886962890625</real>
<real>1.646484375</real>
<real>1.728515625</real>
<real>0.576171875</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.73851561546325684</real>
<real>0.65285521745681763</real>
<real>0.64148437976837158</real>
<real>0.24617186188697815</real>
</array>
<key>blue_density</key>
<array>
<real>0.83835935592651367</real>
<real>1.0087707042694092</real>
<real>1.1216405630111694</real>
<real>0.56082028150558472</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.387725830078125</real>
<real>0.24486541748046875</real>
<real>0.159149169921875</real>
<real>0.1938629150390625</real>
</array>
<key>cloud_color</key>
<array>
<real>0.9296875</real>
<real>0.9296875</real>
<real>0.9296875</real>
<real>0.9296875</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.47999998927116394</real>
<real>0.5</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.53999996185302734</real>
<real>0.4699999988079071</real>
<real>0.059999998658895493</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.3099999725818634</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.49940013885498</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.29999998211860657</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00026999998954124749</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>2.6389377117156982</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>0.74000000953674316</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5.5999994277954102</real>
<real>0.0010000000474974513</real>
<real>-0.29999998211860657</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>1.8899999856948853</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.26999998092651367</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.44095441699028015</real>
<real>0.40274816751480103</real>
<real>0.80209296941757202</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>752</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>21</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.7270753383636475</real>
<key>sunlight_color</key>
<array>
<real>1.4399999380111694</real>
<real>1.4399999380111694</real>
<real>1.4399999380111694</real>
<real>0.47999998927116394</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.53460693359375</real>
<real>1.2169990539550781</real>
<real>1.18414306640625</real>
<real>0.51153564453125</real>
</array>
<key>blue_density</key>
<array>
<real>1.5458984375</real>
<real>1.520721435546875</real>
<real>1.5166015625</real>
<real>0.77294921875</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.5263671875</real>
<real>0.2863616943359375</real>
<real>0.2392578125</real>
<real>0.26318359375</real>
</array>
<key>cloud_color</key>
<array>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
<real>0.50999999046325684</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.34999999403953552</real>
<real>0.31000000238418579</real>
<real>1</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.5</real>
<real>0.5</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.099999994039535522</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.49940013885498</real>
<real>10.01099967956543</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.0002899999963119626</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>0</boolean>
<boolean>0</boolean>
</array>
<key>gamma</key>
<array>
<real>1</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-2.1499998569488525</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.23999999463558197</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0</real>
<real>0.5735776424407959</real>
<real>-0.81915122270584106</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>560</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>21</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>2.5307259559631348</real>
<key>sunlight_color</key>
<array>
<real>0.9609375</real>
<real>0.9609375</real>
<real>0.9609375</real>
<real>0.3203125</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>1.2274205684661865</real>
<real>1.220489501953125</real>
<real>2.107635498046875</real>
<real>0.702545166015625</real>
</array>
<key>blue_density</key>
<array>
<real>0.625</real>
<real>0.625</real>
<real>0.625</real>
<real>0.3125</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.025146491825580597</real>
<real>0.099853508174419403</real>
<real>0.050827037543058395</real>
<real>0.049926754087209702</real>
</array>
<key>cloud_color</key>
<array>
<real>0.54396075010299683</real>
<real>0.1728515625</real>
<real>0.8818359375</real>
<real>0.8818359375</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.70999997854232788</real>
<real>0.53047597408294678</real>
<real>0.32999998331069946</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.29999998211860657</real>
<real>0.29999998211860657</real>
<real>0.25</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.25999999046325684</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.069999694824219</real>
<real>9.7899999618530273</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.43999999761581421</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00069000001531094313</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>10</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>2.3876104354858398</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>2</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>1.6000008583068848</real>
<real>0.0010000000474974513</real>
<real>-1.1999999284744263</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.59999996423721313</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.35999998450279236</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.56918007135391235</real>
<real>0.55557030439376831</real>
<real>0.60611522197723389</real>
<real>1</real>
</array>
<key>max_y</key>
<array>
<real>1493</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>2</integer>
<key>star_brightness</key>
<real>2</real>
<key>sun_angle</key>
<real>5.6941366195678711</real>
<key>sunlight_color</key>
<array>
<real>2.1550192832946777</real>
<real>1.8917083740234375</real>
<real>2.4442291259765625</real>
<real>0.8147430419921875</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>2.03999996185302734375</real>
<real>1.94999992847442626953125</real>
<real>1.83000004291534423828125</real>
<real>2.03999996185302734375</real>
</array>
<key>blue_density</key>
<array>
<real>0.63999998569488525390625</real>
<real>1.17999994754791259765625</real>
<real>2</real>
<real>2</real>
</array>
<key>blue_horizon</key>
<array>
<real>0.2199999988079071044921875</real>
<real>0.2199999988079071044921875</real>
<real>0.319999992847442626953125</real>
<real>0.319999992847442626953125</real>
</array>
<key>cloud_color</key>
<array>
<real>0.2261587334555770212318748</real>
<real>0.2261587334555770212318748</real>
<real>0.2261587334555770212318748</real>
<real>0.9999848079680475620989455</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.884190976619720458984375</real>
<real>0.53047597408294677734375</real>
<real>0.8800030851365363560034893</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.384193003177642822265625</real>
<real>0.5</real>
<real>0.125</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.4199999868869781494140625</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>10.49939242953405482694507</real>
<real>10.0109996795654296875</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.1599999964237213134765625</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.0001799999881768599152565002</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>2</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>1.00530970096588134765625</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.2200000286102294921875</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>5</real>
<real>0.001000000047497451305389404</real>
<real>-0.449999988079071044921875</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.680000007152557373046875</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.08999999612569808959960938</real>
<real>0.1991560012102127075195312</real>
<real>0.1991560012102127075195312</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>-0.8443279266357421875</real>
<real>0</real>
<real>0.535826742649078369140625</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>188</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>2</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>0</real>
<key>sunlight_color</key>
<array>
<real>2.5799999237060546875</real>
<real>2.5799999237060546875</real>
<real>2.610000133514404296875</real>
<real>2.610000133514404296875</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.29999998211860657</real>
</array>
<key>blue_density</key>
<array>
<real>0.36974793672561646</real>
<real>0.38095235824584961</real>
<real>0.39999997615814209</real>
<real>0.19999998807907104</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0.042857140302658081</real>
<real>0.059999998658895493</real>
<real>0.029999999329447746</real>
</array>
<key>cloud_color</key>
<array>
<real>0.039999999105930328</real>
<real>0.056000009179115295</real>
<real>0.079999998211860657</real>
<real>0.079999998211860657</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.40999999642372131</real>
<real>0.23999999463558197</real>
<real>0.099999994039535522</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.32999998331069946</real>
<real>0.45999997854232788</real>
<real>0.11999999731779099</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.25</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.7100000381469727</real>
<real>10.590000152587891</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00014999999257270247</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.9424777626991272</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.48999997973442078</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.049999997019767761</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.30724778771400452</real>
<real>0.92507719993591309</real>
<real>-0.22322858870029449</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>188</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.9603538513183594</real>
<key>sunlight_color</key>
<array>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.049999997019767761</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.29999998211860657</real>
</array>
<key>blue_density</key>
<array>
<real>0.36974793672561646</real>
<real>0.38095235824584961</real>
<real>0.39999997615814209</real>
<real>0.19999998807907104</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0.042857140302658081</real>
<real>0.059999998658895493</real>
<real>0.029999999329447746</real>
</array>
<key>cloud_color</key>
<array>
<real>0.039999999105930328</real>
<real>0.056000009179115295</real>
<real>0.079999998211860657</real>
<real>0.079999998211860657</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.40999999642372131</real>
<real>0.23999999463558197</real>
<real>0.099999994039535522</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.32999998331069946</real>
<real>0.45999997854232788</real>
<real>0.11999999731779099</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.25</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.7100000381469727</real>
<real>10.590000152587891</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.0003499999875202775</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>60.0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.9424777626991272</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.48999997973442078</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.049999997019767761</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.30724778771400452</real>
<real>0.92507719993591309</real>
<real>-0.22322858870029449</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>188</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.9603538513183594</real>
<key>sunlight_color</key>
<array>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.049999997019767761</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.29999998211860657</real>
</array>
<key>blue_density</key>
<array>
<real>0.36974793672561646</real>
<real>0.38095235824584961</real>
<real>0.39999997615814209</real>
<real>0.19999998807907104</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0.042857140302658081</real>
<real>0.059999998658895493</real>
<real>0.029999999329447746</real>
</array>
<key>cloud_color</key>
<array>
<real>0.039999999105930328</real>
<real>0.056000009179115295</real>
<real>0.079999998211860657</real>
<real>0.079999998211860657</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.40999999642372131</real>
<real>0.23999999463558197</real>
<real>0.099999994039535522</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.32999998331069946</real>
<real>0.45999997854232788</real>
<real>0.11999999731779099</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.25</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.7100000381469727</real>
<real>10.590000152587891</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00025000001187436283</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>40.0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.9424777626991272</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.48999997973442078</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.049999997019767761</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.30724778771400452</real>
<real>0.92507719993591309</real>
<real>-0.22322858870029449</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>188</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.9603538513183594</real>
<key>sunlight_color</key>
<array>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.049999997019767761</real>
</array>
</map>
</llsd>

View File

@@ -0,0 +1,141 @@
<llsd>
<map>
<key>ambient</key>
<array>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.89999997615814209</real>
<real>0.29999998211860657</real>
</array>
<key>blue_density</key>
<array>
<real>0.36974793672561646</real>
<real>0.38095235824584961</real>
<real>0.39999997615814209</real>
<real>0.19999998807907104</real>
</array>
<key>blue_horizon</key>
<array>
<real>0</real>
<real>0.042857140302658081</real>
<real>0.059999998658895493</real>
<real>0.029999999329447746</real>
</array>
<key>cloud_color</key>
<array>
<real>0.039999999105930328</real>
<real>0.056000009179115295</real>
<real>0.079999998211860657</real>
<real>0.079999998211860657</real>
</array>
<key>cloud_pos_density1</key>
<array>
<real>0.40999999642372131</real>
<real>0.23999999463558197</real>
<real>0.099999994039535522</real>
<real>1</real>
</array>
<key>cloud_pos_density2</key>
<array>
<real>0.32999998331069946</real>
<real>0.45999997854232788</real>
<real>0.11999999731779099</real>
<real>1</real>
</array>
<key>cloud_scale</key>
<array>
<real>0.25</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>cloud_scroll_rate</key>
<array>
<real>9.7100000381469727</real>
<real>10.590000152587891</real>
</array>
<key>cloud_shadow</key>
<array>
<real>0.35999998450279236</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>density_multiplier</key>
<array>
<real>0.00014999999257270247</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>distance_multiplier</key>
<array>
<real>20.0</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>east_angle</key>
<real>0.9424777626991272</real>
<key>enable_cloud_scroll</key>
<array>
<boolean>1</boolean>
<boolean>1</boolean>
</array>
<key>gamma</key>
<array>
<real>1.5</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>glow</key>
<array>
<real>20</real>
<real>0.0010000000474974513</real>
<real>-0</real>
<real>1</real>
</array>
<key>haze_density</key>
<array>
<real>0.48999997973442078</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>haze_horizon</key>
<array>
<real>0.049999997019767761</real>
<real>0.19915600121021271</real>
<real>0.19915600121021271</real>
<real>1</real>
</array>
<key>lightnorm</key>
<array>
<real>0.30724778771400452</real>
<real>0.92507719993591309</real>
<real>-0.22322858870029449</real>
<real>0</real>
</array>
<key>max_y</key>
<array>
<real>188</real>
<real>0</real>
<real>0</real>
<real>1</real>
</array>
<key>preset_num</key>
<integer>22</integer>
<key>star_brightness</key>
<real>0</real>
<key>sun_angle</key>
<real>1.9603538513183594</real>
<key>sunlight_color</key>
<array>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.14999999105930328</real>
<real>0.049999997019767761</real>
</array>
</map>
</llsd>

View File

@@ -2,7 +2,7 @@
<map>
<key>ambient</key>
<array>
<real>0.29999998211860657</real>
<real>0.29999998211860657</real>
<real>0.17999999225139618</real>
<real>0</real>
<real>0.29999998211860657</real>

View File

@@ -2,9 +2,9 @@
# @brief Create the windows app.config file to redirect crt linkage.
#
# $LicenseInfo:firstyear=2009&license=viewergpl$
#
#
# Copyright (c) 2009, Linden Research, Inc.
#
#
# Second Life Viewer Source Code
# The source code in this file ("Source Code") is provided by Linden Lab
# to you under the terms of the GNU General Public License, version 2.0
@@ -12,17 +12,17 @@
# ("Other License"), formally executed by you and Linden Lab. Terms of
# the GPL can be found in doc/GPL-license.txt in this distribution, or
# online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
#
#
# There are special exceptions to the terms and conditions of the GPL as
# it is applied to this Source Code. View the full text of the exception
# in the file doc/FLOSS-exception.txt in this software distribution, or
# online at
# http://secondlifegrid.net/programs/open_source/licensing/flossexception
#
#
# By copying, modifying or distributing this software, you acknowledge
# that you have read and understood your obligations described above,
# and agree to abide by those obligations.
#
#
# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
# COMPLETENESS OR PERFORMANCE.
@@ -35,23 +35,23 @@ def main():
src_manifest_name = sys.argv[1]
src_config_name = sys.argv[2]
dst_config_name = sys.argv[3]
manifest_dom = parse(src_manifest_name)
node = manifest_dom.getElementsByTagName('assemblyIdentity')[0]
manifest_assm_ver = node.getAttribute('version')
config_dom = parse(src_config_name)
node = config_dom.getElementsByTagName('bindingRedirect')[0]
node.setAttribute('newVersion', manifest_assm_ver)
src_old_ver=re.match('([^-]*-).*', node.getAttribute('oldVersion')).group(1)
src_old_ver=re.match('([^-]*-).*', node.getAttribute('oldVersion')).group(1)
node.setAttribute('oldVersion', src_old_ver + manifest_assm_ver)
comment = config_dom.createComment("This file is automatically generated by the build. see indra/newview/build_win32_appConfig.py")
config_dom.insertBefore(comment, config_dom.childNodes[0])
f = open(dst_config_name, 'w')
config_dom.writexml(f)
f.close()
return 0
if __name__ == "__main__":

Some files were not shown because too many files have changed in this diff Show More