diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sal/osl/w32 | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
37 files changed, 12997 insertions, 0 deletions
diff --git a/sal/osl/w32/backtrace.cxx b/sal/osl/w32/backtrace.cxx new file mode 100644 index 000000000..d9231de44 --- /dev/null +++ b/sal/osl/w32/backtrace.cxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <limits> +#include <memory> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <process.h> +#include <iostream> +#define OPTIONAL +#include <DbgHelp.h> + +#include <rtl/ustrbuf.hxx> +#include <sal/backtrace.hxx> + +#include <backtraceasstring.hxx> + +namespace { + +template<typename T> T clampToULONG(T n) { + auto const maxUlong = std::numeric_limits<ULONG>::max(); + return n > maxUlong ? static_cast<T>(maxUlong) : n; +} + +} + +OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) +{ + std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth ); + return sal::backtrace_to_string( backtrace.get()); +} + +std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth) +{ + assert(maxDepth != 0); + maxDepth = clampToULONG(maxDepth); + + auto pStack = new void *[maxDepth]; + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb204633.aspx + // "CaptureStackBackTrace function" claims that you "can capture up to + // MAXUSHORT frames", and on Windows Server 2003 and Windows XP it even + // "must be less than 63", but assume that a too large input value is + // clamped internally, instead of resulting in an error: + int nFrames = CaptureStackBackTrace( 0, static_cast<ULONG>(maxDepth), pStack, nullptr ); + + return std::unique_ptr<BacktraceState>(new BacktraceState{ pStack, nFrames }); +} + +OUString sal::backtrace_to_string(BacktraceState* backtraceState) +{ + HANDLE hProcess = GetCurrentProcess(); + // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize + // says to not initialize more than once. + [[maybe_unused]] static bool bInitialized = SymInitialize(hProcess, nullptr, false); + SymRefreshModuleList(hProcess); + SYMBOL_INFO * pSymbol; + pSymbol = static_cast<SYMBOL_INFO *>(calloc( sizeof( SYMBOL_INFO ) + 1024 * sizeof( char ), 1 )); + assert(pSymbol); + pSymbol->MaxNameLen = 1024 - 1; + pSymbol->SizeOfStruct = sizeof( SYMBOL_INFO ); + + auto nFrames = backtraceState->nDepth; + OUStringBuffer aBuf; + for( int i = 0; i < nFrames; i++ ) + { + SymFromAddr( hProcess, reinterpret_cast<DWORD64>(backtraceState->buffer[ i ]), nullptr, pSymbol ); + aBuf.append( static_cast<sal_Int32>(nFrames - i - 1) ); + aBuf.append( ": " ); + aBuf.appendAscii( pSymbol->Name ); + aBuf.append( " - 0x" ); + aBuf.append( static_cast<sal_Int64>(pSymbol->Address), 16 ); + aBuf.append( "\n" ); + } + + free( pSymbol ); + + return aBuf.makeStringAndClear(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/conditn.cxx b/sal/osl/w32/conditn.cxx new file mode 100644 index 000000000..6bc6d9d3e --- /dev/null +++ b/sal/osl/w32/conditn.cxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" + +#include <osl/conditn.h> +#include <osl/diagnose.h> +#include <osl/time.h> + +/* + under WIN32, we use the void* oslCondition + as a WIN32 HANDLE (which is also a 32-bit value) +*/ + +oslCondition SAL_CALL osl_createCondition(void) +{ + oslCondition Condition; + + Condition= reinterpret_cast<oslCondition>(CreateEventW(nullptr, /* no security */ + true, /* manual reset */ + false, /* initial state not signaled */ + nullptr)); /* automatic name */ + + return Condition; + +} + +void SAL_CALL osl_destroyCondition(oslCondition Condition) +{ + if(Condition) + OSL_VERIFY(CloseHandle(Condition)); +} + +sal_Bool SAL_CALL osl_setCondition(oslCondition Condition) +{ + OSL_ASSERT(Condition); + + return SetEvent(reinterpret_cast<HANDLE>(Condition)) != FALSE; +} + +sal_Bool SAL_CALL osl_resetCondition(oslCondition Condition) +{ + OSL_ASSERT(Condition); + + return ResetEvent(reinterpret_cast<HANDLE>(Condition)) != FALSE; +} + +oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition, + const TimeValue* pTimeout) +{ + DWORD timeout; + + OSL_ASSERT(Condition); + + if (pTimeout) + timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L; + else + timeout = INFINITE; + + /* It's necessary to process SendMessage calls to the current thread to give other threads + access to COM objects instantiated in this thread */ + + while ( true ) + { + /* Only wake up if a SendMessage call to the threads message loop is detected */ + switch( MsgWaitForMultipleObjects( 1, reinterpret_cast<HANDLE *>(&Condition), FALSE, timeout, QS_SENDMESSAGE ) ) + { + case WAIT_OBJECT_0 + 1: + { + MSG msg; + + /* We Must not dispatch the message. PM_NOREMOVE leaves the message queue untouched + but dispatches SendMessage calls automatically */ + + PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE ); + } + break; + + case WAIT_OBJECT_0: + return osl_cond_result_ok; + + case WAIT_TIMEOUT: + return osl_cond_result_timeout; + + default: + return osl_cond_result_error; + } + } +} + +sal_Bool SAL_CALL osl_checkCondition(oslCondition Condition) +{ + OSL_ASSERT(Condition); + + return WaitForSingleObject(reinterpret_cast<HANDLE>(Condition), 0) == WAIT_OBJECT_0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/dllentry.cxx b/sal/osl/w32/dllentry.cxx new file mode 100644 index 000000000..81139a058 --- /dev/null +++ b/sal/osl/w32/dllentry.cxx @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <systools/win32/uwinapi.h> +#include <tlhelp32.h> +#include <rpc.h> +#include <winsock.h> +#ifdef _DEBUG +#include <crtdbg.h> +#endif +#include <osl/diagnose.h> +#include <sal/types.h> +#include <float.h> + +#include <osl/mutex.h> +#include <osl/thread.h> + +#include "file_url.hxx" +#include <rtllifecycle.h> + +#include "thread.hxx" + +/* +This is needed because DllMain is called after static constructors. A DLL's +startup and shutdown sequence looks like this: + +_pRawDllMain() +_CRT_INIT() +DllMain() +... +DllMain() +_CRT_INIT() +_pRawDllMain() + +*/ + +extern "C" { + +static BOOL WINAPI RawDllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ); +BOOL (WINAPI *_pRawDllMain)(HINSTANCE, DWORD, LPVOID) = RawDllMain; + +} + +static BOOL WINAPI RawDllMain( HINSTANCE, DWORD fdwReason, LPVOID ) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { +#ifdef _DEBUG + WCHAR buf[64]; + DWORD const res = GetEnvironmentVariableW(L"SAL_NO_ASSERT_DIALOGS", buf, SAL_N_ELEMENTS(buf)); + if (res && res < SAL_N_ELEMENTS(buf)) + { + // disable the dialog on abort() + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ERROR, (_CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE)); + // not sure which assertions this affects + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, (_CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE)); + // disable the dialog on assert(false) + _set_error_mode(_OUT_TO_STDERR); + } +#endif + +#if OSL_DEBUG_LEVEL < 2 + /* Suppress file error messages from system like "Floppy A: not inserted" */ + SetErrorMode( SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS ); +#endif + + //We disable floating point exceptions. This is the usual state at program startup + //but on Windows 98 and ME this is not always the case. + _control87(_MCW_EM, _MCW_EM); + break; + } + + case DLL_PROCESS_DETACH: + WSACleanup( ); + + /* + + On a product build memory management finalization might + cause a crash without assertion (assertions off) if heap is + corrupted. But a crash report won't help here because at + this point all other threads have been terminated and only + ntdll is on the stack. No chance to find the reason for the + corrupted heap if so. + + So annoying the user with a crash report is completely useless. + + */ + +#ifndef DBG_UTIL + __try +#endif + { + /* cleanup locale hashtable */ + rtl_locale_fini(); + + /* finalize memory management */ + rtl_cache_fini(); + rtl_arena_fini(); + } +#ifndef DBG_UTIL + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } +#endif + break; + } + + return TRUE; +} + +static DWORD GetParentProcessId() +{ + DWORD dwParentProcessId = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + + if ( IsValidHandle( hSnapshot ) ) + { + PROCESSENTRY32 pe; + bool fSuccess; + + ZeroMemory( &pe, sizeof(pe) ); + pe.dwSize = sizeof(pe); + fSuccess = Process32First( hSnapshot, &pe ); + + while( fSuccess ) + { + if ( GetCurrentProcessId() == pe.th32ProcessID ) + { + dwParentProcessId = pe.th32ParentProcessID; + break; + } + + fSuccess = Process32Next( hSnapshot, &pe ); + } + + CloseHandle( hSnapshot ); + } + + return dwParentProcessId; +} + +static DWORD WINAPI ParentMonitorThreadProc( LPVOID lpParam ) +{ + DWORD_PTR dwParentProcessId = reinterpret_cast<DWORD_PTR>(lpParam); + + HANDLE hParentProcess = OpenProcess( SYNCHRONIZE, FALSE, dwParentProcessId ); + + osl_setThreadName("headless ParentMonitorThread"); + + if ( IsValidHandle( hParentProcess ) ) + { + if ( WAIT_OBJECT_0 == WaitForSingleObject( hParentProcess, INFINITE ) ) + { + TerminateProcess( GetCurrentProcess(), 0 ); + } + CloseHandle( hParentProcess ); + } + return 0; +} + +extern "C" +BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID ) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + WCHAR szBuffer[64]; + + // This code will attach the process to its parent process + // if the parent process had set the environment variable. + // The corresponding code (setting the environment variable) + // is desktop/win32/source/officeloader.cxx + + DWORD dwResult = GetEnvironmentVariableW( L"ATTACHED_PARENT_PROCESSID", szBuffer, SAL_N_ELEMENTS(szBuffer) ); + + if ( dwResult && dwResult < SAL_N_ELEMENTS(szBuffer) ) + { + DWORD dwThreadId = 0; + + DWORD_PTR dwParentProcessId = static_cast<DWORD_PTR>(_wtol( szBuffer )); + + if ( dwParentProcessId && GetParentProcessId() == dwParentProcessId ) + { + // No error check, it works or it does not + // Thread should only be started for headless mode, see desktop/win32/source/officeloader.cxx + HANDLE hThread + = CreateThread(nullptr, 0, ParentMonitorThreadProc, + reinterpret_cast<LPVOID>(dwParentProcessId), 0, &dwThreadId); + // Note: calling CreateThread in DllMain is discouraged + // but this is only done in the headless mode and in + // that case no other threads should be running at startup + // when sal3.dll is loaded; also there is no + // synchronization with the spawned thread, so there + // does not appear to be a real risk of deadlock here + if (hThread) + CloseHandle(hThread); + } + } + + return TRUE; + } + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + osl_callThreadKeyCallbackOnThreadDetach( ); + break; + } + + return TRUE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file-impl.hxx b/sal/osl/w32/file-impl.hxx new file mode 100644 index 000000000..3f1d2136d --- /dev/null +++ b/sal/osl/w32/file-impl.hxx @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SAL_OSL_W32_FILE_IMPL_HXX +#define INCLUDED_SAL_OSL_W32_FILE_IMPL_HXX + +#include <sal/config.h> + +#include <osl/file.h> +#include <sal/types.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +extern "C" oslFileHandle osl_createFileHandleFromOSHandle(HANDLE hFile, sal_uInt32 uFlags); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sal/osl/w32/file.cxx b/sal/osl/w32/file.cxx new file mode 100644 index 000000000..be921aede --- /dev/null +++ b/sal/osl/w32/file.cxx @@ -0,0 +1,1121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <systools/win32/uwinapi.h> + +#include <osl/file.hxx> +#include <rtl/alloc.h> +#include <rtl/byteseq.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/typed_flags_set.hxx> + +#include "file-impl.hxx" +#include "file_url.hxx" +#include "file_error.hxx" + +#include <atomic> +#include <cassert> +#include <algorithm> +#include <limits> + +#ifdef max /* conflict w/ std::numeric_limits<T>::max() */ +#undef max +#endif +#ifdef min +#undef min +#endif + +namespace { + +/** State + */ +enum class StateBits +{ + Seekable = 1, /*< open() sets, iff regular file */ + Readable = 2, /*< open() sets, read() requires */ + Writeable = 4, /*< open() sets, write() requires */ + Modified = 8 /* write() sets, flush() resets */ +}; + +} + +template<> struct o3tl::typed_flags<StateBits>: o3tl::is_typed_flags<StateBits, 0xF> {}; + +namespace { + +/** File handle implementation. +*/ +struct FileHandle_Impl +{ + CRITICAL_SECTION m_mutex; + HANDLE m_hFile; + + StateBits m_state; + + sal_uInt64 m_size; /*< file size */ + LONGLONG m_offset; /*< physical offset from begin of file */ + // m_filepos is hit hard in some situations, where the overhead of a mutex starts to show up, so use an atomic + std::atomic<LONGLONG> m_filepos; /*< logical offset from begin of file */ + + LONGLONG m_bufptr; /*< buffer offset from begin of file */ + SIZE_T m_buflen; /*< buffer filled [0, m_bufsiz - 1] */ + + SIZE_T m_bufsiz; + sal_uInt8 * m_buffer; + + explicit FileHandle_Impl (HANDLE hFile); + ~FileHandle_Impl(); + + static SIZE_T getpagesize(); + + sal_uInt64 getPos() const; + oslFileError setPos (sal_uInt64 uPos); + + sal_uInt64 getSize() const; + oslFileError setSize (sal_uInt64 uPos); + + oslFileError readAt( + LONGLONG nOffset, + void * pBuffer, + DWORD nBytesRequested, + sal_uInt64 * pBytesRead); + + oslFileError writeAt( + LONGLONG nOffset, + void const * pBuffer, + DWORD nBytesToWrite, + sal_uInt64 * pBytesWritten); + + oslFileError readFileAt( + LONGLONG nOffset, + void * pBuffer, + sal_uInt64 uBytesRequested, + sal_uInt64 * pBytesRead); + + oslFileError writeFileAt( + LONGLONG nOffset, + void const * pBuffer, + sal_uInt64 uBytesToWrite, + sal_uInt64 * pBytesWritten); + + oslFileError readLineAt( + LONGLONG nOffset, + sal_Sequence ** ppSequence, + sal_uInt64 * pBytesRead); + + static oslFileError writeSequence_Impl ( + sal_Sequence ** ppSequence, + SIZE_T * pnOffset, + const void * pBuffer, + SIZE_T nBytes); + + oslFileError syncFile(); + + /** Guard. + */ + class Guard + { + LPCRITICAL_SECTION m_mutex; + + public: + explicit Guard(LPCRITICAL_SECTION pMutex); + ~Guard(); + }; +}; + +} + +FileHandle_Impl::Guard::Guard(LPCRITICAL_SECTION pMutex) + : m_mutex (pMutex) +{ + assert(pMutex); + ::EnterCriticalSection (m_mutex); +} + +FileHandle_Impl::Guard::~Guard() +{ + ::LeaveCriticalSection (m_mutex); +} + +FileHandle_Impl::FileHandle_Impl(HANDLE hFile) + : m_hFile (hFile), + m_state (StateBits::Readable | StateBits::Writeable), + m_size (0), + m_offset (0), + m_filepos (0), + m_bufptr (-1), + m_buflen (0), + m_bufsiz (getpagesize()), + m_buffer (nullptr) +{ + ::InitializeCriticalSection (&m_mutex); + m_buffer = static_cast<sal_uInt8 *>(calloc(m_bufsiz, 1)); +} + +FileHandle_Impl::~FileHandle_Impl() +{ + free(m_buffer); + m_buffer = nullptr; + ::DeleteCriticalSection (&m_mutex); +} + +SIZE_T FileHandle_Impl::getpagesize() +{ + SYSTEM_INFO info; + ::GetSystemInfo(&info); + return sal::static_int_cast< SIZE_T >(info.dwPageSize); +} + +sal_uInt64 FileHandle_Impl::getPos() const +{ + return sal::static_int_cast< sal_uInt64 >(m_filepos.load()); +} + +oslFileError FileHandle_Impl::setPos(sal_uInt64 uPos) +{ + m_filepos = sal::static_int_cast< LONGLONG >(uPos); + return osl_File_E_None; +} + +sal_uInt64 FileHandle_Impl::getSize() const +{ + LONGLONG bufend = std::max(LONGLONG(0), m_bufptr) + m_buflen; + return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend)); +} + +oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize) +{ + LARGE_INTEGER nDstPos; nDstPos.QuadPart = sal::static_int_cast< LONGLONG >(uSize); + if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN)) + return oslTranslateFileError(GetLastError()); + + if (!::SetEndOfFile(m_hFile)) + return oslTranslateFileError(GetLastError()); + m_size = uSize; + + nDstPos.QuadPart = m_offset; + if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN)) + return oslTranslateFileError(GetLastError()); + + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::readAt( + LONGLONG nOffset, + void * pBuffer, + DWORD nBytesRequested, + sal_uInt64 * pBytesRead) +{ + SAL_WARN_IF(!(m_state & StateBits::Seekable), "sal.osl", "FileHandle_Impl::readAt(): not seekable"); + if (!(m_state & StateBits::Seekable)) + return osl_File_E_SPIPE; + + SAL_WARN_IF(!(m_state & StateBits::Readable), "sal.osl", "FileHandle_Impl::readAt(): not readable"); + if (!(m_state & StateBits::Readable)) + return osl_File_E_BADF; + + if (nOffset != m_offset) + { + LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset; + if (!::SetFilePointerEx(m_hFile, liOffset, nullptr, FILE_BEGIN)) + return oslTranslateFileError(GetLastError()); + m_offset = nOffset; + } + + DWORD dwDone = 0; + if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr)) + return oslTranslateFileError(GetLastError()); + m_offset += dwDone; + + *pBytesRead = dwDone; + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::writeAt( + LONGLONG nOffset, + void const * pBuffer, + DWORD nBytesToWrite, + sal_uInt64 * pBytesWritten) +{ + SAL_WARN_IF(!(m_state & StateBits::Seekable), "sal.osl", "FileHandle_Impl::writeAt(): not seekable"); + if (!(m_state & StateBits::Seekable)) + return osl_File_E_SPIPE; + + SAL_WARN_IF(!(m_state & StateBits::Writeable), "sal.osl", "FileHandle_Impl::writeAt(): not writeable"); + if (!(m_state & StateBits::Writeable)) + return osl_File_E_BADF; + + if (nOffset != m_offset) + { + LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset; + if (!::SetFilePointerEx (m_hFile, liOffset, nullptr, FILE_BEGIN)) + return oslTranslateFileError(GetLastError()); + m_offset = nOffset; + } + + DWORD dwDone = 0; + if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr)) + return oslTranslateFileError(GetLastError()); + m_offset += dwDone; + + m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(m_offset)); + + *pBytesWritten = dwDone; + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::readFileAt( + LONGLONG nOffset, + void * pBuffer, + sal_uInt64 uBytesRequested, + sal_uInt64 * pBytesRead) +{ + static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max(); + if (g_limit_dword < uBytesRequested) + return osl_File_E_OVERFLOW; + DWORD nBytesRequested = sal::static_int_cast< DWORD >(uBytesRequested); + + if (!(m_state & StateBits::Seekable)) + { + // not seekable (pipe) + DWORD dwDone = 0; + if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr)) + return oslTranslateFileError(GetLastError()); + *pBytesRead = dwDone; + return osl_File_E_None; + } + else if (!m_buffer) + { + // not buffered + return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead); + } + else + { + sal_uInt8 * buffer = static_cast< sal_uInt8* >(pBuffer); + for (*pBytesRead = 0; nBytesRequested > 0;) + { + LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz; + SIZE_T const bufpos = nOffset % m_bufsiz; + + if (bufptr != m_bufptr) + { + // flush current buffer + oslFileError result = syncFile(); + if (result != osl_File_E_None) + return result; + m_bufptr = -1; + m_buflen = 0; + + if (nBytesRequested >= m_bufsiz) + { + // buffer too small, read through from file + sal_uInt64 uDone = 0; + result = readAt (nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone); + if (result != osl_File_E_None) + return result; + + nBytesRequested -= sal::static_int_cast< DWORD >(uDone); + *pBytesRead += uDone; + return osl_File_E_None; + } + + // update buffer (pointer) + sal_uInt64 uDone = 0; + result = readAt(bufptr, m_buffer, m_bufsiz, &uDone); + if (result != osl_File_E_None) + return result; + m_bufptr = bufptr; + m_buflen = sal::static_int_cast< SIZE_T >(uDone); + } + if (bufpos >= m_buflen) + { + // end of file + return osl_File_E_None; + } + + SIZE_T const bytes = std::min(m_buflen - bufpos, static_cast<SIZE_T>(nBytesRequested)); + memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes); + nBytesRequested -= bytes; + *pBytesRead += bytes; + nOffset += bytes; + } + return osl_File_E_None; + } +} + +oslFileError FileHandle_Impl::writeFileAt( + LONGLONG nOffset, + void const * pBuffer, + sal_uInt64 uBytesToWrite, + sal_uInt64 * pBytesWritten) +{ + static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max(); + if (g_limit_dword < uBytesToWrite) + return osl_File_E_OVERFLOW; + DWORD nBytesToWrite = sal::static_int_cast< DWORD >(uBytesToWrite); + + if (!(m_state & StateBits::Seekable)) + { + // not seekable (pipe) + DWORD dwDone = 0; + if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr)) + return oslTranslateFileError(GetLastError()); + *pBytesWritten = dwDone; + return osl_File_E_None; + } + else if (!m_buffer) + { + // not buffered + return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten); + } + else + { + sal_uInt8 const * buffer = static_cast< sal_uInt8 const* >(pBuffer); + for (*pBytesWritten = 0; nBytesToWrite > 0;) + { + LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz; + SIZE_T const bufpos = nOffset % m_bufsiz; + if (bufptr != m_bufptr) + { + // flush current buffer + oslFileError result = syncFile(); + if (result != osl_File_E_None) + return result; + m_bufptr = -1; + m_buflen = 0; + + if (nBytesToWrite >= m_bufsiz) + { + // buffer too small, write through to file + sal_uInt64 uDone = 0; + result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone); + if (result != osl_File_E_None) + return result; + if (uDone != nBytesToWrite) + return osl_File_E_IO; + + nBytesToWrite -= sal::static_int_cast< DWORD >(uDone); + *pBytesWritten += uDone; + return osl_File_E_None; + } + + // update buffer (pointer) + sal_uInt64 uDone = 0; + result = readAt(bufptr, m_buffer, m_bufsiz, &uDone); + if (result != osl_File_E_None) + return result; + m_bufptr = bufptr; + m_buflen = sal::static_int_cast< SIZE_T >(uDone); + } + + SIZE_T const bytes = std::min(m_bufsiz - bufpos, static_cast<SIZE_T>(nBytesToWrite)); + memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes); + nBytesToWrite -= bytes; + *pBytesWritten += bytes; + nOffset += bytes; + + m_buflen = std::max(m_buflen, bufpos + bytes); + m_state |= StateBits::Modified; + } + return osl_File_E_None; + } +} + +oslFileError FileHandle_Impl::readLineAt( + LONGLONG nOffset, + sal_Sequence ** ppSequence, + sal_uInt64 * pBytesRead) +{ + oslFileError result = osl_File_E_None; + + LONGLONG bufptr = (nOffset / m_bufsiz) * m_bufsiz; + if (bufptr != m_bufptr) + { + /* flush current buffer */ + result = syncFile(); + if (result != osl_File_E_None) + return result; + + /* update buffer (pointer) */ + sal_uInt64 uDone = 0; + result = readAt(bufptr, m_buffer, m_bufsiz, &uDone); + if (result != osl_File_E_None) + return result; + + m_bufptr = bufptr; + m_buflen = sal::static_int_cast< SIZE_T >(uDone); + } + + static int const LINE_STATE_BEGIN = 0; + static int const LINE_STATE_CR = 1; + static int const LINE_STATE_LF = 2; + + SIZE_T bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr), curpos = bufpos, dstpos = 0; + int state = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN; + + for (; state != LINE_STATE_LF;) + { + if (curpos >= m_buflen) + { + /* buffer examined */ + if (curpos > bufpos) // actually, curpos can't become less than bufpos, so != could do + { + /* flush buffer to sequence */ + result = writeSequence_Impl( + ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos); + if (result != osl_File_E_None) + return result; + *pBytesRead += curpos - bufpos; + nOffset += curpos - bufpos; + } + + bufptr = nOffset / m_bufsiz * m_bufsiz; + if (bufptr != m_bufptr) + { + /* update buffer (pointer) */ + sal_uInt64 uDone = 0; + result = readAt(bufptr, m_buffer, m_bufsiz, &uDone); + if (result != osl_File_E_None) + return result; + m_bufptr = bufptr; + m_buflen = sal::static_int_cast< SIZE_T >(uDone); + } + + bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr); + curpos = bufpos; + if (bufpos >= m_buflen) + break; + } + switch (state) + { + case LINE_STATE_CR: + state = LINE_STATE_LF; + switch (m_buffer[curpos]) + { + case 0x0A: /* CRLF */ + /* eat current char */ + curpos++; + break; + default: /* single CR */ + /* keep current char */ + break; + } + break; + default: + /* determine next state */ + switch (m_buffer[curpos]) + { + case 0x0A: /* single LF */ + state = LINE_STATE_LF; + break; + case 0x0D: /* CR */ + state = LINE_STATE_CR; + break; + default: /* advance to next char */ + curpos++; + break; + } + if (state != LINE_STATE_BEGIN) + { + /* store (and eat) the newline char */ + m_buffer[curpos] = 0x0A; + curpos++; + + /* flush buffer to sequence */ + result = writeSequence_Impl( + ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1); + if (result != osl_File_E_None) + return result; + *pBytesRead += curpos - bufpos; + nOffset += curpos - bufpos; + } + break; + } + } + + result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0); + if (result != osl_File_E_None) + return result; + if (dstpos > 0) + return osl_File_E_None; + if (bufpos >= m_buflen) + return osl_File_E_AGAIN; + return osl_File_E_None; +} + +oslFileError FileHandle_Impl::writeSequence_Impl( + sal_Sequence ** ppSequence, + SIZE_T * pnOffset, + const void * pBuffer, + SIZE_T nBytes) +{ + sal_Int32 nElements = *pnOffset + nBytes; + if (!*ppSequence) + { + /* construct sequence */ + rtl_byte_sequence_constructNoDefault(ppSequence, nElements); + } + else if (nElements != (*ppSequence)->nElements) + { + /* resize sequence */ + rtl_byte_sequence_realloc(ppSequence, nElements); + } + if (*ppSequence) + { + /* fill sequence */ + memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes); + *pnOffset += nBytes; + } + return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM; +} + +oslFileError FileHandle_Impl::syncFile() +{ + oslFileError result = osl_File_E_None; + if (m_state & StateBits::Modified) + { + sal_uInt64 uDone = 0; + result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone); + if (result != osl_File_E_None) + return result; + if (uDone != m_buflen) + return osl_File_E_IO; + m_state &= ~StateBits::Modified; + } + return result; +} + +extern "C" oslFileHandle osl_createFileHandleFromOSHandle( + HANDLE hFile, + sal_uInt32 uFlags) +{ + if (!IsValidHandle(hFile)) + return nullptr; // EINVAL + + FileHandle_Impl * pImpl = new FileHandle_Impl(hFile); + + /* check for regular file */ + if (GetFileType(hFile) == FILE_TYPE_DISK) + { + /* mark seekable */ + pImpl->m_state |= StateBits::Seekable; + + /* init current size */ + LARGE_INTEGER uSize = { { 0, 0 } }; + (void) ::GetFileSizeEx(hFile, &uSize); + pImpl->m_size = (sal::static_int_cast<sal_uInt64>(uSize.HighPart) << 32) + uSize.LowPart; + } + + if (!(uFlags & osl_File_OpenFlag_Read)) + pImpl->m_state &= ~StateBits::Readable; + if (!(uFlags & osl_File_OpenFlag_Write)) + pImpl->m_state &= ~StateBits::Writeable; + + SAL_WARN_IF( + !((uFlags & osl_File_OpenFlag_Read) || (uFlags & osl_File_OpenFlag_Write)), + "sal.osl", + "osl_createFileHandleFromOSHandle(): missing read/write access flags"); + return static_cast<oslFileHandle>(pImpl); +} + +oslFileError SAL_CALL osl_openFile( + rtl_uString * strPath, + oslFileHandle * pHandle, + sal_uInt32 uFlags) +{ + OUString strSysPath; + oslFileError result = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + if (result != osl_File_E_None) + return result; + + // tdf126742 use FILE_SHARE_WRITE to get closer to non-Windows platform behaviour, + // for details and discussion see task please + DWORD dwAccess = GENERIC_READ, dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE, dwCreation = 0; + + if (uFlags & osl_File_OpenFlag_Write) + dwAccess |= GENERIC_WRITE; + + if (uFlags & osl_File_OpenFlag_NoLock) + dwShare |= FILE_SHARE_DELETE; + + if (uFlags & osl_File_OpenFlag_Create) + dwCreation |= CREATE_NEW; + else + dwCreation |= OPEN_EXISTING; + + HANDLE hFile = CreateFileW( + o3tl::toW(strSysPath.getStr()), + dwAccess, dwShare, nullptr, dwCreation, 0, nullptr); + + // @@@ ERROR HANDLING @@@ + if (!IsValidHandle(hFile)) + result = oslTranslateFileError(GetLastError()); + + *pHandle = osl_createFileHandleFromOSHandle(hFile, uFlags | osl_File_OpenFlag_Read); + + return result; +} + +oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + return result; + + if (!FlushFileBuffers(pImpl->m_hFile)) + return oslTranslateFileError(GetLastError()); + + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile)) + return osl_File_E_INVAL; + + ::EnterCriticalSection(&(pImpl->m_mutex)); + + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + { + /* ignore double failure */ + (void)::CloseHandle(pImpl->m_hFile); + } + else if (!::CloseHandle(pImpl->m_hFile)) + { + /* translate error code */ + result = oslTranslateFileError(GetLastError()); + } + + ::LeaveCriticalSection(&(pImpl->m_mutex)); + delete pImpl; + return result; +} + +namespace { + +// coverity[result_independent_of_operands] - crossplatform requirement +template<typename T> bool exceedsMaxSIZE_T(T n) +{ return n > std::numeric_limits< SIZE_T >::max(); } + +} + +oslFileError SAL_CALL osl_mapFile( + oslFileHandle Handle, + void** ppAddr, + sal_uInt64 uLength, + sal_uInt64 uOffset, + sal_uInt32 uFlags) +{ + struct FileMapping + { + HANDLE m_handle; + + explicit FileMapping(HANDLE hMap) + : m_handle(hMap) + {} + + ~FileMapping() + { + (void)::CloseHandle(m_handle); + } + }; + + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppAddr)) + return osl_File_E_INVAL; + *ppAddr = nullptr; + + if (exceedsMaxSIZE_T(uLength)) + return osl_File_E_OVERFLOW; + SIZE_T const nLength = sal::static_int_cast< SIZE_T >(uLength); + + FileMapping aMap(::CreateFileMappingW(pImpl->m_hFile, nullptr, SEC_COMMIT | PAGE_READONLY, 0, 0, nullptr)); + if (!IsValidHandle(aMap.m_handle)) + return oslTranslateFileError(GetLastError()); + + DWORD const dwOffsetHi = sal::static_int_cast<DWORD>(uOffset >> 32); + DWORD const dwOffsetLo = sal::static_int_cast<DWORD>(uOffset & 0xFFFFFFFF); + + *ppAddr = ::MapViewOfFile(aMap.m_handle, FILE_MAP_READ, dwOffsetHi, dwOffsetLo, nLength); + if (!*ppAddr) + return oslTranslateFileError(GetLastError()); + + if (uFlags & osl_File_MapFlag_RandomAccess) + { + // Determine memory pagesize. + SYSTEM_INFO info; + ::GetSystemInfo(&info); + DWORD const dwPageSize = info.dwPageSize; + + /* + * Pagein, touching first byte of each memory page. + * Note: volatile disables optimizing the loop away. + */ + BYTE volatile * pData(static_cast<BYTE*>(*ppAddr)); + SIZE_T nSize(nLength); + + while (nSize > dwPageSize) + { + pData[0]; + pData += dwPageSize; + nSize -= dwPageSize; + } + if (nSize > 0) + { + pData[0]; + } + } + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 /* uLength */) +{ + if (!pAddr) + return osl_File_E_INVAL; + + if (!::UnmapViewOfFile(pAddr)) + return oslTranslateFileError(GetLastError()); + + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle /* Handle */, void* pAddr, sal_uInt64 uLength) +{ + return osl_unmapFile(pAddr, uLength); +} + +oslFileError +SAL_CALL osl_readLine( + oslFileHandle Handle, + sal_Sequence ** ppSequence) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppSequence)) + return osl_File_E_INVAL; + sal_uInt64 uBytesRead = 0; + + // read at current filepos; filepos += uBytesRead; + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + oslFileError result = pImpl->readLineAt( + pImpl->m_filepos, ppSequence, &uBytesRead); + if (result == osl_File_E_None) + pImpl->m_filepos += uBytesRead; + return result; +} + +oslFileError SAL_CALL osl_readFile( + oslFileHandle Handle, + void * pBuffer, + sal_uInt64 uBytesRequested, + sal_uInt64 * pBytesRead) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead)) + return osl_File_E_INVAL; + + // read at current filepos; filepos += *pBytesRead; + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + oslFileError result = pImpl->readFileAt( + pImpl->m_filepos, pBuffer, uBytesRequested, pBytesRead); + if (result == osl_File_E_None) + pImpl->m_filepos += *pBytesRead; + return result; +} + +oslFileError SAL_CALL osl_writeFile( + oslFileHandle Handle, + const void * pBuffer, + sal_uInt64 uBytesToWrite, + sal_uInt64 * pBytesWritten) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten)) + return osl_File_E_INVAL; + + // write at current filepos; filepos += *pBytesWritten; + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + oslFileError result = pImpl->writeFileAt( + pImpl->m_filepos, pBuffer, uBytesToWrite, pBytesWritten); + if (result == osl_File_E_None) + pImpl->m_filepos += *pBytesWritten; + return result; +} + +LONGLONG const g_limit_longlong = std::numeric_limits< LONGLONG >::max(); + +namespace { + +// coverity[result_independent_of_operands] - crossplatform requirement +template<typename T> bool exceedsMaxLONGLONG(T n) +{ return n > g_limit_longlong; } + +template<typename T> bool exceedsMinLONGLONG(T n) +{ return n < std::numeric_limits<LONGLONG>::min(); } + +} + +oslFileError SAL_CALL osl_readFileAt( + oslFileHandle Handle, + sal_uInt64 uOffset, + void* pBuffer, + sal_uInt64 uBytesRequested, + sal_uInt64* pBytesRead) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead)) + return osl_File_E_INVAL; + if (!(pImpl->m_state & StateBits::Seekable)) + return osl_File_E_SPIPE; + + if (exceedsMaxLONGLONG(uOffset)) + return osl_File_E_OVERFLOW; + LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset); + + // read at specified fileptr + FileHandle_Impl::Guard lock (&(pImpl->m_mutex)); + return pImpl->readFileAt(nOffset, pBuffer, uBytesRequested, pBytesRead); +} + +oslFileError SAL_CALL osl_writeFileAt( + oslFileHandle Handle, + sal_uInt64 uOffset, + const void* pBuffer, + sal_uInt64 uBytesToWrite, + sal_uInt64* pBytesWritten) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten)) + return osl_File_E_INVAL; + if (!(pImpl->m_state & StateBits::Seekable)) + return osl_File_E_SPIPE; + + if (exceedsMaxLONGLONG(uOffset)) + return osl_File_E_OVERFLOW; + LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset); + + // write at specified fileptr + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + return pImpl->writeFileAt(nOffset, pBuffer, uBytesToWrite, pBytesWritten); +} + +oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pIsEOF)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + *pIsEOF = (pImpl->getPos() == pImpl->getSize()); + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64 *pPos) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pPos)) + return osl_File_E_INVAL; + + // no need to lock because pos is atomic + *pPos = pImpl->getPos(); + + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile)) + return osl_File_E_INVAL; + + if (exceedsMaxLONGLONG(uOffset) || exceedsMinLONGLONG(uOffset)) + return osl_File_E_OVERFLOW; + LONGLONG nPos = 0, nOffset = sal::static_int_cast< LONGLONG >(uOffset); + + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + switch (uHow) + { + case osl_Pos_Absolut: + if (nOffset < 0) + return osl_File_E_INVAL; + break; + + case osl_Pos_Current: + nPos = sal::static_int_cast< LONGLONG >(pImpl->getPos()); + if ((nOffset < 0) && (nPos < -1*nOffset)) + return osl_File_E_INVAL; + assert(nPos >= 0); + if (nOffset > g_limit_longlong - nPos) + return osl_File_E_OVERFLOW; + break; + + case osl_Pos_End: + nPos = sal::static_int_cast< LONGLONG >(pImpl->getSize()); + if ((nOffset < 0) && (nPos < -1*nOffset)) + return osl_File_E_INVAL; + assert(nPos >= 0); + if (nOffset > g_limit_longlong - nPos) + return osl_File_E_OVERFLOW; + break; + + default: + return osl_File_E_INVAL; + } + + return pImpl->setPos(nPos + nOffset); +} + +oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64 *pSize) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pSize)) + return osl_File_E_INVAL; + + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + *pSize = pImpl->getSize(); + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize) +{ + FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle); + + if ((!pImpl) || !IsValidHandle(pImpl->m_hFile)) + return osl_File_E_INVAL; + if (!(pImpl->m_state & StateBits::Writeable)) + return osl_File_E_BADF; + + if (exceedsMaxLONGLONG(uSize)) + return osl_File_E_OVERFLOW; + + FileHandle_Impl::Guard lock(&(pImpl->m_mutex)); + oslFileError result = pImpl->syncFile(); + if (result != osl_File_E_None) + return result; + pImpl->m_bufptr = -1; + pImpl->m_buflen = 0; + + return pImpl->setSize(uSize); +} + +oslFileError SAL_CALL osl_removeFile(rtl_uString* strPath) +{ + OUString strSysPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if (error == osl_File_E_None) + { + if (DeleteFileW(o3tl::toW(strSysPath.getStr()))) + error = osl_File_E_None; + else + error = oslTranslateFileError(GetLastError()); + } + return error; +} + +oslFileError SAL_CALL osl_copyFile(rtl_uString* strPath, rtl_uString *strDestPath) +{ + OUString strSysPath, strSysDestPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if (error == osl_File_E_None) + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false); + + if (error == osl_File_E_None) + { + LPCWSTR src = o3tl::toW(strSysPath.getStr()); + LPCWSTR dst = o3tl::toW(strSysDestPath.getStr()); + + if (CopyFileW(src, dst, FALSE)) + error = osl_File_E_None; + else + error = oslTranslateFileError(GetLastError()); + } + + return error; +} + +oslFileError SAL_CALL osl_moveFile(rtl_uString* strPath, rtl_uString *strDestPath) +{ + OUString strSysPath, strSysDestPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if (error == osl_File_E_None) + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false); + + if (error == osl_File_E_None) + { + LPCWSTR src = o3tl::toW(strSysPath.getStr()); + LPCWSTR dst = o3tl::toW(strSysDestPath.getStr()); + + if (MoveFileExW(src, dst, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING)) + error = osl_File_E_None; + else + error = oslTranslateFileError(GetLastError()); + } + + return error; +} + +oslFileError SAL_CALL osl_replaceFile(rtl_uString* strPath, rtl_uString* strDestPath) +{ + OUString strSysPath, strSysDestPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if (error == osl_File_E_None) + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false); + + if (error == osl_File_E_None) + { + LPCWSTR src = o3tl::toW(strSysPath.getStr()); + LPCWSTR dst = o3tl::toW(strSysDestPath.getStr()); + + if (!ReplaceFileW(dst, src, nullptr, + REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS + | REPLACEFILE_IGNORE_ACL_ERRORS, + nullptr, nullptr)) + { + DWORD dwError = GetLastError(); + if (dwError == ERROR_FILE_NOT_FOUND // no strDestPath file? + || dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT // e.g., files on different volumes + || dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 + || dwError == ERROR_UNABLE_TO_REMOVE_REPLACED) + error = osl_moveFile(strPath, strDestPath); + else + error = oslTranslateFileError(dwError); + } + } + + return error; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file_dirvol.cxx b/sal/osl/w32/file_dirvol.cxx new file mode 100644 index 000000000..f9d821605 --- /dev/null +++ b/sal/osl/w32/file_dirvol.cxx @@ -0,0 +1,1691 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <systools/win32/uwinapi.h> + +#include "file_url.hxx" +#include "filetime.hxx" +#include "file_error.hxx" + +#include "path_helper.hxx" + +#include <rtl/alloc.h> +#include <rtl/ustring.hxx> +#include <rtl/character.hxx> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +const wchar_t UNC_PREFIX[] = L"\\\\"; +const wchar_t BACKSLASH = '\\'; +const wchar_t SLASH = '/'; + +BOOL TimeValueToFileTime(const TimeValue *cpTimeVal, FILETIME *pFTime) +{ + SYSTEMTIME BaseSysTime; + FILETIME BaseFileTime; + FILETIME FTime; + bool fSuccess = false; + + BaseSysTime.wYear = 1970; + BaseSysTime.wMonth = 1; + BaseSysTime.wDayOfWeek = 0; + BaseSysTime.wDay = 1; + BaseSysTime.wHour = 0; + BaseSysTime.wMinute = 0; + BaseSysTime.wSecond = 0; + BaseSysTime.wMilliseconds = 0; + + if (cpTimeVal==nullptr) + return fSuccess; + + if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) ) + { + __int64 timeValue; + + __int64 localTime = cpTimeVal->Seconds*__int64(10000000)+cpTimeVal->Nanosec/100; + osl::detail::setFiletime(FTime, localTime); + fSuccess = 0 <= (timeValue= osl::detail::getFiletime(BaseFileTime) + osl::detail::getFiletime(FTime)); + if (fSuccess) + osl::detail::setFiletime(*pFTime, timeValue); + } + return fSuccess; +} + +BOOL FileTimeToTimeValue(const FILETIME *cpFTime, TimeValue *pTimeVal) +{ + SYSTEMTIME BaseSysTime; + FILETIME BaseFileTime; + bool fSuccess = false; /* Assume failure */ + + BaseSysTime.wYear = 1970; + BaseSysTime.wMonth = 1; + BaseSysTime.wDayOfWeek = 0; + BaseSysTime.wDay = 1; + BaseSysTime.wHour = 0; + BaseSysTime.wMinute = 0; + BaseSysTime.wSecond = 0; + BaseSysTime.wMilliseconds = 0; + + if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) ) + { + __int64 Value; + + fSuccess = 0 <= (Value = osl::detail::getFiletime(*cpFTime) - osl::detail::getFiletime(BaseFileTime)); + + if ( fSuccess ) + { + pTimeVal->Seconds = static_cast<unsigned long>(Value / 10000000L); + pTimeVal->Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100); + } + } + return fSuccess; +} + +namespace +{ + + struct Component + { + Component() : + begin_(nullptr), end_(nullptr) + {} + + bool isPresent() const + { return (static_cast<sal_IntPtr>(end_ - begin_) > 0); } + + const sal_Unicode* begin_; + const sal_Unicode* end_; + }; + + struct UNCComponents + { + Component server_; + Component share_; + Component resource_; + }; + + bool is_UNC_path(const sal_Unicode* path) + { return (0 == wcsncmp(UNC_PREFIX, o3tl::toW(path), SAL_N_ELEMENTS(UNC_PREFIX) - 1)); } + + void parse_UNC_path(const sal_Unicode* path, UNCComponents* puncc) + { + OSL_PRECOND(is_UNC_path(path), "Precondition violated: No UNC path"); + OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes"); + + const sal_Unicode* pend = path + rtl_ustr_getLength(path); + const sal_Unicode* ppos = path + 2; + + puncc->server_.begin_ = ppos; + while ((ppos < pend) && (*ppos != BACKSLASH)) + ppos++; + + puncc->server_.end_ = ppos; + + if (BACKSLASH == *ppos) + { + puncc->share_.begin_ = ++ppos; + while ((ppos < pend) && (*ppos != BACKSLASH)) + ppos++; + + puncc->share_.end_ = ppos; + + if (BACKSLASH == *ppos) + { + puncc->resource_.begin_ = ++ppos; + while (ppos < pend) + ppos++; + + puncc->resource_.end_ = ppos; + } + } + + SAL_WARN_IF(!puncc->server_.isPresent() || !puncc->share_.isPresent(), + "sal.osl", + "Postcondition violated: Invalid UNC path detected"); + } + + bool has_path_parent(const sal_Unicode* path) + { + // Has the given path a parent or are we already there, + // e.g. 'c:\' or '\\server\share\'? + + bool has_parent = false; + if (is_UNC_path(path)) + { + UNCComponents unc_comp; + parse_UNC_path(path, &unc_comp); + has_parent = unc_comp.resource_.isPresent(); + } + else + { + has_parent = !osl::systemPathIsLogicalDrivePattern(OUString(path)); + } + return has_parent; + } + + bool has_path_parent(const OUString& path) + { return has_path_parent(path.getStr()); } + +} + +oslFileError SAL_CALL osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle Handle ) +{ + if ( Handle ) + { + rtl_uString_acquire( static_cast<rtl_uString *>(Handle) ); + return osl_File_E_None; + } + else + return osl_File_E_INVAL; +} + +oslFileError SAL_CALL osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle Handle ) +{ + if ( Handle ) + { + rtl_uString_release( static_cast<rtl_uString *>(Handle) ); + return osl_File_E_None; + } + else + return osl_File_E_INVAL; +} + +oslFileError SAL_CALL osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle Handle, rtl_uString **pstrPath ) +{ + if ( Handle && pstrPath ) + { + rtl_uString_assign( pstrPath, static_cast<rtl_uString *>(Handle) ); + return osl_File_E_None; + } + else + return osl_File_E_INVAL; +} + +#define DIRECTORYITEM_DRIVE 0 +#define DIRECTORYITEM_FILE 1 +#define DIRECTORYITEM_SERVER 2 + +namespace { + +struct DirectoryItem_Impl +{ + UINT uType = 0; + union { + WIN32_FIND_DATAW FindData; + WCHAR cDriveString[MAX_PATH]; + }; + OUString m_sFullPath; + bool bFullPathNormalized = false; + int nRefCount = 0; +}; + +} + +#define DIRECTORYTYPE_LOCALROOT 0 +#define DIRECTORYTYPE_NETROOT 1 +#define DIRECTORYTYPE_FILESYSTEM 3 + +namespace { + +struct Directory_Impl +{ + UINT uType = 0; + union { + HANDLE hDirectory = nullptr; + HANDLE hEnumDrives; + }; + OUString m_sDirectoryPath; +}; + +typedef struct tagDRIVEENUM +{ + LPCWSTR lpIdent; + WCHAR cBuffer[/*('Z' - 'A' + 1) * sizeof("A:\\") + 1*/256]; + LPCWSTR lpCurrent; +} DRIVEENUM, *PDRIVEENUM, *LPDRIVEENUM; + +} + +static HANDLE WINAPI OpenLogicalDrivesEnum() +{ + LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(HeapAlloc( GetProcessHeap(), 0, sizeof(DRIVEENUM) )); + if ( pEnum ) + { + DWORD dwNumCopied = GetLogicalDriveStringsW( SAL_N_ELEMENTS(pEnum->cBuffer) - 1, pEnum->cBuffer ); + + if ( dwNumCopied && dwNumCopied < SAL_N_ELEMENTS(pEnum->cBuffer) ) + { + pEnum->lpCurrent = pEnum->cBuffer; + pEnum->lpIdent = L"tagDRIVEENUM"; + } + else + { + HeapFree( GetProcessHeap(), 0, pEnum ); + pEnum = nullptr; + } + } + return pEnum ? static_cast<HANDLE>(pEnum) : INVALID_HANDLE_VALUE; +} + +static bool WINAPI EnumLogicalDrives(HANDLE hEnum, LPWSTR lpBuffer) +{ + LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum); + if ( !pEnum ) + { + SetLastError( ERROR_INVALID_HANDLE ); + return false; + } + + int nLen = wcslen( pEnum->lpCurrent ); + if ( !nLen ) + { + SetLastError( ERROR_NO_MORE_FILES ); + return false; + } + + CopyMemory( lpBuffer, pEnum->lpCurrent, (nLen + 1) * sizeof(WCHAR) ); + pEnum->lpCurrent += nLen + 1; + return true; +} + +static bool WINAPI CloseLogicalDrivesEnum(HANDLE hEnum) +{ + bool fSuccess = false; + LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum); + + if ( pEnum ) + { + HeapFree( GetProcessHeap(), 0, pEnum ); + fSuccess = true; + } + else + SetLastError( ERROR_INVALID_HANDLE ); + + return fSuccess; +} + +namespace { + +typedef struct tagDIRECTORY +{ + HANDLE hFind; + WIN32_FIND_DATAW aFirstData; +} DIRECTORY, *PDIRECTORY, *LPDIRECTORY; + +} + +static HANDLE WINAPI OpenDirectory( rtl_uString* pPath) +{ + if ( !pPath ) + return nullptr; + + sal_uInt32 nLen = rtl_uString_getLength( pPath ); + if ( !nLen ) + return nullptr; + + const WCHAR* pSuffix = nullptr; + sal_uInt32 nSuffLen = 0; + if ( pPath->buffer[nLen - 1] != L'\\' ) + { + pSuffix = L"\\*.*"; + nSuffLen = 4; + } + else + { + pSuffix = L"*.*"; + nSuffLen = 3; + } + + WCHAR* szFileMask = static_cast< WCHAR* >( malloc( sizeof( WCHAR ) * ( nLen + nSuffLen + 1 ) ) ); + assert(szFileMask); // Don't handle OOM conditions + wcscpy( szFileMask, o3tl::toW(rtl_uString_getStr( pPath )) ); + wcscat( szFileMask, pSuffix ); + + LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(HeapAlloc(GetProcessHeap(), 0, sizeof(DIRECTORY))); + assert(pDirectory); // Don't handle OOM conditions + pDirectory->hFind = FindFirstFileW(szFileMask, &pDirectory->aFirstData); + + if (!IsValidHandle(pDirectory->hFind)) + { + if ( GetLastError() != ERROR_NO_MORE_FILES ) + { + HeapFree(GetProcessHeap(), 0, pDirectory); + pDirectory = nullptr; + } + } + free(szFileMask); + + return static_cast<HANDLE>(pDirectory); +} + +static bool WINAPI EnumDirectory(HANDLE hDirectory, LPWIN32_FIND_DATAW pFindData) +{ + LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory); + if ( !pDirectory ) + { + SetLastError( ERROR_INVALID_HANDLE ); + return false; + } + + bool fSuccess = false; + bool fValid; + do + { + if ( pDirectory->aFirstData.cFileName[0] ) + { + *pFindData = pDirectory->aFirstData; + fSuccess = true; + pDirectory->aFirstData.cFileName[0] = 0; + } + else if ( IsValidHandle( pDirectory->hFind ) ) + fSuccess = FindNextFileW( pDirectory->hFind, pFindData ); + else + { + fSuccess = false; + SetLastError( ERROR_NO_MORE_FILES ); + } + + fValid = fSuccess && wcscmp( L".", pFindData->cFileName ) != 0 && wcscmp( L"..", pFindData->cFileName ) != 0; + + } while( fSuccess && !fValid ); + + return fSuccess; +} + +static bool WINAPI CloseDirectory(HANDLE hDirectory) +{ + bool fSuccess = false; + LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory); + + if (pDirectory) + { + if (IsValidHandle(pDirectory->hFind)) + fSuccess = FindClose(pDirectory->hFind); + + fSuccess = HeapFree(GetProcessHeap(), 0, pDirectory) && fSuccess; + } + else + SetLastError(ERROR_INVALID_HANDLE); + + return fSuccess; +} + +static oslFileError osl_openLocalRoot( + rtl_uString *strDirectoryPath, oslDirectory *pDirectory) +{ + if ( !pDirectory ) + return osl_File_E_INVAL; + + *pDirectory = nullptr; + + OUString strSysPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysPath.pData, false); + if ( osl_File_E_None != error ) + return error; + + Directory_Impl* pDirImpl = new (std::nothrow) Directory_Impl; + assert(pDirImpl); // Don't handle OOM conditions + pDirImpl->m_sDirectoryPath = strSysPath; + + /* Append backslash if necessary */ + + /* @@@ToDo + use function ensure backslash + */ + sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength(); + if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != L'\\' ) + { + pDirImpl->m_sDirectoryPath += "\\"; + } + + pDirImpl->uType = DIRECTORYTYPE_LOCALROOT; + pDirImpl->hEnumDrives = OpenLogicalDrivesEnum(); + + /* @@@ToDo + Use IsValidHandle(...) + */ + if ( pDirImpl->hEnumDrives != INVALID_HANDLE_VALUE ) + { + *pDirectory = static_cast<oslDirectory>(pDirImpl); + error = osl_File_E_None; + } + else + { + if ( pDirImpl ) + { + delete pDirImpl; + pDirImpl = nullptr; + } + + error = oslTranslateFileError( GetLastError() ); + } + return error; +} + +static oslFileError osl_openFileDirectory( + rtl_uString *strDirectoryPath, oslDirectory *pDirectory) +{ + oslFileError error = osl_File_E_None; + + if ( !pDirectory ) + return osl_File_E_INVAL; + *pDirectory = nullptr; + + Directory_Impl *pDirImpl = new (std::nothrow) Directory_Impl; + assert(pDirImpl); // Don't handle OOM conditions + pDirImpl->m_sDirectoryPath = strDirectoryPath; + + /* Append backslash if necessary */ + + /* @@@ToDo + use function ensure backslash + */ + sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength(); + if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != '\\' ) + pDirImpl->m_sDirectoryPath += "\\"; + + pDirImpl->uType = DIRECTORYTYPE_FILESYSTEM; + pDirImpl->hDirectory = OpenDirectory( pDirImpl->m_sDirectoryPath.pData ); + + if ( !pDirImpl->hDirectory ) + { + error = oslTranslateFileError( GetLastError() ); + + delete pDirImpl; + pDirImpl = nullptr; + } + + *pDirectory = static_cast<oslDirectory>(pDirImpl); + return error; +} + +static oslFileError osl_openNetworkServer( + rtl_uString *strSysDirPath, oslDirectory *pDirectory) +{ + NETRESOURCEW aNetResource; + HANDLE hEnum; + DWORD dwError; + + ZeroMemory( &aNetResource, sizeof(aNetResource) ); + + aNetResource.lpRemoteName = o3tl::toW(strSysDirPath->buffer); + + dwError = WNetOpenEnumW( + RESOURCE_GLOBALNET, + RESOURCETYPE_DISK, + RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER, + &aNetResource, + &hEnum ); + + if ( ERROR_SUCCESS == dwError ) + { + Directory_Impl *pDirImpl = new (std::nothrow) Directory_Impl; + assert(pDirImpl); // Don't handle OOM conditions + pDirImpl->uType = DIRECTORYTYPE_NETROOT; + pDirImpl->hDirectory = hEnum; + *pDirectory = static_cast<oslDirectory>(pDirImpl); + } + return oslTranslateFileError( dwError ); +} + +static DWORD create_dir_with_callback( + rtl_uString * dir_path, + oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, + void* pData) +{ + // Create the specified directory and call the + // user specified callback function. On success + // the function returns ERROR_SUCCESS else a Win32 error code. + + bool bCreated = CreateDirectoryW( o3tl::toW(rtl_uString_getStr( dir_path )), nullptr ); + + if ( bCreated ) + { + if (aDirectoryCreationCallbackFunc) + { + OUString url; + osl_getFileURLFromSystemPath(dir_path, &(url.pData)); + aDirectoryCreationCallbackFunc(pData, url.pData); + } + return ERROR_SUCCESS; + } + return GetLastError(); +} + +static int path_make_parent(sal_Unicode* path) +{ + /* Cut off the last part of the given path to + get the parent only, e.g. 'c:\dir\subdir' -> + 'c:\dir' or '\\share\sub\dir' -> '\\share\sub' + @return The position where the path has been cut + off (this is the position of the last backslash). + If there are no more parents 0 will be returned, + e.g. 'c:\' or '\\Share' have no more parents */ + + OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes"); + OSL_PRECOND(has_path_parent(path), "Path must have a parent"); + + sal_Unicode* pos_last_backslash = path + rtl_ustr_lastIndexOfChar(path, BACKSLASH); + *pos_last_backslash = 0; + return (pos_last_backslash - path); +} + +static DWORD create_dir_recursively_( + rtl_uString * dir_path, + oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, + void* pData) +{ + OSL_PRECOND( + rtl_ustr_lastIndexOfChar_WithLength(dir_path->buffer, dir_path->length, BACKSLASH) != dir_path->length, + "Path must not end with a backslash"); + + DWORD w32_error = create_dir_with_callback( + dir_path, aDirectoryCreationCallbackFunc, pData); + if (w32_error == ERROR_SUCCESS) + return ERROR_SUCCESS; + + if ((w32_error != ERROR_PATH_NOT_FOUND) || !has_path_parent(dir_path->buffer)) + return w32_error; + + int pos = path_make_parent(dir_path->buffer); // dir_path->buffer[pos] = 0, restore below + + w32_error = create_dir_recursively_( + dir_path, aDirectoryCreationCallbackFunc, pData); + + dir_path->buffer[pos] = BACKSLASH; // restore + + if (ERROR_SUCCESS != w32_error && ERROR_ALREADY_EXISTS != w32_error) + return w32_error; + + return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData); +} + +oslFileError SAL_CALL osl_createDirectoryPath( + rtl_uString* aDirectoryUrl, + oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc, + void* pData) +{ + if (aDirectoryUrl == nullptr) + return osl_File_E_INVAL; + + OUString sys_path; + oslFileError osl_error = + osl_getSystemPathFromFileURL_(OUString::unacquired(&aDirectoryUrl), &sys_path.pData, false); + + if (osl_error != osl_File_E_None) + return osl_error; + + osl::systemPathRemoveSeparator(sys_path); + + // const_cast because sys_path is a local copy + // which we want to modify inplace instead of + // copy it into another buffer on the heap again + return oslTranslateFileError(create_dir_recursively_( + sys_path.pData, aDirectoryCreationCallbackFunc, pData)); +} + +oslFileError SAL_CALL osl_createDirectory(rtl_uString* strPath) +{ + return osl_createDirectoryWithFlags( + strPath, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write); +} + +oslFileError osl_createDirectoryWithFlags(rtl_uString * strPath, sal_uInt32) +{ + OUString strSysPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if ( osl_File_E_None != error ) + return error; + + bool bCreated = CreateDirectoryW(o3tl::toW(strSysPath.getStr()), nullptr); + if ( !bCreated ) + { + /*@@@ToDo + The following case is a hack because the ucb or the webtop had some + problems with the error code that CreateDirectory returns in + case the path is only a logical drive, should be removed! + */ + + if ((strSysPath.getLength() == 2 || (strSysPath.getLength() == 3 && strSysPath[2] == '\\')) + && rtl::isAsciiAlpha(strSysPath[0]) && strSysPath[1] == ':') + SetLastError( ERROR_ALREADY_EXISTS ); + + error = oslTranslateFileError( GetLastError() ); + } + + return error; +} + +oslFileError SAL_CALL osl_removeDirectory(rtl_uString* strPath) +{ + OUString strSysPath; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false); + + if ( osl_File_E_None == error ) + { + if (RemoveDirectoryW(o3tl::toW(strSysPath.getStr()))) + error = osl_File_E_None; + else + error = oslTranslateFileError( GetLastError() ); + } + return error; +} + +oslFileError SAL_CALL osl_openDirectory(rtl_uString *strDirectoryPath, oslDirectory *pDirectory) +{ + oslFileError error; + + if ( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase( strDirectoryPath->buffer, "file:///" ) ) + error = osl_openLocalRoot( strDirectoryPath, pDirectory ); + else + { + OUString strSysDirectoryPath; + DWORD dwPathType; + + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysDirectoryPath.pData, false); + + if ( osl_File_E_None != error ) + return error; + + dwPathType = IsValidFilePath(strSysDirectoryPath, VALIDATEPATH_NORMAL, nullptr); + + if ( dwPathType & PATHTYPE_IS_SERVER ) + error = osl_openNetworkServer(strSysDirectoryPath.pData, pDirectory); + else + error = osl_openFileDirectory(strSysDirectoryPath.pData, pDirectory); + } + return error; +} + +static oslFileError osl_getNextNetResource( + oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ ) +{ + Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory); + DirectoryItem_Impl *pItemImpl = nullptr; + BYTE buffer[16384]; + LPNETRESOURCEW lpNetResource = reinterpret_cast<LPNETRESOURCEW>(buffer); + DWORD dwError, dwCount, dwBufSize; + + if ( !pItem ) + return osl_File_E_INVAL; + *pItem = nullptr; + + if ( !pDirImpl ) + return osl_File_E_INVAL; + + dwCount = 1; + dwBufSize = sizeof(buffer); + dwError = WNetEnumResourceW( pDirImpl->hDirectory, &dwCount, lpNetResource, &dwBufSize ); + + switch ( dwError ) + { + case NO_ERROR: + case ERROR_MORE_DATA: + { + pItemImpl = new (std::nothrow) DirectoryItem_Impl; + if ( !pItemImpl ) + return osl_File_E_NOMEM; + + pItemImpl->uType = DIRECTORYITEM_DRIVE; + osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) ); + + wcscpy( pItemImpl->cDriveString, lpNetResource->lpRemoteName ); + + *pItem = pItemImpl; + } + return osl_File_E_None; + case ERROR_NO_MORE_ITEMS: + return osl_File_E_NOENT; + default: + return oslTranslateFileError( dwError ); + } +} + +static oslFileError osl_getNextDrive( + oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ ) +{ + Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory); + DirectoryItem_Impl *pItemImpl = nullptr; + bool fSuccess; + + if ( !pItem ) + return osl_File_E_INVAL; + *pItem = nullptr; + + if ( !pDirImpl ) + return osl_File_E_INVAL; + + pItemImpl = new (std::nothrow) DirectoryItem_Impl; + if ( !pItemImpl ) + return osl_File_E_NOMEM; + + pItemImpl->uType = DIRECTORYITEM_DRIVE; + osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) ); + fSuccess = EnumLogicalDrives( pDirImpl->hEnumDrives, pItemImpl->cDriveString ); + + if ( fSuccess ) + { + *pItem = pItemImpl; + return osl_File_E_None; + } + else + { + delete pItemImpl; + return oslTranslateFileError( GetLastError() ); + } +} + +static oslFileError osl_getNextFileItem( + oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/) +{ + Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory); + DirectoryItem_Impl *pItemImpl = nullptr; + bool fFound; + + if ( !pItem ) + return osl_File_E_INVAL; + *pItem = nullptr; + + if ( !pDirImpl ) + return osl_File_E_INVAL; + + pItemImpl = new (std::nothrow) DirectoryItem_Impl; + if ( !pItemImpl ) + return osl_File_E_NOMEM; + + fFound = EnumDirectory( pDirImpl->hDirectory, &pItemImpl->FindData ); + if ( !fFound ) + { + delete pItemImpl; + return oslTranslateFileError( GetLastError() ); + } + + pItemImpl->uType = DIRECTORYITEM_FILE; + pItemImpl->nRefCount = 1; + + pItemImpl->m_sFullPath = pDirImpl->m_sDirectoryPath + o3tl::toU(pItemImpl->FindData.cFileName); + + pItemImpl->bFullPathNormalized = true; + *pItem = static_cast<oslDirectoryItem>(pItemImpl); + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_getNextDirectoryItem( + oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint) +{ + Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory); + + /* Assume failure */ + + if ( !pItem ) + return osl_File_E_INVAL; + *pItem = nullptr; + + if ( !pDirImpl ) + return osl_File_E_INVAL; + + switch ( pDirImpl->uType ) + { + case DIRECTORYTYPE_LOCALROOT: + return osl_getNextDrive( Directory, pItem, uHint ); + case DIRECTORYTYPE_NETROOT: + return osl_getNextNetResource( Directory, pItem, uHint ); + case DIRECTORYTYPE_FILESYSTEM: + return osl_getNextFileItem( Directory, pItem, uHint ); + default: + return osl_File_E_INVAL; + } +} + +oslFileError SAL_CALL osl_closeDirectory(oslDirectory Directory) +{ + Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory); + oslFileError eError = osl_File_E_INVAL; + + if ( pDirImpl ) + { + switch ( pDirImpl->uType ) + { + case DIRECTORYTYPE_FILESYSTEM: + eError = CloseDirectory( pDirImpl->hDirectory ) ? osl_File_E_None : oslTranslateFileError( GetLastError() ); + break; + case DIRECTORYTYPE_LOCALROOT: + eError = CloseLogicalDrivesEnum( pDirImpl->hEnumDrives ) ? osl_File_E_None : oslTranslateFileError( GetLastError() ); + break; + case DIRECTORYTYPE_NETROOT: + { + DWORD err = WNetCloseEnum(pDirImpl->hDirectory); + eError = (err == NO_ERROR) ? osl_File_E_None : oslTranslateFileError(err); + } + break; + default: + OSL_FAIL( "Invalid directory type" ); + break; + } + + delete pDirImpl; + } + return eError; +} + +namespace { + +/* Different types of paths */ +enum PATHTYPE +{ + PATHTYPE_SYNTAXERROR = 0, + PATHTYPE_NETROOT, + PATHTYPE_NETSERVER, + PATHTYPE_VOLUME, + PATHTYPE_FILE +}; + +} + +oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString *strFilePath, oslDirectoryItem *pItem) +{ + oslFileError error = osl_File_E_None; + OUString strSysFilePath; + PATHTYPE type = PATHTYPE_FILE; + DWORD dwPathType; + + /* Assume failure */ + + if ( !pItem ) + return osl_File_E_INVAL; + + *pItem = nullptr; + + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strFilePath), &strSysFilePath.pData, false); + + if ( osl_File_E_None != error ) + return error; + + dwPathType = IsValidFilePath( strSysFilePath, VALIDATEPATH_NORMAL, nullptr ); + + if ( dwPathType & PATHTYPE_IS_VOLUME ) + type = PATHTYPE_VOLUME; + else if ( dwPathType & PATHTYPE_IS_SERVER ) + type = PATHTYPE_NETSERVER; + else + type = PATHTYPE_FILE; + + switch ( type ) + { + case PATHTYPE_NETSERVER: + { + DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl; + + if ( !pItemImpl ) + error = osl_File_E_NOMEM; + + if ( osl_File_E_None == error ) + { + pItemImpl->uType = DIRECTORYITEM_SERVER; + + osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) ); + pItemImpl->m_sFullPath = strSysFilePath; + + // Assign a title anyway + { + int iSrc = 2; + int iDst = 0; + + while( iSrc < strSysFilePath.getLength() && strSysFilePath[iSrc] && strSysFilePath[iSrc] != '\\') + { + pItemImpl->FindData.cFileName[iDst++] = strSysFilePath[iSrc++]; + } + } + + *pItem = pItemImpl; + } + } + break; + case PATHTYPE_VOLUME: + { + DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl; + + if ( !pItemImpl ) + error = osl_File_E_NOMEM; + + if ( osl_File_E_None == error ) + { + pItemImpl->uType = DIRECTORYITEM_DRIVE; + + osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) ); + + wcscpy( pItemImpl->cDriveString, o3tl::toW(strSysFilePath.getStr()) ); + pItemImpl->cDriveString[0] = rtl::toAsciiUpperCase( pItemImpl->cDriveString[0] ); + + if ( pItemImpl->cDriveString[wcslen(pItemImpl->cDriveString) - 1] != '\\' ) + wcscat( pItemImpl->cDriveString, L"\\" ); + + *pItem = pItemImpl; + } + } + break; + case PATHTYPE_SYNTAXERROR: + case PATHTYPE_NETROOT: + case PATHTYPE_FILE: + { + HANDLE hFind; + WIN32_FIND_DATAW aFindData; + + if (!strSysFilePath.isEmpty() && strSysFilePath[strSysFilePath.getLength() - 1] == '\\') + strSysFilePath = strSysFilePath.copy(0, strSysFilePath.getLength() - 1); + + hFind = FindFirstFileW( o3tl::toW(strSysFilePath.getStr()), &aFindData ); + + if ( hFind != INVALID_HANDLE_VALUE ) + { + DirectoryItem_Impl *pItemImpl = new (std::nothrow) DirectoryItem_Impl; + if (!pItemImpl) + error = osl_File_E_NOMEM; + + if (osl_File_E_None == error) + { + osl_acquireDirectoryItem(static_cast<oslDirectoryItem>(pItemImpl)); + + CopyMemory(&pItemImpl->FindData, &aFindData, sizeof(WIN32_FIND_DATAW)); + pItemImpl->m_sFullPath = strSysFilePath; + + pItemImpl->uType = DIRECTORYITEM_FILE; + *pItem = pItemImpl; + } + + FindClose( hFind ); + } + else + error = oslTranslateFileError( GetLastError() ); + } + break; + } + + return error; +} + +oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item ) +{ + DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item); + + if ( !pItemImpl ) + return osl_File_E_INVAL; + + pItemImpl->nRefCount++; + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item ) +{ + DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item); + + if ( !pItemImpl ) + return osl_File_E_INVAL; + + if ( ! --pItemImpl->nRefCount ) + delete pItemImpl; + + return osl_File_E_None; +} + +sal_Bool +SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b) +{ + DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a); + DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b); + if (a == b) + return true; + /* same name => same item, unless renaming / moving madness has occurred */ + if (pA->m_sFullPath == pB->m_sFullPath) + return true; + + // FIXME: as/when/if this is used in anger on Windows we could + // do better here. + + return false; +} + +static bool is_floppy_A_present() +{ return (GetLogicalDrives() & 1); } + +static bool is_floppy_B_present() +{ return (GetLogicalDrives() & 2); } + +static bool is_floppy_volume_mount_point(const OUString& path) +{ + // determines if a volume mount point shows to a floppy + // disk by comparing the unique volume names + static const LPCWSTR FLOPPY_A = L"A:\\"; + static const LPCWSTR FLOPPY_B = L"B:\\"; + + OUString p(path); + osl::systemPathEnsureSeparator(p); + + WCHAR vn[51]; + if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn))) + { + WCHAR vnfloppy[51]; + if (is_floppy_A_present() && + GetVolumeNameForVolumeMountPointW(FLOPPY_A, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) && + (0 == wcscmp(vn, vnfloppy))) + return true; + + if (is_floppy_B_present() && + GetVolumeNameForVolumeMountPointW(FLOPPY_B, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) && + (0 == wcscmp(vn, vnfloppy))) + return true; + } + return false; +} + +static bool is_floppy_drive(const OUString& path) +{ + static const LPCWSTR FLOPPY_DRV_LETTERS = L"AaBb"; + + // we must take into account that even a floppy + // drive may be mounted to a directory so checking + // for the drive letter alone is not sufficient + // we must compare the unique volume name with + // that of the available floppy disks + + const sal_Unicode* pszPath = path.getStr(); + return ((wcschr(FLOPPY_DRV_LETTERS, pszPath[0]) && (L':' == pszPath[1])) || is_floppy_volume_mount_point(path)); +} + +static bool is_volume_mount_point(const OUString& path) +{ + OUString p(path); + osl::systemPathRemoveSeparator(p); + + if (is_floppy_drive(p)) + return false; + + DWORD fattr = GetFileAttributesW(o3tl::toW(p.getStr())); + if ((INVALID_FILE_ATTRIBUTES == fattr) || + !(FILE_ATTRIBUTE_REPARSE_POINT & fattr)) + return false; + + bool is_volume_root = false; + WIN32_FIND_DATAW find_data; + HANDLE h_find = FindFirstFileW(o3tl::toW(p.getStr()), &find_data); + + if (IsValidHandle(h_find) && + (FILE_ATTRIBUTE_REPARSE_POINT & find_data.dwFileAttributes) && + (IO_REPARSE_TAG_MOUNT_POINT == find_data.dwReserved0)) + { + is_volume_root = true; + } + if (IsValidHandle(h_find)) + FindClose(h_find); + return is_volume_root; +} + +static UINT get_volume_mount_point_drive_type(const OUString& path) +{ + if (0 == path.getLength()) + return GetDriveTypeW(nullptr); + + OUString p(path); + osl::systemPathEnsureSeparator(p); + + WCHAR vn[51]; + if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn))) + return GetDriveTypeW(vn); + + return DRIVE_NO_ROOT_DIR; +} + +static bool is_drivetype_request(sal_uInt32 field_mask) +{ + return (field_mask & osl_VolumeInfo_Mask_Attributes); +} + +static oslFileError osl_get_drive_type( + const OUString& path, oslVolumeInfo* pInfo) +{ + // GetDriveType fails on empty volume mount points + // see Knowledge Base Q244089 + UINT drive_type; + if (is_volume_mount_point(path)) + drive_type = get_volume_mount_point_drive_type(path); + else + drive_type = GetDriveTypeW(o3tl::toW(path.getStr())); + + if (DRIVE_NO_ROOT_DIR == drive_type) + return oslTranslateFileError(ERROR_INVALID_DRIVE); + + pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes; + + switch (drive_type) + { + case DRIVE_CDROM: + pInfo->uAttributes |= osl_Volume_Attribute_CompactDisc | osl_Volume_Attribute_Removeable; + break; + case DRIVE_REMOVABLE: + pInfo->uAttributes |= osl_Volume_Attribute_Removeable; + if (is_floppy_drive(path)) + pInfo->uAttributes |= osl_Volume_Attribute_FloppyDisk; + break; + case DRIVE_FIXED: + pInfo->uAttributes |= osl_Volume_Attribute_FixedDisk; + break; + case DRIVE_RAMDISK: + pInfo->uAttributes |= osl_Volume_Attribute_RAMDisk; + break; + case DRIVE_REMOTE: + pInfo->uAttributes |= osl_Volume_Attribute_Remote; + break; + case DRIVE_UNKNOWN: + pInfo->uAttributes = 0; + break; + default: + pInfo->uValidFields &= ~osl_VolumeInfo_Mask_Attributes; + pInfo->uAttributes = 0; + break; + } + return osl_File_E_None; +} + +static bool is_volume_space_info_request(sal_uInt32 field_mask) +{ + return (field_mask & + (osl_VolumeInfo_Mask_TotalSpace | + osl_VolumeInfo_Mask_UsedSpace | + osl_VolumeInfo_Mask_FreeSpace)); +} + +static void get_volume_space_information( + const OUString& path, oslVolumeInfo *pInfo) +{ + bool ret = GetDiskFreeSpaceExW( + o3tl::toW(path.getStr()), + reinterpret_cast<PULARGE_INTEGER>(&pInfo->uFreeSpace), + reinterpret_cast<PULARGE_INTEGER>(&pInfo->uTotalSpace), + nullptr); + + if (ret) + { + pInfo->uUsedSpace = pInfo->uTotalSpace - pInfo->uFreeSpace; + pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace | + osl_VolumeInfo_Mask_UsedSpace | + osl_VolumeInfo_Mask_FreeSpace; + } +} + +static bool is_filesystem_attributes_request(sal_uInt32 field_mask) +{ + return (field_mask & + (osl_VolumeInfo_Mask_MaxNameLength | + osl_VolumeInfo_Mask_MaxPathLength | + osl_VolumeInfo_Mask_FileSystemName | + osl_VolumeInfo_Mask_FileSystemCaseHandling)); +} + +static oslFileError get_filesystem_attributes( + const OUString& path, sal_uInt32 field_mask, oslVolumeInfo* pInfo) +{ + pInfo->uAttributes = 0; + + // osl_get_drive_type must be called first because + // this function resets osl_VolumeInfo_Mask_Attributes + // on failure + if (is_drivetype_request(field_mask)) + { + oslFileError osl_error = osl_get_drive_type(path, pInfo); + if (osl_File_E_None != osl_error) + return osl_error; + } + if (is_filesystem_attributes_request(field_mask)) + { + /* the following two parameters can not be longer than MAX_PATH+1 */ + WCHAR vn[MAX_PATH+1]; + WCHAR fsn[MAX_PATH+1]; + + DWORD serial; + DWORD mcl; + DWORD flags; + + LPCWSTR pszPath = o3tl::toW(path.getStr()); + if (GetVolumeInformationW(pszPath, vn, MAX_PATH+1, &serial, &mcl, &flags, fsn, MAX_PATH+1)) + { + // Currently sal does not use this value, instead MAX_PATH is used + pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxNameLength; + pInfo->uMaxNameLength = mcl; + + // Should the uMaxPathLength be set to 32767, "\\?\" prefix allows it + pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxPathLength; + pInfo->uMaxPathLength = MAX_PATH; + + pInfo->uValidFields |= osl_VolumeInfo_Mask_FileSystemName; + rtl_uString_newFromStr(&pInfo->ustrFileSystemName, o3tl::toU(fsn)); + + // volumes (even NTFS) will always be considered case + // insensitive because the Win32 API is not able to + // deal with case sensitive volumes see M$ Knowledge Base + // article 100625 that's why we never set the attribute + // osl_Volume_Attribute_Case_Sensitive + + if (flags & FS_CASE_IS_PRESERVED) + pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved; + + pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes; + } + } + return osl_File_E_None; +} + +static bool path_get_parent(OUString& path) +{ + OSL_PRECOND(path.lastIndexOf(SLASH) == -1, "Path must not have slashes"); + + if (!has_path_parent(path)) + { + sal_Int32 i = path.lastIndexOf(BACKSLASH); + if (-1 < i) + { + path = OUString(path.getStr(), i); + return true; + } + } + return false; +} + +static void path_travel_to_volume_root(const OUString& system_path, OUString& volume_root) +{ + OUString sys_path(system_path); + + while(!is_volume_mount_point(sys_path) && path_get_parent(sys_path)) + /**/; + + volume_root = sys_path; + osl::systemPathEnsureSeparator(volume_root); +} + +oslFileError SAL_CALL osl_getVolumeInformation( + rtl_uString *ustrURL, oslVolumeInfo *pInfo, sal_uInt32 uFieldMask ) +{ + if (!pInfo) + return osl_File_E_INVAL; + + OUString system_path; + oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), &system_path.pData, false); + + if (osl_File_E_None != error) + return error; + + OUString volume_root; + path_travel_to_volume_root(system_path, volume_root); + + pInfo->uValidFields = 0; + + if ((error = get_filesystem_attributes(volume_root, uFieldMask, pInfo)) != osl_File_E_None) + return error; + + if (is_volume_space_info_request(uFieldMask)) + get_volume_space_information(volume_root, pInfo); + + if (uFieldMask & osl_VolumeInfo_Mask_DeviceHandle) + { + error = osl_getFileURLFromSystemPath(volume_root.pData, reinterpret_cast<rtl_uString**>(&pInfo->pDeviceHandle)); + if (error != osl_File_E_None) + return error; + pInfo->uValidFields |= osl_VolumeInfo_Mask_DeviceHandle; + } + + return osl_File_E_None; +} + +static oslFileError osl_getDriveInfo( + oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask) +{ + DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item); + WCHAR cDrive[3] = L"A:"; + WCHAR cRoot[4] = L"A:\\"; + + if ( !pItemImpl ) + return osl_File_E_INVAL; + + pStatus->uValidFields = 0; + + cDrive[0] = pItemImpl->cDriveString[0]; + cRoot[0] = pItemImpl->cDriveString[0]; + + if ( uFieldMask & osl_FileStatus_Mask_FileName ) + { + if ( pItemImpl->cDriveString[0] == '\\' && pItemImpl->cDriveString[1] == '\\' ) + { + LPCWSTR lpFirstBkSlash = wcschr( &pItemImpl->cDriveString[2], '\\' ); + + if ( lpFirstBkSlash && lpFirstBkSlash[1] ) + { + LPCWSTR lpLastBkSlash = wcschr( &lpFirstBkSlash[1], '\\' ); + + if ( lpLastBkSlash ) + rtl_uString_newFromStr_WithLength( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]), lpLastBkSlash - lpFirstBkSlash - 1 ); + else + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]) ); + pStatus->uValidFields |= osl_FileStatus_Mask_FileName; + } + } + else switch ( GetDriveTypeW( cRoot ) ) + { + case DRIVE_REMOTE: + { + WCHAR szBuffer[1024]; + DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szBuffer); + DWORD dwBufsize = dwBufsizeConst; + + DWORD dwResult = WNetGetConnectionW( cDrive, szBuffer, &dwBufsize ); + if ( NO_ERROR == dwResult ) + { + WCHAR szFileName[dwBufsizeConst + 16]; + + swprintf( szFileName, L"%s [%s]", cDrive, szBuffer ); + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) ); + } + else + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) ); + } + pStatus->uValidFields |= osl_FileStatus_Mask_FileName; + break; + case DRIVE_FIXED: + { + WCHAR szVolumeNameBuffer[1024]; + DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szVolumeNameBuffer); + + if ( GetVolumeInformationW( cRoot, szVolumeNameBuffer, dwBufsizeConst, nullptr, nullptr, nullptr, nullptr, 0 ) ) + { + WCHAR szFileName[dwBufsizeConst + 16]; + + swprintf( szFileName, L"%s [%s]", cDrive, szVolumeNameBuffer ); + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) ); + } + else + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) ); + } + pStatus->uValidFields |= osl_FileStatus_Mask_FileName; + break; + case DRIVE_CDROM: + case DRIVE_REMOVABLE: + pStatus->uValidFields |= osl_FileStatus_Mask_FileName; + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cRoot) ); + break; + case DRIVE_UNKNOWN: + default: + break; + } + } + + pStatus->eType = osl_File_Type_Volume; + pStatus->uValidFields |= osl_FileStatus_Mask_Type; + + if ( uFieldMask & osl_FileStatus_Mask_FileURL ) + { + rtl_uString *ustrSystemPath = nullptr; + + rtl_uString_newFromStr( &ustrSystemPath, o3tl::toU(pItemImpl->cDriveString) ); + oslFileError error = osl_getFileURLFromSystemPath( ustrSystemPath, &pStatus->ustrFileURL ); + rtl_uString_release( ustrSystemPath ); + if (error != osl_File_E_None) + return error; + pStatus->uValidFields |= osl_FileStatus_Mask_FileURL; + } + return osl_File_E_None; +} + +static oslFileError osl_getServerInfo( + oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask ) +{ + DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item); + if ( !pItemImpl ) + return osl_File_E_INVAL; + + pStatus->uValidFields = 0; + pStatus->eType = osl_File_Type_Directory; + pStatus->uValidFields |= osl_FileStatus_Mask_Type; + + if ( uFieldMask & osl_FileStatus_Mask_FileURL ) + { + oslFileError error = osl_getFileURLFromSystemPath( pItemImpl->m_sFullPath.pData, &pStatus->ustrFileURL ); + if (error != osl_File_E_None) + return error; + pStatus->uValidFields |= osl_FileStatus_Mask_FileURL; + } + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_getFileStatus( + oslDirectoryItem Item, + oslFileStatus *pStatus, + sal_uInt32 uFieldMask ) +{ + DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item); + + if ( !pItemImpl ) + return osl_File_E_INVAL; + + switch ( pItemImpl->uType ) + { + case DIRECTORYITEM_DRIVE: + return osl_getDriveInfo( Item, pStatus, uFieldMask ); + case DIRECTORYITEM_SERVER: + return osl_getServerInfo( Item, pStatus, uFieldMask ); + default: + break; + } + + OUString sFullPath(pItemImpl->m_sFullPath); + + // Prefix long paths, windows API calls expect this prefix + // (only local paths starting with something like C: or D:) + if (sFullPath.getLength() >= MAX_PATH && isalpha(sFullPath[0]) && sFullPath[1] == ':') + sFullPath = "\\\\?\\" + sFullPath; + + if ( uFieldMask & osl_FileStatus_Mask_Validate ) + { + HANDLE hFind = FindFirstFileW( o3tl::toW(sFullPath.getStr() ), &pItemImpl->FindData ); + + if ( hFind != INVALID_HANDLE_VALUE ) + FindClose( hFind ); + else + return oslTranslateFileError( GetLastError() ); + + uFieldMask &= ~ osl_FileStatus_Mask_Validate; + } + + /* If no fields to retrieve left ignore pStatus */ + if ( !uFieldMask ) + return osl_File_E_None; + + /* Otherwise, this must be a valid pointer */ + if ( !pStatus ) + return osl_File_E_INVAL; + + if ( pStatus->uStructSize != sizeof(oslFileStatus) ) + return osl_File_E_INVAL; + + pStatus->uValidFields = 0; + + /* File time stamps */ + + if ( (uFieldMask & osl_FileStatus_Mask_ModifyTime) && + FileTimeToTimeValue( &pItemImpl->FindData.ftLastWriteTime, &pStatus->aModifyTime ) ) + pStatus->uValidFields |= osl_FileStatus_Mask_ModifyTime; + + if ( (uFieldMask & osl_FileStatus_Mask_AccessTime) && + FileTimeToTimeValue( &pItemImpl->FindData.ftLastAccessTime, &pStatus->aAccessTime ) ) + pStatus->uValidFields |= osl_FileStatus_Mask_AccessTime; + + if ( (uFieldMask & osl_FileStatus_Mask_CreationTime) && + FileTimeToTimeValue( &pItemImpl->FindData.ftCreationTime, &pStatus->aCreationTime ) ) + pStatus->uValidFields |= osl_FileStatus_Mask_CreationTime; + + /* Most of the fields are already set, regardless of required fields */ + + rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(pItemImpl->FindData.cFileName) ); + pStatus->uValidFields |= osl_FileStatus_Mask_FileName; + + if ((FILE_ATTRIBUTE_REPARSE_POINT & pItemImpl->FindData.dwFileAttributes) && + (IO_REPARSE_TAG_MOUNT_POINT == pItemImpl->FindData.dwReserved0)) + pStatus->eType = osl_File_Type_Volume; + else if (pItemImpl->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + pStatus->eType = osl_File_Type_Directory; + else + pStatus->eType = osl_File_Type_Regular; + + pStatus->uValidFields |= osl_FileStatus_Mask_Type; + + pStatus->uAttributes = pItemImpl->FindData.dwFileAttributes; + pStatus->uValidFields |= osl_FileStatus_Mask_Attributes; + + pStatus->uFileSize = static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeLow) + (static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeHigh) << 32); + pStatus->uValidFields |= osl_FileStatus_Mask_FileSize; + + if ( uFieldMask & osl_FileStatus_Mask_LinkTargetURL ) + { + oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrLinkTargetURL ); + if (error != osl_File_E_None) + return error; + + pStatus->uValidFields |= osl_FileStatus_Mask_LinkTargetURL; + } + + if ( uFieldMask & osl_FileStatus_Mask_FileURL ) + { + if ( !pItemImpl->bFullPathNormalized ) + { + ::osl::LongPathBuffer<sal_Unicode> aBuffer(MAX_LONG_PATH); + sal_uInt32 nNewLen = GetLongPathNameW(o3tl::toW(sFullPath.getStr()), o3tl::toW(aBuffer), + aBuffer.getBufSizeInSymbols()); + + if ( nNewLen ) + { + /* Capitalizes drive name (single letter). Windows file paths are processed + case-sensitively. While parsing a path, function osl_DirectoryItem has case + PATHTYPE_VOLUME for drives, and capitalizes them. That can be overwritten by + function osl_getFileStatus, in it win32 api GetLongPathNameW does no + capitalization. Thus it needs to be postprocessed.*/ + sal_Int32 nIndex = rtl_ustr_indexOfChar(aBuffer, ':'); + if (nIndex > 0) { + aBuffer[nIndex - 1] = rtl::toAsciiUpperCase(aBuffer[nIndex - 1]); + } + + pItemImpl->m_sFullPath = OUString(&*aBuffer, nNewLen); + sFullPath = pItemImpl->m_sFullPath; + pItemImpl->bFullPathNormalized = true; + } + } + + oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrFileURL ); + if (error != osl_File_E_None) + return error; + pStatus->uValidFields |= osl_FileStatus_Mask_FileURL; + } + + return osl_File_E_None; +} + +oslFileError SAL_CALL osl_setFileAttributes( + rtl_uString *ustrFileURL, + sal_uInt64 uAttributes ) +{ + oslFileError error; + OUString ustrSysPath; + DWORD dwFileAttributes; + bool fSuccess; + + // Converts the normalized path into a systempath + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileURL), &ustrSysPath.pData, false); + + if ( osl_File_E_None != error ) + return error; + + dwFileAttributes = GetFileAttributesW(o3tl::toW(ustrSysPath.getStr())); + + if ( DWORD(-1) != dwFileAttributes ) + { + dwFileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN); + + if ( uAttributes & osl_File_Attribute_ReadOnly ) + dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + + if ( uAttributes & osl_File_Attribute_Hidden ) + dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + + fSuccess = SetFileAttributesW(o3tl::toW(ustrSysPath.getStr()), dwFileAttributes); + } + else + { + fSuccess = false; + } + + if ( !fSuccess ) + error = oslTranslateFileError( GetLastError() ); + + return error; +} + +oslFileError SAL_CALL osl_setFileTime( + rtl_uString *filePath, + const TimeValue *aCreationTime, + const TimeValue *aLastAccessTime, + const TimeValue *aLastWriteTime) +{ + oslFileError error; + OUString sysPath; + FILETIME *lpCreationTime=nullptr; + FILETIME *lpLastAccessTime=nullptr; + FILETIME *lpLastWriteTime=nullptr; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + HANDLE hFile; + bool fSuccess; + + error=osl_getSystemPathFromFileURL_(OUString::unacquired(&filePath), &sysPath.pData, false); + + if (error==osl_File_E_INVAL) + return error; + + hFile=CreateFileW(o3tl::toW(sysPath.getStr()), GENERIC_WRITE, 0, nullptr , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (hFile==INVALID_HANDLE_VALUE) + return osl_File_E_NOENT; + + if (TimeValueToFileTime(aCreationTime, &ftCreationTime)) + lpCreationTime=&ftCreationTime; + + if (TimeValueToFileTime(aLastAccessTime, &ftLastAccessTime)) + lpLastAccessTime=&ftLastAccessTime; + + if (TimeValueToFileTime(aLastWriteTime, &ftLastWriteTime)) + lpLastWriteTime=&ftLastWriteTime; + + fSuccess=SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); + + CloseHandle(hFile); + + if (!fSuccess) + return osl_File_E_INVAL; + else + return osl_File_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file_error.cxx b/sal/osl/w32/file_error.cxx new file mode 100644 index 000000000..feefaca38 --- /dev/null +++ b/sal/osl/w32/file_error.cxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "file_error.hxx" +#include <winerror.h> + +namespace { + +/* OS error to oslFileError values mapping table */ +struct osl_file_error_entry +{ + unsigned long oscode; /* OS return value */ + int errnocode; /* oslFileError code */ +}; + +} + +const struct osl_file_error_entry errtable[] = { + { ERROR_SUCCESS, osl_File_E_None }, /* 0 */ + { ERROR_INVALID_FUNCTION, osl_File_E_INVAL }, /* 1 */ + { ERROR_FILE_NOT_FOUND, osl_File_E_NOENT }, /* 2 */ + { ERROR_PATH_NOT_FOUND, osl_File_E_NOENT }, /* 3 */ + { ERROR_TOO_MANY_OPEN_FILES, osl_File_E_MFILE }, /* 4 */ + { ERROR_ACCESS_DENIED, osl_File_E_ACCES }, /* 5 */ + { ERROR_INVALID_HANDLE, osl_File_E_BADF }, /* 6 */ + { ERROR_ARENA_TRASHED, osl_File_E_NOMEM }, /* 7 */ + { ERROR_NOT_ENOUGH_MEMORY, osl_File_E_NOMEM }, /* 8 */ + { ERROR_INVALID_BLOCK, osl_File_E_NOMEM }, /* 9 */ + { ERROR_BAD_ENVIRONMENT, osl_File_E_2BIG }, /* 10 */ + { ERROR_BAD_FORMAT, osl_File_E_NOEXEC }, /* 11 */ + { ERROR_INVALID_ACCESS, osl_File_E_INVAL }, /* 12 */ + { ERROR_INVALID_DATA, osl_File_E_INVAL }, /* 13 */ + { ERROR_INVALID_DRIVE, osl_File_E_NOENT }, /* 15 */ + { ERROR_CURRENT_DIRECTORY, osl_File_E_ACCES }, /* 16 */ + { ERROR_NOT_SAME_DEVICE, osl_File_E_XDEV }, /* 17 */ + { ERROR_NO_MORE_FILES, osl_File_E_NOENT }, /* 18 */ + { ERROR_NOT_READY, osl_File_E_NOTREADY }, /* 21 */ + { ERROR_SHARING_VIOLATION, osl_File_E_ACCES }, /* 32 */ + { ERROR_LOCK_VIOLATION, osl_File_E_ACCES }, /* 33 */ + { ERROR_BAD_NETPATH, osl_File_E_NOENT }, /* 53 */ + { ERROR_NETWORK_ACCESS_DENIED, osl_File_E_ACCES }, /* 65 */ + { ERROR_BAD_NET_NAME, osl_File_E_NOENT }, /* 67 */ + { ERROR_FILE_EXISTS, osl_File_E_EXIST }, /* 80 */ + { ERROR_CANNOT_MAKE, osl_File_E_ACCES }, /* 82 */ + { ERROR_FAIL_I24, osl_File_E_ACCES }, /* 83 */ + { ERROR_INVALID_PARAMETER, osl_File_E_INVAL }, /* 87 */ + { ERROR_NO_PROC_SLOTS, osl_File_E_AGAIN }, /* 89 */ + { ERROR_DRIVE_LOCKED, osl_File_E_ACCES }, /* 108 */ + { ERROR_BROKEN_PIPE, osl_File_E_PIPE }, /* 109 */ + { ERROR_DISK_FULL, osl_File_E_NOSPC }, /* 112 */ + { ERROR_INVALID_TARGET_HANDLE, osl_File_E_BADF }, /* 114 */ + { ERROR_INVALID_NAME, osl_File_E_NOENT }, /* 123 */ + { ERROR_INVALID_HANDLE, osl_File_E_INVAL }, /* 124 */ + { ERROR_WAIT_NO_CHILDREN, osl_File_E_CHILD }, /* 128 */ + { ERROR_CHILD_NOT_COMPLETE, osl_File_E_CHILD }, /* 129 */ + { ERROR_DIRECT_ACCESS_HANDLE, osl_File_E_BADF }, /* 130 */ + { ERROR_NEGATIVE_SEEK, osl_File_E_INVAL }, /* 131 */ + { ERROR_SEEK_ON_DEVICE, osl_File_E_ACCES }, /* 132 */ + { ERROR_DIR_NOT_EMPTY, osl_File_E_NOTEMPTY }, /* 145 */ + { ERROR_NOT_LOCKED, osl_File_E_ACCES }, /* 158 */ + { ERROR_BAD_PATHNAME, osl_File_E_NOENT }, /* 161 */ + { ERROR_MAX_THRDS_REACHED, osl_File_E_AGAIN }, /* 164 */ + { ERROR_LOCK_FAILED, osl_File_E_ACCES }, /* 167 */ + { ERROR_ALREADY_EXISTS, osl_File_E_EXIST }, /* 183 */ + { ERROR_FILENAME_EXCED_RANGE, osl_File_E_NOENT }, /* 206 */ + { ERROR_NESTING_NOT_ALLOWED, osl_File_E_AGAIN }, /* 215 */ + { ERROR_FILE_CHECKED_OUT, osl_File_E_ACCES }, /* 220 */ + { ERROR_DIRECTORY, osl_File_E_NOTDIR }, /* 267 */ + { ERROR_NOT_ENOUGH_QUOTA, osl_File_E_NOMEM }, /* 1816 */ + { ERROR_CANT_ACCESS_FILE, osl_File_E_ACCES }, /* 1920 */ + { ERROR_UNEXP_NET_ERR, osl_File_E_NETWORK } /* 59 */ +}; + +/* The following two constants must be the minimum and maximum + values in the (contiguous) range of osl_File_E_xec Failure errors. +*/ +#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG +#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN + +/* These are the low and high value in the range of errors that are + access violations +*/ +#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT +#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED + +oslFileError oslTranslateFileError (/*DWORD*/ unsigned long dwError) +{ + static const int n = SAL_N_ELEMENTS(errtable); + + int i; + for (i = 0; i < n; ++i ) + { + if (dwError == errtable[i].oscode) + return static_cast<oslFileError>(errtable[i].errnocode); + } + + /* The error code wasn't in the table. We check for a range of + osl_File_E_ACCES errors or exec failure errors (ENOEXEC). + Otherwise osl_File_E_INVAL is returned. + */ + if ( (dwError >= MIN_EACCES_RANGE) && (dwError <= MAX_EACCES_RANGE) ) + return osl_File_E_ACCES; + else if ( (dwError >= MIN_EXEC_ERROR) && (dwError <= MAX_EXEC_ERROR) ) + return osl_File_E_NOEXEC; + else + return osl_File_E_INVAL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file_error.hxx b/sal/osl/w32/file_error.hxx new file mode 100644 index 000000000..4da87c132 --- /dev/null +++ b/sal/osl/w32/file_error.hxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_OSL_W32_FILE_ERROR_HXX +#define INCLUDED_SAL_OSL_W32_FILE_ERROR_HXX + +#include <osl/file.h> + +oslFileError oslTranslateFileError(/*DWORD*/ unsigned long dwError); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file_url.cxx b/sal/osl/w32/file_url.cxx new file mode 100644 index 000000000..8c525042d --- /dev/null +++ b/sal/osl/w32/file_url.cxx @@ -0,0 +1,990 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <algorithm> +#include <optional> +#include <stack> +#include <string_view> + +#include <systools/win32/uwinapi.h> + +#include "file_url.hxx" +#include "file_error.hxx" + +#include <rtl/alloc.h> +#include <rtl/character.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/mutex.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/string_view.hxx> + +#include "path_helper.hxx" + +// FileURL functions + +namespace +{ +constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH = u"\\\\.\\"; +constexpr std::u16string_view WSTR_LONG_PATH_PREFIX = u"\\\\?\\"; +constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC = u"\\\\?\\UNC\\"; + +// Internal functions that expect only backslashes as path separators + +bool startsWithDriveColon(std::u16string_view s) +{ + return s.length() >= 2 && rtl::isAsciiAlpha(s[0]) && s[1] == ':'; +} + +bool startsWithDriveColonSlash(std::u16string_view s) +{ + return s.length() >= 3 && startsWithDriveColon(s) && s[2] == '\\'; +} + +bool startsWithSlashSlash(std::u16string_view s) { return o3tl::starts_with(s, u"\\\\"); } + +// An absolute path starts either with \\ (an UNC or device path like \\.\ or \\?\) +// or with a ASCII alpha character followed by colon followed by backslash. +bool isAbsolute(std::u16string_view s) +{ + return startsWithSlashSlash(s) || startsWithDriveColonSlash(s); +} + +bool onSameDrive(std::u16string_view s1, std::u16string_view s2) +{ + assert(startsWithDriveColon(s1) && startsWithDriveColon(s2)); + return rtl::toAsciiUpperCase(s1[0]) == rtl::toAsciiUpperCase(s2[0]) && s1[1] == s2[1]; +} + +sal_Int32 getRootLength(std::u16string_view path) +{ + assert(isAbsolute(path)); + size_t nResult = 0; + if (startsWithSlashSlash(path)) + { + // Cases: + // 1. Device UNC: \\?\UNC\server\share or \\.\UNC\server\share + // 2. Non-device UNC: \\server\share + // 3. Device non-UNC: \\?\C: or \\.\C: + bool bUNC = false; + if (path.length() > 3 && (path[2] == '.' || path[2] == '?') && path[3] == '\\') + { + if (path.substr(4, 4) == u"UNC\\") + { + // \\?\UNC\server\share or \\.\UNC\server\share + nResult = 8; + bUNC = true; + } + else + { + // \\?\C: or \\.\C: + assert(startsWithDriveColon(path.substr(4))); + nResult = 6; + } + } + else + { + // \\server\share + nResult = 2; + bUNC = true; + } + if (bUNC) + { + // \\?\UNC\server\share or \\.\UNC\server\share or \\server\share + assert(nResult < path.length() && path[nResult] != '\\'); + // Skip server name and share name + for (int nSlashes = 0; nResult < path.length(); ++nResult) + { + if (path[nResult] == '\\' && ++nSlashes == 2) + break; + } + } + } + else + { + // C: + assert(startsWithDriveColon(path)); + nResult = 2; + } + return std::min(nResult, path.length()); +} + +std::u16string_view pathView(std::u16string_view path, bool bOnlyRoot) +{ + return bOnlyRoot ? path.substr(0, getRootLength(path)) : path; +} + +OUString combinePath(std::u16string_view basePath, std::u16string_view relPath) +{ + const bool needSep = !o3tl::ends_with(basePath, u'\\'); + const auto sSeparator = needSep ? std::u16string_view(u"\\") : std::u16string_view(); + if (o3tl::starts_with(relPath, u'\\')) + relPath.remove_prefix(1); // avoid two adjacent backslashes + return OUString::Concat(basePath) + sSeparator + relPath; +} + +OUString removeRelativeParts(const OUString& p) +{ + const sal_Int32 rootPos = getRootLength(p); + OUStringBuffer buf(p.getLength()); + buf.append(p.subView(0, rootPos)); + std::stack<sal_Int32> partPositions; + bool bAfterSlash = false; + for (sal_Int32 i = rootPos; i < p.getLength(); ++i) + { + sal_Unicode c = p[i]; + if (c == '\\') + { + if (i + 1 < p.getLength() && p[i + 1] == '.') + { + if (i + 2 == p.getLength() || p[i + 2] == '\\') + { + // 1. Skip current directory (\.\ or trailing \.) + ++i; // process next slash: it may start another "\.\" + } + else if (p[i + 2] == '.' && (i + 3 == p.getLength() || p[i + 3] == '\\')) + { + // 2. For parent directory (\..\), drop previous part and skip + if (bAfterSlash && partPositions.size()) + partPositions.pop(); + sal_Int32 nParentPos = partPositions.size() ? partPositions.top() : rootPos; + if (partPositions.size()) + partPositions.pop(); + buf.truncate(nParentPos); + bAfterSlash = false; // we have just removed slash after parent part + i += 2; // process next slash: it may start another "\.\" + } + } + if (bAfterSlash) + continue; // 3. Skip double backslashes (\\) + partPositions.push(buf.getLength()); + bAfterSlash = true; + } + else + bAfterSlash = false; + + buf.append(c); + } + return buf.makeStringAndClear(); +} +} + +static bool IsValidFilePathComponent( + std::optional<std::u16string_view>& roComponent, + DWORD dwFlags) +{ + assert(roComponent); + auto lpComponentEnd = roComponent->end(); + auto lpCurrent = roComponent->begin(); + bool bValid = lpCurrent != lpComponentEnd; // Empty components are not allowed + sal_Unicode cLast = 0; + + while (bValid) + { + /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */ + if (lpCurrent - roComponent->begin() >= MAX_PATH) + { + bValid = false; + break; + } + + switch ( *lpCurrent ) + { + /* Both backslash and slash determine the end of a path component */ + case '\0': + case '/': + case '\\': + switch ( cLast ) + { + /* Component must not end with '.' or blank and can't be empty */ + + case '.': + if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE ) + { + if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) || + 1 == lpCurrent - roComponent->begin() ) + { + /* Either do allow periods anywhere, or current directory */ + lpComponentEnd = lpCurrent; + break; + } + else if ( 2 == lpCurrent - roComponent->begin() && '.' == roComponent->front() ) + { + /* Parent directory is O.K. */ + lpComponentEnd = lpCurrent; + break; + } + } + [[fallthrough]]; + case 0: + case ' ': + if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD ) + lpComponentEnd = lpCurrent; + else + bValid = false; + break; + default: + lpComponentEnd = lpCurrent; + break; + } + break; + /* The following characters are reserved */ + case '?': + case '*': + case '<': + case '>': + case '\"': + case '|': + case ':': + bValid = false; + break; + default: + /* Characters below ASCII 32 are not allowed */ + if ( *lpCurrent < ' ' ) + bValid = false; + break; + } + + if (lpCurrent != lpComponentEnd) + cLast = *lpCurrent++; + + if (lpCurrent == lpComponentEnd) + break; + } + + if ( bValid ) + { + // Empty components are not allowed + if (lpComponentEnd - roComponent->begin() < 1) + bValid = false; + // If we reached the end of the string nullopt is returned + else if (lpComponentEnd == roComponent->end()) + roComponent.reset(); + else + roComponent->remove_prefix(lpComponentEnd - roComponent->begin()); + } + + return bValid; +} + +static sal_Int32 countInitialSeparators(std::u16string_view path) { + size_t n = 0; + while (n < path.length() && (path[n] == '\\' || path[n] == '/')) + ++n; + return n; +} + +DWORD IsValidFilePath(const OUString& path, DWORD dwFlags, OUString* corrected) +{ + std::optional<std::u16string_view> oComponent = path; + bool bValid = true; + DWORD dwPathType = PATHTYPE_ERROR; + + if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE ) + dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE; + + DWORD dwCandidatPathType = PATHTYPE_ERROR; + + if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC)) + { + /* This is long path in UNC notation */ + oComponent = path.subView(WSTR_LONG_PATH_PREFIX_UNC.size()); + dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH; + } + else if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX)) + { + /* This is long path */ + oComponent = path.subView(WSTR_LONG_PATH_PREFIX.size()); + + if (startsWithDriveColon(*oComponent)) + { + oComponent->remove_prefix(2); + dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH; + } + } + else if ( 2 == countInitialSeparators(path) ) + { + /* The UNC path notation */ + oComponent = path.subView(2); + dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC; + } + else if (startsWithDriveColon(path)) + { + /* Local path verification. Must start with <drive>: */ + oComponent = path.subView(2); + dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL; + } + + if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC ) + { + bValid = IsValidFilePathComponent(oComponent, VALIDATEPATH_ALLOW_ELLIPSE); + + /* So far we have a valid servername. Now let's see if we also have a network resource */ + + dwPathType = dwCandidatPathType; + + if ( bValid ) + { + if (oComponent) + { + oComponent->remove_prefix(1); + if (oComponent->empty()) + oComponent.reset(); + } + + if (!oComponent) + { + dwPathType |= PATHTYPE_IS_SERVER; + } + else + { + /* Now test the network resource */ + + bValid = IsValidFilePathComponent(oComponent, 0); + + /* If we now reached the end of the path, everything is O.K. */ + + if (bValid) + { + if (oComponent) + { + oComponent->remove_prefix(1); + if (oComponent->empty()) + oComponent.reset(); + } + if (!oComponent) + dwPathType |= PATHTYPE_IS_VOLUME; + } + } + } + } + else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL ) + { + if (1 == countInitialSeparators(*oComponent)) + oComponent->remove_prefix(1); + else if (!oComponent->empty()) + bValid = false; + + dwPathType = dwCandidatPathType; + + /* Now we are behind the backslash or it was a simple drive without backslash */ + + if (bValid && oComponent->empty()) + { + oComponent.reset(); + dwPathType |= PATHTYPE_IS_VOLUME; + } + } + else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE ) + { + /* Can be a relative path */ + oComponent = path; + + /* Relative path can start with a backslash */ + + if (1 == countInitialSeparators(*oComponent)) + { + oComponent->remove_prefix(1); + if (oComponent->empty()) + oComponent.reset(); + } + + dwPathType = PATHTYPE_RELATIVE; + } + else + { + /* Anything else is an error */ + bValid = false; + } + + /* Now validate each component of the path */ + OUString lastCorrected = path; + while (bValid && oComponent) + { + // Correct path by merging consecutive slashes: + if (o3tl::starts_with(*oComponent, u"\\") && corrected != nullptr) { + sal_Int32 i = oComponent->data() - lastCorrected.getStr(); + *corrected = lastCorrected.replaceAt(i, 1, {}); + //TODO: handle out-of-memory + lastCorrected = *corrected; + oComponent = lastCorrected.subView(i); + } + + bValid = IsValidFilePathComponent(oComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD); + + if (bValid && oComponent) + { + oComponent->remove_prefix(1); + + /* If the string behind the backslash is empty, we've done */ + + if (oComponent->empty()) + oComponent.reset(); + } + } + + /* The path can be longer than MAX_PATH only in case it has the longpath prefix */ + if (bValid && !(dwPathType & PATHTYPE_IS_LONGPATH) && path.getLength() >= MAX_PATH) + { + bValid = false; + } + + return bValid ? dwPathType : PATHTYPE_ERROR; +} + +static std::optional<OUString> osl_decodeURL_(const OString& sUTF8) +{ + const char *pSrcEnd; + const char *pSrc; + bool bValidEncoded = true; /* Assume success */ + + /* The resulting decoded string length is shorter or equal to the source length */ + + const sal_Int32 nSrcLen = sUTF8.getLength(); + OStringBuffer aBuffer(nSrcLen + 1); + + pSrc = sUTF8.getStr(); + pSrcEnd = pSrc + nSrcLen; + + /* Now decode the URL what should result in a UTF-8 string */ + while ( bValidEncoded && pSrc < pSrcEnd ) + { + switch ( *pSrc ) + { + case '%': + { + char aToken[3]; + char aChar; + + pSrc++; + aToken[0] = *pSrc++; + aToken[1] = *pSrc++; + aToken[2] = 0; + + aChar = static_cast<char>(strtoul( aToken, nullptr, 16 )); + + /* The chars are path delimiters and must not be encoded */ + + if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar ) + bValidEncoded = false; + else + aBuffer.append(aChar); + } + break; + case '\0': + case '#': + case '?': + bValidEncoded = false; + break; + default: + aBuffer.append(*pSrc++); + break; + } + } + + return bValidEncoded ? OUString(aBuffer.getStr(), aBuffer.getLength(), RTL_TEXTENCODING_UTF8) + : std::optional<OUString>(); +} + +static OUString osl_encodeURL_(std::u16string_view sURL) +{ + /* Encode non ascii characters within the URL */ + + const char *pURLScan; + sal_Int32 nURLScanLen; + sal_Int32 nURLScanCount; + + OString sUTF8 = OUStringToOString(sURL, RTL_TEXTENCODING_UTF8); + + OUStringBuffer sEncodedURL(sUTF8.getLength() * 3 + 1); + pURLScan = sUTF8.getStr(); + nURLScanLen = sUTF8.getLength(); + nURLScanCount = 0; + + while ( nURLScanCount < nURLScanLen ) + { + char cCurrent = *pURLScan; + switch ( cCurrent ) + { + default: + if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) ) + { + char buf[3]; + sprintf( buf, "%02X", static_cast<unsigned char>(cCurrent) ); + sEncodedURL.append('%').appendAscii(buf, 2); + break; + } + [[fallthrough]]; + case '!': + case '\'': + case '(': + case ')': + case '*': + case '-': + case '.': + case '_': + case '~': + case '$': + case '&': + case '+': + case ',': + case '=': + case '@': + case ':': + case '/': + case '\\': + case '|': + sEncodedURL.appendAscii(&cCurrent, 1); + break; + case 0: + break; + } + + pURLScan++; + nURLScanCount++; + } + + return sEncodedURL.makeStringAndClear(); +} + +// A helper that makes sure that for existing part of the path, the case is correct. +// Unlike GetLongPathNameW that it wraps, this function does not require the path to exist. +static OUString GetCaseCorrectPathName(std::u16string_view sysPath) +{ + // Prepare a null-terminated string first. + // Neither OUString, nor u16string_view are guaranteed to be null-terminated + osl::LongPathBuffer<wchar_t> szPath(sysPath.size() + WSTR_LONG_PATH_PREFIX_UNC.size() + 1); + wchar_t* const pPath = szPath; + wchar_t* pEnd = pPath; + size_t sysPathOffset = 0; + if (sysPath.size() >= MAX_PATH && isAbsolute(sysPath) + && !o3tl::starts_with(sysPath, WSTR_LONG_PATH_PREFIX)) + { + // Allow GetLongPathNameW consume long paths + std::u16string_view prefix = WSTR_LONG_PATH_PREFIX; + if (startsWithSlashSlash(sysPath)) + { + sysPathOffset = 2; // skip leading "\\" + prefix = WSTR_LONG_PATH_PREFIX_UNC; + } + pEnd = std::copy(prefix.begin(), prefix.end(), pEnd); + } + wchar_t* const pStart = pEnd; + pEnd = std::copy(sysPath.begin() + sysPathOffset, sysPath.end(), pStart); + *pEnd = 0; + osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH); + while (pEnd > pStart) + { + std::u16string_view curPath(o3tl::toU(pPath), pEnd - pPath); + if (curPath == u"\\\\" || curPath == WSTR_SYSTEM_ROOT_PATH + || curPath == WSTR_LONG_PATH_PREFIX + || o3tl::equalsIgnoreAsciiCase(curPath, WSTR_LONG_PATH_PREFIX_UNC)) + break; // Do not check if the special path prefix exists itself + + DWORD nNewLen = GetLongPathNameW(pPath, aBuf, aBuf.getBufSizeInSymbols()); + if (nNewLen == 0) + { + // Error? + const DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + { + // Check the base path; skip possible trailing separator + size_t sepPos = curPath.substr(0, curPath.size() - 1).rfind(u'\\'); + if (sepPos != std::u16string_view::npos) + { + pEnd = pPath + sepPos; + *pEnd = 0; + continue; + } + } + else + { + SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code " + << err << " processing path " << OUString(curPath)); + } + break; // All other errors, or no separators left + } + assert(nNewLen < aBuf.getBufSizeInSymbols()); + // Combine the case-correct leading part with the non-existing trailing part + return OUString::Concat(std::u16string_view(o3tl::toU(aBuf), nNewLen)) + + sysPath.substr(pEnd - pStart + sysPathOffset); + }; + return OUString(sysPath); // We found no existing parts - just assume it's OK +} + +oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString **pustrPath, bool bAllowRelative) +{ + OUString sTempPath; + oslFileError nError = osl_File_E_INVAL; /* Assume failure */ + + /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from + having a mixed encoded URL later */ + + OString sUTF8 = OUStringToOString(strURL, RTL_TEXTENCODING_UTF8); + + /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */ + + SAL_WARN_IF( + sUTF8.getLength() != strURL.getLength() && + strURL.matchIgnoreAsciiCase("file:\\") + , "sal.osl" + ,"osl_getSystemPathFromFileURL: \"" << strURL << "\" is not encoded !!!"); + + if (auto sDecodedURL = osl_decodeURL_(sUTF8)) + { + /* Replace backslashes and pipes */ + + sDecodedURL = sDecodedURL->replace('/', '\\').replace('|', ':'); + + /* Must start with "file:/" */ + if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\") ) + { + sal_uInt32 nSkip; + + if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\\\") ) + nSkip = 8; + else if ( + sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\localhost\\") || + sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\127.0.0.1\\") + ) + nSkip = 17; + else if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\") ) + nSkip = 5; + else + nSkip = 6; + + const sal_uInt32 nDecodedLen = sDecodedURL->getLength(); + + /* Indicates local root */ + if ( nDecodedLen == nSkip ) + sTempPath = WSTR_SYSTEM_ROOT_PATH; + else + { + /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */ + if ( nDecodedLen - nSkip <= MAX_PATH - 12 ) + { + sTempPath = sDecodedURL->subView(nSkip); + } + else + { + sDecodedURL = GetCaseCorrectPathName(sDecodedURL->subView(nSkip)); + if (sDecodedURL->getLength() <= MAX_PATH - 12 + || sDecodedURL->startsWith(WSTR_SYSTEM_ROOT_PATH) + || sDecodedURL->startsWith(WSTR_LONG_PATH_PREFIX)) + { + sTempPath = *sDecodedURL; + } + else if (sDecodedURL->startsWith("\\\\")) + { + /* it should be an UNC path, use the according prefix */ + sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + sDecodedURL->subView(2); + } + else + { + sTempPath = WSTR_LONG_PATH_PREFIX + *sDecodedURL; + } + } + } + + if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath)) + nError = osl_File_E_None; + } + else if ( bAllowRelative ) /* This maybe a relative file URL */ + { + /* In future the relative path could be converted to absolute if it is too long */ + sTempPath = *sDecodedURL; + + if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath)) + nError = osl_File_E_None; + } + else + SAL_INFO_IF(nError, "sal.osl", + "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not an absolute FileURL"); + + } + + if ( osl_File_E_None == nError ) + rtl_uString_assign(pustrPath, sTempPath.pData); + + SAL_INFO_IF(nError, "sal.osl", + "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not a FileURL"); + + return nError; +} + +oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL ) +{ + oslFileError nError = osl_File_E_INVAL; /* Assume failure */ + OUString sTempURL; + DWORD dwPathType = PATHTYPE_ERROR; + + if (strPath) + dwPathType = IsValidFilePath(OUString::unacquired(&strPath), VALIDATEPATH_ALLOW_RELATIVE, nullptr); + + if (dwPathType) + { + OUString sTempPath; + const OUString& sPath = OUString::unacquired(&strPath); + + if ( dwPathType & PATHTYPE_IS_LONGPATH ) + { + /* the path has the longpath prefix, lets remove it */ + switch ( dwPathType & PATHTYPE_MASK_TYPE ) + { + case PATHTYPE_ABSOLUTE_UNC: + static_assert(WSTR_LONG_PATH_PREFIX_UNC.size() == 8, + "Unexpected long path UNC prefix!"); + + /* generate the normal UNC path */ + sTempPath = "\\\\" + sPath.copy(8).replace('\\', '/'); + break; + + case PATHTYPE_ABSOLUTE_LOCAL: + static_assert(WSTR_LONG_PATH_PREFIX.size() == 4, + "Unexpected long path prefix!"); + + /* generate the normal path */ + sTempPath = sPath.copy(4).replace('\\', '/'); + break; + + default: + OSL_FAIL( "Unexpected long path format!" ); + sTempPath = sPath.replace('\\', '/'); + break; + } + } + else + { + /* Replace backslashes */ + sTempPath = sPath.replace('\\', '/'); + } + + switch ( dwPathType & PATHTYPE_MASK_TYPE ) + { + case PATHTYPE_RELATIVE: + sTempURL = sTempPath; + nError = osl_File_E_None; + break; + case PATHTYPE_ABSOLUTE_UNC: + sTempURL = "file:" + sTempPath; + nError = osl_File_E_None; + break; + case PATHTYPE_ABSOLUTE_LOCAL: + sTempURL = "file:///" + sTempPath; + nError = osl_File_E_None; + break; + default: + break; + } + } + + if ( osl_File_E_None == nError ) + { + /* Encode the URL */ + rtl_uString_assign(pstrURL, osl_encodeURL_(sTempURL).pData); + OSL_ASSERT(*pstrURL != nullptr); + } + + SAL_INFO_IF(nError, "sal.osl", + "osl_getFileURLFromSystemPath: \"" << OUString::unacquired(&strPath) << "\" is not a systemPath"); + return nError; +} + +oslFileError SAL_CALL osl_getSystemPathFromFileURL( + rtl_uString *ustrURL, rtl_uString **pustrPath) +{ + return osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), pustrPath, true); +} + +oslFileError SAL_CALL osl_searchFileURL( + rtl_uString *ustrFileName, + rtl_uString *ustrSystemSearchPath, + rtl_uString **pustrPath) +{ + OUString ustrUNCPath; + OUString ustrSysPath; + oslFileError error; + + /* First try to interpret the file name as a URL even a relative one */ + error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileName), &ustrUNCPath.pData, true); + + /* So far we either have an UNC path or something invalid + Now create a system path */ + if ( osl_File_E_None == error ) + error = osl_getSystemPathFromFileURL_(ustrUNCPath, &ustrSysPath.pData, true); + + if ( osl_File_E_None == error ) + { + DWORD nBufferLength; + DWORD dwResult; + LPWSTR lpBuffer = nullptr; + LPWSTR lpszFilePart; + + /* Repeat calling SearchPath ... + Start with MAX_PATH for the buffer. In most cases this + will be enough and does not force the loop to run twice */ + dwResult = MAX_PATH; + + do + { + /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */ + LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr; + LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath.getStr()); + + /* Allocate space for buffer according to previous returned count of required chars */ + /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */ + nBufferLength = dwResult + 1; + lpBuffer = lpBuffer ? + static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) : + static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR))); + + dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart ); + } while ( dwResult && dwResult >= nBufferLength ); + + /* ... until an error occurs or buffer is large enough. + dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */ + + if ( dwResult ) + { + ustrSysPath = o3tl::toU(lpBuffer); + error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath); + } + else + { + WIN32_FIND_DATAW aFindFileData; + HANDLE hFind; + + /* something went wrong, perhaps the path was absolute */ + error = oslTranslateFileError( GetLastError() ); + + hFind = FindFirstFileW(o3tl::toW(ustrSysPath.getStr()), &aFindFileData); + + if ( IsValidHandle(hFind) ) + { + error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath); + FindClose( hFind ); + } + } + + free( lpBuffer ); + } + + return error; +} + +oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL ) +{ + oslFileError eError = osl_File_E_None; + OUString ustrRelSysPath; + OUString ustrBaseSysPath; + + if ( ustrBaseURL && ustrBaseURL->length ) + { + eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrBaseURL), &ustrBaseSysPath.pData, false); + OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" ); + } + if (eError == osl_File_E_None) + { + eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrRelativeURL), &ustrRelSysPath.pData, + !ustrBaseSysPath.isEmpty()); + OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" ); + } + + if ( !eError ) + { + OUString sResultPath; +/*@@@ToDo + The whole FileURL implementation should be merged + with the rtl/uri class. +*/ + // If ustrRelSysPath is absolute, we don't need ustrBaseSysPath. + if (!ustrBaseSysPath.isEmpty() && !isAbsolute(ustrRelSysPath)) + { + // ustrBaseSysPath is known here to be a valid absolute path -> its first two characters + // are ASCII (either alpha + colon, or double backslashes) + + // Don't use SetCurrentDirectoryW together with GetFullPathNameW, because: + // (a) it needs synchronization and may affect threads that may access relative paths; + // (b) it would give wrong results for non-existing base path (allowed by RFC2396). + + if (startsWithDriveColon(ustrRelSysPath)) + { + // Special case: a path relative to a specific drive's current directory. + // Should we error out here? + + // If ustrBaseSysPath is on the same drive as ustrRelSysPath, then take base path + // as is; otherwise, use current directory on ustrRelSysPath's drive as base path + if (onSameDrive(ustrRelSysPath, ustrBaseSysPath)) + { + sResultPath = combinePath(ustrBaseSysPath, ustrRelSysPath.subView(2)); + } + else + { + // Call GetFullPathNameW to get current directory on ustrRelSysPath's drive + wchar_t baseDrive[3] = { ustrRelSysPath[0], ':' }; // just "C:" + osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH); + DWORD dwResult + = GetFullPathNameW(baseDrive, aBuf.getBufSizeInSymbols(), aBuf, nullptr); + if (dwResult) + { + if (dwResult >= aBuf.getBufSizeInSymbols()) + eError = osl_File_E_INVAL; + else + sResultPath = combinePath(o3tl::toU(aBuf), ustrRelSysPath.subView(2)); + } + else + eError = oslTranslateFileError(GetLastError()); + } + } + else + { + // Is this a rooted relative path (starting with a backslash)? + // Then we need only root from base. E.g., + // ustrBaseSysPath is "\\server\share\path1\" and ustrRelSysPath is "\path2\to\file" + // => \\server\share\path2\to\file + // ustrBaseSysPath is "D:\path1\" and ustrRelSysPath is "\path2\to\file" + // => D:\path2\to\file + auto sBaseView(pathView(ustrBaseSysPath, ustrRelSysPath.startsWith("\\"))); + sResultPath = combinePath(sBaseView, ustrRelSysPath); + } + } + else + sResultPath = ustrRelSysPath; + + if (eError == osl_File_E_None) + { + sResultPath = removeRelativeParts(sResultPath); + eError = osl_getFileURLFromSystemPath(sResultPath.pData, pustrAbsoluteURL); + } + } + + return eError; +} + +oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid ) +{ + rtl_uString_newFromString(strValid, strRequested); + return osl_File_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/file_url.hxx b/sal/osl/w32/file_url.hxx new file mode 100644 index 000000000..86ce27060 --- /dev/null +++ b/sal/osl/w32/file_url.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_OSL_W32_FILE_URL_HXX +#define INCLUDED_SAL_OSL_W32_FILE_URL_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <osl/file.h> +#include <osl/mutex.hxx> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#define PATHTYPE_ERROR 0 +#define PATHTYPE_RELATIVE 1 +#define PATHTYPE_ABSOLUTE_UNC 2 +#define PATHTYPE_ABSOLUTE_LOCAL 3 +#define PATHTYPE_MASK_TYPE 0xFF +#define PATHTYPE_IS_VOLUME 0x0100 +#define PATHTYPE_IS_SERVER 0x0200 +#define PATHTYPE_IS_LONGPATH 0x0400 + +#define VALIDATEPATH_NORMAL 0x0000 +#define VALIDATEPATH_ALLOW_ELLIPSE 0x0002 +#define VALIDATEPATH_ALLOW_RELATIVE 0x0004 +#define VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD 0x0010 + +#define MAX_LONG_PATH 32767 + +DWORD IsValidFilePath ( + const OUString& path, + DWORD dwFlags, + OUString* corrected +); + +oslFileError osl_getSystemPathFromFileURL_ ( + const OUString& strURL, + rtl_uString** pustrPath, + bool bAllowRelative +); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/filetime.hxx b/sal/osl/w32/filetime.hxx new file mode 100644 index 000000000..dc355591a --- /dev/null +++ b/sal/osl/w32/filetime.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SAL_OSL_W32_FILETIME_HXX +#define INCLUDED_SAL_OSL_W32_FILETIME_HXX + +#include <sal/config.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include <osl/time.h> + +BOOL TimeValueToFileTime(TimeValue const* cpTimeVal, FILETIME* pFTime); + +BOOL FileTimeToTimeValue(FILETIME const* cpFTime, TimeValue* pTimeVal); + +namespace osl::detail +{ +inline __int64 getFiletime(FILETIME const& ft) +{ + return (DWORD64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +} + +inline void setFiletime(FILETIME& ft, __int64 value) +{ + ft.dwHighDateTime = value >> 32; + ft.dwLowDateTime = value & 0xFFFFFFFF; +} +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sal/osl/w32/interlck.cxx b/sal/osl/w32/interlck.cxx new file mode 100644 index 000000000..b9ecf6d78 --- /dev/null +++ b/sal/osl/w32/interlck.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" + +#include <osl/interlck.h> + +oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount) +{ + return InterlockedIncrement(pCount); +} + +oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount) +{ + return InterlockedDecrement(pCount); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/memory.cxx b/sal/osl/w32/memory.cxx new file mode 100644 index 000000000..1f1a5b516 --- /dev/null +++ b/sal/osl/w32/memory.cxx @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <oslmemory.h> + +#include <malloc.h> + +void* osl_aligned_alloc( sal_Size align, sal_Size size ) +{ + return _aligned_malloc(size, align); +} + +void osl_aligned_free( void* p ) +{ + _aligned_free(p); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/module.cxx b/sal/osl/w32/module.cxx new file mode 100644 index 000000000..8379e14b2 --- /dev/null +++ b/sal/osl/w32/module.cxx @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" + +#include "file_url.hxx" +#include "path_helper.hxx" + +#include <osl/module.h> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <osl/file.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <vector> + +/* + under WIN32, we use the void* oslModule + as a WIN32 HANDLE (which is also a 32-bit value) +*/ + +oslModule SAL_CALL osl_loadModule(rtl_uString *strModuleName, sal_Int32 /*nRtldMode*/ ) +{ + HMODULE h; +#if OSL_DEBUG_LEVEL < 2 + UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); +#endif + rtl_uString* Module = nullptr; + oslModule ret = nullptr; + oslFileError nError; + + SAL_INFO( "sal.osl", "osl_loadModule: " << OUString(strModuleName) ); + OSL_ASSERT(strModuleName); + + nError = osl_getSystemPathFromFileURL(strModuleName, &Module); + + if ( osl_File_E_None != nError ) + rtl_uString_assign(&Module, strModuleName); + + h = LoadLibraryW(o3tl::toW(Module->buffer)); + + if (h == nullptr) + h = LoadLibraryExW(o3tl::toW(Module->buffer), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); + + // In case of long path names (\\?\c:\...) try to shorten the filename. + // LoadLibrary cannot handle file names which exceed 260 letters. + // In case the path is too long, the function will fail. However, the error + // code can be different. For example, it returned ERROR_FILENAME_EXCED_RANGE + // on Windows XP and ERROR_INSUFFICIENT_BUFFER on Windows 7 (64bit) + if (h == nullptr && Module->length > 260) + { + std::vector<WCHAR> vec(Module->length + 1); + DWORD len = GetShortPathNameW(o3tl::toW(Module->buffer), vec.data(), Module->length + 1); + if (len ) + { + h = LoadLibraryW(vec.data()); + + if (h == nullptr) + h = LoadLibraryExW(vec.data(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); + } + } + + ret = static_cast<oslModule>(h); + rtl_uString_release(Module); +#if OSL_DEBUG_LEVEL < 2 + SetErrorMode(errorMode); +#endif + + return ret; +} + +oslModule SAL_CALL osl_loadModuleAscii(const char *pModuleName, sal_Int32 ) +{ + HMODULE h; + UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + oslModule ret = nullptr; + + SAL_INFO( "sal.osl", "osl_loadModule: " << pModuleName ); + OSL_ASSERT(pModuleName); + + h = LoadLibraryA(pModuleName); + if (h == nullptr) + h = LoadLibraryExA(pModuleName, nullptr, + LOAD_WITH_ALTERED_SEARCH_PATH); + + ret = static_cast<oslModule>(h); + SetErrorMode(errorMode); + + return ret; +} + +oslModule osl_loadModuleRelativeAscii( + oslGenericFunction baseModule, char const * relativePath, sal_Int32 mode) +{ + return osl_loadModuleRelative(baseModule, OUString::createFromAscii(relativePath).pData, mode); +} + +sal_Bool SAL_CALL +osl_getModuleHandle(rtl_uString *pModuleName, oslModule *pResult) +{ + LPCWSTR pName = pModuleName ? o3tl::toW(pModuleName->buffer) : nullptr; + HMODULE h = GetModuleHandleW(pName); + if( h ) + { + *pResult = static_cast<oslModule>(h); + return true; + } + + return false; +} + +void SAL_CALL osl_unloadModule(oslModule Module) +{ + FreeLibrary(static_cast<HMODULE>(Module)); +} + +void* SAL_CALL osl_getSymbol(oslModule Module, rtl_uString *strSymbolName) +{ + /* casting from a function pointer to a data pointer is invalid + be in this case unavoidable because the API has to stay + compatible. We need to keep this function which returns a + void* by definition */ + return reinterpret_cast<void*>(osl_getFunctionSymbol(Module, strSymbolName)); +} + +oslGenericFunction SAL_CALL osl_getFunctionSymbol( oslModule Module, rtl_uString *strSymbolName ) +{ + rtl_String *symbolName = nullptr; + oslGenericFunction address; + + OSL_ASSERT(Module); + OSL_ASSERT(strSymbolName); + + rtl_uString2String( + &symbolName, + strSymbolName->buffer, + strSymbolName->length, + RTL_TEXTENCODING_UTF8, + OUSTRING_TO_OSTRING_CVTFLAGS + ); + + address=osl_getAsciiFunctionSymbol(Module, rtl_string_getStr(symbolName)); + rtl_string_release(symbolName); + + return address; +} + +oslGenericFunction SAL_CALL +osl_getAsciiFunctionSymbol( oslModule Module, const char *pSymbol ) +{ + oslGenericFunction fncAddr = nullptr; + + if( pSymbol ) + fncAddr=reinterpret_cast<oslGenericFunction>(GetProcAddress(static_cast<HMODULE>(Module), pSymbol)); + + return fncAddr; +} + +sal_Bool SAL_CALL osl_getModuleURLFromAddress( void *pv, rtl_uString **pustrURL ) +{ + HMODULE hModule{}; + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + static_cast<LPCWSTR>(pv), &hModule); + if (!hModule) + return false; + + ::osl::LongPathBuffer<sal_Unicode> aBuffer(MAX_LONG_PATH); + + DWORD nch = GetModuleFileNameW(hModule, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols()); + + OUString ustrSysPath(aBuffer, nch); + return osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrURL) == osl_File_E_None; +} + +sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress( oslGenericFunction addr, rtl_uString ** ppLibraryUrl ) +{ + /* casting a function pointer to a data pointer (void*) is + not allowed according to the C/C++ standards. In this case + it is unavoidable because we have to stay compatible we + cannot remove any function. */ + return osl_getModuleURLFromAddress(reinterpret_cast<void*>(addr), ppLibraryUrl); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/mutex.cxx b/sal/osl/w32/mutex.cxx new file mode 100644 index 000000000..7de4c41ee --- /dev/null +++ b/sal/osl/w32/mutex.cxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include "system.h" + +#include <osl/mutex.h> +#include <osl/diagnose.h> + +/* + Implementation notes: + The void* hidden by oslMutex points to a WIN32 + CRITICAL_SECTION structure. +*/ + +oslMutex SAL_CALL osl_createMutex(void) +{ + CRITICAL_SECTION* pMutexImpl; + + pMutexImpl = static_cast<CRITICAL_SECTION*>(calloc(sizeof(CRITICAL_SECTION), 1)); + + OSL_ASSERT(pMutexImpl); /* alloc successful? */ + + InitializeCriticalSection(pMutexImpl); + + return reinterpret_cast<oslMutex>(pMutexImpl); +} + +void SAL_CALL osl_destroyMutex(oslMutex Mutex) +{ + CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex); + + if (pMutexImpl) + { + DeleteCriticalSection(pMutexImpl); + free(pMutexImpl); + } +} + +sal_Bool SAL_CALL osl_acquireMutex(oslMutex Mutex) +{ + CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex); + + OSL_ASSERT(Mutex); + + EnterCriticalSection(pMutexImpl); + + return true; +} + +sal_Bool SAL_CALL osl_tryToAcquireMutex(oslMutex Mutex) +{ + CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex); + + OSL_ASSERT(Mutex); + + return TryEnterCriticalSection(pMutexImpl) != FALSE; +} + +sal_Bool SAL_CALL osl_releaseMutex(oslMutex Mutex) +{ + CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex); + + OSL_ASSERT(Mutex); + + LeaveCriticalSection(pMutexImpl); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/nlsupport.cxx b/sal/osl/w32/nlsupport.cxx new file mode 100644 index 000000000..f27b97aa7 --- /dev/null +++ b/sal/osl/w32/nlsupport.cxx @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include <wchar.h> + +#include "nlsupport.hxx" + +#include <osl/mutex.h> +#include <osl/nlsupport.h> +#include <osl/diagnose.h> +#include <osl/process.h> +#include <rtl/tencinfo.h> +#include <o3tl/char16_t2wchar_t.hxx> + +/* XXX NOTE: + * http://msdn.microsoft.com/en-us/library/windows/desktop/dd373848.aspx + * (retrieved 2013-02-13) has some weird description for the LOCALE_SISO* + * constants: "The maximum number of characters allowed for this string is + * nine, including a terminating null character." NINE?!? In ISO 639 and ISO + * 3166? + */ +#define ELP_LANGUAGE_FIELD_LENGTH 4 +#define ELP_COUNTRY_FIELD_LENGTH 3 + +namespace { + +/** Struct used in EnumLocalesProcW() called via EnumSystemLocalesW() to obtain + available locales. +*/ +struct EnumLocalesParams +{ + WCHAR Language[ELP_LANGUAGE_FIELD_LENGTH]; + WCHAR Country[ELP_COUNTRY_FIELD_LENGTH]; + LCID Locale; +}; + +} + +static DWORD g_dwTLSLocaleEncId = DWORD(-1); + +/***************************************************************************** + * callback function test + *****************************************************************************/ + +static BOOL CALLBACK EnumLocalesProcW( LPWSTR lpLocaleStringW ) +{ + /* check params received via TLS */ + EnumLocalesParams * params = static_cast<EnumLocalesParams *>(TlsGetValue( g_dwTLSLocaleEncId )); + if( nullptr == params || '\0' == params->Language[0] ) + return FALSE; + + LPWSTR pszEnd; + WCHAR langCode[ELP_LANGUAGE_FIELD_LENGTH]; + + /* convert hex-string to LCID */ + LCID localeId = wcstol(lpLocaleStringW, &pszEnd, 16); + + /* + get the ISO language code for this locale + */ + if( !GetLocaleInfoW( localeId, LOCALE_SISO639LANGNAME , langCode, ELP_LANGUAGE_FIELD_LENGTH ) ) + /* retry by going on */ + return TRUE; + + WCHAR ctryCode[ELP_COUNTRY_FIELD_LENGTH]; + + /* continue if language code does not match */ + if( 0 != wcscmp( langCode, params->Language ) ) + return TRUE; + + /* check if country code is set and equals the current locale */ + if( '\0' != params->Country[0] && GetLocaleInfoW( localeId, + LOCALE_SISO3166CTRYNAME , ctryCode, ELP_COUNTRY_FIELD_LENGTH ) ) + { + /* save return value in TLS and break if found desired locale */ + if( 0 == wcscmp( ctryCode, params->Country ) ) + { + params->Locale = localeId; + return FALSE; + } + } + else + { + /* fill with default values for that language */ + LANGID langId = LANGIDFROMLCID( localeId ); + + /* exchange sublanguage with SUBLANG_NEUTRAL */ + langId = MAKELANGID( PRIMARYLANGID( langId ), SUBLANG_NEUTRAL ); + + /* and use default sorting order */ + params->Locale = MAKELCID( langId, SORT_DEFAULT ); + + return FALSE; + } + + /* retry by going on */ + return TRUE; +} + +static rtl_TextEncoding GetTextEncodingFromLCID( LCID localeId ) +{ + /* query ansi codepage for given locale */ + WCHAR ansiCP[6]; + if( !localeId || !GetLocaleInfoW( localeId, LOCALE_IDEFAULTANSICODEPAGE, ansiCP, 6 ) ) + return RTL_TEXTENCODING_DONTKNOW; + + /* if GetLocaleInfo returns "0", it is a UNICODE only locale */ + if( 0 == wcscmp( ansiCP, L"0" ) ) + return RTL_TEXTENCODING_UNICODE; + + /* values returned from GetLocaleInfo are decimal based */ + WCHAR *pwcEnd; + UINT codepage = wcstol( ansiCP, &pwcEnd, 10 ); + + /* find matching rtl encoding */ + return rtl_getTextEncodingFromWindowsCodePage( codepage ); +} + +rtl_TextEncoding SAL_CALL osl_getTextEncodingFromLocale( rtl_Locale * pLocale ) +{ + struct EnumLocalesParams params = { L"", L"", 0 }; + + /* initialise global TLS id */ + if( DWORD(-1) == g_dwTLSLocaleEncId ) + { + oslMutex globalMutex = * osl_getGlobalMutex(); + + /* initializing must be thread save */ + osl_acquireMutex( globalMutex ); + + if( DWORD(-1) == g_dwTLSLocaleEncId ) + g_dwTLSLocaleEncId = TlsAlloc(); + + osl_releaseMutex( globalMutex ); + } + + /* if pLocale is NULL, use process locale as default */ + if( nullptr == pLocale ) + osl_getProcessLocale( &pLocale ); + + /* copy in parameters to structure */ + if( !pLocale || !pLocale->Language || pLocale->Language->length >= ELP_LANGUAGE_FIELD_LENGTH ) + return RTL_TEXTENCODING_DONTKNOW; + + wcscpy( params.Language, o3tl::toW(pLocale->Language->buffer) ); + + if( pLocale->Country && pLocale->Country->length < ELP_COUNTRY_FIELD_LENGTH ) + wcscpy( params.Country, o3tl::toW(pLocale->Country->buffer) ); + + /* save pointer to local structure in TLS */ + TlsSetValue( g_dwTLSLocaleEncId, ¶ms ); + + /* enum all locales known to Windows */ + EnumSystemLocalesW( EnumLocalesProcW, LCID_SUPPORTED ); + + /* use the LCID found in iteration */ + return GetTextEncodingFromLCID( params.Locale ); +} + +void imp_getProcessLocale( rtl_Locale ** ppLocale ) +{ + WCHAR langCode[ELP_LANGUAGE_FIELD_LENGTH]; + WCHAR ctryCode[ELP_COUNTRY_FIELD_LENGTH]; + LCID localeId; + + OSL_ASSERT( ppLocale ); + + /* get the LCID to retrieve information from */ + localeId = GetUserDefaultLCID(); + + /* call GetLocaleInfo to retrieve the iso codes */ + if( GetLocaleInfoW( localeId, LOCALE_SISO639LANGNAME , langCode, ELP_LANGUAGE_FIELD_LENGTH ) && + GetLocaleInfoW( localeId, LOCALE_SISO3166CTRYNAME , ctryCode, ELP_COUNTRY_FIELD_LENGTH ) ) + { + *ppLocale = rtl_locale_register( o3tl::toU(langCode), o3tl::toU(ctryCode), u"" ); + } + else + { + *ppLocale = rtl_locale_register( u"C", u"", u"" ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/nlsupport.hxx b/sal/osl/w32/nlsupport.hxx new file mode 100644 index 000000000..2d818c378 --- /dev/null +++ b/sal/osl/w32/nlsupport.hxx @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SAL_OSL_W32_NLSUPPORT_HXX +#define INCLUDED_SAL_OSL_W32_NLSUPPORT_HXX + +#include <sal/config.h> + +#include <rtl/locale.h> + +void imp_getProcessLocale(rtl_Locale** ppLocale); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sal/osl/w32/path_helper.cxx b/sal/osl/w32/path_helper.cxx new file mode 100644 index 000000000..a2dd134fd --- /dev/null +++ b/sal/osl/w32/path_helper.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "path_helper.hxx" +#include <osl/diagnose.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <algorithm> +#include <wchar.h> + +constexpr OUStringLiteral BACKSLASH (u"\\"); +constexpr OUStringLiteral SLASH (u"/"); + +void osl_systemPathEnsureSeparator(/*inout*/ rtl_uString** ppustrPath) +{ + OSL_PRECOND(ppustrPath && (nullptr != *ppustrPath), + "osl_systemPathEnsureSeparator: Invalid parameter"); + + OUString path(*ppustrPath); + sal_Int32 i = std::max<sal_Int32>(path.lastIndexOf(BACKSLASH), path.lastIndexOf(SLASH)); + + if (i < (path.getLength()-1)) + { + path += BACKSLASH; + rtl_uString_assign(ppustrPath, path.pData); + } + + SAL_WARN_IF( !path.endsWith(BACKSLASH), + "sal.osl", + "osl_systemPathEnsureSeparator: Post condition failed"); +} + +void osl_systemPathRemoveSeparator(/*inout*/ rtl_uString** ppustrPath) +{ + OUString path(*ppustrPath); + + if (!osl::systemPathIsLogicalDrivePattern(path)) + { + sal_Int32 i = std::max<sal_Int32>(path.lastIndexOf(BACKSLASH), path.lastIndexOf(SLASH)); + + if (i > -1 && (i == (path.getLength() - 1))) + { + path = OUString(path.getStr(), path.getLength() - 1); + rtl_uString_assign(ppustrPath, path.pData); + } + } +} + +// is [A-Za-z]:[/|\]\0 +const char* const LDP = ":"; +const char* const LDP_WITH_BACKSLASH = ":\\"; +const char* const LDP_WITH_SLASH = ":/"; + +// degenerated case returned by the Windows FileOpen dialog +// when someone enters for instance "x:filename", the Win32 +// API accepts this case +const char* const LDP_WITH_DOT_BACKSLASH = ":.\\"; + +bool osl_systemPathIsLogicalDrivePattern(/*in*/ const rtl_uString* pustrPath) +{ + const sal_Unicode* p = rtl_uString_getStr(const_cast<rtl_uString*>(pustrPath)); + if (iswalpha(*p++)) + { + return ((0 == rtl_ustr_ascii_compare(p, LDP)) || + (0 == rtl_ustr_ascii_compare(p, LDP_WITH_BACKSLASH)) || + (0 == rtl_ustr_ascii_compare(p, LDP_WITH_SLASH)) || + (0 == rtl_ustr_ascii_compare(p, LDP_WITH_DOT_BACKSLASH))); + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/path_helper.hxx b/sal/osl/w32/path_helper.hxx new file mode 100644 index 000000000..bcb4f83af --- /dev/null +++ b/sal/osl/w32/path_helper.hxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_OSL_W32_PATH_HELPER_HXX +#define INCLUDED_SAL_OSL_W32_PATH_HELPER_HXX + +#include <sal/config.h> + +#include <osl/diagnose.h> +#include <osl/file.h> +#include <rtl/alloc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +/** + Adds a trailing path separator to the given system path if not + already there and if the path is not the root path or a logical + drive alone +*/ + +void osl_systemPathEnsureSeparator(/*inout*/ rtl_uString** ppustrPath); + +/** + Removes the last separator from the given system path if any and + if the path is not the root path '\' +*/ + +void osl_systemPathRemoveSeparator(/*inout*/ rtl_uString** ppustrPath); + +/** + Returns whether a given path is only a logical drive pattern or not. + A logical drive pattern is something like "a:\", "c:\". + No logical drive pattern is something like "c:\test" +*/ + +bool osl_systemPathIsLogicalDrivePattern(/*in*/ const rtl_uString* pustrPath); + +namespace osl +{ + +/** + Adds a trailing path separator to the given system path if not + already there and if the path is not the root path or a logical + drive alone +*/ + +inline void systemPathEnsureSeparator(/*inout*/ OUString& Path) +{ + osl_systemPathEnsureSeparator(&Path.pData); +} + +/** + Removes the last separator from the given system path if any and + if the path is not the root path '\' +*/ + +inline void systemPathRemoveSeparator(/*inout*/ OUString& Path) +{ + osl_systemPathRemoveSeparator(&Path.pData); +} + +inline bool systemPathIsLogicalDrivePattern(/*in*/ const OUString& path) +{ + return osl_systemPathIsLogicalDrivePattern(path.pData); +} + +template< class T > +class LongPathBuffer +{ + T* m_pBuffer; + sal_uInt32 m_nCharNum; + + LongPathBuffer(); + LongPathBuffer( const LongPathBuffer& ); + LongPathBuffer& operator=( const LongPathBuffer& ); + +public: + explicit LongPathBuffer( sal_uInt32 nCharNum ) + : m_pBuffer( static_cast<T*>( malloc( nCharNum * sizeof( T ) ) ) ) + , m_nCharNum( nCharNum ) + { + OSL_ENSURE( m_pBuffer, "Can not allocate the buffer!" ); + } + + ~LongPathBuffer() + { + if ( m_pBuffer ) + free( m_pBuffer ); + m_pBuffer = nullptr; + } + + sal_uInt32 getBufSizeInSymbols() + { + return m_nCharNum; + } + + operator T* () + { + return m_pBuffer; + } + +}; + +} // end namespace osl + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/pipe.cxx b/sal/osl/w32/pipe.cxx new file mode 100644 index 000000000..c94441e63 --- /dev/null +++ b/sal/osl/w32/pipe.cxx @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" + +#include <osl/pipe.h> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <osl/mutex.h> +#include <osl/conditn.h> +#include <osl/interlck.h> +#include <osl/process.h> +#include <rtl/alloc.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <cassert> +#include <string.h> + +#define PIPESYSTEM "\\\\.\\pipe\\" +#define PIPEPREFIX "OSL_PIPE_" + +struct oslPipeImpl +{ + oslInterlockedCount m_Reference; + HANDLE m_File; + HANDLE m_NamedObject; + PSECURITY_ATTRIBUTES m_Security; + HANDLE m_ReadEvent; + HANDLE m_WriteEvent; + HANDLE m_AcceptEvent; + rtl_uString* m_Name; + oslPipeError m_Error; + bool m_bClosed; +}; + +static oslPipe osl_createPipeImpl(void) +{ + oslPipe pPipe; + + pPipe = static_cast< oslPipe >(rtl_allocateZeroMemory(sizeof(struct oslPipeImpl))); + + pPipe->m_bClosed = false; + pPipe->m_Reference = 0; + pPipe->m_Name = nullptr; + pPipe->m_File = INVALID_HANDLE_VALUE; + pPipe->m_NamedObject = nullptr; + + pPipe->m_ReadEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + pPipe->m_WriteEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + pPipe->m_AcceptEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + + return pPipe; +} + +static void osl_destroyPipeImpl(oslPipe pPipe) +{ + if (!pPipe) + return; + + if (pPipe->m_NamedObject) + CloseHandle(pPipe->m_NamedObject); + + if (pPipe->m_Security) + { + free(pPipe->m_Security->lpSecurityDescriptor); + free(pPipe->m_Security); + } + + CloseHandle(pPipe->m_ReadEvent); + CloseHandle(pPipe->m_WriteEvent); + CloseHandle(pPipe->m_AcceptEvent); + + if (pPipe->m_Name) + rtl_uString_release(pPipe->m_Name); + + free(pPipe); +} + +oslPipe SAL_CALL osl_createPipe(rtl_uString *strPipeName, oslPipeOptions Options, + oslSecurity Security) +{ + rtl_uString* name = nullptr; + rtl_uString* path = nullptr; + rtl_uString* temp = nullptr; + oslPipe pPipe; + + PSECURITY_ATTRIBUTES pSecAttr = nullptr; + + rtl_uString_newFromAscii(&path, PIPESYSTEM); + rtl_uString_newFromAscii(&name, PIPEPREFIX); + + if (Security) + { + rtl_uString *Ident = nullptr; + rtl_uString *Delim = nullptr; + + OSL_VERIFY(osl_getUserIdent(Security, &Ident)); + rtl_uString_newFromAscii(&Delim, "_"); + + rtl_uString_newConcat(&temp, name, Ident); + rtl_uString_newConcat(&name, temp, Delim); + + rtl_uString_release(Ident); + rtl_uString_release(Delim); + } + else + { + if (Options & osl_Pipe_CREATE) + { + PSECURITY_DESCRIPTOR pSecDesc; + + pSecDesc = static_cast< PSECURITY_DESCRIPTOR >(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH)); + assert(pSecDesc); // Don't handle OOM conditions + + /* add a NULL disc. ACL to the security descriptor */ + OSL_VERIFY(InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)); + OSL_VERIFY(SetSecurityDescriptorDacl(pSecDesc, TRUE, nullptr, FALSE)); + + pSecAttr = static_cast< PSECURITY_ATTRIBUTES >(malloc(sizeof(SECURITY_ATTRIBUTES))); + assert(pSecAttr); // Don't handle OOM conditions + pSecAttr->nLength = sizeof(SECURITY_ATTRIBUTES); + pSecAttr->lpSecurityDescriptor = pSecDesc; + pSecAttr->bInheritHandle = TRUE; + } + } + + rtl_uString_assign(&temp, name); + rtl_uString_newConcat(&name, temp, strPipeName); + + /* alloc memory */ + pPipe = osl_createPipeImpl(); + + assert(pPipe); // if osl_createPipeImpl() cannot init. a new pipe, this is a failure + + osl_atomic_increment(&(pPipe->m_Reference)); + + /* build system pipe name */ + rtl_uString_assign(&temp, path); + rtl_uString_newConcat(&path, temp, name); + rtl_uString_release(temp); + temp = nullptr; + + if (Options & osl_Pipe_CREATE) + { + SetLastError(ERROR_SUCCESS); + + pPipe->m_NamedObject = CreateMutexW(nullptr, FALSE, o3tl::toW(name->buffer)); + + if (pPipe->m_NamedObject) + { + if (GetLastError() != ERROR_ALREADY_EXISTS) + { + pPipe->m_Security = pSecAttr; + rtl_uString_assign(&pPipe->m_Name, name); + + /* try to open system pipe */ + pPipe->m_File = CreateNamedPipeW( + o3tl::toW(path->buffer), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 4096, 4096, + NMPWAIT_WAIT_FOREVER, + pPipe->m_Security); + + if (pPipe->m_File != INVALID_HANDLE_VALUE) + { + rtl_uString_release( name ); + rtl_uString_release( path ); + + return pPipe; + } + } + else + { + CloseHandle(pPipe->m_NamedObject); + pPipe->m_NamedObject = nullptr; + } + } + } + else + { + bool bPipeAvailable; + + do + { + /* free instance should be available first */ + bPipeAvailable = WaitNamedPipeW(o3tl::toW(path->buffer), NMPWAIT_WAIT_FOREVER); + + /* first try to open system pipe */ + if (bPipeAvailable) + { + pPipe->m_File = CreateFileW( + o3tl::toW(path->buffer), + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + nullptr); + + if (pPipe->m_File != INVALID_HANDLE_VALUE) + { + // We got it ! + rtl_uString_release(name); + rtl_uString_release(path); + + // We should try to transfer our privilege to become foreground process + // to the other process, so that it may show popups (otherwise, they might + // be blocked by SPI_GETFOREGROUNDLOCKTIMEOUT setting - + // see SystemParametersInfo function at MSDN + ULONG ServerProcessId = 0; + if (GetNamedPipeServerProcessId(pPipe->m_File, &ServerProcessId)) + AllowSetForegroundWindow(ServerProcessId); + + return pPipe; + } + else + { + // Pipe instance maybe caught by another client -> try again + } + } + } while (bPipeAvailable); + } + + /* if we reach here something went wrong */ + osl_destroyPipeImpl(pPipe); + + return nullptr; +} + +void SAL_CALL osl_acquirePipe(oslPipe pPipe) +{ + osl_atomic_increment(&(pPipe->m_Reference)); +} + +void SAL_CALL osl_releasePipe(oslPipe pPipe) +{ + if (!pPipe) + return; + + if (osl_atomic_decrement(&(pPipe->m_Reference)) == 0) + { + if (!pPipe->m_bClosed) + osl_closePipe(pPipe); + + osl_destroyPipeImpl(pPipe); + } +} + +void SAL_CALL osl_closePipe(oslPipe pPipe) +{ + if (pPipe && !pPipe->m_bClosed) + { + pPipe->m_bClosed = true; + /* if we have a system pipe close it */ + if (pPipe->m_File != INVALID_HANDLE_VALUE) + { + DisconnectNamedPipe(pPipe->m_File); + CloseHandle(pPipe->m_File); + } + } +} + +oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe) +{ + oslPipe pAcceptedPipe = nullptr; + + OVERLAPPED os = {}; + + DWORD nBytesTransferred; + rtl_uString* path = nullptr; + rtl_uString* temp = nullptr; + + SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_acceptPipe: invalid pipe"); + if (!pPipe) + return nullptr; + + SAL_WARN_IF(pPipe->m_File == INVALID_HANDLE_VALUE, "sal.osl.pipe", "osl_acceptPipe: invalid handle"); + + os.hEvent = pPipe->m_AcceptEvent; + ResetEvent(pPipe->m_AcceptEvent); + + if (!ConnectNamedPipe(pPipe->m_File, &os)) + { + switch (GetLastError()) + { + case ERROR_PIPE_CONNECTED: // Client already connected to pipe + case ERROR_NO_DATA: // Client was connected but has already closed pipe end + // should only appear in nonblocking mode but in fact does + // in blocking asynchronous mode. + break; + case ERROR_PIPE_LISTENING: // Only for nonblocking mode but see ERROR_NO_DATA + case ERROR_IO_PENDING: // This is normal if not client is connected yet + case ERROR_MORE_DATA: // Should not happen + // blocking call to accept + if( !GetOverlappedResult(pPipe->m_File, &os, &nBytesTransferred, TRUE)) + { + // Possible error could be that between ConnectNamedPipe and + // GetOverlappedResult a connect took place. + + switch (GetLastError()) + { + case ERROR_PIPE_CONNECTED: // Pipe was already connected + case ERROR_NO_DATA: // Pipe was connected but client has already closed -> ver fast client ;-) + break; // Everything's fine !!! + default: + // Something went wrong + return nullptr; + } + } + break; + default: // All other error say that somethings going wrong. + return nullptr; + } + } + + pAcceptedPipe = osl_createPipeImpl(); + assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized + + osl_atomic_increment(&(pAcceptedPipe->m_Reference)); + rtl_uString_assign(&pAcceptedPipe->m_Name, pPipe->m_Name); + pAcceptedPipe->m_File = pPipe->m_File; + + rtl_uString_newFromAscii(&temp, PIPESYSTEM); + rtl_uString_newConcat(&path, temp, pPipe->m_Name); + rtl_uString_release(temp); + + // prepare for next accept + pPipe->m_File = + CreateNamedPipeW(o3tl::toW(path->buffer), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 4096, 4096, + NMPWAIT_WAIT_FOREVER, + pAcceptedPipe->m_Security); + rtl_uString_release(path); + + return pAcceptedPipe; +} + +sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe, + void* pBuffer, + sal_Int32 BytesToRead) +{ + DWORD nBytes; + OVERLAPPED os = {}; + + assert(pPipe); + + os.hEvent = pPipe->m_ReadEvent; + + ResetEvent(pPipe->m_ReadEvent); + + if (!ReadFile(pPipe->m_File, pBuffer, BytesToRead, &nBytes, &os) && + ((GetLastError() != ERROR_IO_PENDING) || + !GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE))) + { + DWORD lastError = GetLastError(); + + if (lastError == ERROR_MORE_DATA) + { + nBytes = BytesToRead; + } + else + { + if (lastError == ERROR_PIPE_NOT_CONNECTED) + nBytes = 0; + else + nBytes = DWORD(-1); + + pPipe->m_Error = osl_Pipe_E_ConnectionAbort; + } + } + + return nBytes; +} + +sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe, + const void* pBuffer, + sal_Int32 BytesToSend) +{ + DWORD nBytes; + OVERLAPPED os = {}; + + assert(pPipe); + + os.hEvent = pPipe->m_WriteEvent; + ResetEvent(pPipe->m_WriteEvent); + + if (!WriteFile(pPipe->m_File, pBuffer, BytesToSend, &nBytes, &os) && + ((GetLastError() != ERROR_IO_PENDING) || + !GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE))) + { + if (GetLastError() == ERROR_PIPE_NOT_CONNECTED) + nBytes = 0; + else + nBytes = DWORD(-1); + + pPipe->m_Error = osl_Pipe_E_ConnectionAbort; + } + + return nBytes; +} + +sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer , sal_Int32 n) +{ + /* loop until all desired bytes were send or an error occurred */ + sal_Int32 BytesSend = 0; + sal_Int32 BytesToSend = n; + + SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); + while (BytesToSend > 0) + { + sal_Int32 RetVal; + + RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend); + + /* error occurred? */ + if (RetVal <= 0) + break; + + BytesToSend -= RetVal; + BytesSend += RetVal; + pBuffer= static_cast< char const* >(pBuffer) + RetVal; + } + + return BytesSend; +} + +sal_Int32 SAL_CALL osl_readPipe(oslPipe pPipe, void *pBuffer, sal_Int32 n) +{ + /* loop until all desired bytes were read or an error occurred */ + sal_Int32 BytesRead = 0; + sal_Int32 BytesToRead = n; + + SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); + while (BytesToRead > 0) + { + sal_Int32 RetVal; + RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead); + + /* error occurred? */ + if(RetVal <= 0) + break; + + BytesToRead -= RetVal; + BytesRead += RetVal; + pBuffer= static_cast< char* >(pBuffer) + RetVal; + } + return BytesRead; +} + +oslPipeError SAL_CALL osl_getLastPipeError(oslPipe pPipe) +{ + oslPipeError Error; + + if (pPipe) + { + Error = pPipe->m_Error; + pPipe->m_Error = osl_Pipe_E_None; + } + else + { + Error = osl_Pipe_E_NotFound; + } + + return Error; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/process.cxx b/sal/osl/w32/process.cxx new file mode 100644 index 000000000..065415f2c --- /dev/null +++ b/sal/osl/w32/process.cxx @@ -0,0 +1,525 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" +#include <string.h> + +#include <shellapi.h> + +#include <cassert> + +#include <osl/mutex.hxx> +#include <osl/nlsupport.h> +#include <o3tl/char16_t2wchar_t.hxx> + +#include "filetime.hxx" +#include "nlsupport.hxx" +#include "procimpl.hxx" +#include "file_url.hxx" +#include "path_helper.hxx" +#include <rtl/alloc.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process) +{ + if (Process == nullptr) + return osl_Process_E_Unknown; + + HANDLE hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess; + DWORD dwPID = GetProcessId(hProcess); + + // cannot be System Process (0x00000000) + if (dwPID == 0x0) + return osl_Process_E_InvalidError; + + // Test to see if we can create a thread in a process... adapted from: + // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32 + // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547 + + // TODO: we really should firstly check to see if we have access to create threads and only + // duplicate the handle with elevated access if we don't have access... this can be done, but + // it's not exactly easy - an example can be found here: + // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt + + HANDLE hDupProcess = nullptr; + + + // we need to make sure we can create a thread in the remote process, if the handle was created + // in something that doesn't give us appropriate levels of access then we will need to give it the + // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible + // that the handle doesn't have the appropriate level of access... + + // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx + DWORD const dwAccessFlags = (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE | PROCESS_VM_READ); + + bool bHaveDuplHdl = DuplicateHandle(GetCurrentProcess(), // handle to process that has handle + hProcess, // handle to be duplicated + GetCurrentProcess(), // process that will get the dup handle + &hDupProcess, // store duplicate process handle here + dwAccessFlags, // desired access + FALSE, // handle can't be inherited + 0); // zero means no additional action needed + + if (bHaveDuplHdl) + hProcess = hDupProcess; // so we were able to duplicate the handle, all good... + else + SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best..."); + + DWORD dwProcessStatus = 0; + HANDLE hRemoteThread = nullptr; + + if (GetExitCodeProcess(hProcess, &dwProcessStatus) && (dwProcessStatus == STILL_ACTIVE)) + { + // We need to get the address of the Win32 procedure ExitProcess, can't call it + // directly because we'll be calling the thunk and that will probably lead to an + // access violation. Once we have the address, then we need to create a new + // thread in the process (which we might need to run in the address space of + // another process) and then call on ExitProcess to try to cleanly terminate that + // process + + DWORD dwTID = 0; // dummy variable as we don't need to track the thread ID + + // Note: we want to call on ExitProcess() and not TerminateProcess() - this is + // because with ExitProcess() Windows notifies all attached dlls that the process + // is detaching from the dll, but TerminateProcess() terminates all threads + // immediately, doesn't call any termination handlers and doesn't notify any dlls + // that it is detaching from them + + HINSTANCE hKernel = GetModuleHandleW(L"kernel32.dll"); + FARPROC pfnExitProc = GetProcAddress(hKernel, "ExitProcess"); + hRemoteThread = CreateRemoteThread( + hProcess, /* process handle */ + nullptr, /* default security descriptor */ + 0, /* initial size of stack in bytes is default + size for executable */ + reinterpret_cast<LPTHREAD_START_ROUTINE>(pfnExitProc), /* Win32 ExitProcess() */ + reinterpret_cast<PVOID>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */ + 0, /* value of 0 tells thread to run immediately + after creation */ + &dwTID); /* new remote thread's identifier */ + + } + + bool bHasExited = false; + + if (hRemoteThread) + { + WaitForSingleObject(hProcess, INFINITE); // wait for process to terminate, never stop waiting... + CloseHandle(hRemoteThread); // close the thread handle to allow the process to exit + bHasExited = true; + } + + // need to close this duplicated process handle... + if (bHaveDuplHdl) + CloseHandle(hProcess); + + if (bHasExited) + return osl_Process_E_None; + + // fallback - given that we wait for an infinite time on WaitForSingleObject, this should + // never occur... unless CreateRemoteThread failed + SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!"); + return (TerminateProcess(hProcess, 0) == FALSE) ? osl_Process_E_Unknown : osl_Process_E_None; +} + +oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident) +{ + oslProcessImpl* pProcImpl; + HANDLE hProcess = OpenProcess( + STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(Ident)); + + if (hProcess) + { + pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) ); + pProcImpl->m_hProcess = hProcess; + pProcImpl->m_IdProcess = Ident; + } + else + pProcImpl = nullptr; + + return pProcImpl; +} + +void SAL_CALL osl_freeProcessHandle(oslProcess Process) +{ + if (Process != nullptr) + { + CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess); + + free(Process); + } +} + +oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, + oslProcessInfo* pInfo) +{ + HANDLE hProcess; + DWORD IdProcess; + + if (Process == nullptr) + { + hProcess = GetCurrentProcess(); + IdProcess = GetCurrentProcessId(); + } + else + { + hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess; + IdProcess = static_cast<oslProcessImpl*>(Process)->m_IdProcess; + } + + if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo))) + return osl_Process_E_Unknown; + + pInfo->Fields = 0; + + if (Fields & osl_Process_IDENTIFIER) + { + pInfo->Ident = IdProcess; + pInfo->Fields |= osl_Process_IDENTIFIER; + } + + if (Fields & osl_Process_EXITCODE) + { + if (GetExitCodeProcess(hProcess, &(pInfo->Code)) && (pInfo->Code != STILL_ACTIVE)) + pInfo->Fields |= osl_Process_EXITCODE; + } + + if (Fields & osl_Process_HEAPUSAGE) + { + void* lpAddress=nullptr; + MEMORY_BASIC_INFORMATION Info; + + pInfo->HeapUsage = 0; + + do + { + if (VirtualQueryEx(hProcess, lpAddress, &Info, sizeof(Info)) == 0) + break; + + if ((Info.State == MEM_COMMIT) && (Info.Type == MEM_PRIVATE)) + pInfo->HeapUsage += Info.RegionSize; + + lpAddress = static_cast<LPBYTE>(lpAddress) + Info.RegionSize; + } + while (reinterpret_cast<uintptr_t>(lpAddress) <= 0x7FFFFFFFU); // 2GB address space + + pInfo->Fields |= osl_Process_HEAPUSAGE; + } + + if (Fields & osl_Process_CPUTIMES) + { + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + + if (GetProcessTimes(hProcess, &CreationTime, &ExitTime, + &KernelTime, &UserTime)) + { + __int64 Value; + + Value = osl::detail::getFiletime(UserTime); + pInfo->UserTime.Seconds = static_cast<unsigned long>(Value / 10000000L); + pInfo->UserTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100); + + Value = osl::detail::getFiletime(KernelTime); + pInfo->SystemTime.Seconds = static_cast<unsigned long>(Value / 10000000L); + pInfo->SystemTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100); + + pInfo->Fields |= osl_Process_CPUTIMES; + } + } + + return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown; +} + +oslProcessError SAL_CALL osl_joinProcess(oslProcess Process) +{ + return osl_joinProcessWithTimeout(Process, nullptr); +} + +oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout) +{ + DWORD timeout = INFINITE; + oslProcessError osl_error = osl_Process_E_None; + DWORD ret; + + if (nullptr == Process) + return osl_Process_E_Unknown; + + if (pTimeout) + timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L; + + ret = WaitForSingleObject(static_cast<oslProcessImpl*>(Process)->m_hProcess, timeout); + + if (WAIT_FAILED == ret) + osl_error = osl_Process_E_Unknown; + else if (WAIT_TIMEOUT == ret) + osl_error = osl_Process_E_TimedOut; + + return osl_error; +} + +namespace { + +oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL) +{ + oslProcessError result = osl_Process_E_NotFound; + + ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH ); + DWORD buflen = 0; + + if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0) + { + rtl_uString * pAbsPath = nullptr; + rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen); + if (pAbsPath) + { + /* Convert from path to url. */ + if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None) + { + /* Success. */ + result = osl_Process_E_None; + } + rtl_uString_release (pAbsPath); + } + } + + return result; +} + +struct CommandArgs_Impl +{ + sal_uInt32 m_nCount; + rtl_uString ** m_ppArgs; +}; + +} + +static struct CommandArgs_Impl g_command_args = +{ + 0, + nullptr +}; + +static rtl_uString ** osl_createCommandArgs_Impl (int argc, char **) +{ + rtl_uString ** ppArgs = + static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*))); + if (ppArgs != nullptr) + { + int i; + int nArgs; + LPWSTR *wargv = CommandLineToArgvW( GetCommandLineW(), &nArgs ); + assert( nArgs == argc ); + for (i = 0; i < nArgs; i++) + { + /* Convert to unicode */ + rtl_uString_newFromStr( &(ppArgs[i]), o3tl::toU(wargv[i]) ); + } + if (ppArgs[0] != nullptr) + { + /* Ensure absolute path */ + ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH ); + DWORD dwResult + = GetModuleFileNameW(nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols()); + if ((0 < dwResult) && (dwResult < aBuffer.getBufSizeInSymbols())) + { + /* Replace argv[0] with its absolute path */ + rtl_uString_newFromStr_WithLength( + &(ppArgs[0]), aBuffer, dwResult); + } + } + if (ppArgs[0] != nullptr) + { + /* Convert to FileURL, see @ osl_getExecutableFile() */ + rtl_uString * pResult = nullptr; + osl_getFileURLFromSystemPath (ppArgs[0], &pResult); + if (pResult != nullptr) + { + rtl_uString_assign (&(ppArgs[0]), pResult); + rtl_uString_release (pResult); + } + } + } + return ppArgs; + +} + +oslProcessError SAL_CALL osl_getExecutableFile( rtl_uString **ppustrFile ) +{ + { + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + if (g_command_args.m_nCount > 0) + { + /* CommandArgs set. Obtain arv[0]. */ + rtl_uString_assign(ppustrFile, g_command_args.m_ppArgs[0]); + return osl_Process_E_None; + } + } + return bootstrap_getExecutableFile(ppustrFile); +} + +sal_uInt32 SAL_CALL osl_getCommandArgCount() +{ + sal_uInt32 result = 0; + + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + SAL_INFO_IF( + g_command_args.m_nCount == 0, "sal.osl", + "osl_getCommandArgCount w/o prior call to osl_setCommandArgs"); + if (g_command_args.m_nCount > 0) + { + /* We're not counting argv[0] here. */ + result = g_command_args.m_nCount - 1; + } + + return result; +} + +oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg) +{ + oslProcessError result = osl_Process_E_NotFound; + + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + assert(g_command_args.m_nCount > 0); + if (g_command_args.m_nCount > (nArg + 1)) + { + /* We're not counting argv[0] here. */ + rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]); + result = osl_Process_E_None; + } + + return result; +} + +void SAL_CALL osl_setCommandArgs (int argc, char ** argv) +{ + assert(argc > 0); + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set"); + if (g_command_args.m_nCount == 0) + { + rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv); + if (ppArgs != nullptr) + { + g_command_args.m_nCount = argc; + g_command_args.m_ppArgs = ppArgs; + } + } +} + +/* TODO because of an issue with GetEnvironmentVariableW we have to + allocate a buffer large enough to hold the requested environment + variable instead of testing for the required size. This wastes + some stack space, maybe we should revoke this work around if + this is no longer a problem */ +#define ENV_BUFFER_SIZE (32*1024-1) + +oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue) +{ + WCHAR buff[ENV_BUFFER_SIZE]; + + if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0) + { + rtl_uString_newFromStr(ustrValue, o3tl::toU(buff)); + return osl_Process_E_None; + } + return osl_Process_E_Unknown; +} + +oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue) +{ + // set Windows environment variable + if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), o3tl::toW(ustrValue->buffer))) + { + OUString sAssign = OUString::unacquired(&ustrVar) + "=" + OUString::unacquired(&ustrValue); + _wputenv(o3tl::toW(sAssign.getStr())); + return osl_Process_E_None; + } + return osl_Process_E_Unknown; +} + +oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar) +{ + // delete the variable from the current process environment + // by setting SetEnvironmentVariable's second parameter to NULL + if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), nullptr)) + { + OUString sAssign = OUString::unacquired(&ustrVar) + "="; + _wputenv(o3tl::toW(sAssign.getStr())); + return osl_Process_E_None; + } + return osl_Process_E_Unknown; +} + +oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir ) +{ + ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH ); + DWORD dwLen = GetCurrentDirectoryW(aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer)); + + if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() ) + { + oslFileError eError; + rtl_uString *ustrTemp = nullptr; + + rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen ); + eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir ); + + rtl_uString_release( ustrTemp ); + + if ( osl_File_E_None != eError ) + return osl_Process_E_Unknown; + else + return osl_Process_E_None; + } + else + return osl_Process_E_Unknown; +} + +static rtl_Locale * g_theProcessLocale = nullptr; + +oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale ) +{ + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + + /* determine the users default locale */ + if( nullptr == g_theProcessLocale ) + imp_getProcessLocale( &g_theProcessLocale ); + + /* or return the cached value */ + *ppLocale = g_theProcessLocale; + + return osl_Process_E_None; +} + +oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale ) +{ + osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex()); + + /* check if locale is supported */ + if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) ) + return osl_Process_E_Unknown; + + /* just remember the locale here */ + g_theProcessLocale = pLocale; + + return osl_Process_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/procimpl.cxx b/sal/osl/w32/procimpl.cxx new file mode 100644 index 000000000..5fb9a4634 --- /dev/null +++ b/sal/osl/w32/procimpl.cxx @@ -0,0 +1,596 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +#endif + +#include "file-impl.hxx" +#include "procimpl.hxx" +#include <rtl/alloc.h> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include "secimpl.hxx" +#include <osl/file.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <vector> +#include <algorithm> + +namespace /* private */ +{ + /* Function object that compares two strings that are + expected to be environment variables in the form + "name=value". Only the 'name' part will be compared. + The comparison is in upper case and returns true + if the first of both strings is less than the + second one. */ + struct less_environment_variable + { + bool operator() (const OUString& lhs, const OUString& rhs) const + { + OSL_ENSURE((lhs.indexOf(L'=') > -1) && + (rhs.indexOf(L'=') > -1), + "Malformed environment variable"); + + // Windows compares environment variables uppercase + // so we do it, too + return (rtl_ustr_compare_WithLength( + lhs.toAsciiUpperCase().pData->buffer, + lhs.indexOf(L'='), + rhs.toAsciiUpperCase().pData->buffer, + rhs.indexOf(L'=')) < 0); + } + }; + + /* Function object used by for_each algorithm to + calculate the sum of the length of all strings + in a string container. */ + class sum_of_string_lengths + { + public: + + sum_of_string_lengths() : sum_(0) {} + + void operator() (const OUString& string) + { + OSL_ASSERT(string.getLength()); + + // always include the terminating '\0' + if (string.getLength()) + sum_ += string.getLength() + 1; + } + + operator size_t () const + { + return sum_; + } + private: + size_t sum_; + }; + + size_t calc_sum_of_string_lengths(const std::vector<OUString>& string_cont) + { + return std::for_each( + string_cont.begin(), string_cont.end(), sum_of_string_lengths()); + } + + void read_environment(/*out*/ std::vector<OUString>* environment) + { + // GetEnvironmentStrings returns a sorted list, Windows + // sorts environment variables upper case + LPWSTR env = GetEnvironmentStringsW(); + LPWSTR p = env; + + while (size_t l = wcslen(p)) + { + environment->push_back(OUString(o3tl::toU(p))); + p += l + 1; + } + FreeEnvironmentStringsW(env); + + // it is apparently possible that the environment is not completely + // sorted; Cygwin may append entries, which breaks the equal_range + std::stable_sort(environment->begin(), environment->end(), + less_environment_variable()); + } + + /* the environment list must be sorted, new values + should either replace existing ones or should be + added to the list, environment variables will + be handled case-insensitive */ + bool create_merged_environment( + rtl_uString* env_vars[], + sal_uInt32 env_vars_count, + /*in|out*/ std::vector<OUString>* merged_env) + { + OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env); + + read_environment(merged_env); + + for (sal_uInt32 i = 0; i < env_vars_count; i++) + { + OUString env_var(env_vars[i]); + + if (env_var.getLength() == 0) + return false; + + auto iter_pair = std::equal_range( + merged_env->begin(), + merged_env->end(), + env_var, + less_environment_variable()); + + if (env_var.indexOf(L'=') == -1) + { + merged_env->erase(iter_pair.first, iter_pair.second); + } + else + { + if (iter_pair.first != iter_pair.second) // found + *iter_pair.first = env_var; + else // not found + merged_env->insert(iter_pair.first, env_var); + } + } + return true; + } + + /* Create a merged environment */ + bool setup_process_environment( + rtl_uString* environment_vars[], + sal_uInt32 n_environment_vars, + /*in|out*/ std::vector<sal_Unicode>& environment) + { + std::vector<OUString> merged_env; + if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env)) + return false; + + // allocate enough space for the '\0'-separated environment strings and + // a final '\0' + environment.resize(calc_sum_of_string_lengths(merged_env) + 1); + + sal_uInt32 pos = 0; + for (auto& envv : merged_env) + { + OSL_ASSERT(envv.getLength()); + + sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too + memcpy(&environment[pos], envv.getStr(), n * sizeof(sal_Unicode)); + pos += n; + } + environment[pos] = 0; // append a final '\0' + + return true; + } + + /* In contrast to the Win32 API function CreatePipe with + this function the caller is able to determine separately + which handle of the pipe is inheritable. */ + bool create_pipe( + PHANDLE p_read_pipe, + bool b_read_pipe_inheritable, + PHANDLE p_write_pipe, + bool b_write_pipe_inheritable, + LPVOID p_security_descriptor = nullptr, + DWORD pipe_size = 0) + { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = p_security_descriptor; + sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable; + + bool bRet = false; + HANDLE hTemp = nullptr; + + if (!b_read_pipe_inheritable && b_write_pipe_inheritable) + { + bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size); + + if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, + GetCurrentProcess(), p_read_pipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + CloseHandle(hTemp); + CloseHandle(*p_read_pipe); + return false; + } + } + else if (b_read_pipe_inheritable && !b_write_pipe_inheritable) + { + bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size); + + if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, + GetCurrentProcess(), p_write_pipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + CloseHandle(hTemp); + CloseHandle(*p_write_pipe); + return false; + } + } + else + { + bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size); + } + return bRet; + } + + // Add a quote sign to the start and the end of a string + // if not already present + OUString quote_string(const OUString& string) + { + OUStringBuffer quoted; + if (string.indexOf(L'"') != 0) + quoted.append('"'); + + quoted.append(string); + + if (string.lastIndexOf(L'"') != (string.getLength() - 1)) + quoted.append('"'); + + return quoted.makeStringAndClear(); + } + + // The parameter path must be a system path. If it is longer than 260 characters + // then it is shortened using the GetShortPathName function. This function only + // works if the path exists. Because "path" can be the path to an executable, it + // may not have the file extension ".exe". However, if the file on disk has the + // ".exe" extension, then the function will fail. In this case a second attempt + // is started by adding the parameter "extension" to "path". + OUString getShortPath(OUString const & path, OUString const & extension) + { + OUString ret(path); + if (path.getLength() > 260) + { + std::vector<sal_Unicode> vec(path.getLength() + 1); + //GetShortPathNameW only works if the file can be found! + const DWORD len = GetShortPathNameW( + o3tl::toW(path.getStr()), o3tl::toW(vec.data()), path.getLength() + 1); + + if (!len && GetLastError() == ERROR_FILE_NOT_FOUND + && extension.getLength()) + { + const OUString extPath(path + extension); + std::vector<sal_Unicode> vec2( + extPath.getLength() + 1); + const DWORD len2 = GetShortPathNameW( + o3tl::toW(extPath.getStr()), o3tl::toW(vec2.data()), extPath.getLength() + 1); + ret = OUString(vec2.data(), len2); + } + else + { + ret = OUString(vec.data(), len); + } + } + return ret; + } + + // Returns the system path of the executable which can either + // be provided via the strImageName parameter or as first + // element of the strArguments list. + // The returned path will be quoted if it contains spaces. + OUString get_executable_path( + rtl_uString* image_name, + rtl_uString* cmdline_args[], + sal_uInt32 n_cmdline_args, + bool search_path) + { + OUString exe_name; + + if (image_name) + exe_name = image_name; + else if (n_cmdline_args) + exe_name = OUString(cmdline_args[0]); + + OUString exe_url = exe_name; + if (search_path) + osl_searchFileURL(exe_name.pData, nullptr, &exe_url.pData); + + OUString exe_path; + if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path)) + return OUString(); + + exe_path = getShortPath(exe_path, ".exe"); + + if (exe_path.indexOf(' ') != -1) + exe_path = quote_string(exe_path); + + return exe_path; + } + + OUString get_file_extension(const OUString& file_name) + { + // Quoted file name + if ((file_name.indexOf(L'"') == 0) && (file_name.lastIndexOf(L'"') == (file_name.getLength() - 1))) + { + sal_Int32 index = file_name.lastIndexOf('.'); + if ((index != -1) && ((index + 2) < file_name.getLength())) + return file_name.copy(index + 1, file_name.getLength() - (index + 2)); + } + // Unquoted file name + else + { + sal_Int32 index = file_name.lastIndexOf('.'); + if ((index != -1) && ((index + 1) < file_name.getLength())) + return file_name.copy(index + 1); + } + return OUString(); + } + + bool is_batch_file(const OUString& file_name) + { + OUString ext = get_file_extension(file_name); + return (ext.equalsIgnoreAsciiCase("bat") || + ext.equalsIgnoreAsciiCase("cmd") || + ext.equalsIgnoreAsciiCase("btm")); + } + + const OUString ENV_COMSPEC ("COMSPEC"); + OUString get_batch_processor() + { + OUString comspec; + osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData); + + OSL_ASSERT(comspec.getLength()); + + /* check if comspec path contains blanks and quote it if any */ + if (comspec.indexOf(' ') != -1) + comspec = quote_string(comspec); + + return comspec; + } + +} // namespace private + +oslProcessError SAL_CALL osl_executeProcess( + rtl_uString *strImageName, + rtl_uString *strArguments[], + sal_uInt32 nArguments, + oslProcessOption Options, + oslSecurity Security, + rtl_uString *strDirectory, + rtl_uString *strEnvironmentVars[], + sal_uInt32 nEnvironmentVars, + oslProcess *pProcess +) +{ + return osl_executeProcess_WithRedirectedIO( + strImageName, + strArguments, + nArguments, + Options, + Security, + strDirectory, + strEnvironmentVars, + nEnvironmentVars, + pProcess, + nullptr, nullptr, nullptr ); +} + +oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO( + rtl_uString *ustrImageName, + rtl_uString *ustrArguments[], + sal_uInt32 nArguments, + oslProcessOption Options, + oslSecurity Security, + rtl_uString *ustrDirectory, + rtl_uString *ustrEnvironmentVars[], + sal_uInt32 nEnvironmentVars, + oslProcess *pProcess, + oslFileHandle *pProcessInputWrite, + oslFileHandle *pProcessOutputRead, + oslFileHandle *pProcessErrorRead) +{ + OUString exe_path = get_executable_path( + ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH) != 0); + + if (0 == exe_path.getLength()) + return osl_Process_E_NotFound; + + if (pProcess == nullptr) + return osl_Process_E_InvalidError; + + DWORD flags = NORMAL_PRIORITY_CLASS; + OUStringBuffer command_line; + + if (is_batch_file(exe_path)) + { + OUString batch_processor = get_batch_processor(); + + if (batch_processor.getLength()) + { + /* cmd.exe does not work without a console window */ + if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED)) + flags |= CREATE_NEW_CONSOLE; + + command_line.append(batch_processor); + command_line.append(" /c "); + } + else + // should we return here in case of error? + return osl_Process_E_Unknown; + } + + command_line.append(exe_path); + + /* Add remaining arguments to command line. If ustrImageName is nullptr + the first parameter is the name of the executable so we have to + start at 1 instead of 0 */ + for (sal_uInt32 n = (nullptr != ustrImageName) ? 0 : 1; n < nArguments; n++) + { + command_line.append(" "); + + /* Quote arguments containing blanks */ + if (OUString(ustrArguments[n]).indexOf(' ') != -1) + command_line.append(quote_string(ustrArguments[n])); + else + command_line.append(ustrArguments[n]); + } + + std::vector<sal_Unicode> environment; + LPVOID p_environment = nullptr; + + if (nEnvironmentVars && ustrEnvironmentVars) + { + if (!setup_process_environment( + ustrEnvironmentVars, nEnvironmentVars, environment)) + return osl_Process_E_InvalidError; + + flags |= CREATE_UNICODE_ENVIRONMENT; + p_environment = environment.data(); + } + + OUString cwd; + if (ustrDirectory && ustrDirectory->length && (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd))) + return osl_Process_E_InvalidError; + + LPCWSTR p_cwd = (cwd.getLength()) ? o3tl::toW(cwd.getStr()) : nullptr; + + if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE)) + flags |= DETACHED_PROCESS; + + STARTUPINFOW startup_info = {}; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.lpDesktop = const_cast<LPWSTR>(L""); + + /* Create pipes for redirected IO */ + HANDLE hInputRead = nullptr; + HANDLE hInputWrite = nullptr; + if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false)) + startup_info.hStdInput = hInputRead; + + HANDLE hOutputRead = nullptr; + HANDLE hOutputWrite = nullptr; + if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true)) + startup_info.hStdOutput = hOutputWrite; + + HANDLE hErrorRead = nullptr; + HANDLE hErrorWrite = nullptr; + if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true)) + startup_info.hStdError = hErrorWrite; + + bool b_inherit_handles = false; + if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead) + { + startup_info.dwFlags |= STARTF_USESTDHANDLES; + b_inherit_handles = true; + } + + switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN)) + { + case osl_Process_HIDDEN: + startup_info.wShowWindow = SW_HIDE; + flags |= CREATE_NO_WINDOW; // ignored for non-console + // applications; ignored on + // Win9x + break; + + case osl_Process_MINIMIZED: + startup_info.wShowWindow = SW_MINIMIZE; + break; + + case osl_Process_MAXIMIZED: + case osl_Process_FULLSCREEN: + startup_info.wShowWindow = SW_MAXIMIZE; + break; + + default: + startup_info.wShowWindow = SW_NORMAL; + } + + OUString cmdline = command_line.makeStringAndClear(); + PROCESS_INFORMATION process_info; + bool bRet = false; + + if ((Security != nullptr) && (static_cast<oslSecurityImpl*>(Security)->m_hToken != nullptr)) + { + bRet = CreateProcessAsUserW( + static_cast<oslSecurityImpl*>(Security)->m_hToken, + nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr, + b_inherit_handles, flags, p_environment, p_cwd, + &startup_info, &process_info); + } + else + { + bRet = CreateProcessW( + nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr, + b_inherit_handles, flags, p_environment, p_cwd, + &startup_info, &process_info); + } + + /* Now we can close the pipe ends that are used by the child process */ + + if (hInputRead) + CloseHandle(hInputRead); + + if (hOutputWrite) + CloseHandle(hOutputWrite); + + if (hErrorWrite) + CloseHandle(hErrorWrite); + + if (bRet) + { + CloseHandle(process_info.hThread); + + oslProcessImpl* pProcImpl = static_cast<oslProcessImpl*>( + malloc(sizeof(oslProcessImpl))); + + if (pProcImpl != nullptr) + { + pProcImpl->m_hProcess = process_info.hProcess; + pProcImpl->m_IdProcess = process_info.dwProcessId; + + *pProcess = static_cast<oslProcess>(pProcImpl); + + if (Options & osl_Process_WAIT) + WaitForSingleObject(pProcImpl->m_hProcess, INFINITE); + + if (pProcessInputWrite) + *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write); + + if (pProcessOutputRead) + *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read); + + if (pProcessErrorRead) + *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read); + + return osl_Process_E_None; + } + } + + /* if an error occurred we have to close the server side pipe ends too */ + + if (hInputWrite) + CloseHandle(hInputWrite); + + if (hOutputRead) + CloseHandle(hOutputRead); + + if (hErrorRead) + CloseHandle(hErrorRead); + + return osl_Process_E_Unknown; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/procimpl.hxx b/sal/osl/w32/procimpl.hxx new file mode 100644 index 000000000..fb1263fa7 --- /dev/null +++ b/sal/osl/w32/procimpl.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_OSL_W32_PROCIMPL_HXX +#define INCLUDED_SAL_OSL_W32_PROCIMPL_HXX + +#include <osl/process.h> + +struct oslProcessImpl +{ + HANDLE m_hProcess; + DWORD m_IdProcess; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/profile.cxx b/sal/osl/w32/profile.cxx new file mode 100644 index 000000000..a0790c3de --- /dev/null +++ b/sal/osl/w32/profile.cxx @@ -0,0 +1,2341 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" + +#include "file_url.hxx" +#include "path_helper.hxx" + +#include <string.h> +#include <osl/diagnose.h> +#include <osl/profile.h> +#include <osl/process.h> +#include <osl/thread.h> +#include <osl/file.h> +#include <rtl/alloc.h> +#include <sal/macros.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <algorithm> +#include <vector> +using std::min; +static void copy_ustr_n( void *dest, const void *source, size_t length ) { memcpy(dest, source, length*sizeof(sal_Unicode)); } + +#define LINES_INI 32 +#define LINES_ADD 10 +#define SECTIONS_INI 5 +#define SECTIONS_ADD 3 +#define ENTRIES_INI 5 +#define ENTRIES_ADD 3 + +#define STR_INI_EXTENSION L".ini" +#define STR_INI_METAHOME "?~" +#define STR_INI_METASYS "?$" +#define STR_INI_METACFG "?^" +#define STR_INI_METAINS "?#" + +#define STR_INI_BOOLYES "yes" +#define STR_INI_BOOLON "on" +#define STR_INI_BOOLONE "1" +#define STR_INI_BOOLNO "no" +#define STR_INI_BOOLOFF "off" +#define STR_INI_BOOLZERO "0" + +#define FLG_USER 0x00FF +#define FLG_AUTOOPEN 0x0100 +#define FLG_MODIFIED 0x0200 + +#define SVERSION_LOCATION STR_INI_METACFG +#define SVERSION_FALLBACK STR_INI_METASYS +#define SVERSION_NAME "sversion" +#define SVERSION_SECTION "Versions" +#define SVERSION_SOFFICE "StarOffice" +#define SVERSION_PROFILE "soffice.ini" +#define SVERSION_DIRS { "bin", "program" } +#define SVERSION_USER "user" + +/*#define DEBUG_OSL_PROFILE 1*/ + +typedef FILETIME osl_TStamp; + +namespace { + +enum osl_TLockMode +{ + un_lock, read_lock, write_lock +}; + +struct osl_TFile +{ + HANDLE m_Handle; + char* m_pReadPtr; + char m_ReadBuf[512]; + char* m_pWriteBuf; + sal_uInt32 m_nWriteBufLen; + sal_uInt32 m_nWriteBufFree; +}; + +struct osl_TProfileEntry +{ + sal_uInt32 m_Line; + sal_uInt32 m_Offset; + sal_uInt32 m_Len; +}; + +struct osl_TProfileSection +{ + sal_uInt32 m_Line; + sal_uInt32 m_Offset; + sal_uInt32 m_Len; + sal_uInt32 m_NoEntries; + sal_uInt32 m_MaxEntries; + osl_TProfileEntry* m_Entries; +}; + +/* + Profile-data structure hidden behind oslProfile: +*/ +struct osl_TProfileImpl +{ + sal_uInt32 m_Flags; + osl_TFile* m_pFile; + osl_TStamp m_Stamp; + sal_uInt32 m_NoLines; + sal_uInt32 m_MaxLines; + sal_uInt32 m_NoSections; + sal_uInt32 m_MaxSections; + char** m_Lines; + rtl_uString *m_strFileName; + osl_TProfileSection* m_Sections; +}; + +} + +static osl_TFile* openFileImpl(rtl_uString * strFileName, oslProfileOption ProfileFlags ); +static osl_TStamp closeFileImpl(osl_TFile* pFile); +static bool lockFile(const osl_TFile* pFile, osl_TLockMode eMode); +static bool rewindFile(osl_TFile* pFile, bool bTruncate); +static osl_TStamp getFileStamp(osl_TFile* pFile); + +static bool getLine(osl_TFile* pFile, char *pszLine, int MaxLen); +static bool putLine(osl_TFile* pFile, const char *pszLine); +static const char* stripBlanks(const char* String, sal_uInt32* pLen); +static const char* addLine(osl_TProfileImpl* pProfile, const char* Line); +static const char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo); +static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo); +static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection, + sal_uInt32 NoEntry, sal_uInt32 Line, + const char* Entry, sal_uInt32 Len); +static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection, + int Line, const char* Entry, sal_uInt32 Len); +static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry); +static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len); +static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection); +static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section, + const char* Entry, sal_uInt32 *pNoEntry); +static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile); +static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup); +static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable); +static bool releaseProfile(osl_TProfileImpl* pProfile); +static bool lookupProfile(const sal_Unicode *strPath, const sal_Unicode *strFile, sal_Unicode *strProfile); + +static bool writeProfileImpl (osl_TFile* pFile); +static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl*); +static bool osl_ProfileSwapProfileNames(osl_TProfileImpl*); +static rtl_uString* osl_ProfileGenerateExtension(rtl_uString* ustrFileName, rtl_uString* ustrExtension); + +static bool osl_getProfileName(rtl_uString* strPath, rtl_uString* strName, rtl_uString** strProfileName); + +oslProfile SAL_CALL osl_openProfile(rtl_uString *strProfileName, oslProfileOption Flags) +{ + osl_TFile* pFile = nullptr; + osl_TProfileImpl* pProfile; + rtl_uString *FileName=nullptr; + + OSL_VERIFY(strProfileName); + + if (rtl_uString_getLength(strProfileName) == 0 ) + { + OSL_VERIFY(osl_getProfileName(nullptr, nullptr, &FileName)); + } + else + { + rtl_uString_assign(&FileName, strProfileName); + } + + osl_getSystemPathFromFileURL(FileName, &FileName); + +#ifdef DEBUG_OSL_PROFILE + Flags=osl_Profile_FLUSHWRITE; + + if ( Flags == osl_Profile_DEFAULT ) + { + SAL_INFO("sal.osl", "with osl_Profile_DEFAULT"); + } + if ( Flags & osl_Profile_SYSTEM ) + { + SAL_INFO("sal.osl", "with osl_Profile_SYSTEM"); + } + if ( Flags & osl_Profile_READLOCK ) + { + SAL_INFO("sal.osl", "with osl_Profile_READLOCK"); + } + if ( Flags & osl_Profile_WRITELOCK ) + { + SAL_INFO("sal.osl", "with osl_Profile_WRITELOCK"); + } + if ( Flags & osl_Profile_FLUSHWRITE ) + { + SAL_INFO("sal.osl", "with osl_Profile_FLUSHWRITE"); + } +#endif + + if ( (! (Flags & osl_Profile_SYSTEM)) && ( (pFile = openFileImpl(FileName, Flags) ) == nullptr ) ) + { + if( FileName) + rtl_uString_release( FileName); + + return nullptr; + } + + pProfile = static_cast<osl_TProfileImpl*>(calloc(1, sizeof(osl_TProfileImpl))); + if (!pProfile) + return nullptr; + + pProfile->m_Flags = Flags & FLG_USER; + osl_getSystemPathFromFileURL(strProfileName, &pProfile->m_strFileName); + + if (Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )) + pProfile->m_pFile = pFile; + + pProfile->m_Stamp = getFileStamp(pFile); + + loadProfile(pFile, pProfile); + + if (pProfile->m_pFile == nullptr) + closeFileImpl(pFile); + + if( FileName) + rtl_uString_release( FileName); + + return pProfile; +} + +sal_Bool SAL_CALL osl_closeProfile(oslProfile Profile) +{ + osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile); + + + if ( Profile == nullptr ) + { + return false; + } + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + pProfile = acquireProfile(Profile,true); + + if ( pProfile != nullptr ) + { + if ( !( pProfile->m_Flags & osl_Profile_READLOCK ) && ( pProfile->m_Flags & FLG_MODIFIED ) ) + { + storeProfile(pProfile, false); + } + } + else + { + pProfile = acquireProfile(Profile,false); + } + + if ( pProfile == nullptr ) + { + return false; + } + + if (pProfile->m_pFile != nullptr) + closeFileImpl(pProfile->m_pFile); + } + + pProfile->m_pFile = nullptr; + rtl_uString_release(pProfile->m_strFileName); + pProfile->m_strFileName = nullptr; + + /* release whole profile data types memory */ + if ( pProfile->m_NoLines > 0) + { + unsigned int index=0; + if ( pProfile->m_Lines != nullptr ) + { + for ( index = 0 ; index < pProfile->m_NoLines ; ++index) + { + if ( pProfile->m_Lines[index] != nullptr ) + { + free(pProfile->m_Lines[index]); + } + } + free(pProfile->m_Lines); + } + if ( pProfile->m_Sections != nullptr ) + { + for ( index = 0 ; index < pProfile->m_NoSections ; ++index ) + { + if ( pProfile->m_Sections[index].m_Entries != nullptr ) + free(pProfile->m_Sections[index].m_Entries); + } + free(pProfile->m_Sections); + } + + } + free(pProfile); + + return true; +} + +sal_Bool SAL_CALL osl_flushProfile(oslProfile Profile) +{ + osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile); + osl_TFile* pFile; + bool bRet = false; + + + if ( pProfile == nullptr ) + { + return false; + } + + pFile = pProfile->m_pFile; + if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE ) + { + return false; + } + + if ( pProfile->m_Flags & FLG_MODIFIED ) + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "swapping to storeprofile"); +#endif + bRet = storeProfile(pProfile,false); + } + + return bRet; +} + +static bool writeProfileImpl(osl_TFile* pFile) +{ + DWORD BytesWritten=0; + bool bRet; + + if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE || ( pFile->m_pWriteBuf == nullptr ) ) + { + return false; + } + + bRet=WriteFile(pFile->m_Handle, pFile->m_pWriteBuf, pFile->m_nWriteBufLen - pFile->m_nWriteBufFree,&BytesWritten,nullptr); + + if ( !bRet || BytesWritten == 0 ) + { + OSL_ENSURE(bRet,"WriteFile failed!!!"); + SAL_WARN("sal.osl", "write failed " << strerror(errno)); + + return false; + } + + free(pFile->m_pWriteBuf); + pFile->m_pWriteBuf=nullptr; + pFile->m_nWriteBufLen=0; + pFile->m_nWriteBufFree=0; + + return true; +} + +namespace { +// Use Unicode version of GetPrivateProfileString, to work with Multi-language paths +DWORD GetPrivateProfileStringWrapper(const osl_TProfileImpl* pProfile, + const char* pszSection, const char* pszEntry, + char* pszString, sal_uInt32 MaxLen, + const char* pszDefault) +{ + OSL_ASSERT(pProfile && (!MaxLen || pszString)); + + rtl_uString *pSection = nullptr, *pEntry = nullptr, *pDefault = nullptr; + if (pszSection) + { + rtl_string2UString(&pSection, pszSection, strlen(pszSection), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pSection); + } + if (pszEntry) + { + rtl_string2UString(&pEntry, pszEntry, strlen(pszEntry), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pEntry); + } + if (pszDefault) + { + rtl_string2UString(&pDefault, pszDefault, strlen(pszDefault), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pDefault); + } + + LPCWSTR pWSection = (pSection ? o3tl::toW(rtl_uString_getStr(pSection)) : nullptr), + pWEntry = (pEntry ? o3tl::toW(rtl_uString_getStr(pEntry)) : nullptr), + pWDefault = (pDefault ? o3tl::toW(rtl_uString_getStr(pDefault)) : nullptr); + + std::vector<wchar_t> aBuf(MaxLen + 1); + GetPrivateProfileStringW(pWSection, pWEntry, pWDefault, aBuf.data(), MaxLen, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName))); + + if (pDefault) + rtl_uString_release(pDefault); + if (pEntry) + rtl_uString_release(pEntry); + if (pSection) + rtl_uString_release(pSection); + + return WideCharToMultiByte(CP_ACP, 0, aBuf.data(), -1, pszString, MaxLen, nullptr, nullptr); +} + +// Use Unicode version of WritePrivateProfileString, to work with Multi-language paths +bool WritePrivateProfileStringWrapper(const osl_TProfileImpl* pProfile, + const char* pszSection, const char* pszEntry, + const char* pszString) +{ + OSL_ASSERT(pProfile && pszSection); + rtl_uString *pSection, *pEntry = nullptr, *pString = nullptr; + rtl_string2UString(&pSection, pszSection, strlen(pszSection), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pSection); + if (pszEntry) + { + rtl_string2UString(&pEntry, pszEntry, strlen(pszEntry), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pEntry); + } + if (pszString) + { + rtl_string2UString(&pString, pszString, strlen(pszString), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS); + OSL_ASSERT(pString); + } + + LPCWSTR pWSection = o3tl::toW(pSection->buffer), + pWEntry = (pEntry ? o3tl::toW(rtl_uString_getStr(pEntry)) : nullptr), + pWString = (pString ? o3tl::toW(rtl_uString_getStr(pString)) : nullptr); + + bool bResult = WritePrivateProfileStringW(pWSection, pWEntry, pWString, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName))); + + if (pString) + rtl_uString_release(pString); + if (pEntry) + rtl_uString_release(pEntry); + rtl_uString_release(pSection); + + return bResult; +} +} + +sal_Bool SAL_CALL osl_readProfileString(oslProfile Profile, + const char* pszSection, const char* pszEntry, + char* pszString, sal_uInt32 MaxLen, + const char* pszDefault) +{ + sal_uInt32 NoEntry; + const char* pStr = nullptr; + osl_TProfileImpl* pProfile = nullptr; + + pProfile = acquireProfile(Profile, false); + + if (pProfile == nullptr) + { + return false; + } + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + osl_TProfileSection* pSec; + if (((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) != nullptr) && + (NoEntry < pSec->m_NoEntries) && + ((pStr = strchr(pProfile->m_Lines[pSec->m_Entries[NoEntry].m_Line], + '=')) != nullptr)) + pStr++; + else + pStr = pszDefault; + + if ( pStr != nullptr ) + { + pStr = stripBlanks(pStr, nullptr); + MaxLen = (MaxLen - 1 < strlen(pStr)) ? (MaxLen - 1) : strlen(pStr); + pStr = stripBlanks(pStr, &MaxLen); + strncpy(pszString, pStr, MaxLen); + pszString[MaxLen] = '\0'; + } + } + else + { + if (GetPrivateProfileStringWrapper(pProfile, pszSection, pszEntry, pszString, MaxLen, pszDefault) > 0) + pStr = pszString; // required to return true below + } + + releaseProfile(pProfile); + + if ( pStr == nullptr ) + { + return false; + } + + return true; +} + +sal_Bool SAL_CALL osl_readProfileBool(oslProfile Profile, + const char* pszSection, const char* pszEntry, + sal_Bool Default) +{ + char Line[32]; + + if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), "")) + { + if ((stricmp(Line, STR_INI_BOOLYES) == 0) || + (stricmp(Line, STR_INI_BOOLON) == 0) || + (stricmp(Line, STR_INI_BOOLONE) == 0)) + Default = true; + else + if ((stricmp(Line, STR_INI_BOOLNO) == 0) || + (stricmp(Line, STR_INI_BOOLOFF) == 0) || + (stricmp(Line, STR_INI_BOOLZERO) == 0)) + Default = false; + } + + return Default; +} + +sal_uInt32 SAL_CALL osl_readProfileIdent(oslProfile Profile, + const char* pszSection, const char* pszEntry, + sal_uInt32 FirstId, const char* Strings[], + sal_uInt32 Default) +{ + sal_uInt32 i; + char Line[256]; + + if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), "")) + { + i = 0; + while (Strings[i] != nullptr) + { + if (stricmp(Line, Strings[i]) == 0) + { + Default = i + FirstId; + break; + } + i++; + } + } + + return Default; +} + +sal_Bool SAL_CALL osl_writeProfileString(oslProfile Profile, + const char* pszSection, const char* pszEntry, + const char* pszString) +{ + sal_uInt32 i; + bool bRet = false; + sal_uInt32 NoEntry; + const char* pStr; + char Line[4096]; + osl_TProfileSection* pSec; + osl_TProfileImpl* pProfile = nullptr; + + pProfile = acquireProfile(Profile, true); + + if (pProfile == nullptr) + { + return false; + } + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + if ((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) == nullptr) + { + Line[0] = '\0'; + addLine(pProfile, Line); + + Line[0] = '['; + strcpy(&Line[1], pszSection); + Line[1 + strlen(pszSection)] = ']'; + Line[2 + strlen(pszSection)] = '\0'; + + if (((pStr = addLine(pProfile, Line)) == nullptr) || + (! addSection(pProfile, pProfile->m_NoLines - 1, &pStr[1], strlen(pszSection)))) + { + releaseProfile(pProfile); + return false; + } + + pSec = &pProfile->m_Sections[pProfile->m_NoSections - 1]; + NoEntry = pSec->m_NoEntries; + } + + Line[0] = '\0'; + strcpy(&Line[0], pszEntry); + Line[0 + strlen(pszEntry)] = '='; + strcpy(&Line[1 + strlen(pszEntry)], pszString); + + if (NoEntry >= pSec->m_NoEntries) + { + if (pSec->m_NoEntries > 0) + i = pSec->m_Entries[pSec->m_NoEntries - 1].m_Line + 1; + else + i = pSec->m_Line + 1; + + if (((pStr = insertLine(pProfile, Line, i)) == nullptr) || + (! addEntry(pProfile, pSec, i, pStr, strlen(pszEntry)))) + { + releaseProfile(pProfile); + return false; + } + + pProfile->m_Flags |= FLG_MODIFIED; + } + else + { + i = pSec->m_Entries[NoEntry].m_Line; + free(pProfile->m_Lines[i]); + pProfile->m_Lines[i] = strdup(Line); + setEntry(pProfile, pSec, NoEntry, i, pProfile->m_Lines[i], strlen(pszEntry)); + + pProfile->m_Flags |= FLG_MODIFIED; + } + } + else + { + WritePrivateProfileStringWrapper(pProfile, pszSection, pszEntry, pszString); + } + + bRet = releaseProfile(pProfile); + return bRet; +} + +sal_Bool SAL_CALL osl_writeProfileBool(oslProfile Profile, + const char* pszSection, const char* pszEntry, + sal_Bool Value) +{ + bool bRet = false; + + if (Value) + bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLONE); + else + bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLZERO); + + return bRet; +} + +sal_Bool SAL_CALL osl_writeProfileIdent(oslProfile Profile, + const char* pszSection, const char* pszEntry, + sal_uInt32 FirstId, const char* Strings[], + sal_uInt32 Value) +{ + int i, n; + bool bRet = false; + + for (n = 0; Strings[n] != nullptr; n++); + + if ((i = Value - FirstId) >= n) + bRet=false; + else + bRet=osl_writeProfileString(Profile, pszSection, pszEntry, Strings[i]); + + return bRet; +} + +sal_Bool SAL_CALL osl_removeProfileEntry(oslProfile Profile, + const char *pszSection, const char *pszEntry) +{ + sal_uInt32 NoEntry; + osl_TProfileImpl* pProfile = nullptr; + bool bRet = false; + + pProfile = acquireProfile(Profile, true); + + if (pProfile == nullptr) + return false; + + if (!(pProfile->m_Flags & osl_Profile_SYSTEM)) + { + osl_TProfileSection* pSec; + if (((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) != nullptr) && + (NoEntry < pSec->m_NoEntries)) + { + removeLine(pProfile, pSec->m_Entries[NoEntry].m_Line); + removeEntry(pSec, NoEntry); + if (pSec->m_NoEntries == 0) + { + removeLine(pProfile, pSec->m_Line); + + /* remove any empty separation line */ + if ((pSec->m_Line > 0) && (pProfile->m_Lines[pSec->m_Line - 1][0] == '\0')) + removeLine(pProfile, pSec->m_Line - 1); + + removeSection(pProfile, pSec); + } + + pProfile->m_Flags |= FLG_MODIFIED; + } + } + else + { + WritePrivateProfileStringWrapper(pProfile, pszSection, pszEntry, nullptr); + } + + bRet = releaseProfile(pProfile); + return bRet; +} + +sal_uInt32 SAL_CALL osl_getProfileSectionEntries(oslProfile Profile, const char *pszSection, + char* pszBuffer, sal_uInt32 MaxLen) +{ + sal_uInt32 i, n = 0; + sal_uInt32 NoEntry; + osl_TProfileImpl* pProfile = nullptr; + + pProfile = acquireProfile(Profile, false); + + if (pProfile == nullptr) + return 0; + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + osl_TProfileSection* pSec; + if ((pSec = findEntry(pProfile, pszSection, "", &NoEntry)) != nullptr) + { + if (MaxLen != 0) + { + for (i = 0; i < pSec->m_NoEntries; i++) + { + if ((n + pSec->m_Entries[i].m_Len + 1) < MaxLen) + { + strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Entries[i].m_Line] + [pSec->m_Entries[i].m_Offset], pSec->m_Entries[i].m_Len); + n += pSec->m_Entries[i].m_Len; + pszBuffer[n++] = '\0'; + } + else + { + break; + } + + } + + pszBuffer[n++] = '\0'; + } + else + { + for (i = 0; i < pSec->m_NoEntries; i++) + { + n += pSec->m_Entries[i].m_Len + 1; + } + + n += 1; + } + } + else + { + n = 0; + } + } + else + { + n = GetPrivateProfileStringWrapper(pProfile, pszSection, nullptr, pszBuffer, MaxLen, nullptr); + } + + releaseProfile(pProfile); + + return n; +} + +bool osl_getProfileName(rtl_uString* strPath, rtl_uString* strName, rtl_uString** strProfileName) +{ + bool bFailed; + ::osl::LongPathBuffer< sal_Unicode > aFile( MAX_LONG_PATH ); + ::osl::LongPathBuffer< sal_Unicode > aPath( MAX_LONG_PATH ); + sal_uInt32 nFileLen = 0; + sal_uInt32 nPathLen = 0; + + rtl_uString * strTmp = nullptr; + oslFileError nError; + + /* build file name */ + if (strName && strName->length) + { + if( ::sal::static_int_cast< sal_uInt32 >( strName->length ) >= aFile.getBufSizeInSymbols() ) + return false; + + copy_ustr_n( aFile, strName->buffer, strName->length+1); + nFileLen = strName->length; + + if (rtl_ustr_indexOfChar( aFile, L'.' ) == -1) + { + if (nFileLen + wcslen(STR_INI_EXTENSION) >= aFile.getBufSizeInSymbols()) + return false; + + /* add default extension */ + copy_ustr_n( aFile + nFileLen, STR_INI_EXTENSION, wcslen(STR_INI_EXTENSION)+1 ); + nFileLen += wcslen(STR_INI_EXTENSION); + } + } + else + { + rtl_uString *strProgName = nullptr; + sal_Unicode *pProgName; + sal_Int32 nOffset = 0; + sal_Int32 nLen; + sal_Int32 nPos; + + if (osl_getExecutableFile(&strProgName) != osl_Process_E_None) + return false; + + /* remove path and extension from filename */ + pProgName = strProgName->buffer; + nLen = strProgName->length ; + + if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L'/' )) != -1) + nOffset = nPos + 1; + else if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L':' )) != -1) + nOffset = nPos + 1; + + if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L'.' )) != -1 ) + nLen -= 4; + + if ((nFileLen = nLen - nOffset) >= aFile.getBufSizeInSymbols()) + return false; + + copy_ustr_n(aFile, pProgName + nOffset, nFileLen); + + if (nFileLen + wcslen(STR_INI_EXTENSION) >= aFile.getBufSizeInSymbols()) + return false; + + /* add default extension */ + copy_ustr_n(aFile + nFileLen, STR_INI_EXTENSION, wcslen(STR_INI_EXTENSION)+1); + nFileLen += wcslen(STR_INI_EXTENSION); + + rtl_uString_release( strProgName ); + } + + if (aFile[0] == 0) + return false; + + /* build directory path */ + if (strPath && strPath->length) + { + sal_Unicode *pPath = rtl_uString_getStr(strPath); + sal_Int32 nLen = rtl_uString_getLength(strPath); + + if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METAHOME) , STR_INI_METAHOME) == 0) && + ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METAHOME)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAHOME)] == '/'))) + { + rtl_uString * strHome = nullptr; + oslSecurity security = osl_getCurrentSecurity(); + + bFailed = ! osl_getHomeDir(security, &strHome); + osl_freeSecurityHandle(security); + + if (bFailed) return false; + + if ( ::sal::static_int_cast< sal_uInt32 >( strHome->length ) >= aPath.getBufSizeInSymbols()) + return false; + + copy_ustr_n( aPath, strHome->buffer, strHome->length+1); + nPathLen = strHome->length; + + if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METAHOME)) + { + pPath += RTL_CONSTASCII_LENGTH(STR_INI_METAHOME); + nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METAHOME); + + if (nLen + nPathLen >= aPath.getBufSizeInSymbols()) + return false; + + copy_ustr_n(aPath + nPathLen, pPath, nLen+1); + nPathLen += nLen; + } + + rtl_uString_release(strHome); + } + + else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METACFG), STR_INI_METACFG) == 0) && + ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METACFG)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METACFG)] == '/'))) + { + rtl_uString * strConfig = nullptr; + oslSecurity security = osl_getCurrentSecurity(); + + bFailed = ! osl_getConfigDir(security, &strConfig); + osl_freeSecurityHandle(security); + + if (bFailed) return false; + + if ( ::sal::static_int_cast< sal_uInt32 >( strConfig->length ) >= aPath.getBufSizeInSymbols()) + return false; + + copy_ustr_n( aPath, strConfig->buffer, strConfig->length+1 ); + nPathLen = strConfig->length; + + if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METACFG)) + { + pPath += RTL_CONSTASCII_LENGTH(STR_INI_METACFG); + nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METACFG); + + if (nLen + nPathLen >= aPath.getBufSizeInSymbols()) + return false; + + copy_ustr_n(aPath + nPathLen, pPath, nLen+1); + nPathLen += nLen; + } + + rtl_uString_release(strConfig); + } + + else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METASYS), STR_INI_METASYS) == 0) && + ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METASYS)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METASYS)] == '/'))) + { + if (((nPathLen = GetWindowsDirectoryW(o3tl::toW(aPath), aPath.getBufSizeInSymbols())) == 0) || (nPathLen >= aPath.getBufSizeInSymbols())) + return false; + + if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METASYS)) + { + pPath += RTL_CONSTASCII_LENGTH(STR_INI_METASYS); + nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METASYS); + + if (nLen + nPathLen >= aPath.getBufSizeInSymbols()) + return false; + + copy_ustr_n(aPath + nPathLen, pPath, nLen+1); + nPathLen += nLen; + } + } + + else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METAINS), STR_INI_METAINS) == 0) && + ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METAINS)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAINS)] == '/') || + (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAINS)] == '"') ) ) + { + if (! lookupProfile(pPath + RTL_CONSTASCII_LENGTH(STR_INI_METAINS), aFile, aPath)) + return false; + + nPathLen = rtl_ustr_getLength(aPath); + } + + else if( ::sal::static_int_cast< sal_uInt32 >( nLen ) < aPath.getBufSizeInSymbols()) + { + copy_ustr_n(aPath, pPath, nLen+1); + nPathLen = rtl_ustr_getLength(aPath); + } + else + return false; + } + else + { + rtl_uString * strConfigDir = nullptr; + oslSecurity security = osl_getCurrentSecurity(); + + bFailed = ! osl_getConfigDir(security, &strConfigDir); + osl_freeSecurityHandle(security); + + if (bFailed) return false; + if ( ::sal::static_int_cast< sal_uInt32 >( strConfigDir->length ) >= aPath.getBufSizeInSymbols() ) + return false; + + copy_ustr_n(aPath, strConfigDir->buffer, strConfigDir->length+1); + nPathLen = strConfigDir->length; + } + + if (nPathLen && (aPath[nPathLen - 1] != L'/') && (aPath[nPathLen - 1] != L'\\')) + { + aPath[nPathLen++] = L'\\'; + aPath[nPathLen] = 0; + } + + if (nPathLen + nFileLen >= aPath.getBufSizeInSymbols()) + return false; + + /* append file name */ + copy_ustr_n(aPath + nPathLen, aFile, nFileLen+1); + nPathLen += nFileLen; + + /* copy filename */ + rtl_uString_newFromStr_WithLength(&strTmp, aPath, nPathLen); + nError = osl_getFileURLFromSystemPath(strTmp, strProfileName); + rtl_uString_release(strTmp); + + return nError == osl_File_E_None; +} + +sal_uInt32 SAL_CALL osl_getProfileSections(oslProfile Profile, char* pszBuffer, sal_uInt32 MaxLen) +{ + sal_uInt32 i, n = 0; + osl_TProfileImpl* pProfile = acquireProfile(Profile, false); + + if (pProfile == nullptr) + return 0; + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + if (MaxLen != 0) + { + for (i = 0; i < pProfile->m_NoSections; i++) + { + osl_TProfileSection* pSec = &pProfile->m_Sections[i]; + + if ((n + pSec->m_Len + 1) < MaxLen) + { + strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], + pSec->m_Len); + n += pSec->m_Len; + pszBuffer[n++] = '\0'; + } + else + break; + } + + pszBuffer[n++] = '\0'; + } + else + { + for (i = 0; i < pProfile->m_NoSections; i++) + n += pProfile->m_Sections[i].m_Len + 1; + + n += 1; + } + } + else + { + std::vector<wchar_t> aBuf(MaxLen + 1); + GetPrivateProfileSectionNamesW(aBuf.data(), MaxLen, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName))); + + n = WideCharToMultiByte(CP_ACP, 0, aBuf.data(), -1, pszBuffer, MaxLen, nullptr, nullptr); + } + + releaseProfile(pProfile); + + return n; +} + +static osl_TStamp getFileStamp(osl_TFile* pFile) +{ + FILETIME FileTime; + + if ((pFile->m_Handle == INVALID_HANDLE_VALUE) || + (! GetFileTime(pFile->m_Handle, nullptr, nullptr, &FileTime))) + memset(&FileTime, 0, sizeof(FileTime)); + + return FileTime; +} + +static bool lockFile(const osl_TFile* pFile, osl_TLockMode eMode) +{ + bool status = false; + OVERLAPPED Overlapped = {}; + + if (pFile->m_Handle == INVALID_HANDLE_VALUE) + return false; + + switch (eMode) + { + case un_lock: + status = UnlockFileEx( + pFile->m_Handle, 0, 0xFFFFFFFF, 0, &Overlapped); + break; + + case read_lock: + status = LockFileEx( + pFile->m_Handle, 0, 0, 0xFFFFFFFF, 0, &Overlapped); + break; + + case write_lock: + status = LockFileEx( + pFile->m_Handle, LOCKFILE_EXCLUSIVE_LOCK, 0, 0xFFFFFFFF, 0, + &Overlapped); + break; + } + + return status; +} + +static osl_TFile* openFileImpl(rtl_uString * strFileName, oslProfileOption ProfileFlags ) +{ + osl_TFile* pFile = static_cast< osl_TFile*>( calloc( 1, sizeof(osl_TFile) ) ); + if (!pFile) + return nullptr; + bool bWriteable = false; + + if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) ) + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "setting bWriteable to TRUE"); +#endif + bWriteable=true; + } + + if (! bWriteable) + { + pFile->m_Handle = CreateFileW( o3tl::toW(rtl_uString_getStr( strFileName )), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + + /* mfe: argghh!!! do not check if the file could be opened */ + /* default mode expects it that way!!! */ + } + else + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "opening read/write " << pszFilename); +#endif + + if ((pFile->m_Handle = CreateFileW( o3tl::toW(rtl_uString_getStr( strFileName )), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) + == INVALID_HANDLE_VALUE) + { + free(pFile); + return nullptr; + } + } + + pFile->m_pWriteBuf=nullptr; + pFile->m_nWriteBufFree=0; + pFile->m_nWriteBufLen=0; + + if ( ProfileFlags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) ) + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "locking file " << pszFilename); +#endif + + lockFile(pFile, bWriteable ? write_lock : read_lock); + } + + return pFile; +} + +static osl_TStamp closeFileImpl(osl_TFile* pFile) +{ + osl_TStamp stamp = {0, 0}; + + if ( pFile == nullptr ) + { + return stamp; + } + + if (pFile->m_Handle != INVALID_HANDLE_VALUE) + { + stamp = getFileStamp(pFile); + + lockFile(pFile, un_lock); + + CloseHandle(pFile->m_Handle); + pFile->m_Handle = INVALID_HANDLE_VALUE; + } + + if ( pFile->m_pWriteBuf != nullptr ) + { + free(pFile->m_pWriteBuf); + } + + free(pFile); + + return stamp; +} + +static bool rewindFile(osl_TFile* pFile, bool bTruncate) +{ + if (pFile->m_Handle != INVALID_HANDLE_VALUE) + { + pFile->m_pReadPtr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf); + + SetFilePointer(pFile->m_Handle, 0, nullptr, FILE_BEGIN); + + if (bTruncate) + SetEndOfFile(pFile->m_Handle); + } + + return true; +} + +static bool getLine(osl_TFile* pFile, char *pszLine, int MaxLen) +{ + DWORD Max; + size_t Free; + char* pChr; + char* pLine = pszLine; + + if (pFile->m_Handle == INVALID_HANDLE_VALUE) + return false; + + MaxLen -= 1; + + do + { + size_t Bytes = sizeof(pFile->m_ReadBuf) - (pFile->m_pReadPtr - pFile->m_ReadBuf); + + if (Bytes <= 1) + { + /* refill buffer */ + memcpy(pFile->m_ReadBuf, pFile->m_pReadPtr, Bytes); + pFile->m_pReadPtr = pFile->m_ReadBuf; + + Free = sizeof(pFile->m_ReadBuf) - Bytes; + + if (! ReadFile(pFile->m_Handle, &pFile->m_ReadBuf[Bytes], Free, &Max, nullptr)) + { + *pLine = '\0'; + return false; + } + + if (Max < Free) + { + if ((Max == 0) && (pLine == pszLine)) + { + *pLine = '\0'; + return false; + } + + pFile->m_ReadBuf[Bytes + Max] = '\0'; + } + } + + for (pChr = pFile->m_pReadPtr; + (*pChr != '\n') && (*pChr != '\r') && (*pChr != '\0') && + (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1)); + pChr++); + + Max = min(static_cast<int>(pChr - pFile->m_pReadPtr), MaxLen); + memcpy(pLine, pFile->m_pReadPtr, Max); + MaxLen -= Max; + pLine += Max; + + if (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1)) + { + if (*pChr != '\0') + { + if ((pChr[0] == '\r') && (pChr[1] == '\n')) + pChr += 2; + else + pChr += 1; + } + + if ((pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf))) && + (*pChr == '\0')) + pChr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf); + + *pLine = '\0'; + + /* setting MaxLen to -1 indicates terminating read loop */ + MaxLen = -1; + } + + pFile->m_pReadPtr = pChr; + } + while (MaxLen > 0); + + return true; +} + +static bool putLine(osl_TFile* pFile, const char *pszLine) +{ + unsigned int Len = strlen(pszLine); + + if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE ) + { + return false; + } + + if ( pFile->m_pWriteBuf == nullptr ) + { + pFile->m_pWriteBuf = static_cast<char*>(malloc(Len+3)); + pFile->m_nWriteBufLen = Len+3; + pFile->m_nWriteBufFree = Len+3; + } + else + { + if ( pFile->m_nWriteBufFree <= Len + 3 ) + { + char* pTmp; + + pTmp=static_cast<char*>(realloc(pFile->m_pWriteBuf,( ( pFile->m_nWriteBufLen + Len ) * 2) )); + if ( pTmp == nullptr ) + { + return false; + } + pFile->m_pWriteBuf = pTmp; + pFile->m_nWriteBufFree = pFile->m_nWriteBufFree + pFile->m_nWriteBufLen + ( 2 * Len ); + pFile->m_nWriteBufLen = ( pFile->m_nWriteBufLen + Len ) * 2; + memset( (pFile->m_pWriteBuf) + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ), 0, pFile->m_nWriteBufFree); + } + } + + memcpy(pFile->m_pWriteBuf + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ),pszLine,Len+1); + + pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len]='\r'; + pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 1]='\n'; + pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 2]='\0'; + + pFile->m_nWriteBufFree-=Len+2; + + return true; +} + +/* platform specific end */ + +static const char* stripBlanks(const char* String, sal_uInt32* pLen) +{ + if ( (pLen != nullptr) && ( *pLen != 0 ) ) + { + while ((String[*pLen - 1] == ' ') || (String[*pLen - 1] == '\t')) + (*pLen)--; + + while ((*String == ' ') || (*String == '\t')) + { + String++; + (*pLen)--; + } + } + else + while ((*String == ' ') || (*String == '\t')) + String++; + + return String; +} + +static const char* addLine(osl_TProfileImpl* pProfile, const char* Line) +{ + if (pProfile->m_NoLines >= pProfile->m_MaxLines) + { + if (pProfile->m_Lines == nullptr) + { + pProfile->m_MaxLines = LINES_INI; + pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *))); + } + else + { + unsigned int index=0; + unsigned int oldmax=pProfile->m_MaxLines; + + pProfile->m_MaxLines += LINES_ADD; + if (auto p = static_cast<char **>(realloc(pProfile->m_Lines, pProfile->m_MaxLines * sizeof(char *)))) + { + pProfile->m_Lines = p; + + for ( index = oldmax ; index < pProfile->m_MaxLines ; ++index ) + { + pProfile->m_Lines[index]=nullptr; + } + } + else + { + free(pProfile->m_Lines); + pProfile->m_Lines = nullptr; + } + } + + if (pProfile->m_Lines == nullptr) + { + pProfile->m_NoLines = 0; + pProfile->m_MaxLines = 0; + return nullptr; + } + + } + + if ( pProfile->m_Lines != nullptr && pProfile->m_Lines[pProfile->m_NoLines] != nullptr ) + { + free(pProfile->m_Lines[pProfile->m_NoLines]); + } + pProfile->m_Lines[pProfile->m_NoLines++] = strdup(Line); + + return pProfile->m_Lines[pProfile->m_NoLines - 1]; +} + +static const char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo) +{ + if (pProfile->m_NoLines >= pProfile->m_MaxLines) + { + if (pProfile->m_Lines == nullptr) + { + pProfile->m_MaxLines = LINES_INI; + pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *))); + } + else + { + pProfile->m_MaxLines += LINES_ADD; + if (auto p = static_cast<char**>( + realloc(pProfile->m_Lines, pProfile->m_MaxLines * sizeof(char*)))) + { + pProfile->m_Lines = p; + + memset(&pProfile->m_Lines[pProfile->m_NoLines], 0, + (pProfile->m_MaxLines - pProfile->m_NoLines - 1) * sizeof(char*)); + } + else + { + free(pProfile->m_Lines); + pProfile->m_Lines = nullptr; + } + } + + if (pProfile->m_Lines == nullptr) + { + pProfile->m_NoLines = 0; + pProfile->m_MaxLines = 0; + return nullptr; + } + } + + LineNo = std::min(LineNo, pProfile->m_NoLines); + + if (LineNo < pProfile->m_NoLines) + { + sal_uInt32 i, n; + + memmove(&pProfile->m_Lines[LineNo + 1], &pProfile->m_Lines[LineNo], + (pProfile->m_NoLines - LineNo) * sizeof(char *)); + + /* adjust line references */ + for (i = 0; i < pProfile->m_NoSections; i++) + { + osl_TProfileSection* pSec = &pProfile->m_Sections[i]; + + if (pSec->m_Line >= LineNo) + pSec->m_Line++; + + for (n = 0; n < pSec->m_NoEntries; n++) + if (pSec->m_Entries[n].m_Line >= LineNo) + pSec->m_Entries[n].m_Line++; + } + } + + pProfile->m_NoLines++; + + pProfile->m_Lines[LineNo] = strdup(Line); + + return pProfile->m_Lines[LineNo]; +} + +static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo) +{ + if (LineNo < pProfile->m_NoLines) + { + free(pProfile->m_Lines[LineNo]); + pProfile->m_Lines[LineNo]=nullptr; + if (pProfile->m_NoLines - LineNo > 1) + { + sal_uInt32 i, n; + + memmove(&pProfile->m_Lines[LineNo], &pProfile->m_Lines[LineNo + 1], + (pProfile->m_NoLines - LineNo - 1) * sizeof(char *)); + + memset(&pProfile->m_Lines[pProfile->m_NoLines - 1], + 0, + (pProfile->m_MaxLines - pProfile->m_NoLines) * sizeof(char*)); + + /* adjust line references */ + for (i = 0; i < pProfile->m_NoSections; i++) + { + osl_TProfileSection* pSec = &pProfile->m_Sections[i]; + + if (pSec->m_Line > LineNo) + pSec->m_Line--; + + for (n = 0; n < pSec->m_NoEntries; n++) + if (pSec->m_Entries[n].m_Line > LineNo) + pSec->m_Entries[n].m_Line--; + } + } + else + { + pProfile->m_Lines[LineNo] = nullptr; + } + + pProfile->m_NoLines--; + } + + return; +} + +static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection, + sal_uInt32 NoEntry, sal_uInt32 Line, + const char* Entry, sal_uInt32 Len) +{ + Entry = stripBlanks(Entry, &Len); + pSection->m_Entries[NoEntry].m_Line = Line; + pSection->m_Entries[NoEntry].m_Offset = Entry - pProfile->m_Lines[Line]; + pSection->m_Entries[NoEntry].m_Len = Len; + + return; +} + +static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection, + int Line, const char* Entry, sal_uInt32 Len) +{ + if (pSection != nullptr) + { + if (pSection->m_NoEntries >= pSection->m_MaxEntries) + { + if (pSection->m_Entries == nullptr) + { + pSection->m_MaxEntries = ENTRIES_INI; + pSection->m_Entries = static_cast<osl_TProfileEntry *>(malloc( + pSection->m_MaxEntries * sizeof(osl_TProfileEntry))); + } + else + { + pSection->m_MaxEntries += ENTRIES_ADD; + if (auto p = static_cast<osl_TProfileEntry*>(realloc( + pSection->m_Entries, pSection->m_MaxEntries * sizeof(osl_TProfileEntry)))) + pSection->m_Entries = p; + else + { + free(pSection->m_Entries); + pSection->m_Entries = nullptr; + } + } + + if (pSection->m_Entries == nullptr) + { + pSection->m_NoEntries = 0; + pSection->m_MaxEntries = 0; + return false; + } + } + + pSection->m_NoEntries++; + + Entry = stripBlanks(Entry, &Len); + setEntry(pProfile, pSection, pSection->m_NoEntries - 1, Line, + Entry, Len); + + return true; + } + + return false; +} + +static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry) +{ + if (NoEntry < pSection->m_NoEntries) + { + if (pSection->m_NoEntries - NoEntry > 1) + { + memmove(&pSection->m_Entries[NoEntry], + &pSection->m_Entries[NoEntry + 1], + (pSection->m_NoEntries - NoEntry - 1) * sizeof(osl_TProfileEntry)); + pSection->m_Entries[pSection->m_NoEntries - 1].m_Line=0; + pSection->m_Entries[pSection->m_NoEntries - 1].m_Offset=0; + pSection->m_Entries[pSection->m_NoEntries - 1].m_Len=0; + } + + pSection->m_NoEntries--; + } + + return; +} + +static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len) +{ + if (pProfile->m_NoSections >= pProfile->m_MaxSections) + { + if (pProfile->m_Sections == nullptr) + { + pProfile->m_MaxSections = SECTIONS_INI; + pProfile->m_Sections = static_cast<osl_TProfileSection*>(calloc(pProfile->m_MaxSections, sizeof(osl_TProfileSection))); + } + else + { + unsigned int index=0; + unsigned int oldmax=pProfile->m_MaxSections; + + pProfile->m_MaxSections += SECTIONS_ADD; + if (auto p = static_cast<osl_TProfileSection*>(realloc( + pProfile->m_Sections, pProfile->m_MaxSections * sizeof(osl_TProfileSection)))) + { + pProfile->m_Sections = p; + for ( index = oldmax ; index < pProfile->m_MaxSections ; ++index ) + { + pProfile->m_Sections[index].m_Entries=nullptr; + } + } + else + { + free(pProfile->m_Sections); + pProfile->m_Sections = nullptr; + } + } + + if (pProfile->m_Sections == nullptr) + { + pProfile->m_NoSections = 0; + pProfile->m_MaxSections = 0; + return false; + } + } + + pProfile->m_NoSections++; + + if ( pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries != nullptr ) + { + free(pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries); + } + pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr; + pProfile->m_Sections[pProfile->m_NoSections - 1].m_NoEntries = 0; + pProfile->m_Sections[pProfile->m_NoSections - 1].m_MaxEntries = 0; + + Section = stripBlanks(Section, &Len); + pProfile->m_Sections[pProfile->m_NoSections - 1].m_Line = Line; + pProfile->m_Sections[pProfile->m_NoSections - 1].m_Offset = Section - pProfile->m_Lines[Line]; + pProfile->m_Sections[pProfile->m_NoSections - 1].m_Len = Len; + + return true; +} + +static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection) +{ + sal_uInt32 Section; + + if ((Section = pSection - pProfile->m_Sections) < pProfile->m_NoSections) + { + free (pSection->m_Entries); + pSection->m_Entries=nullptr; + if (pProfile->m_NoSections - Section > 1) + { + memmove(&pProfile->m_Sections[Section], &pProfile->m_Sections[Section + 1], + (pProfile->m_NoSections - Section - 1) * sizeof(osl_TProfileSection)); + + memset(&pProfile->m_Sections[pProfile->m_NoSections - 1], + 0, + (pProfile->m_MaxSections - pProfile->m_NoSections) * sizeof(osl_TProfileSection)); + pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr; + } + else + { + pSection->m_Entries = nullptr; + } + + pProfile->m_NoSections--; + } + + return; +} + +static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section, + const char* Entry, sal_uInt32 *pNoEntry) +{ + static sal_uInt32 Sect = 0; + sal_uInt32 i, n; + sal_uInt32 Len; + osl_TProfileSection* pSec = nullptr; + + Len = strlen(Section); + Section = stripBlanks(Section, &Len); + + n = Sect; + + for (i = 0; i < pProfile->m_NoSections; i++) + { + n %= pProfile->m_NoSections; + pSec = &pProfile->m_Sections[n]; + if ((Len == pSec->m_Len) && + (strnicmp(Section, &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], pSec->m_Len) + == 0)) + break; + n++; + } + + Sect = n; + + if (i < pProfile->m_NoSections) + { + Len = strlen(Entry); + Entry = stripBlanks(Entry, &Len); + + *pNoEntry = pSec->m_NoEntries; + + for (i = 0; i < pSec->m_NoEntries; i++) + { + const char* pStr = &pProfile->m_Lines[pSec->m_Entries[i].m_Line] + [pSec->m_Entries[i].m_Offset]; + if ((Len == pSec->m_Entries[i].m_Len) && + (strnicmp(Entry, pStr, pSec->m_Entries[i].m_Len) + == 0)) + { + *pNoEntry = i; + break; + } + } + } + else + pSec = nullptr; + + return pSec; +} + +static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile) +{ + sal_uInt32 i; + char const * pStr; + char const * pChar; + char Line[4096]; + + pProfile->m_NoLines = 0; + pProfile->m_NoSections = 0; + + OSL_VERIFY(rewindFile(pFile, false)); + + while (getLine(pFile, Line, sizeof(Line))) + { + if (! addLine(pProfile, Line)) + return false; + } + + for (i = 0; i < pProfile->m_NoLines; i++) + { + pStr = stripBlanks(pProfile->m_Lines[i], nullptr); + + if ((*pStr == '\0') || (*pStr == ';')) + continue; + + if ((*pStr != '[') || ((pChar = strrchr(pStr, ']')) == nullptr) || + ((pChar - pStr) <= 2)) + { + /* insert entry */ + + if (pProfile->m_NoSections < 1) + continue; + + if ((pChar = strchr(pStr, '=')) == nullptr) + pChar = pStr + strlen(pStr); + + if (! addEntry(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1], + i, pStr, pChar - pStr)) + return false; + } + else + { + /* new section */ + if (! addSection(pProfile, i, pStr + 1, pChar - pStr - 1)) + return false; + } + } + + return true; +} + +static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup) +{ + if (pProfile->m_Lines != nullptr) + { + if (pProfile->m_Flags & FLG_MODIFIED) + { + sal_uInt32 i; + + osl_TFile* pTmpFile = osl_openTmpProfileImpl(pProfile); + + if ( pTmpFile == nullptr ) + { + return false; + } + + OSL_VERIFY(rewindFile(pTmpFile, true)); + + for (i = 0; i < pProfile->m_NoLines; i++) + { + OSL_VERIFY(putLine(pTmpFile, pProfile->m_Lines[i])); + } + + if ( ! writeProfileImpl(pTmpFile) ) + { + if ( pTmpFile->m_pWriteBuf != nullptr ) + { + free(pTmpFile->m_pWriteBuf); + } + + pTmpFile->m_pWriteBuf=nullptr; + pTmpFile->m_nWriteBufLen=0; + pTmpFile->m_nWriteBufFree=0; + + closeFileImpl(pTmpFile); + + return false; + } + + pProfile->m_Flags &= ~FLG_MODIFIED; + + closeFileImpl(pProfile->m_pFile); + closeFileImpl(pTmpFile); + + osl_ProfileSwapProfileNames(pProfile); + + pProfile->m_pFile = openFileImpl(pProfile->m_strFileName,pProfile->m_Flags); + + } + + if (bCleanup) + { + while (pProfile->m_NoLines > 0) + removeLine(pProfile, pProfile->m_NoLines - 1); + + free(pProfile->m_Lines); + pProfile->m_Lines = nullptr; + pProfile->m_MaxLines = 0; + + while (pProfile->m_NoSections > 0) + removeSection(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1]); + + free(pProfile->m_Sections); + pProfile->m_Sections = nullptr; + pProfile->m_MaxSections = 0; + } + } + + return true; +} + +static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl* pProfile) +{ + osl_TFile* pFile=nullptr; + rtl_uString* ustrExtension=nullptr; + rtl_uString* ustrTmpName=nullptr; + oslProfileOption PFlags=0; + + rtl_uString_newFromAscii(&ustrExtension,"tmp"); + + /* generate tmp profilename */ + ustrTmpName=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension); + rtl_uString_release(ustrExtension); + + if (ustrTmpName == nullptr) + return nullptr; + + if (!(pProfile->m_Flags & osl_Profile_READLOCK)) + PFlags |= osl_Profile_WRITELOCK; + + /* open this file */ + pFile = openFileImpl(ustrTmpName,pProfile->m_Flags | PFlags); + + /* return new pFile */ + return pFile; +} + +static bool osl_ProfileSwapProfileNames(osl_TProfileImpl* pProfile) +{ + rtl_uString* ustrBakFile=nullptr; + rtl_uString* ustrTmpFile=nullptr; + rtl_uString* ustrIniFile=nullptr; + rtl_uString* ustrExtension=nullptr; + + rtl_uString_newFromAscii(&ustrExtension,"bak"); + + ustrBakFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension); + rtl_uString_release(ustrExtension); + ustrExtension=nullptr; + + rtl_uString_newFromAscii(&ustrExtension,"ini"); + + ustrIniFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension); + rtl_uString_release(ustrExtension); + ustrExtension=nullptr; + + rtl_uString_newFromAscii(&ustrExtension,"tmp"); + + ustrTmpFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension); + rtl_uString_release(ustrExtension); + ustrExtension=nullptr; + + /* unlink bak */ + DeleteFileW( o3tl::toW(rtl_uString_getStr( ustrBakFile )) ); + + /* rename ini bak */ + MoveFileExW( o3tl::toW(rtl_uString_getStr( ustrIniFile )), o3tl::toW(rtl_uString_getStr( ustrBakFile )), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH ); + + /* rename tmp ini */ + MoveFileExW( o3tl::toW(rtl_uString_getStr( ustrTmpFile )), o3tl::toW(rtl_uString_getStr( ustrIniFile )), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH ); + + return false; +} + +static rtl_uString* osl_ProfileGenerateExtension(rtl_uString* ustrFileName, rtl_uString* ustrExtension) +{ + rtl_uString* ustrNewFileName = nullptr; + rtl_uString* ustrOldExtension = nullptr; + + sal_Unicode* pFileNameBuf = rtl_uString_getStr(ustrFileName); + + rtl_uString_newFromAscii(&ustrOldExtension, "."); + + sal_Unicode* pExtensionBuf = rtl_uString_getStr(ustrOldExtension); + + sal_Int32 nIndex = rtl_ustr_lastIndexOfChar(pFileNameBuf, *pExtensionBuf); + + rtl_uString_newReplaceStrAt(&ustrNewFileName, + ustrFileName, + nIndex+1, + 3, + ustrExtension); + + return ustrNewFileName; +} + +static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable) +{ + osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile); + oslProfileOption PFlags=0; + + if ( bWriteable ) + { + PFlags = osl_Profile_DEFAULT | osl_Profile_WRITELOCK; + } + else + { + PFlags = osl_Profile_DEFAULT; + } + + if (pProfile == nullptr) + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "AUTOOPEN MODE"); +#endif + + if ( ( pProfile = static_cast<osl_TProfileImpl*>(osl_openProfile( nullptr, PFlags )) ) != nullptr ) + { + pProfile->m_Flags |= FLG_AUTOOPEN; + } + } + else + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "try to acquire"); +#endif + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + if (! (pProfile->m_Flags & (osl_Profile_READLOCK | + osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE))) + { + osl_TStamp Stamp; +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "DEFAULT MODE"); +#endif + pProfile->m_pFile = openFileImpl( + pProfile->m_strFileName, pProfile->m_Flags | PFlags); + if (!pProfile->m_pFile) + return nullptr; + + Stamp = getFileStamp(pProfile->m_pFile); + + if (memcmp(&Stamp, &(pProfile->m_Stamp), sizeof(osl_TStamp))) + { + pProfile->m_Stamp = Stamp; + + loadProfile(pProfile->m_pFile, pProfile); + } + } + else + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "READ/WRITELOCK MODE"); +#endif + + /* A readlock file could not be written */ + if ((pProfile->m_Flags & osl_Profile_READLOCK) && bWriteable) + { + return nullptr; + } + } + } + } + + return pProfile; +} + +static bool releaseProfile(osl_TProfileImpl* pProfile) +{ + if ( pProfile == nullptr ) + { + return false; + } + + if (! (pProfile->m_Flags & osl_Profile_SYSTEM)) + { + if (pProfile->m_Flags & FLG_AUTOOPEN) + { + return osl_closeProfile(static_cast<oslProfile>(pProfile)); + } + else + { +#ifdef DEBUG_OSL_PROFILE + SAL_INFO("sal.osl", "DEFAULT MODE"); +#endif + if (! (pProfile->m_Flags & (osl_Profile_READLOCK | + osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE))) + { + if (pProfile->m_Flags & FLG_MODIFIED) + storeProfile(pProfile, false); + + closeFileImpl(pProfile->m_pFile); + pProfile->m_pFile = nullptr; + } + } + } + + return true; +} + +static bool lookupProfile(const sal_Unicode *strPath, const sal_Unicode *strFile, sal_Unicode *strProfile) +{ + char *pChr; + char Buffer[4096] = ""; + char Product[132] = ""; + + ::osl::LongPathBuffer< sal_Unicode > aPath( MAX_LONG_PATH ); + aPath[0] = 0; + + if (*strPath == L'"') + { + int i = 0; + + strPath++; + + while ((strPath[i] != L'"') && (strPath[i] != L'\0')) + i++; + + WideCharToMultiByte(CP_ACP,0, o3tl::toW(strPath), i, Product, sizeof(Product), nullptr, nullptr); + Product[i] = '\0'; + strPath += i; + + if (*strPath == L'"') + strPath++; + + if ( (*strPath == L'/') || (*strPath == L'\\') ) + { + strPath++; + } + } + + else + { + /* if we have not product identification, do a special handling for soffice.ini */ + if (rtl_ustr_ascii_compare(strFile, SVERSION_PROFILE) == 0) + { + rtl_uString * strSVProfile = nullptr; + rtl_uString * strSVFallback = nullptr; + rtl_uString * strSVLocation = nullptr; + rtl_uString * strSVName = nullptr; + ::osl::LongPathBuffer< char > aDir( MAX_LONG_PATH ); + oslProfile hProfile; + + rtl_uString_newFromAscii(&strSVFallback, SVERSION_FALLBACK); + rtl_uString_newFromAscii(&strSVLocation, SVERSION_LOCATION); + rtl_uString_newFromAscii(&strSVName, SVERSION_NAME); + + /* open sversion.ini in the system directory, and try to locate the entry + with the highest version for StarOffice */ + if (osl_getProfileName( strSVFallback, strSVName, &strSVProfile)) + { + hProfile = osl_openProfile(strSVProfile, osl_Profile_READLOCK); + if (hProfile) + { + osl_getProfileSectionEntries( + hProfile, SVERSION_SECTION, Buffer, sizeof(Buffer)); + + for (pChr = Buffer; *pChr != '\0'; pChr += strlen(pChr) + 1) + { + if ((strnicmp( + pChr, SVERSION_SOFFICE, + sizeof(SVERSION_SOFFICE) - 1) + == 0) + && (stricmp(Product, pChr) < 0)) + { + osl_readProfileString( + hProfile, SVERSION_SECTION, pChr, aDir, + aDir.getBufSizeInSymbols(), ""); + + /* check for existence of path */ + if (access(aDir, 0) >= 0) + strcpy(Product, pChr); + } + } + + osl_closeProfile(hProfile); + } + rtl_uString_release(strSVProfile); + strSVProfile = nullptr; + } + + /* open sversion.ini in the users directory, and try to locate the entry + with the highest version for StarOffice */ + if ( osl_getProfileName(strSVLocation, strSVName, &strSVProfile) ) + { + hProfile = osl_openProfile(strSVProfile, osl_Profile_READLOCK); + if (hProfile) + { + osl_getProfileSectionEntries( + hProfile, SVERSION_SECTION, Buffer, sizeof(Buffer)); + + for (pChr = Buffer; *pChr != '\0'; pChr += strlen(pChr) + 1) + { + if ((strnicmp( + pChr, SVERSION_SOFFICE, + sizeof(SVERSION_SOFFICE) - 1) + == 0) + && (stricmp(Product, pChr) < 0)) + { + osl_readProfileString( + hProfile, SVERSION_SECTION, pChr, aDir, + aDir.getBufSizeInSymbols(), ""); + + /* check for existence of path */ + if (access(aDir, 0) >= 0) + strcpy(Product, pChr); + } + } + + osl_closeProfile(hProfile); + } + rtl_uString_release(strSVProfile); + } + + rtl_uString_release(strSVFallback); + rtl_uString_release(strSVLocation); + rtl_uString_release(strSVName); + + /* remove any trailing build number */ + if ((pChr = strrchr(Product, '/')) != nullptr) + *pChr = '\0'; + } + } + + rtl_uString * strExecutable = nullptr; + rtl_uString * strTmp = nullptr; + sal_Int32 nPos; + + /* try to find the file in the directory of the executable */ + if (osl_getExecutableFile(&strTmp) != osl_Process_E_None) + return false; + + /* convert to native path */ + if (osl_getSystemPathFromFileURL(strTmp, &strExecutable) != osl_File_E_None) + { + rtl_uString_release(strTmp); + return false; + } + + rtl_uString_release(strTmp); + + DWORD dwPathLen = 0; + + /* separate path from filename */ + if ((nPos = rtl_ustr_lastIndexOfChar(strExecutable->buffer, L'\\')) == -1) + { + if ((nPos = rtl_ustr_lastIndexOfChar(strExecutable->buffer, L':')) == -1) + { + rtl_uString_release(strExecutable); + return false; + } + else + { + copy_ustr_n(aPath, strExecutable->buffer, nPos); + aPath[nPos] = 0; + dwPathLen = nPos; + } + } + else + { + copy_ustr_n(aPath, strExecutable->buffer, nPos); + dwPathLen = nPos; + aPath[dwPathLen] = 0; + } + + /* if we have no product identification use the executable file name */ + if (*Product == 0) + { + WideCharToMultiByte(CP_ACP,0, o3tl::toW(strExecutable->buffer + nPos + 1), -1, Product, sizeof(Product), nullptr, nullptr); + + /* remove extension */ + if ((pChr = strrchr(Product, '.')) != nullptr) + *pChr = '\0'; + } + + rtl_uString_release(strExecutable); + + /* remember last subdir */ + nPos = rtl_ustr_lastIndexOfChar(aPath, L'\\'); + + copy_ustr_n(aPath + dwPathLen++, L"\\", 2); + + if (*strPath) + { + copy_ustr_n(aPath + dwPathLen, strPath, rtl_ustr_getLength(strPath)+1); + dwPathLen += rtl_ustr_getLength(strPath); + } + + { + ::osl::LongPathBuffer< char > aTmpPath( MAX_LONG_PATH ); + + WideCharToMultiByte(CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath, aTmpPath.getBufSizeInSymbols(), nullptr, nullptr); + + /* if file not exists, remove any specified subdirectories + like "bin" or "program" */ + + if (((access(aTmpPath, 0) < 0) && (nPos != -1)) || (*strPath == 0)) + { + static const char *SubDirs[] = SVERSION_DIRS; + + unsigned i = 0; + char *pStr = aTmpPath + nPos; + + for (i = 0; i < SAL_N_ELEMENTS(SubDirs); i++) + if (strnicmp(pStr + 1, SubDirs[i], strlen(SubDirs[i])) == 0) + { + if ( *strPath == 0) + { + strcpy(pStr + 1,SVERSION_USER); + if ( access(aTmpPath, 0) < 0 ) + { + *(pStr+1)='\0'; + } + else + { + dwPathLen = nPos + MultiByteToWideChar( CP_ACP, 0, SVERSION_USER, -1, o3tl::toW(aPath + nPos + 1), aPath.getBufSizeInSymbols() - (nPos + 1) ); + } + } + else + { + copy_ustr_n(aPath + nPos + 1, strPath, rtl_ustr_getLength(strPath)+1); + dwPathLen = nPos + 1 + rtl_ustr_getLength(strPath); + } + + break; + } + } + } + + if ((aPath[dwPathLen - 1] != L'/') && (aPath[dwPathLen - 1] != L'\\')) + { + aPath[dwPathLen++] = L'\\'; + aPath[dwPathLen] = 0; + } + + copy_ustr_n(aPath + dwPathLen, strFile, rtl_ustr_getLength(strFile)+1); + + { + ::osl::LongPathBuffer< char > aTmpPath( MAX_LONG_PATH ); + + WideCharToMultiByte(CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath, aTmpPath.getBufSizeInSymbols(), nullptr, nullptr); + + if ((access(aTmpPath, 0) < 0) && (Product[0] != '\0')) + { + rtl_uString * strSVFallback = nullptr; + rtl_uString * strSVProfile = nullptr; + rtl_uString * strSVLocation = nullptr; + rtl_uString * strSVName = nullptr; + oslProfile hProfile; + + rtl_uString_newFromAscii(&strSVFallback, SVERSION_FALLBACK); + rtl_uString_newFromAscii(&strSVLocation, SVERSION_LOCATION); + rtl_uString_newFromAscii(&strSVName, SVERSION_NAME); + + /* open sversion.ini in the system directory, and try to locate the entry + with the highest version for StarOffice */ + if (osl_getProfileName(strSVLocation, strSVName, &strSVProfile)) + { + hProfile = osl_openProfile( + strSVProfile, osl_Profile_READLOCK); + if (hProfile) + { + osl_readProfileString( + hProfile, SVERSION_SECTION, Product, Buffer, + sizeof(Buffer), ""); + osl_closeProfile(hProfile); + + /* if not found, try the fallback */ + if (Buffer[0] == '\0') + { + if (osl_getProfileName( + strSVFallback, strSVName, &strSVProfile)) + { + hProfile = osl_openProfile( + strSVProfile, osl_Profile_READLOCK); + if (hProfile) + { + osl_readProfileString( + hProfile, SVERSION_SECTION, Product, + Buffer, sizeof(Buffer), ""); + } + } + + osl_closeProfile(hProfile); + } + + if (Buffer[0] != '\0') + { + dwPathLen = MultiByteToWideChar( + CP_ACP, 0, Buffer, -1, o3tl::toW(aPath), aPath.getBufSizeInSymbols() ); + dwPathLen -=1; + + /* build full path */ + if ((aPath[dwPathLen - 1] != L'/') + && (aPath[dwPathLen - 1] != L'\\')) + { + copy_ustr_n(aPath + dwPathLen++, L"\\", 2); + } + + if (*strPath) + { + copy_ustr_n(aPath + dwPathLen, strPath, rtl_ustr_getLength(strPath)+1); + dwPathLen += rtl_ustr_getLength(strPath); + } + else + { + ::osl::LongPathBuffer< char > aTmpPath2( MAX_LONG_PATH ); + int n; + + if ((n = WideCharToMultiByte( + CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath2, + aTmpPath2.getBufSizeInSymbols(), nullptr, nullptr)) + > 0) + { + strcpy(aTmpPath2 + n, SVERSION_USER); + if (access(aTmpPath2, 0) >= 0) + { + dwPathLen += MultiByteToWideChar( + CP_ACP, 0, SVERSION_USER, -1, + o3tl::toW(aPath + dwPathLen), + aPath.getBufSizeInSymbols() - dwPathLen ); + } + } + } + } + } + + rtl_uString_release(strSVProfile); + } + + rtl_uString_release(strSVFallback); + rtl_uString_release(strSVLocation); + rtl_uString_release(strSVName); + } + } + + aPath[dwPathLen] = 0; + + /* copy filename */ + copy_ustr_n(strProfile, aPath, dwPathLen+1); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/random.cxx b/sal/osl/w32/random.cxx new file mode 100644 index 000000000..a2c364da2 --- /dev/null +++ b/sal/osl/w32/random.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#define _CRT_RAND_S + +#include <stdlib.h> +#include <memory.h> + +#include <oslrandom.h> + +int osl_get_system_random_data(char* buffer, size_t desired_len) +{ + unsigned int val; + + /* if unaligned fill to alignment */ + if (reinterpret_cast<uintptr_t>(buffer) & 3) + { + size_t len = 4 - (reinterpret_cast<size_t>(buffer) & 3); + + if (len > desired_len) + { + len = desired_len; + } + if (rand_s(&val)) + { + return 0; + } + memcpy(buffer, &val, len); + buffer += len; + desired_len -= len; + } + /* fill directly into the buffer as long as we can */ + while (desired_len >= 4) + { + if (rand_s(reinterpret_cast<unsigned int*>(buffer))) + { + return 0; + } + else + { + buffer += 4; + desired_len -= 4; + } + } + /* deal with the partial int reminder to fill */ + if (desired_len) + { + if (rand_s(&val)) + { + return 0; + } + memcpy(buffer, &val, desired_len); + } + return 1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/salinit.cxx b/sal/osl/w32/salinit.cxx new file mode 100644 index 000000000..c0b917127 --- /dev/null +++ b/sal/osl/w32/salinit.cxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include "system.h" +#include "time.hxx" + +#include <osl/process.h> +#include <sal/main.h> +#include <sal/types.h> + +extern "C" { + +// Prototypes for initialization and deinitialization of SAL library + +void sal_detail_initialize(int argc, char ** argv) +{ + if (argc == sal::detail::InitializeSoffice) + { + return; + } + sal_initGlobalTimer(); +#ifndef _WIN64 + SetProcessDEPPolicy(PROCESS_DEP_ENABLE); +#endif + SetDllDirectoryW(L""); // remove the current directory from the default DLL search order + SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT); + + WSADATA wsaData; + int error; + WORD wVersionRequested; + + wVersionRequested = MAKEWORD(1, 1); + + error = WSAStartup(wVersionRequested, &wsaData); + if ( 0 == error ) + { + WORD const wMajorVersionRequired = 1; + WORD const wMinorVersionRequired = 1; + + if ((LOBYTE(wsaData.wVersion) < wMajorVersionRequired) || + ((LOBYTE(wsaData.wVersion) == wMajorVersionRequired) && + (HIBYTE(wsaData.wVersion) < wMinorVersionRequired))) + { + // How to handle a very unlikely error ??? + } + } + else + { + // How to handle a very unlikely error ??? + } + + osl_setCommandArgs(argc, argv); +} + +void sal_detail_deinitialize() +{ + if ( SOCKET_ERROR == WSACleanup() ) + { + // We should never reach this point or we did wrong elsewhere + } +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/secimpl.hxx b/sal/osl/w32/secimpl.hxx new file mode 100644 index 000000000..7c952bb64 --- /dev/null +++ b/sal/osl/w32/secimpl.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_OSL_W32_SECIMPL_HXX +#define INCLUDED_SAL_OSL_W32_SECIMPL_HXX + +#include <winnetwk.h> + +#include <osl/security.h> + +#define USER_BUFFER_SIZE 256 + +typedef struct +{ + HANDLE m_hProfile; + HANDLE m_hToken; + sal_Unicode m_User[USER_BUFFER_SIZE]; + /* extension by fileserver login */ + NETRESOURCEW* m_pNetResource; +} oslSecurityImpl; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/security.cxx b/sal/osl/w32/security.cxx new file mode 100644 index 000000000..21ed64a78 --- /dev/null +++ b/sal/osl/w32/security.cxx @@ -0,0 +1,662 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" +#include <userenv.h> + +#include <cassert> +#include <osl/security.h> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <osl/file.h> +#include <systools/win32/uwinapi.h> +#include <sddl.h> +#include <sal/macros.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include "secimpl.hxx" + +/* To get an impersonation token we need to create an impersonation + duplicate so every access token has to be created with duplicate + access rights */ + +#define TOKEN_DUP_QUERY (TOKEN_QUERY|TOKEN_DUPLICATE) + +static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder); +// We use LPCTSTR here, because we use it with SE_foo_NAME constants +// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName") +static bool Privilege(LPCTSTR pszPrivilege, bool bEnable); +static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain); + +oslSecurity SAL_CALL osl_getCurrentSecurity(void) +{ + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = nullptr; + pSecImpl->m_User[0] = '\0'; + pSecImpl->m_hToken = nullptr; + pSecImpl->m_hProfile = nullptr; + } + return pSecImpl; +} + +oslSecurityError SAL_CALL osl_loginUser( rtl_uString *strUserName, rtl_uString *strPasswd, oslSecurity *pSecurity ) +{ + oslSecurityError ret; + + sal_Unicode* strUser; + sal_Unicode* strDomain = o3tl::toU(_wcsdup(o3tl::toW(rtl_uString_getStr(strUserName)))); + HANDLE hUserToken; + LUID luid; + + if (nullptr != (strUser = o3tl::toU(wcschr(o3tl::toW(strDomain), L'/')))) + *strUser++ = L'\0'; + else + { + strUser = strDomain; + strDomain = nullptr; + } + + // this process must have the right: 'act as a part of operatingsystem' + OSL_ASSERT(LookupPrivilegeValue(nullptr, SE_TCB_NAME, &luid)); + (void) luid; + + if (LogonUserW(o3tl::toW(strUser), strDomain ? o3tl::toW(strDomain) : L"", o3tl::toW(rtl_uString_getStr(strPasswd)), + LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, + &hUserToken)) + { + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = nullptr; + pSecImpl->m_hToken = hUserToken; + pSecImpl->m_hProfile = nullptr; + wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(strUser)); + } + *pSecurity = pSecImpl; + ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown; + } + else + { + ret = osl_Security_E_UserUnknown; + } + + if (strDomain) + free(strDomain); + else + free(strUser); + + return ret; +} + +oslSecurityError SAL_CALL osl_loginUserOnFileServer(rtl_uString *strUserName, + rtl_uString *strPasswd, + rtl_uString *strFileServer, + oslSecurity *pSecurity) +{ + oslSecurityError ret; + DWORD err; + NETRESOURCEW netResource; + sal_Unicode* remoteName; + sal_Unicode* userName; + + remoteName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 4) * sizeof(sal_Unicode))); + userName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 2) * sizeof(sal_Unicode))); + + wcscpy(o3tl::toW(remoteName), L"\\\\"); + wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strFileServer))); + wcscat(o3tl::toW(remoteName), L"\\"); + wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strUserName))); + + wcscpy(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strFileServer))); + wcscat(o3tl::toW(userName), L"\\"); + wcscat(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strUserName))); + + netResource.dwScope = RESOURCE_GLOBALNET; + netResource.dwType = RESOURCETYPE_DISK; + netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; + netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE; + netResource.lpLocalName = nullptr; + netResource.lpRemoteName = o3tl::toW(remoteName); + netResource.lpComment = nullptr; + netResource.lpProvider = nullptr; + + err = WNetAddConnection2W(&netResource, o3tl::toW(rtl_uString_getStr(strPasswd)), o3tl::toW(userName), 0); + + if ((err == NO_ERROR) || (err == ERROR_ALREADY_ASSIGNED)) + { + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = static_cast<NETRESOURCEW *>(malloc(sizeof(NETRESOURCE))); + if (pSecImpl->m_pNetResource) + { + *pSecImpl->m_pNetResource = netResource; + pSecImpl->m_hToken = nullptr; + pSecImpl->m_hProfile = nullptr; + wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(rtl_uString_getStr(strUserName))); + } + else + { + free(pSecImpl); + pSecImpl = nullptr; + } + } + *pSecurity = pSecImpl; + + ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown; + } + else + { + ret = osl_Security_E_UserUnknown; + } + + free(remoteName); + free(userName); + + return ret; +} + +sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security) +{ + if (!Security) + return false; + + HANDLE hImpersonationToken = nullptr; + PSID psidAdministrators; + SID_IDENTIFIER_AUTHORITY siaNtAuthority = { SECURITY_NT_AUTHORITY }; + bool bSuccess = false; + + /* If Security contains an access token we need to duplicate it to an impersonation + access token. NULL works with CheckTokenMembership() as the current effective + impersonation token + */ + + if ( static_cast<oslSecurityImpl*>(Security)->m_hToken ) + { + if ( !DuplicateToken (static_cast<oslSecurityImpl*>(Security)->m_hToken, SecurityImpersonation, &hImpersonationToken) ) + return false; + } + + /* CheckTokenMembership() can be used on W2K and higher (NT4 no longer supported by OOo) + and also works on Vista to retrieve the effective user rights. Just checking for + membership of Administrators group is not enough on Vista this would require additional + complicated checks as described in KB article Q118626: http://support.microsoft.com/kb/118626/en-us + */ + + if (AllocateAndInitializeSid(&siaNtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &psidAdministrators)) + { + BOOL fSuccess = FALSE; + + if (CheckTokenMembership(hImpersonationToken, psidAdministrators, &fSuccess) && fSuccess) + bSuccess = true; + + FreeSid(psidAdministrators); + } + + if (hImpersonationToken) + CloseHandle(hImpersonationToken); + + return bSuccess; +} + +void SAL_CALL osl_freeSecurityHandle(oslSecurity Security) +{ + if (!Security) + return; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + WNetCancelConnection2W(pSecImpl->m_pNetResource->lpRemoteName, 0, true); + + free(pSecImpl->m_pNetResource->lpRemoteName); + free(pSecImpl->m_pNetResource); + } + + if (pSecImpl->m_hToken) + CloseHandle(pSecImpl->m_hToken); + + if ( pSecImpl->m_hProfile ) + CloseHandle(pSecImpl->m_hProfile); + + free (pSecImpl); +} + +sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **strIdent) +{ + if (!Security) + return false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + HANDLE hAccessToken = pSecImpl->m_hToken; + + if (hAccessToken == nullptr) + OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken); + + if (hAccessToken) + { + DWORD nInfoBuffer = 512; + UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer)); + + while (!GetTokenInformation(hAccessToken, TokenUser, + pInfoBuffer, nInfoBuffer, &nInfoBuffer)) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer))) + pInfoBuffer = p; + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + + if (pSecImpl->m_hToken == nullptr) + CloseHandle(hAccessToken); + + if (pInfoBuffer) + { + PSID pSid = reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid; + + LPWSTR pSidStr = nullptr; + bool bResult = ConvertSidToStringSidW(pSid, &pSidStr); + if (bResult) + { + rtl_uString_newFromStr(strIdent, o3tl::toU(pSidStr)); + LocalFree(pSidStr); + } + else + { + const DWORD dwError = GetLastError(); + SAL_WARN( + "sal.osl", + "ConvertSidToStringSidW failed. GetLastError returned: " << dwError); + } + + free(pInfoBuffer); + + return bResult; + } + } + else + { + DWORD needed = 0; + + WNetGetUserW(nullptr, nullptr, &needed); + if (needed < 16) + needed = 16; + + if (auto Ident = static_cast<sal_Unicode *>(malloc(needed*sizeof(sal_Unicode)))) + { + if (WNetGetUserW(nullptr, o3tl::toW(Ident), &needed) != NO_ERROR) + { + wcscpy(o3tl::toW(Ident), L"unknown"); + Ident[7] = L'\0'; + } + + rtl_uString_newFromStr( strIdent, Ident); + free(Ident); + return true; + } + } + return false; +} + +sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **strName) +{ + return getUserNameImpl(Security, strName, true); +} + +sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **strName) +{ + return getUserNameImpl(Security, strName, false); +} + +sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory) +{ + if (!Security) + return false; + + rtl_uString *ustrSysDir = nullptr; + bool bSuccess = false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory ); + } + else + { + bSuccess = GetSpecialFolder(&ustrSysDir, FOLDERID_Documents) && + (osl_File_E_None == osl_getFileURLFromSystemPath(ustrSysDir, pustrDirectory)); + } + + if ( ustrSysDir ) + rtl_uString_release( ustrSysDir ); + + return bSuccess; +} + +sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory) +{ + if (!Security) + return false; + + bool bSuccess = false; + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + rtl_uString *ustrSysDir = nullptr; + + rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory); + + if ( ustrSysDir ) + rtl_uString_release( ustrSysDir ); + } + else + { + if (pSecImpl->m_hToken) + { + /* not implemented */ + OSL_ASSERT(false); + } + else + { + rtl_uString *ustrFile = nullptr; + sal_Unicode sFile[_MAX_PATH]; + + if ( !GetSpecialFolder( &ustrFile, FOLDERID_RoamingAppData) ) + { + OSL_VERIFY(GetWindowsDirectoryW(o3tl::toW(sFile), _MAX_DIR) > 0); + + rtl_uString_newFromStr( &ustrFile, sFile); + } + + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath(ustrFile, pustrDirectory); + + if ( ustrFile ) + rtl_uString_release( ustrFile ); + } + } + + return bSuccess; +} + +sal_Bool SAL_CALL osl_loadUserProfile(oslSecurity Security) +{ + /* CreateProcessAsUser does not load the specified user's profile + into the HKEY_USERS registry key. This means that access to information + in the HKEY_CURRENT_USER registry key may not produce results consistent + with a normal interactive logon. + It is your responsibility to load the user's registry hive into HKEY_USERS + with the LoadUserProfile function before calling CreateProcessAsUser. + */ + + RegCloseKey(HKEY_CURRENT_USER); + + if (!Privilege(SE_RESTORE_NAME, true)) + return false; + + bool bOk = false; + HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken; + + /* try to create user profile */ + if ( !hAccessToken ) + { + /* retrieve security handle if not done before e.g. osl_getCurrentSecurity() + */ + HANDLE hProcess = GetCurrentProcess(); + + if (hProcess != nullptr) + { + OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken); + CloseHandle(hProcess); + } + } + + rtl_uString *buffer = nullptr; + PROFILEINFOW pi; + + getUserNameImpl(Security, &buffer, false); + + ZeroMemory(&pi, sizeof(pi)); + pi.dwSize = sizeof(pi); + pi.lpUserName = o3tl::toW(rtl_uString_getStr(buffer)); + pi.dwFlags = PI_NOUI; + + if (LoadUserProfileW(hAccessToken, &pi)) + { + UnloadUserProfile(hAccessToken, pi.hProfile); + + bOk = true; + } + + rtl_uString_release(buffer); + + if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken)) + CloseHandle(hAccessToken); + + return bOk; +} + +void SAL_CALL osl_unloadUserProfile(oslSecurity Security) +{ + if ( static_cast<oslSecurityImpl*>(Security)->m_hProfile == nullptr ) + return; + + HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken; + + if ( !hAccessToken ) + { + /* retrieve security handle if not done before e.g. osl_getCurrentSecurity() + */ + HANDLE hProcess = GetCurrentProcess(); + + if (hProcess != nullptr) + { + OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken); + CloseHandle(hProcess); + } + } + + /* unloading the user profile */ + UnloadUserProfile(hAccessToken, static_cast<oslSecurityImpl*>(Security)->m_hProfile); + + static_cast<oslSecurityImpl*>(Security)->m_hProfile = nullptr; + + if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken)) + CloseHandle(hAccessToken); +} + +static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder) +{ + bool bRet = false; + PWSTR PathW; + if (SUCCEEDED(SHGetKnownFolderPath(rFolder, KF_FLAG_CREATE, nullptr, &PathW))) + { + rtl_uString_newFromStr(strPath, o3tl::toU(PathW)); + CoTaskMemFree(PathW); + bRet = true; + } + + return bRet; +} + +// We use LPCTSTR here, because we use it with SE_foo_NAME constants +// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName") +static bool Privilege(LPCTSTR strPrivilege, bool bEnable) +{ + HANDLE hToken; + TOKEN_PRIVILEGES tp; + + // obtain the processes token + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_DUP_QUERY, &hToken)) + return false; + + // get the luid + if (!LookupPrivilegeValue(nullptr, strPrivilege, &tp.Privileges[0].Luid)) + return false; + + tp.PrivilegeCount = 1; + + if (bEnable) + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + // enable or disable the privilege + if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr)) + return false; + + if (!CloseHandle(hToken)) + return false; + + return true; +} + +static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain) +{ + if (!Security) + return false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + HANDLE hAccessToken = pSecImpl->m_hToken; + + if (hAccessToken == nullptr) + OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken); + + if (hAccessToken) + { + DWORD nInfoBuffer = 512; + UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer)); + + while (!GetTokenInformation(hAccessToken, TokenUser, + pInfoBuffer, nInfoBuffer, &nInfoBuffer)) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer))) + pInfoBuffer = p; + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + + if (pSecImpl->m_hToken == nullptr) + CloseHandle(hAccessToken); + + if (pInfoBuffer) + { + sal_Unicode UserName[128]; + sal_Unicode DomainName[128]; + sal_Unicode Name[257]; + DWORD nUserName = SAL_N_ELEMENTS(UserName); + DWORD nDomainName = SAL_N_ELEMENTS(DomainName); + SID_NAME_USE sUse; + + if (LookupAccountSidW(nullptr, reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid, + o3tl::toW(UserName), &nUserName, + o3tl::toW(DomainName), &nDomainName, &sUse)) + { + if (bIncludeDomain) + { + wcscpy(o3tl::toW(Name), o3tl::toW(DomainName)); + wcscat(o3tl::toW(Name), L"/"); + wcscat(o3tl::toW(Name), o3tl::toW(UserName)); + } + else + { + wcscpy(o3tl::toW(Name), o3tl::toW(UserName)); + } + + rtl_uString_newFromStr(strName, Name); + free(pInfoBuffer); + return true; + } + } + } + else + { + DWORD needed=0; + sal_Unicode *pNameW=nullptr; + + WNetGetUserW(nullptr, nullptr, &needed); + pNameW = static_cast<sal_Unicode *>(malloc (needed*sizeof(sal_Unicode))); + assert(pNameW); // Don't handle OOM conditions + + if (WNetGetUserW(nullptr, o3tl::toW(pNameW), &needed) == NO_ERROR) + { + rtl_uString_newFromStr( strName, pNameW); + + if (pNameW) + free(pNameW); + return true; + } + else if (pSecImpl->m_User[0] != '\0') + { + rtl_uString_newFromStr(strName, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + + if (pNameW) + free(pNameW); + + return true; + } + + if (pNameW) + free(pNameW); + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/signal.cxx b/sal/osl/w32/signal.cxx new file mode 100644 index 000000000..dcb05beae --- /dev/null +++ b/sal/osl/w32/signal.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <stdlib.h> + +#include <config_features.h> + +#include <signalshared.hxx> + +#include <systools/win32/uwinapi.h> +#include <errorrep.h> +#include <werapi.h> + +namespace +{ +long WINAPI signalHandlerFunction(LPEXCEPTION_POINTERS lpEP); + +LPTOP_LEVEL_EXCEPTION_FILTER pPreviousHandler = nullptr; +} + +bool onInitSignal() +{ + pPreviousHandler = SetUnhandledExceptionFilter(signalHandlerFunction); + + WerAddExcludedApplication(L"SOFFICE.EXE", FALSE); + + return true; +} + +bool onDeInitSignal() +{ + SetUnhandledExceptionFilter(pPreviousHandler); + + return false; +} + +namespace +{ +/* magic Microsoft C++ compiler exception constant */ +#define EXCEPTION_MSC_CPP_EXCEPTION 0xe06d7363 + +long WINAPI signalHandlerFunction(LPEXCEPTION_POINTERS lpEP) +{ +#if HAVE_FEATURE_BREAKPAD + // we should make sure to call the breakpad handler as + // first step when we hit a problem + if (pPreviousHandler) + pPreviousHandler(lpEP); +#endif + + static bool bNested = false; + + oslSignalInfo info; + + info.UserSignal = lpEP->ExceptionRecord->ExceptionCode; + info.UserData = nullptr; + + switch (lpEP->ExceptionRecord->ExceptionCode) + { + /* Transform unhandled exceptions into access violations. + Microsoft C++ compiler (add more for other compilers if necessary). + */ + case EXCEPTION_MSC_CPP_EXCEPTION: + case EXCEPTION_ACCESS_VIOLATION: + info.Signal = osl_Signal_AccessViolation; + break; + + case EXCEPTION_INT_DIVIDE_BY_ZERO: + info.Signal = osl_Signal_IntegerDivideByZero; + break; + + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + info.Signal = osl_Signal_FloatDivideByZero; + break; + + case EXCEPTION_BREAKPOINT: + info.Signal = osl_Signal_DebugBreak; + break; + + default: + info.Signal = osl_Signal_System; + break; + } + + oslSignalAction action; + + if (!bNested) + { + bNested = true; + action = callSignalHandler(&info); + } + else + action = osl_Signal_ActKillApp; + + switch (action) + { + case osl_Signal_ActCallNextHdl: + return EXCEPTION_CONTINUE_SEARCH; + + case osl_Signal_ActAbortApp: + return EXCEPTION_EXECUTE_HANDLER; + + case osl_Signal_ActKillApp: + SetErrorMode(SEM_NOGPFAULTERRORBOX); + exit(255); + break; + default: + break; + } + + return EXCEPTION_CONTINUE_EXECUTION; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/socket.cxx b/sal/osl/w32/socket.cxx new file mode 100644 index 000000000..2548be0d5 --- /dev/null +++ b/sal/osl/w32/socket.cxx @@ -0,0 +1,1611 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <utility> + +#include "system.h" + +#include <osl/socket.h> +#include <osl/thread.h> +#include <osl/diagnose.h> +#include <rtl/alloc.h> +#include <rtl/byteseq.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/safeint.hxx> +#include <comphelper/windowserrorstring.hxx> + +#include "sockimpl.hxx" + +/* + oslSocketAddr is a pointer to a Berkeley struct sockaddr. + I refrained from using sockaddr_in because of possible further + extensions of this socket-interface (IP-NG?). + The intention was to hide all Berkeley data-structures from + direct access past the osl-interface. + + The current implementation is internet (IP) centered. All + the constructor-functions (osl_create...) take parameters + that will probably make sense only in the IP-environment + (e.g. because of using the dotted-Addr-format). + + If the interface will be extended to host other protocol- + families, I expect no externally visible changes in the + existing functions. You'll probably need only new + constructor-functions who take the different Addr + formats into consideration (maybe a long dotted Addr + or whatever). +*/ + +/* + _Note_ that I rely on the fact that oslSocketAddr and struct sockaddr + are the same! I don't like it very much but see no other easy way to + conceal the struct sockaddr from the eyes of the user. +*/ + +#define OSL_INVALID_SOCKET INVALID_SOCKET /* WIN32 */ +#define OSL_SOCKET_ERROR SOCKET_ERROR /* WIN32 */ + +static DWORD FamilyMap[]= { + AF_INET, /* osl_Socket_FamilyInet */ + AF_IPX, /* osl_Socket_FamilyIpx */ + 0 /* osl_Socket_FamilyInvalid */ +}; + +static oslAddrFamily osl_AddrFamilyFromNative(DWORD nativeType) +{ + oslAddrFamily i= oslAddrFamily(0); + while(i != osl_Socket_FamilyInvalid) + { + if(FamilyMap[i] == nativeType) + return i; + i = static_cast<oslAddrFamily>( static_cast<int>(i) + 1); + } + return i; +} + +#define FAMILY_FROM_NATIVE(y) osl_AddrFamilyFromNative(y) +#define FAMILY_TO_NATIVE(x) static_cast<short>(FamilyMap[x]) + +static DWORD ProtocolMap[]= { + 0, /* osl_Socket_FamilyInet */ + NSPROTO_IPX, /* osl_Socket_FamilyIpx */ + NSPROTO_SPX, /* osl_Socket_ProtocolSpx */ + NSPROTO_SPXII, /* osl_Socket_ProtocolSpx_ii */ + 0 /* osl_Socket_ProtocolInvalid */ +}; + +#define PROTOCOL_TO_NATIVE(x) ProtocolMap[x] + +static DWORD TypeMap[]= { + SOCK_STREAM, /* osl_Socket_TypeStream */ + SOCK_DGRAM, /* osl_Socket_TypeDgram */ + SOCK_RAW, /* osl_Socket_TypeRaw */ + SOCK_RDM, /* osl_Socket_TypeRdm */ + SOCK_SEQPACKET, /* osl_Socket_TypeSeqPacket */ + 0 /* osl_Socket_TypeInvalid */ +}; + +static oslSocketType osl_SocketTypeFromNative(DWORD nativeType) +{ + oslSocketType i= oslSocketType(0); + while(i != osl_Socket_TypeInvalid) + { + if(TypeMap[i] == nativeType) + return i; + i = static_cast<oslSocketType>(static_cast<int>(i)+1); + } + return i; +} + +#define TYPE_TO_NATIVE(x) TypeMap[x] +#define TYPE_FROM_NATIVE(y) osl_SocketTypeFromNative(y) + +static DWORD OptionMap[]= { + SO_DEBUG, /* osl_Socket_OptionDebug */ + SO_ACCEPTCONN, /* osl_Socket_OptionAcceptConn */ + SO_REUSEADDR, /* osl_Socket_OptionReuseAddr */ + SO_KEEPALIVE, /* osl_Socket_OptionKeepAlive */ + SO_DONTROUTE, /* osl_Socket_OptionDontRoute */ + SO_BROADCAST, /* osl_Socket_OptionBroadcast */ + SO_USELOOPBACK, /* osl_Socket_OptionUseLoopback */ + SO_LINGER, /* osl_Socket_OptionLinger */ + SO_OOBINLINE, /* osl_Socket_OptionOOBinLine */ + SO_SNDBUF, /* osl_Socket_OptionSndBuf */ + SO_RCVBUF, /* osl_Socket_OptionRcvBuf */ + SO_SNDLOWAT, /* osl_Socket_OptionSndLowat */ + SO_RCVLOWAT, /* osl_Socket_OptionRcvLowat */ + SO_SNDTIMEO, /* osl_Socket_OptionSndTimeo */ + SO_RCVTIMEO, /* osl_Socket_OptionRcvTimeo */ + SO_ERROR, /* osl_Socket_OptionError */ + SO_TYPE, /* osl_Socket_OptionType */ + TCP_NODELAY, /* osl_Socket_OptionTcpNoDelay */ + 0 /* osl_Socket_OptionInvalid */ +}; + +#define OPTION_TO_NATIVE(x) OptionMap[x] + +static DWORD OptionLevelMap[]= { + SOL_SOCKET, /* osl_Socket_LevelSocket */ + IPPROTO_TCP, /* osl_Socket_LevelTcp */ + 0 /* osl_invalid_SocketLevel */ +}; + +#define OPTION_LEVEL_TO_NATIVE(x) OptionLevelMap[x] + +static DWORD SocketMsgFlagMap[]= { + 0, /* osl_Socket_MsgNormal */ + MSG_OOB, /* osl_Socket_MsgOOB */ + MSG_PEEK, /* osl_Socket_MsgPeek */ + MSG_DONTROUTE, /* osl_Socket_MsgDontRoute */ + MSG_MAXIOVLEN /* osl_Socket_MsgMaxIOVLen */ +}; + +#define MSG_FLAG_TO_NATIVE(x) SocketMsgFlagMap[x] + +static DWORD SocketDirection[]= { + SD_RECEIVE, /* osl_Socket_DirRead */ + SD_SEND, /* osl_Socket_DirWrite */ + SD_BOTH /* osl_Socket_DirReadwrite */ +}; + +#define DIRECTION_TO_NATIVE(x) SocketDirection[x] + +static int SocketError[]= { + 0, /* no error */ + WSAENOTSOCK, /* Socket operation on non-socket */ + WSAEDESTADDRREQ, /* Destination address required */ + WSAEMSGSIZE, /* Message too long */ + WSAEPROTOTYPE, /* Protocol wrong type for socket */ + WSAENOPROTOOPT, /* Protocol not available */ + WSAEPROTONOSUPPORT, /* Protocol not supported */ + WSAESOCKTNOSUPPORT, /* Socket type not supported */ + WSAEOPNOTSUPP, /* Operation not supported on socket */ + WSAEPFNOSUPPORT, /* Protocol family not supported */ + WSAEAFNOSUPPORT, /* Address family not supported by protocol family */ + WSAEADDRINUSE, /* Address already in use */ + WSAEADDRNOTAVAIL, /* Can't assign requested address */ + WSAENETDOWN, /* Network is down */ + WSAENETUNREACH, /* Network is unreachable */ + WSAENETRESET, /* Network dropped connection because of reset */ + WSAECONNABORTED, /* Software caused connection abort */ + WSAECONNRESET, /* Connection reset by peer */ + WSAENOBUFS, /* No buffer space available */ + WSAEISCONN, /* Socket is already connected */ + WSAENOTCONN, /* Socket is not connected */ + WSAESHUTDOWN, /* Can't send after socket shutdown */ + WSAETOOMANYREFS, /* Too many references: can't splice */ + WSAETIMEDOUT, /* Connection timed out */ + WSAECONNREFUSED, /* Connection refused */ + WSAEHOSTDOWN, /* Host is down */ + WSAEHOSTUNREACH, /* No route to host */ + WSAEWOULDBLOCK, /* call would block on non-blocking socket */ + WSAEALREADY, /* operation already in progress */ + WSAEINPROGRESS /* operation now in progress */ +}; + +static oslSocketError osl_SocketErrorFromNative(int nativeType) +{ + oslSocketError i= oslSocketError(0); + + while(i != osl_Socket_E_InvalidError) + { + if(SocketError[i] == nativeType) + return i; + + i = static_cast<oslSocketError>( static_cast<int>(i) + 1); + } + return i; +} + +#define ERROR_FROM_NATIVE(y) osl_SocketErrorFromNative(y) + +#if OSL_DEBUG_LEVEL > 0 +static sal_uInt32 g_nSocketAddr = 0; + +namespace { + +struct LeakWarning +{ + ~LeakWarning() + { + SAL_WARN_IF( g_nSocketAddr, "sal.osl", "sal_socket: " << g_nSocketAddr << " socket address instances leak" ); + } +}; + +} + +static LeakWarning socketWarning; +#endif + +static oslSocket createSocketImpl(SOCKET Socket) +{ + oslSocket pSockImpl = static_cast<oslSocket>(rtl_allocateZeroMemory( sizeof(struct oslSocketImpl))); + pSockImpl->m_Socket = Socket; + pSockImpl->m_nRefCount = 1; + return pSockImpl; +} + +static void destroySocketImpl(oslSocketImpl *pImpl) +{ + if (pImpl) + { + free (pImpl); + } +} + +static oslSocketAddr createSocketAddr( ) +{ + oslSocketAddr pAddr = static_cast<oslSocketAddr>(rtl_allocateZeroMemory( sizeof( struct oslSocketAddrImpl ))); + pAddr->m_nRefCount = 1; +#if OSL_DEBUG_LEVEL > 0 + g_nSocketAddr ++; +#endif + return pAddr; +} + +static oslSocketAddr createSocketAddrWithFamily( + oslAddrFamily family, sal_Int32 port, sal_uInt32 nAddr ) +{ + OSL_ASSERT( family == osl_Socket_FamilyInet ); + + oslSocketAddr pAddr = createSocketAddr(); + switch( family ) + { + case osl_Socket_FamilyInet: + { + struct sockaddr_in* pInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr); + + pInetAddr->sin_family = FAMILY_TO_NATIVE(osl_Socket_FamilyInet); + pInetAddr->sin_addr.s_addr = nAddr; + pInetAddr->sin_port = static_cast<sal_uInt16>(port&0xffff); + break; + } + default: + pAddr->m_sockaddr.sa_family = FAMILY_TO_NATIVE(family); + } + return pAddr; +} + +static oslSocketAddr createSocketAddFromSystem( struct sockaddr *pSystemSockAddr ) +{ + oslSocketAddr pAddr = createSocketAddr(); + memcpy( &(pAddr->m_sockaddr), pSystemSockAddr, sizeof( sockaddr ) ); + return pAddr; +} + +static void destroySocketAddr( oslSocketAddr addr ) +{ +#if OSL_DEBUG_LEVEL > 0 + g_nSocketAddr --; +#endif + free( addr ); +} + +oslSocketAddr SAL_CALL osl_createEmptySocketAddr(oslAddrFamily Family) +{ + oslSocketAddr pAddr = nullptr; + + /* is it an internet-Addr? */ + if (Family == osl_Socket_FamilyInet) + pAddr = createSocketAddrWithFamily(Family, 0 , htonl(INADDR_ANY) ); + else + pAddr = createSocketAddrWithFamily( Family , 0 , 0 ); + + return pAddr; +} + +/** @deprecated, to be removed */ +oslSocketAddr SAL_CALL osl_copySocketAddr(oslSocketAddr Addr) +{ + oslSocketAddr pCopy = nullptr; + if (Addr) + { + pCopy = createSocketAddr(); + + if (pCopy) + memcpy(&(pCopy->m_sockaddr),&(Addr->m_sockaddr), sizeof(struct sockaddr)); + } + return pCopy; +} + +sal_Bool SAL_CALL osl_isEqualSocketAddr(oslSocketAddr Addr1, oslSocketAddr Addr2) +{ + OSL_ASSERT(Addr1); + OSL_ASSERT(Addr2); + struct sockaddr* pAddr1= &(Addr1->m_sockaddr); + struct sockaddr* pAddr2= &(Addr2->m_sockaddr); + + OSL_ASSERT(pAddr1); + OSL_ASSERT(pAddr2); + + if (pAddr1->sa_family == pAddr2->sa_family) + { + switch (pAddr1->sa_family) + { + case AF_INET: + { + struct sockaddr_in* pInetAddr1= reinterpret_cast<struct sockaddr_in*>(pAddr1); + struct sockaddr_in* pInetAddr2= reinterpret_cast<struct sockaddr_in*>(pAddr2); + + if ((pInetAddr1->sin_family == pInetAddr2->sin_family) && + (pInetAddr1->sin_addr.s_addr == pInetAddr2->sin_addr.s_addr) && + (pInetAddr1->sin_port == pInetAddr2->sin_port)) + return true; + [[fallthrough]]; + } + + default: + { + return (memcmp(pAddr1, pAddr2, sizeof(struct sockaddr)) == 0); + } + } + } + + return false; +} + +oslSocketAddr SAL_CALL osl_createInetBroadcastAddr ( + rtl_uString *strDottedAddr, + sal_Int32 Port) +{ + sal_uInt32 nAddr = OSL_INADDR_NONE; + + if (strDottedAddr && strDottedAddr->length) + { + IN_ADDR addr; + INT ret = InetPtonW(AF_INET, o3tl::toW(strDottedAddr->buffer), & addr); + if (1 == ret) + { + nAddr = addr.S_un.S_addr; + } + } + + if (nAddr != OSL_INADDR_NONE) + { + /* Limited broadcast */ + nAddr = ntohl(nAddr); + if (IN_CLASSA(nAddr)) + { + nAddr &= IN_CLASSA_NET; + nAddr |= IN_CLASSA_HOST; + } + else if (IN_CLASSB(nAddr)) + { + nAddr &= IN_CLASSB_NET; + nAddr |= IN_CLASSB_HOST; + } + else if (IN_CLASSC(nAddr)) + { + nAddr &= IN_CLASSC_NET; + nAddr |= IN_CLASSC_HOST; + } + else + { + /* No broadcast in class D */ + return nullptr; + } + nAddr = htonl(nAddr); + } + + oslSocketAddr pAddr = + createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( static_cast<sal_uInt16>(Port)), nAddr ); + return pAddr; +} + +oslSocketAddr SAL_CALL osl_createInetSocketAddr ( + rtl_uString *strDottedAddr, + sal_Int32 Port) +{ + sal_uInt32 Addr; + + IN_ADDR addr; + INT ret = InetPtonW(AF_INET, o3tl::toW(strDottedAddr->buffer), & addr); + Addr = ret == 1 ? addr.S_un.S_addr : OSL_INADDR_NONE; + + oslSocketAddr pAddr = nullptr; + if(Addr != OSL_INADDR_NONE) + { + pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( static_cast<sal_uInt16>(Port)), Addr ); + } + return pAddr; +} + +oslSocketResult SAL_CALL osl_setAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence *pByteSeq ) +{ + OSL_ASSERT( pAddr ); + OSL_ASSERT( pByteSeq ); + + oslSocketResult res = osl_Socket_Error; + if( pAddr && pByteSeq ) + { + OSL_ASSERT( pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE( osl_Socket_FamilyInet ) ); + OSL_ASSERT( pByteSeq->nElements == 4 ); + struct sockaddr_in * pSystemInetAddr = reinterpret_cast<struct sockaddr_in *>(&pAddr->m_sockaddr); + memcpy( &(pSystemInetAddr->sin_addr) , pByteSeq->elements , 4 ); + res = osl_Socket_Ok; + } + return res; +} + +/** Returns the addr field in the struct sockaddr. ppByteSeq is in network byteorder. *ppByteSeq may + either be 0 or contain a constructed sal_Sequence. + */ +oslSocketResult SAL_CALL osl_getAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence **ppByteSeq ) +{ + OSL_ASSERT( pAddr ); + OSL_ASSERT( ppByteSeq ); + + oslSocketResult res = osl_Socket_Error; + if( pAddr && ppByteSeq ) + { + struct sockaddr_in * pSystemInetAddr = reinterpret_cast<struct sockaddr_in *>(&pAddr->m_sockaddr); + rtl_byte_sequence_constructFromArray( ppByteSeq , reinterpret_cast<sal_Int8 *>(&pSystemInetAddr->sin_addr),4); + res = osl_Socket_Ok; + } + return res; +} + +struct oslHostAddrImpl { + rtl_uString *pHostName; + oslSocketAddr pSockAddr; +} ; + +oslHostAddr SAL_CALL osl_createHostAddr ( + rtl_uString *strHostname, + const oslSocketAddr pSocketAddr) +{ + oslHostAddr pAddr; + rtl_uString *cn= nullptr; + + if ((strHostname == nullptr) || (strHostname->length == 0) || (pSocketAddr == nullptr)) + return nullptr; + + rtl_uString_newFromString( &cn, strHostname); + + pAddr= static_cast<oslHostAddr>(malloc (sizeof (struct oslHostAddrImpl))); + + if (pAddr == nullptr) + { + rtl_uString_release(cn); + return nullptr; + } + + pAddr->pHostName= cn; + pAddr->pSockAddr= osl_copySocketAddr( pSocketAddr ); + + return pAddr; +} + +oslHostAddr SAL_CALL osl_createHostAddrByName(rtl_uString *strHostname) +{ + if ((strHostname == nullptr) || (strHostname->length == 0)) + return nullptr; + + PADDRINFOW pAddrInfo = nullptr; + int ret = GetAddrInfoW( + o3tl::toW(strHostname->buffer), nullptr, nullptr, & pAddrInfo); + if (0 != ret) + { + SAL_INFO("sal.osl", "GetAddrInfoW failed: " << WSAGetLastError()); + return nullptr; + } + + oslHostAddr pRet = nullptr; + for (PADDRINFOW pIter = pAddrInfo; pIter; pIter = pIter->ai_next) + { + if (AF_INET == pIter->ai_family) + { + pRet = static_cast<oslHostAddr>( + rtl_allocateZeroMemory(sizeof(struct oslHostAddrImpl))); + if (pIter->ai_canonname == nullptr) { + rtl_uString_new(&pRet->pHostName); + } else { + rtl_uString_newFromStr(&pRet->pHostName, o3tl::toU(pIter->ai_canonname)); + } + pRet->pSockAddr = createSocketAddr(); + memcpy(& pRet->pSockAddr->m_sockaddr, + pIter->ai_addr, pIter->ai_addrlen); + break; // ignore other results + } + } + FreeAddrInfoW(pAddrInfo); + return pRet; +} + +oslHostAddr SAL_CALL osl_createHostAddrByAddr(const oslSocketAddr pAddr) +{ + if (pAddr == nullptr) + return nullptr; + + if (pAddr->m_sockaddr.sa_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet)) + return nullptr; + + const struct sockaddr_in *sin= reinterpret_cast<const struct sockaddr_in *>(&pAddr->m_sockaddr); + + if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) + return nullptr; + + WCHAR buf[NI_MAXHOST]; + int ret = GetNameInfoW( + & pAddr->m_sockaddr, sizeof(struct sockaddr), + buf, NI_MAXHOST, + nullptr, 0, 0); + if (0 != ret) + { + SAL_INFO("sal.osl", "GetNameInfoW failed: " << WSAGetLastError()); + return nullptr; + } + + oslHostAddr pRet = static_cast<oslHostAddr>( + rtl_allocateZeroMemory(sizeof(struct oslHostAddrImpl))); + rtl_uString_newFromStr(&pRet->pHostName, o3tl::toU(buf)); + pRet->pSockAddr = createSocketAddr(); + memcpy(& pRet->pSockAddr->m_sockaddr, + & pAddr->m_sockaddr, sizeof(struct sockaddr)); + return pRet; +} + +oslHostAddr SAL_CALL osl_copyHostAddr(const oslHostAddr Addr) +{ + oslHostAddr pAddr = Addr; + + if (pAddr) + return osl_createHostAddr (pAddr->pHostName, pAddr->pSockAddr); + else + return nullptr; +} + +void SAL_CALL osl_getHostnameOfHostAddr( + const oslHostAddr pAddr, rtl_uString **strHostname) +{ + if (pAddr) + rtl_uString_assign (strHostname, pAddr->pHostName); + else + rtl_uString_new (strHostname); +} + +oslSocketAddr SAL_CALL osl_getSocketAddrOfHostAddr(const oslHostAddr pAddr) +{ + if (pAddr) + return pAddr->pSockAddr; + else + return nullptr; +} + +void SAL_CALL osl_destroyHostAddr(oslHostAddr pAddr) +{ + if (pAddr) + { + if (pAddr->pHostName) + rtl_uString_release (pAddr->pHostName); + if (pAddr->pSockAddr) + osl_destroySocketAddr( pAddr->pSockAddr ); + + free (pAddr); + } +} + +oslSocketResult SAL_CALL osl_getLocalHostname (rtl_uString **strLocalHostname) +{ + static auto const init = []() -> std::pair<oslSocketResult, OUString> { + sal_Unicode LocalHostname[256] = {0}; + + char Host[256]= ""; + if (gethostname(Host, sizeof(Host)) == 0) + { + OUString u; + if (rtl_convertStringToUString( + &u.pData, Host, strlen(Host), osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)) + && o3tl::make_unsigned(u.getLength()) < SAL_N_ELEMENTS(LocalHostname)) + { + memcpy(LocalHostname, u.getStr(), (u.getLength() + 1) * sizeof (sal_Unicode)); + } + } + + if (rtl_ustr_getLength(LocalHostname) > 0) + { + return {osl_Socket_Ok, OUString(LocalHostname)}; + } + + return {osl_Socket_Error, OUString()}; + }(); + + rtl_uString_assign (strLocalHostname, init.second.pData); + + return init.first; +} + +oslSocketAddr SAL_CALL osl_resolveHostname(rtl_uString* strHostname) +{ + oslHostAddr pAddr = osl_createHostAddrByName (strHostname); + if (pAddr) + { + oslSocketAddr SockAddr = osl_copySocketAddr( pAddr->pSockAddr ); + osl_destroyHostAddr(pAddr); + return SockAddr; + } + return nullptr; +} + +sal_Int32 SAL_CALL osl_getServicePort ( + rtl_uString* strServicename, + rtl_uString* strProtocol) +{ + struct servent* ps; + + rtl_String *str_Servicename=nullptr; + rtl_String *str_Protocol=nullptr; + + rtl_uString2String( + &str_Servicename, + rtl_uString_getStr(strServicename), + rtl_uString_getLength(strServicename), + RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS); + rtl_uString2String( + &str_Protocol, + rtl_uString_getStr(strProtocol), + rtl_uString_getLength(strProtocol), + RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS); + + ps= getservbyname( + rtl_string_getStr(str_Servicename), + rtl_string_getStr(str_Protocol)); + + rtl_string_release( str_Servicename ); + rtl_string_release( str_Protocol ); + + if (ps != nullptr) + return ntohs(ps->s_port); + + return OSL_INVALID_PORT; +} + +void SAL_CALL osl_destroySocketAddr(oslSocketAddr pAddr) +{ + destroySocketAddr( pAddr ); +} + +oslAddrFamily SAL_CALL osl_getFamilyOfSocketAddr(oslSocketAddr pAddr) +{ + if (pAddr) + return FAMILY_FROM_NATIVE(pAddr->m_sockaddr.sa_family); + else + return osl_Socket_FamilyInvalid; +} + +sal_Int32 SAL_CALL osl_getInetPortOfSocketAddr(oslSocketAddr pAddr) +{ + if( pAddr ) + { + struct sockaddr_in* pSystemInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr); + + if (pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet)) + return ntohs(pSystemInetAddr->sin_port); + } + return OSL_INVALID_PORT; +} + +sal_Bool SAL_CALL osl_setInetPortOfSocketAddr ( + oslSocketAddr pAddr, + sal_Int32 Port) +{ + if (pAddr == nullptr) + return false; + + struct sockaddr_in* pSystemInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr); + + if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet)) + return false; + + pSystemInetAddr->sin_port= htons(static_cast<short>(Port)); + return true; +} + +oslSocketResult SAL_CALL osl_getHostnameOfSocketAddr ( + oslSocketAddr Addr, + rtl_uString **strHostName) +{ + oslHostAddr pAddr= osl_createHostAddrByAddr (Addr); + + if (pAddr) + { + rtl_uString_newFromString(strHostName, pAddr->pHostName); + + osl_destroyHostAddr(pAddr); + + return osl_Socket_Ok; + } + + return osl_Socket_Error; +} + +oslSocketResult SAL_CALL osl_getDottedInetAddrOfSocketAddr ( + oslSocketAddr pAddr, + rtl_uString **strDottedInetAddr) +{ + if (pAddr == nullptr) + return osl_Socket_Error; + + struct sockaddr_in *pSystemInetAddr = reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr); + if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet)) + return osl_Socket_Error; + + *strDottedInetAddr = nullptr; + WCHAR buf[16]; // 16 for IPV4, 46 for IPV6 + PCWSTR ret = InetNtopW( + AF_INET, & pSystemInetAddr->sin_addr, + buf, SAL_N_ELEMENTS(buf)); + if (nullptr == ret) + { + SAL_INFO("sal.osl", "InetNtopW failed: " << WSAGetLastError()); + return osl_Socket_Error; + } + rtl_uString_newFromStr(strDottedInetAddr, o3tl::toU(ret)); + OSL_ASSERT(*strDottedInetAddr != nullptr); + + return osl_Socket_Ok; +} + +oslSocket SAL_CALL osl_createSocket( + oslAddrFamily Family, + oslSocketType Type, + oslProtocol Protocol) +{ + /* alloc memory */ + oslSocket pSocket = createSocketImpl(0); + + if (pSocket == nullptr) + return nullptr; + + /* create socket */ + pSocket->m_Socket = socket(FAMILY_TO_NATIVE(Family), + TYPE_TO_NATIVE(Type), + PROTOCOL_TO_NATIVE(Protocol)); + + /* creation failed => free memory */ + if(pSocket->m_Socket == OSL_INVALID_SOCKET) + { + int nErrno = WSAGetLastError(); + SAL_WARN("sal.osl", "socket creation failed: (" << nErrno << "): " << WindowsErrorString(nErrno)); + + destroySocketImpl(pSocket); + pSocket = nullptr; + } + else + { + pSocket->m_Flags = 0; + } + + return pSocket; +} + +void SAL_CALL osl_acquireSocket(oslSocket pSocket) +{ + osl_atomic_increment(&(pSocket->m_nRefCount)); +} + +void SAL_CALL osl_releaseSocket(oslSocket pSocket) +{ + if (pSocket && osl_atomic_decrement(&(pSocket->m_nRefCount)) == 0) + { + osl_closeSocket(pSocket); + destroySocketImpl(pSocket); + } +} + +void SAL_CALL osl_closeSocket(oslSocket pSocket) +{ + /* socket already invalid */ + if (!pSocket) + return; + + /* close */ + closesocket(pSocket->m_Socket); + + pSocket->m_Socket = OSL_INVALID_SOCKET; +} + +/** + Note that I rely on the fact that oslSocketAddr and struct sockaddr + are the same! I don't like it very much but see no other easy way + to conceal the struct sockaddr from the eyes of the user. +*/ +oslSocketAddr SAL_CALL osl_getLocalAddrOfSocket(oslSocket pSocket) +{ + struct sockaddr Addr; + int AddrLen; + + if (pSocket == nullptr) /* ENOTSOCK */ + return nullptr; + + AddrLen= sizeof(struct sockaddr); + + if (getsockname(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR) + return nullptr; + + oslSocketAddr pAddr = createSocketAddFromSystem( &Addr ); + return pAddr; +} + +oslSocketAddr SAL_CALL osl_getPeerAddrOfSocket(oslSocket pSocket) +{ + struct sockaddr Addr; + int AddrLen; + + if (pSocket == nullptr) /* ENOTSOCK */ + return nullptr; + + AddrLen= sizeof(struct sockaddr); + + if (getpeername(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR) + return nullptr; + + oslSocketAddr pAddr = createSocketAddFromSystem( &Addr ); + return pAddr; +} + +sal_Bool SAL_CALL osl_bindAddrToSocket ( oslSocket pSocket, oslSocketAddr pAddr) +{ + OSL_ASSERT( pAddr ); + + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + return (bind(pSocket->m_Socket, + &(pAddr->m_sockaddr), + sizeof(struct sockaddr)) != OSL_SOCKET_ERROR); +} + +oslSocketResult SAL_CALL osl_connectSocketTo ( + oslSocket pSocket, + oslSocketAddr pAddr, + const TimeValue* pTimeout) +{ + + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + if (pAddr == nullptr) /* EDESTADDRREQ */ + return osl_Socket_Error; + + if (pTimeout == nullptr) + { + if(connect(pSocket->m_Socket, + &(pAddr->m_sockaddr), + sizeof(struct sockaddr)) == OSL_SOCKET_ERROR) + return osl_Socket_Error; + else + return osl_Socket_Ok; + } + else + { + fd_set fds; + int error; + struct timeval tv; + unsigned long Param; + oslSocketResult Result= osl_Socket_Ok; + + if (pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING) + { + if (connect(pSocket->m_Socket, + &(pAddr->m_sockaddr), + sizeof(struct sockaddr)) == OSL_SOCKET_ERROR) + { + switch (WSAGetLastError()) + { + case WSAEWOULDBLOCK: + case WSAEINPROGRESS: + return osl_Socket_InProgress; + + default: + return osl_Socket_Error; + } + } + else + return osl_Socket_Ok; + } + + /* set socket temporarily to non-blocking */ + Param= 1; + OSL_VERIFY(ioctlsocket( + pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR); + + /* initiate connect */ + if (connect(pSocket->m_Socket, + &(pAddr->m_sockaddr), + sizeof(struct sockaddr)) != OSL_SOCKET_ERROR) + { + /* immediate connection */ + + Param= 0; + ioctlsocket(pSocket->m_Socket, FIONBIO, &Param); + + return osl_Socket_Ok; + } + else + { + error = WSAGetLastError(); + + /* really an error or just delayed? */ + if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS) + { + Param= 0; + ioctlsocket(pSocket->m_Socket, FIONBIO, &Param); + + return osl_Socket_Error; + } + } + + /* prepare select set for socket */ + FD_ZERO(&fds); + FD_SET(pSocket->m_Socket, &fds); + + /* divide milliseconds into seconds and microseconds */ + tv.tv_sec= pTimeout->Seconds; + tv.tv_usec= pTimeout->Nanosec / 1000L; + + /* select */ + error= select(pSocket->m_Socket+1, + nullptr, + &fds, + nullptr, + &tv); + + if (error > 0) /* connected */ + { + SAL_WARN_IF( + !FD_ISSET(pSocket->m_Socket, &fds), + "sal.osl", + "osl_connectSocketTo(): select returned but socket not set"); + + Result= osl_Socket_Ok; + + } + else if(error < 0) /* error */ + { + /* errno == EBADF: most probably interrupted by close() */ + if(WSAGetLastError() == WSAEBADF) + { + /* do not access pSockImpl because it is about to be or */ + /* already destroyed */ + return osl_Socket_Interrupted; + } + else + Result= osl_Socket_Error; + + } + else /* timeout */ + Result= osl_Socket_TimedOut; + + /* clean up */ + Param= 0; + ioctlsocket(pSocket->m_Socket, FIONBIO, &Param); + + return Result; + } +} + +sal_Bool SAL_CALL osl_listenOnSocket ( + oslSocket pSocket, + sal_Int32 MaxPendingConnections) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + return (listen(pSocket->m_Socket, + MaxPendingConnections == -1 ? + SOMAXCONN : + MaxPendingConnections) != OSL_SOCKET_ERROR); +} + +oslSocket SAL_CALL osl_acceptConnectionOnSocket ( + oslSocket pSocket, + oslSocketAddr* ppAddr) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return nullptr; + + SOCKET Connection; + if(ppAddr) + { + if( *ppAddr ) + { + osl_destroySocketAddr( *ppAddr ); + *ppAddr = nullptr; + } + int AddrLen= sizeof(struct sockaddr); + + /* user wants to know peer Addr */ + struct sockaddr Addr; + + Connection= accept(pSocket->m_Socket, &Addr, &AddrLen); + OSL_ASSERT(AddrLen == sizeof(struct sockaddr)); + + if(Connection != static_cast<SOCKET>(OSL_SOCKET_ERROR)) + *ppAddr= createSocketAddFromSystem(&Addr); + else + *ppAddr = nullptr; + } + else + { + /* user is not interested in peer-addr */ + Connection= accept(pSocket->m_Socket, nullptr, nullptr); + } + + /* accept failed? */ + if(Connection == static_cast<SOCKET>(OSL_SOCKET_ERROR)) + return nullptr; + + /* alloc memory */ + oslSocket pConnectionSocket; + pConnectionSocket= createSocketImpl(Connection); + + pConnectionSocket->m_Flags = 0; + + return pConnectionSocket; +} + +sal_Int32 SAL_CALL osl_receiveSocket ( + oslSocket pSocket, + void* pBuffer, + sal_uInt32 BytesToRead, + oslSocketMsgFlag Flag) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + return recv(pSocket->m_Socket, + static_cast<char*>(pBuffer), + BytesToRead, + MSG_FLAG_TO_NATIVE(Flag)); +} + +sal_Int32 SAL_CALL osl_receiveFromSocket ( + oslSocket pSocket, + oslSocketAddr SenderAddr, + void* pBuffer, + sal_uInt32 BufferSize, + oslSocketMsgFlag Flag) +{ + struct sockaddr *pSystemSockAddr = nullptr; + int AddrLen = 0; + if( SenderAddr ) + { + AddrLen = sizeof( struct sockaddr ); + pSystemSockAddr = &(SenderAddr->m_sockaddr); + } + + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + return recvfrom(pSocket->m_Socket, + static_cast<char*>(pBuffer), + BufferSize, + MSG_FLAG_TO_NATIVE(Flag), + pSystemSockAddr, + &AddrLen); +} + +sal_Int32 SAL_CALL osl_sendSocket ( + oslSocket pSocket, + const void* pBuffer, + sal_uInt32 BytesToSend, + oslSocketMsgFlag Flag) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + return send(pSocket->m_Socket, + static_cast<char const *>(pBuffer), + BytesToSend, + MSG_FLAG_TO_NATIVE(Flag)); +} + +sal_Int32 SAL_CALL osl_sendToSocket ( + oslSocket pSocket, + oslSocketAddr ReceiverAddr, + const void* pBuffer, + sal_uInt32 BytesToSend, + oslSocketMsgFlag Flag) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + /* ReceiverAddr might be 0 when used on a connected socket. */ + /* Then sendto should behave like send. */ + + struct sockaddr *pSystemSockAddr = nullptr; + if( ReceiverAddr ) + pSystemSockAddr = &(ReceiverAddr->m_sockaddr); + + return sendto(pSocket->m_Socket, + static_cast<char const *>(pBuffer), + BytesToSend, + MSG_FLAG_TO_NATIVE(Flag), + pSystemSockAddr, + pSystemSockAddr == nullptr ? 0 : sizeof(struct sockaddr)); +} + +sal_Int32 SAL_CALL osl_readSocket( oslSocket pSocket, void *pBuffer, sal_Int32 n ) +{ + sal_uInt8 * Ptr = static_cast<sal_uInt8 *>(pBuffer); + + OSL_ASSERT( pSocket); + + /* loop until all desired bytes were read or an error occurred */ + sal_uInt32 BytesRead= 0; + sal_uInt32 BytesToRead= n; + while (BytesToRead > 0) + { + sal_Int32 RetVal; + RetVal= osl_receiveSocket(pSocket, + Ptr, + BytesToRead, + osl_Socket_MsgNormal); + + /* error occurred? */ + if(RetVal <= 0) + { + break; + } + + BytesToRead -= RetVal; + BytesRead += RetVal; + Ptr += RetVal; + } + + return BytesRead; +} + +sal_Int32 SAL_CALL osl_writeSocket( oslSocket pSocket, const void *pBuffer, sal_Int32 n ) +{ + OSL_ASSERT( pSocket ); + + /* loop until all desired bytes were send or an error occurred */ + sal_uInt32 BytesSend= 0; + sal_uInt32 BytesToSend= n; + sal_uInt8 const *Ptr = static_cast<sal_uInt8 const *>(pBuffer); + while (BytesToSend > 0) + { + sal_Int32 RetVal; + + RetVal= osl_sendSocket( pSocket,Ptr,BytesToSend,osl_Socket_MsgNormal); + + /* error occurred? */ + if(RetVal <= 0) + { + break; + } + + BytesToSend -= RetVal; + BytesSend += RetVal; + Ptr += RetVal; + + } + return BytesSend; +} + +sal_Bool SAL_CALL osl_isReceiveReady ( + oslSocket pSocket, + const TimeValue* pTimeout) +{ + fd_set fds; + struct timeval tv; + + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + FD_ZERO(&fds); + FD_SET(pSocket->m_Socket, &fds); + + if (pTimeout) + { + tv.tv_sec = pTimeout->Seconds; + tv.tv_usec = pTimeout->Nanosec / 1000L; + } + + return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */ + &fds, /* check read operations */ + nullptr, /* check write ops */ + nullptr, /* ckeck for OOB */ + pTimeout ? &tv : nullptr)==1); /* use timeout? */ +} + +/*****************************************************************************/ +/* osl_isSendReady */ +/*****************************************************************************/ +sal_Bool SAL_CALL osl_isSendReady ( + oslSocket pSocket, + const TimeValue* pTimeout) +{ + fd_set fds; + struct timeval tv; + + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + FD_ZERO(&fds); + FD_SET(pSocket->m_Socket, &fds); + + if (pTimeout) + { + tv.tv_sec = pTimeout->Seconds; + tv.tv_usec = pTimeout->Nanosec / 1000L; + } + + return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */ + nullptr, /* check read operations */ + &fds, /* check write ops */ + nullptr, /* ckeck for OOB */ + pTimeout ? &tv : nullptr)==1); /* use timeout? */ +} + +sal_Bool SAL_CALL osl_isExceptionPending ( + oslSocket pSocket, + const TimeValue* pTimeout) +{ + fd_set fds; + struct timeval tv; + + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + FD_ZERO(&fds); + FD_SET(pSocket->m_Socket, &fds); + + if (pTimeout) + { + tv.tv_sec = pTimeout->Seconds; + tv.tv_usec = pTimeout->Nanosec / 1000L; + } + + return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */ + nullptr, /* check read operations */ + nullptr, /* check write ops */ + &fds, /* ckeck for OOB */ + pTimeout ? &tv : nullptr)==1); /* use timeout? */ +} + +sal_Bool SAL_CALL osl_shutdownSocket ( + oslSocket pSocket, + oslSocketDirection Direction) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + return (shutdown(pSocket->m_Socket, DIRECTION_TO_NATIVE(Direction))==0); +} + +sal_Int32 SAL_CALL osl_getSocketOption ( + oslSocket pSocket, + oslSocketOptionLevel Level, + oslSocketOption Option, + void* pBuffer, + sal_uInt32 BufferLen) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_Error; + + int len = BufferLen; + if (getsockopt(pSocket->m_Socket, + OPTION_LEVEL_TO_NATIVE(Level), + OPTION_TO_NATIVE(Option), + static_cast<char *>(pBuffer), + &len) == -1) + { + return -1; + } + + return len; +} + +sal_Bool SAL_CALL osl_setSocketOption ( + oslSocket pSocket, + oslSocketOptionLevel Level, + oslSocketOption Option, + void* pBuffer, + sal_uInt32 BufferLen) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + return(setsockopt(pSocket->m_Socket, + OPTION_LEVEL_TO_NATIVE(Level), + OPTION_TO_NATIVE(Option), + static_cast<char*>(pBuffer), + BufferLen) == 0); +} + +sal_Bool SAL_CALL osl_enableNonBlockingMode ( oslSocket pSocket, sal_Bool On) +{ + unsigned long Param= On ? 1 : 0; + + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + pSocket->m_Flags = Param ? + (pSocket->m_Flags | OSL_SOCKET_FLAGS_NONBLOCKING) : + (pSocket->m_Flags & ~OSL_SOCKET_FLAGS_NONBLOCKING) ; + + return ( + ioctlsocket(pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR); +} + +sal_Bool SAL_CALL osl_isNonBlockingMode(oslSocket pSocket) +{ + if (pSocket == nullptr) /* ENOTSOCK */ + return false; + + return (pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING) != 0; +} + +oslSocketType SAL_CALL osl_getSocketType(oslSocket pSocket) +{ + int Type=0; + int TypeSize= sizeof(Type); + + if (pSocket == nullptr) /* ENOTSOCK */ + return osl_Socket_TypeInvalid; + + if(getsockopt(pSocket->m_Socket, + OPTION_LEVEL_TO_NATIVE(osl_Socket_LevelSocket), + OPTION_TO_NATIVE(osl_Socket_OptionType), + reinterpret_cast<char *>(&Type), + &TypeSize) == -1) + { + /* error */ + return osl_Socket_TypeInvalid; + } + + return TYPE_FROM_NATIVE(Type); +} + +void SAL_CALL osl_getLastSocketErrorDescription ( + oslSocket /*Socket*/, + rtl_uString **strError) +{ + int error; + + switch(error = WSAGetLastError()) + { + case WSAENOTSOCK: + rtl_uString_newFromAscii (strError, "WSAENOTSOCK, Socket operation on non-socket. A socket created in one process is used by another process."); + break; + + case WSAEDESTADDRREQ: + rtl_uString_newFromAscii (strError, "WSAEDESTADDRREQ, Destination Addr required"); + break; + + case WSAEMSGSIZE: + rtl_uString_newFromAscii (strError, "WSAEMSGSIZE, Message too long"); + break; + + case WSAEPROTOTYPE: + rtl_uString_newFromAscii (strError, "WSAEPROTOTYPE, Protocol wrong type for socket"); + break; + + case WSAENOPROTOOPT: + rtl_uString_newFromAscii (strError, "WSAENOPROTOOPT, Protocol not available"); + break; + + case WSAEPROTONOSUPPORT: + rtl_uString_newFromAscii (strError, "WSAEPROTONOSUPPORT, Protocol not supported"); + break; + + case WSAESOCKTNOSUPPORT: + rtl_uString_newFromAscii (strError, "WSAESOCKTNOSUPPORT, Socket type not supported"); + break; + + case WSAEOPNOTSUPP: + rtl_uString_newFromAscii (strError, "WSAEOPNOTSUPP, Operation not supported on socket"); + break; + + case WSAEPFNOSUPPORT: + rtl_uString_newFromAscii (strError, "WSAEPFNOSUPPORT, Protocol family not supported"); + break; + + case WSAEAFNOSUPPORT: + rtl_uString_newFromAscii (strError, "WSEAFNOSUPPORT, Addr family not supported by protocol family"); + break; + + case WSAEADDRINUSE: + rtl_uString_newFromAscii (strError, "WSAEADDRINUSE, Triggered by bind() because a process went down without closing a socket."); + break; + + case WSAEADDRNOTAVAIL: + rtl_uString_newFromAscii (strError, "WSAEADDRNOTAVAIL, Can't assign requested Addr"); + break; + + case WSAENETDOWN: + rtl_uString_newFromAscii (strError, "WSAENETDOWN, Network is down"); + break; + + case WSAENETUNREACH: + rtl_uString_newFromAscii (strError, "WSAENETUNREACH, Network is unreachable"); + break; + + case WSAENETRESET: + rtl_uString_newFromAscii (strError, "WSAENETRESET, Network dropped connection or reset"); + break; + + case WSAECONNABORTED: + rtl_uString_newFromAscii (strError, "WSAECONNABORTED, Software caused connection abort"); + break; + + case WSAECONNRESET: + rtl_uString_newFromAscii (strError, "WSAECONNRESET, Connection reset by peer"); + break; + + case WSAENOBUFS: + rtl_uString_newFromAscii (strError, "WSAENOBUFS, No buffer space available."); + break; + + case WSAEISCONN: + rtl_uString_newFromAscii (strError, "WSAEISCONN, Socket is already connected"); + break; + + case WSAENOTCONN: + rtl_uString_newFromAscii (strError, "WSAENOTCONN, Socket is not connected"); + break; + + case WSAESHUTDOWN: + rtl_uString_newFromAscii (strError, "WSAESHUTDOWN, Can't send after socket shutdown"); + break; + + case WSAETIMEDOUT: + rtl_uString_newFromAscii (strError, "WSAETIMEDOUT, Connection timed out"); + break; + + case WSAECONNREFUSED: + rtl_uString_newFromAscii (strError, "WSAECONNREFUSED, Connection refused"); + break; + + case WSAEHOSTDOWN: + rtl_uString_newFromAscii (strError, "WSAEHOSTDOWN, Networking subsystem not started"); + break; + + case WSAEHOSTUNREACH: + rtl_uString_newFromAscii (strError, "WSAEHOSTUNREACH, No route to host"); + break; + + case WSAEWOULDBLOCK: + rtl_uString_newFromAscii (strError, "WSAEWOULDBLOCK, Operation would block"); + break; + + case WSAEINPROGRESS: + rtl_uString_newFromAscii (strError, "WSAEINPROGRESS, Operation now in progress"); + break; + + case WSAEALREADY: + rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation already in progress"); + break; + + case WSAEINTR: + rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation was interrupted"); + break; + + case WSAEBADF: + rtl_uString_newFromAscii (strError, "WSAEBADF, Bad file number"); + break; + + case WSAEACCES: + rtl_uString_newFromAscii (strError, "WSAEACCES, Access is denied"); + break; + + case WSAEFAULT: + rtl_uString_newFromAscii (strError, "WSAEFAULT, Bad memory Addr"); + break; + + case WSAEINVAL: + rtl_uString_newFromAscii (strError, "WSAEINVAL, The socket has not been bound with bind() or is already connected"); + break; + + case WSAEMFILE: + rtl_uString_newFromAscii (strError, "WSAEMFILE, No more file descriptors are available"); + break; + + case WSAETOOMANYREFS: + rtl_uString_newFromAscii (strError, "WSAETOOMANYREFS, Undocumented WinSock error"); + break; + + case WSAENAMETOOLONG: + rtl_uString_newFromAscii (strError, "WSAENAMETOOLONG, Undocumented WinSock error"); + break; + + case WSAENOTEMPTY: + rtl_uString_newFromAscii (strError, "WSAENOTEMPTY, Undocumented WinSock error"); + break; + + case WSAEPROCLIM: + rtl_uString_newFromAscii (strError, "WSAEPROCLIM, Undocumented WinSock error"); + break; + + case WSAEUSERS: + rtl_uString_newFromAscii (strError, "WSAEUSERS, Undocumented WinSock error"); + break; + + case WSAEDQUOT: + rtl_uString_newFromAscii (strError, "WSAEDQUOT, Undocumented WinSock error"); + break; + + case WSAESTALE: + rtl_uString_newFromAscii (strError, "WSAESTALE, Undocumented WinSock error"); + break; + + case WSAEREMOTE: + rtl_uString_newFromAscii (strError, "WSAEREMOTE, Undocumented WinSock error"); + break; + + case WSAEDISCON: + rtl_uString_newFromAscii (strError, "WSAEDISCON, Circuit was gracefully terminated"); + break; + + case WSASYSNOTREADY: + rtl_uString_newFromAscii (strError, "WSASYSNOTREADY, The underlying network subsystem is not ready for network communication"); + break; + + case WSAVERNOTSUPPORTED: + rtl_uString_newFromAscii (strError, "WSAVERNOTSUPPORTED, The version of Windows Sockets API support requested is not provided by this particular Windows Sockets implementation"); + break; + + case WSANOTINITIALISED: + rtl_uString_newFromAscii (strError, "WSANOTINITIALISED, WSAStartup() has not been called"); + break; + + case WSAHOST_NOT_FOUND: + rtl_uString_newFromAscii (strError, "WSAHOST_NOT_FOUND, Authoritative answer host not found"); + break; + + case WSATRY_AGAIN: + rtl_uString_newFromAscii (strError, "WSATRY_AGAIN, Non-authoritative answer host not found or SERVERFAIL"); + break; + + case WSANO_RECOVERY: + rtl_uString_newFromAscii (strError, "WSANO_RECOVERY, Non recoverable errors, FORMERR, REFUSED, NOTIMP"); + break; + + case WSANO_DATA: + rtl_uString_newFromAscii (strError, "WSANO_DATA or WSANO_ADDRESS, Valid name, no data record of requested type"); + break; + + default: + { + sal_Unicode message[128]; + + wsprintfW(o3tl::toW(message), L"Unknown WinSock Error Number %d", error); + rtl_uString_newFromStr (strError, message); + } + + return; + + } +} + +oslSocketError SAL_CALL osl_getLastSocketError(oslSocket /*Socket*/) +{ + return ERROR_FROM_NATIVE(WSAGetLastError()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/sockimpl.hxx b/sal/osl/w32/sockimpl.hxx new file mode 100644 index 000000000..72b204a2e --- /dev/null +++ b/sal/osl/w32/sockimpl.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/socket.h> +#include <osl/interlck.h> + +#define OSL_SOCKET_FLAGS_NONBLOCKING 0x0001 + +struct oslSocketImpl { + oslInterlockedCount m_nRefCount; + SOCKET m_Socket; + int m_Flags; +}; + +struct oslSocketAddrImpl +{ + struct sockaddr m_sockaddr; + oslInterlockedCount m_nRefCount; +}; + +oslSocket osl_createSocketImpl_(SOCKET Socket); +void osl_destroySocketImpl_(oslSocket pImpl); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/system.h b/sal/osl/w32/system.h new file mode 100644 index 000000000..921d746fd --- /dev/null +++ b/sal/osl/w32/system.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#if OSL_DEBUG_LEVEL <= 3 +#define NO_DEBUG_CRT +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <malloc.h> +#include <limits.h> +#include <process.h> +#include <time.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <io.h> +#include <share.h> +#include <direct.h> + +/* Must define this else build breaks because Winsock2.h + includes Windows.h and without WIN32_LEAN_AND_MEAN + also includes mswsock.h which needs a forward typedef + of SOCKET ... +*/ +#define WIN32_LEAN_AND_MEAN + +// winsock2.h includes windows.h +#include <winsock2.h> +#include <wsipx.h> +#include <ws2tcpip.h> +#include <shlobj.h> +#ifndef NO_DEBUG_CRT +#include <crtdbg.h> +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/tempfile.cxx b/sal/osl/w32/tempfile.cxx new file mode 100644 index 000000000..f0065bf2d --- /dev/null +++ b/sal/osl/w32/tempfile.cxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <systools/win32/uwinapi.h> + +#include <osl/file.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <rtl/ustring.hxx> + +#include "file-impl.hxx" +#include "file_error.hxx" +#include "file_url.hxx" +#include "path_helper.hxx" + +#include <malloc.h> +#include <cassert> + +// Allocate n number of t's on the stack return a pointer to it in p +#define STACK_ALLOC(p, t, n) __try {(p) = static_cast<t*>(_alloca((n)*sizeof(t)));} \ + __except(EXCEPTION_EXECUTE_HANDLER) {(p) = nullptr;} + +// Temp file functions + +static oslFileError osl_setup_base_directory_impl_( + rtl_uString* pustrDirectoryURL, + rtl_uString** ppustr_base_dir) +{ + OUString dir_url; + OUString dir; + oslFileError error = osl_File_E_None; + + if (pustrDirectoryURL) + dir_url = pustrDirectoryURL; + else + error = osl_getTempDirURL(&dir_url.pData); + + if (error == osl_File_E_None) + error = osl_getSystemPathFromFileURL_(dir_url, &dir.pData, false); + + if (error == osl_File_E_None) + rtl_uString_assign(ppustr_base_dir, dir.pData); + + return error; +} + +static oslFileError osl_setup_createTempFile_impl_( + rtl_uString* pustrDirectoryURL, + oslFileHandle* pHandle, + rtl_uString** ppustrTempFileURL, + rtl_uString** ppustr_base_dir, + sal_Bool* b_delete_on_close) +{ + oslFileError osl_error; + + OSL_PRECOND(((pHandle != nullptr) || (ppustrTempFileURL != nullptr)), "Invalid parameter!"); + + if ((pHandle == nullptr) && (ppustrTempFileURL == nullptr)) + { + osl_error = osl_File_E_INVAL; + } + else + { + osl_error = osl_setup_base_directory_impl_( + pustrDirectoryURL, ppustr_base_dir); + + *b_delete_on_close = (ppustrTempFileURL == nullptr); + } + + return osl_error; +} + +static oslFileError osl_win32_GetTempFileName_impl_( + rtl_uString* base_directory, LPWSTR temp_file_name) +{ + oslFileError osl_error = osl_File_E_None; + + if (GetTempFileNameW( + o3tl::toW(rtl_uString_getStr(base_directory)), + L"", + 0, + temp_file_name) == 0) + { + osl_error = oslTranslateFileError(GetLastError()); + } + + return osl_error; +} + +static bool osl_win32_CreateFile_impl_( + LPCWSTR file_name, bool b_delete_on_close, oslFileHandle* p_handle) +{ + DWORD flags = FILE_ATTRIBUTE_NORMAL; + HANDLE hFile; + + assert(p_handle); + + if (b_delete_on_close) + flags |= FILE_FLAG_DELETE_ON_CLOSE; + + hFile = CreateFileW( + file_name, + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + TRUNCATE_EXISTING, + flags, + nullptr); + + // @@@ ERROR HANDLING @@@ + if (IsValidHandle(hFile)) + *p_handle = osl_createFileHandleFromOSHandle(hFile, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write); + + return IsValidHandle(hFile); +} + +static oslFileError osl_createTempFile_impl_( + rtl_uString* base_directory, + LPWSTR tmp_name, + bool b_delete_on_close, + oslFileHandle* pHandle, + rtl_uString** ppustrTempFileURL) +{ + oslFileError osl_error; + + do + { + osl_error = osl_win32_GetTempFileName_impl_(base_directory, tmp_name); + + /* if file could not be opened try again */ + + if ((osl_File_E_None != osl_error) || (nullptr == pHandle) || + osl_win32_CreateFile_impl_(tmp_name, b_delete_on_close, pHandle)) + break; + + } while(true); // try until success + + if ((osl_error == osl_File_E_None) && !b_delete_on_close) + { + rtl_uString* pustr = nullptr; + rtl_uString_newFromStr(&pustr, o3tl::toU(tmp_name)); + osl_getFileURLFromSystemPath(pustr, ppustrTempFileURL); + rtl_uString_release(pustr); + } + + return osl_error; +} + +oslFileError SAL_CALL osl_createTempFile( + rtl_uString* pustrDirectoryURL, + oslFileHandle* pHandle, + rtl_uString** ppustrTempFileURL) +{ + rtl_uString* base_directory = nullptr; + LPWSTR tmp_name; + sal_Bool b_delete_on_close; + oslFileError osl_error; + + osl_error = osl_setup_createTempFile_impl_( + pustrDirectoryURL, + pHandle, + ppustrTempFileURL, + &base_directory, + &b_delete_on_close); + + if (osl_error != osl_File_E_None) + return osl_error; + + /* allocate enough space on the stack, the file name can not be longer than MAX_PATH */ + STACK_ALLOC(tmp_name, WCHAR, (rtl_uString_getLength(base_directory) + MAX_PATH)); + + if (tmp_name) + { + osl_error = osl_createTempFile_impl_( + base_directory, + tmp_name, + b_delete_on_close, + pHandle, + ppustrTempFileURL); + } + else // stack alloc failed + { + osl_error = osl_File_E_NOMEM; + } + + if (base_directory) + rtl_uString_release(base_directory); + + return osl_error; +} + +oslFileError SAL_CALL osl_getTempDirURL(rtl_uString** pustrTempDir) +{ + ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH ); + LPWSTR lpBuffer = o3tl::toW(aBuffer); + DWORD nBufferLength = aBuffer.getBufSizeInSymbols() - 1; + + DWORD nLength; + oslFileError error; + + nLength = GetTempPathW( aBuffer.getBufSizeInSymbols(), lpBuffer ); + + if ( nLength > nBufferLength ) + { + // the provided path has invalid length + error = osl_File_E_NOENT; + } + else if ( nLength ) + { + if ( '\\' == lpBuffer[nLength-1] ) + --nLength; + + const OUString ustrTempPath(o3tl::toU(lpBuffer), static_cast<sal_Int32>(nLength)); + + error = osl_getFileURLFromSystemPath(ustrTempPath.pData, pustrTempDir); + } + else + error = oslTranslateFileError( GetLastError() ); + + return error; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/thread.cxx b/sal/osl/w32/thread.cxx new file mode 100644 index 000000000..fe5ac30b0 --- /dev/null +++ b/sal/osl/w32/thread.cxx @@ -0,0 +1,540 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "system.h" +#include "thread.hxx" + +#include <comphelper/windowserrorstring.hxx> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <osl/thread.h> +#include <rtl/alloc.h> +#include <osl/time.h> +#include <osl/interlck.h> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <systools/win32/comtools.hxx> + +#include <errno.h> + +namespace { + +/** + Thread-data structure hidden behind oslThread: + */ +typedef struct +{ + HANDLE m_hThread; /* OS-handle used for all thread-functions */ + DWORD m_ThreadId; /* identifier for this thread */ + sal_Int32 m_nTerminationRequested; + oslWorkerFunction m_WorkerFunction; + void* m_pData; + +} osl_TThreadImpl; + +} + +static oslThread oslCreateThread(oslWorkerFunction pWorker, void* pThreadData, sal_uInt32 nFlags); + +static DWORD WINAPI oslWorkerWrapperFunction(_In_ LPVOID pData) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(pData); + + /* Initialize COM - Multi Threaded Apartment (MTA) for all threads */ + sal::systools::CoInitializeGuard aGuard(COINIT_MULTITHREADED, false, + sal::systools::CoInitializeGuard::WhenFailed::NoThrow); + + /* call worker-function with data */ + + pThreadImpl->m_WorkerFunction(pThreadImpl->m_pData); + + return 0; +} + +static oslThread oslCreateThread(oslWorkerFunction pWorker, + void* pThreadData, + sal_uInt32 nFlags) +{ + osl_TThreadImpl* pThreadImpl; + + /* alloc mem. for our internal data structure */ + pThreadImpl= static_cast<osl_TThreadImpl *>(malloc(sizeof(osl_TThreadImpl))); + + OSL_ASSERT(pThreadImpl); + + if ( pThreadImpl == nullptr ) + { + return nullptr; + } + + pThreadImpl->m_WorkerFunction= pWorker; + pThreadImpl->m_pData= pThreadData; + pThreadImpl->m_nTerminationRequested= 0; + + pThreadImpl->m_hThread= CreateThread( + nullptr, /* no security */ + 0, /* default stack-size */ + oslWorkerWrapperFunction, /* worker-function */ + pThreadImpl, /* provide worker-function with data */ + nFlags, /* start thread immediately or suspended */ + &pThreadImpl->m_ThreadId); + + if(pThreadImpl->m_hThread == nullptr) + { + SAL_WARN("sal.osl", "CreateThread failed:" << WindowsErrorString(GetLastError())); + + /* create failed */ + free(pThreadImpl); + return nullptr; + } + + return pThreadImpl; +} + +oslThread SAL_CALL osl_createThread(oslWorkerFunction pWorker, + void* pThreadData) +{ + return oslCreateThread(pWorker, pThreadData, 0); +} + +oslThread SAL_CALL osl_createSuspendedThread(oslWorkerFunction pWorker, + void* pThreadData) +{ + return oslCreateThread(pWorker, pThreadData, CREATE_SUSPENDED); +} + +oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + if (pThreadImpl != nullptr) + return static_cast<oslThreadIdentifier>(pThreadImpl->m_ThreadId); + else + return static_cast<oslThreadIdentifier>(GetCurrentThreadId()); +} + +void SAL_CALL osl_destroyThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + if (Thread == nullptr) /* valid ptr? */ + { + /* thread already destroyed or not created */ + return; + } + + /* !!!! _exitthreadex does _not_ call CloseHandle !!! */ + CloseHandle( pThreadImpl->m_hThread ); + + /* free memory */ + free(Thread); +} + +void SAL_CALL osl_resumeThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + OSL_ASSERT(pThreadImpl); /* valid ptr? */ + + ResumeThread(pThreadImpl->m_hThread); +} + +void SAL_CALL osl_suspendThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + OSL_ASSERT(pThreadImpl); /* valid ptr? */ + + SuspendThread(pThreadImpl->m_hThread); +} + +void SAL_CALL osl_setThreadPriority(oslThread Thread, + oslThreadPriority Priority) +{ + int winPriority; + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + OSL_ASSERT(pThreadImpl); /* valid ptr? */ + + /* map enum to WIN32 levels + it would be faster and more elegant to preset + the enums, but that would require an #ifdef in + the exported header, which is not desired. + */ + switch(Priority) { + + case osl_Thread_PriorityHighest: + winPriority= THREAD_PRIORITY_HIGHEST; + break; + + case osl_Thread_PriorityAboveNormal: + winPriority= THREAD_PRIORITY_ABOVE_NORMAL; + break; + + case osl_Thread_PriorityNormal: + winPriority= THREAD_PRIORITY_NORMAL; + break; + + case osl_Thread_PriorityBelowNormal: + winPriority= THREAD_PRIORITY_BELOW_NORMAL; + break; + + case osl_Thread_PriorityLowest: + winPriority= THREAD_PRIORITY_LOWEST; + break; + + case osl_Thread_PriorityUnknown: + OSL_ASSERT(FALSE); /* only fools try this...*/ + + /* let release-version behave friendly */ + return; + + default: + OSL_ASSERT(FALSE); /* enum expanded, but forgotten here...*/ + + /* let release-version behave friendly */ + return; + } + + SetThreadPriority(pThreadImpl->m_hThread, winPriority); +} + +oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread) +{ + int winPriority; + oslThreadPriority Priority; + + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + /* invalid arguments ?*/ + if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr) + { + return osl_Thread_PriorityUnknown; + } + + winPriority= + GetThreadPriority(pThreadImpl->m_hThread); + + if(winPriority == THREAD_PRIORITY_ERROR_RETURN) + { + return osl_Thread_PriorityUnknown; + } + + /* map WIN32 priority to enum */ + switch(winPriority) + { + case THREAD_PRIORITY_TIME_CRITICAL: + case THREAD_PRIORITY_HIGHEST: + Priority= osl_Thread_PriorityHighest; + break; + + case THREAD_PRIORITY_ABOVE_NORMAL: + Priority= osl_Thread_PriorityAboveNormal; + break; + + case THREAD_PRIORITY_NORMAL: + Priority= osl_Thread_PriorityNormal; + break; + + case THREAD_PRIORITY_BELOW_NORMAL: + Priority= osl_Thread_PriorityBelowNormal; + break; + + case THREAD_PRIORITY_IDLE: + case THREAD_PRIORITY_LOWEST: + Priority= osl_Thread_PriorityLowest; + break; + + default: + OSL_ASSERT(FALSE); /* WIN32 API changed, incorporate new prio-level! */ + + /* release-version behaves friendly */ + Priority= osl_Thread_PriorityUnknown; + } + + return Priority; +} + +sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + /* invalid arguments ?*/ + if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr) + { + return false; + } + + return WaitForSingleObject(pThreadImpl->m_hThread, 0) != WAIT_OBJECT_0; +} + +void SAL_CALL osl_joinWithThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + /* invalid arguments?*/ + if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr) + { + /* assume thread is not running */ + return; + } + + WaitForSingleObject(pThreadImpl->m_hThread, INFINITE); +} + +void SAL_CALL osl_waitThread(const TimeValue* pDelay) +{ + if (pDelay) + { + DWORD millisecs = pDelay->Seconds * 1000L + pDelay->Nanosec / 1000000L; + + Sleep(millisecs); + } +} + +void SAL_CALL osl_terminateThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + /* invalid arguments?*/ + if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr) + { + /* assume thread is not running */ + return; + } + + osl_atomic_increment(&(pThreadImpl->m_nTerminationRequested)); +} + +sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread) +{ + osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread); + + osl_yieldThread(); + + /* invalid arguments?*/ + if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr) + { + /* assume thread is not running */ + return false; + } + + return 0 == pThreadImpl->m_nTerminationRequested; +} + +void SAL_CALL osl_yieldThread(void) +{ + Sleep(0); +} + +static void impSetThreadDescription(char const * name) { + // SetThreadDescription is only available since Windows 10 version 1607 + typedef HRESULT(WINAPI * TSetThreadDescription)(HANDLE, PCWSTR); + static const auto pSetThreadDescription = reinterpret_cast<TSetThreadDescription>( + GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription")); + if (pSetThreadDescription) + { + if (const int nReqCCh = MultiByteToWideChar(CP_ACP, 0, name, -1, nullptr, 0)) + { + if (PWSTR wStr = static_cast<PWSTR>(malloc(nReqCCh * sizeof(WCHAR)))) + { + if (MultiByteToWideChar(CP_ACP, 0, name, -1, wStr, nReqCCh)) + pSetThreadDescription(GetCurrentThread(), wStr); + free(wStr); + } + } + } +} + +void SAL_CALL osl_setThreadName(char const * name) { + /* See < https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code >: */ +#pragma pack(push, 8) + struct { + DWORD dwType = 0x1000; + LPCSTR szName; + DWORD dwThreadID = DWORD(-1); + DWORD dwFlags = 0; + } info; +#pragma pack(pop) + info.szName = name; + __try { + RaiseException( + 0x406D1388, 0, sizeof info / sizeof (ULONG_PTR), + reinterpret_cast<ULONG_PTR *>(&info)); + } __except (EXCEPTION_EXECUTE_HANDLER) {} + + impSetThreadDescription(name); +} + +namespace { + +typedef struct TLS_ +{ + DWORD dwIndex; + oslThreadKeyCallbackFunction pfnCallback; + struct TLS_ *pNext, *pPrev; +} TLS, *PTLS; + +PTLS g_pThreadKeyList = nullptr; +osl::Mutex& getThreadKeyListMutex() +{ + static osl::Mutex g_ThreadKeyListMutex; + return g_ThreadKeyListMutex; +} + +} + +static void AddKeyToList( PTLS pTls ) +{ + if ( pTls ) + { + osl::MutexGuard aGuard(getThreadKeyListMutex()); + + pTls->pNext = g_pThreadKeyList; + pTls->pPrev = nullptr; + + if ( g_pThreadKeyList ) + g_pThreadKeyList->pPrev = pTls; + + g_pThreadKeyList = pTls; + } +} + +static void RemoveKeyFromList( PTLS pTls ) +{ + if ( pTls ) + { + osl::MutexGuard aGuard(getThreadKeyListMutex()); + if ( pTls->pPrev ) + pTls->pPrev->pNext = pTls->pNext; + else + { + OSL_ASSERT( pTls == g_pThreadKeyList ); + g_pThreadKeyList = pTls->pNext; + } + + if ( pTls->pNext ) + pTls->pNext->pPrev = pTls->pPrev; + } +} + +void osl_callThreadKeyCallbackOnThreadDetach(void) +{ + PTLS pTls; + + osl::MutexGuard aGuard(getThreadKeyListMutex()); + pTls = g_pThreadKeyList; + while ( pTls ) + { + if ( pTls->pfnCallback ) + { + void *pValue = TlsGetValue( pTls->dwIndex ); + + if ( pValue ) + pTls->pfnCallback( pValue ); + } + + pTls = pTls->pNext; + } +} + +oslThreadKey SAL_CALL osl_createThreadKey(oslThreadKeyCallbackFunction pCallback) +{ + PTLS pTls = static_cast<PTLS>(malloc( sizeof(TLS) )); + + if ( pTls ) + { + pTls->pfnCallback = pCallback; + if ( DWORD(-1) == (pTls->dwIndex = TlsAlloc()) ) + { + free( pTls ); + pTls = nullptr; + } + else + AddKeyToList( pTls ); + } + + return pTls; +} + +void SAL_CALL osl_destroyThreadKey(oslThreadKey Key) +{ + if (Key != nullptr) + { + PTLS pTls = static_cast<PTLS>(Key); + + RemoveKeyFromList( pTls ); + TlsFree( pTls->dwIndex ); + free( pTls ); + } +} + +void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key) +{ + if (Key != nullptr) + { + PTLS pTls = static_cast<PTLS>(Key); + + return TlsGetValue( pTls->dwIndex ); + } + + return nullptr; +} + +sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData) +{ + if (Key != nullptr) + { + PTLS pTls = static_cast<PTLS>(Key); + void* pOldData = nullptr; + bool fSuccess; + + if ( pTls->pfnCallback ) + pOldData = TlsGetValue( pTls->dwIndex ); + + fSuccess = TlsSetValue( pTls->dwIndex, pData ); + + if ( fSuccess && pTls->pfnCallback && pOldData ) + pTls->pfnCallback( pOldData ); + + return fSuccess; + } + + return false; +} + +namespace +{ +rtl_TextEncoding& getThreadTextEncodingImpl() +{ + static thread_local rtl_TextEncoding s_enc = rtl_getTextEncodingFromWindowsCodePage(GetACP()); + return s_enc; +} +} + +rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding(void) { return getThreadTextEncodingImpl(); } + +rtl_TextEncoding SAL_CALL osl_setThreadTextEncoding( rtl_TextEncoding Encoding ) +{ + rtl_TextEncoding oldEncoding = getThreadTextEncodingImpl(); + getThreadTextEncodingImpl() = Encoding; + return oldEncoding; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/thread.hxx b/sal/osl/w32/thread.hxx new file mode 100644 index 000000000..8002d8b7f --- /dev/null +++ b/sal/osl/w32/thread.hxx @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/config.h> + +#include <sal/types.h> + +void osl_callThreadKeyCallbackOnThreadDetach(void); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sal/osl/w32/time.cxx b/sal/osl/w32/time.cxx new file mode 100644 index 000000000..3f11746fe --- /dev/null +++ b/sal/osl/w32/time.cxx @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include "system.h" + +#include "filetime.hxx" +#include "time.hxx" + +#include <osl/diagnose.h> +#include <osl/time.h> +#include <sys/timeb.h> + +sal_Bool SAL_CALL osl_getSystemTime(TimeValue* pTimeVal) +{ + unsigned __int64 CurTime; + + typedef VOID (WINAPI *GetSystemTimePreciseAsFileTime_PROC)(LPFILETIME); + + OSL_ASSERT(pTimeVal != nullptr); + + static GetSystemTimePreciseAsFileTime_PROC pGetSystemTimePreciseAsFileTime = []() + { + HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" ); + return reinterpret_cast<GetSystemTimePreciseAsFileTime_PROC>( + GetProcAddress(hModule, "GetSystemTimePreciseAsFileTime")); + }(); + + // use ~1 microsecond resolution if available + if (pGetSystemTimePreciseAsFileTime) + pGetSystemTimePreciseAsFileTime(reinterpret_cast<LPFILETIME>(&CurTime)); + else + { + SYSTEMTIME SystemTime; + GetSystemTime(&SystemTime); + SystemTimeToFileTime(&SystemTime, reinterpret_cast<LPFILETIME>(&CurTime)); + } + + static const unsigned __int64 OffTime = [] { + SYSTEMTIME SystemTime; + SystemTime.wYear = 1970; + SystemTime.wMonth = 1; + SystemTime.wDayOfWeek = 0; + SystemTime.wDay = 1; + SystemTime.wHour = 0; + SystemTime.wMinute = 0; + SystemTime.wSecond = 0; + SystemTime.wMilliseconds = 0; + + unsigned __int64 ft; + SystemTimeToFileTime(&SystemTime, reinterpret_cast<LPFILETIME>(&ft)); + return ft; + }(); + + const unsigned __int64 Value = CurTime - OffTime; + + pTimeVal->Seconds = static_cast<unsigned long>(Value / 10000000L); + pTimeVal->Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100); + + return true; +} + +sal_Bool SAL_CALL osl_getDateTimeFromTimeValue( const TimeValue* pTimeVal, oslDateTime* pDateTime ) +{ + FILETIME aFileTime; + SYSTEMTIME aSystemTime; + + if ( TimeValueToFileTime(pTimeVal, &aFileTime) ) + { + if ( FileTimeToSystemTime( &aFileTime, &aSystemTime ) ) + { + pDateTime->NanoSeconds = pTimeVal->Nanosec; + + pDateTime->Seconds = aSystemTime.wSecond; + pDateTime->Minutes = aSystemTime.wMinute; + pDateTime->Hours = aSystemTime.wHour; + pDateTime->Day = aSystemTime.wDay; + pDateTime->DayOfWeek = aSystemTime.wDayOfWeek; + pDateTime->Month = aSystemTime.wMonth; + pDateTime->Year = aSystemTime.wYear; + + return true; + } + } + + return false; +} + +sal_Bool SAL_CALL osl_getTimeValueFromDateTime( const oslDateTime* pDateTime, TimeValue* pTimeVal ) +{ + FILETIME aFileTime; + SYSTEMTIME aSystemTime; + + aSystemTime.wMilliseconds = 0; + aSystemTime.wSecond = pDateTime->Seconds; + aSystemTime.wMinute = pDateTime->Minutes; + aSystemTime.wHour = pDateTime->Hours; + aSystemTime.wDay = pDateTime->Day; + aSystemTime.wDayOfWeek = pDateTime->DayOfWeek; + aSystemTime.wMonth = pDateTime->Month; + aSystemTime.wYear = pDateTime->Year; + + if ( SystemTimeToFileTime( &aSystemTime, &aFileTime ) ) + { + if (FileTimeToTimeValue( &aFileTime, pTimeVal ) ) + { + pTimeVal->Nanosec = pDateTime->NanoSeconds; + return true; + } + } + + return false; +} + +sal_Bool SAL_CALL osl_getLocalTimeFromSystemTime( const TimeValue* pSystemTimeVal, TimeValue* pLocalTimeVal ) +{ + TIME_ZONE_INFORMATION aTimeZoneInformation; + + // get timezone information + DWORD Success = GetTimeZoneInformation( &aTimeZoneInformation ); + if (Success == TIME_ZONE_ID_INVALID) + return false; + + sal_Int64 bias = aTimeZoneInformation.Bias; + + // add bias for daylight saving time + if ( Success == TIME_ZONE_ID_DAYLIGHT ) + bias+=aTimeZoneInformation.DaylightBias; + + if ( static_cast<sal_Int64>(pSystemTimeVal->Seconds) > ( bias * 60 ) ) + { + pLocalTimeVal->Seconds = static_cast<sal_uInt32>(pSystemTimeVal->Seconds - ( bias * 60) ); + pLocalTimeVal->Nanosec = pSystemTimeVal->Nanosec; + + return true; + } + return false; +} + +sal_Bool SAL_CALL osl_getSystemTimeFromLocalTime( const TimeValue* pLocalTimeVal, TimeValue* pSystemTimeVal ) +{ + TIME_ZONE_INFORMATION aTimeZoneInformation; + + // get timezone information + DWORD Success = GetTimeZoneInformation( &aTimeZoneInformation ); + if ( Success == TIME_ZONE_ID_INVALID ) + return false; + + sal_Int64 bias = aTimeZoneInformation.Bias; + + // add bias for daylight saving time + if ( Success == TIME_ZONE_ID_DAYLIGHT ) + bias+=aTimeZoneInformation.DaylightBias; + + if ( static_cast<sal_Int64>(pLocalTimeVal->Seconds) + ( bias * 60 ) > 0 ) + { + pSystemTimeVal->Seconds = static_cast<sal_uInt32>( pLocalTimeVal->Seconds + ( bias * 60) ); + pSystemTimeVal->Nanosec = pLocalTimeVal->Nanosec; + + return true; + } + + return false; +} + +static struct _timeb startTime; +void sal_initGlobalTimer() +{ + _ftime( &startTime ); +} + +sal_uInt32 SAL_CALL osl_getGlobalTimer(void) +{ + struct _timeb currentTime; + sal_uInt32 nSeconds; + + _ftime( ¤tTime ); + + nSeconds = static_cast<sal_uInt32>( currentTime.time - startTime.time ); + + return ( nSeconds * 1000 ) + static_cast<long>( currentTime.millitm - startTime.millitm ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/time.hxx b/sal/osl/w32/time.hxx new file mode 100644 index 000000000..c0ca9d777 --- /dev/null +++ b/sal/osl/w32/time.hxx @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SAL_OSL_W32_TIME_HXX +#define INCLUDED_SAL_OSL_W32_TIME_HXX + +#include <sal/config.h> + +void sal_initGlobalTimer(void); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |