summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCallum Prentice <callum@lindenlab.com>2025-10-22 19:16:59 -0700
committerCallum Prentice <callum@lindenlab.com>2025-10-22 19:16:59 -0700
commit21e9b38f06e187414d30c12c8c69ebf7b4e4e636 (patch)
treed41d6b2db8a878cc18f2215e01fd710fb663a76e
parentc64c16a59ae2220b29ba0ed1f3f22da07e694612 (diff)
parent0da96a508a5d8d384e017f707babd2193f6ca6a7 (diff)
Merge branch 'develop' of https://github.com/secondlife/viewer into callum/voice-moderation-2
-rw-r--r--.github/workflows/build.yaml6
-rw-r--r--.github/workflows/pre-commit.yaml4
-rw-r--r--autobuild.xml14
-rw-r--r--indra/cmake/APR.cmake5
-rw-r--r--indra/llcommon/llfile.cpp390
-rw-r--r--indra/llcommon/llfile.h108
-rw-r--r--indra/llcommon/llsys.cpp8
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp4
-rw-r--r--indra/llcrashlogger/llcrashlogger.cpp4
-rw-r--r--indra/llfilesystem/llfilesystem.cpp3
-rw-r--r--indra/llmessage/llcachename.cpp1
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp1
-rw-r--r--indra/llui/llviewereventrecorder.cpp2
-rw-r--r--indra/llwebrtc/llwebrtc.cpp7
-rw-r--r--indra/llwebrtc/llwebrtc.h1
-rw-r--r--indra/llwebrtc/llwebrtc_impl.h1
-rw-r--r--indra/newview/llappviewer.cpp1
-rw-r--r--indra/newview/llfloateravatarpicker.cpp150
-rw-r--r--indra/newview/llfloateravatarpicker.h3
-rw-r--r--indra/newview/llfloaterimcontainer.cpp5
-rw-r--r--indra/newview/llselectmgr.cpp1
-rw-r--r--indra/newview/lltooldraganddrop.cpp2
-rw-r--r--indra/newview/llvoicevivox.cpp1
-rw-r--r--indra/newview/llvoicewebrtc.cpp57
-rw-r--r--indra/newview/llvoicewebrtc.h1
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml1
26 files changed, 555 insertions, 226 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 5a5628ede2..4c948e5586 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -86,7 +86,7 @@ jobs:
variants: ${{ matrix.configuration }}
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
@@ -95,14 +95,14 @@ jobs:
with:
python-version: "3.11"
- name: Checkout build variables
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
repository: secondlife/build-variables
ref: master
path: .build-variables
- name: Checkout master-message-template
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
repository: secondlife/master-message-template
path: .master-message-template
diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml
index d626eef38d..726e1cd889 100644
--- a/.github/workflows/pre-commit.yaml
+++ b/.github/workflows/pre-commit.yaml
@@ -11,8 +11,8 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: actions/setup-python@v4
with:
python-version: 3.x
- - uses: pre-commit/action@v3.0.0
+ - uses: pre-commit/action@v3.0.1
diff --git a/autobuild.xml b/autobuild.xml
index 2983307cbe..dbbe1981b6 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -46,11 +46,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>a21487f4e3a68721fd97edef117795a1b7212a77</string>
+ <string>3f8e58fe74e144b771858e89a5514bf354964c76</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-darwin64-12259255574.tar.zst</string>
+ <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-darwin64-18696779749.tar.zst</string>
</map>
<key>name</key>
<string>darwin64</string>
@@ -60,11 +60,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>45baf82d3366734e542a2a3749f495b64f5513b4</string>
+ <string>df36114eb8fb66fdcbb190011222a7d336791d98</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-linux64-12259255574.tar.zst</string>
+ <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-linux64-18696779749.tar.zst</string>
</map>
<key>name</key>
<string>linux64</string>
@@ -74,11 +74,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>bdd35d3b9580d3cdcb98afae639936aaa40e24c4</string>
+ <string>164705831819d247753c9fd608c2c1ac8dca1883</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-windows64-12259255574.tar.zst</string>
+ <string>https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-windows64-18696779749.tar.zst</string>
</map>
<key>name</key>
<string>windows64</string>
@@ -91,7 +91,7 @@
<key>copyright</key>
<string>Copyright © 2012 The Apache Software Foundation, Licensed under the Apache License, Version 2.0.</string>
<key>version</key>
- <string>1.7.5-12259255574</string>
+ <string>1.7.5-18696779749</string>
<key>name</key>
<string>apr_suite</string>
<key>description</key>
diff --git a/indra/cmake/APR.cmake b/indra/cmake/APR.cmake
index 310659686d..e0807a7d19 100644
--- a/indra/cmake/APR.cmake
+++ b/indra/cmake/APR.cmake
@@ -31,8 +31,3 @@ if(DARWIN)
endif()
target_include_directories(ll::apr SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/apr-1)
-
-# Fix erroneous check for __attribute__ definition introduced with APR 1.7.5, causing lots of "this declaration may not have extern 'C' linkage" errors in VS
-file(READ ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h APR_HEADER_CONTENTS)
-string(REPLACE "#if !(defined(__attribute__) || defined(__has_attribute))" "#if !defined(__attribute__)" APR_HEADER_CONTENTS "${APR_HEADER_CONTENTS}")
-file(WRITE ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h "${APR_HEADER_CONTENTS}")
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 53659ac13f..a539e4fe28 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -35,7 +35,6 @@
#if LL_WINDOWS
#include "llwin32headers.h"
-#include <stdlib.h> // Windows errno
#include <vector>
#else
#include <errno.h>
@@ -49,6 +48,86 @@ static std::string empty;
// variants of strerror() to report errors.
#if LL_WINDOWS
+// For the situations where we directly call into Windows API functions we need to translate
+// the Windows error codes into errno values
+namespace
+{
+ struct errentry
+ {
+ unsigned long oserr; // Windows OS error value
+ int errcode; // System V error code
+ };
+}
+
+// translation table between Windows OS error value and System V errno code
+static errentry const errtable[]
+{
+ { ERROR_INVALID_FUNCTION, EINVAL }, // 1
+ { ERROR_FILE_NOT_FOUND, ENOENT }, // 2
+ { ERROR_PATH_NOT_FOUND, ENOENT }, // 3
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4
+ { ERROR_ACCESS_DENIED, EACCES }, // 5
+ { ERROR_INVALID_HANDLE, EBADF }, // 6
+ { ERROR_ARENA_TRASHED, ENOMEM }, // 7
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8
+ { ERROR_INVALID_BLOCK, ENOMEM }, // 9
+ { ERROR_BAD_ENVIRONMENT, E2BIG }, // 10
+ { ERROR_BAD_FORMAT, ENOEXEC }, // 11
+ { ERROR_INVALID_ACCESS, EINVAL }, // 12
+ { ERROR_INVALID_DATA, EINVAL }, // 13
+ { ERROR_INVALID_DRIVE, ENOENT }, // 15
+ { ERROR_CURRENT_DIRECTORY, EACCES }, // 16
+ { ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
+ { ERROR_NO_MORE_FILES, ENOENT }, // 18
+ { ERROR_LOCK_VIOLATION, EACCES }, // 33
+ { ERROR_BAD_NETPATH, ENOENT }, // 53
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
+ { ERROR_BAD_NET_NAME, ENOENT }, // 67
+ { ERROR_FILE_EXISTS, EEXIST }, // 80
+ { ERROR_CANNOT_MAKE, EACCES }, // 82
+ { ERROR_FAIL_I24, EACCES }, // 83
+ { ERROR_INVALID_PARAMETER, EINVAL }, // 87
+ { ERROR_NO_PROC_SLOTS, EAGAIN }, // 89
+ { ERROR_DRIVE_LOCKED, EACCES }, // 108
+ { ERROR_BROKEN_PIPE, EPIPE }, // 109
+ { ERROR_DISK_FULL, ENOSPC }, // 112
+ { ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114
+ { ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130
+ { ERROR_NEGATIVE_SEEK, EINVAL }, // 131
+ { ERROR_SEEK_ON_DEVICE, EACCES }, // 132
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145
+ { ERROR_NOT_LOCKED, EACCES }, // 158
+ { ERROR_BAD_PATHNAME, ENOENT }, // 161
+ { ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164
+ { ERROR_LOCK_FAILED, EACCES }, // 167
+ { ERROR_ALREADY_EXISTS, EEXIST }, // 183
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215
+ { ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
+};
+
+static int set_errno_from_oserror(unsigned long oserr)
+{
+ if (!oserr)
+ return 0;
+
+ // Check the table for the Windows OS error code
+ for (const struct errentry &entry : errtable)
+ {
+ if (oserr == entry.oserr)
+ {
+ _set_errno(entry.errcode);
+ return -1;
+ }
+ }
+
+ _set_errno(EINVAL);
+ return -1;
+}
+
// On Windows, use strerror_s().
std::string strerr(int errn)
{
@@ -57,7 +136,67 @@ std::string strerr(int errn)
return buffer;
}
-typedef std::basic_ios<char,std::char_traits < char > > _Myios;
+inline bool is_slash(wchar_t const c)
+{
+ return c == L'\\' || c == L'/';
+}
+
+static std::wstring utf8path_to_wstring(const std::string& utf8path)
+{
+ if (utf8path.size() >= MAX_PATH)
+ {
+ // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
+ std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
+ // We need to make sure that the path does not contain forward slashes as above
+ // prefix does bypass the path normalization that replaces slashes with backslashes
+ // before passing the path to kernel mode APIs
+ std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
+ return utf16path;
+ }
+ return ll_convert<std::wstring>(utf8path);
+}
+
+static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
+{
+ unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (dontFollowSymLink)
+ {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+ HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, flags, nullptr);
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ FILE_ATTRIBUTE_TAG_INFO attribute_info;
+ if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
+ {
+ // A volume path alone (only drive letter) is not recognized as directory while it technically is
+ bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
+ (iswalpha(utf16path[0]) && utf16path[1] == ':' &&
+ (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
+ unsigned short st_mode = is_directory ? S_IFDIR :
+ (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
+ st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
+ // we do not try to guess executable flag
+
+ // propagate user bits to group/other fields:
+ st_mode |= (st_mode & 0700) >> 3;
+ st_mode |= (st_mode & 0700) >> 6;
+
+ CloseHandle(file_handle);
+ return st_mode;
+ }
+ }
+ // Retrieve last error and set errno before calling CloseHandle()
+ set_errno_from_oserror(GetLastError());
+
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(file_handle);
+ }
+ return 0;
+}
#else
// On Posix we want to call strerror_r(), but alarmingly, there are two
@@ -108,18 +247,13 @@ std::string strerr(int errn)
}
#endif // ! LL_WINDOWS
-// On either system, shorthand call just infers global 'errno'.
-std::string strerr()
-{
- return strerr(errno);
-}
-
-int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
+static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
{
if (rc < 0)
{
// Capture errno before we start emitting output
int errn = errno;
+
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
// EEXIST. Don't warn if caller explicitly says this errno is okay.
@@ -176,62 +310,59 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
+ // We often use mkdir() to ensure the existence of a directory that might
+ // already exist. There is no known case in which we want to call out as
+ // an error the requested directory already existing.
#if LL_WINDOWS
// permissions are ignored on Windows
- std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
- int rc = _wmkdir(utf16dirname.c_str());
+ int rc = 0;
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
+ if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
+ {
+ // Only treat other errors than an already existing file as a real error
+ unsigned long oserr = GetLastError();
+ if (oserr != ERROR_ALREADY_EXISTS)
+ {
+ rc = set_errno_from_oserror(oserr);
+ }
+ }
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
-#endif
- // We often use mkdir() to ensure the existence of a directory that might
- // already exist. There is no known case in which we want to call out as
- // an error the requested directory already existing.
if (rc < 0 && errno == EEXIST)
{
// this is not the error you want, move along
return 0;
}
+#endif
// anything else might be a problem
- return warnif("mkdir", dirname, rc, EEXIST);
+ return warnif("mkdir", dirname, rc);
}
// static
-int LLFile::rmdir(const std::string& dirname)
+int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
#if LL_WINDOWS
- // permissions are ignored on Windows
- std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
int rc = _wrmdir(utf16dirname.c_str());
#else
int rc = ::rmdir(dirname.c_str());
#endif
- return warnif("rmdir", dirname, rc);
+ return warnif("rmdir", dirname, rc, suppress_error);
}
// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */
-{
-#if LL_WINDOWS
- std::wstring utf16filename = ll_convert<std::wstring>(filename);
- std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
- return _wfopen(utf16filename.c_str(),utf16mode.c_str());
-#else
- return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */
-#endif
-}
-
-LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag)
+LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
{
#if LL_WINDOWS
- std::wstring utf16filename = ll_convert<std::wstring>(filename);
+ std::wstring utf16filename = utf8path_to_wstring(filename);
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
- return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
+ return _wfopen(utf16filename.c_str(), utf16mode.c_str());
#else
- llassert(0);//No corresponding function on non-windows
- return NULL;
+ return ::fopen(filename.c_str(),mode);
#endif
}
+// static
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
@@ -242,9 +373,10 @@ int LLFile::close(LLFILE * file)
return ret_value;
}
+// static
std::string LLFile::getContents(const std::string& filename)
{
- LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */
+ LLFILE* fp = LLFile::fopen(filename, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
@@ -261,42 +393,80 @@ std::string LLFile::getContents(const std::string& filename)
return LLStringUtil::null;
}
-int LLFile::remove(const std::string& filename, int supress_error)
+// static
+int LLFile::remove(const std::string& filename, int suppress_error)
{
#if LL_WINDOWS
- std::wstring utf16filename = ll_convert<std::wstring>(filename);
- int rc = _wremove(utf16filename.c_str());
+ // Posix remove() works on both files and directories although on Windows
+ // remove() and its wide char variant _wremove() only removes files just
+ // as its siblings unlink() and _wunlink().
+ // If we really only want to support files we should instead use
+ // unlink() in the non-Windows part below too
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename);
+ if (S_ISDIR(st_mode))
+ {
+ rc = _wrmdir(utf16filename.c_str());
+ }
+ else if (S_ISREG(st_mode))
+ {
+ rc = _wunlink(utf16filename.c_str());
+ }
+ else if (st_mode)
+ {
+ // it is something else than a file or directory
+ // this should not really happen as long as we do not allow for symlink
+ // detection in the optional parameter to get_fileattr()
+ rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ // get_fileattr() failed and already set errno, preserve it for correct error reporting
+ }
#else
int rc = ::remove(filename.c_str());
#endif
- return warnif("remove", filename, rc, supress_error);
+ return warnif("remove", filename, rc, suppress_error);
}
-int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error)
+// static
+int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
{
#if LL_WINDOWS
- std::wstring utf16filename = ll_convert<std::wstring>(filename);
- std::wstring utf16newname = ll_convert<std::wstring>(newname);
- int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
+ // Posix rename() will gladly overwrite a file at newname if it exists, the Windows
+ // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
+ // API MoveFileEx() and use its flags to specify that overwrite is allowed.
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ std::wstring utf16newname = utf8path_to_wstring(newname);
+ int rc = 0;
+ if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ {
+ rc = set_errno_from_oserror(GetLastError());
+ }
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
- return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error);
+ return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
+// Make this a define rather than using magic numbers multiple times in the code
+#define LLFILE_COPY_BUFFER_SIZE 16384
+
+// static
bool LLFile::copy(const std::string& from, const std::string& to)
{
bool copied = false;
- LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
+ LLFILE* in = LLFile::fopen(from, "rb");
if (in)
{
- LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
+ LLFILE* out = LLFile::fopen(to, "wb");
if (out)
{
- char buf[16384]; /* Flawfinder: ignore */
+ char buf[LLFILE_COPY_BUFFER_SIZE];
size_t readbytes;
bool write_ok = true;
- while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
+ while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
@@ -315,33 +485,62 @@ bool LLFile::copy(const std::string& from, const std::string& to)
return copied;
}
-int LLFile::stat(const std::string& filename, llstat* filestatus)
+// static
+int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
{
#if LL_WINDOWS
- std::wstring utf16filename = ll_convert<std::wstring>(filename);
- int rc = _wstat(utf16filename.c_str(),filestatus);
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ int rc = _wstat64(utf16filename.c_str(), filestatus);
#else
- int rc = ::stat(filename.c_str(),filestatus);
+ int rc = ::stat(filename.c_str(), filestatus);
#endif
- // We use stat() to determine existence (see isfile(), isdir()).
- // Don't spam the log if the subject pathname doesn't exist.
- return warnif("stat", filename, rc, ENOENT);
+ return warnif("stat", filename, rc, suppress_error);
}
-bool LLFile::isdir(const std::string& filename)
+// static
+unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
{
- llstat st;
+#if LL_WINDOWS
+ // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
+ // to just get the attributes
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
+ if (st_mode)
+ {
+ return st_mode;
+ }
+#else
+ llstat filestatus;
+ int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
+ if (rc == 0)
+ {
+ return filestatus.st_mode;
+ }
+#endif
+ warnif("getattr", filename, rc, suppress_error);
+ return 0;
+}
- return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
+// static
+bool LLFile::isdir(const std::string& filename)
+{
+ return S_ISDIR(getattr(filename));
}
+// static
bool LLFile::isfile(const std::string& filename)
{
- llstat st;
+ return S_ISREG(getattr(filename));
+}
- return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
+// static
+bool LLFile::islink(const std::string& filename)
+{
+ return S_ISLNK(getattr(filename, true));
}
+// static
const char *LLFile::tmpdir()
{
static std::string utf8path;
@@ -368,75 +567,8 @@ const char *LLFile::tmpdir()
return utf8path.c_str();
}
-
-/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
-
#if LL_WINDOWS
-LLFILE * LLFile::_Fiopen(const std::string& filename,
- std::ios::openmode mode)
-{ // open a file
- static const char *mods[] =
- { // fopen mode strings corresponding to valid[i]
- "r", "w", "w", "a", "rb", "wb", "wb", "ab",
- "r+", "w+", "a+", "r+b", "w+b", "a+b",
- 0};
- static const int valid[] =
- { // valid combinations of open flags
- ios_base::in,
- ios_base::out,
- ios_base::out | ios_base::trunc,
- ios_base::out | ios_base::app,
- ios_base::in | ios_base::binary,
- ios_base::out | ios_base::binary,
- ios_base::out | ios_base::trunc | ios_base::binary,
- ios_base::out | ios_base::app | ios_base::binary,
- ios_base::in | ios_base::out,
- ios_base::in | ios_base::out | ios_base::trunc,
- ios_base::in | ios_base::out | ios_base::app,
- ios_base::in | ios_base::out | ios_base::binary,
- ios_base::in | ios_base::out | ios_base::trunc
- | ios_base::binary,
- ios_base::in | ios_base::out | ios_base::app
- | ios_base::binary,
- 0};
-
- LLFILE *fp = 0;
- int n;
- ios_base::openmode atendflag = mode & ios_base::ate;
- ios_base::openmode norepflag = mode & ios_base::_Noreplace;
-
- if (mode & ios_base::_Nocreate)
- mode |= ios_base::in; // file must exist
- mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
- for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
- ; // look for a valid mode
-
- if (valid[n] == 0)
- return (0); // no valid mode
- else if (norepflag && mode & (ios_base::out | ios_base::app)
- && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
- { // file must not exist, close and fail
- fclose(fp);
- return (0);
- }
- else if (fp != 0 && fclose(fp) != 0)
- return (0); // can't close after test open
-// should open with protection here, if other than default
- else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
- return (0); // open failed
-
- if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
- return (fp); // no need to seek to end, or seek succeeded
-
- fclose(fp); // can't position at end
- return (0);
-}
-
-#endif /* LL_WINDOWS */
-
-
-#if LL_WINDOWS
/************** input file stream ********************************/
llifstream::llifstream() {}
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 1661cbeb55..04a2946ac4 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -41,8 +41,9 @@ typedef FILE LLFILE;
#include <sys/stat.h>
#if LL_WINDOWS
-// windows version of stat function and stat data structure are called _stat
-typedef struct _stat llstat;
+// The Windows version of stat function and stat data structure are called _stat64
+// We use _stat64 here to support 64-bit st_size and time_t values
+typedef struct _stat64 llstat;
#else
typedef struct stat llstat;
#include <sys/types.h>
@@ -56,35 +57,110 @@ typedef struct stat llstat;
# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
+// Windows C runtime library does not define this and does not support symlink detection in the
+// stat functions but we do in our getattr() function
+#ifndef S_IFLNK
+#define S_IFLNK 0xA000 /* symlink */
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#endif
+
#include "llstring.h" // safe char* -> std::string conversion
+/// LLFile is a class of static functions operating on paths
+/// All the functions with a path string input take UTF8 path/filenames
class LL_COMMON_API LLFile
{
public:
- // All these functions take UTF8 path/filenames.
- static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
- static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
+ /// open a file with the specified access mode
+ static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
+ ///< 'accessmode' follows the rules of the Posix fopen() mode parameter
+ /// "r" open the file for reading only and positions the stream at the beginning
+ /// "r+" open the file for reading and writing and positions the stream at the beginning
+ /// "w" open the file for reading and writing and truncate it to zero length
+ /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
+ /// "a" open the file for reading and writing and position the stream at the end of the file
+ /// "a+" open or create the file for reading and writing and position the stream at the end of the file
+ ///
+ /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
+ /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
+ /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
+ /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
+ /// This means that it is always a good idea to append "b" specifically for binary file access to
+ /// avoid corruption of the binary consistency of the data stream when reading or writing
+ /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
+ /// @returns a valid LLFILE* pointer on success or NULL on failure
static int close(LLFILE * file);
+ /// retrieve the content of a file into a string
static std::string getContents(const std::string& filename);
+ ///< @returns the content of the file or an empty string on failure
- // perms is a permissions mask like 0777 or 0700. In most cases it will
- // be overridden by the user's umask. It is ignored on Windows.
- // mkdir() considers "directory already exists" to be SUCCESS.
+ /// create a directory
static int mkdir(const std::string& filename, int perms = 0700);
+ ///< perms is a permissions mask like 0777 or 0700. In most cases it will be
+ /// overridden by the user's umask. It is ignored on Windows.
+ /// mkdir() considers "directory already exists" to be not an error.
+ /// @returns 0 on success and -1 on failure.
+
+ //// remove a directory
+ static int rmdir(const std::string& filename, int suppress_error = 0);
+ ///< pass ENOENT in the optional 'suppress_error' parameter
+ /// if you don't want a warning in the log when the directory does not exist
+ /// @returns 0 on success and -1 on failure.
+
+ /// remove a file or directory
+ static int remove(const std::string& filename, int suppress_error = 0);
+ ///< pass ENOENT in the optional 'suppress_error' parameter
+ /// if you don't want a warning in the log when the directory does not exist
+ /// @returns 0 on success and -1 on failure.
- static int rmdir(const std::string& filename);
- static int remove(const std::string& filename, int supress_error = 0);
- static int rename(const std::string& filename,const std::string& newname, int supress_error = 0);
+ /// rename a file
+ static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
+ ///< it will silently overwrite newname if it exists without returning an error
+ /// Posix guarantees that if newname already exists, then there will be no moment
+ /// in which for other processes newname does not exist. There is no such guarantee
+ /// under Windows at this time. It may do it in the same way but the used Windows API
+ /// does not make such guarantees.
+ /// @returns 0 on success and -1 on failure.
+
+
+ /// copy the contents of file from 'from' to 'to' filename
static bool copy(const std::string& from, const std::string& to);
+ ///< @returns true on success and false on failure.
+
+ /// return the file stat structure for filename
+ static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
+ ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
+ /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
+ /// is used to detect if a file exists
+ /// @returns 0 on success and -1 on failure.
+
+ /// get the file or directory attributes for filename
+ static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
+ ///< a more lightweight function on Windows to stat, that just returns the file attribute flags
+ /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
+ /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
+ /// warnings when the file or directory does not exist
+ /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
+ /// together with the three access bits which under Windows only the write bit is relevant.
+
+ /// check if filename is an existing directory
+ static bool isdir(const std::string& filename);
+ ///< @returns true if the path is for an existing directory
+
+ /// check if filename is an existing file
+ static bool isfile(const std::string& filename);
+ ///< @returns true if the path is for an existing file
- static int stat(const std::string& filename,llstat* file_status);
- static bool isdir(const std::string& filename);
- static bool isfile(const std::string& filename);
- static LLFILE * _Fiopen(const std::string& filename,
- std::ios::openmode mode);
+ /// check if filename is a symlink
+ static bool islink(const std::string& filename);
+ ///< @returns true if the path is pointing at a symlink
+ /// return a path to the temporary directory on the system
static const char * tmpdir();
};
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 21b11c5311..270ca40086 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -1348,10 +1348,6 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
} while(gzeof(src) == 0);
fclose(dst);
dst = NULL;
-#if LL_WINDOWS
- // Rename in windows needs the dstfile to not exist.
- LLFile::remove(dstfile, ENOENT);
-#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:
@@ -1399,10 +1395,6 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
gzclose(dst);
dst = NULL;
-#if LL_WINDOWS
- // Rename in windows needs the dstfile to not exist.
- LLFile::remove(dstfile);
-#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 13422612d6..9eaf5bf3e3 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -126,6 +126,8 @@ void waitfor(LLProcess& proc, int timeout=60)
{
yield();
}
+ // Pump once more after the process exits to flush any final events such as EOF.
+ yield(0);
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
tut::ensure(msg, i < timeout);
}
@@ -137,6 +139,8 @@ void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
{
yield();
}
+ // Pump once more after the process exits to flush any final events such as EOF.
+ yield(0);
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
tut::ensure(msg, i < timeout);
}
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
index b1b698375a..d5c585b45e 100644
--- a/indra/llcrashlogger/llcrashlogger.cpp
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -576,10 +576,6 @@ bool LLCrashLogger::init()
std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
-#if LL_WINDOWS
- LLAPRFile::remove(old_log_file);
-#endif
-
LLFile::rename(log_file.c_str(), old_log_file.c_str());
// Set the log file to crashreport.log
diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp
index 5ce5244107..541266af4f 100644
--- a/indra/llfilesystem/llfilesystem.cpp
+++ b/indra/llfilesystem/llfilesystem.cpp
@@ -103,9 +103,6 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp
const std::string old_filename = LLDiskCache::metaDataToFilepath(old_file_id, old_file_type);
const std::string new_filename = LLDiskCache::metaDataToFilepath(new_file_id, new_file_type);
- // Rename needs the new file to not exist.
- LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT);
-
if (LLFile::rename(old_filename, new_filename) != 0)
{
// We would like to return false here indicating the operation
diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp
index 64f660d0ce..4980e0a1da 100644
--- a/indra/llmessage/llcachename.cpp
+++ b/indra/llmessage/llcachename.cpp
@@ -780,6 +780,7 @@ void LLCacheName::Impl::processPendingAsks()
void LLCacheName::Impl::processPendingReplies()
{
// First call all the callbacks, because they might send messages.
+ // Todo: needs cleanup logic, otherwise invalid ids might stay here indefinitely
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
{
PendingReply* reply = *it;
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index afee099697..02c7d9507d 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -983,6 +983,7 @@ void LLPluginProcessParent::poll(F64 timeout)
}
// Remove instances in the done state from the sInstances map.
+ LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator itClean = sInstances.begin();
while (itClean != sInstances.end())
{
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
index 6d907d7e45..9eefba1390 100644
--- a/indra/llui/llviewereventrecorder.cpp
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -34,8 +34,6 @@ LLViewerEventRecorder::LLViewerEventRecorder() {
logEvents = false;
// Remove any previous event log file
std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
- LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT);
-
mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT);
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index b9f126e511..699e6b94a8 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -582,6 +582,13 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)
deployDevices();
}
+void LLWebRTCImpl::setDevices(const std::string& capture_id, const std::string& render_id)
+{
+ mRecordingDevice = capture_id;
+ mPlayoutDevice = render_id;
+ deployDevices();
+}
+
// updateDevices needs to happen on the worker thread.
void LLWebRTCImpl::updateDevices()
{
diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h
index 7d06b7d2b4..69dec8b2ce 100644
--- a/indra/llwebrtc/llwebrtc.h
+++ b/indra/llwebrtc/llwebrtc.h
@@ -150,6 +150,7 @@ class LLWebRTCDeviceInterface
// set the capture and render devices using the unique identifier for the device
virtual void setCaptureDevice(const std::string& id) = 0;
virtual void setRenderDevice(const std::string& id) = 0;
+ virtual void setDevices(const std::string& caprure_id, const std::string& render_id) = 0;
// Device observers for device change callbacks.
virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0;
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
index 01cfb17ced..5e0885fda1 100644
--- a/indra/llwebrtc/llwebrtc_impl.h
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -443,6 +443,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceO
void setCaptureDevice(const std::string& id) override;
void setRenderDevice(const std::string& id) override;
+ void setDevices(const std::string& caprure_id, const std::string& render_id) override;
void setTuningMode(bool enable) override;
float getTuningAudioLevel() override;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index eadc5e7e63..bfd8b1dcc7 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2398,7 +2398,6 @@ void LLAppViewer::initLoggingAndGetLastDuration()
if (gDirUtilp->fileExists(user_data_path_cef_log))
{
std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old");
- LLFile::remove(user_data_path_cef_old, ENOENT);
LLFile::rename(user_data_path_cef_log, user_data_path_cef_old);
}
}
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index 08a54b7369..fd9bbf00ae 100644
--- a/indra/newview/llfloateravatarpicker.cpp
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -34,6 +34,7 @@
#include "llfloaterreg.h"
#include "llimview.h" // for gIMMgr
#include "lltooldraganddrop.h" // for LLToolDragAndDrop
+#include "lltrans.h"
#include "llviewercontrol.h"
#include "llviewerregion.h" // getCapability()
#include "llworld.h"
@@ -405,15 +406,50 @@ bool LLFloaterAvatarPicker::visibleItemsSelected() const
}
/*static*/
-void LLFloaterAvatarPicker::findCoro(std::string url, LLUUID queryID, std::string name)
+void LLFloaterAvatarPicker::findByIdCoro(std::string url, LLUUID query_id, LLUUID agent_id, std::string floater_key)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy));
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("findByIdCoro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
- LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL;
+ httpOpts->setTimeout(AVATAR_PICKER_SEARCH_TIMEOUT);
+
+ LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts);
+
+ LL_DEBUGS("Agent") << result << LL_ENDL;
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (status || (status == LLCore::HttpStatus(HTTP_BAD_REQUEST)))
+ {
+ result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
+ }
+ else
+ {
+ result["failure_reason"] = status.toString();
+ }
+
+ LLFloaterAvatarPicker* floater =
+ LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker", floater_key);
+ if (floater)
+ {
+ floater->processResponse(query_id, result);
+ }
+}
+
+/*static*/
+void LLFloaterAvatarPicker::findByNameCoro(std::string url, LLUUID queryID, std::string name)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("findByNameCoro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+
+ LL_INFOS("HttpCoroutineAdapter", "genericPostCoro", "Agent") << "Generic POST for " << url << LL_ENDL;
httpOpts->setTimeout(AVATAR_PICKER_SEARCH_TIMEOUT);
@@ -447,6 +483,7 @@ void LLFloaterAvatarPicker::find()
std::string text = getChild<LLUICtrl>("Edit")->getValue().asString();
+ LLUUID agent_id;
size_t separator_index = text.find_first_of(" ._");
if (separator_index != text.npos)
{
@@ -458,51 +495,92 @@ void LLFloaterAvatarPicker::find()
text = first;
}
}
+ else if (!text.empty())
+ {
+ agent_id.set(text);
+ }
mQueryID.generate();
+ mNumResultsReturned = 0;
- std::string url;
- url.reserve(128); // avoid a memory allocation or two
+ getChild<LLScrollListCtrl>("SearchResults")->deleteAllItems();
+ getChild<LLScrollListCtrl>("SearchResults")->setCommentText(getString("searching"));
+ getChildView("ok_btn")->setEnabled(false);
- LLViewerRegion* region = gAgent.getRegion();
- if(region)
+ if (agent_id.notNull())
{
- url = region->getCapability("AvatarPickerSearch");
- // Prefer use of capabilities to search on both SLID and display name
- if (!url.empty())
+ // Search by uuid
+ // While cache could have been nicer, it neither has a failure callback, nor
+ // can cleanup in case of an invalid uuid. So we go directly to the capability.
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
{
- // capability urls don't end in '/', but we need one to parse
- // query parameters correctly
- if (url.size() > 0 && url[url.size()-1] != '/')
+ std::string url;
+ url.reserve(128);
+ url = region->getCapability("GetDisplayNames");
+ if (!url.empty())
{
- url += "/";
- }
- url += "?page_size=100&names=";
- std::replace(text.begin(), text.end(), '.', ' ');
- url += LLURI::escape(text);
- LL_INFOS() << "avatar picker " << url << LL_ENDL;
+ // capability urls don't end in '/', but we need one to parse
+ // query parameters correctly
+ if (url[url.size() - 1] != '/')
+ {
+ url += "/";
+ }
+ url += "?ids=";
+ url += agent_id.asString();
+ LL_DEBUGS("Agent") << "avatar picker " << url << LL_ENDL;
- LLCoros::instance().launch("LLFloaterAvatarPicker::findCoro",
- boost::bind(&LLFloaterAvatarPicker::findCoro, url, mQueryID, getKey().asString()));
+ LLCoros::instance().launch("LLFloaterAvatarPicker::findCoro",
+ boost::bind(&LLFloaterAvatarPicker::findByIdCoro, url, mQueryID, agent_id, getKey().asString()));
+ }
+ else
+ {
+ LLSD content;
+ content["failure_reason"] = LLTrans::getString("ServerUnavailable");
+ processResponse(mQueryID, content);
+ }
}
- else
+ }
+ else
+ {
+ std::string url;
+ url.reserve(128); // avoid a memory allocation or two
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
{
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("AvatarPickerRequest");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->addUUID("QueryID", mQueryID); // not used right now
- msg->nextBlock("Data");
- msg->addString("Name", text);
- gAgent.sendReliableMessage();
+ url = region->getCapability("AvatarPickerSearch");
+ // Prefer use of capabilities to search on both SLID and display name
+ if (!url.empty())
+ {
+ // capability urls don't end in '/', but we need one to parse
+ // query parameters correctly
+ if (url.size() > 0 && url[url.size() - 1] != '/')
+ {
+ url += "/";
+ }
+ url += "?page_size=100&names=";
+ std::replace(text.begin(), text.end(), '.', ' ');
+ url += LLURI::escape(text);
+ LL_DEBUGS("Agent") << "avatar picker " << url << LL_ENDL;
+
+ LLCoros::instance().launch("LLFloaterAvatarPicker::findCoro",
+ boost::bind(&LLFloaterAvatarPicker::findByNameCoro, url, mQueryID, getKey().asString()));
+ }
+ else
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("AvatarPickerRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("QueryID", mQueryID); // not used right now
+ msg->nextBlock("Data");
+ msg->addString("Name", text);
+ gAgent.sendReliableMessage();
+ }
}
}
- getChild<LLScrollListCtrl>("SearchResults")->deleteAllItems();
- getChild<LLScrollListCtrl>("SearchResults")->setCommentText(getString("searching"));
-
- getChildView("ok_btn")->setEnabled(false);
- mNumResultsReturned = 0;
}
void LLFloaterAvatarPicker::setAllowMultiple(bool allow_multiple)
diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h
index 330f1a1226..817427aae6 100644
--- a/indra/newview/llfloateravatarpicker.h
+++ b/indra/newview/llfloateravatarpicker.h
@@ -86,7 +86,8 @@ private:
void populateFriend();
bool visibleItemsSelected() const; // Returns true if any items in the current tab are selected.
- static void findCoro(std::string url, LLUUID mQueryID, std::string mName);
+ static void findByIdCoro(std::string url, LLUUID query_id, LLUUID agent_id, std::string floater_key);
+ static void findByNameCoro(std::string url, LLUUID mQueryID, std::string mName);
void find();
void setAllowMultiple(bool allow_multiple);
LLScrollListCtrl* getActiveList();
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 2b9fb9dd37..c25fbdcc51 100644
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -1721,6 +1721,11 @@ void LLFloaterIMContainer::showConversation(const LLUUID& session_id)
if (session_floater)
{
session_floater->restoreFloater();
+ if (session_floater->isTornOff() && session_floater->isMinimized())
+ {
+ session_floater->setMinimized(false);
+ session_floater->setFocus(true);
+ }
}
}
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index be4b449e2a..4762fc555d 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -2073,7 +2073,6 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
objectp->clearTEWaterExclusion(te);
// Blank out most override data on the object and send to server
- objectp->setRenderMaterialID(te, asset_id);
if (should_preserve_transforms && preserved_override)
{
// Apply material with preserved transforms
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index d0c0bdb5ce..f78ff2226c 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1537,7 +1537,7 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
}
else
{
- hit_obj->setRenderMaterialID(te, asset_id, false, true);
+ hit_obj->setRenderMaterialID(te, asset_id);
}
}
}
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 6d29038a23..8792ae3285 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -1014,7 +1014,6 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
std::string old_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SLVoice.old");
if (gDirUtilp->fileExists(new_log))
{
- LLFile::remove(old_log, ENOENT);
LLFile::rename(new_log, old_log);
}
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index e6fcc7bd6b..93f8f0d14a 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -396,7 +396,7 @@ void LLWebRTCVoiceClient::updateSettings()
config.mNoiseSuppressionLevel = noiseSuppressionLevel;
audioConfigChanged = true;
}
- if (audioConfigChanged)
+ if (audioConfigChanged && mWebRTCDeviceInterface)
{
mWebRTCDeviceInterface->setAudioConfig(config);
}
@@ -699,6 +699,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name)
{
if (mWebRTCDeviceInterface)
{
+ LL_DEBUGS("Voice") << "new capture device is " << name << LL_ENDL;
mWebRTCDeviceInterface->setCaptureDevice(name);
}
}
@@ -727,10 +728,15 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
return;
}
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
+
+ LL_DEBUGS("Voice") << "Reiniting " << LL_ENDL;
+
std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
LL_DEBUGS("Voice") << "Setting devices to-input: '" << inputDevice << "' output: '" << outputDevice << "'" << LL_ENDL;
+ bool update_render = false;
+ bool update_capture = false;
// only set the render device if the device list has changed.
if (mRenderDevices.size() != render_devices.size() || !std::equal(mRenderDevices.begin(),
@@ -744,7 +750,7 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
{
addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID));
}
- setRenderDevice(outputDevice);
+ update_render = true;
}
// only set the capture device if the device list has changed.
@@ -761,6 +767,22 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID));
}
+ update_capture = true;
+ }
+
+ if (update_render && update_capture)
+ {
+ // Do both in one go to avoid multiple deployDevices calls.
+ // And to avoid situation where workerDeployDevices has an
+ // obsolete device id
+ setDevices(inputDevice, outputDevice);
+ }
+ else if (update_render)
+ {
+ setRenderDevice(outputDevice);
+ }
+ else if (update_capture)
+ {
setCaptureDevice(inputDevice);
}
@@ -789,15 +811,28 @@ void LLWebRTCVoiceClient::setRenderDevice(const std::string& name)
{
if (mWebRTCDeviceInterface)
{
+ LL_DEBUGS("Voice") << "new render device is " << name << LL_ENDL;
mWebRTCDeviceInterface->setRenderDevice(name);
}
}
+void LLWebRTCVoiceClient::setDevices(const std::string& capture_name, const std::string& render_name)
+{
+ if (mWebRTCDeviceInterface)
+ {
+ LL_DEBUGS("Voice") << "new capture device: " << capture_name << " New render device: " << render_name << LL_ENDL;
+ mWebRTCDeviceInterface->setDevices(capture_name, render_name);
+ }
+}
+
void LLWebRTCVoiceClient::tuningStart()
{
if (!mIsInTuningMode)
{
- mWebRTCDeviceInterface->setTuningMode(true);
+ if (mWebRTCDeviceInterface)
+ {
+ mWebRTCDeviceInterface->setTuningMode(true);
+ }
mIsInTuningMode = true;
}
}
@@ -806,7 +841,10 @@ void LLWebRTCVoiceClient::tuningStop()
{
if (mIsInTuningMode)
{
- mWebRTCDeviceInterface->setTuningMode(false);
+ if (mWebRTCDeviceInterface)
+ {
+ mWebRTCDeviceInterface->setTuningMode(false);
+ }
mIsInTuningMode = false;
}
}
@@ -839,6 +877,10 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
float LLWebRTCVoiceClient::tuningGetEnergy(void)
{
+ if (!mWebRTCDeviceInterface)
+ {
+ return 0.f;
+ }
float rms = mWebRTCDeviceInterface->getTuningAudioLevel();
return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms;
}
@@ -866,7 +908,10 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)
clearCaptureDevices();
clearRenderDevices();
}
- mWebRTCDeviceInterface->refreshDevices();
+ if (mWebRTCDeviceInterface)
+ {
+ mWebRTCDeviceInterface->refreshDevices();
+ }
}
@@ -1174,7 +1219,7 @@ void LLWebRTCVoiceClient::sendPositionUpdate(bool force)
void LLWebRTCVoiceClient::updateOwnVolume()
{
F32 audio_level = 0.0f;
- if (!mMuteMic)
+ if (!mMuteMic && mWebRTCDeviceInterface)
{
float rms = mWebRTCDeviceInterface->getPeerConnectionAudioLevel();
audio_level = LEVEL_START_POINT - LEVEL_SCALE * rms;
diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h
index 2ce575852a..54d8e54ce2 100644
--- a/indra/newview/llvoicewebrtc.h
+++ b/indra/newview/llvoicewebrtc.h
@@ -124,6 +124,7 @@ public:
void setCaptureDevice(const std::string& name) override;
void setRenderDevice(const std::string& name) override;
+ void setDevices(const std::string& capture_name, const std::string& render_name);
LLVoiceDeviceList& getCaptureDevices() override;
LLVoiceDeviceList& getRenderDevices() override;
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 99c7d7b7d4..8e0eea97d1 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -327,6 +327,7 @@ are allowed.
<!-- searching - generic -->
<string name="Searching">Searching...</string>
<string name="NoneFound">None found.</string>
+ <string name="ServerUnavailable">Service not available.</string>
<!-- Indicates that an avatar name or other similar datum is being retrieved. General usage. -->
<string name="RetrievingData">Retrieving...</string>