From 53041adc3d0c257ea44f23071f2ba383c1639acc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:32:36 -0700 Subject: Bump pre-commit/action from 3.0.0 to 3.0.1 (#4864) Bumps [pre-commit/action](https://github.com/pre-commit/action) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/pre-commit/action/releases) - [Commits](https://github.com/pre-commit/action/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: pre-commit/action dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pre-commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index d626eef38d..9391677c15 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -15,4 +15,4 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.x - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 -- cgit v1.3 From 6f1df1a8d2db537b4962a8ef1118119db849b336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:32:50 -0700 Subject: Bump actions/checkout from 4 to 5 (#4867) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 6 +++--- .github/workflows/pre-commit.yaml | 2 +- 2 files changed, 4 insertions(+), 4 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 9391677c15..726e1cd889 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -11,7 +11,7 @@ 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 -- cgit v1.3 From e12958161c6f4edec1300db2045bba97aff26048 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Wed, 15 Oct 2025 01:00:42 +0200 Subject: Improve LLFile to be consistent between Windows and Linux/Mac --- indra/llcommon/llfile.cpp | 352 ++++++++++++++++++++++------------ indra/llcommon/llfile.h | 90 +++++++-- indra/llcommon/llsys.cpp | 8 - indra/llcrashlogger/llcrashlogger.cpp | 4 - indra/llfilesystem/llfilesystem.cpp | 3 - indra/llui/llviewereventrecorder.cpp | 2 - indra/newview/llvoicevivox.cpp | 1 - 7 files changed, 302 insertions(+), 158 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 53659ac13f..a24524d699 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -35,7 +35,6 @@ #if LL_WINDOWS #include "llwin32headers.h" -#include // Windows errno #include #else #include @@ -49,6 +48,86 @@ static std::string empty; // variants of strerror() to report errors. #if LL_WINDOWS +namespace +{ + struct errentry + { + unsigned long oserr; // OS return value + int errcode; // System V error 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 +}; + +// Number of elements in the error translation table +#define ERRTABLECOUNT (sizeof(errtable) / sizeof(errtable[0])) + +static int set_errno_from_oserror(unsigned long oserr) +{ + if (!oserr) + return 0; + + // Check the table for the OS error code + for (unsigned i{0}; i < ERRTABLECOUNT; ++i) + { + if (oserr == errtable[i].oserr) + { + _set_errno(errtable[i].errcode); + return -1; + } + } + + _set_errno(EINVAL); + return -1; +} + // On Windows, use strerror_s(). std::string strerr(int errn) { @@ -57,7 +136,61 @@ std::string strerr(int errn) return buffer; } -typedef std::basic_ios > _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(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(utf8path); +} + +static unsigned short get_fileattr(const std::wstring& utf16path) +{ + unsigned short st_mode = 0; + HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (file_handle == INVALID_HANDLE_VALUE) + { + set_errno_from_oserror(GetLastError()); + return 0; + } + + 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]))); + st_mode |= is_directory ? S_IFDIR : 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; + } + else + { + // Retrieve last error before calling CloseHandle() + set_errno_from_oserror(GetLastError()); + } + CloseHandle(file_handle); + return st_mode; +} #else // On Posix we want to call strerror_r(), but alarmingly, there are two @@ -108,18 +241,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) +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 +304,58 @@ 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(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 supress_error) { #if LL_WINDOWS - // permissions are ignored on Windows - std::wstring utf16dirname = ll_convert(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, supress_error); } // static LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); + std::wstring utf16filename = utf8path_to_wstring(filename); std::wstring utf16mode = ll_convert(std::string(mode)); - return _wfopen(utf16filename.c_str(),utf16mode.c_str()); + 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) -{ -#if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - std::wstring utf16mode = ll_convert(std::string(mode)); - return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag); -#else - llassert(0);//No corresponding function on non-windows - return NULL; -#endif -} - int LLFile::close(LLFILE * file) { int ret_value = 0; @@ -264,8 +388,27 @@ std::string LLFile::getContents(const std::string& filename) int LLFile::remove(const std::string& filename, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(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 = 0; + ; + 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 + { + rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER); + } #else int rc = ::remove(filename.c_str()); #endif @@ -275,28 +418,38 @@ int LLFile::remove(const std::string& filename, int supress_error) int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - std::wstring utf16newname = ll_convert(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); } +// Make this a define rather than using magic numbers multiple times in the code +#define LLFILE_COPY_BUFFER_SIZE 16384 + bool LLFile::copy(const std::string& from, const std::string& to) { bool copied = false; - LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */ + LLFILE* in = fopen(from, "rb"); /* Flawfinder: ignore */ if (in) { - LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */ + LLFILE* out = fopen(to, "wb"); /* Flawfinder: ignore */ if (out) { - char buf[16384]; /* Flawfinder: ignore */ + char buf[LLFILE_COPY_BUFFER_SIZE]; /* Flawfinder: ignore */ 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))) /* Flawfinder: ignore */ { if (fwrite(buf, 1, readbytes, out) != readbytes) { @@ -315,31 +468,51 @@ bool LLFile::copy(const std::string& from, const std::string& to) return copied; } -int LLFile::stat(const std::string& filename, llstat* filestatus) +int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(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, supress_error); } -bool LLFile::isdir(const std::string& filename) +// _wstat() is a bit heavyweight on Windows, use a more lightweight API +// to just get the attributes +unsigned short LLFile::getattr(const std::string& filename, int suppress_error) { - llstat st; +#if LL_WINDOWS + int rc = -1; + std::wstring utf16filename = utf8path_to_wstring(filename); + unsigned short st_mode = get_fileattr(utf16filename); + if (st_mode) + { + return st_mode; + } +#else + llstat filestatus; + int rc = ::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); +bool LLFile::isdir(const std::string& filename) +{ + // Don't spam the log if the subject pathname doesn't exist. + return S_ISDIR(getattr(filename, ENOENT)); } bool LLFile::isfile(const std::string& filename) { - llstat st; - - return stat(filename, &st) == 0 && S_ISREG(st.st_mode); + // Don't spam the log if the subject pathname doesn't exist. + return S_ISREG(getattr(filename, ENOENT)); } const char *LLFile::tmpdir() @@ -368,75 +541,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..0bd6be7f94 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -41,8 +41,8 @@ typedef FILE LLFILE; #include #if LL_WINDOWS -// windows version of stat function and stat data structure are called _stat -typedef struct _stat llstat; +// windows version of stat function and stat data structure are called _statxx +typedef struct _stat64 llstat; #else typedef struct stat llstat; #include @@ -58,33 +58,89 @@ typedef struct stat llstat; #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); - - static int rmdir(const std::string& filename); + ///< 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 supress_error = 0); + ///< pass ENOENT in the optional 'supress_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 supress_error = 0); - static int rename(const std::string& filename,const std::string& newname, int supress_error = 0); - static bool copy(const std::string& from, const std::string& to); + ///< pass ENOENT in the optional 'supress_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 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); + /// rename a file, + static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); + ///< it will silently overwrite newname if it exists without returning an error + /// @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 supress_error = ENOENT); + ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the + /// optional 'supress_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, int supress_error = 0); + ///< a more lightweight function on Windows to stat, that just returns the file attribute flags + /// pass ENOENT in the optional 'supress_error' parameter if you don't want a warning + /// in the log 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 + + /// 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/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/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/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); } -- cgit v1.3 From 49a850ccc14de253f9645427594f25c74931e4c5 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Wed, 15 Oct 2025 20:00:27 +0200 Subject: Remove a trailing space and a missed LLFile::remove() call right before the LLFile::rename() --- indra/llcommon/llfile.h | 2 +- indra/newview/llappviewer.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 0bd6be7f94..9d9c3cd08b 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -107,7 +107,7 @@ public: /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. - /// rename a file, + /// rename a file static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); ///< it will silently overwrite newname if it exists without returning an error /// @returns 0 on success and -1 on failure. 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); } } -- cgit v1.3 From 2adf1bbdcb64a86614cbe565636fe9203eaa7994 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Thu, 16 Oct 2025 22:56:21 +0200 Subject: Remove trailing space --- indra/llcommon/llfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 9d9c3cd08b..9747bb67d8 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -135,7 +135,7 @@ public: /// 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 -- cgit v1.3 From f58a7c767354705962788864274c7a60ed89544e Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sat, 18 Oct 2025 19:13:19 +0200 Subject: Make changes according to recommendations by Copilot - correction spelling of suppress_error - improved error handling in remove() based on functionality in get_fileattr() and somewhat changed error handling in get_fileattr() itself - call explicitly LLFile::fopen() to make sure we use the correct file path conversion under Windows Removing Flawfinder comments since Flawfinder isn't used in the viewer anymore Adding an option to support symlink detection in getattr() Adding comments to function implementation to indicate that they are really static functions of the LLFile class --- indra/llcommon/llfile.cpp | 154 +++++++++++++++++++++++++++------------------- indra/llcommon/llfile.h | 46 ++++++++++---- 2 files changed, 123 insertions(+), 77 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index a24524d699..492c7c690b 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -48,15 +48,18 @@ 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; // OS return value + 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 @@ -106,20 +109,17 @@ static errentry const errtable[] { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816 }; -// Number of elements in the error translation table -#define ERRTABLECOUNT (sizeof(errtable) / sizeof(errtable[0])) - static int set_errno_from_oserror(unsigned long oserr) { if (!oserr) return 0; - // Check the table for the OS error code - for (unsigned i{0}; i < ERRTABLECOUNT; ++i) + // Check the table for the Windows OS error code + for (const struct errentry &entry : errtable) { - if (oserr == errtable[i].oserr) + if (oserr == entry.oserr) { - _set_errno(errtable[i].errcode); + _set_errno(entry.errcode); return -1; } } @@ -156,40 +156,46 @@ static std::wstring utf8path_to_wstring(const std::string& utf8path) return ll_convert(utf8path); } -static unsigned short get_fileattr(const std::wstring& utf16path) +static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false) { - unsigned short st_mode = 0; - HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - if (file_handle == INVALID_HANDLE_VALUE) + unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; + if (dontFollowSymLink) { - set_errno_from_oserror(GetLastError()); - return 0; + flags |= FILE_FLAG_OPEN_REPARSE_POINT; } - - FILE_ATTRIBUTE_TAG_INFO attribute_info; - if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) + 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) { - // 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]))); - st_mode |= is_directory ? S_IFDIR : 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; + 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; + } } - else + // Retrieve last error and set errno before calling CloseHandle() + set_errno_from_oserror(GetLastError()); + + if (file_handle != INVALID_HANDLE_VALUE) { - // Retrieve last error before calling CloseHandle() - set_errno_from_oserror(GetLastError()); + CloseHandle(file_handle); } - CloseHandle(file_handle); - return st_mode; + return 0; } #else @@ -241,7 +247,7 @@ std::string strerr(int errn) } #endif // ! LL_WINDOWS -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) { @@ -333,7 +339,7 @@ int LLFile::mkdir(const std::string& dirname, int perms) } // static -int LLFile::rmdir(const std::string& dirname, int supress_error) +int LLFile::rmdir(const std::string& dirname, int suppress_error) { #if LL_WINDOWS std::wstring utf16dirname = utf8path_to_wstring(dirname); @@ -341,21 +347,22 @@ int LLFile::rmdir(const std::string& dirname, int supress_error) #else int rc = ::rmdir(dirname.c_str()); #endif - return warnif("rmdir", dirname, rc, supress_error); + return warnif("rmdir", dirname, rc, suppress_error); } // static -LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ +LLFILE* LLFile::fopen(const std::string& filename, const char* mode) { #if LL_WINDOWS std::wstring utf16filename = utf8path_to_wstring(filename); std::wstring utf16mode = ll_convert(std::string(mode)); return _wfopen(utf16filename.c_str(), utf16mode.c_str()); #else - return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */ + return ::fopen(filename.c_str(),mode); #endif } +// static int LLFile::close(LLFILE * file) { int ret_value = 0; @@ -366,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); @@ -385,7 +393,8 @@ 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 // Posix remove() works on both files and directories although on Windows @@ -393,8 +402,7 @@ int LLFile::remove(const std::string& filename, int supress_error) // 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 = 0; - ; + int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); unsigned short st_mode = get_fileattr(utf16filename); if (S_ISDIR(st_mode)) @@ -405,17 +413,25 @@ int LLFile::remove(const std::string& filename, int supress_error) { rc = _wunlink(utf16filename.c_str()); } - else + 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_filattr() 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 // Posix rename() will gladly overwrite a file at newname if it exists, the Windows @@ -431,25 +447,26 @@ int LLFile::rename(const std::string& filename, const std::string& newname, int #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 = fopen(from, "rb"); /* Flawfinder: ignore */ + LLFILE* in = LLFile::fopen(from, "rb"); if (in) { - LLFILE* out = fopen(to, "wb"); /* Flawfinder: ignore */ + LLFILE* out = LLFile::fopen(to, "wb"); if (out) { - char buf[LLFILE_COPY_BUFFER_SIZE]; /* Flawfinder: ignore */ + char buf[LLFILE_COPY_BUFFER_SIZE]; size_t readbytes; bool write_ok = true; - while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) /* Flawfinder: ignore */ + while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) { if (fwrite(buf, 1, readbytes, out) != readbytes) { @@ -468,7 +485,8 @@ bool LLFile::copy(const std::string& from, const std::string& to) return copied; } -int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_error) +// static +int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error) { #if LL_WINDOWS std::wstring utf16filename = utf8path_to_wstring(filename); @@ -476,24 +494,25 @@ int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_er #else int rc = ::stat(filename.c_str(), filestatus); #endif - return warnif("stat", filename, rc, supress_error); + return warnif("stat", filename, rc, suppress_error); } -// _wstat() is a bit heavyweight on Windows, use a more lightweight API -// to just get the attributes -unsigned short LLFile::getattr(const std::string& filename, int suppress_error) +// static +unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error) { #if LL_WINDOWS - int rc = -1; + // _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); + unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); if (st_mode) { return st_mode; } #else llstat filestatus; - int rc = ::stat(filename.c_str(), &filestatus); + int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus); if (rc == 0) { return filestatus.st_mode; @@ -503,18 +522,25 @@ unsigned short LLFile::getattr(const std::string& filename, int suppress_error) return 0; } +// static bool LLFile::isdir(const std::string& filename) { - // Don't spam the log if the subject pathname doesn't exist. - return S_ISDIR(getattr(filename, ENOENT)); + return S_ISDIR(getattr(filename)); } +// static bool LLFile::isfile(const std::string& filename) { - // Don't spam the log if the subject pathname doesn't exist. - return S_ISREG(getattr(filename, ENOENT)); + return S_ISREG(getattr(filename)); +} + +// static +bool LLFile::islink(const std::string& filename) +{ + return S_ISLNK(getattr(filename, true)); } +// static const char *LLFile::tmpdir() { static std::string utf8path; diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 9747bb67d8..04a2946ac4 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -41,8 +41,9 @@ typedef FILE LLFILE; #include #if LL_WINDOWS -// windows version of stat function and stat data structure are called _statxx -typedef struct _stat64 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 @@ -56,6 +57,16 @@ 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 @@ -96,39 +107,44 @@ public: /// @returns 0 on success and -1 on failure. //// remove a directory - static int rmdir(const std::string& filename, int supress_error = 0); - ///< pass ENOENT in the optional 'supress_error' parameter + 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 supress_error = 0); - ///< pass ENOENT in the optional 'supress_error' parameter + 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. /// rename a file - static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); + 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 + /// 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 supress_error = ENOENT); + 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 'supress_error' parameter to avoid spamming the log with warnings when the API + /// 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, int supress_error = 0); + 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 - /// pass ENOENT in the optional 'supress_error' parameter if you don't want a warning - /// in the log when the file or directory does not exist + /// 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. @@ -140,6 +156,10 @@ public: static bool isfile(const std::string& filename); ///< @returns true if the path is for an existing file + /// 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(); }; -- cgit v1.3 From aa49a3d7e6da7b2b83d78759c65d21f9865ddea5 Mon Sep 17 00:00:00 2001 From: fmartian Date: Mon, 20 Oct 2025 22:23:59 +0200 Subject: Fix typo in comment --- indra/llcommon/llfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 492c7c690b..a539e4fe28 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -422,7 +422,7 @@ int LLFile::remove(const std::string& filename, int suppress_error) } else { - // get_filattr() failed and already set errno, preserve it for correct error reporting + // get_fileattr() failed and already set errno, preserve it for correct error reporting } #else int rc = ::remove(filename.c_str()); @@ -503,7 +503,7 @@ unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLi #if LL_WINDOWS // _wstat64() is a bit heavyweight on Windows, use a more lightweight API // to just get the attributes - int rc = -1; + int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); if (st_mode) -- cgit v1.3 From 528c4b5f312a68056073c210c415c76d4eab4091 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:54:00 +0300 Subject: #2427 Show torn off chat when clicking on a message popup --- indra/newview/llfloaterimcontainer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index d821d9a4a5..a0f2dbe197 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1698,6 +1698,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); + } } } -- cgit v1.3 From 9a2ff1657d380c19ecb491bee1598e9ba037ef56 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:30:14 +0300 Subject: #4871 Fix webrtc crash on tuningGetEnergy --- indra/newview/llvoicewebrtc.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e6fcc7bd6b..fba7098906 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); } @@ -797,7 +797,10 @@ void LLWebRTCVoiceClient::tuningStart() { if (!mIsInTuningMode) { - mWebRTCDeviceInterface->setTuningMode(true); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->setTuningMode(true); + } mIsInTuningMode = true; } } @@ -806,7 +809,10 @@ void LLWebRTCVoiceClient::tuningStop() { if (mIsInTuningMode) { - mWebRTCDeviceInterface->setTuningMode(false); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->setTuningMode(false); + } mIsInTuningMode = false; } } @@ -839,6 +845,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 +876,10 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) clearCaptureDevices(); clearRenderDevices(); } - mWebRTCDeviceInterface->refreshDevices(); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->refreshDevices(); + } } @@ -1174,7 +1187,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; -- cgit v1.3 From 0d5138c1f93a77ff52551a76d0d8575413550859 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:34:23 +0300 Subject: #4839 Crash at LLPluginProcessParent::poll Missed lock --- indra/llplugin/llpluginprocessparent.cpp | 1 + 1 file changed, 1 insertion(+) 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()) { -- cgit v1.3 From b4090c24d25c202eb68be8bcd129d524d155e739 Mon Sep 17 00:00:00 2001 From: Andrey Lihatskiy <118752495+marchcat@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:09:34 +0300 Subject: Ensure final EOF in LLProcess test (#4877) --- indra/llcommon/tests/llprocess_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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); } -- cgit v1.3 From ab3106d58e2abe9fc3a9e74ac20ada9ca46a7f46 Mon Sep 17 00:00:00 2001 From: Hecklezz Date: Wed, 22 Oct 2025 19:27:51 +1000 Subject: Updated APR library and removed hotfix patch in APR cmake --- autobuild.xml | 14 +++++++------- indra/cmake/APR.cmake | 5 ----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 2983307cbe..dbbe1981b6 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -46,11 +46,11 @@ archive hash - a21487f4e3a68721fd97edef117795a1b7212a77 + 3f8e58fe74e144b771858e89a5514bf354964c76 hash_algorithm sha1 url - https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-darwin64-12259255574.tar.zst + https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-darwin64-18696779749.tar.zst name darwin64 @@ -60,11 +60,11 @@ archive hash - 45baf82d3366734e542a2a3749f495b64f5513b4 + df36114eb8fb66fdcbb190011222a7d336791d98 hash_algorithm sha1 url - https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-linux64-12259255574.tar.zst + https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-linux64-18696779749.tar.zst name linux64 @@ -74,11 +74,11 @@ archive hash - bdd35d3b9580d3cdcb98afae639936aaa40e24c4 + 164705831819d247753c9fd608c2c1ac8dca1883 hash_algorithm sha1 url - https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r1/apr_suite-1.7.5-12259255574-windows64-12259255574.tar.zst + https://github.com/secondlife/3p-apr_suite/releases/download/v1.7.5-r2/apr_suite-1.7.5-18696779749-windows64-18696779749.tar.zst name windows64 @@ -91,7 +91,7 @@ copyright Copyright © 2012 The Apache Software Foundation, Licensed under the Apache License, Version 2.0. version - 1.7.5-12259255574 + 1.7.5-18696779749 name apr_suite description 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}") -- cgit v1.3 From 6a5d4e0e3de5448835b58177ce7a64119facbd28 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 22 Oct 2025 16:57:04 +0300 Subject: #4876 Fix material not applying 1. Dupplicate call in llselectmgr can cause race conditions 2. lltooldragndrop wasn't updating server in one case --- indra/newview/llselectmgr.cpp | 1 - indra/newview/lltooldraganddrop.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) 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); } } } -- cgit v1.3 From bb26aa3c2cb8ff961668cf0ad8180d3e9c57f941 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:19:52 +0300 Subject: #4652 Improve switching of webrtc devices --- indra/llwebrtc/llwebrtc.cpp | 7 +++++++ indra/llwebrtc/llwebrtc.h | 1 + indra/llwebrtc/llwebrtc_impl.h | 1 + indra/newview/llvoicewebrtc.cpp | 34 +++++++++++++++++++++++++++++++++- indra/newview/llvoicewebrtc.h | 1 + 5 files changed, 43 insertions(+), 1 deletion(-) 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/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index fba7098906..93f8f0d14a 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -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,10 +811,20 @@ 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) 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; -- cgit v1.3 From 0da96a508a5d8d384e017f707babd2193f6ca6a7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:43:38 +0300 Subject: p#494 Add search by agent ID to resident chooser --- indra/llmessage/llcachename.cpp | 1 + indra/newview/llfloateravatarpicker.cpp | 150 +++++++++++++++++++------ indra/newview/llfloateravatarpicker.h | 3 +- indra/newview/skins/default/xui/en/strings.xml | 1 + 4 files changed, 118 insertions(+), 37 deletions(-) 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/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("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("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("SearchResults")->deleteAllItems(); + getChild("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("SearchResults")->deleteAllItems(); - getChild("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/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... None found. + Service not available. Retrieving... -- cgit v1.3