pax_global_header00006660000000000000000000000064147743162370014530gustar00rootroot0000000000000052 comment=8a45170e087f7d800b15ae20e682e34374bf2e05 far2l-2.6.5~beta+ds/000077500000000000000000000000001477431623700142045ustar00rootroot00000000000000far2l-2.6.5~beta+ds/.cirrus.yml000066400000000000000000000011701477431623700163130ustar00rootroot00000000000000freebsd_test_task: freebsd_instance: image_family: freebsd-14-2 name: cirrus/FreeBSD install_script: pkg install -y cmake git pkgconf bash wx32-gtk3 libssh openssl libnfs neon libarchive samba416 uchardet libxml2 aws-sdk-cpp script: mkdir build && cd build && cmake .. && make -j $(getconf _NPROCESSORS_ONLN) && ./install/far2l --help macos_test_task: macos_instance: image: ghcr.io/cirruslabs/macos-ventura-xcode:14.2 name: cirrus/MacOS_M1 install_script: brew update && brew bundle -v script: mkdir build && cd build && cmake -G Ninja .. && ninja -j$(getconf _NPROCESSORS_ONLN) -v far2l-2.6.5~beta+ds/.clang-format000066400000000000000000000054511477431623700165640ustar00rootroot00000000000000 ColumnLimit: 110 IndentWidth: 4 TabWidth: 4 Language: Cpp Standard: Latest SortIncludes: Never AccessModifierOffset: -4 AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: Enabled: false AlignConsecutiveDeclarations: Enabled: false AlignConsecutiveBitFields: Enabled: true AcrossEmptyLines: true AcrossComments: true AlignConsecutiveMacros: Enabled: true AcrossEmptyLines: true AcrossComments: true AllowAllArgumentsOnNextLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortLambdasOnASingleLine: Empty AlwaysBreakTemplateDeclarations: Yes AlignTrailingComments: true BinPackArguments: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 8 IndentCaseLabels: true BreakBeforeBraces: Custom BraceWrapping: AfterClass: true AfterControlStatement: Never AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false FixNamespaceComments: true KeepEmptyLinesAtTheStartOfBlocks: true #PenaltyBreakAssignment: 10000 PenaltyBreakBeforeFirstCallParameter: 10000 #PenaltyBreakComment: 1000 PenaltyBreakFirstLessLess: 16 #PenaltyBreakOpenParenthesis: 1000 #PenaltyBreakString: 1000 #PenaltyBreakTemplateDeclaration: 1000 #PenaltyExcessCharacter: 1000 PenaltyIndentedWhitespace: 16 PenaltyReturnTypeOnItsOwnLine: 96 PointerAlignment: Right ReferenceAlignment: Pointer SpaceAfterCStyleCast: false SpacesInCStyleCastParentheses: false SpaceAfterLogicalNot: false SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false # needs a separate setting for namespace SpacesBeforeTrailingComments: 4 SpacesInAngles: false SpacesInParentheses: false SpacesInSquareBrackets: false #OperandAlignmentStyle: DontAlign AlignOperands: DontAlign #BinPackParameters: true BreakBeforeBinaryOperators: NonAssignment PackConstructorInitializers: NextLine AlignArrayOfStructures: Left ############ Options added for far2l, see https://github.com/elfmz/llvm-project UseTab: ForContinuationAndIndentationAndComments AlignByTab: true BreakConstructorInitializers: AfterColonSeparated SpaceBeforeAssignmentOperators: OnlyTrivial AlignConsecutiveMembersAssignments: true AllowLongTrailingComments: true far2l-2.6.5~beta+ds/.clang-format.json000066400000000000000000000034541477431623700175350ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "far2l-clang-format", "title": "ClangFormat Options for far2l", "allOf": [ { "$ref": "http://jetbrains.com/schema/clangformat.json" } ], "properties": { "UseTab": { "title": "The way to use tab characters in the resulting file.", "x-intellij-html-description": "Modified to match https://github.com/elfmz/llvm-project", "enum": [ "Never", "ForIndentation", "ForContinuationAndIndentation", "AlignWithSpaces", "Always", "ForContinuationAndIndentationAndComments" ], "type": "string" }, "AlignByTab": { "title": "Align by tab.", "x-intellij-html-description": "Added to match https://github.com/elfmz/llvm-project", "type": "boolean" }, "BreakConstructorInitializers": { "title": "Break constructor initializers.", "x-intellij-html-description": "Modified to match https://github.com/elfmz/llvm-project", "enum": [ "AfterColonSeparated" ], "type": "string" }, "SpaceBeforeAssignmentOperators": { "title": "If `false`, spaces will be removed before assignment operators.", "x-intellij-html-description": "Modified to match https://github.com/elfmz/llvm-project", "enum": [ "OnlyTrivial" ], "type": "string" }, "AlignConsecutiveMembersAssignments": { "title": "Align consecutive members assignments.", "x-intellij-html-description": "Added to match https://github.com/elfmz/llvm-project", "type": "boolean" }, "AllowLongTrailingComments": { "title": "Allow long trailing comments.", "x-intellij-html-description": "Added to match https://github.com/elfmz/llvm-project", "type": "boolean" } } }far2l-2.6.5~beta+ds/.editorconfig000066400000000000000000000002521477431623700166600ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_style = tab indent_size = 4 [*.yml] indent_style = space indent_size = 2 [CMakeLists.txt] indent_style = space far2l-2.6.5~beta+ds/.github/000077500000000000000000000000001477431623700155445ustar00rootroot00000000000000far2l-2.6.5~beta+ds/.github/workflows/000077500000000000000000000000001477431623700176015ustar00rootroot00000000000000far2l-2.6.5~beta+ds/.github/workflows/artifacts.yml000066400000000000000000000036101477431623700223040ustar00rootroot00000000000000name: Artifacts on: push: branches: - master env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: if: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository != 'elfmz/far2l') }} # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. runs-on: ubuntu-latest strategy: matrix: include: - dependencies: "-minimal" - dependencies: "" options: "-DPYTHON=yes" steps: - uses: actions/checkout@v4 - name: Dependencies run: sudo apt-get update ; sudo apt-get -y install $(cat dependencies${{ matrix.dependencies }}.txt) - name: Create Build Environment # Create a separate build directory as working directory for all subsequent commands run: mkdir -p _build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash # -S and -B options specify source and build directories run: cmake -S . -B _build -Wno-dev -DCMAKE_BUILD_TYPE=$BUILD_TYPE ${{ matrix.options }} - name: Build shell: bash # Execute the build. You can specify a specific target with "--target " run: | cmake --build _build --config $BUILD_TYPE - name: File Tree shell: bash run: tree _build - name: Packing Artifact uses: actions/upload-artifact@v4 with: name: far2l-release${{ matrix.dependencies }} path: _build/install - name: Test working-directory: _build shell: bash # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C $BUILD_TYPE far2l-2.6.5~beta+ds/.github/workflows/build.yml000066400000000000000000000143231477431623700214260ustar00rootroot00000000000000name: check build on: # allow manually running workflow, useful for testing branches workflow_dispatch: # run on push in master push: branches: - master paths-ignore: - '.vscode/**' - '.cirrus.yml' - '.clang-format' - '.clang-format.json' - '.editorconfig' - '.gitignore' - '.travis.yml' - 'Brewfile' - 'CODESTYLE.md' - 'dependencies-minimal.txt' - 'dependencies.txt' - 'Dockerfile' - 'HACKING.md' - 'LICENSE.txt' - 'README.md' # run on pull request to master pull_request: branches: - master env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: ubuntu-low-compiler: if: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository != 'elfmz/far2l') }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: compiler: [ gcc7, clang7 ] name: ubuntu-${{ matrix.compiler }} steps: - name: Checkout source uses: actions/checkout@v4 - name: 'Build in docker' run: | docker build -f _ci/Dockerfile.${{ matrix.compiler }} -t tempx:latest . ubuntu-medium-compiler: if: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository != 'elfmz/far2l') }} runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: compiler: [ gcc, clang ] include: - compiler: gcc version: 12 c: /usr/bin/gcc-12 cxx: /usr/bin/g++-12 - compiler: clang version: 15 c: /usr/bin/clang-15 cxx: /usr/bin/clang++-15 name: ubuntu-${{ matrix.compiler }}-${{ matrix.version }} steps: - name: Checkout source uses: actions/checkout@v4 - name: Install far2l dependencies run: > sudo apt-get update; sudo apt-get -y install libuchardet-dev libxml2-dev libwxgtk3.0-gtk3-dev libx11-dev libxi-dev libssl-dev libsmbclient-dev libnfs-dev libneon27-dev libssh-dev libarchive-dev python3-dev python3-cffi - name: Create Build Environment # Create a separate build directory as working directory for all subsequent commands run: mkdir -p _build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash # -S and -B options specify source and build directories run: > cmake -S . -B _build -Wno-dev -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPYTHON=yes -DCMAKE_C_COMPILER=${{ matrix.c }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} - name: Build shell: bash # Execute the build. You can specify a specific target with "--target " run: | cmake --build _build --config $BUILD_TYPE -j$(nproc --all) ubuntu-high-compiler: if: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository != 'elfmz/far2l') }} runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: compiler: [ gcc, clang ] include: - compiler: gcc version: 14 c: /usr/bin/gcc-14 cxx: /usr/bin/g++-14 - compiler: clang version: 18 c: /usr/bin/clang-18 cxx: /usr/bin/clang++-18 name: ubuntu-${{ matrix.compiler }}-${{ matrix.version }} steps: - name: Checkout source uses: actions/checkout@v4 - name: Install far2l dependencies run: > sudo apt-get update; sudo apt-get -y install libuchardet-dev libxml2-dev libwxgtk3.2-dev libx11-dev libxi-dev libssl-dev libsmbclient-dev libnfs-dev libneon27-dev libssh-dev libarchive-dev python3-dev python3-cffi python3-markdown appstream - name: Create Build Environment # Create a separate build directory as working directory for all subsequent commands run: mkdir -p _build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash # -S and -B options specify source and build directories run: > cmake -S . -B _build -Wno-dev -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPYTHON=yes -DCMAKE_C_COMPILER=${{ matrix.c }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} - name: Build shell: bash # Execute the build. You can specify a specific target with "--target " run: | cmake --build _build --config $BUILD_TYPE -j$(nproc --all) - name: Validate AppStream metainfo file run: appstreamcli validate --strict _build/far2l/DE/io.github.elfmz.far2l.metainfo.xml macos14-arm64-clang15: if: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository != 'elfmz/far2l') }} runs-on: macos-14 steps: - name: Checkout source uses: actions/checkout@v4 - name: Install far2l dependencies # libx11, openssl is already installed run: > brew install uchardet libxml2 wxwidgets libxi samba libnfs neon libssh libarchive - name: Relink 'keg-only' packages run: brew link libarchive --force - name: Create Build Environment # Create a separate build directory as working directory for all subsequent commands run: mkdir -p _build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash # -S and -B options specify source and build directories run: cmake -S . -B _build -Wno-dev -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPYTHON=yes - name: Build shell: bash # Execute the build. You can specify a specific target with "--target " run: | cmake --build _build --config $BUILD_TYPE -j$(sysctl -n hw.logicalcpu) far2l-2.6.5~beta+ds/.github/workflows/clean_workflow.yml000066400000000000000000000010731477431623700233410ustar00rootroot00000000000000name: Delete old workflow runs on: workflow_dispatch: # allow manually running workflow, schedule: - cron: '0 0 1 * *' # Run monthly, at 00:00 on the 1st day of month. jobs: del_runs: runs-on: ubuntu-latest permissions: actions: write contents: read steps: - name: Delete workflow runs uses: Mattraks/delete-workflow-runs@v2 with: token: ${{ github.token }} repository: ${{ github.repository }} retain_days: 30 keep_minimum_runs: 6 check_pullrequest_exist: true far2l-2.6.5~beta+ds/.gitignore000066400000000000000000000006401477431623700161740ustar00rootroot00000000000000CMakeCache.txt CMakeFiles CMakeScripts CMakeLists.txt.user Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake install/ tools/ *.a far2l/bootstrap python/staging python/python python/incpy build/ .idea CPackConfig.cmake CPackSourceConfig.cmake *.cbp cmake-build-* .DS_Store _build/ packaging/osx/FixupBundle.cmake packaging/osx/Setup.scpt far2l_askpass far2l_sudoapp far2l-2.6.5~beta+ds/.travis.yml000066400000000000000000000100461477431623700163160ustar00rootroot00000000000000# Continuous Integration with https://travis-ci.org/ language: cpp matrix: include: - os: osx osx_image: xcode12 before_install: - git clone https://github.com/microsoft/vcpkg.git - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install aws-sdk-cpp - os: linux dist: xenial sudo: required compiler: gcc addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-5 - cmake - libwxgtk3.0-dev - libxml2-dev - libuchardet-dev - libssh-dev - libsmbclient-dev - libnfs-dev - libneon27-dev - libarchive-dev - ninja-build env: CXX=g++-5 before_install: - git clone https://github.com/microsoft/vcpkg.git - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install aws-sdk-cpp install: - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi - os: linux dist: xenial sudo: required compiler: gcc addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-6 - cmake - libwxgtk3.0-dev - libxml2-dev - libuchardet-dev - libssh-dev - libsmbclient-dev - libnfs-dev - libneon27-dev - libarchive-dev - ninja-build env: CXX=g++-6 before_install: - git clone https://github.com/microsoft/vcpkg.git - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install aws-sdk-cpp install: - if [ "$CXX" = "g++" ]; then export CXX="g++-6" CC="gcc-6"; fi - os: linux dist: xenial sudo: required compiler: clang addons: apt: sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.6 packages: - clang-3.6 - cmake - libwxgtk3.0-dev - libxml2-dev - libuchardet-dev - libssh-dev - libsmbclient-dev - libnfs-dev - libneon27-dev - libarchive-dev - ninja-build env: CXX=clang++-3.6 before_install: - git clone https://github.com/microsoft/vcpkg.git - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install aws-sdk-cpp install: - if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.6" CC="clang-3.6"; fi - os: linux dist: xenial sudo: required compiler: clang addons: apt: sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.7 packages: - clang-3.7 - cmake - libwxgtk3.0-dev - libxml2-dev - libuchardet-dev - libssh-dev - libsmbclient-dev - libnfs-dev - libneon27-dev - libarchive-dev - zlib1g-dev - ninja-build env: CXX=clang++-3.7 before_install: - git clone https://github.com/microsoft/vcpkg.git - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install aws-sdk-cpp install: - if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.7" CC="clang-3.7"; fi before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2 ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ( brew update || brew update || brew update ) ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew pin tmate ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew bundle -v ; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:saiarcot895/chromium-beta ; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo sudo apt-get update ; fi script: - mkdir build - cd build - cmake -G Ninja .. -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake - ninja -v - cd install && zip -r ../far2l-${TRAVIS_COMMIT}.zip * && cd - far2l-2.6.5~beta+ds/.vscode/000077500000000000000000000000001477431623700155455ustar00rootroot00000000000000far2l-2.6.5~beta+ds/.vscode/launch.json000066400000000000000000000025161477431623700177160ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "(gdb) Launch", "type": "cppdbg", "request": "launch", // Resolved by CMake Tools: "program": "${command:cmake.launchTargetPath}", "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [ { // add the directory where our target was built to the PATHs // it gets resolved by CMake Tools: "name": "PATH", "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}" }, { "name": "OTHER_VALUE", "value": "Something something" } ], "externalConsole": true, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }far2l-2.6.5~beta+ds/.vscode/settings.json000066400000000000000000000002711477431623700203000ustar00rootroot00000000000000{ "cmake.buildDirectory": "${workspaceFolder}/_build", "cmake.configureOnOpen": true, "cmake.cmakePath": "cmake", "cmake.configureArgs": [ "-DUSEWX=yes" ] } far2l-2.6.5~beta+ds/Brewfile000066400000000000000000000003231477431623700156640ustar00rootroot00000000000000brew "cmake" brew "libarchive" brew "libnfs" brew "libssh" brew "neon" brew "ninja" brew "openssl" brew "pkg-config" brew "uchardet" brew "wget" brew "wxwidgets" brew "libxml2" brew "libxml2" brew "aws-sdk-cpp" far2l-2.6.5~beta+ds/CMakeLists.txt000066400000000000000000000546511477431623700167570ustar00rootroot00000000000000set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) cmake_minimum_required (VERSION 3.0.2) project (far2l) set(APP_NAME "far2l") set(APP_IDENTIFIER "com.far2l") set(CPACK_PACKAGE_NAME "far2l") string(ASCII 27 Esc) set(ColorNormal "${Esc}[m") set(ColorRed "${Esc}[1;31m") include(GNUInstallDirs) # just to force reconfigure if packaging/version changed configure_file(${CMAKE_SOURCE_DIR}/packaging/version ${CMAKE_BINARY_DIR}/packaging/version @ONLY) # reconfigure in case .git directory changed to update version correctly IF(EXISTS ${CMAKE_SOURCE_DIR}/.git) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/.git) endif() file(READ "${CMAKE_SOURCE_DIR}/packaging/version" VERSION) string(REGEX REPLACE "[\r\n\t ]$" "" VERSION "${VERSION}") string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VERSION}") string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VERSION}") string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.(.*)" "\\1" VERSION_PATCH "${VERSION}") if("${VERSION_MAJOR}" STREQUAL "" OR "${VERSION_MAJOR}" STREQUAL "${VERSION}" OR "${VERSION_MINOR}" STREQUAL "" OR "${VERSION_MINOR}" STREQUAL "${VERSION}" OR "${VERSION_PATCH}" STREQUAL "" OR "${VERSION_PATCH}" STREQUAL "${VERSION}") message(FATAL_ERROR "${ColorRed}packaging/version is corrupted, please write actual X.Y.Z version into it.${ColorNormal}") endif() find_package(Git) if(GIT_FOUND) execute_process(COMMAND "${GIT_EXECUTABLE}" describe --tag WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) endif() if(NOT "${GIT_TAG}" STREQUAL "v_${VERSION}") if(GIT_FOUND) if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) execute_process(COMMAND "${GIT_EXECUTABLE}" show --no-patch --format=%ci HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) string(SUBSTRING ${GIT_DATE} 0 10 GIT_DATE) execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) endif() endif() if(NOT "${GIT_DATE}" STREQUAL "") message(STATUS "git date: ${GIT_DATE}") set(VERSION_PATCH "${VERSION_PATCH}-${GIT_DATE}") endif() if(NOT "${GIT_HASH}" STREQUAL "") message(STATUS "git hash: ${GIT_HASH}") set(VERSION_PATCH "${VERSION_PATCH}-${GIT_HASH}") else() message(WARNING "git information unavailable, assuming its a build from v_${VERSION}") endif() endif() set(VERSION_PATCH "${VERSION_PATCH}-beta") set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() message(STATUS "Build Type: ${CMAKE_BUILD_TYPE} Version: ${VERSION}") if(NOT DEFINED CMAKE_INSTALL_PREFIX) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(CMAKE_INSTALL_PREFIX "/usr/local") elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") set(CMAKE_INSTALL_PREFIX "/usr/local") else() set(CMAKE_INSTALL_PREFIX "/usr") endif() endif() ############################## if (NOT DEFINED TESTING) set(TESTING "NO") endif() if (NOT DEFINED USEWX) set(USEWX "YES") endif() if (NOT DEFINED UNRAR) set(UNRAR "bundled") endif() # EACP disabled by default for now as FAR's GetCodePageInfo rejects all multibyte charsets due to BUGBUG. # Uncomment EACP-stuff here when that will be fixed. See https://github.com/elfmz/far2l/issues/1179 #if (NOT DEFINED EACP) # set(EACP "YES") #endif() if (NOT DEFINED LEGACY) # TODO: default it to NO somewhere in 2022, after all obsolete settings # registry-upgrade-to-ini stuff will be safe to remove set(LEGACY "YES") elseif(FARFTP) set(LEGACY "YES") message(STATUS "Force-enable LEGACY due to enabled FARFTP") endif() #if (NOT EACP) # message(STATUS "${ColorRed}Building without East Asian codepages support due to EACP=${EACP}${ColorNormal}") #endif() # set(RM rm) actually not needed: use $(CMAKE_COMMAND) -E remove if(MUSL) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MUSL__") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__MUSL__") endif() if(TAR_LIMITED_ARGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__TAR_LIMITED_ARGS__") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__TAR_LIMITED_ARGS__") endif() if(${CMAKE_VERSION} VERSION_LESS "3.1.0") message("Please consider to switch to newer CMake") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") endif() if("${ICU_MODE}" STREQUAL "runtime") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRUNTIME_ICU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRUNTIME_ICU") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wno-unused-function -D_FILE_OFFSET_BITS=64") # -fsanitize=address set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c99 -fPIC -Wno-unused-function -D_FILE_OFFSET_BITS=64") # -fsanitize=address set(CMAKE_CXX_FLAGS_RELEASE "-O2") set(CMAKE_C_FLAGS_RELEASE "-O2") if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(APP_DIR ${CMAKE_BINARY_DIR}/install) set(INSTALL_DIR ${APP_DIR}/${APP_NAME}.app/Contents/MacOS) set(EXECUTABLE_NAME ${APP_NAME}) else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") set(INSTALL_DIR ${CMAKE_BINARY_DIR}/install) set(EXECUTABLE_NAME far2l) if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--gc-sections") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,--gc-sections") endif() endif() if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=600") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=600") message(STATUS "Running under cygwin, wow!") endif() if (CMAKE_SYSTEM_NAME MATCHES "Haiku") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -D_DEFAULT_SOURCE -Wno-attributes") endif() set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_C_VISIBILITY_PRESET hidden) if (LEGACY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINPORT_REGISTRY") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPORT_REGISTRY") message(STATUS "Enabling registry support due to enabled LEGACY") else() message(STATUS "LEGACY is not enabled, your old registry settings will be forgotten, if any") endif() if(CMAKE_VERSION VERSION_GREATER "3.3") cmake_policy(SET CMP0063 NEW) cmake_policy(SET CMP0057 NEW) endif() include_directories(utils/include) add_subdirectory (WinPort) add_subdirectory (utils) add_subdirectory (far2l) if (NOT ${USEWX}) message(STATUS "Building without GUI/wxWidgets backend due to USEWX=${USEWX}") else() message(STATUS "Building with GUI/wxWidgets backend") if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") if (DEFINED wxWidgets_CONFIG_EXECUTABLE) message(STATUS "Using pre-defined wx config: ${wxWidgets_CONFIG_EXECUTABLE}") elseif(EXISTS "/usr/local/bin/wxgtk3u-3.2-config") set(wxWidgets_CONFIG_EXECUTABLE "/usr/local/bin/wxgtk3u-3.2-config") set(wxWidgets_wxrc_EXECUTABLE "/usr/local/bin/wxrc-gtk3u-3.2") message(STATUS "Using known location of wx-config 3.2") elseif(EXISTS "/usr/local/bin/wxgtk3u-3.1-config") set(wxWidgets_CONFIG_EXECUTABLE "/usr/local/bin/wxgtk3u-3.1-config") set(wxWidgets_wxrc_EXECUTABLE "/usr/local/bin/wxrc-gtk3u-3.1") message(STATUS "Using known location of wx-config 3.1") elseif (EXISTS "/usr/local/bin/wxgtk3u-3.0-config") set(wxWidgets_CONFIG_EXECUTABLE "/usr/local/bin/wxgtk3u-3.0-config") set(wxWidgets_wxrc_EXECUTABLE "/usr/local/bin/wxrc-gtk3u-3.0") message(STATUS "Using known location of wx-config 3.0") else() message(STATUS "No wxgtk3u-3.*-config in expected locations") endif() elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if (EXISTS "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.2/bin/wx-config") # MacPorts set(wxWidgets_CONFIG_EXECUTABLE "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.2/bin/wx-config") set(wxWidgets_wxrc_EXECUTABLE "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.2/bin/wxrc") message(STATUS "Using known location of wx-config 3.2") elseif (EXISTS "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.1/bin/wx-config") # MacPorts set(wxWidgets_CONFIG_EXECUTABLE "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.1/bin/wx-config") set(wxWidgets_wxrc_EXECUTABLE "/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.1/bin/wxrc") message(STATUS "Using known location of wx-config 3.1") else() message(STATUS "No wx-config in expected locations") endif() endif() add_subdirectory(WinPort/src/Backend/WX) endif() if (NOT DEFINED TTYX) find_package(X11) if(X11_FOUND) message(STATUS "Building with TTY X11 extensions due to X11 found") add_subdirectory(WinPort/src/Backend/TTY/TTYX) else() message(STATUS "Building without TTY X11 extensions due to X11 not found") endif() elseif (TTYX) message(STATUS "Building with TTY X11 extensions due to TTYX=${TTYX}") add_subdirectory(WinPort/src/Backend/TTY/TTYX) else() message(STATUS "Building without TTY X11 extensions due to TTYX=${TTYX}") endif() ############################## # plugins directives if (NOT DEFINED ALIGN OR ALIGN) message(STATUS "ALIGN plugin enabled") add_subdirectory (align) else() message(STATUS "${ColorRed}ALIGN plugin disabled due to ALIGN=${ALIGN}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/align/plug/align.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED AUTOWRAP OR AUTOWRAP) message(STATUS "AUTOWRAP plugin enabled") add_subdirectory (autowrap) else() message(STATUS "${ColorRed}AUTOWRAP plugin disabled due to AUTOWRAP=${AUTOWRAP}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/autowrap/plug/autowrap.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED CALC OR CALC) message(STATUS "CALC plugin enabled") add_subdirectory (calc) else() message(STATUS "${ColorRed}CALC plugin disabled due to CALC=${CALC}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/calc/plug/calc.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED COLORER OR COLORER) message(STATUS "COLORER plugin enabled") add_subdirectory (colorer) else() message(STATUS "${ColorRed}COLORER plugin disabled due to COLORER=${COLORER}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/colorer/plug/colorer.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED COMPARE OR COMPARE) message(STATUS "COMPARE plugin enabled") add_subdirectory (compare) else() message(STATUS "${ColorRed}COMPARE plugin disabled due to COMPARE=${COMPARE}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/compare/plug/compare.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED DRAWLINE OR DRAWLINE) message(STATUS "DRAWLINE plugin enabled") add_subdirectory (drawline) else() message(STATUS "${ColorRed}DRAWLINE plugin disabled due to DRAWLINE=${DRAWLINE}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/drawline/plug/drawline.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED EDITCASE OR EDITCASE) message(STATUS "EDITCASE plugin enabled") add_subdirectory (editcase) else() message(STATUS "${ColorRed}EDITCASE plugin disabled due to EDITCASE=${EDITCASE}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/editcase/plug/editcase.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED EDITORCOMP OR EDITORCOMP) message(STATUS "EDITORCOMP plugin enabled") add_subdirectory (editorcomp) else() message(STATUS "${ColorRed}EDITORCOMP plugin disabled due to EDITORCOMP=${EDITORCOMP}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/editorcomp/plug/editorcomp.far-plug-wide) " COMPONENT system) endif() if (DEFINED FARFTP AND FARFTP) message(STATUS "FARFTP plugin enabled (obsolete)") add_subdirectory (FARStdlib) add_subdirectory (farftp) else() install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/farftp/plug/farftp.far-plug-mb) " COMPONENT system) endif() if (NOT DEFINED FILECASE OR FILECASE) message(STATUS "FILECASE plugin enabled") add_subdirectory (filecase) else() message(STATUS "${ColorRed}FILECASE plugin disabled due to FILECASE=${FILECASE}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/filecase/plug/filecase.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED INCSRCH OR INCSRCH) message(STATUS "INCSRCH plugin enabled") add_subdirectory (incsrch) else() message(STATUS "${ColorRed}Building without plugin due to INCSRCH=${INCSRCH}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/incsrch/plug/incsrch.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED INSIDE OR INSIDE) message(STATUS "INSIDE plugin enabled") add_subdirectory (inside) else() message(STATUS "${ColorRed}INSIDE plugin disabled due to INSIDE=${INSIDE}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/inside/plug/inside.far-plug-mb) " COMPONENT system) endif() if (NOT DEFINED MULTIARC OR MULTIARC) message(STATUS "MULTIARC plugin enabled") find_package(PkgConfig REQUIRED) pkg_search_module(LibArchive QUIET libarchive) find_package(LibArchive) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # workaround for MacOS brew's/macports' libarchive execute_process(COMMAND brew --prefix OUTPUT_VARIABLE BREW_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if("${BREW_PREFIX}" STREQUAL "") set(BREW_PREFIX "/usr/local") message(STATUS "Fallback BREW_PREFIX=${BREW_PREFIX}") else() message(STATUS "Detected BREW_PREFIX=${BREW_PREFIX}") endif() if(IS_DIRECTORY "${BREW_PREFIX}/opt/libarchive/include") set(LibArchive_INCLUDE_DIR "${BREW_PREFIX}/opt/libarchive/include") set(LibArchive_LIBRARY "${BREW_PREFIX}/opt/libarchive/lib/libarchive.dylib") elseif(EXISTS "/opt/local/lib/libarchive.dylib") set(LibArchive_LIBRARY "/opt/local/lib/libarchive.dylib") endif() find_package(LibArchive) if(NOT LibArchive_FOUND) message(WARNING "${ColorRed}libarchive not found, multiarc will have weaker archives support. Its recommended to install libarchive-dev and reconfigure far2l.${ColorNormal}") endif() endif() add_subdirectory (multiarc) else() message(STATUS "${ColorRed}MULTIARC plugin disabled due to MULTIARC=${MULTIARC}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/multiarc/plug/multiarc.far-plug-mb) " COMPONENT system) endif() if (NOT DEFINED NETROCKS OR NETROCKS) message(STATUS "NETROCKS plugin enabled") find_package(OpenSSL) if(OPENSSL_FOUND) message(STATUS "OpenSSL found -> enjoy FTPS support in NetRocks") else() message(WARNING "${ColorRed}OpenSSL not found, NetRocks will not have FTPS protocol support. Install libssl-dev if you want FTPS protocol available in NetRocks.${ColorNormal}") endif(OPENSSL_FOUND) find_package(LibSSH 0.5.0) if(LIBSSH_FOUND) message(STATUS "libssh found -> enjoy SFTP support in NetRocks") else() message(WARNING "${ColorRed}libssh not found, NetRocks will not have SFTP protocol support. Install libssh-dev if you want SFTP protocol available in NetRocks.${ColorNormal}") endif(LIBSSH_FOUND) find_package(Libsmbclient) if(LIBSMBCLIENT_FOUND) message(STATUS "libsmbclient found -> enjoy SMB support in NetRocks") else() message(WARNING "${ColorRed}libsmbclient not found, NetRocks will not have SMB protocol support. Install libsmbclient-dev if you want SMB protocol available in NetRocks.${ColorNormal}") endif(LIBSMBCLIENT_FOUND) find_package(LibNfs) if(LIBNFS_FOUND) message(STATUS "libnfs found -> enjoy NFS support in NetRocks") else() message(WARNING "${ColorRed}libnfs not found, NetRocks will not have NFS protocol support. Install libnfs-dev if you want NFS protocol available in NetRocks.${ColorNormal}") endif(LIBNFS_FOUND) find_package(LibNEON) if(LIBNEON_FOUND) message(STATUS "libneon found -> enjoy WebDav support in NetRocks") else() message(WARNING "${ColorRed}libneon not found, NetRocks will not have WebDav protocol support. Install libneon*-dev if you want WebDav protocol available in NetRocks.${ColorNormal}") endif(LIBNEON_FOUND) find_package(AWSSDK QUIET COMPONENTS s3) if(AWSSDK_FOUND) message(STATUS "aws-sdk-cpp found -> enjoy AWS s3 support in NetRocks") else() message(WARNING "${ColorRed}aws-sdk-cpp not found, NetRocks will not have AWS S3 protocol support. Install aws-sdk-cpp if you want AWS S3 protocol available in NetRocks.${ColorNormal}") endif(AWSSDK_FOUND) add_subdirectory (NetRocks) else() message(STATUS "${ColorRed}NETROCKS plugin disabled due to NETROCKS=${NETROCKS}${ColorNormal}") install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/NetRocks/plug/NetRocks.far-plug-wide) " COMPONENT system) endif() if (DEFINED PYTHON AND PYTHON) message(STATUS "PYTHON plugin with interpreter from virtualenv enabled") add_subdirectory (python) # Copy this explicitly cuz generic copier command excludes 'far2l' install(DIRECTORY "${INSTALL_DIR}/Plugins/python/plug/far2l" DESTINATION "lib/far2l/Plugins/python/plug/" USE_SOURCE_PERMISSIONS COMPONENT base FILES_MATCHING PATTERN "*") install(DIRECTORY "${INSTALL_DIR}/Plugins/python/plug/plugins" DESTINATION "lib/far2l/Plugins/python/plug/" USE_SOURCE_PERMISSIONS COMPONENT base FILES_MATCHING PATTERN "*") else() message(STATUS "${ColorRed}PYTHON plugin disabled, use -DPYTHON=yes if you need it${ColorNormal}") install(CODE " execute_process(COMMAND rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/python/plug/python.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED SIMPLEINDENT OR SIMPLEINDENT) message(STATUS "SIMPLEINDENT plugin enabled") add_subdirectory (SimpleIndent) else() message(STATUS "${ColorRed}SIMPLEINDENT plugin disabled due to SIMPLEINDENT=${SIMPLEINDENT}${ColorNormal}") install(CODE " execute_process(COMMAND rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/SimpleIndent/plug/SimpleIndent.far-plug-wide) " COMPONENT system) endif() if (NOT DEFINED TMPPANEL OR TMPPANEL) message(STATUS "TMPPANEL plugin enabled") add_subdirectory (tmppanel) else() message(STATUS "${ColorRed}TMPPANEL plugin disabled due to TMPPANEL=${TMPPANEL}${ColorNormal}") install(CODE " execute_process(COMMAND rm -f ${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/tmppanel/plug/tmppanel.far-plug-wide) " COMPONENT system) endif() ############################## # common install directives add_subdirectory (packaging) add_subdirectory(man) add_subdirectory(bash-completion) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(FIXUP_SCRIPT packaging/osx/FixupBundle.cmake) configure_file(${FIXUP_SCRIPT}.in ${PROJECT_BINARY_DIR}/${FIXUP_SCRIPT} @ONLY) install(DIRECTORY ${APP_DIR}/${APP_NAME}.app DESTINATION . USE_SOURCE_PERMISSIONS COMPONENT app EXCLUDE_FROM_ALL) install(SCRIPT ${PROJECT_BINARY_DIR}/${FIXUP_SCRIPT} COMPONENT app EXCLUDE_FROM_ALL) endif() install(PROGRAMS "${INSTALL_DIR}/${EXECUTABLE_NAME}" DESTINATION "bin" RENAME far2l COMPONENT base) install(DIRECTORY "${INSTALL_DIR}/" DESTINATION "lib/far2l" USE_SOURCE_PERMISSIONS COMPONENT base FILES_MATCHING PATTERN "colorer/base" EXCLUDE PATTERN "far2l_gui.so" PATTERN "far2l_ttyx.broker" PATTERN "plug/*.far-plug-*" PATTERN "plug/*.broker" ) install(DIRECTORY "${INSTALL_DIR}/" DESTINATION "share/far2l" USE_SOURCE_PERMISSIONS COMPONENT base FILES_MATCHING PATTERN "${EXECUTABLE_NAME}" EXCLUDE PATTERN "far2ledit" EXCLUDE PATTERN "far2l_*" EXCLUDE PATTERN "*.far-plug-*" EXCLUDE PATTERN "*.broker" EXCLUDE PATTERN "python/plug/python" EXCLUDE PATTERN "*" ) # setup some symlinks and remove deprecated stuff from previous installation execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../bin/far2l ${PROJECT_BINARY_DIR}/far2l/far2l_askpass) execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../bin/far2l ${PROJECT_BINARY_DIR}/far2l/far2l_sudoapp) execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink far2l ${PROJECT_BINARY_DIR}/far2l/far2ledit) install(FILES "${PROJECT_BINARY_DIR}/far2l/far2l_askpass" DESTINATION "lib/far2l" COMPONENT system) install(FILES "${PROJECT_BINARY_DIR}/far2l/far2l_sudoapp" DESTINATION "lib/far2l" COMPONENT system) install(FILES "${PROJECT_BINARY_DIR}/far2l/far2ledit" DESTINATION "bin" COMPONENT base) install(CODE " execute_process(COMMAND ${CMAKE_COMMAND} -E remove -f \"\${CMAKE_INSTALL_PREFIX}/lib/far2l/Plugins/objinfo/plug/objinfo.far-plug-mb\") " COMPONENT system) far2l-2.6.5~beta+ds/CODESTYLE.md000066400000000000000000000124001477431623700160560ustar00rootroot00000000000000## Legacy code In case you're doing minor changes in existing legacy code - use existing code style as you see in that code. Otherwise follow guidelines below. ## Indentation and alignment: Code block indentation and alignment of hyphenated continuations - tabs. Alignment of trailing comments - also tabs. All other alignment in the middle of text line (if need) - spaces. ## Spaces and parenthesis: ``` Examples: // No space between parenthesis and function name, no space in empty arguments list: void FunctionWithoutArgs(); // Spaces between arguments, but no spaces between parenthesis and contained argument: void ConstFunctionWithArgs(int i, std::string s) const; // Spaces between expression elements and after if, no spaces between parenthesis and contained stuff: if (condition1 && (condition2 || condition3)) { } // Space between try and opening brace, spaces around catch: try { } catch (std::exception &) { } int arr[] = {1, 2, 3}; // - space both sides of equal sign here int i{}; // - no space between variable and list initializer's brace i = arr[1]; // - simple assignment - space surrounds both sides of equal sign i+= arr[2]; // - incremental assignment - space only on the right void *ptr_i = (void *)&i; // - pointer: space between target type and asterisk int &ref_i = i; // - reference: space between target type and amperans // Complex ternary expressions should have parenthesis to segregate things: int ternary_result1 = simple_condition ? 1 : 2; int ternary_result2 = (complex && condition) ? 1 : 2; int ternary_result3 = simple_condition ? ((complex && condition) ? 1 : 2) : ((complex && condition) ? 3 : 4); ``` ## Line length limit: 110 characters, buts its a soft limit, i.e. prefer better readability more than fiting limit. Trailing comments are not affected by this limit at all. ## Code braces: Use direct (same line) braces for class-es/struct-s/union-s, functions, namespaces Use egyptian braces for if-s, loop-s, try..catch-es, lambda-s In case of if..elseif.. with long complex code within block - use empty line to accent end of code block. In case code-block-start operator has short own condition and has as child single another code-block-starter, you may put that secondary operator at same line as first one and use single indentation for its code block. In case of very short inlined class methods - you may write method definition's code block as single line. In all other cases put any nested operator and its code block starting from separate line and with its own indentation level. ``` Examples: namespace Foo { struct Bar { int Short() const { return _result; } void Baz() const { if (short_cond) try { SomeCode(); } catch (...) { } if (long && complicated && condition) { try { SomeCode(); } catch (...) { } } } void Qux() { if (cond1) { SomeShortCode(); } else if (cond2) { SomeShortCode(); } else { SomeShortCode(); } if (cond1) { Some(); Long(); Code(); } else if (cond2) { Some(); Long(); Code(); } else { Some(); Long(); Code(); } } }; }; ``` ## Hyphenated continuations: Indent second line of condition using two tabs to separate it from code block. Put hyphenated operators on new line's beginning. ``` Example: if (i == 10 || i == 20 || i == 30) { SomeCode(); } ``` ## Naming: Use CamelCase for name of enums, namespaces, classes, structures, functions and methods Use snake_case for all variables, however:    Private and protected class's fields - prefix by '\_'    Static variables - prefix by 's\_'    Global nonstatic variables - prefix by 'g\_' Use UPPER_CASE_WITH_UNDERSCORES for macroses, values of enum-s. Additionally values of enums must be prefixes with an abbreviation of corresponding enum's name. Templates:    For template arguments that represent type name - use CamelCaseT (camel case with T suffix).    For template arguments that typed constant value - use UPPER_CASE_WITH_UNDERSCORES_T.    If template function represents 'internal' implementation for some nearby non-templated functions - you may add T suffix for its name to clearly denote this. ``` Examples: enum SomeEnum { SE_ZERO = 0, SE_ONE = 1, SE_TWO = 2 }; int g_counter = 0; class FooBar { int _private_field; public: void CallMe(SomeEnum argument) { static int s_static_var; int local_var = argument + _private_field; s_static_var = std::max(local_var, s_static_var); ++g_counter; } }; template < class ClassT, int VALUE_T > ClassT TemplateFunction(int arg) { return ClassT(arg + VALUE_T); } ``` ## class/struct: Use struct if your class exposes some public data fields or/and only public methods otherwise prefer using class. ## class/struct layouts: ``` class FooBar { int _private_field; void PrivateMethod(); protected: int _protected_field; void ProtectedMethod(); public: void PublicMethod(); }; struct FooBar { int public_field; void PublicMethod(); }; ``` ## File naming: Use CamelCase.cpp unless you're adding file into a directory that already have lots of files with different naming convention. In such case follow existing conventions. far2l-2.6.5~beta+ds/Dockerfile000066400000000000000000000004551477431623700162020ustar00rootroot00000000000000FROM ubuntu:22.04 WORKDIR /workdir COPY dependencies.txt . RUN apt-get update && apt-get -y install $(cat dependencies.txt) COPY . . RUN mkdir -p _build RUN cd _build && \ cmake -DUSEWX=yes -DCMAKE_BUILD_TYPE=Release .. && \ cmake --build . -j$(nproc --all) CMD /workdir/_build/install/far2l far2l-2.6.5~beta+ds/FARStdlib/000077500000000000000000000000001477431623700157565ustar00rootroot00000000000000far2l-2.6.5~beta+ds/FARStdlib/CMakeLists.txt000066400000000000000000000016161477431623700205220ustar00rootroot00000000000000 project(FARStdlib) find_package(PkgConfig REQUIRED) set(SOURCES src/fstd_scr.cpp src/fstd_stdlibCS.cpp src/fstd_asrt.cpp src/fstd_Patt.cpp src/fstd_OEM.cpp src/fstd_SText.cpp src/fstd_exSCHC.cpp src/fstd_Reg.cpp src/fstd_plg.cpp src/fstd_Con.cpp src/fstd_exSCPY.cpp src/fstd_menu.cpp src/fstd_exSCAT.cpp src/fstd_log.cpp src/fstd_mklog.cpp src/fstd_String.cpp src/fstd_err.cpp src/fstd_mesg.cpp src/fstd_SCol.cpp src/fstd_Msg.cpp src/fstd_exSCMP.cpp src/fstd_exSPCH.cpp src/fstd_crc32.cpp src/fstd_exit.cpp src/fstd_exSNCH.cpp src/fstd_Dialog.cpp src/fstd_per.cpp src/fstd_Utils.cpp src/fstd_ilist.cpp src/fstd_Arg.cpp src/fstd_FMsg.cpp src/fstd_exSPS.cpp src/fstd_FUtils.cpp src/fstd_ClpS.cpp ) add_library (FARStdlib ${SOURCES}) target_include_directories(FARStdlib PRIVATE include) target_include_directories(FARStdlib PRIVATE ../WinPort) target_include_directories(FARStdlib PRIVATE ../far2l/far2sdk) far2l-2.6.5~beta+ds/FARStdlib/include/000077500000000000000000000000001477431623700174015ustar00rootroot00000000000000far2l-2.6.5~beta+ds/FARStdlib/include/all_far.h000066400000000000000000000217541477431623700211630ustar00rootroot00000000000000#ifndef __FAR_PLUGINS_STD_HEADERS #define __FAR_PLUGINS_STD_HEADERS #define WIN32_LEAN_AND_MEAN #include #include #include // SIGxxx #include // stat #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__DragonFly__) #include // alloc,NULL #endif #include // sqrt #include // isprint #include // sprintf #include // atexit #include // strXXX #include // O_RDWR,xxx #include // exseptions #include // assert #include // errors #include // va_list #include // timespec #include #include #include #include #include #ifndef ARRAYSIZE #define ARRAYSIZE(A) (sizeof(A) / sizeof((A)[0])) #endif //- MACROSES #define MAX_DWORD ((DWORD)0xFFFFFFFFUL) #define MAX_WORD ((WORD)0xFFFFU) #define MAX_BYTE ((WORD)0xFFU) template inline T Max(T a, T b) { return (a > b) ? a : b; } template inline T Min(T a, T b) { return (a < b) ? a : b; } template inline T Abs(T v) { return (v > 0) ? v : (-v); } template inline BOOL Between(T val, T a, T b) { return (val >= a && val <= b) ? TRUE : FALSE; } template void Swap(T &a, T &b) { T tmp = a; a = b; b = tmp; } /** @def SET_HI_WORD( dw,w ) Sets high subword in DWORD value. */ /** @def SET_LO_WORD( dw,w ) Sets low subword in DWORD value. */ #define SET_HI_WORD(dw, w) ((((DWORD)dw) & 0x0000FFFFUL) | (((DWORD)w) << 16)) #define SET_LO_WORD(dw, w) ((((DWORD)dw) & 0xFFFF0000UL) | (((WORD)w) & 0xFFFFUL)) #ifndef HI_WORD #define HI_WORD(dw) ((WORD)(((DWORD)(dw)) >> 16)) #define LO_WORD(dw) ((WORD)(((DWORD)(dw)) & 0x0000FFFFUL)) #endif #define MK_DWORD(hw, lw) (((((DWORD)hw) & 0x0000FFFFUL) << 16) | (((WORD)lw) & 0x0000FFFFUL)) #define SET_HI_BYTE(w, b) ((((WORD)(w)) & 0x00FFU) | (((BYTE)(b)) << 8)) #define SET_LO_BYTE(w, b) ((((WORD)(w)) & 0xFF00U) | ((BYTE)(b))) #ifndef HI_BYTE #define HI_BYTE(w) (((WORD)(w)) >> 8) #define LO_BYTE(w) ((BYTE)((WORD)(w)) & 0x00FFU) #endif #define MK_WORD(hb, lb) ((WORD)(((((WORD)(hb)) & 0x00FFU) << 8) | (((BYTE)(lb)) & 0x00FFU))) #define sSET_HI_WORD(dw, w) ((((int)(dw)) & 0x0000FFFFUL) | (((int)(w)) << 16)) #define sSET_LO_WORD(dw, w) ((((int)(dw)) & 0xFFFF0000UL) | (((short)(w)) & 0xFFFFUL)) #define sHI_WORD(dw) ((short)((dw) >> 16)) #define sLO_WORD(dw) ((short)(dw)) #define sMK_DWORD(lw, hw) ((int)(((((int)(hw)) & 0x0000FFFFUL) << 16) | (((short)(lw)) & 0xFFFFUL))) #define IS_BIT(val, num) IS_FLAG(((DWORD)(val)),1UL<<(num))) #define IS_FLAG(val, flag) (((val) & (flag)) == (flag)) #define SET_FLAG(val, flag) (val|= (flag)) #define CLR_FLAG(val, flag) (val&= ~(flag)) #define SWITCH_FLAG(f, v) \ do { \ if (IS_FLAG(f, v)) \ CLR_FLAG(f, v); \ else \ SET_FLAG(f, v); \ } while (0) //- MK_ID #define MK_ID(v, v1, v2, v3) MK_DWORD(MK_WORD(v3, v2), MK_WORD(v1, v)) #define HAbort __WinAbort #define THROW_ERROR(err, fl, nm) HAbort("Assertion...\nConditin: \"%s\"\nAt file: \"%s:%d\"", err, fl, nm) #if defined(__DEBUG__) || defined(__USEASSERT__) #define Assert(p) \ do { \ if (!(p)) \ THROW_ERROR(#p, __FILE__, __LINE__); \ } while (0) #else #define Assert(p) #endif //- ASSERT #if !defined(__FP_NOT_FUNCTIONS__) //[fstd_asrt.cpp] extern void _cdecl __WinAbort(LPCSTR msg, ...); //[fstd_err.cpp] extern const char *_cdecl __WINError(void); //[fstd_exit.cpp] typedef void(_cdecl *AbortProc)(void); extern AbortProc WINAPI AtExit(AbortProc p); extern void WINAPI CallAtExit(void); #endif // -------------------------------------------------------------- /** @defgroup TimePeriod Time/Period measurement @{ [fstd_tm.cpp] Procedures for get current time, calculate difference and check periods of time. */ #define CMP_TIME_FORMAT "%3.3lf" inline void GET_TIME(DWORD &var) { var = WINPORT(GetTickCount)(); } inline double CMP_TIME(DWORD evar, DWORD bvar) { return ((double)evar - (double)bvar) / 1000.; } extern HANDLE WINAPI FP_PeriodCreate(DWORD ms); extern BOOL WINAPI FP_PeriodEnd(HANDLE p); extern DWORD WINAPI FP_PeriodTime(HANDLE p); extern void WINAPI FP_PeriodDestroy(HANDLE &p); extern void WINAPI FP_PeriodReset(HANDLE p); /** @brief Helper class for time period. */ struct FPPeriod { HANDLE Handle; public: FPPeriod(void) { Handle = NULL; } FPPeriod(DWORD ms) { Handle = FP_PeriodCreate(ms); } ~FPPeriod() { if (Handle) FP_PeriodDestroy(Handle); } bool End(void) { return FP_PeriodEnd(Handle) != FALSE; } void Reset(void) { FP_PeriodReset(Handle); } void Create(DWORD ms) { Handle = FP_PeriodCreate(ms); } }; /**@} Period*/ // -------------------------------------------------------------- /** @defgroup StdLib RTL extension\replacement @{ Wrappers for stdlib functions and some useful extended stdlib functions. */ #if !defined(__FP_NOT_FUNCTIONS__) // string extension inline char ToUpperI(char c) { return (char)((c) + 'A' - 'a'); } inline char ToLowerI(char c) { return (char)((c) + 'a' - 'A'); } extern int WINAPI StrCmp(LPCSTR str, LPCSTR str1, int maxlen = -1, BOOL isCaseSens = TRUE); inline int StrCmpI(LPCSTR str, LPCSTR str1) { return StrCmp(str, str1, -1, FALSE); } inline int StrNCmp(LPCSTR str, LPCSTR str1, int maxlen) { return StrCmp(str, str1, maxlen, TRUE); } inline int StrNCmpI(LPCSTR str, LPCSTR str1, int maxlen) { return StrCmp(str, str1, maxlen, FALSE); } extern char *WINAPI StrCpy(char *dest, LPCSTR src, int dest_sz); extern char *WINAPI StrCat(char *dest, LPCSTR src, int dest_sz); extern int WINAPI StrNChr(LPCSTR str, char ch, int maxlen = -1); extern int WINAPI strchrCount(LPCSTR str, char ch, int maxlen = -1); extern int WINAPI StrPosChr(LPCSTR str, char ch, int pos = 0); extern int WINAPI StrPosStr(LPCSTR str, LPCSTR s, int pos = 0); #endif // -------------------------------------------------------------- template T AtoI(LPCSTR str, T Def) { if (!str || !str[0]) return Def; T Sign = 1; if (*str == '-') { Sign = -1; str++; } else if (*str == '+') str++; if (!isdigit(*str)) return Def; for (Def = 0; *str && isdigit(*str); str++) { Def*= 10; Def+= (T)(*str - '0'); } return Def * Sign; } /**@} RTL*/ // Args #if !defined(__FP_NOT_FUNCTIONS__) extern void WINAPI CTArgInit(int argc, char **argv, BOOL CaseSensitive = FALSE); extern LPSTR WINAPI CTArgGet(int num); // get base argument extern LPSTR WINAPI CTArgGetArg(int num); // get`s argument that not switch extern LPSTR WINAPI CTArgGet(LPCSTR name); // find "[-/][=]" key and ret || NULL extern BOOL WINAPI CTArgCheck(LPCSTR name); // check for "[-/]" exist extern char *WINAPI Str2Text(LPCSTR name, char *Buff, DWORD BuffSz); extern char *WINAPI Text2Str(LPCSTR name, char *Buff, DWORD BuffSz); // Std columns extern int WINAPI StrColCount(LPCSTR str, LPCSTR seps); extern LPCSTR WINAPI StrGetCol(LPCSTR str, int number, LPCSTR seps); // Generate 32bit CRC for buffer. extern DWORD WINAPI Crc32(DWORD StartCrc /*=0*/, const BYTE *buf, DWORD len); //[fstd_Mesg.cpp] extern LPCSTR _cdecl Message(LPCSTR patt, ...); extern LPCSTR WINAPI MessageV(LPCSTR patt, va_list a); // Utils extern LPCSTR WINAPI FCps(char *buff, double val); // Create CPS value string (Always 3+1+3+1 length) extern LPSTR WINAPI AddLastSlash(char *path, char Slash = '/'); extern LPSTR WINAPI DelLastSlash(char *path, char Slash = '/'); extern LPCSTR WINAPI FPath(LPCSTR nm, char Slash = '/'); extern LPCSTR WINAPI FName(LPCSTR nm, char Slash = '/'); extern LPCSTR WINAPI FExtOnly(LPCSTR nm, char Slash = '/'); #endif // -------------------------------------------------------------- /** @defgroup Log Logging and debug @{ [fstd_InProc.cpp] Helpers for logging and debug purposes. */ class FARINProc { static int Counter; LPCSTR Name; public: FARINProc(LPCSTR nm, LPCSTR s, ...); ~FARINProc(); static void _cdecl Say(LPCSTR s, ...); }; extern int FP_LogErrorStringLength; /** @brief Procedure callback for specify name of current log file */ extern void _cdecl FP_FILELog(LPCSTR msg, ...); ///< Writes text to current log file. extern LPCSTR WINAPI FP_GetLogFullFileName(void); ///< Returns full current file log. /**@} Log*/ #endif far2l-2.6.5~beta+ds/FARStdlib/include/fstd_String.h000066400000000000000000000035441477431623700220460ustar00rootroot00000000000000#pragma once #include #define DEF_STR_ALLOC 50 class String { char *str; int len, maxchar; private: void BeginSet(size_t start_size = DEF_STR_ALLOC); public: virtual ~String(); String(void); String(const String &s); String(LPCSTR s); String(size_t DefaultAllocateSize); char *c_str(void) const { return str; } int Length(void) const { return len; } LPCSTR Alloc(LPCSTR s, int maxLen = -1); LPCSTR Alloc(int NewAllocatedSize); void SetLength(int ValueLessThenExistingLength); void Null(void) { SetLength(0); } LPCSTR Set(LPCSTR s, int StartToAdd_Inclusive, int EndToAdd_Exclusive /*-1*/); LPCSTR Set(LPCSTR s) { return Alloc(s); } LPCSTR Set(const String &s) { return Alloc(s.c_str()); } int printf(LPCSTR fmt, ...); int vprintf(LPCSTR fmt, va_list list); void cat(LPCSTR s, ...); void vcat(LPCSTR s, va_list a); String &Add(const String &s); String &Add(LPCSTR s); String &Add(char s); String &Add(LPCSTR s, int StartToAdd_Inclusive, int EndToAdd_Exclusive /*-1*/); void Del(int StartChar, int Count); void DelChars(char AllCharsToDel = ' '); void InsCharPos(int InsertPos, char ch); // Rets -1 if not found. int Chr(char ch, int StartToSearchFrom = 0) const; // strchr int Chr(LPCSTR set, int StartToSearchFrom = 0) const; // search first char contains in set int RChr(char ch, int EndToSearchAt = -1) const; // strrchr int Str(LPCSTR ch, int pos = 0) const; // strstr String &operator=(const String &s); String &operator=(LPCSTR s); // Exact compare BOOL operator!=(const String &s) const; BOOL operator!=(LPCSTR s) const; BOOL operator==(const String &s) const; BOOL operator==(LPCSTR s) const; BOOL Cmp(LPCSTR s, int count = -1, BOOL isCase = FALSE) const; char operator[](int num) const; char SetChar(int num, char ch); void Trim(char ch = ' '); void RTrim(char ch = ' '); void LTrim(char ch = ' '); }; far2l-2.6.5~beta+ds/FARStdlib/include/fstdlib.h000066400000000000000000000255651477431623700212160ustar00rootroot00000000000000#ifndef __FAR_PLUGINS_UTILITIES #define __FAR_PLUGINS_UTILITIES /** @mainpage FAR Standard Library @author Jouri Mamaev JouriM@uran.ru @Eng @section intro Introduction C++ library for easy create plugins for file and archive manager FAR.
Contains set of classes and functions designed to decrease time for write all types of FAR plugins. Initially created by JouriM@uran.ru @section Structure Document structure This documentation structure: - FarStdLib Full documentation tree; - Consts Different constants and defines; - Types Data types; - Functions Global functions; - Variables Global variables; @section Add Additional helpers. Additional helpers and classes does not relative to FAR API directly but used by other wrappers or incapsulate useful functionality. - FP_Screen - helper for save and restore FAR screen buffer.
Class for save and restore whole screen buffer. Can be used in nested calls. In this case the first call save screen and restored after last FP_Screen object destroyed. - FPOpMode - helper for save panel plugin OpMode value. - SRect - helper class to incapsulate Win API SMALL_RECT structure with additional public methods to manipulate its contents. */ /** @defgroup FSTDLib Library Compilation/Porting FARStdLibrary already ported to next compilers: - Borland C compiler version 5.xx - Visual C compiler version 6.xx - Symantec compiler version 7.2 - GCC */ #define _FAR_USE_FARFINDDATA 1 #include #include using namespace oldfar; #include #include #ifdef __GNUC__ #pragma pack(1) #else #pragma pack(push, 1) #endif // -------------------------------------------------------------- #include "funi.h" // ------------------------------------------------------------------------ #if defined(__FILELOG__) #define Log(v) FARINProc::Say v #define PROC(v) FARINProc _inp v; #else #define PROC(v) #define Log(v) #endif // ------------------------------------------------------------------------ /** [fstd_menu.cpp] @brief */ #if !defined(__FP_NOT_FUNCTIONS__) struct FMenuItem : public FarMenuItem { FMenuItem(LPCSTR txt, bool sel = false, char ch = 0) { Assign(txt, sel, ch); } FMenuItem(const FMenuItem &p) { Assign(p); } FMenuItem(void) { Assign(); } void Assign(LPCSTR txt, bool sel = false, char ch = 0); void Assign(const FMenuItem &p); void Assign(void); BOOL isChecked(void) { return Checked; } void isChecked(BOOL v) { Checked = v; } BOOL isSeparator(void) { return Separator; } void isSeparator(BOOL v) { Separator = v; } BOOL isSelected(void) { return Selected; } void isSelected(BOOL v) { Selected = v; } }; struct FMenuItemEx : public FarMenuItemEx { FMenuItemEx(LPCSTR txt, bool sel = false, char ch = 0) { Assign(txt, sel, ch); } FMenuItemEx(const FMenuItemEx &p) { Assign(p); } FMenuItemEx(void) { Assign(); } void Assign(LPCSTR txt, bool sel = false, char ch = 0); void Assign(const FMenuItemEx &p); void Assign(void); BOOL isChecked(void) { return IS_FLAG(Flags, MIF_CHECKED); } void isChecked(BOOL v) { if (v) SET_FLAG(Flags, MIF_CHECKED); else CLR_FLAG(Flags, MIF_CHECKED); } BOOL isSeparator(void) { return IS_FLAG(Flags, MIF_SEPARATOR); } void isSeparator(BOOL v) { if (v) SET_FLAG(Flags, MIF_SEPARATOR); else CLR_FLAG(Flags, MIF_SEPARATOR); } BOOL isSelected(void) { return IS_FLAG(Flags, MIF_SELECTED); } void isSelected(BOOL v) { if (v) SET_FLAG(Flags, MIF_SELECTED); else CLR_FLAG(Flags, MIF_SELECTED); } BOOL isGrayed(void) { return IS_FLAG(Flags, MIF_DISABLE); } void isGrayed(BOOL v) { if (v) SET_FLAG(Flags, MIF_DISABLE); else CLR_FLAG(Flags, MIF_DISABLE); } }; // ------------------------------------------------------------------------ /** @brief FP_Menu [fstd_menu.cpp] */ template class FP_MenuTyped { T *List; int ItemsCount; int MaxCount; private: BOOL Realloc(int DeltaSize); public: FP_MenuTyped(void); FP_MenuTyped(LPCSTR strings[], int SelNum = -1, int CheckNum = -1, char CheckChar = 0); ~FP_MenuTyped() { Free(); } T *Add(const T *src, int cn); // Add a `cn` items to list T *Add(const T &p) { return Add(&p, 1); } // Add 1 item to list void Free(void); // Clear list void DeleteAll(void) { Free(); } // Clear list T *Items(void) { return List; } int Count(void) { return ItemsCount; } T *Item(int num) { return (num >= 0 && num < ItemsCount) ? (List + num) : NULL; } T *operator[](int num) { return Item(num); } int Execute(LPCSTR Title, DWORD Type = 0, LPCSTR Footer = NULL, LPCSTR Help = NULL, const int *BreakKeys = NULL, int *BreakCode = NULL); }; template FP_MenuTyped::FP_MenuTyped(void) { List = NULL; ItemsCount = 0; MaxCount = 0; } template FP_MenuTyped::FP_MenuTyped(LPCSTR strings[], int SelNum, int CheckNum, char CheckChar) { int n, cn; T *it; List = NULL; ItemsCount = 0; MaxCount = 0; if (!strings) return; for (cn = n = 0; strings[n]; cn++, n++) ; if (!cn) return; Realloc(cn); for (n = 0; n < cn; n++) { it = Add(T()); it->Assign(strings[n], SelNum == n, CheckNum == n ? CheckChar : 0); } } template BOOL FP_MenuTyped::Realloc(int NewSize) { if (!NewSize) return FALSE; if (NewSize < MaxCount) return TRUE; MaxCount = NewSize; if (!List) List = (T *)malloc(sizeof(T) * MaxCount); else List = (T *)realloc(List, sizeof(T) * MaxCount); if (!List) return FALSE; return TRUE; } template T *FP_MenuTyped::Add(const T *pi, int icn) { if (!Realloc(ItemsCount + icn)) return NULL; T *p = List + ItemsCount; memmove(p, pi, sizeof(*p) * icn); ItemsCount+= icn; return p; } template void FP_MenuTyped::Free(void) { if (!ItemsCount) return; free(List); List = NULL; ItemsCount = 0; MaxCount = 0; } template int FP_MenuTyped::Execute(LPCSTR Title, DWORD Type, LPCSTR Footer, LPCSTR Help, const int *BreakKeys, int *BreakCode) { return FP_Info->Menu(FP_Info->ModuleNumber, -1, -1, 0, Type | (sizeof(T) != sizeof(FarMenuItem) ? FMENU_USEEXT : 0), (const char *)FP_GetMsg(Title), (const char *)FP_GetMsg(Footer), (const char *)Help, BreakKeys, BreakCode, (const struct FarMenuItem *)Items(), Count()); } typedef FP_MenuTyped FP_Menu; typedef FP_MenuTyped *PFP_Menu; typedef FP_MenuTyped FP_MenuEx; typedef FP_MenuTyped *PFP_MenuEx; // ------------------------------------------------------------------------ /** @brief FP_Dialog [fstd_Dialog.cpp] */ struct FP_Dialog { HANDLE Handle; int LockCount; public: FP_Dialog(HANDLE h); void Assign(HANDLE h) { Handle = h; } int Focused(void) const { return (int)FP_Info->SendDlgMessage(Handle, DM_GETFOCUS, 0, 0); } void Focused(int num) const { FP_Info->SendDlgMessage(Handle, DM_SETFOCUS, num, 0); } int Enabled(int num, int v = -1) const { return (int)FP_Info->SendDlgMessage(Handle, DM_ENABLE, num, v); } BOOL Visible(int num, int v = -1) const { return (BOOL)FP_Info->SendDlgMessage(Handle, DM_SHOWITEM, num, v); } bool Checked(int num) const { return FP_Info->SendDlgMessage(Handle, DM_GETCHECK, num, 0) == BSTATE_CHECKED; } void Checked(int num, bool v) const { FP_Info->SendDlgMessage(Handle, DM_SETCHECK, num, v ? BSTATE_CHECKED : BSTATE_UNCHECKED); } void CheckToggle(int num) const { Checked(num, !Checked(num)); } bool GetItem(int num, FarDialogItem *p) const { return FP_Info->SendDlgMessage(Handle, DM_GETDLGITEM, num, (LONG_PTR)p) == TRUE; } bool SetItem(int num, FarDialogItem *p) const { return FP_Info->SendDlgMessage(Handle, DM_SETDLGITEM, num, (LONG_PTR)p) == TRUE; } bool DlgRect(SRect *p) const { return FP_Info->SendDlgMessage(Handle, DM_GETDLGRECT, 0, (LONG_PTR)p) == TRUE; } bool ItemRect(int num, SRect *p) const { return FP_Info->SendDlgMessage(Handle, DM_GETITEMPOSITION, num, (LONG_PTR)p) == TRUE; } int CursorPos(int num) const { COORD cp = {0, 0}; return FP_Info->SendDlgMessage(Handle, DM_GETCURSORPOS, num, (LONG_PTR)&cp) == TRUE ? cp.X : 0; } bool CursorPos(int num, int pos) const; void Lock(bool v = true); void Unlock(void) { Lock(false); } bool Locked(void) const { return LockCount != 0; } void FullUnlock(void); void Close(int id = 0) const { FP_Info->SendDlgMessage(Handle, DM_CLOSE, id, 0); } void Invalidate(void) const { FP_Info->SendDlgMessage(Handle, DM_REDRAW, 0, 0); } void Visible(bool v) const { FP_Info->SendDlgMessage(Handle, DM_SHOWDIALOG, (BOOL)v, 0); } LONG_PTR User(int msg, int p = 0, LONG_PTR p1 = 0) const { return FP_Info->SendDlgMessage(Handle, msg, p, p1); } int SetText(int num, LPCSTR str, int sz = 0) const; LPCSTR GetText(int num) const; int GetText(int num, char *buff, int bSz) const; }; #endif // !defined(__FP_NOT_FUNCTIONS__) // ------------------------------------------------------------------------ /** @brief Dynamic array of panel item elements. [fstd_ilist.cpp] PluginPanelItem Reserved and can be used to store additional data. This data will be correctly copied and deleted in FP_ItemList. You MUST use _Alloc or strdup to allocate data space. Data CAT NOT BE zero sized. */ inline BOOL FPIL_ADDEXIST(const PluginPanelItem *p) { return ((p)->Reserved[0] && (p)->Reserved[1]); } inline DWORD FPIL_ADDSIZE(const PluginPanelItem *p) { return FPIL_ADDEXIST(p) ? (DWORD)(p)->Reserved[0] : 0; } inline LPVOID FPIL_ADDDATA(const PluginPanelItem *p) { return FPIL_ADDEXIST(p) ? ((void *)(p)->Reserved[1]) : NULL; } inline void FPIL_ADDSET(PluginPanelItem *p, DWORD sz, LPVOID dt) { (p)->Reserved[0] = sz; (p)->Reserved[1] = (DWORD_PTR)dt; } struct FP_ItemList { PluginPanelItem *List; ///< Panel items array int ItemsCount; ///< Number of items in array BOOL needToDelete; ///< Indicate if local copy of items array need to be deleted at destructor int MaxCount; ///< Number of elements may be inserted into list without expand list private: BOOL Realloc(int DeltaSize); public: FP_ItemList(BOOL NeedToDelete = TRUE); ~FP_ItemList() { Clear(); } PluginPanelItem *Add(const PluginPanelItem *src, int cn); ///< Add a `cn` items to list PluginPanelItem *Add(const PluginPanelItem *src) { return Add(src, 1); } void Clear(void); // Clear list PluginPanelItem *Items(void) { return List; } int Count(void) { return ItemsCount; } PluginPanelItem *Item(int num) { return (num >= 0 && num < ItemsCount) ? (List + num) : NULL; } PluginPanelItem *operator[](int num) { return Item(num); } static void Free(PluginPanelItem *List, int count); // Free items, allocated by FP_ItemList static void Copy(PluginPanelItem *dest, const PluginPanelItem *src, int cn); // Copy all data from `src` to `dest` }; struct FP_SizeItemList : public FP_ItemList { int64_t TotalFullSize; int64_t TotalFiles; public: FP_SizeItemList(BOOL NeedToDelete = TRUE) : FP_ItemList(NeedToDelete) { TotalFullSize = 0; TotalFiles = 0; } }; #ifdef __GNUC__ #pragma pack() #else #pragma pack(pop) #endif #endif far2l-2.6.5~beta+ds/FARStdlib/include/funi.h000066400000000000000000000332211477431623700205140ustar00rootroot00000000000000#ifndef __FAR_PLUGIN_UNIPLACE_HEADER #define __FAR_PLUGIN_UNIPLACE_HEADER //- FAR `farplug-mb.h` header missed\addons #define FAR_MAX_LANGID 2000 #define FAR_MAX_CMDLINE 1024 #define FAR_MAX_TITLE 512 #define FAR_MAX_NAME 256 #define FAR_MAX_REG 1024 #define FAR_MAX_REGNAME 80 #define FAR_MAX_MSGLINE 13 #define FAR_MAX_CAPTION 512 #define FAR_MAX_DLGITEM 512 // Turn off fake NM const, use FAR_MAX_PATHNAME instead #undef NM // Panel #define FAR_MAX_COLS 10 // Maximum FAR columns modes #define FAR_MAX_TITLES 20 // Maximum user defined titles // Extend panle col numbers #define FAR_NAME_COL \ (FAR_MAX_COLS + 0) // Place of string to hold dynamic allocated string for assign to NAME panel field #define FAR_OWNER_COL (FAR_MAX_COLS + 1) // ... for OWNER field #define FAR_DESC_COL (FAR_MAX_COLS + 2) // ... for DESCRIPTION field #define FAR_MAX_USERCOLS (FAR_DESC_COL + 1) // Maximum number of holded dynamic strings // Key bar titles #define FKBT_N 0 #define FKBT_C 1 #define FKBT_A 2 #define FKBT_S 3 #define FKBT_CS 4 #define FKBT_AS 5 #define FKBT_CA 6 #define FKBT_MAX (FKBT_CA + 1) #define FK_MAX 12 #define FMSG_MB_MASK 0x000F0000 // Mask for all FMSG_MB_xxx // F-key operations #define FKBT_N2F(n) ((n) + 1) // Index2F #define FKBT_F2N(n) ((n)-1) // F2Index #define FKBT_F2M(n) (1UL << ((n)-1)) // F2Mask #define FKBT_N2M(n) (1UL << (n)) // F2Mask #define FKM_F1 FKBT_F2M(1) #define FKM_F2 FKBT_F2M(2) #define FKM_F3 FKBT_F2M(3) #define FKM_F4 FKBT_F2M(4) #define FKM_F5 FKBT_F2M(5) #define FKM_F6 FKBT_F2M(6) #define FKM_F7 FKBT_F2M(7) #define FKM_F8 FKBT_F2M(8) #define FKM_F9 FKBT_F2M(9) #define FKM_F10 FKBT_F2M(10) #define FKM_F11 FKBT_F2M(11) #define FKM_F12 FKBT_F2M(12) typedef char *FKeyBarTitle[12]; typedef FKeyBarTitle *PFKeyBarTitle; // Hot key for operate with codes got from plugin `ProcessKey` and passed to FPanel `DoProcessKey` #define FHK_MAKE(sh, key) MK_DWORD(((WORD)sh), ((WORD)key)) #define FHK_SHIFT(hkey) ((unsigned int)LO_WORD((DWORD)hkey)) #define FHK_KEY(hkey) ((int)HI_WORD((DWORD)hkey)) // Useful console chars /* #define FAR_SHADOW_CHAR '\xB0' //° #define FAR_FULL_CHAR '\xDB' //Û #define FAR_VERT_CHAR '\xB3' //³ #define FAR_DVERT_CHAR '\xBA' //º #define FAR_HORZ_CHAR '\xC4' //Ä #define FAR_DHORZ_CHAR '\xCD' //Í #define FAR_CHECK_CHAR '\xFb' //û #define FAR_SBMENU_CHAR '\x10' // #define FAR_LEFT_CHAR '\x11' // #define FAR_RIGHT_CHAR FAR_SBMENU_CHAR #define FAR_SPACE_CHAR '\xFA' //ú #define FAR_TAB_CHAR '\xFE' //þ #define FAR_DOWN_CHAR '\x19' // #define FAR_SKIP_CHAR '\x20' //' ' #define FAR_SHADOW_STR "\xB0" //° #define FAR_FULL_STR "\xDB" //Û #define FAR_VERT_STR "\xB3" //³ #define FAR_DVERT_STR "\xBA" //º #define FAR_HORZ_STR "\xC4" //Ä #define FAR_DHORZ_STR "\xCD" //Í #define FAR_CHECK_STR "\xFb" //û #define FAR_SBMENU_STR "\x10" // #define FAR_LEFT_STR "\x11" // #define FAR_RIGHT_STR FAR_SBMENU_STR #define FAR_SPACE_STR "\xFA" //ú #define FAR_TAB_STR "\xFE" //þ #define FAR_DOWN_STR "\x19" // #define FAR_SKIP_STR "\x20" //" " */ #define FAR_SBMENU_CHAR '\x10' //  #define FAR_LEFT_CHAR '\x11' //  #define FAR_RIGHT_CHAR FAR_SBMENU_CHAR #define FAR_VERT_CHAR '|' // ³ #define FAR_DOWN_CHAR '\x19' //  #define FAR_SKIP_CHAR '\x20' //' ' #define FAR_SHADOW_CHAR '=' #define FAR_FULL_CHAR '#' #define FAR_SHADOW_STR "=" #define FAR_FULL_STR "#" // Menu spec characters #define FMENU_CHECKED FAR_CHECK_STR #define FMENU_DELIMITER FAR_VERT_STR #define FMENU_SUBMENU FAR_SBMENU_STR #define FMENU_DIALOG FAR_SKIP_STR #define FMENU_NORMAL FAR_SKIP_STR // Diffs #define FE_CURSTRING -1 // number of current string in editor // Console colors enum FarConsoleColors { fccBLACK, /* dark colors */ fccBLUE, fccGREEN, fccCYAN, fccRED, fccMAGENTA, fccBROWN, fccLIGHTGRAY, fccDARKGRAY, /* light colors */ fccLIGHTBLUE, fccLIGHTGREEN, fccLIGHTCYAN, fccLIGHTRED, fccLIGHTMAGENTA, fccYELLOW, fccWHITE }; #define FAR_COLOR(foreground, background) ((((background)&0x0F) << 4) | ((foreground)&0x0F)) #define FAR_COLOR_BK(color) (((color) >> 4) & 0x0F) #define FAR_COLOR_FORE(color) ((color)&0x0F) extern int WINAPI FP_Color(int far_color_num); // -------------------------------------------------------------- #if !defined(__FP_NOT_FUNCTIONS__) extern PluginStartupInfo *FP_Info; ///< Pointer to FAR plugin communication info structure. extern FarStandardFunctions *FP_FSF; extern char *FP_PluginRootKey; extern BOOL FP_IsOldFar; extern int FP_LastOpMode; extern char *FP_PluginStartPath; #endif /** @brief Return name of plugin DLL Plugin must define this function. */ #if !defined(__FP_NOT_FUNCTIONS__) extern LPCSTR WINAPI FP_GetPluginName(void); #endif /** @defgroup DefDialog Dialog description @{ Useful tools for define and set arrays of FAR dialog items. */ #define FFDI_MASK 0xFFUL // ------------------------------------------------------------------------ #include "fstd_String.h" // ------------------------------------------------------------------------ extern int WINAPI FP_Message(unsigned int Flags, LPCSTR HelpTopic, LPCSTR *Items, int ItemsNumber, int ButtonsNumber, LPBOOL Delayed = NULL); // -------------------------------------------------------------- /** @defgroup GetMsg Language messages @{ [fstd_Msg.cpp] Wrappers for FAR language API. */ #define FMSG(v) ((LPCSTR)(INT_PTR)(v)) #define FISMSG(v) ((v) != NULL && (DWORD_PTR)(v) > FAR_MAX_LANGID) #define FGETID(v) Abs((int)LO_WORD((DWORD)(DWORD_PTR)(v))) #if !defined(__FP_NOT_FUNCTIONS__) typedef LPCSTR(WINAPI *FP_GetMsgINT_t)(int MsgId); typedef LPCSTR(WINAPI *FP_GetMsgSTR_t)(LPCSTR Msg); extern FP_GetMsgINT_t FP_GetMsgINT; extern FP_GetMsgSTR_t FP_GetMsgSTR; inline LPCSTR FP_GetMsg(int MsgId) { return FP_GetMsgINT(MsgId); } inline LPCSTR FP_GetMsg(LPCSTR Msg) { return FP_GetMsgSTR(Msg); } #endif /**@}*/ // -------------------------------------------------------------- /** @defgroup FarRegXX Registry manipulations. @{ [fstd_RegXX.cpp] Wrappers for registry Win API. */ #if !defined(__FP_NOT_FUNCTIONS__) extern int WINAPI FP_GetRegKey(LPCSTR Key, LPCSTR ValueName, DWORD Default); extern BYTE *WINAPI FP_GetRegKey(LPCSTR Key, LPCSTR ValueName, BYTE *ValueData, const BYTE *Default, DWORD DataMaxSize); extern char *WINAPI FP_GetRegKey(LPCSTR Key, LPCSTR ValueName, char *ValueData, LPCSTR Default, DWORD DataMaxSize); inline int WINAPI FP_GetRegKey(LPCSTR ValueName, DWORD Default) { return FP_GetRegKey(NULL, ValueName, Default); } inline char *WINAPI FP_GetRegKey(LPCSTR ValueName, char *ValueData, LPCSTR Default, DWORD DataSize) { return FP_GetRegKey(NULL, ValueName, ValueData, Default, DataSize); } inline BYTE *WINAPI FP_GetRegKey(LPCSTR ValueName, BYTE *ValueData, const BYTE *Default, DWORD DataSize) { return FP_GetRegKey(NULL, ValueName, ValueData, Default, DataSize); } extern BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, DWORD ValueData); extern BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, const BYTE *ValueData, DWORD ValueSize); extern BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, LPCSTR ValueData); inline BOOL WINAPI FP_SetRegKey(LPCSTR ValueName, DWORD ValueData) { return FP_SetRegKey(NULL, ValueName, ValueData); } inline BOOL WINAPI FP_SetRegKey(LPCSTR ValueName, LPCSTR ValueData) { return FP_SetRegKey(NULL, ValueName, ValueData); } inline BOOL WINAPI FP_SetRegKey(LPCSTR ValueName, const BYTE *ValueData, DWORD ValueSize) { return FP_SetRegKey(NULL, ValueName, ValueData, ValueSize); } extern HKEY WINAPI FP_CreateRegKey(LPCSTR Key); extern HKEY WINAPI FP_OpenRegKey(LPCSTR Key); extern BOOL WINAPI FP_DeleteRegKey(LPCSTR Key); extern BOOL WINAPI FP_CheckRegKey(LPCSTR Key); extern BOOL WINAPI FP_DeleteRegKeyFull(LPCSTR Key); //!!Do not uses FP_PluginRootKey - absolute path from HKCU extern BOOL WINAPI FP_DeleteRegKeyAll(HKEY BaseKey, LPCSTR SubKeyName); extern BOOL WINAPI FP_DeleteRegKeyAll(LPCSTR hParentKey, LPCSTR Key); // HKCU + hParentKey + Key extern BOOL WINAPI FP_DeleteRegKeyAll(LPCSTR Key); // HKCU + PluginKey + Key extern HANDLE WINAPI FP_PushKey(LPCSTR Subkey); extern void WINAPI FP_PopKey(HANDLE PushedKey); #endif /**@}*/ // -------------------------------------------------------------- /** @defgroup Clipboard Clipboard operations. @{ [fstd_ClpS.cpp] Wrappers for clipboard Win API. */ #if !defined(__FP_NOT_FUNCTIONS__) extern BOOL WINAPI FP_CopyToClipboard(LPVOID Data, SIZE_T DataSize); extern BOOL WINAPI FP_GetFromClipboard(LPVOID &Data, SIZE_T &DataSize); // The callees should call `free()` to recvd data #endif /**@}*/ // -------------------------------------------------------------- /** @defgroup Other Other utilities functions. @{ */ #if !defined(__FP_NOT_FUNCTIONS__) extern int WINAPI FP_ConWidth(void); extern int WINAPI FP_ConHeight(void); #endif /**@}*/ // -------------------------------------------------------------- /* - [] isFARWin9x, isFARWinNT Returns TRUE if plugin executed under expected operation system. - [std_Patt.cpp] FP_InPattern - [f_ChEsc.cpp] Returns `key index+1` if one of keys in zero-terminated array, or ESC if array not set, pressed. Return 0 if no key pressed. - [] Debug functions and helpers @def CHK_INITED Assertly checks if FSTD initialized. @def IS_SILENT( ) Checks if OpMode is `silent` or interactive mode. @def SET_SILENT( ) Sets "silent" bits to OpMode. */ #if !defined(__FP_NOT_FUNCTIONS__) extern void WINAPI FP_SetStartupInfo(const PluginStartupInfo *Info, const char *KeyName); extern BOOL WINAPI FP_PluginStartup(DWORD Reason); extern LPCSTR WINAPI FP_GetPluginLogName(void); extern BOOL WINAPI FP_InPattern(LPCSTR ListOfPatterns, LPCSTR NameToFitInPattern); extern int WINAPI FP_CheckKeyPressed(int *keys = NULL); #define OPM_NODIALOG 0x1000 #define OPM_USER 0x2000 #define CHK_INITED Assert(FP_Info && FP_Info->StructSize); #define IS_SILENT(v) (((v) & (OPM_FIND | OPM_VIEW | OPM_EDIT)) != 0) #define SET_SILENT(v) (v)|= OPM_FIND | OPM_VIEW | OPM_EDIT #endif // ------------------------------------------------------------------------ /** @brief [] */ #if !defined(__FP_NOT_FUNCTIONS__) class FP_Screen { public: FP_Screen(void) { Save(); } ~FP_Screen() { Restore(); } static void WINAPI Save(void); // Save console screen, inc save counter static void WINAPI Restore(void); // Dec save counter, Restore console screen on zero static void WINAPI RestoreWithoutNotes(void); // Restore screen without counter changes static void WINAPI FullRestore(void); // Dec counter to zero and restore screen static int WINAPI isSaved(void); // Save counter value }; #endif // ------------------------------------------------------------------------ /** @brief [] */ #if !defined(__FP_NOT_FUNCTIONS__) class FPOpMode { int OpMode; public: FPOpMode(int mode) { OpMode = FP_LastOpMode; FP_LastOpMode = mode; } ~FPOpMode() { FP_LastOpMode = OpMode; } }; #endif // ------------------------------------------------------------------------ /** @brief [] */ struct SRect : public SMALL_RECT { int Width(void) const { return Right - Left; } int Height(void) const { return Bottom - Top; } int X(void) const { return Left; } int Y(void) const { return Top; } int X1(void) const { return Right; } int Y1(void) const { return Bottom; } void Set(int x, int y, int x1, int y1) { Left = x; Top = y; Right = x1; Bottom = y1; } void Set(int x, int y) { Left = x; Top = y; Right = x; Bottom = y; } bool isEmpty(void) const { return Right - Left == 0 && Bottom - Top == 0; } bool isNull(void) const { return Right == 0 && Left == 0 && Bottom == 0 && Top == 0; } void Empty(void) { Left = Top = Right = Bottom = 0; } void Normalize(void) { if (Top > Bottom) { Swap(Bottom, Top); Swap(Left, Right); } if (Top == Bottom && Left > Right) Swap(Left, Right); } operator SMALL_RECT *() { return (SMALL_RECT *)this; } }; // ------------------------------------------------------------------------ /** @brief [] */ struct SaveConsoleTitle { WCHAR SaveTitle[FAR_MAX_TITLE]; BOOL NeedToRestore; int Usage; public: DWORD LastChange; public: SaveConsoleTitle(BOOL NeedToRestore = TRUE); ~SaveConsoleTitle(); static void Text(LPCSTR buff); void Set(LPCSTR Buff); void Using(void); void Restore(void); double Changed(void); }; // ------------------------------------------------------------------------ /** @brief [] */ struct SaveLastError { DWORD Error; public: SaveLastError(void) { Error = WINPORT(GetLastError)(); } ~SaveLastError() { WINPORT(SetLastError)(Error); } }; // FUtils extern int WINAPI CheckForKeyPressed(WORD *Codes, int NumCodes); #endif far2l-2.6.5~beta+ds/FARStdlib/src/000077500000000000000000000000001477431623700165455ustar00rootroot00000000000000far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Arg.cpp000066400000000000000000000030601477431623700210010ustar00rootroot00000000000000#include #include "fstdlib.h" static char *CT_Def_argv[] = {(char *)"", NULL}; static char **CT_argv = CT_Def_argv; static int CT_argc = 1; static BOOL CT_CaseSensitive; void WINAPI CTArgInit(int argc, char **argv, BOOL CaseSensitive) { CT_argc = argc; CT_argv = argv; CT_CaseSensitive = CaseSensitive; #if defined(__QNX__) if (strchr(argv[0], '/') == NULL) CT_argv[0] = strdup(MakePathName(GetCurDir(), argv[0]).Text()); #endif } char *WINAPI CTArgGet(int num) { return (num < 0 || num >= CT_argc) ? NULL : CT_argv[num]; } char *WINAPI CTArgGetArg(int num) { for (int n = 1; num >= 0 && n < CT_argc && CT_argv[n]; n++) if ((CT_argv[n][0] == '-' || CT_argv[n][0] == '/')) continue; else { if (!num) return CT_argv[n]; num--; } return NULL; } char *WINAPI CTArgGet(LPCSTR name) { int cn = StrColCount(name, ";"), n, i, len; LPCSTR m; for (n = 1; n < CT_argc && CT_argv[n]; n++) if ((CT_argv[n][0] == '-' || CT_argv[n][0] == '/')) for (i = 1; i <= cn; i++) { m = StrGetCol(name, i, ";"); if (StrCmp(CT_argv[n] + 1, m, len = (int)strlen(m), CT_CaseSensitive) == 0 && CT_argv[n][1 + len] == '=') return CT_argv[n] + 1 + len + 1; } return NULL; } BOOL WINAPI CTArgCheck(LPCSTR name) { int cn = StrColCount(name, ";"), n, i; LPCSTR m; for (n = 1; n < CT_argc && CT_argv[n]; n++) if ((CT_argv[n][0] == '-' || CT_argv[n][0] == '/')) for (i = 1; i <= cn; i++) { m = StrGetCol(name, i, ";"); if (StrCmp(CT_argv[n] + 1, m, -1, CT_CaseSensitive) == 0) return TRUE; } return FALSE; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_ClpS.cpp000066400000000000000000000014701477431623700211340ustar00rootroot00000000000000#include #include "fstdlib.h" BOOL WINAPI FP_CopyToClipboard(LPVOID Data, SIZE_T DataSize) { void *CData; BOOL rc = FALSE; if (!Data || !DataSize || !WINPORT(OpenClipboard)(NULL)) return FALSE; WINPORT(EmptyClipboard)(); if ((CData = WINPORT(ClipboardAlloc)(DataSize + 1)) != NULL) { memcpy(CData, Data, DataSize + 1); if (WINPORT(SetClipboardData)(CF_TEXT, CData)) { rc = TRUE; } else { WINPORT(ClipboardFree)(CData); } } WINPORT(CloseClipboard)(); return rc; } BOOL WINAPI FP_GetFromClipboard(LPVOID &Data, SIZE_T &DataSize) { void *CData; BOOL rc = FALSE; Data = NULL; if (!WINPORT(OpenClipboard)(NULL)) return FALSE; CData = WINPORT(GetClipboardData)(CF_TEXT); if (CData) { Data = strdup((char *)CData); rc = TRUE; } WINPORT(CloseClipboard)(); return rc; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Con.cpp000066400000000000000000000004761477431623700210170ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI FP_ConWidth(void) { CONSOLE_SCREEN_BUFFER_INFO ci; return WINPORT(GetConsoleScreenBufferInfo)(0, &ci) ? ci.dwSize.X : 0; } int WINAPI FP_ConHeight(void) { CONSOLE_SCREEN_BUFFER_INFO ci; return WINPORT(GetConsoleScreenBufferInfo)(0, &ci) ? ci.dwSize.Y : 0; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Dialog.cpp000066400000000000000000000024301477431623700214670ustar00rootroot00000000000000#include #include "fstdlib.h" //------------------------------------------------------------------------ FP_Dialog::FP_Dialog(HANDLE h) { Handle = h; LockCount = 0; } void FP_Dialog::Lock(bool v) { if (v == (LockCount != 0)) return; if (v) LockCount++; else LockCount--; FP_Info->SendDlgMessage(Handle, DM_ENABLEREDRAW, v ? FALSE : TRUE, 0); } void FP_Dialog::FullUnlock(void) { if (!LockCount) return; while (LockCount) Unlock(); } bool FP_Dialog::CursorPos(int num, int pos) const { COORD cp; cp.X = pos; cp.Y = 0; if (!FP_Info->SendDlgMessage(Handle, DM_SETCURSORPOS, num, (LONG_PTR)&cp)) return false; return true; } int FP_Dialog::SetText(int num, LPCSTR str, int sz) const { FarDialogItemData dd = {sz, (char *)str}; return (int)FP_Info->SendDlgMessage(Handle, DM_SETTEXT, num, (LONG_PTR)&dd); } LPCSTR FP_Dialog::GetText(int num) const { static char buff[FAR_MAX_DLGITEM]; FarDialogItemData dd = {sizeof(buff) - 1, buff}; buff[FP_Info->SendDlgMessage(Handle, DM_GETTEXT, num, (LONG_PTR)&dd)] = 0; return buff; } int FP_Dialog::GetText(int num, char *buff, int bSz) const { FarDialogItemData dd = {bSz, buff}; int rc; rc = (int)FP_Info->SendDlgMessage(Handle, DM_GETTEXT, num, (LONG_PTR)&dd); if (rc < bSz) buff[rc] = 0; return rc; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_FMsg.cpp000066400000000000000000000026251477431623700211320ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI FP_Message(unsigned int Flags, LPCSTR HelpTopic, LPCSTR *Items, int ItemsNumber, int ButtonsNumber, LPBOOL Delayed /*NULL*/) { LPCSTR litems[100]; int rc; static int CMsgWidth = 0; static int CMsgHeight = 0; // Check width and set repaint flag size_t width = 0; // If all lines in one (text) if (IS_FLAG(Flags, FMSG_ALLINONE)) { char *b = (char *)Items, *e; for (ItemsNumber = 0; (e = strchr(b, '\n')) != NULL; ItemsNumber++, b = e + 1) width = Max(width, (size_t)(e - b)); } else // Array of lines - check if lines are message id for (rc = 0; rc < (int)ARRAYSIZE(litems) && rc < ItemsNumber; rc++) { if (!FISMSG(Items[rc])) litems[rc] = FP_GetMsg(FGETID(Items[rc])); else litems[rc] = Items[rc]; width = Max(width, strlen(litems[rc])); } // Calc if message need to be redrawn with smaller dimensions if (!CMsgWidth || (int)width < CMsgWidth || !CMsgHeight || ItemsNumber < CMsgHeight) // Need restore bk if (!ButtonsNumber && // No buttons (Flags & FMSG_MB_MASK) == 0) // No FAR buttons FP_Screen::RestoreWithoutNotes(); rc = FP_Info->Message(FP_Info->ModuleNumber, Flags, HelpTopic, IS_FLAG(Flags, FMSG_ALLINONE) ? Items : litems, ItemsNumber, ButtonsNumber); if (Delayed) *Delayed = ButtonsNumber || (Flags & FMSG_MB_MASK) != 0; CMsgWidth = (int)width; CMsgHeight = ItemsNumber; return rc; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_FUtils.cpp000066400000000000000000000026531477431623700215050ustar00rootroot00000000000000#include #include "fstdlib.h" //------------------------------------------------------------------------ SaveConsoleTitle::SaveConsoleTitle(BOOL rest /*= TRUE*/) { Usage = rest; NeedToRestore = rest; GET_TIME(LastChange); WINPORT(GetConsoleTitle)(NULL, SaveTitle, ARRAYSIZE(SaveTitle)); Log(("TITLE: Save")); } SaveConsoleTitle::~SaveConsoleTitle() { Restore(); } void SaveConsoleTitle::Set(LPCSTR buff) { GET_TIME(LastChange); Text(buff); NeedToRestore = TRUE; } void SaveConsoleTitle::Text(LPCSTR buff) { Log(("TITLE: Set [%s]", buff)); /* char _buff[ 1024 ]; if(FP_WinVer->dwPlatformId != VER_PLATFORM_WIN32_NT) { OemToCharBuff(buff, _buff, sizeof(_buff)); buff = _buff; } */ WINPORT(SetConsoleTitle)(NULL, MB2Wide(buff).c_str()); } void SaveConsoleTitle::Restore(void) {} void SaveConsoleTitle::Using(void) { Usage++; Log(("TITLE: Use[%d]", Usage)); } double SaveConsoleTitle::Changed(void) { DWORD t; GET_TIME(t); return CMP_TIME(t, LastChange); } //------------------------------------------------------------------------ int WINAPI CheckForKeyPressed(WORD *Codes, int NumCodes) { WORD KeyCode = VK_ESCAPE; return WINPORT(CheckForKeyPress)(NULL, Codes, NumCodes, CFKP_KEEP_OTHER_EVENTS); } //------------------------------------------------------------------------ int WINAPI FP_Color(int tp) { return (int)FP_Info->AdvControl(FP_Info->ModuleNumber, ACTL_GETCOLOR, (void *)(INT_PTR)tp); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_INProc.cpp000066400000000000000000000032011477431623700214170ustar00rootroot00000000000000#include #include "fstdlib.h" /** @class FARINProc Used to mark nested, named code block. Designed for use as procedure Enter|Exit marker. Write to log name of procedure and given parameters at start and increment log indent level. Do not use directly. Use PROC macro instead, which defined differentry for debug and release. */ /** @def Log(v) Log(( )); Writes data to current log file indenting output by current indent level. */ /** @def PROC(v) PROC(( , )) Define procedure and write procedure header into current log file. Increase current log indent level. Indent level will be decreased after code block where PROC placed ends. */ /** @var FARINProc::Counter @brief Current global log file indent level. */ int FARINProc::Counter = 0; /** @brief Writes named data to log; Writes named data to log file and increment log indent level. @param Name name of indent block. Usually function name. @param Format printf-like format string for formatted output following parameters @return */ FARINProc::FARINProc(LPCSTR Name, LPCSTR Format, ...) : Name(Name) { va_list ap; char str[500]; sprintf(str, "%*c%s(", Counter * 2, ' ', Name); if (Format) { va_start(ap, Format); StrCat(str, MessageV(Format, ap), sizeof(str)); va_end(ap); } StrCat(str, ") {", sizeof(str)); FP_FILELog("%s", str); Counter++; } FARINProc::~FARINProc() { Counter--; FP_FILELog("%*c}<%s>", Counter * 2, ' ', Name); } void WINAPI FARINProc::Say(LPCSTR s, ...) { va_list ap; va_start(ap, s); FP_FILELog("%*c%s", Counter * 2, ' ', MessageV(s, ap)); va_end(ap); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Msg.cpp000066400000000000000000000034111477431623700210160ustar00rootroot00000000000000#include #include "fstdlib.h" /** @ingroup GetMsg @fn LPCSTR FP_GetMsg( int MsgId ) @brief Retrieve text message by it number. @param MsgId Index of message in LNG file. @return Text message from language file if parameter is message index or static empty string on error. */ /** @ingroup GetMsg @fn LPCSTR FP_GetMsg( LPCSTR Msg ) @brief Retrieve text message by it number. @param Msg Index of message in LNG file. Index can be converted to LPCSTR using FMSG parameter. You can set this parameter to real local string pointer. @return Text message from language file if parameter is message index or parameter if it real text string. */ /** @def FISMSG( v ) @brief Checks if string real string or emulated LNG value. Returns TRUE for real strings. */ /** @def FGETID( v ) @brief Returns FAR LNG number from emulated strings. ! Use it only after FISMSG returns FALSE. */ /** @def FMSG( v ) @brief Create LPCSTR data from parameter. Parameter may be a real string or index of messssage in language file. */ static LPCSTR WINAPI _FP_GetMsgINT(int MsgId) { CHK_INITED MsgId = Abs(MsgId); if (MsgId < FAR_MAX_LANGID) return FP_Info->GetMsg(FP_Info->ModuleNumber, MsgId); else return NULL; } static LPCSTR WINAPI _FP_GetMsgSTR(LPCSTR String) { LPCSTR res; CHK_INITED if (!String) return ""; if (!FISMSG(String)) res = FP_Info->GetMsg(FP_Info->ModuleNumber, FGETID(String)); else res = String; return res; } /** @var FP_GetMsgINT @brief Callback function for get language message specified by WORD identifier. */ FP_GetMsgINT_t FP_GetMsgINT = _FP_GetMsgINT; /** @var FP_GetMsgSTR @ingroup GetMsg @brief Callback function for get language message specified by LPCSTR identifier. */ FP_GetMsgSTR_t FP_GetMsgSTR = _FP_GetMsgSTR; far2l-2.6.5~beta+ds/FARStdlib/src/fstd_OEM.cpp000066400000000000000000000000531477431623700207070ustar00rootroot00000000000000#include #include "fstdlib.h" far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Patt.cpp000066400000000000000000000007671477431623700212130ustar00rootroot00000000000000#include #include "fstdlib.h" BOOL WINAPI FP_InPattern(LPCSTR patt, LPCSTR nm) { char c; size_t n; char str[MAX_PATH]; if (!patt || !patt[0] || !nm) return FALSE; do { for (n = 0; (c = *patt) != 0 && n < sizeof(str) - 1 && *patt != ',' && *patt != ';'; patt++) str[n++] = *patt; str[n] = 0; if (*patt) patt++; if (strchr(str, '.') == NULL) StrCat(str, ".*", sizeof(str)); if (FP_Info->CmpName(str, nm, TRUE)) return TRUE; } while (c); return FALSE; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Reg.cpp000066400000000000000000000136741477431623700210210ustar00rootroot00000000000000#include #include "fstdlib.h" BOOL WINAPI FP_CheckRegKey(const char *Key) { HKEY hKey = FP_OpenRegKey(Key); if (hKey != NULL) WINPORT(RegCloseKey)(hKey); return hKey != NULL; } HKEY WINAPI FP_CreateRegKey(const char *Key) { HKEY hKey; DWORD Disposition; char name[FAR_MAX_REG]; CHK_INITED if (Key && *Key) sprintf(name, "%s" "/" "%s", FP_PluginRootKey, Key); else sprintf(name, "%s", FP_PluginRootKey); // WINPORT(SetLastError)(WINPORT(RegOpenKey)(HKEY_CURRENT_USER, MB2Wide(name).c_str(),&hKey)); // if(WINPORT(GetLastError)() == ERROR_SUCCESS) // return hKey; hKey = NULL; WINPORT(SetLastError) (WINPORT(RegCreateKeyEx)(HKEY_CURRENT_USER, MB2Wide(name).c_str(), 0, NULL, 0, KEY_WRITE, NULL, &hKey, &Disposition)); return hKey; } BOOL WINAPI FP_DeleteRegKey(const char *Key) { char name[FAR_MAX_REG]; CHK_INITED if (!Key || !Key[0]) return FALSE; sprintf(name, "%s" "/" "%s", FP_PluginRootKey, Key); return FP_DeleteRegKeyFull(name); } BOOL WINAPI FP_DeleteRegKeyFull(const char *Key) { WINPORT(SetLastError)(WINPORT(RegDeleteKey)(HKEY_CURRENT_USER, MB2Wide(Key).c_str())); return WINPORT(GetLastError)() == ERROR_SUCCESS; } BOOL WINAPI FP_DeleteRegKeyAll(LPCSTR hParentKey, LPCSTR Key) { char name[FAR_MAX_REG]; if (!Key || !Key[0]) return FALSE; if (!hParentKey || !hParentKey[0]) return FP_DeleteRegKeyAll(HKEY_CURRENT_USER, Key); else { sprintf(name, "%s" "/" "%s", hParentKey, Key); return FP_DeleteRegKeyAll(HKEY_CURRENT_USER, name); } } BOOL WINAPI FP_DeleteRegKeyAll(LPCSTR Key) { char name[FAR_MAX_REG]; CHK_INITED if (!Key || !Key[0]) return FALSE; sprintf(name, "%s" "/" "%s", FP_PluginRootKey, Key); return FP_DeleteRegKeyAll(HKEY_CURRENT_USER, name); } BOOL WINAPI FP_DeleteRegKeyAll(HKEY hParentKey, LPCSTR szKey) { // PROC(( "FP_DeleteRegKeyAll","%s",szKey )) WCHAR szSubKey[FAR_MAX_REG]; HKEY hKey = NULL; LONG nRes; FILETIME tTime; DWORD nSize; do { nRes = WINPORT(RegOpenKeyEx)(hParentKey, MB2Wide(szKey).c_str(), 0, KEY_ENUMERATE_SUB_KEYS | KEY_READ | KEY_WRITE, &hKey); WINPORT(SetLastError)(nRes); // Log(( "open [%s] rc: %s",szKey,__WINError() )); if (nRes != ERROR_SUCCESS) break; for (nSize = sizeof(szSubKey); 1; nSize = sizeof(szSubKey)) { szSubKey[0] = 0; nRes = WINPORT(RegEnumKeyEx)(hKey, 0, szSubKey, &nSize, 0, NULL, NULL, &tTime); WINPORT(SetLastError)(nRes); // Log(( "enum [%s] rc: %s",szSubKey,__WINError() )); if (nRes == ERROR_NO_MORE_ITEMS) { nRes = ERROR_SUCCESS; break; } if (nRes != ERROR_SUCCESS) break; if (!FP_DeleteRegKeyAll(hKey, Wide2MB(szSubKey).c_str())) { nRes = WINPORT(GetLastError)(); break; } } if (nRes == ERROR_SUCCESS) { nRes = WINPORT(RegDeleteKey)(hParentKey, MB2Wide(szKey).c_str()); WINPORT(SetLastError)(nRes); // Log(( "delete rc: %s", __WINError() )); } } while (0); if (hKey) WINPORT(RegCloseKey)(hKey); return nRes == ERROR_SUCCESS; } BYTE *WINAPI FP_GetRegKey(const char *Key, const char *ValueName, BYTE *ValueData, const BYTE *Default, DWORD DataSize) { DWORD Type, sz = DataSize; int ExitCode = -1; // not ERROR_SUCCESS, not ERROR_MORE_DATA Assert(ValueData); HKEY hKey = FP_OpenRegKey(Key); if (hKey) { ExitCode = WINPORT(RegQueryValueEx)(hKey, MB2Wide(ValueName).c_str(), nullptr, &Type, ValueData, &sz); WINPORT(RegCloseKey)(hKey); } if (!hKey || (ExitCode != ERROR_SUCCESS && ExitCode != ERROR_MORE_DATA)) { if (Default) memmove(ValueData, Default, DataSize); else memset(ValueData, 0, DataSize); } return ValueData; } char *WINAPI FP_GetRegKey(const char *Key, const char *ValueName, char *ValueData, LPCSTR Default, DWORD DataSize) { DWORD Type, sz = DataSize; int ExitCode = -1; // not ERROR_SUCCESS, not ERROR_MORE_DATA Assert(ValueData); HKEY hKey = FP_OpenRegKey(Key); if (hKey) { ExitCode = WINPORT( RegQueryValueEx)(hKey, MB2Wide(ValueName).c_str(), nullptr, &Type, (LPBYTE)ValueData, &sz); WINPORT(RegCloseKey)(hKey); } if (!hKey || (ExitCode != ERROR_SUCCESS && ExitCode != ERROR_MORE_DATA)) { if (Default) StrCpy(ValueData, Default, DataSize); else memset(ValueData, 0, DataSize); } return ValueData; } int WINAPI FP_GetRegKey(const char *Key, const char *ValueName, DWORD Default) { int data; DWORD Type, Size = sizeof(data); HKEY hKey = FP_OpenRegKey(Key); if (hKey) { if (WINPORT(RegQueryValueEx)(hKey, MB2Wide(ValueName).c_str(), 0, &Type, (BYTE *)&data, &Size) != ERROR_SUCCESS) data = Default; WINPORT(RegCloseKey)(hKey); } else data = Default; return data; } HKEY WINAPI FP_OpenRegKey(const char *Key) { HKEY hKey; char name[FAR_MAX_REG]; CHK_INITED if (Key && *Key) snprintf(name, sizeof(name), "%s" "/" "%s", FP_PluginRootKey, Key); else snprintf(name, sizeof(name), "%s", FP_PluginRootKey); if (WINPORT(RegOpenKeyEx)(HKEY_CURRENT_USER, MB2Wide(name).c_str(), 0, KEY_ENUMERATE_SUB_KEYS | KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS) { return NULL; } return hKey; } BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, const BYTE *ValueData, DWORD ValueSize) { HKEY hKey = FP_CreateRegKey(Key); BOOL rc = hKey && WINPORT(RegSetValueEx)(hKey, MB2Wide(ValueName).c_str(), 0, REG_BINARY, ValueData, ValueSize) == ERROR_SUCCESS; WINPORT(RegCloseKey)(hKey); return rc; } BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, LPCSTR ValueData) { HKEY hKey = FP_CreateRegKey(Key); BOOL rc = hKey && WINPORT(RegSetValueEx)(hKey, MB2Wide(ValueName).c_str(), 0, REG_SZ_MB, (const BYTE *)ValueData, (int)strlen(ValueData) + 1) == ERROR_SUCCESS; WINPORT(RegCloseKey)(hKey); return rc; } BOOL WINAPI FP_SetRegKey(LPCSTR Key, LPCSTR ValueName, DWORD ValueData) { HKEY hKey = FP_CreateRegKey(Key); BOOL rc = hKey && WINPORT(RegSetValueEx)(hKey, MB2Wide(ValueName).c_str(), 0, REG_DWORD, (BYTE *)&ValueData, sizeof(ValueData)) == ERROR_SUCCESS; WINPORT(RegCloseKey)(hKey); return rc; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_SCol.cpp000066400000000000000000000012031477431623700211250ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI StrColCount(LPCSTR str, LPCSTR seps) { int res = 1; if (!str || !seps || !str[0] || !seps[0]) return 0; for (int n = 0; str[n]; n++) if (strchr((char *)seps, str[n]) != NULL) res++; return res; } LPCSTR WINAPI StrGetCol(LPCSTR str, int number, LPCSTR seps) { static char resStr[MAX_PATH]; int res; int num; for (res = 1; *str && res < number; str++) if (strchr((char *)seps, *str) != NULL) res++; resStr[num = 0] = 0; if (res == number) for (; *str && strchr((char *)seps, *str) == NULL; str++) resStr[num++] = *str; resStr[num] = 0; return resStr; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_SText.cpp000066400000000000000000000023651477431623700213460ustar00rootroot00000000000000#include #include "fstdlib.h" char *WINAPI Str2Text(LPCSTR name, char *Buff, DWORD BuffSz) { char *buff = Buff; for (; BuffSz && *name; BuffSz--, name++, Buff++) switch (*name) { case '\\': *Buff++ = '\\'; *Buff = '\\'; break; case '\t': *Buff++ = '\\'; *Buff = 't'; break; case '\n': *Buff++ = '\\'; *Buff = 'n'; break; case '\r': *Buff++ = '\\'; *Buff = 'r'; break; case '\b': *Buff++ = '\\'; *Buff = 'b'; break; case '\'': *Buff++ = '\\'; *Buff = '\''; break; case '\"': *Buff++ = '\\'; *Buff = '\"'; break; default: *Buff = *name; } *Buff = 0; return buff; } char *WINAPI Text2Str(LPCSTR name, char *Buff, DWORD BuffSz) { char *buff = Buff; for (; BuffSz && *name; BuffSz--, name++, Buff++) if (*name == '\\') { name++; switch (*name) { case '\\': *Buff = '\\'; break; case 't': *Buff = '\t'; break; case 'n': *Buff = '\n'; break; case 'r': *Buff = '\r'; break; case 'b': *Buff = '\b'; break; case '\'': *Buff = '\''; break; case '\"': *Buff = '\"'; break; } } else *Buff = *name; *Buff = 0; return buff; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_String.cpp000066400000000000000000000127751477431623700215530ustar00rootroot00000000000000#include #include "fstdlib.h" /************************************ String ************************************/ String::~String() { if (str) { free(str); str = NULL; } } String::String(void) { BeginSet(); } String::String(const String &s) { BeginSet(s.Length()); Alloc(s.c_str()); } String::String(size_t def_size) { BeginSet(def_size); } String::String(LPCSTR fmt) { if (!fmt || !fmt[0]) BeginSet(); else { int sz = (int)strlen(fmt); BeginSet(Max(DEF_STR_ALLOC, sz + 1)); StrCpy(str, fmt, maxchar); len = sz; } } int String::printf(LPCSTR fmt, ...) { va_list a; int res; va_start(a, fmt); res = vprintf(fmt, a); va_end(a); return res; } int String::vprintf(LPCSTR fmt, va_list list) { char tmp; va_list list_copy; va_copy(list_copy, list); int sz = vsnprintf(&tmp, 0, fmt, list_copy); Alloc(sz + 1); va_end(list_copy); return len = vsnprintf(str, maxchar, fmt, list); } void String::BeginSet(size_t sz) { str = (char *)malloc(maxchar = (int)Max((size_t)DEF_STR_ALLOC, sz)); str[len = 0] = 0; } LPCSTR String::Alloc(LPCSTR s, int maxLen) { if (maxLen == -1) maxLen = (int)strlen(s); Alloc(maxLen + 1); if (!s || !maxLen) { str[len = 0] = 0; return c_str(); } strncpy(str, s, maxLen); str[maxLen] = 0; len = (int)strlen(str); return c_str(); } LPCSTR String::Alloc(int t) { if (t < maxchar) return str; str = (char *)realloc(str, maxchar = Max(DEF_STR_ALLOC, t)); return str; } String &String::Add(const String &s) { Alloc(Length() + s.Length() + 1); strcpy(str + Length(), s.c_str()); len+= s.Length(); return *this; } String &String::Add(LPCSTR s) { int slen = (int)strlen(s); if (slen) { Alloc(Length() + slen + 1); strcpy(str + Length(), s); len+= slen; } return *this; } String &String::Add(LPCSTR s, int from, int to /*-1*/) { if (to <= from) return *this; if (to == -1) return Add(s + from); Alloc(len + (to - from + 1)); StrCpy(str + len, s + from, to - from); len = (int)strlen(str); return *this; } String &String::Add(char ch) { if (ch == 0) return *this; if (len + 1 >= maxchar) Alloc(len + 10); str[len++] = ch; str[len] = 0; return *this; } void String::cat(LPCSTR s, ...) { va_list a; va_start(a, s); vcat(s, a); va_end(a); } void String::vcat(LPCSTR s, va_list a) { if (!s || !s[0]) return; va_list a_copy; va_copy(a, a_copy); int slen = vsnprintf(nullptr, 0, s, a_copy); va_end(a_copy); if (!slen) return; int clen = Length(); if (!Alloc(clen + slen + 1)) return; vsprintf(str + clen, s, a); len+= slen; } void String::InsCharPos(int pos, char ch) { if (!str) pos = 0; pos = Max(Min(pos, len), 0); Alloc(Length() + 1); if (pos < len) memmove(str + pos + 1, str + pos, len - pos); str[pos] = ch; str[++len] = 0; } void String::Del(int pos, int cn) { if (!cn || pos >= len) return; cn = Min(len - pos, cn); memmove(str + pos, str + pos + cn, len - pos - cn); len-= cn; str[len] = 0; } void String::DelChars(char ch) { if (str) { for (int n = 0; str[n]; n++) if (str[n] == ch) { memmove(str + n, str + n + 1, len - n); len--; } str[len] = 0; } } int String::RChr(char ch, int pos) const { if (!str || !str[0]) return -1; if (pos == -1) pos = Length() - 1; for (pos = Max(0, Min(Length(), pos)); pos && str[pos] != ch; pos--) ; return pos; } char String::SetChar(int num, char ch) { if (!str || num < 0 || num > maxchar) return 0; str[num] = ch; if (ch && num == len) str[++len] = 0; else if (!ch && num < len) len = (int)strlen(str); return ch; } int String::Chr(LPCSTR ch, int pos) const { if (pos >= len) return -1; for (int n = pos; n < len; n++) if (strchr(ch, str[n]) != NULL) return n; return -1; } int String::Chr(char ch, int pos) const { return StrPosChr(c_str(), ch, pos); } int String::Str(LPCSTR s, int pos) const { return StrPosStr(c_str(), s, pos); } String &String::operator=(const String &s) { Alloc(s.c_str()); return *this; } String &String::operator=(LPCSTR s) { Alloc(s); return *this; } BOOL String::operator!=(const String &s) const { return len != s.len || !Cmp(s.c_str()); } BOOL String::operator!=(LPCSTR s) const { return !Cmp(s); } BOOL String::operator==(const String &s) const { return len == s.len && Cmp(s.c_str()); } BOOL String::operator==(LPCSTR s) const { return Cmp(s); } char String::operator[](int num) const { return (num >= 0 && num <= len && str) ? str[num] : '\0'; } void String::SetLength(int sz) { if (sz >= 0 && sz < maxchar) { len = sz; str[sz] = 0; } } LPCSTR String::Set(LPCSTR s, int from, int to) { if (to == -1) { Alloc(s + from); return c_str(); } else if (to <= from) return c_str(); Alloc(to - from + 1); StrCpy(str, s + from, to - from + 1); len = (int)strlen(str); return c_str(); } BOOL String::Cmp(LPCSTR s, int count, BOOL isCase) const { if (!s) return FALSE; if (!s[0]) return str[0] == 0; if (count < 0) return strcmp(c_str(), s) == 0; else return StrCmp(c_str(), s, count, isCase) == 0; } void String::LTrim(char ch) { int n; if (len == 0) return; for (n = 0; str[n] && str[n] == ch; n++) ; if (n) memmove(str, str + n, len - n); len-= n; str[len] = 0; } void String::RTrim(char ch) { int n; if (len == 0) return; for (n = len - 1; n >= 0 && str[n] == ch; len--, n--) str[n] = 0; } void String::Trim(char ch) { int n; if (len == 0) return; for (n = 0; str[n] && str[n] == ch; n++) ; if (n) memmove(str, str + n, len - n); len-= n; str[len] = 0; for (n = len - 1; n >= 0 && str[n] == ch; len--, n--) str[n] = 0; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_Utils.cpp000066400000000000000000000031631477431623700213740ustar00rootroot00000000000000#include #include "fstdlib.h" LPSTR WINAPI AddLastSlash(char *path, char Slash) { size_t len; if ((len = strlen(path)) != 0 && path[len - 1] != Slash) { path[len] = Slash; path[len + 1] = 0; } return path; } LPSTR WINAPI DelLastSlash(char *path, char Slash) { size_t len; if (path && path[0] && path[len = (strlen(path) - 1)] == Slash) path[len] = 0; return path; } LPCSTR WINAPI FPath(LPCSTR nm, char Slash) { static char str[MAX_PATH]; char *m; StrCpy(str, nm, sizeof(str)); m = strrchr(str, Slash); if (m) *m = 0; else str[0] = 0; return AddLastSlash(str); } LPCSTR WINAPI FName(LPCSTR nm, char Slash) { static char str[MAX_PATH]; LPCSTR m = strrchr(nm, Slash); if (!m) m = nm; else m++; StrCpy(str, m, sizeof(str)); return str; } LPCSTR WINAPI FExtOnly(LPCSTR nm, char Slash) { LPCSTR m = strrchr(nm, Slash), ext; if (!m) m = nm; ext = strrchr(m, '.'); if (ext) return ext + 1; else return ""; } /* Create CPS value string The digit always 3+1+3+1 characters length (8) Digit right alignmented, filled with ' ' at left */ LPCSTR WINAPI FCps(char *buff, double val) { char Letter; char str[50]; LPCSTR _buff = buff; // 1M if (val >= 1000000.) { Letter = 'M'; val/= 1000000; } else // 1K if (val >= 1000.) { Letter = 'K'; val/= 1000; } else { //<1K Letter = 'b'; } if (Letter == 'b') sprintf(str, "%db", (int)val); else sprintf(str, "%3.3lf%c", val, Letter); int sz; for (sz = 8 - (int)strlen(str); sz > 0; buff++, sz--) *buff = ' '; for (sz = 0; str[sz]; buff++, sz++) *buff = str[sz]; *buff = 0; return _buff; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_asrt.cpp000066400000000000000000000003141477431623700212400ustar00rootroot00000000000000#include #include "fstdlib.h" void _cdecl __WinAbort(LPCSTR msg, ...) { va_list a; if (!msg) exit(1); // Message va_start(a, msg); vfprintf(stderr, msg, a); va_end(a); exit(1); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_crc32.cpp000066400000000000000000000037561477431623700212200ustar00rootroot00000000000000#include #include "fstdlib.h" /******************************************************************* Crc32 Generate 32bit CRC for buffer. Algorithm got from sources of ZLib. *******************************************************************/ static int crc_table_empty = 1; static DWORD crc_table[256]; static void make_crc_table(void) { DWORD c, n; int k; DWORD poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static const BYTE p[] = {0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26}; /* make exclusive-or pattern from polynomial (0xedb88320L) */ poly = 0L; for (n = 0; n < sizeof(p) / sizeof(BYTE); n++) poly|= 1L << (31 - p[n]); for (n = 0; n < 256; n++) { c = n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[n] = c; } crc_table_empty = 0; } /* ========================================================================= */ #define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); #define DO2(buf) \ DO1(buf); \ DO1(buf); #define DO4(buf) \ DO2(buf); \ DO2(buf); #define DO8(buf) \ DO4(buf); \ DO4(buf); DWORD WINAPI Crc32(DWORD crc, const BYTE *buf, DWORD len) { if (buf == NULL) return 0L; if (crc_table_empty) make_crc_table(); crc = crc ^ 0xffffffffL; while (len >= 8) { DO8(buf); len-= 8; } if (len) do { DO1(buf); } while (--len); return crc ^ 0xffffffffL; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_err.cpp000066400000000000000000000011041477431623700210550ustar00rootroot00000000000000#include #include "fstdlib.h" const char *_cdecl __WINError(void) { return "Unknown error"; #if 0 static char *WinEBuff = NULL; DWORD err = WINPORT(GetLastError)(); if(WinEBuff) LocalFree(WinEBuff); if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &WinEBuff,0,NULL) == 0) return "Unknown error"; char *m; if((m=strchr(WinEBuff,'\n')) != 0) *m = 0; if((m=strchr(WinEBuff,'\r')) != 0) *m = 0; CharToOem(WinEBuff, WinEBuff); return WinEBuff; #endif } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSCAT.cpp000066400000000000000000000011571477431623700213640ustar00rootroot00000000000000#include #include "fstdlib.h" /* static void WINAPI _strncat( char *dest,const char *src,int dest_sz ) { if ( !dest || !dest[0] || !src || !src[0] ) return; int len = (int)strlen(dest); if ( len >= dest_sz ) return; for( dest += len; *src && len < dest_sz; len++ ) *dest++ = *src++; *dest = 0; } */ char *WINAPI StrCat(char *dest, LPCSTR src, int dest_sz) { if (!dest) return NULL; if (dest_sz == 0) return dest; if (!src) { *dest = 0; return dest; } if (dest_sz != -1) { dest_sz--; strncat(dest, src, dest_sz); dest[dest_sz] = 0; } else strcat(dest, src); return dest; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSCHC.cpp000066400000000000000000000003431477431623700213460ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI strchrCount(const char *str, char ch, int maxlen) { int cn = 0; for (int n = 0; str[n] && (maxlen == -1 || n < maxlen); n++) if (str[n] == ch) cn++; return cn; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSCMP.cpp000066400000000000000000000012251477431623700213700ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI StrCmp(const char *str, const char *str1, int maxlen, BOOL isCaseSens) { int n, diff; if (!str) return (str1 == NULL) ? 0 : (-1); if (!str1) return (str == NULL) ? 0 : 1; if (!isCaseSens) { for (n = 0; str[n] && str1[n] && (maxlen == -1 || n < maxlen); n++) { diff = tolower(str[n]) - tolower(str1[n]); if (diff) return diff; } diff = tolower(str[n]) - tolower(str1[n]); } else { for (n = 0; str[n] && str1[n] && (maxlen == -1 || n < maxlen); n++) if ((diff = (str[n] - str1[n])) != 0) return diff; diff = str[n] - str1[n]; } return (n == maxlen) ? 0 : diff; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSCPY.cpp000066400000000000000000000005321477431623700214040ustar00rootroot00000000000000#include #include "fstdlib.h" char *WINAPI StrCpy(char *dest, LPCSTR src, int dest_sz) { if (dest == NULL) return NULL; if (dest_sz == 0) return dest; if (!src) { *dest = 0; return dest; } if (dest_sz != -1) { strncpy(dest, src, dest_sz - 1); dest[dest_sz - 1] = 0; } else strcpy(dest, src); return dest; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSNCH.cpp000066400000000000000000000003251477431623700213610ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI StrNChr(const char *str, char ch, int maxlen) { for (int n = 0; str[n] && (maxlen == -1 || n < maxlen); n++) if (str[n] == ch) return n; return -1; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSPCH.cpp000066400000000000000000000003651477431623700213670ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI StrPosChr(const char *str, char ch, int pos) { if (!str || pos < 0 || pos >= (int)strlen(str)) return -1; for (int n = pos; str[n]; n++) if (str[n] == ch) return n; return -1; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exSPS.cpp000066400000000000000000000004531477431623700212750ustar00rootroot00000000000000#include #include "fstdlib.h" int WINAPI StrPosStr(const char *str, const char *s, int pos) { if (!str || !s || !*s || pos < 0 || pos >= (int)strlen(str)) return -1; for (int l = (int)strlen(s), n = pos; str[n]; n++) if (StrNCmp(str + n, s, l) == 0) return n; return -1; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_exit.cpp000066400000000000000000000004301477431623700212370ustar00rootroot00000000000000#include #include "fstdlib.h" static AbortProc CTAbortProc = NULL; AbortProc WINAPI AtExit(AbortProc p) { AbortProc old = CTAbortProc; CTAbortProc = p; return old; } void WINAPI CallAtExit(void) { if (CTAbortProc) { CTAbortProc(); CTAbortProc = NULL; } } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_ilist.cpp000066400000000000000000000062031477431623700214160ustar00rootroot00000000000000#include #include "fstdlib.h" //--------------------------------------------------------------------------------- FP_ItemList::FP_ItemList(BOOL NeedToDelete) { needToDelete = NeedToDelete; List = NULL; ItemsCount = 0; MaxCount = 0; } BOOL FP_ItemList::Realloc(int NewSize) { if (!NewSize) return FALSE; if (NewSize <= MaxCount) return TRUE; MaxCount = NewSize; if (!List) List = (PluginPanelItem *)malloc(sizeof(PluginPanelItem) * MaxCount); else List = (PluginPanelItem *)realloc(List, sizeof(PluginPanelItem) * MaxCount); if (!List) { ItemsCount = 0; MaxCount = 0; return FALSE; } return TRUE; } PluginPanelItem *FP_ItemList::Add(const PluginPanelItem *pi, int icn) { if (!Realloc(ItemsCount + icn)) return NULL; PluginPanelItem *p = List + ItemsCount; //!! Do not use Item(ItemsCount) because we need point after last element Copy(p, pi, icn); ItemsCount+= icn; return p; } void FP_ItemList::Clear(void) { if (needToDelete) Free(List, ItemsCount); ItemsCount = 0; MaxCount = 0; List = NULL; } bool IsBadReadPtr(const void *p, size_t l) { ULONG_PTR v = (ULONG_PTR)p; return v < 0x10000; } void FP_ItemList::Copy(PluginPanelItem *dest, const PluginPanelItem *src, int cn) { if (!cn) return; memmove(dest, src, sizeof(*dest) * cn); for (; cn; cn--, src++, dest++) { // User data if (IS_FLAG(src->Flags, PPIF_USERDATA)) { DWORD sz = (src->UserData && !IsBadReadPtr((void *)src->UserData, sizeof(DWORD))) ? (*((DWORD *)src->UserData)) : 0; if (sz && !IsBadReadPtr((void *)src->UserData, sz)) { dest->UserData = (DWORD_PTR)malloc(sz + 1); memmove((char *)dest->UserData, (char *)src->UserData, sz); } else { dest->UserData = 0; CLR_FLAG(dest->Flags, PPIF_USERDATA); } } // CustomColumn if (src->CustomColumnNumber) { dest->CustomColumnData = (LPSTR *)malloc(sizeof(LPSTR *) * src->CustomColumnNumber); for (int n = 0; n < src->CustomColumnNumber; n++) { dest->CustomColumnData[n] = strdup(src->CustomColumnData[n]); } } // Description if (src->Description) dest->Description = strdup(src->Description); // Owner if (src->Owner) dest->Owner = strdup(src->Owner); // Additionals if (FPIL_ADDEXIST(src)) { DWORD sz = FPIL_ADDSIZE(src); LPVOID ptr = malloc(sz); if (ptr) { memmove(ptr, FPIL_ADDDATA(src), sz); FPIL_ADDSET(dest, sz, ptr); } else FPIL_ADDSET(dest, 0, NULL); } } } void FP_ItemList::Free(PluginPanelItem *List, int count) { PluginPanelItem *p = List; for (int i = 0; i < count; i++, List++) { // UserData if (IS_FLAG(List->Flags, PPIF_USERDATA)) { free((char *)List->UserData); List->UserData = 0; } // CustomColumn for (int n = 0; n < List->CustomColumnNumber; n++) free(List->CustomColumnData[n]); if (List->CustomColumnData) free(List->CustomColumnData); // Description if (List->Description) free(List->Description), List->Description = NULL; // Owner if (List->Owner) free(List->Owner), List->Owner = NULL; // Additionals if (FPIL_ADDEXIST(List)) { free(FPIL_ADDDATA(List)); List->Reserved[0] = 0; List->Reserved[1] = 0; } } free(p); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_log.cpp000066400000000000000000000036031477431623700210540ustar00rootroot00000000000000#include #include "fstdlib.h" #include //------------------------------------------------------------------------ //[fstd_mklog.cpp] extern BOOL WINAPI LOGInit(void); std::recursive_mutex PLOG_cs; /** @brief Length of text with std error written to log file. Length of left part log string with description of __WINError() in moment of write log call. Default value 30 characters. Set this value to 0 to disable write error description. */ int FP_LogErrorStringLength = 30; //------------------------------------------------------------------------ void WINAPI FP_FILELog(LPCSTR msg, ...) { FILE *f; BOOL first; va_list argptr; char str[3000], *m; DWORD err = WINPORT(GetLastError)(); if (!msg) return; first = LOGInit(); PLOG_cs.lock(); m = (char *)FP_GetLogFullFileName(); f = (m && *m) ? fopen(m, first ? "w" : "a") : NULL; if (f) { // Time static SYSTEMTIME stOld = {0}; SYSTEMTIME st; WINPORT(GetLocalTime)(&st); fprintf(f, "%4d.%02d.%02d %02d:%02d:%02d:%04d ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); if (!stOld.wYear) fprintf(f, "---- "); else fprintf(f, "%04d ", (st.wSecond - stOld.wSecond) * 1000 + (st.wMilliseconds - stOld.wMilliseconds)); stOld = st; // Error if (FP_LogErrorStringLength) { WINPORT(SetLastError)(err); StrCpy(str, __WINError(), FP_LogErrorStringLength); if ((m = strchr(str, '\n')) != NULL) *m = 0; if ((m = strchr(str, '\r')) != NULL) *m = 0; fprintf(f, "%-*s->", FP_LogErrorStringLength, str); } else fprintf(f, "->"); // Message StrCpy(str, msg, sizeof(str)); if ((m = strchr(str, '\n')) != NULL) *m = 0; if ((m = strchr(str, '\r')) != NULL) *m = 0; va_start(argptr, msg); vfprintf(f, str, argptr); va_end(argptr); // EOL fprintf(f, "\n"); fclose(f); } PLOG_cs.unlock(); WINPORT(SetLastError)(err); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_menu.cpp000066400000000000000000000021171477431623700212360ustar00rootroot00000000000000#include #include "fstdlib.h" //--------------------------------------------------------------------------------- void FMenuItem::Assign(LPCSTR txt, bool sel, char ch) { if (txt) { txt = FP_GetMsg(txt); StrCpy(Text, txt, sizeof(Text)); } Separator = txt == NULL || txt[0] == 0; Selected = sel; Checked = ch; } void FMenuItem::Assign(const FMenuItem &p) { memcpy(this, &p, sizeof(*this)); } void FMenuItem::Assign(void) { memset(this, 0, sizeof(*this)); Separator = 1; } //--------------------------------------------------------------------------------- void FMenuItemEx::Assign(LPCSTR txt, bool sel, char ch) { memset(this, 0, sizeof(*this)); if (txt) { txt = FP_GetMsg(txt); StrCpy(Text.Text, txt, sizeof(Text.Text)); } if (txt == NULL || txt[0] == 0) SET_FLAG(Flags, MIF_SEPARATOR); if (sel) SET_FLAG(Flags, MIF_SELECTED); if (ch) SET_FLAG(Flags, MIF_CHECKED); } void FMenuItemEx::Assign(const FMenuItemEx &p) { memcpy(this, &p, sizeof(*this)); } void FMenuItemEx::Assign(void) { memset(this, 0, sizeof(*this)); SET_FLAG(Flags, MIF_SEPARATOR); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_mesg.cpp000066400000000000000000000011341477431623700212230ustar00rootroot00000000000000#include #include "fstdlib.h" #define MSG_BUFFSIZE 10000 static char *messbuff = NULL; static AbortProc ExitProc; static void _cdecl MSG_DelBuff(void) { if (messbuff) { delete[] messbuff; messbuff = NULL; } if (ExitProc) ExitProc(); } LPCSTR _cdecl Message(LPCSTR patt, ...) { va_list a; LPCSTR m; va_start(a, patt); m = MessageV(patt, a); va_end(a); return m; } LPCSTR WINAPI MessageV(LPCSTR patt, va_list a) { if (!messbuff) { ExitProc = AtExit(MSG_DelBuff); messbuff = new char[MSG_BUFFSIZE]; } vsnprintf(messbuff, MSG_BUFFSIZE, patt, a); return messbuff; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_mklog.cpp000066400000000000000000000012121477431623700213760ustar00rootroot00000000000000#include #include "fstdlib.h" // DATA BOOL WINAPI LOGInit(void) { static BOOL inited = FALSE; if (inited) return FALSE; inited = TRUE; #if defined(__QNX__) setbuf(stdout, NULL); #endif return TRUE; } LPCSTR WINAPI FP_GetLogFullFileName(void) { static char str[MAX_PATH] = ""; LPCSTR m; // char *tmp; if (!str[0]) { m = FP_GetPluginLogName(); if (!m || !m[0]) return ""; // str[ GetModuleFileName(FP_HModule,str,sizeof(str))] = 0; strcpy(str, "/var/log/"); // tmp = strrchr(str,'/'); // if(tmp) // { // tmp[1] = 0; strcat(str, m); // } // else // strcpy(str,m); } return str; } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_per.cpp000066400000000000000000000016101477431623700210550ustar00rootroot00000000000000#include #include "fstdlib.h" struct PRPeriod { DWORD b, e; DWORD Period; DWORD LastDiff; }; /** @brief Create time period. @param ms Period length. @return handle of new period or NULL on error. */ HANDLE WINAPI FP_PeriodCreate(DWORD ms) { PRPeriod *p = new PRPeriod; GET_TIME(p->b); p->Period = ms; return (HANDLE)p; } void WINAPI FP_PeriodDestroy(HANDLE &_p) { delete ((PRPeriod *)_p); _p = NULL; } BOOL WINAPI FP_PeriodEnd(HANDLE _p) { PRPeriod *p = (PRPeriod *)_p; BOOL res; if (!p) return FALSE; GET_TIME(p->e); p->LastDiff = (DWORD)(CMP_TIME(p->e, p->b) * 1000.); res = p->LastDiff >= p->Period; if (res) p->b = p->e; return res; } DWORD WINAPI FP_PeriodTime(HANDLE _p) { PRPeriod *p = (PRPeriod *)_p; if (!p) return 0; return p->LastDiff; } void WINAPI FP_PeriodReset(HANDLE _p) { PRPeriod *p = (PRPeriod *)_p; if (p) GET_TIME(p->b); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_plg.cpp000066400000000000000000000021401477431623700210500ustar00rootroot00000000000000#include #include "fstdlib.h" PluginStartupInfo *FP_Info = NULL; FarStandardFunctions *FP_FSF = NULL; char *FP_PluginRootKey = NULL; char *FP_PluginStartPath = NULL; int FP_LastOpMode = 0; DWORD FP_WinVerDW; static void _cdecl idAtExit(void) { delete FP_Info; FP_Info = NULL; delete FP_FSF; FP_FSF = NULL; delete[] FP_PluginRootKey; FP_PluginRootKey = NULL; delete[] FP_PluginStartPath; FP_PluginStartPath = NULL; // FP_HModule = NULL; } void WINAPI FP_SetStartupInfo(const PluginStartupInfo *Info, const char *KeyName) { // Info FP_Info = new PluginStartupInfo; memcpy(FP_Info, Info, sizeof(*Info)); // FSF FP_FSF = new FarStandardFunctions; memcpy(FP_FSF, Info->FSF, sizeof(*FP_FSF)); // Version // Plugin Reg key FP_PluginRootKey = new char[FAR_MAX_REG + 1]; StrCpy(FP_PluginRootKey, Info->RootKey, FAR_MAX_REG); StrCat(FP_PluginRootKey, "/", FAR_MAX_REG); StrCat(FP_PluginRootKey, KeyName, FAR_MAX_REG); // Start path char *m = strrchr(FP_PluginStartPath, '/'); if (m) *m = 0; } SHAREDSYMBOL void PluginModuleOpen(const char *path) { FP_PluginStartPath = strdup(path); } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_scr.cpp000066400000000000000000000020061477431623700210560ustar00rootroot00000000000000#include #include "fstdlib.h" static int nSaveCount = 0; static HANDLE hScreen = NULL; static WCHAR SaveTitle[FAR_MAX_TITLE]; int WINAPI FP_Screen::isSaved(void) { return nSaveCount; } void WINAPI FP_Screen::Save(void) { CHK_INITED Log(("SCREEN: SAVE[%d]", nSaveCount)); if (IS_FLAG(FP_LastOpMode, OPM_FIND)) return; if (nSaveCount == 0) { hScreen = FP_Info->SaveScreen(0, 0, -1, -1); WINPORT(GetConsoleTitle)(NULL, SaveTitle, ARRAYSIZE(SaveTitle)); } nSaveCount++; } void WINAPI FP_Screen::FullRestore(void) { while (isSaved()) Restore(); } void WINAPI FP_Screen::RestoreWithoutNotes(void) { if (hScreen) { FP_Info->RestoreScreen(hScreen); hScreen = FP_Info->SaveScreen(0, 0, -1, -1); } } void WINAPI FP_Screen::Restore(void) { CHK_INITED if (!hScreen || !nSaveCount) return; nSaveCount--; if (nSaveCount == 0) { Log(("SCREEN: RESTORE")); WINPORT(SetConsoleTitle)(NULL, SaveTitle); FP_Info->RestoreScreen(hScreen); FP_Info->Text(0, 0, 0, NULL); hScreen = NULL; } } far2l-2.6.5~beta+ds/FARStdlib/src/fstd_stdlibCS.cpp000066400000000000000000000000531477431623700217760ustar00rootroot00000000000000#include #include "fstdlib.h" far2l-2.6.5~beta+ds/HACKING.md000066400000000000000000000231161477431623700155750ustar00rootroot00000000000000## Coding style See: [CODESTYLE.md](CODESTYLE.md) ## Lyric I implemented/borrowed from WINE some commonly used WinAPI functions. They are all declared in WinPort/WinPort.h and corresponding defines can be found in WinPort/WinCompat.h (both are included by WinPort/windows.h). Note that this stuff may not be 1-to-1 to corresponding Win32 functionality also doesn't provide full-UNIX functionality, but it simplifies porting and can be considered as temporary scaffold. However, only the main executable is linked statically to WinPort, although it also _exports_ WinPort functionality, so plugins use it without the neccessity to bring their own copies of this code. This is the reason that each plugin's binary should not statically link to WinPort. While FAR internally is UTF16 (because WinPort contains UTF16-related stuff), native Linux wchar_t size is 4 bytes (rather than 2 bytes) so potentially Linux FAR may be fully UTF32-capable console interaction in the future, but while it uses Win32-style UTF16 functions it does not. However, programmers need to be aware that wchar_t is not 2 bytes long anymore. Inspect all printf format strings: unlike Windows, in Linux both wide and multibyte printf-like functions have the same multibyte and wide specifiers. This means that %s is always multibyte while %ls is always wide. So, any %s used in wide-printf-s or %ws used in any printf should be replaced with %ls. Update from 27aug: now it's possible by defining WINPORT_DIRECT to avoid renaming used Windows API and also to avoid changing format strings as swprintf will be intercepted by a compatibility wrapper. Update from 03/11/22: far2l's console emulator capable to correctly render full-width and combining characters as well as 24 bit colors. This caused following deviation of console-simulation functions behavior comparing to original Win32 API counterparts: * CHAR_INFO's Char::UnicodeChar field extended to 64 bit length to be able to associate sequence of multiple WCHARs with single cell. * Writing to console full-width character causes two cells to be used: first will get given character code in UnicodeChar field but next one will have UnicodeChar set to zero. * Writing combined characters - normal character followed by set of diactrical marks - will make UnicodeChar field to contain so-called 'composite' character code that represents sequence of character codes registered with WINPORT(CompositeCharRegister). Actual sequence of WCHARs can be obtained by WINPORT(CompositeCharLookup). There is macro CI_USING_COMPOSITE_CHAR that allows to detect if given CHAR_INFO contains composite character code or normal WCHAR. * Both above transformations happen automatically _only_ if using WriteConsole API. If one uses WriteConsoleOutput - then its up to caller to perform that transformations. Failing to do so will cause incorrect rendering of full-width or diactrical characters. * CHAR_INFO's and CONSOLE_SCREEN_BUFFER_INFO's Attributes fields extended to 64 bit to be able to hold 24 bit RGB colors in higher bytes. Use macroses GET_RGB_FORE/GET_RGB_BACK/SET_RGB_FORE/SET_RGB_BACK/SET_RGB_BOTH to access that colors. Note that such colors will be used only if FOREGROUND_TRUECOLOR/BACKGROUND_TRUECOLOR attribute is set. Old attributes define colors from usual 16-elements palette used to render if ..._TRUECOLOR is not set or if backend's target doesn't support more than 16 colors. ## Plugin API Plugins API based on FAR Manager v2 plus following changes: ### Added following entries to FarStandardFunctions: * `int Execute(const wchar_t *CmdStr, unsigned int ExecFlags);` ...where ExecFlags - combination of values of EXECUTEFLAGS. Executes given command line, if EF_HIDEOUT and EF_NOWAIT are not specified then command will be executed on far2l virtual terminal. * `int ExecuteLibrary(const wchar_t *Library, const wchar_t *Symbol, const wchar_t *CmdStr, unsigned int ExecFlags)` Executes given shared library symbol in separate process (process creation behaviour is the same as for Execute). symbol function must be defined as: `int 'Symbol'(int argc, char *argv[])` * `void DisplayNotification(const wchar_t *action, const wchar_t *object);` Shows (depending on settings - always or if far2l in background) system shell-wide notification with given title and text. * `int DispatchInterThreadCalls();` far2l supports calling APIs from different threads by marshalling API calls from non-main threads into main one and dispatching them on main thread at certain known-safe points inside of dialog processing loops. DispatchInterThreadCalls() allows plugin to explicitly dispatch such calls and plugin must use it periodically in case it blocks main thread with some non-UI activity that may wait for other threads. * `void BackgroundTask(const wchar_t *Info, BOOL Started);` If plugin implements tasks running in background it may invoke this function to indicate about pending task in left-top corner. * Info is a short description of task or just its owner and must be same string when invoked with Started TRUE or FALSE. * `size_t StrCellsCount(const wchar_t *Str, size_t CharsCount);` Returns count of console cells which will be used to display given string of CharsCount characters. * `size_t StrSizeOfCells(const wchar_t *Str, size_t CharsCount, size_t *CellsCount, BOOL RoundUp);` Returns count of characters which will be used to fill up to CellsCount cells from given string of CharsCount characters. RoundUp argument tells what to do with full-width characters that crossed by CellsCount. On return CellsCount contains cells count that will be filled by returned characters count, that: * Can be smaller than initial value if string has too few characters to fill all CellsCount cells or if RoundUp was set to FALSE and last character would then overflow wanted amount. * Can be larger by one than initial value if RoundUp was set to TRUE and last full-width character crossed initial value specified in *CellsCount. * `TruncStr and TruncPathStr` This two functions not added but changed to use console cells count as string limiting factor. * `BOOL VTLogExport(HANDLE con_hnd, DWORD flags, const char *file);` Exports to file virtual terminal history of given VT console. Returns TRUE on success. Arguments: * con_hnd - NULL means active console, otherise - must be one of handles obtained from VTEnumBackground * flags - zero or combination of VT_LOGEXPORT_* constants: VT_LOGEXPORT_COLORED and VT_LOGEXPORT_WITH_SCREENLINES * file - is a file path to be exported, if it points to empty string (i.e. *file = 0) then it MUST be buffer of size at least MAX_PATH (4096) characters and VT history log will be exported to file at autogenerated temporary path that will be copied into that buffer * `SIZE_T VTEnumBackground(HANDLE *con_hnds, SIZE_T count);` Fills given array with handles to background terminal session consoles. Fills up to specified count handles, but returns background terminal session count, so if returned value > count argument - then need to extend array buffer and retry ### Added following commands into FILE_CONTROL_COMMANDS: * `FCTL_GETPANELPLUGINHANDLE` Can be used to interact with plugin that renders other panel. `hPlugin` can be set to `PANEL_ACTIVE` or `PANEL_PASSIVE`. `Param1` ignored. `Param2` points to value of type `HANDLE`, call sets that value to handle of plugin that renders specified panel or `INVALID_HANDLE_VALUE`. ### Added following plugin-exported functions: * `int MayExitFARW();` far2l asks plugin if it can exit now. If plugin has some background tasks pending it may block exiting of far2l, however it highly recommended to give user choice using UI prompt. * `int GetLinkTargetW(HANDLE hPlugin, struct PluginPanelItem *PanelItem, wchar_t *Target, size_t TargetSize, int OpMode);` far2l uses this to resolve symlink destination when user selects plugin's item that has FILE_ATTRIBUTE_REPARSE_POINT. Target is displayed in status field as for local symlinks. ### Added following dialog messages: * `DM_SETREADONLY` - changes readonly-ness of selected dialog edit control item * `DM_GETDEFAULTCOLOR` * `DM_GETCOLOR = DM_SETTRUECOLOR` - sets 24-bit RGB colors to selected dialog item (see DN_CTLCOLORDLGITEM). * `DM_SETCOLOR = DM_GETTRUECOLOR` - retrieves 24-bit RGB colors of selected dialog item. * `DM_SETTEXTPTRSILENT` * `ECTL_ADDTRUECOLOR` - applies coloring to editor like ECTL_ADDCOLOR does but allows to specify 24 RGB color using EditorTrueColor structure. * `ECTL_GETTRUECOLOR` - retrieves coloring of editor like ECTL_GETCOLOR does but gets 24 RGB color using EditorTrueColor structure. * `DN_KEY` - Param2 retrives bitmasked state of the keys KEY_SHIFT, KEY_ALT and KEY_CTRL when Param1 equals to -1. Note that all true-color capable messages extend but don't replace 'base' 16 palette colors. This is done intentionally as far2l may run in terminal that doesn't support true color palette, and in such case 24bit colors will be ignored and base palette attributes will be used instead. ### Added new flags: * Flags to **manage markers** in **panel** from plugins API (a la global parameters `Opt.ShowFilenameMarks` and `Opt.FilenameMarksAlign`): - `OPIF_HL_MARKERS_NOSHOW` and `OPIF_HL_MARKERS_NOALIGN` (in `enum OPENPLUGININFO_FLAGS`); - `PFLAGS_HL_MARKERS_NOSHOW` and `PFLAGS_HL_MARKERS_NOALIGN` (in `enum PANELINFOFLAGS`). * Flag which prevents the removal of ampersand characters from menu items being iterated in `VMenu::FindItem()` (for the proper work of `History::GetAllSimilar()` function and in other similar situations): - `LIFIND_KEEPAMPERSAND` (in `enum FARLISTFINDFLAGS`); * Flags in `enum PROCESSNAME_FLAGS` (added in #2452): - `PN_GENERATENAME`, `PN_CHECKMASK`, `PN_SHOWERRORMESSAGE`, `PN_RESERVED1`, `PN_CASESENSITIVE`, `PN_NONE` far2l-2.6.5~beta+ds/LICENSE.txt000066400000000000000000000446721477431623700160440ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ----------------------------------------------------------------------------- In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. far2l-2.6.5~beta+ds/NetRocks/000077500000000000000000000000001477431623700157345ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/CMakeLists.txt000066400000000000000000000177761477431623700205160ustar00rootroot00000000000000project(NetRocks) set(SOURCES src/Erroring.cpp src/Globals.cpp src/SitesConfig.cpp src/NetRocks.cpp src/PluginImpl.cpp src/PluginPanelItems.cpp src/PooledStrings.cpp src/GetItems.cpp src/BackgroundTasks.cpp src/Location.cpp src/ConnectionsPool.cpp src/ImportFarFtpSites.cpp src/Host/HostLocal.cpp src/Host/HostRemote.cpp src/Host/InitDeinitCmd.cpp src/UI/DialogUtils.cpp src/UI/Settings/ConfigurePlugin.cpp src/UI/Settings/SiteConnectionEditor.cpp src/UI/Settings/ExtraSiteSettings.cpp src/UI/Settings/ProxySettings.cpp src/UI/Settings/ConfigureProtocolSFTPSCP.cpp src/UI/Settings/ConfigureProtocolFTP.cpp src/UI/Settings/ConfigureProtocolAWS.cpp src/UI/Settings/ConfigureProtocolSMB.cpp src/UI/Settings/ConfigureProtocolNFS.cpp src/UI/Settings/ConfigureProtocolWebDAV.cpp src/UI/Settings/ConfigureProtocolSHELL.cpp src/Protocol/SHELL/WayToShellConfig.cpp src/UI/Activities/Confirm.cpp src/UI/Activities/ConfirmXfer.cpp src/UI/Activities/ConfirmOverwrite.cpp src/UI/Activities/ConfirmChangeMode.cpp src/UI/Activities/SimpleOperationProgress.cpp src/UI/Activities/ComplexOperationProgress.cpp src/UI/Activities/AbortOperationRequest.cpp src/UI/Activities/InteractiveLogin.cpp src/UI/Activities/ConfirmNewServerIdentity.cpp src/UI/Activities/WhatOnError.cpp src/UI/Activities/BackgroundTasksUI.cpp src/Op/Utils/ProgressStateUpdate.cpp src/Op/Utils/Enumer.cpp src/Op/Utils/IOBuffer.cpp src/Op/OpBase.cpp src/Op/OpConnect.cpp src/Op/OpCheckDirectory.cpp src/Op/OpChangeMode.cpp src/Op/OpEnumDirectory.cpp src/Op/OpXfer.cpp src/Op/OpRemove.cpp src/Op/OpMakeDirectory.cpp src/Op/OpExecute.cpp src/Op/OpGetLinkTarget.cpp src/Protocol/SplitLocationSpecification.cpp src/Protocol/Protocol.cpp ) set(PROTOCOL_SOURCES src/Erroring.cpp src/Host/HostRemoteBroker.cpp ) add_executable (NetRocks-FILE ${PROTOCOL_SOURCES} src/Protocol/File/ProtocolFile.cpp src/Host/HostLocal.cpp ) add_executable (NetRocks-SHELL ${PROTOCOL_SOURCES} src/Protocol/SHELL/ProtocolSHELL.cpp src/Protocol/SHELL/ProtocolSHELL_ExecCmd.cpp src/Protocol/SHELL/WayToShellConfig.cpp src/Protocol/SHELL/WayToShell.cpp src/Protocol/SHELL/Request.cpp src/Protocol/SHELL/Parse.cpp src/Protocol/SHELL/RemoteSh.cpp src/Protocol/ShellParseUtils.cpp ) # get rid of warning: dynamic exception specifications are deprecated in C++11 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") target_link_libraries(NetRocks-FILE utils) target_include_directories(NetRocks-FILE PRIVATE src) set_target_properties(NetRocks-FILE PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") target_compile_options(NetRocks-FILE PUBLIC -DNETROCKS_PROTOCOL) target_link_libraries(NetRocks-SHELL utils) target_include_directories(NetRocks-SHELL PRIVATE src) set_target_properties(NetRocks-SHELL PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") target_compile_options(NetRocks-SHELL PUBLIC -DNETROCKS_PROTOCOL) add_custom_command(TARGET NetRocks-SHELL POST_BUILD DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/Protocol/SHELL/Helpers COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/Protocol/SHELL/Helpers "${INSTALL_DIR}/Plugins/NetRocks/plug/SHELL" ) if (LIBSSH_FOUND AND ((NOT DEFINED NR_SFTP) OR NR_SFTP)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_SFTP") add_executable (NetRocks-SFTP ${PROTOCOL_SOURCES} src/Protocol/SSH/SSHConnection.cpp src/Protocol/SSH/ProtocolSFTP.cpp src/Protocol/SSH/ProtocolSCP.cpp src/Protocol/FileStatsOverride.cpp src/Protocol/ShellParseUtils.cpp ) target_link_libraries(NetRocks-SFTP utils ${LIBSSH_LIBRARIES}) target_include_directories(NetRocks-SFTP PRIVATE src ${LIBSSH_INCLUDE_DIRS}) set_target_properties(NetRocks-SFTP PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") endif () if (LIBSMBCLIENT_FOUND AND ((NOT DEFINED NR_SMB) OR NR_SMB)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_SMB") add_executable (NetRocks-SMB ${PROTOCOL_SOURCES} src/Protocol/SMB/ProtocolSMB.cpp src/Protocol/SMB/NMBEnum.cpp ) target_link_libraries(NetRocks-SMB utils ${LIBSMBCLIENT_LIBRARIES}) target_include_directories(NetRocks-SMB PRIVATE src ${LIBSMBCLIENT_INCLUDE_DIRS}) set_target_properties(NetRocks-SMB PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") endif () if (LIBNFS_FOUND AND ((NOT DEFINED NR_NFS) OR NR_NFS)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_NFS") add_executable (NetRocks-NFS ${PROTOCOL_SOURCES} src/Protocol/NFS/ProtocolNFS.cpp ) target_link_libraries(NetRocks-NFS utils ${LIBNFS_LIBRARIES}) find_package(GnuTLS) if (GNUTLS_FOUND) target_link_libraries(NetRocks-NFS ${GNUTLS_LIBRARIES}) endif () target_include_directories(NetRocks-NFS PRIVATE src ${LIBNFS_INCLUDE_DIRS}) set_target_properties(NetRocks-NFS PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") endif () if (LIBNEON_FOUND AND ((NOT DEFINED NR_WEBDAV) OR NR_WEBDAV)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_WEBDAV") add_executable (NetRocks-WebDAV ${PROTOCOL_SOURCES} src/Protocol/WebDAV/ProtocolWebDAV.cpp ) target_link_libraries(NetRocks-WebDAV utils ${LIBNEON_LIBRARIES}) target_include_directories(NetRocks-WebDAV PRIVATE src ${LIBNEON_INCLUDE_DIRS}) set_target_properties(NetRocks-WebDAV PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") endif () if (AWSSDK_FOUND AND ((NOT DEFINED NR_AWS) OR NR_AWS)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_AWS") add_executable (NetRocks-AWS ${PROTOCOL_SOURCES} src/Protocol/AWS/AWSFile.cpp src/Protocol/AWS/AWSFileWriter.cpp src/Protocol/AWS/AWSFileReader.cpp src/Protocol/AWS/S3Repository.cpp src/Protocol/AWS/ProtocolAWS.cpp ) target_include_directories(NetRocks-AWS PRIVATE src ${AWSSDK_INCLUDE_DIR}) target_link_libraries(NetRocks-AWS utils ${AWSSDK_LINK_LIBRARIES}) set_target_properties(NetRocks-AWS PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") endif () add_executable (NetRocks-FTP ${PROTOCOL_SOURCES} src/Protocol/FTP/ProtocolFTP.cpp src/Protocol/FTP/FTPConnection.cpp src/Protocol/FTP/FTPParseMLST.cpp src/Protocol/FTP/FTPParseLIST.cpp src/Protocol/DirectoryEnumCache.cpp ) target_link_libraries(NetRocks-FTP utils) target_include_directories(NetRocks-FTP PRIVATE src) if (OPENSSL_FOUND AND ((NOT DEFINED NR_OPENSSL) OR NR_OPENSSL)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_OPENSSL") target_link_libraries(NetRocks-FTP ${OPENSSL_LIBRARIES}) target_include_directories(NetRocks-FTP PRIVATE ${OPENSSL_INCLUDE_DIR}) endif () set_target_properties(NetRocks-FTP PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".broker") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FAR_HAS_NAMELESS_UNIONS") add_library (NetRocks MODULE ${SOURCES}) target_link_libraries(NetRocks utils far2l) target_include_directories(NetRocks PRIVATE src) target_include_directories(NetRocks PRIVATE ../WinPort) target_include_directories(NetRocks PRIVATE ../far2l/far2sdk) target_compile_definitions(NetRocks PRIVATE) set_target_properties(NetRocks PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${INSTALL_DIR}/Plugins/NetRocks/plug" PREFIX "" SUFFIX ".far-plug-wide") add_custom_command(TARGET NetRocks POST_BUILD DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/configs COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/configs "${INSTALL_DIR}/Plugins/NetRocks") far2l-2.6.5~beta+ds/NetRocks/HACKING.txt000066400000000000000000000036001477431623700175400ustar00rootroot00000000000000Architecture overview: /----------[PluginImpl]----{shared_ptr or temporary}----\ [g_background_tasks] | | | | V | | | [UI/Settings] | | {shared_ptr} | | | {shared_ptr} V V | [Operations:OpConnect,OpXfer etc]----\ | | | | | | V | | [IHost:HostRemote,HostLocal] <---{dedicated thread, shared_ptr}---/ [UI/Activities: Confirmations, Errors, Progress] / \ {IPC to broker process} | | V V [Local filesystem] [HostRemoteBroker] | {aggregation} | V [IProtocol:ProtocolSFTP,ProtocolFile] To add additional protocol: + Write IProtocol implementation and function that instantiates it: see Protocol/ProtocolSFTP.cpp as example + Write protocol-specific options configuration UI: see UI/Settings/ConfigureProtocolSFTP.cpp as example + Define protocol name, broker name and other protocol properties in s_protocols array in Protocol/Protocol.cpp * Dont forget to add new protocol broker related-files into NetRocks/CMakeLists.txt * Keep in mind that protocol implementation hosted in separate process, so it cannot interchange any data via global variables etc far2l-2.6.5~beta+ds/NetRocks/configs/000077500000000000000000000000001477431623700173645ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/configs/plug/000077500000000000000000000000001477431623700203335ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/configs/plug/bel.lng000066400000000000000000000261671477431623700216130ustar00rootroot00000000000000.Language=Belarusian,Belarusian (Беларуская) "NetRocks" "SFTP" "&ОК" "А&дмена" "Яшчэ раз" "&Прапусціць" "Памылка" "В&ышей" "Налады NetRocks" "Уключыць апавяшчэнні працоўнага стала" "<&ENTER> выконвае файлы на серверы, калі гэтае магчыма" "Разумнае капіяванне сімвалічных &спасылак" "Выкарыстоўваць &chmod:" "Аўтаматычна" "Заўсёды (ігнараваць umask)" "Ніколі" "Запамінаць працоўны каталог у наладах сайта" "Таймаўт неўжываемых злучэнняў (сек.):" "Запомніць мой выбар для гэтай аперацыі" "Адбылася памылка" "Не магу падлучыцца" "Няправільна паказаны шлях" "Аўтарызацыя" "Аўтарызацыя - яшчэ раз" "Аўтарызуемся на:" "Паўторная аўтарызацыя на:" "&Пратакол :" "&Імя сервера :" "&Нумар порта :" "&Рэжым уваходу :" "Без пароля" "Спытаць пароль" "Захаваны пароль" "Імя &карыстальніка :" "&Пароль уваходу :" "П&рацоўная дырэкторыя:" "Трымаць &жывым, сек. :" "Кадыр&оўка :" "Папраўка &часу, сек. :" "&Імя падлучэння :" "&Дад. налады" "Нал. пратаколу" "Нал. проксi" "&Захаваць" "Падклю&чыць" "Захаваць/падклю&чыць" "<Стварыць новае падключэнне>" "Налады падлучэння" "Зліць з сервера" "Запампаваць на сервер" "Перазаліць паміж серверамі" "Зліць і выдаліць" "Запампаваць і выдаліць" "Перазаліць і выдаліць" "Пераназваць на серверы" "Зліць абраныя файлы &у:" "Запампаваць абраныя файлы &у:" "Перазаліць абраныя файлы &у:" "Перамясціць залітыя файлы ў:" "Перамясціць залітыя файлы ў:" "Перамясціць перазаліць файлы ў:" "Пераназваць абраныя файлы &ў" "Пры перазапісу:" "&Спытаць" "&Перазапісаць" "&Прапусціць" "&Перазапісаць калі больш новы" "&Працягнуць" "Стварыць новае імя" "Трэба абраць дзеянне перазапісу" "Бягучы файл" "Памер файла" "Агульны памер" "Колькасць" " З" "Часу на файл ВЫДАТКАВАНА:" "ЗАСТАЛОСЬ:" "Усяго часу ВЫДАТКАВАНА:" "Хуткасць () БЯГУЧАЯ:" "СЯРЭДНЯЯ:" "У &фон" "Прыпынак" "Працягнуць" "Запыт пры памылцы" "Зліць" "Запампаваць" "Перазаліць" "Зліць-выдаліць" "Запампаваць-выдаліць" "Перазаліць-выдаліць" "Пераназваць" "Ужыць змены" "Файл прызначэння ўжо існуе:" "Даведка пра зыходны файл:" "Даведка пра файл прызначэння" "Варыянты дзеянняў" "Выдаліць з сервера" "Выдаліць абраныя аб'екты з:" "Выдаліць" "Выдаліць падключэнне(-і)" "Выдаліць налады абраных падлучэнняў?" "Скапіяваць падлучэнне(-і)" "Скапіяваць налады абраных падлучэнняў?" "Перамясціць падлучэнне(-і)" "Перамясціць налады абраных падлучэнняў?" "Імпарт падлучэнняў" "Імпартаваць абраныя файлы падлучэнняў?" "Паглынанне падлучэнняў" "Паглынуць абраныя файлы падлучэнняў?" "Экспарт падлучэнняў" "Экспартаваць абраныя падлучэнні ў файлы?" "Экспарт падлучэнняў з выдаленнем" "Экспартаваць з выдаленнем абраныя падлучэнні ў файлы?" "Стварыць каталог" "Увядзіце імя каталогу:" "Стварыць" "Падлучаемся да сайта" "Апытваем шлях" "Ужываем рэжым" "Скануем каталог" "Ствараем каталог" "Выконваем каманду" "Апытанне спасылкі" "Адмена аперацыі" "Пацвердзіце адмену аперацыі" "&Адмяніць аперацыю" "&Прадоўжыць" "Адмяняем аперацыю..." "&Абарваць злучэнне" "NetRocks () завершана" "NetRocks () памылка" "запампоўванне" "зліўка" "перазаліўка" "пераназванне" "Памылка зліўкі" "Памылка запампоўвання" "Памылка перазаліўку" "Памылка апытання" "Памылка праверкі каталогу" "Памылка сканавання каталогу" "Памылка стварэння каталогу" "Памылка пераназвання" "Памылка выдалення каталогу" "Памылка выдалення" "Памылка ўсталёўкі часу файла" "Памылка ўсталёўкі рэжыму файла" "Памылка апытання спасылкі" "Памылка стварэння спасылкі" "Памылка:" "Няўдалы аб'ект:" "Збоі на сайце:" "Лакальная ФС" "СПРОБ: %llu ПРОПУСКАЎ: %llu" "Пратакол не падтрымлівае падаленыя каманды" "Можна ўжываць толькі каманды: cd, pushd, popd" "выкананне каманды" "Пацвердзіць сапраўднасць сервера" "Імя сайта:" "Бягучы ключ сервера:" "Дазволіць аднойчы" "Дазволіць назаўжды" "Фонавыя задачы NetRocks" "АКТЫЎНА" "ПРЫПЫНАК" "ЗАВЕРШАНЫ" "ПЕРАПЫНЕНЫ" "Няма фонавых задач" "Пацверджанне выхаду NetRocks" "Закрыццё FAR перапыніць % u фонавыя задачы." "Вы сапраўды жадаеце зачыніць FAR?" "&Фонавыя задачы" "Налады пратаколу SFTP" "Налады пратаколу SCP" "&Аўтарызацыя:" "Імя карыстальніка і пароль" "І&нтэрактыўнае імя карыстальніка" "Шлях да &файла ключа:" "&SSH-agent (па ${SSH_AUTH_SOCK})" "Шлях да файла &ключа:" "С&ціск:" "Без сціску" "Сціскаць ўваходны трафік" "Сціскаць выходны трафік" "Сціскаць ўвесь трафік" "Запыт асаблівай падсістэмы:" "Памер блока чытання, байт:" "Памер блока запісу, байт:" "Уключыць наладу TCP_&NODELAY" "Уключыць наладу TCP_&QUICKACK" "Ігнараваць &памылкі часу і рэжымаў" "Спроб падлучэння, &разоў:" "Таймаўт падлучэння, &секунд:" "Дазволеныя &ключы:" "Дазволеныя KE&X:" "Дазволеныя HMAC к&ліента:" "Дазволеныя HMAC с&ервера:" "Наладкi OpenSS&H:" "(Звычайныя файлы наладак)" "(Не выкарыстоўваць)" "(Уведзеныя файлы)" "Уключыць абарону (калі вы не давяраеце серверу)" "Дадатковыя наладкі злучэння" "Каманда, якая выконваецца пры падлучэнні:" "Каманда, якая выконваецца пры адключэнні:" "Дадатковы радок, які перадаецца камандзе:" "Абмежаванне часу выканання каманды, секунд:" "Дадатковыя зменныя асяроддзя, якія даступныя працэсу каманды:" "Налады пратаколу SMB" "Працоўная група:" "Ужываць &SMB для агляду сеткі" "Ужываць &NMB для агляду сеткі" "Налады пратаколу NFS" "Асаблівыя параметры ідэнтыфікацыі:" "&Вузел:" "&UID:" "&GID:" "Г&рупы:" "Налады пратаколу WebDAV" "Карыстальніцкі агент:" "Падлучэнне праз проксі:" "Проксі сервер:" "Порт проксі:" "Аўтарызацыя проксі сервера:" "Карыстальнік:" "Пароль:" "Змяніць рэжым файла" "Змяніць рэжым абранага аб'екта:" "Змяніць рэжым %u &аб'ектаў абраных у:" "Які з'яўляецца &спасылкай на:" "І &рэкурсіўна ў падкаталогах" " Валоданне (толькі прагляд) " "У&ладальнік:" "С&уполка:" "(некалькі значэнняў)" "(невядомы ўладальнік)" "(невядомая група)" " Дазволы " "Карыстальнік:" "Група:" "Астатнія:" "&R" "&W" "&X" "R" "W" "X" "&SUID" "SGID" "Sticky" "П&ершасны" " Timestamp YYYY-MM-DD hh:mm:ss.ms (read only) " "Час апошняга доступу:" "Час апошняй змены:" "Час змены статута:" "Налады пратаколу FTP" "Налады пратаколу FTPS" "&Уключыць шыфраванне" "Найменьшы пратакол &шыфравання:" "Каманда &спісу каталогаў:" "&Кодавая старонка сервера" "&Пасіўны рэжым" "<Аўтавызначэнне>" "Ужываць &MLSD/MLST калі магчыма" "Ужываць канвеерызацыю &камандаў" "Дазволіць &дадзеныя толькі з таго ж самага сервера" "Уключыць наладу TCP_&NODELAY" "Уключыць наладу TCP_&QUICKACK" "Налады пратаколу SHELL" "Спосаб &доступу" "Налады спосабу доступу" "Налады проксі" "Не выкарыстоўваць праксіфікатар" "Выкарыстоўваны &праксіфікатар:" "&Рэдагаваць канфігурацыю праксіфікатара" "Ховішча AWS S3" "Карыстальніцкі агент" "Рэгіён" "Выкарыстоўваць праксы" "Хост праксы" "Порт праксы" "Пракса з аўтэнтыфікацыяй" "Імя карыстальніка праксы" "Пароль праксы" far2l-2.6.5~beta+ds/NetRocks/configs/plug/eng.lng000066400000000000000000000144151477431623700216130ustar00rootroot00000000000000.Language=English,English "NetRocks" "SFTP" "&Ok" "&Cancel" "&Retry" "&Skip" "Error" "Go&Up" "NetRocks options" "Enable desktop ¬ifications" "<&ENTER> to execute files remotely when possible" "Smart &symlinks copying" "Use of &chmod:" "Automatic" "Always (umask override)" "Never" "Remember working &directory in site settings" "Connections pool e&xpiration (seconds):" "Re&member my choice for current operation" "Operation failed" "Could not connect" "Wrong path specification" "Authentication" "Authentication - retry" "Authenting to:" "Reauthenting to:" "P&rotocol :" "&Host name :" "Port &number :" "Login &mode :" "No password" "Ask password" "Saved password" "Login &username :" "Login &password :" "&Working directory :" "&Keepalive, seconds :" "Cod&epage :" "&Time adjust, seconds:" "&Display name :" "E&xtra options" "Pro&tocol options" "Prox&y options" "&Save" "C&onnect" "Save/C&onnect" "" "Site connection settings" "Download from server" "Upload to server" "Crossload between servers" "Download/Move from server" "Upload/Move to server" "Crossload/Move between servers" "Rename on server" "Download selected files &to:" "Upload selected files &to:" "Crossload selected files &to:" "Move downloaded selected &files to:" "Move uploaded selected files &to:" "Move crossloaded selected files &to:" "Rename selected files &to" "Default overwrite action:" "&Ask" "&Overwrite" "&Skip" "Overwrite if &newer" "&Resume" "Create &different name" "Need to choose overwrite action" "Current file" "File Size" "Total Size" "Objects Count" " Of" "Current file time SPENT:" "REMAIN:" "Total time SPENT:" "Speed () CURRENT:" "AVERAGE:" "&Background" "Paus&e" "Resum&e" "&Ask on error" "Down&load" "Up&load" "Cross&load" "Download-&Move" "Upload-&Move" "Crossload-&Move" "Re&name" "Cha&nge mode" "Destination file already exists:" "Source file information:" "Destination information" "Options to proceed" "Remove from server" "Remove selected items from:" "Proceed &removal" "Remove connection site(s)" "Remove selected connection site(s) settings?" "Copy connection site(s)" "Copy selected connection site(s) settings?" "Move connection site(s)" "Move selected connection site(s) settings?" "Import connection site(s)" "Import selected connection site(s) settings?" "Consume connection site(s)" "Consume selected connection site(s) settings?" "Export connection site(s)" "Export selected site(s) into files?" "Export and remove connection site(s)" "Export selected site(s) into files and remove?" "Create directory" "Enter name of directory to create:" "Proceed Create" "Connecting to site" "Querying path" "Applying mode" "Enumerating directory" "Creating directory" "Executing command" "Querying symlink" "Aborting operation" "Confirm abort current operation" "&Abort operation" "&Continue" "Aborting operation..." "&Break connection" "NetRocks () complete" "NetRocks () failed" "upload" "download" "crossload" "rename" "Download error" "Upload error" "Crossload error" "Query information error" "Check directory error" "Enum directory error" "Make directory error" "Rename error" "Remove directory error" "Remove error" "Set file times error" "Change file mode error" "Symlink query error" "Symlink create error" "Error:" "Failed object:" "Failed site:" "Local filesystem" "RETRIES: %llu SKIPS: %llu" "Protocol doesn't support remote commands" "Only this commands allowed: cd, pushd, popd" "command execution" "Confirm new server identity" "Site name:" "Site's server current identity:" "Allow &once" "Allow &always" "NetRocks background tasks" "ACTIVE" "PAUSED" "COMPLETE" "ABORTED" "No background tasks" "NetRocks exit confirmation" "Exiting FAR will disrupt %u pending background tasks." "Are you really sure you want to exit FAR?" "&Background tasks" "SFTP protocol options" "SCP protocol options" "&Authentication:" "&Username and password" "&Interactive login" "&Private key file path:" "&SSH-agent (by ${SSH_AUTH_SOCK})" "Private &key file path:" "&Compression:" "No compression" "Compress incoming traffic" "Compress outgoing traffic" "Compress all traffic" "Custom &subsystem request/exec:" "Max &read block size, bytes:" "Max &write block size, bytes:" "Enable &TCP_NODELAY option" "Enable TCP_&QUICKACK option" "Ignore time and mode &errors" "Automatically retr&y connect, times:" "Connection t&imeout, seconds:" "Allowed host &keys:" "Allowed KE&X algorithms:" "All&owed HMAC client->server:" "Allowe&d HMAC server->client:" "OpenSS&H config files:" "(Default config files)" "(Don't use config files)" "(Specified config files)" "Enable &sandbox (if you don't trust server)" "Extra connection options" "Command to e&xecute on connect:" "Command to execute on &disconnect:" "Extra &string passed to command:" "Limit command execution time, seconds:" "Extra environment variables available for command process:" "SMB Protocol options" "Work&group:" "Enum network with &SMB" "Enum network with &NMB" "NFS Protocol options" "O&verride client credentials:" "&Host:" "&UID:" "&GID:" "G&roups:" "WebDAV Protocol options" "User a&gent:" "Connect via pro&xy:" "Proxy &host:" "Proxy &port:" "Use proxy &authentication:" "Proxy &user:" "Proxy pa&ssword:" "Confirm change mode" "Edit mode of selected &object:" "Edit mode of %u objects selected in:" "That is s&ymlink pointing to:" "Recurse into sub&directories" " Ownership (read only) " "Own&er:" "Grou&p:" "(multiple values)" "(unknown owner)" "(unknown group)" " Permissions " "User:" "Group:" "Other:" "&R" "&W" "&X" "R" "W" "X" "&SUID" "SGID" "Sticky" "Ori&ginal" " Timestamp YYYY-MM-DD hh:mm:ss.ms (read only) " "Last access time:" "Last modification time:" "Last status change time:" "FTP Protocol Options" "FTPS Protocol Options" "E&xplicit encryption" "Minimal &encryption protocol:" "Directory &list command:" "Server &codepage" "&Passive mode" "" "Use &MLSD/MLST if possible" "Enable commands pipe&lining" "En&sure data connection peer matches server" "Enable &TCP_NODELAY option" "Enable TCP_&QUICKACK option" "SHELL Protocol Options" "&Way to access shell" "Shell access way settings" "Proxy settings" "Not using proxifier" "&Proxifier kind to use:" "&Edit proxifier configuration" "AWS S3 Storage" "User agent" "Region" "Use proxy" "Proxy host" "Proxy port" "Auth proxy" "Proxy username" "Proxy password" far2l-2.6.5~beta+ds/NetRocks/configs/plug/helpe.hlf000066400000000000000000000427561477431623700221410ustar00rootroot00000000000000.Language=English,English .PluginContents=NetRocks @Contents $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Contents This plugin adds SFTP/SCP/SHELL/NFS/SMB/WebDAV/FTP(S) connectivity to far2l with possibility to add other protocols. To access this module open plugins menu (F11) or use Location (Alt+F1/Alt+F2) menu. Then choose the "NetRocks". See further topics for more detail. ~Location/Plugin menu and sites list~@LocationMenu@ ~Background tasks menu~@BackgroundTasksMenu@ ~Plugin configuration~@PluginOptions@ ~Site connection editor~@SiteConnectionEditor@ ~Command line and remote FAR2L~@CommandLine@ ~Contact information~@Contact@ Tips and tricks: - during any NetRocks copy operation (on any panel must be NetRocks, on other side may be standard far2l) you can switch it to background; - for control/cancel any background action you can use ~Background tasks menu~@BackgroundTasksMenu@ (available only during background action via Plugin commands list by #F11# or via #F9#/Options/Plugins configuration). @LocationMenu $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Location/Plugin menu# After choosing the plugin from Location menu (#Alt+F1#/#Alt+F2#) or from Plugin menu (#F11#) you will see connection sites list. Initially there're no sites defined, but you can add site using ## entry or pressing #Shift+F4#. After being added any site connection can be edited by pressing #F4# or removed by pressing #F8#. You can #export# selected sites settings to filesystem by opening disk directory in another panel and using #F5#/#F6# keys. After site being exported you can #import# it into NetRocks sites list or enter into it as an archive to browse site(s) that present in that config. ~Contents~@Contents@ @BackgroundTasksMenu $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Background tasks menu# This NetRocks-related menu item available in F11 or Plugins configuration but appears only if there're any background tasks spawned. Here you can examine state of each background task and switch to working (usually destination) directory of completed task by selecting them in opened submenu. Note that selection of completed tasks items automatically removes them from this list. ~Contents~@Contents@ @PluginOptions $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Configuration menu# Here you can change some plugin-wide options: #Enable desktop notifications# this options controls if NetRocks will use desktop environment notifications on operation completions or errors. Note that behavior of this notifications partially controlled by FAR. # to execute files remotely when possible# if enabled then pressing on remote executable file will execute it remotely instead of download and run locally. Note that this option only works for protocols that support it (like SFTP/SCP) and doesn't affect non-executable files, like documents, - they will be still downloaded and opened locally. #Smart symlinks copying# if (by default) enabled then NetRocks will translate symlinks paths to refer file that copied in same copy operation, or, if symlinks refer file that is not being copied - then such symlink will be converted to plain file. If disabled then NetRocks will just copy symlinks as is, without any efforts to ensure their validity in the new location. #Use of chmod# change this options if want to have copied files modes to be exactly same as on source files, even in target system umask prevents some mode bits from being set. Or if you want to disable using of chmod at all - for example to avoid other inherited ACLs from being overriden by it. #Connections pool expiration# when exiting from some remote FS navigation NetRocks will keep actual connection active for specified amount of time and if same server connection will be established before expiration - it will use preserved connection instead of establishing new. ~Contents~@Contents@ @SiteConnectionEditor $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Site connection editor# This dialog allows you to create new connection site or modify existing connection site settings. You should select protocol you want use and the define connection settings, like #hostname and port# to connect to, #login mode# and #username and password# if needed. #Display name# can further be used for quick access from the ~command line~@CommandLine@. Also by clicking on #Extra options# button you can modify some ~extra site settings~@ExtraSiteSettings@. Also by clicking on #Protocol options# button you can modify some protocol-specific settings. Also by clicking on #Proxy options# button you can modify generic ~proxy settings~@ProxySettings@. ~SFTP:// and SCP:// protocols specific options~@ProtocolOptionsSFTPSCP@ ~SHELL:// protocols specific options~@ProtocolOptionsSHELL@ ~FTP:// and FTPS:// protocols specific options~@ProtocolOptionsFTP@ ~SMB:// protocol specific options~@ProtocolOptionsSMB@ ~NFS:// protocol specific options~@ProtocolOptionsNFS@ ~DAV:// and DAVS:// protocol specific options~@ProtocolOptionsWebDAV@ ~FILE:// protocol specific options~@ProtocolOptionsFILE@ ~Contents~@Contents@ @CommandLine $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Command line and remote FAR2L# When entering commands in command line when panel displays usual files list you can open connection by typing NetRocks-supported protocol URL, like #sftp://192.168.1.15# or alternatively you can open preconfigured site by invoking its name between triangle brackets and prefixed with net: prefix, like: #net:# When entering commands in command line when panel displays active NetRocks connection of SFTP and SCP protocols - NetRocks will execute them directly on remote host, opening full-featured pseudoterminal for controlling remotely-executed commands. This essentially #allows using NetRocks as SSH client# with FAR2L-extended pseudoterminal. If you're working in GUI-based FAR2L you can run #remote TTY-mode FAR2L# directly in NetRocks SFTP/SCP connected panel and work in that remote FAR2L with user experience of local GUI-based version (full keyboard support, clipboard sharing, desktop notifications) as well as being sure that if connection suddenly drops - remote work will not be killed instantly, since remote terminal-based FAR2L will remain alive and active in background and next time you will reconnect and re-launch far2l - it will prompt to activate that backgrounded FAR2L instance. ~Contents~@Contents@ @ProtocolOptionsSMB $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#SMB:// protocol specific options# This dialog allows you to modify "smb://" protocol specific settings, which is file sharing protocol primarily used in Windows networks. #Workgroup:# here you can specify workgroup name where to search hosts. #Enum network with SMB:# check this option to enable using of libsmbclient to scan for hosts when open empty path ("smb://") #Enum network with NMB:# check this option to enable using of NetRock's builtin NetBios name service scanner to scan for hosts when open empty path ("smb://") ~Contents~@Contents@ @ProtocolOptionsSFTPSCP $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#SFTP:// SCP:// protocols specific options# This dialog allows you to modify "sftp://" and "scp://" protocols specific settings. Here you can enable authentication by key file, change maximum IO block size or enable TCP_NODELAY socket option. #Private key file:# can be used for SSH server instead of 'usual' username:password authentication. Note that in such case password field in the main connection settings is actually 'passphrase' used to access private key file. #IO block size:# increasing this value usually gives performance improvement, especially on uploading files. However not all servers support block size more than 32768 bytes, so use higher values only if you sure it will work with your server. #TCP_NODELAY socket option:# also can improve network performance by eliminating delay used by TCP stack to buffer outgoing data. However in some cases it may also increase network packets rate, so use it when you know that its better. #TCP_QUICKACK socket option:# if enabled, TCP ack packets are sent immediately, rather than delayed that may improve receive performance. #Custom subsystem request/exec# here you can replace default SFTP subsystem handler with specific command, usually its used to get superuser access from sudo'er account by using command line like [sudo /usr/lib/openssh/sftp-server] #Allowed host keys# if non-empty then forces using only specified host key algorithms. Beside of restricting other algorithms this option can be used to allow using some deprecated algorithm e.g. ssh-rsa if server doesn't support modern ones. #Allowed KEX algorithms# if non-empty then forces using only specified key-exchange algorithms. Beside of restricting other algorithms this option can be used to allow using some deprecated algorithm e.g. diffie-hellman-group1-sha1 if server doesn't support modern ones. #Allowed HMAC client->server# if non-empty then forces using only specified HMAC client->server algorithms. Beside of restricting other algorithms this option can be used to allow using some deprecated algorithm e.g. hmac-sha1 if server doesn't support modern ones. #Allowed HMAC server->client# if non-empty then forces using only specified HMAC server->client algorithms. Beside of restricting other algorithms this option can be used to allow using some deprecated algorithm e.g. hmac-sha1 if server doesn't support modern ones. #OpenSSH config files# allows to specify which OpenSSH config files to use for this connection. By default ~~/.ssh/config and /etc/ssh/ssh_config are used. Note that libssh version prior 0.9.0 always parses default config files regardless of this option (so you can only add extra configs), but since version 0.9.0 it's possible to disable/override default config files parsing. To specify more than one file - use colon to separate their paths. ~Contents~@Contents@ @ProtocolOptionsSHELL $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#SHELL:// protocols specific options# This dialog allows you to modify "shell://" protocols specific settings. Note that this protocol is specific in a way it communicates with server - instead of using dedicated file access protocol it runs on server special helper script that handles required text commands, allowing to browse and transfer files. Due to this, SHELL protocol in general picky about server's shell and which standard UNIX tools are accessible there. Currently you may choose from two predefines ways to access server's shell - either using installed on #client SSH client# (ssh) either using #serial port interface#. Note that #serial port way# is very sensitive to losses on communication line and sensitive to printouts noise that may arrive from server's kernel or whatever else. So don't use serial port way unless your surely knows what you need and be careful with file transfers afterwards. You may reduce printouts from kernel by following command: #echo 0 > /proc/sys/kernel/printk# before using serial port for this. And make sure serial port is not used by any other process that otherwise can interfere with NetRocks SHELL protocol. ~Contents~@Contents@ @ProtocolOptionsFTP $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#FTP:// FTPS:// protocols specific options# This dialog allows you to modify "ftp://" and "ftps://" protocols specific settings. Here you can setup encryption and adjust various options of protocol and networking. #Explicit encryption:# can be used to enable encryption through AUTH command if FTP server supports it. Note that this option not enabled for FTPS cuz it always uses encryption. #Minimal encryption protocol:# here you can adjust which protocols can be used for encryption. Note that currently SSL and TLSv1.0 not considered as secure so its not recommended to use them. #Directory list command:# specify exact command to be used to list files in directory. Most FTP server accepts LIST -la, where -la specifies full list format that includes also .dotfiles, however some servers don't understand -la argument and may require this to be changed to LIST to properly list files. #Use MLSD/MLST if possible:# use MLSD and MLST commands to retrieve directory listing or information about particular file. If unchecked only LIST command can be used, that is potentially less reliable and slower. #Passive mode:# selecting this makes NetRocks to use PASV command for data transmission that is most compatible setting. Unchecking it will make NetRocks to use PORT command instead, that uses reversed connection schema and may be incompatible with some firewalls and NAT'ed networks. #Enable commands pipelining:# allow sending multiple FTP commands as once before receiving response for each command. Reduces delays, but some FTP servers may be incompatible with it. #Ensure data connection peer matches server:# if checked NetRocks will check that data connection peer IP address of accepted PORT connection matches to actual IP server address. Also if encryption used this will require data connection's certificate to match with command connection's. #TCP_NODELAY socket option:# can improve network performance by eliminating delay used by TCP stack to buffer outgoing data. However in some cases it may also increase network packets rate, so use it when you know that its better. #TCP_QUICKACK socket option:# if enabled, TCP ack packets are sent immediately, rather than delayed that may improve receive performance. ~Contents~@Contents@ @ProtocolOptionsNFS $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#NFS:// protocol specific options# This dialog allows you to modify "nfs://" protocol specific settings. Here you can override default user credentials: host, UID, GID and comma-separated list of additional group IDs. Note that some old libnfs may not support this options - in such case they will have no effect. ~Contents~@Contents@ @ProtocolOptionsFILE $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#FILE:// protocol specific options# Pseudo-protocol "file:" has not additional options. You can use "file:" to access to local file system and use background copy operations: - unlike copying by base far2l, the NetRocks can do background copying; - type in far2l's command line command "file:" to show local filesystem via NetRocks; - during any NetRocks copy operation (on any panel must be NetRocks, on other side may be standard far2l) you can switch it to background; - for control/cancel any background action you can use ~Background tasks menu~@BackgroundTasksMenu@ (available only during background action via Plugin commands list by #F11# or via #F9#/Options/Plugins configuration). ~Contents~@Contents@ @ExtraSiteSettings $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Extra site settings# This dialog allows you to modify some extra (not frequently used) site settings: To prevent connection from disconnect-due-to-idle - set non-zero #keepalive# period. Apply remote files timestamps adjustement. Change #codepage# being used by server. It's possible to #execute specific command# when opening or closing site connection, and that command can, for example, do mounting of some resource that is to be accessed using this connection site. This command will have defined as environment fields of host (#$HOST#), port (#$PORT#), username (#$USER#), password (#$PASSWORD#) and additional extra string configured in this dialog (#$EXTRA#). In this dialog its also possible to define amount of time NetRocks will wait for completion of this command (if command will not complete during that time - timeout error will be raised). Special environment variable #$SINGULAR# equals to 1 in case command being executed on a connection that is singular to specified protocol/user/host/port across all NetRocks instances, so you may use this to initialize/cleanup shared things. In case your init/cleanup have to exchange some data - save it into file indicated by $STORAGE environment variable, and then don't forget to delete this file from cleanup script running in singular context. ~Contents~@Contents@ @ProtocolOptionsWebDAV $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#DAV:// and DAVS:// protocol specific options# This dialog allows you to modify "dav://" and "davs://" protocol specific settings: enable connect via HTTP/HTTPS proxy with optional proxy authentication. ~Contents~@Contents@ @ProxySettings $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Proxy settings dialog# This dialog allows you to specify generic proxy settings for connection. Currently such generic support is implemented by using of external 'proxifier' tool. Two such tools are supported: #proxychains# and #tsocks#. So in order to use this option you have to install any of them (however proxychains is recommended) and edit its configuration in this dialog. This configuration will be used only with chosen connection and stored in site connection settings. ~Contents~@Contents@ @Contact $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Contact information# elfmz #http://github.com/elfmz ~Contents~@Contents@ far2l-2.6.5~beta+ds/NetRocks/configs/plug/helpr.hlf000066400000000000000000001025701477431623700221450ustar00rootroot00000000000000.Language=Russian,Russian (Русский) .PluginContents=NetRocks @Contents $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Содержание Плагин расширяет функциональность far2l, добавляя поддержку протоколов SFTP/SCP/SHELL/NFS/SMB/WebDAV/FTP(S). Чтобы получить доступ к этому модулю, откройте меню плагинов (#F11#) или используйте меню перехода (#Alt+F1#/#Alt+F2#). Затем выберите "NetRocks". Подробнее см. в следующих темах. ~Меню перехода, меню плагинов и список подключений~@LocationMenu@ ~Фоновые задачи NetRocks~@BackgroundTasksMenu@ ~Настройки NetRocks~@PluginOptions@ ~Настройки подключения~@SiteConnectionEditor@ ~Командная строка и удаленный FAR2L~@CommandLine@ ~Контакты~@Contact@ Советы и хитрости: - во время любой операции копирования NetRocks (на одной панели должен быть NetRocks, другая может быть стандартной файловой панелью far2l) вы можете переключить её в фоновый режим; - для управления/отмены любого фонового действия вы можете использовать меню ~Фоновые задачи NetRocks~@BackgroundTasksMenu@ (доступно через меню плагинов (#F11#) или конфигурации плагинов (#Alt+Shift+F9#), если запущена хотя бы одна фоновая задача). @LocationMenu $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Меню перехода и меню плагинов# После выбора NetRocks из меню перехода (#Alt+F1#/#Alt+F2#) или из меню плагинов (#F11#) вы увидите список подключений (сайтов). Изначально список пуст, но вы можете добавить сайт, выбрав пункт #<Создать новое подключение># или нажав #Shift+F4#. После добавления любое подключение можно отредактировать нажатием #F4# или удалить нажатием #F8#. Вы можете #экспортировать# настройки выбранных подключений в файловую систему посредством #F5#/#F6#, открыв нужный каталог в другой панели. После экспорта сайта вы можете #импортировать# его обратно в список подключений NetRocks, либо войти в него как в архив, чтобы просмотреть сайт(ы), которые присутствуют в этом конфиге. ~Содержание~@Contents@ @BackgroundTasksMenu $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Фоновые задачи NetRocks# Этот пункт, относящийся к NetRocks, доступен из меню плагинов (#F11#) или конфигурации плагинов (#Alt+Shift+F9#), но появляется только в том случае, если запущены какие-либо фоновые задачи. Здесь вы можете просмотреть состояние каждой фоновой задачи и перейти в рабочий (обычно конечный) каталог завершенной задачи, выбрав их в открывшемся подменю. Обратите внимание, что выбор завершенных задач автоматически удаляет их из этого списка. ~Содержание~@Contents@ @PluginOptions $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки NetRocks# Здесь вы можете изменить некоторые общие для плагина параметры: #Включить уведомления рабочего стола#. Этот параметр управляет тем, будет ли NetRocks использовать уведомления рабочего стола при завершении операций или ошибках. Обратите внимание, что поведение этих уведомлений частично контролируется FAR2L. # исполняет файлы на сервере если возможно#. Если опция включена, то нажатие на удаленном исполняемом файле приведет к его удаленному выполнению вместо загрузки и запуска локально. Обратите внимание, что эта опция работает только для протоколов, которые это поддерживают (например, SFTP/SCP), и не влияет на неисполняемые файлы, такие как документы, - они все равно будут загружены и открыты локально. #Умное копирование символических ссылок#. При включённой опции (по умолчанию) во время копирования NetRocks изменяет символические ссылки так, чтобы они указывали на скопированные в рамках той же операции файлы. Если же ссылка ведёт на файл, который не копируется, NetRocks преобразует её в обычный файл. При отключённой опции NetRocks копирует символические ссылки в неизменном виде, не предпринимая попыток адаптировать их к новому расположению. #Использовать chmod#. Измените эту опцию, если хотите, чтобы права скопированных файлов были бы точно такими же, как у исходных файлов, даже если в целевой системе umask не позволяет установить некоторые биты режима. Или чтобы отключить использование chmod вообще для предотвращения перезаписи унаследованных прав доступа (ACL). #Таймаут неиспользуемых соединений#. При выходе из навигации по удаленной файловой системе NetRocks будет поддерживать фактическое соединение активным в течение указанного периода времени. Если до истечения этого срока будет установлено соединение с тем же сервером, NetRocks будет использовать имеющееся соединение вместо того, чтобы устанавливать новое. ~Содержание~@Contents@ @SiteConnectionEditor $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки подключения# Этот диалог позволяет создать новое или изменить настройки существующего подключения. Вы должны выбрать протокол, который хотите использовать, и определить параметры подключения, такие как #имя сервера и порт# для подключения, #режим входа# и #имя пользователя и пароль#, если необходимо. #Имя подключения# может в дальнейшем использоваться для быстрого доступа из ~командной строки~@CommandLine@. Также, кнопка #Доп. настройки# позволяет изменить ~Дополнительные настройки соединения~@ExtraSiteSettings@. Кнопка #Настр. протокола# позволяет изменить специфичные для конкретного протокола настройки. Кнопка #Настр. прокси# позволяет изменить общие ~Настройки прокси~@ProxySettings@. Подробнее о протокол-специфичных настройках: ~Настройки SFTP:// и SCP://~@ProtocolOptionsSFTPSCP@ ~Настройки SHELL://~@ProtocolOptionsSHELL@ ~Настройки FTP:// и FTPS://~@ProtocolOptionsFTP@ ~Настройки SMB://~@ProtocolOptionsSMB@ ~Настройки NFS://~@ProtocolOptionsNFS@ ~Настройки DAV:// и DAVS://~@ProtocolOptionsWebDAV@ ~Настройки FILE://~@ProtocolOptionsFILE@ ~Содержание~@Contents@ @CommandLine $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Командная строка и удаленный FAR2L# Вы можете установить соединение, находясь в обычной файловой панели, прямо из командной строки: либо введя URL протокола, поддерживаемого NetRocks (например #sftp://192.168.1.15#), либо указав имя подключения ~предварительно сконфигурированного~@SiteConnectionEditor@ сайта в треугольных скобках и префиксом net (например: #net:#). При вводе команд в командной строке, когда на панели NetRocks отображается активное соединение по протоколам SFTP и SCP, NetRocks выполнит их непосредственно на удаленном хосте, открыв полнофункциональный псевдотерминал для управления удаленно выполняемыми командами. По сути, это позволяет использовать #NetRocks как SSH-клиент# с FAR2L-расширенным псевдотерминалом. Если вы работаете в FAR2L с графическим интерфейсом, вы можете запустить #удаленный FAR2L в TTY-режиме# непосредственно из панели NetRocks с SFTP/SCP-подключением и работать в этом удаленном FAR2L с пользовательскими возможностями локальной GUI-версии (полная поддержка клавиатуры, совместное использование буфера обмена, уведомления на рабочем столе), а также быть уверенным, что в случае внезапного разрыва соединения удаленная работа не будет мгновенно прервана, так как удаленный терминальный FAR2L останется активным в фоновом режиме, и в следующий раз, когда вы снова подключитесь и перезапустите FAR2L - он предложит активировать этот фоновый экземпляр FAR2L. ~Содержание~@Contents@ @ProtocolOptionsSMB $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки SMB://# Этот диалог позволяет изменять настройки протокола "smb://", который является протоколом совместного доступа к файлам и используется в основном в сетях Windows. #Рабочая группа:# здесь вы можете указать имя рабочей группы, в которой следует искать хосты. #Использовать SMB для обзора сети:# установите этот флажок, чтобы включить использование libsmbclient для сканирования хостов при открытии пустого пути ("smb://"). #Использовать NMB для обзора сети:# установите этот флажок, чтобы включить встроенный в NetRocks сканер службы имен NetBIOS для сканирования хостов при открытии пустого пути ("smb://"). ~Содержание~@Contents@ @ProtocolOptionsSFTPSCP $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки SFTP:// и SCP://# Этот диалог позволяет изменять специфичные для "sftp://" и "scp://" настройки протоколов. Здесь вы можете включить аутентификацию по ключу, изменить максимальный размер блока чтения/записи или включить опцию TCP_NODELAY. #Файл приватного ключа:# может быть использован для сервера SSH вместо "обычной" аутентификации по имени пользователя и паролю. Обратите внимание, что в этом случае поле пароля в основных ~настройках подключения~@SiteConnectionEditor@ фактически является "парольной фразой", используемой для доступа к файлу приватного ключа. #Размер блока ввода-вывода:# увеличение этого значения обычно улучшает производительность, особенно при загрузке файлов. Однако не все серверы поддерживают размер блока больше 32768 байт, поэтому используйте более высокие значения только в том случае, если вы уверены, что они будут работать с вашим сервером. #Опция сокета TCP_NODELAY:# также может улучшить производительность сети, устраняя задержки, используемые стеком TCP для буферизации исходящих данных. Однако в некоторых случаях это может также увеличить скорость передачи сетевых пакетов, поэтому используйте её, только если уверены, что это улучшит работу. #Опция сокета TCP_QUICKACK:# при включении пакеты TCP-подтверждения (ACK) отправляются немедленно, а не с задержкой, что может улучшить производительность приема. #Запрос особой подсистемы:# здесь вы можете заменить стандартный обработчик подсистемы SFTP на определенную команду, обычно используется для получения доступа суперпользователя от sudoers учетной записи с помощью командной строки, например [sudo /usr/lib/openssh/sftp-server]. #Разрешенные ключи:# если поле не пустое, то принудительно используются только указанные алгоритмы ключей хоста. Помимо ограничения других алгоритмов, эта опция может использоваться для разрешения использования некоторых устаревших алгоритмов, например, ssh-rsa, если сервер не поддерживает современные. #Разрешенные KEX:# если поле не пустое, то принудительно используются только указанные алгоритмы обмена ключами. Помимо ограничения других алгоритмов, эта опция может использоваться для разрешения использования некоторых устаревших алгоритмов, например, diffie-hellman-group1-sha1, если сервер не поддерживает современные. #Разрешенные HMAC клиента:# если поле не пустое, то принудительно используются только указанные алгоритмы HMAC клиент->сервер. Помимо ограничения других алгоритмов, эта опция может использоваться для разрешения использования некоторых устаревших алгоритмов, например, hmac-sha1, если сервер не поддерживает современные. #Разрешенные HMAC сервера:# если поле не пустое, то принудительно используются только указанные алгоритмы HMAC сервер->клиент. Помимо ограничения других алгоритмов, эта опция может использоваться для разрешения использования некоторых устаревших алгоритмов, например, hmac-sha1, если сервер не поддерживает современные. #OpenSSH конфиги:# позволяет указать, какие конфигурационные файлы OpenSSH использовать для этого подключения. По умолчанию используются ~~/.ssh/config и /etc/ssh/ssh_config. Обратите внимание, что версии libssh до 0.9.0 всегда анализируют файлы конфигурации по умолчанию независимо от этой опции (поэтому вы можете только добавлять дополнительные конфигурации), но начиная с версии 0.9.0 можно отключить/переопределить анализ файлов конфигурации по умолчанию. Для указания нескольких файлов используйте двоеточие для разделения путей. ~Содержание~@Contents@ @ProtocolOptionsSHELL $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки SHELL://# Этот диалог позволяет изменять настройки протокола "shell://". Обратите внимание на особенность протокола при взаимодействии с сервером - вместо использования специального протокола доступа к файлам он запускает на сервере вспомогательный скрипт, который обрабатывает посылаемые ему текстовые команды, позволяя просматривать и передавать файлы. В связи с этим протокол SHELL в целом требователен к оболочке сервера и к тому, какие стандартные инструменты UNIX в ней доступны. В настоящее время вы можете выбрать один из двух предопределенных способов доступа к оболочке сервера - либо используя установленный #на клиенте SSH-клиент# (ssh), либо используя #интерфейс последовательного порта#. Обратите внимание, что способ с последовательным портом очень чувствителен к потерям в линии связи и шуму вывода, который может поступать от ядра сервера или от других источников. Поэтому не используйте способ с последовательным портом, если вы не уверены в необходимости, и будьте осторожны с передачей файлов впоследствии. Вы можете снизить количество выводов ядра, выполнив команду: #echo 0 > /proc/sys/kernel/printk# перед использованием последовательного порта для этого. И убедитесь, что последовательный порт не используется другим процессом, который может помешать работе протокола NetRocks SHELL. ~Содержание~@Contents@ @ProtocolOptionsFTP $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки FTP:// и FTPS://# Этот диалог позволяет изменять специфичные для "ftp://" и "ftps://" настройки протоколов. Здесь вы можете настроить шифрование и различные параметры протокола и сети. #Включить шифрование:# можно использовать для включения шифрования через команду AUTH, если сервер FTP его поддерживает. Обратите внимание, что эта опция недоступна для протокола FTPS, так как он всегда использует шифрование. #Минимальный протокол шифрования:# здесь можно настроить, какие протоколы могут использоваться для шифрования. Обратите внимание, что в настоящее время SSL и TLSv1.0 не считаются безопасными, поэтому их использование не рекомендуется. #Команда листинга директории:# укажите точную команду, которая будет использоваться для перечисления файлов в каталоге. Большинство серверов FTP принимают LIST -la, где -la задает полный формат списка, включающий также скрытые файлы (.dotfiles), однако некоторые серверы не понимают аргумент -la и могут потребовать изменить его на LIST для правильного перечисления файлов. #Использовать MLSD/MLST если возможно:# при возможности используйте команды MLSD и MLST для получения списка каталога или информации о конкретном файле. Если этот параметр не выбран, будет использоваться только команда LIST, что медленнее и потенциально менее надежно. #Пассивный режим:# выбор этой опции заставит NetRocks использовать команду PASV для передачи данных, что является наиболее совместимым режимом. При отключенной опции NetRocks пошлёт команду PORT, что использует схему обратного подключения и может быть несовместимо с некоторыми брандмауэрами и сетями с NAT. #Использовать конвейеризацию команд:# позволяет отправлять несколько команд FTP одновременно, прежде чем получать ответ на каждую из них. Уменьшает задержки, но некоторые серверы FTP могут быть с этим несовместимы. #Разрешить данные только с того же сервера:# при включенной опции NetRocks удостоверится, что IP адрес пира принятого им PORT-подключения соответствует IP адресу настоящего сервера, с которым соединение изначально инициировалось. Также, если используется шифрование, он потребует соответствия сертификатов канала данных и управляющего канала. #Опция сокета TCP_NODELAY:# может улучшить производительность сети, устраняя задержки, используемые стеком TCP для буферизации исходящих данных. Однако в некоторых случаях это может также увеличить скорость передачи сетевых пакетов, поэтому используйте её, только если уверены, что это улучшит работу. #Опция сокета TCP_QUICKACK:# при включении пакеты TCP-подтверждения (ACK) отправляются немедленно, а не с задержкой, что может улучшить производительность приема. ~Содержание~@Contents@ @ProtocolOptionsNFS $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки NFS://# Этот диалог позволяет изменять настройки протокола "nfs://". Здесь вы можете переопределить стандартные учетные данные пользователя: хост, UID, GID и список дополнительных идентификаторов групп, разделенный запятыми. Обратите внимание, что некоторые старые libnfs могут не поддерживать эти параметры - в этом случае они не будут иметь никакого эффекта. ~Содержание~@Contents@ @ProtocolOptionsFILE $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки FILE://# Псевдопротокол «file:» не имеет дополнительных опций. Вы можете использовать "file:" для доступа к локальной файловой системе, осуществляя фоновые операции копирования: - в отличие от копирования через базовый far2l, NetRocks может выполнять фоновое копирование; - введите в командной строке far2l команду "file:" для открытия локальной файловой системы в NetRocks; - во время любой операции копирования NetRocks (на одной панели должен быть NetRocks, другая может быть стандартной файловой панелью far2l) вы можете переключить её в фоновый режим; - для управления/отмены любого фонового действия вы можете использовать меню ~Фоновые задачи NetRocks~@BackgroundTasksMenu@ (доступно через меню плагинов (#F11#) или конфигурации плагинов (#Alt+Shift+F9#), если запущена хотя бы одна фоновая задача). ~Содержание~@Contents@ @ExtraSiteSettings $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Дополнительные настройки соединения# Этот диалог позволяет изменять некоторые дополнительные (нечасто используемые) настройки подключения. Настройка #Держать живым# (keep-alive) предназначена для поддержания активного соединения, даже если нет обмена данными. Это полезно в ситуациях, когда соединение может прерваться из-за длительного бездействия. Чтобы предотвратить преждевременный разрыв, установите ненулевое значение, задающее периодичность отправки пакетов в секундах. #Поправка времени# может использоваться для корректировки временных меток файлов с удалённого хоста. Доступно изменение #Кодировки#, используемой сервером. Можно #выполнить определенную команду# при открытии или закрытии соединения с сайтом, и эта команда может, например, смонтировать какой-либо ресурс, к которому должен быть получен доступ с использованием этого соединения. В этой команде будут доступны переменные среды: #$HOST#, #$PORT#, #$USER#, #$PASSWORD# и дополнительная строка #$EXTRA#, настроенная в этом диалоге. В этом диалоге также можно задать время ожидания завершения этой команды (если команда не будет выполнена за отпущенное время, будет выдана ошибка таймаута). Специальная переменная среды #$SINGULAR# равна 1 в том случае, если команда выполняется для единственного соединения, уникального для указанного протокола, пользователя, хоста и порта по всем экземплярам NetRocks, что позволяет инициализировать/очищать общие ресурсы. Если ваша инициализация/очистка должна обмениваться какими-то данными, сохраните их в файл, указанный переменной среды #$STORAGE#, а затем не забудьте удалить этот файл из скрипта очистки, выполняемого в уникальном контексте. ~Содержание~@Contents@ @ProtocolOptionsWebDAV $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки DAV:// и DAVS://# Этот диалог позволяет изменять специфичные для "dav://" и "davs://" настройки протоколов: задать строку user agent, и разрешить подключение через HTTP/HTTPS-прокси с опциональной авторизацией на прокси-сервере. ~Содержание~@Contents@ @ProxySettings $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Настройки прокси# Этот диалог позволяет задать общие настройки прокси для подключения. В настоящее время такая поддержка реализована с помощью внешнего инструмента 'проксирования' (proxifier). Поддерживаются два таких инструмента: #proxychains# и #tsocks#. Поэтому для использования этой опции вам нужно установить любой из них (рекомендуется proxychains) и отредактировать его конфигурацию в этом диалоге. Эта конфигурация будет использоваться только с выбранным подключением и сохранена в настройках подключения к сайту. ~Содержание~@Contents@ @Contact $^#NetRocks plugin# $^#Version 1.0# $^#Copyright (C) 2019 elfmz# $^#Контактная информация# elfmz #http://github.com/elfmz ~Содержание~@Contents@ far2l-2.6.5~beta+ds/NetRocks/configs/plug/rus.lng000066400000000000000000000266441477431623700216620ustar00rootroot00000000000000.Language=Russian,Russian "NetRocks" "SFTP" "&ОК" "О&тмена" "&Еще раз" "&Пропустить" "Ошибка" "В&ыше" "Настройки NetRocks" "Включить уведомления рабочего стола" "<&ENTER> исполняет файлы на сервере если возможно" "Умное копирование символических &ссылок" "Использовать &chmod:" "Автоматически" "Всегда (игнорировать umask)" "Никогда" "Запоминать рабочий каталог в настройках сайта" "Таймаут неиспользуемых соединений (сек.):" "Запомнить мой выбор для этой операции" "Произошла ошибка" "Не могу подключиться" "Неправильно указан путь" "Авторизация" "Авторизация - еще раз" "Авторизуемся на:" "Переавторизуемся на:" "&Протокол :" "&Имя сервера :" "&Номер порта :" "&Режим входа :" "Без пароля" "Спросить пароль" "Сохраненный пароль" "Имя &пользователя :" "П&ароль входа :" "&Рабочая директория :" "Держать &живым, сек :" "К&одировка :" "Поправка &времени, сек:" "&Имя подключения :" "&Доп. настройки" "Настр. протокола" "Настр. прокси" "&Сохранить" "Под&ключить" "Сохр./под&ключить" "<Создать новое подключение>" "Настройки подключения" "Скачать с сервера" "Закачать на сервер" "Перекачать между серверами" "Скачать и удалить" "Закачать и удалить" "Перекачать и удалить" "Переименовать на сервере" "Скачать выделенные файлы &в:" "Закачать выделенные файлы &в:" "Перекачать выделенные файлы &в:" "Переместить скачанные файлы в:" "Переместить закачанные файлы в:" "Переместить перекачанные файлы в:" "Переименовать выделенные файлы &в" "При перезаписи:" "&Спросить" "&Перезаписать" "&Пропустить" "&Перезаписать если новее" "&Продолжить" "Создать новое имя" "Нужно выбрать действие перезаписи" "Текущий файл" "Размер файла" "Общий размер" "Количество" " Из" "Времени на файл ПОТРАЧЕНО:" "ОСТАЛОСЬ:" "Всего времени ПОТРАЧЕНО:" "Скорость () ТЕКУЩАЯ:" "СРЕДНЯЯ:" "В &фон" "Пауза" "Продолжить" "&Запрос при ошибке" "Скачать" "Закачать" "Перекачать" "Скачать-удалить" "Закачать-удалить" "Перекачать-удалить" "Переименовать" "Применить изменения" "Файл назначения уже существует:" "Инфо про исходный файл:" "Инфо про файл назначения" "Варианты действий" "Удалить с сервера" "Удалить выбранные объекты из:" "Удалить" "Удалить подключение(-я)" "Удалить настройки выбранных подключений?" "Скопировать подключение(-я)" "Скопировать настройки выбранных подключений?" "Переместить подключение(-я)" "Переместить настройки выбранных подключений?" "Импорт подключений" "Импортировать выбранные файлы подключений?" "Поглощение подключений" "Поглотить выбранные файлы подключений?" "Экспорт подключений" "Экспортировать выбранные подключения в файлы?" "Экспорт подключений с удалением" "Экспортировать с удалением выбранные подключения в файлы?" "Создать директорию" "Введите имя директории:" "Создать" "Подключаемся к сайту" "Опрашиваем путь" "Применяем режим" "Сканируем директорию" "Создаем директорию" "Исполняем команду" "Опрос ссылки" "Отмена операции" "Подтвердите отмену операции" "&Отменить операцию" "&Продолжить" "Отменяем операцию..." "&Оборвать соединение" "NetRocks () завершено" "NetRocks () ошибка" "закачивание" "скачивание" "перекачивание" "переименование" "Ошибка скачивания" "Ошибка закачивания" "Ошибка перекачивания" "Ошибка опроса" "Ошибка проверки директории" "Ошибка сканирования директории" "Ошибка создания директории" "Ошибка переименование" "Ошибка удаления директории" "Ошибка удаления" "Ошибка установки времени файла" "Ошибка установки режима файла" "Ошибка опроса ссылки" "Ошибка создания ссылки" "Ошибка:" "Сбойный объект:" "Сбой на сайте:" "Локальная ФС" "ПОПЫТОК: %llu ПРОПУСКОВ: %llu" "Протокол не поддерживает удаленные команды" "Можно использовать только команды: cd, pushd, popd" "исполнение команды" "Подтвердить подлинность сервера" "Имя сайта:" "Текущий ключ сервера:" "Разрешить разок" "Разрешить навсегда" "Фоновые задачи NetRocks" "АКТИВНО" "ПАУЗА" "ЗАВЕРШЕНО" "ПРЕРВАНО" "Нет фоновых задач" "Подтверждение выхода NetRocks" "Закрытие FAR прервет %u фоновые задачи." "Вы действительно уверены что ходите закрыть FAR?" "&Фоновые задачи" "Настройки протокола SFTP" "Настройки протокола SCP" "&Авторизация:" "&Имя пользователя и пароль" "И&нтерактивный логин" "Путь к &файлу ключа:" "&SSH-agent (по ${SSH_AUTH_SOCK})" "Путь к файлу &ключа:" "С&жатие:" "Без сжатия" "Сжимать входящий траффик" "Сжимать исходящий траффик" "Сжимать весь траффик" "Запрос особой подсистемы:" "Размер блока чтения, байт:" "Размер блока записи, байт:" "Включить опцию &TCP_NODELAY" "Включить опцию TCP_&QUICKACK" "Игнорировать &ошибки времени и режимов" "Попыток подключиться, &раз:" "Таймаут подключения, &секунд:" "Разрешенные &ключи:" "Разрешенные KE&X:" "Разрешенные HMAC к&лиента:" "Разрешенные HMAC с&ервера:" "OpenSS&H конфиги:" "(Обычные файлы конфигурации)" "(Не использовать конфиги)" "(Указать пути к конфигам)" "Включить защиту (если вы не доверяете серверу)" "Дополнительные настройки соединения" "Команда, исполняемая при подключении:" "Команда, исполняемая при отключении:" "Дополнительная строка, передаваемая команде:" "Ограничение времени исполнения команды, секунд:" "Доп. переменные среды, доступные процессу команды:" "Настройки протокола SMB" "Рабочая &группа:" "Использовать &SMB для обзора сети" "Использовать &NMB для обзора сети" "Настройки протокола NFS" "Особые параметры идентификации:" "&Хост:" "&UID:" "&GID:" "Г&руппы:" "Настройки протокола WebDAV" "Пользовательский агент:" "Подключение через прокси:" "Прокси сервер:" "Порт прокси:" "Авторизация прокси сервера:" "Пользователь:" "Пароль:" "Изменить режим файла" "Изменить режим выбранного &объекта:" "Изменить режим %u объектов выбранных в:" "Являющегося &ссылкой на:" "И &рекурсивно в поддиректориях" " Владение (только просмотр) " "В&ладелец:" "Гр&уппа:" "(несколько значений)" "(неизвестный владелец)" "(неизвестная группа)" " Права доступа " "Пользователь:" "Группа:" "Остальные:" "&R" "&W" "&X" "R" "W" "X" "&SUID" "SGID" "Sticky" "Исходно&е" " Timestamp YYYY-MM-DD hh:mm:ss.ms (read only) " "Время последнего доступа:" "Время последней модификации:" "Время изменения статуса:" "Настройки протокола FTP" "Настройки протокола FTPS" "&Включить шифрование" "Минимальный протокол &шифрования:" "Команда &листинга директории:" "&Кодовая страница сервера" "&Пассивный режим" "<Автоопределение>" "Использовать &MLSD/MLST если возможно" "Использовать конвейеризацию &команд" "Разрешить &данные только с того же сервера" "Включить опцию &TCP_NODELAY" "Включить опцию TCP_&QUICKACK" "Настройки протокола SHELL" "Способ &доступа:" "Настройки способа доступа" "Настройки прокси" "Не использовать проксификатор" "Используемый &проксификатор:" "&Редактировать конфигурацию проксификатора" "Хранилище AWS S3" "Пользовательский агент" "Регион" "Использовать прокси" "Хост прокси" "Порт прокси" "Прокси с аутентификацией" "Имя пользователя прокси" "Пароль прокси" far2l-2.6.5~beta+ds/NetRocks/src/000077500000000000000000000000001477431623700165235ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/BackgroundTasks.cpp000066400000000000000000000042761477431623700223250ustar00rootroot00000000000000#include #include #include #include "BackgroundTasks.h" #include "Globals.h" static std::map > g_background_tasks; static std::mutex g_background_tasks_mutex; static unsigned long g_background_task_id = 0; void AddBackgroundTask(std::shared_ptr &task) { std::lock_guard locker(g_background_tasks_mutex); ++g_background_task_id; g_background_tasks[g_background_task_id] = task; } size_t CountOfAllBackgroundTasks() { std::lock_guard locker(g_background_tasks_mutex); return g_background_tasks.size(); } size_t CountOfPendingBackgroundTasks() { std::lock_guard locker(g_background_tasks_mutex); size_t out = 0; for (auto &task : g_background_tasks) { if (task.second->GetStatus() == BTS_ACTIVE || task.second->GetStatus() == BTS_PAUSED) { ++out; } } return out; } void GetBackgroundTasksInfo(BackgroundTasksInfo &info) { std::lock_guard locker(g_background_tasks_mutex); for (auto &it : g_background_tasks) { info.emplace_back(BackgroundTaskInfo {it.first, it.second->GetStatus(), it.second->GetInformation()}); } } void ShowBackgroundTask(unsigned long id) { std::lock_guard locker(g_background_tasks_mutex); auto it = g_background_tasks.find(id); if ( it != g_background_tasks.end()) { it->second->Show(); if (it->second->GetStatus() != BTS_ACTIVE && it->second->GetStatus() != BTS_PAUSED) { bool destination_directory; std::wstring destination_path = StrMB2Wide(it->second->GetDestination(destination_directory)); g_background_tasks.erase(it); FarPanelLocation pl = {}; pl.PluginName = destination_directory ? nullptr : G.info.ModuleName; pl.Path = destination_path.c_str(); fprintf(stderr, "ShowBackgroundTask: opening %ls '%ls'\n", destination_directory ? L"directory" : pl.PluginName, destination_path.c_str()); G.info.Control(PANEL_ACTIVE, FCTL_SETPANELLOCATION, 0, (LONG_PTR)(void *)&pl); } } } void AbortBackgroundTask(unsigned long id) { std::lock_guard locker(g_background_tasks_mutex); auto it = g_background_tasks.find(id); if ( it != g_background_tasks.end()) it->second->Abort(); } far2l-2.6.5~beta+ds/NetRocks/src/BackgroundTasks.h000066400000000000000000000015451477431623700217660ustar00rootroot00000000000000#pragma once #include #include #include enum BackgroundTaskStatus { BTS_ACTIVE, BTS_PAUSED, BTS_COMPLETE, BTS_ABORTED }; struct IBackgroundTask { virtual ~IBackgroundTask() {}; virtual BackgroundTaskStatus GetStatus() = 0; virtual std::string GetInformation() = 0; virtual std::string GetDestination(bool &directory) = 0; virtual void Show() = 0; virtual void Abort() = 0; }; struct BackgroundTaskInfo { unsigned long id; BackgroundTaskStatus status; std::string information; }; typedef std::vector BackgroundTasksInfo; void AddBackgroundTask(std::shared_ptr &task); size_t CountOfAllBackgroundTasks(); size_t CountOfPendingBackgroundTasks(); void GetBackgroundTasksInfo(BackgroundTasksInfo &info); void ShowBackgroundTask(unsigned long id); void AbortBackgroundTask(unsigned long id); far2l-2.6.5~beta+ds/NetRocks/src/ConnectionsPool.cpp000066400000000000000000000053221477431623700223450ustar00rootroot00000000000000#include #include "Globals.h" #include "ConnectionsPool.h" static time_t GetGlobalConfigExpiration() { return G.GetGlobalConfigInt("ConnectionsPoolExpiration", 30); } ConnectionsPool::~ConnectionsPool() { PurgeAll(); WaitThread(); } void ConnectionsPool::Put(const std::string &id, std::shared_ptr &host) { if (id.empty() || !host || !host->Alive()) { return; } std::vector > purgeds; // destroy hosts out of lock std::lock_guard locker(_mutex); auto &pp = _id_2_pooled_host[id]; if (pp.host) { purgeds.emplace_back(pp.host); } pp.ts = time(nullptr); pp.host = host; PurgeExpired(purgeds); UpdateThreadState(); } std::shared_ptr ConnectionsPool::Get(const std::string &id) { std::shared_ptr out; std::lock_guard locker(_mutex); auto it = _id_2_pooled_host.find(id); if (it != _id_2_pooled_host.end()) { out = it->second.host; _id_2_pooled_host.erase(it); UpdateThreadState(); } return out; } void ConnectionsPool::PurgeAll() { std::vector > purgeds; // destroy hosts out of lock std::lock_guard locker(_mutex); for (auto &it : _id_2_pooled_host) { purgeds.emplace_back(it.second.host); } _id_2_pooled_host.clear(); _cond.notify_all(); } void ConnectionsPool::PurgeExpired(std::vector > &purgeds) { const time_t now = time(NULL); const time_t expiration = GetGlobalConfigExpiration(); for (auto it = _id_2_pooled_host.begin(); it != _id_2_pooled_host.end(); ) { if (now - it->second.ts >= expiration) { purgeds.emplace_back(it->second.host); it = _id_2_pooled_host.erase(it); } else ++it; } } time_t ConnectionsPool::EstimateTimeToSleep() { const time_t now = time(NULL); const time_t expiration = GetGlobalConfigExpiration(); time_t out = expiration / 2; for (const auto &it : _id_2_pooled_host) { time_t t = (now - it.second.ts); if (t >= expiration) { out = 0; break; } t = expiration - t; if (out > t) { out = t; } } return out + 1; } void *ConnectionsPool::ThreadProc() { std::vector > purgeds; std::unique_lock lock(_mutex); while (!_id_2_pooled_host.empty()) { time_t sleep_time = EstimateTimeToSleep(); _cond.wait_for(lock, std::chrono::seconds(sleep_time)); PurgeExpired(purgeds); if (!purgeds.empty()) { lock.unlock(); purgeds.clear(); // destroy hosts out of lock lock.lock(); } } return nullptr; } void ConnectionsPool::UpdateThreadState() { if (!_id_2_pooled_host.empty()) { if (StartThread()) { return; } } _cond.notify_all(); } void ConnectionsPool::OnGlobalSettingsChanged() { std::unique_lock lock(_mutex); _cond.notify_all(); } far2l-2.6.5~beta+ds/NetRocks/src/ConnectionsPool.h000066400000000000000000000013651477431623700220150ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "Host/Host.h" #include class ConnectionsPool : Threaded { struct PooledHost { time_t ts; std::shared_ptr host; }; std::map _id_2_pooled_host; std::mutex _mutex; std::condition_variable _cond; void UpdateThreadState(); void PurgeExpired(std::vector > &purgeds); time_t EstimateTimeToSleep(); protected: virtual void *ThreadProc(); public: ~ConnectionsPool(); void Put(const std::string &id, std::shared_ptr &host); std::shared_ptr Get(const std::string &id); void PurgeAll(); void OnGlobalSettingsChanged(); }; far2l-2.6.5~beta+ds/NetRocks/src/Erroring.cpp000066400000000000000000000031711477431623700210200ustar00rootroot00000000000000#include #include #include "Erroring.h" unsigned char InitNetrocksVerbosity() { const char *verbose_env = getenv("NETROCKS_VERBOSE"); if (verbose_env) { switch (*verbose_env) { case 'y': case 'Y': case 't': case 'T': return 1; default: if (*verbose_env >= '1' && *verbose_env <= '9') { return 1 + (*verbose_env - '1'); } } } return 0; } unsigned char g_netrocks_verbosity = InitNetrocksVerbosity(); static std::string FormatProtocolError(const char *msg, const char *info = nullptr, int err = 0) { std::string s = msg; if (info) { s+= " - "; s+= info; } if (err) { char sz[64]; snprintf(sz, sizeof(sz), " (%d)", err); s+= sz; } return s; } static const std::string &TeeStr(const std::string &s) { fprintf(stderr, "%d: %s\n", getpid(), s.c_str()); return s; } static const char *TeeSZ(const char *s) { fprintf(stderr, "%d: %s\n", getpid(), s); return s; } ProtocolError::ProtocolError(const char *msg, const char *info, int err) : std::runtime_error(TeeStr(FormatProtocolError(msg, info, err))) { } ProtocolError::ProtocolError(const char *msg, int err) : std::runtime_error(TeeStr(FormatProtocolError(msg, nullptr, err))) { } ProtocolError::ProtocolError(const char *msg) : std::runtime_error(TeeSZ(msg)) { } ProtocolError::ProtocolError(const std::string &msg) : std::runtime_error(TeeStr(msg)) { } //////////// ProtocolAuthFailedError::ProtocolAuthFailedError(const std::string &info) : std::runtime_error(info) { } //////////// ServerIdentityMismatchError::ServerIdentityMismatchError(const std::string &identity) : std::runtime_error(identity) { } //////////// far2l-2.6.5~beta+ds/NetRocks/src/Erroring.h000066400000000000000000000014711477431623700204660ustar00rootroot00000000000000#pragma once #include #include #include extern unsigned char g_netrocks_verbosity; struct ProtocolError : std::runtime_error { ProtocolError(const char *msg, const char *info, int err = 0); ProtocolError(const char *msg, int err); ProtocolError(const char *msg); ProtocolError(const std::string &msg); }; struct ProtocolAuthFailedError : std::runtime_error { ProtocolAuthFailedError(const std::string &info = std::string()); }; struct ProtocolUnsupportedError : ProtocolError { ProtocolUnsupportedError(const std::string &msg) : ProtocolError(msg) {} }; struct ServerIdentityMismatchError : std::runtime_error { ServerIdentityMismatchError(const std::string &identity); }; struct AbortError : std::runtime_error { AbortError() : std::runtime_error("Operation aborted") {} }; far2l-2.6.5~beta+ds/NetRocks/src/FileInformation.h000066400000000000000000000007651477431623700217710ustar00rootroot00000000000000#pragma once #include #include # include # include #define DEFAULT_ACCESS_MODE_FILE (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) #define DEFAULT_ACCESS_MODE_DIRECTORY (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH) struct FileInformation { timespec access_time; timespec modification_time; timespec status_change_time; unsigned long long size; mode_t mode; }; typedef std::map Path2FileInformation; far2l-2.6.5~beta+ds/NetRocks/src/GetItems.cpp000066400000000000000000000025041477431623700207510ustar00rootroot00000000000000#include "GetItems.h" GetItems::GetItems(bool only_selected) { PanelInfo pi = {}; G.info.Control(PANEL_ACTIVE, FCTL_GETPANELINFO, 0, (LONG_PTR)(void *)&pi); int n = only_selected ? pi.SelectedItemsNumber : pi.ItemsNumber; if (n <= 0) { fprintf(stderr, "GetItems(%d): empty\n", only_selected); return; } _items_to_free.reserve(n); reserve(n); int getitem_cmd = only_selected ? FCTL_GETSELECTEDPANELITEM : FCTL_GETPANELITEM; for (int i = 0; i < n; ++i) { size_t len = G.info.Control(PANEL_ACTIVE, getitem_cmd, i, 0); if (len >= sizeof(PluginPanelItem)) { PluginPanelItem *pi = (PluginPanelItem *)calloc(1, len + 0x20); if (pi == nullptr) { fprintf(stderr, "GetItems: no memory\n"); break; } G.info.Control(PANEL_ACTIVE, getitem_cmd, i, (LONG_PTR)(void *)pi); _items_to_free.push_back(pi); if (pi->FindData.lpwszFileName) { push_back(*pi); } } } } GetItems::~GetItems() { for (auto &pi : _items_to_free) { free(pi); } } /// GetFocusedItem::GetFocusedItem(HANDLE plug) { intptr_t size = G.info.Control(plug, FCTL_GETSELECTEDPANELITEM, 0, 0); if (size < (intptr_t)sizeof(PluginPanelItem)) { return; } _ptr = (PluginPanelItem *) malloc(size + 0x100); G.info.Control(plug, FCTL_GETSELECTEDPANELITEM, 0, (LONG_PTR)(void *)_ptr); } GetFocusedItem::~GetFocusedItem() { free(_ptr); } far2l-2.6.5~beta+ds/NetRocks/src/GetItems.h000066400000000000000000000007731477431623700204240ustar00rootroot00000000000000#pragma once #include #include "Globals.h" struct GetItems : std::vector { GetItems(bool only_selected); ~GetItems(); protected: std::vector _items_to_free; }; class GetFocusedItem { PluginPanelItem *_ptr = nullptr; GetFocusedItem(const GetFocusedItem&) = delete; public: GetFocusedItem(HANDLE plug); ~GetFocusedItem(); inline bool IsValid() const { return _ptr != nullptr; } inline PluginPanelItem *operator ->() { return _ptr; } }; far2l-2.6.5~beta+ds/NetRocks/src/Globals.cpp000066400000000000000000000043431477431623700206160ustar00rootroot00000000000000#include #include "Globals.h" #include "PooledStrings.h" #include struct Globals G; // GlobalConfigWriter::GlobalConfigWriter(std::unique_ptr &config, std::mutex &mutex) : _config(config), _lock(mutex) { } GlobalConfigWriter::GlobalConfigWriter(GlobalConfigWriter&& src) noexcept : _config(src._config), _lock(std::move(src._lock)) { } GlobalConfigWriter::~GlobalConfigWriter() { if (_config) _config->Save(); } void GlobalConfigWriter::SetInt(const char *name, int value) { if (_config) _config->SetInt("Options", name, value); } void GlobalConfigWriter::SetBool(const char *name, bool value) { SetInt(name, value ? 1 : 0); } // bool ImportFarFtpSites(); void Globals::Startup(const struct PluginStartupInfo *Info) { if (!_started) { info = *Info; fsf = *(Info->FSF); info.FSF = &fsf; _global_config.reset(new KeyFileHelper(InMyConfig("plugins/NetRocks/options.cfg"))); tsocks_config = InMyConfig("plugins/NetRocks/tsocks.cfg"); if (!GetGlobalConfigBool("ImportFarFtpSitesDone", false)) { if (ImportFarFtpSites()) { GlobalConfigWriter w = GetGlobalConfigWriter(); w.SetBool("ImportFarFtpSitesDone", true); } } _started = true; } } const wchar_t *Globals::GetMsgWide(int id) { return info.GetMsg(info.ModuleNumber, id); } const char *Globals::GetMsgMB(int id) { std::lock_guard locker(_msg_mb_mtx); auto it = _msg_mb.find(id); if (it != _msg_mb.end()) return it->second.c_str(); auto ir = _msg_mb.emplace(id, Wide2MB(info.GetMsg(info.ModuleNumber, id))); return ir.first->second.c_str(); } // GlobalConfigWriter Globals::GetGlobalConfigWriter() { return GlobalConfigWriter(_global_config, _global_config_mtx); } int Globals::GetGlobalConfigInt(const char *name, int def) { std::lock_guard locker(_global_config_mtx); if (!_global_config) return def; return _global_config->GetInt("Options", name, def); } bool Globals::GetGlobalConfigBool(const char *name, bool def) { return GetGlobalConfigInt(name, def ? 1 : 0) != 0; } /////////// Globals::BackgroundTaskScope::BackgroundTaskScope() { G.info.FSF->BackgroundTask(L"NR", TRUE); } Globals::BackgroundTaskScope::~BackgroundTaskScope() { G.info.FSF->BackgroundTask(L"NR", FALSE); } far2l-2.6.5~beta+ds/NetRocks/src/Globals.h000066400000000000000000000025571477431623700202700ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include "lng.h" struct GlobalConfigWriter { std::unique_ptr &_config; std::unique_lock _lock; GlobalConfigWriter(const GlobalConfigWriter &) = delete; public: GlobalConfigWriter(std::unique_ptr &config, std::mutex &mutex); GlobalConfigWriter(GlobalConfigWriter&& src) noexcept; ~GlobalConfigWriter(); void SetInt(const char *name, int value); void SetBool(const char *name, bool value); }; extern struct Globals { struct BackgroundTaskScope { BackgroundTaskScope(); ~BackgroundTaskScope(); }; std::wstring plugin_path; PluginStartupInfo info = {}; FarStandardFunctions fsf = {}; std::string tsocks_config; void Startup(const struct PluginStartupInfo *Info); inline bool IsStarted() const {return _started; } const wchar_t *GetMsgWide(int id); const char *GetMsgMB(int id); GlobalConfigWriter GetGlobalConfigWriter(); int GetGlobalConfigInt(const char *name, int def = -1); bool GetGlobalConfigBool(const char *name, bool def = false); private: std::unique_ptr _global_config; std::mutex _global_config_mtx; bool _started = false; std::map _msg_mb; std::mutex _msg_mb_mtx; } G; far2l-2.6.5~beta+ds/NetRocks/src/Host/000077500000000000000000000000001477431623700174405ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Host/Host.h000066400000000000000000000015141477431623700205270ustar00rootroot00000000000000#pragma once #include #include #include "../Protocol/Protocol.h" // all methods of this interface are NOT thread-safe unless explicitly marked as MT-safe struct IHost : IProtocol { struct Identity { std::string protocol; std::string host; std::string username; unsigned int port = 0; }; virtual std::string SiteName() = 0; // MT-safe, human-readable site's name virtual void GetIdentity(Identity &identity) = 0; // MT-safe, returns connection host identity details virtual std::shared_ptr Clone() = 0; // MT-safe, creates clone of this host that will init automatically with same creds virtual void ReInitialize() = 0; virtual void Abort() = 0; // MT-safe, forcefully aborts connection and any outstanding operation virtual bool Alive() = 0; // MT-safe, returns true if connection looks alive }; far2l-2.6.5~beta+ds/NetRocks/src/Host/HostLocal.cpp000066400000000000000000000175621477431623700220470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "Erroring.h" #ifdef __APPLE__ #include #elif !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__HAIKU__) #include #endif #include #include #include #include #include #include #include #include #include #include "HostLocal.h" #include "../../WinPort/WinCompat.h" #ifdef NETROCKS_PROTOCOL # include # define API(x) x #else # include "../lng.h" # include "../Globals.h" # include # define API(x) sdc_##x #endif HostLocal::HostLocal() { } HostLocal::~HostLocal() { } std::string HostLocal::SiteName() { #ifdef NETROCKS_PROTOCOL return std::string(); #else return G.GetMsgMB(MHostLocalName); #endif } void HostLocal::GetIdentity(Identity &identity) { identity = Identity(); } std::shared_ptr HostLocal::Clone() { return std::make_shared(); } void HostLocal::ReInitialize() { } void HostLocal::Abort() { } mode_t HostLocal::GetMode(const std::string &path, bool follow_symlink) { struct stat s = {}; int r = follow_symlink ? API(stat)(path.c_str(), &s) : API(lstat)(path.c_str(), &s); if (r == -1) { throw ProtocolError("stat failed", errno); } return s.st_mode; } unsigned long long HostLocal::GetSize(const std::string &path, bool follow_symlink) { struct stat s = {}; int r = follow_symlink ? API(stat)(path.c_str(), &s) : API(lstat)(path.c_str(), &s); if (r == -1) { throw ProtocolError("stat failed", errno); } return s.st_size; } void HostLocal::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { struct stat s = {}; int r = follow_symlink ? API(stat)(path.c_str(), &s) : API(lstat)(path.c_str(), &s); if (r == -1) { throw ProtocolError("stat failed", errno); } file_info.access_time = s.st_atim; file_info.modification_time = s.st_mtim; file_info.status_change_time = s.st_ctim; file_info.size = s.st_size; file_info.mode = s.st_mode; } void HostLocal::FileDelete(const std::string &path) { int r = API(unlink)(path.c_str()); if (r == -1) { throw ProtocolError("unlink failed", errno); } } void HostLocal::DirectoryDelete(const std::string &path) { int r = API(rmdir)(path.c_str()); if (r == -1) { throw ProtocolError("rmdir failed", errno); } } void HostLocal::DirectoryCreate(const std::string &path, mode_t mode) { int r = API(mkdir)(path.c_str(), mode); if (r == -1) { throw ProtocolError("mkdir failed", errno); } } void HostLocal::Rename(const std::string &path_old, const std::string &path_new) { int r = API(rename)(path_old.c_str(), path_new.c_str()); if (r == -1) { throw ProtocolError("rename failed", errno); } } void HostLocal::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { struct timespec times[2] = {access_time, modification_time}; int r = API(utimens)(path.c_str(), times); if (r == -1) { throw ProtocolError("utimens failed", errno); } } void HostLocal::SetMode(const std::string &path, mode_t mode) { int r = API(chmod)(path.c_str(), mode); if (r == -1) { throw ProtocolError("chmod failed", errno); } } void HostLocal::SymlinkCreate(const std::string &link_path, const std::string &link_target) { int r = API(symlink)(link_target.c_str(), link_path.c_str()); if (r == -1) { throw ProtocolError("symlink failed", errno); } } void HostLocal::SymlinkQuery(const std::string &link_path, std::string &link_target) { char buf[PATH_MAX]; ssize_t r = API(readlink)(link_path.c_str(), buf, sizeof(buf)); if (r < 0 || (size_t)r >= sizeof(buf)) { throw ProtocolError("read failed", errno); } link_target.assign(buf, (size_t)r); } //////////////////////////////////////// class HostLocalDirectoryEnumer : public IDirectoryEnumer { std::string _path, _subpath; DIR *_d = nullptr; std::map _cached_users; std::map _cached_groups; const std::string &UserByID(uid_t id) { auto it = _cached_users.find(id); if (it != _cached_users.end()) { return it->second; } struct passwd *pw = getpwuid(id); const auto &ir = _cached_users.emplace(id, (pw ? pw->pw_name : "")); return ir.first->second; } const std::string &GroupByID(gid_t id) { auto it = _cached_groups.find(id); if (it != _cached_groups.end()) { return it->second; } struct group *gr = getgrgid(id); const auto &ir = _cached_groups.emplace(id, (gr ? gr->gr_name : "")); return ir.first->second; } public: HostLocalDirectoryEnumer(const std::string &path) : _path(path) { _d = API(opendir)(_path.c_str()); if (!_d) { throw ProtocolError("opendir failed", errno); } if (!_path.empty() && _path[_path.size() - 1] != '/') { _path+= '/'; } } virtual ~HostLocalDirectoryEnumer() { if (_d != nullptr) { API(closedir)(_d); } } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { for (;;) { struct dirent *de = API(readdir)(_d); if (!de) { return false; } if (!FILENAME_ENUMERABLE(de->d_name)) { continue; } name = de->d_name; _subpath = _path; _subpath+= name; struct stat s = {}; if (API(lstat)(_subpath.c_str(), &s) == -1) { owner.clear(); group.clear(); file_info = FileInformation(); return true; } owner = UserByID(s.st_uid); group = GroupByID(s.st_gid); file_info.access_time = s.st_atim; file_info.modification_time = s.st_mtim; file_info.status_change_time = s.st_ctim; file_info.size = s.st_size; file_info.mode = s.st_mode; return true; } } }; std::shared_ptr HostLocal::DirectoryEnum(const std::string &path) { return std::make_shared(path); } class HostLocalFileIO : public IFileReader, public IFileWriter { int _fd = -1; public: HostLocalFileIO(const std::string &path, unsigned long long resume_pos, int oflag, mode_t mode) { _fd = API(open)(path.c_str(), oflag, mode); if (_fd == -1) { throw ProtocolError("open failed", errno); } if (resume_pos) { if (API(lseek)(_fd, resume_pos, SEEK_SET) == -1) { CheckedCloseFD(_fd); throw ProtocolError("lseek failed", errno); } } } virtual ~HostLocalFileIO() { CheckedCloseFD(_fd); } virtual size_t Read(void *buf, size_t len) { ssize_t rv = API(read)(_fd, buf, len); if (rv < 0 && errno == EIO) { // workaround for SMB's read error when requested fragment overlaps end of file off_t pos = API(lseek)(_fd, 0, SEEK_CUR); struct stat st{}; if (pos != -1 && API(fstat)(_fd, &st) == 0 && pos < (off_t)st.st_size && (off_t)(pos + len) > (off_t)st.st_size) { rv = API(read)(_fd, buf, (size_t)(st.st_size - pos)); } else { errno = EIO; } } if (rv < 0) { throw ProtocolError("read failed", errno); } return (size_t)rv; } virtual void Write(const void *buf, size_t len) { while (len) { ssize_t rv = API(write)(_fd, buf, len); if (rv < 0 || (size_t)rv > len) { throw ProtocolError("write failed", errno); } len-= (size_t)rv; buf = (const char *)buf + rv; } } virtual void WriteComplete() { /* #ifdef __APPLE__ if (fcntl(_fd, F_FULLFSYNC) == -1) { #else if (fdatasync(_fd) == -1) { #endif throw ProtocolError("sync failed", errno); } */ } }; std::shared_ptr HostLocal::FileGet(const std::string &path, unsigned long long resume_pos) { return std::make_shared(path, resume_pos, O_RDONLY, 0 ); } std::shared_ptr HostLocal::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { return std::make_shared(path, resume_pos, (resume_pos == 0) ? O_CREAT | O_TRUNC | O_RDWR : O_RDWR, mode ); } bool HostLocal::Alive() { return true; } far2l-2.6.5~beta+ds/NetRocks/src/Host/HostLocal.h000066400000000000000000000031571477431623700215070ustar00rootroot00000000000000#pragma once #include #include #include #include #include "Host.h" #include "FileInformation.h" class HostLocal : public IHost { public: HostLocal(); virtual ~HostLocal(); virtual std::string SiteName(); virtual void GetIdentity(Identity &identity); virtual std::shared_ptr Clone(); virtual void ReInitialize(); virtual void Abort(); virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_timem, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); virtual bool Alive(); }; far2l-2.6.5~beta+ds/NetRocks/src/Host/HostRemote.cpp000066400000000000000000000466641477431623700222550ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "HostRemote.h" #include "Protocol/Protocol.h" #include "IPC.h" #include "Globals.h" #include "PooledStrings.h" #include "UI/Activities/InteractiveLogin.h" #include "UI/Activities/ConfirmNewServerIdentity.h" //////////////////////////////////////////// HostRemote::HostRemote(const SiteSpecification &site_specification) : _site_specification(site_specification) { SitesConfig sc(_site_specification.sites_cfg_location); _identity.protocol = sc.GetProtocol(_site_specification.site); _identity.host = sc.GetHost(_site_specification.site); _identity.port = sc.GetPort(_site_specification.site); _identity.username = sc.GetUsername(_site_specification.site); _login_mode = sc.GetLoginMode(_site_specification.site); _password = sc.GetPassword(_site_specification.site); _options = sc.GetProtocolOptions(_site_specification.site, _identity.protocol); if (_login_mode == 0) { _password.clear(); } } HostRemote::HostRemote(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password) : _login_mode( ( (username.empty() || username == "anonymous") && password.empty()) ? 0 : (password.empty() ? 1 : 2)), _password(password) { _identity.protocol = protocol; _identity.host = host; _identity.port = port; _identity.username = username; } HostRemote::~HostRemote() { AssertNotBusy(); } const std::string &HostRemote::CodepageLocal2Remote(const std::string &str) { if (_codepage == CP_UTF8 || str.empty()) { return str; } _codepage_wstr.clear(); size_t uc_len = str.size(); const unsigned wide_cvt_rv = UtfConvertStd(str.data(), uc_len, _codepage_wstr, false); if (wide_cvt_rv != 0) { fprintf(stderr, "%s(%s): wide_cvt_rv=%u\n", __FUNCTION__, str.c_str(), wide_cvt_rv); return str; } _codepage_str.resize(_codepage_wstr.size() * 8); // ought to should be enough for any encoding const int cp_cvt_rv = WINPORT(WideCharToMultiByte)(_codepage, 0, _codepage_wstr.c_str(), _codepage_wstr.size(), &_codepage_str[0], (int)_codepage_str.size(), nullptr, nullptr); if (cp_cvt_rv <= 0) { fprintf(stderr, "%s(%s): cp_cvt_rv=%u\n", __FUNCTION__, str.c_str(), cp_cvt_rv); return str; } _codepage_str.resize(cp_cvt_rv); return _codepage_str; } void HostRemote::CodepageRemote2Local(std::string &str) { if (_codepage == CP_UTF8 || str.empty()) { return; } _codepage_wstr.resize(str.size() * 2); const int cp_cvt_rv = WINPORT(MultiByteToWideChar)(_codepage, 0, str.c_str(), str.size(), &_codepage_wstr[0], (int)_codepage_wstr.size()); if (cp_cvt_rv <= 0) { fprintf(stderr, "%s(%s): cp_cvt_rv=%u\n", __FUNCTION__, str.c_str(), cp_cvt_rv); return; } _codepage_wstr.resize(cp_cvt_rv); str.clear(); size_t uc_len = _codepage_wstr.size(); UtfConvertStd(_codepage_wstr.c_str(), uc_len, str, false); } static void TimespecAdjust(timespec &ts, int adjust) { if (ts.tv_sec == 0) { ; // dont adjust zero timestamps } else if (adjust > 0) { ts.tv_sec+= adjust; } else if (adjust < 0) { ts.tv_sec-= -adjust; } } const timespec &HostRemote::TimespecLocal2Remote(const timespec &ts) { _ts_2_remote = ts; TimespecAdjust(_ts_2_remote, -_timeadjust); return _ts_2_remote; } void HostRemote::TimespecRemote2Local(timespec &ts) { TimespecAdjust(ts, _timeadjust); } std::shared_ptr HostRemote::Clone() { auto cloned = std::make_shared(); std::unique_lock locker(_mutex); cloned->_site_specification = _site_specification; cloned->_identity = _identity; cloned->_login_mode = _login_mode; cloned->_password = _password; cloned->_options = _options; cloned->_peer = 0; cloned->_busy = false; cloned->_cloning = true; return cloned; } std::string HostRemote::SiteName() { if (_site_specification.IsValid()) { std::string out; out+= '<'; out+= _site_specification.ToString(); out+= '>'; return out; } const auto *pi = ProtocolInfoLookup(_identity.protocol.c_str()); std::unique_lock locker(_mutex); std::string out = _identity.protocol; out+= ":"; if (!_identity.username.empty()) { out+= _identity.username; out+= "@"; } out+= _identity.host; if (_identity.port && pi && pi->default_port != -1 && pi->default_port != (int)_identity.port) { out+= StrPrintf(":%u", _identity.port); } for (auto &c : out) { if (c == '/') c = '\\'; } return out; } void HostRemote::GetIdentity(Identity &identity) { std::unique_lock locker(_mutex); identity = _identity; } void HostRemote::BusySet() { _busy = true; } void HostRemote::BusyReset() { ASSERT_MSG(_busy, "not busy"); _busy = false; } void HostRemote::AssertNotBusy() { ASSERT_MSG(!_busy, "busy"); } void HostRemote::CheckReady() { AssertNotBusy(); if (_cloning) { _cloning = false; ReInitialize(); } if (_peer == 0) { throw std::runtime_error("IPC broken"); } } void HostRemote::OnBroken() { IPCEndpoint::SetFD(-1, -1); _peer = 0; _init_deinit_cmd.reset(); } void HostRemote::ReInitialize() { OnBroken(); _aborted = false; AssertNotBusy(); const auto *pi = ProtocolInfoLookup(_identity.protocol.c_str()); if (!pi) { throw std::runtime_error(std::string("Wrong protocol: ").append(_identity.protocol)); } if (_identity.host.empty() && pi->require_server) { throw std::runtime_error("No server specified"); } std::string broker_path = StrWide2MB(G.plugin_path); CutToSlash(broker_path, true); std::string broker_pathname = broker_path; broker_pathname+= pi->broker; broker_pathname+= ".broker"; PipeIPCFD ipc_fd; StringConfig sc_options(_options); _codepage = sc_options.GetInt("CodePage", CP_UTF8); _timeadjust = sc_options.GetInt("TimeAdjust", 0); char keep_alive_arg[32]; sprintf(keep_alive_arg, "%d", sc_options.GetInt("KeepAlive", 0)); std::string work_path = broker_path; TranslateInstallPath_Lib2Share(work_path); fprintf(stderr, "NetRocks: starting broker '%s' '%s' '%s' in '%s'\n", broker_pathname.c_str(), ipc_fd.broker_arg_r, ipc_fd.broker_arg_w, work_path.c_str()); UnlinkScope prxf_cfg; std::string prxf = sc_options.GetString("Proxifier"); if (!prxf.empty()) { prxf_cfg = InMyTempFmt("NetRocks/proxy/run_%u.cfg", getpid()); const std::string &cfg_content = sc_options.GetString(std::string("Proxifier_").append(prxf).c_str()); if (!WriteWholeFile(prxf_cfg.c_str(), cfg_content)) { fprintf(stderr, "NetRocks: error %d creating proxifier config '%s'\n", errno, prxf_cfg.c_str()); prxf.clear(); } } pid_t pid = fork(); if (pid == 0) { // avoid holding arbitrary dir for broker's whole runtime if (chdir(work_path.c_str()) == -1) { fprintf(stderr, "chdir '%s' error %u\n", work_path.c_str(), errno); if (chdir(broker_path.c_str()) == -1) { fprintf(stderr, "chdir '%s' error %u\n", broker_path.c_str(), errno); if (chdir("/tmp") == -1) { fprintf(stderr, "chdir '/tmp' error %u\n", errno); } } } if (prxf == "tsocks") { setenv("LD_PRELOAD", "libtsocks.so", 1); setenv("TSOCKS_CONFFILE", prxf_cfg.c_str(), 1); } if (fork() == 0) { if (prxf == "proxychains") { execlp("proxychains", "proxychains", "-f", prxf_cfg.c_str(), broker_pathname.c_str(), ipc_fd.broker_arg_r, ipc_fd.broker_arg_w, keep_alive_arg, NULL); } else { execl(broker_pathname.c_str(), broker_pathname.c_str(), ipc_fd.broker_arg_r, ipc_fd.broker_arg_w, keep_alive_arg, NULL); } _exit(-1); exit(-2); } _exit(0); exit(0); } else if (pid != -1) { _init_deinit_cmd.reset(InitDeinitCmd::sMake(_identity.protocol, _identity.host, _identity.port, _identity.username, _password, sc_options, _aborted)); waitpid(pid, 0, 0); } else { perror("fork"); } // G.info.FSF->Execute(cmdstr.c_str(), EF_HIDEOUT | EF_NOWAIT); //_interactive IPCEndpoint::SetFD(ipc_fd.broker2master[0], ipc_fd.master2broker[1]); // so far so good - avoid automatic closing of pipes FDs in ipc_fd's d-tor ipc_fd.Detach(); uint32_t ipc_ver_magic = 0; try { pid_t peer = 0; RecvPOD(ipc_ver_magic); RecvPOD(peer); _peer = peer; } catch (std::exception &) { OnBroken(); throw std::runtime_error(StrPrintf("Failed to start '%s' '%s' '%s'", broker_path.c_str(), ipc_fd.broker_arg_r, ipc_fd.broker_arg_w)); } if (ipc_ver_magic != IPC_VERSION_MAGIC) { OnBroken(); throw std::runtime_error(StrPrintf("Wrong version of '%s' '%s' '%s'", broker_path.c_str(), ipc_fd.broker_arg_r, ipc_fd.broker_arg_w)); } std::unique_lock locker(_mutex); for (unsigned int auth_failures = 0;;) { // fprintf(stderr, "login_mode=%d retry=%d\n", login_mode, retry); if (_login_mode == 1) { std::string tmp_username = _identity.username, tmp_password = _password; locker.unlock(); if (!InteractiveLogin(SiteName(), auth_failures, tmp_username, tmp_password)) { SendString(std::string()); OnBroken(); throw AbortError(); } locker.lock(); _identity.username = tmp_username; _password = tmp_password; } SendString(_identity.protocol); SendString(_identity.host); SendPOD(_identity.port); SendPOD(_login_mode); SendString(CodepageLocal2Remote(_identity.username)); SendString(CodepageLocal2Remote(_password)); SendString(_options); locker.unlock(); IPCProtocolInitStatus status; RecvPOD(status); locker.lock(); if (status == IPC_PI_OK) { if (_login_mode == 1) { // on next reinitialization try to autouse same password that now succeeded _login_mode = 2; } break; } std::string info; RecvString(info); fprintf(stderr, "HostRemote::ReInitialize: status=%d info='%s'\n", status, info.c_str()); switch (status) { case IPC_PI_SERVER_IDENTITY_CHANGED: if (!OnServerIdentityChanged(info)) { OnBroken(); throw ProtocolError("Server identity mismatch", info.c_str()); } if (_login_mode == 1) { // on next reinitialization try to autouse same password that was already entered _login_mode = 2; } break; case IPC_PI_AUTHORIZATION_FAILED: if (auth_failures >= 3) { OnBroken(); throw ProtocolError("Authorization failed", info.c_str()); } ++auth_failures; _login_mode = 1; break; case IPC_PI_PROTOCOL_ERROR: OnBroken(); throw ProtocolError(info); case IPC_PI_GENERIC_ERROR: OnBroken(); throw std::runtime_error(info); default: OnBroken(); throw PipeIPCError("Unexpected protocol init status", status); } } } bool HostRemote::OnServerIdentityChanged(const std::string &new_identity) { StringConfig protocol_options(_options); const std::string &prev_identity = protocol_options.GetString("ServerIdentity"); protocol_options.SetString("ServerIdentity", new_identity); if (!prev_identity.empty()) { switch (ConfirmNewServerIdentity(_site_specification.ToString(), new_identity, _site_specification.IsValid()).Ask()) { case ConfirmNewServerIdentity::R_ALLOW_ONCE: { _options = protocol_options.Serialize(); return true; } case ConfirmNewServerIdentity::R_ALLOW_ALWAYS: break; default: return false; } } _options = protocol_options.Serialize(); if (_site_specification.IsValid()) { SitesConfig(_site_specification.sites_cfg_location).SetProtocolOptions(_site_specification.site, _identity.protocol, _options); } return true; } void HostRemote::Abort() { _aborted = true; pid_t peer = _peer.exchange(0); AbortReceiving(); if (peer != 0) { kill(peer, SIGQUIT); } } void HostRemote::RecvReply(IPCCommand cmd) { IPCCommand reply = RecvCommand(); if (reply != cmd) { if (reply == IPC_ERROR) { std::string str; RecvString(str); throw ProtocolError(str); } else if (reply == IPC_UNSUPPORTED) { std::string str; RecvString(str); throw ProtocolUnsupportedError(str); } throw PipeIPCError("Wrong command reply", (unsigned int)cmd); } } mode_t HostRemote::GetMode(const std::string &path, bool follow_symlink) { CheckReady(); SendCommand(IPC_GET_MODE); SendString(CodepageLocal2Remote(path)); SendPOD(follow_symlink); RecvReply(IPC_GET_MODE); mode_t out; RecvPOD(out); return out; } void HostRemote::GetModes(bool follow_symlink, size_t count, const std::string *paths, mode_t *modes) noexcept { size_t j = 0; try { CheckReady(); SendCommand(IPC_GET_MODES); SendPOD(follow_symlink); SendPOD(count); for (size_t i = 0; i < count; ++i) { SendString(CodepageLocal2Remote(paths[i])); } RecvReply(IPC_GET_MODES); for (; j < count; ++j) { RecvPOD(modes[j]); } } catch (std::exception &e) { fprintf(stderr, "%s: %s\n", __FUNCTION__, e.what()); } catch (...) { fprintf(stderr, "%s: ...\n", __FUNCTION__); } for (; j < count; ++j) { modes[j] = ~(mode_t)0; } } unsigned long long HostRemote::GetSize(const std::string &path, bool follow_symlink) { CheckReady(); SendCommand(IPC_GET_SIZE); SendString(CodepageLocal2Remote(path)); SendPOD(follow_symlink); RecvReply(IPC_GET_SIZE); unsigned long long out; RecvPOD(out); return out; } void HostRemote::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { CheckReady(); SendCommand(IPC_GET_INFORMATION); SendString(CodepageLocal2Remote(path)); SendPOD(follow_symlink); RecvReply(IPC_GET_INFORMATION); RecvPOD(file_info); TimespecRemote2Local(file_info.access_time); TimespecRemote2Local(file_info.modification_time); TimespecRemote2Local(file_info.status_change_time); } void HostRemote::FileDelete(const std::string &path) { CheckReady(); SendCommand(IPC_FILE_DELETE); SendString(CodepageLocal2Remote(path)); RecvReply(IPC_FILE_DELETE); } void HostRemote::DirectoryDelete(const std::string &path) { CheckReady(); SendCommand(IPC_DIRECTORY_DELETE); SendString(CodepageLocal2Remote(path)); RecvReply(IPC_DIRECTORY_DELETE); } void HostRemote::DirectoryCreate(const std::string &path, mode_t mode) { CheckReady(); SendCommand(IPC_DIRECTORY_CREATE); SendString(CodepageLocal2Remote(path)); SendPOD(mode); RecvReply(IPC_DIRECTORY_CREATE); } void HostRemote::Rename(const std::string &path_old, const std::string &path_new) { CheckReady(); SendCommand(IPC_RENAME); SendString(CodepageLocal2Remote(path_old)); SendString(CodepageLocal2Remote(path_new)); RecvReply(IPC_RENAME); } void HostRemote::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { CheckReady(); SendCommand(IPC_SET_TIMES); SendString(CodepageLocal2Remote(path)); SendPOD(TimespecLocal2Remote(access_time)); SendPOD(TimespecLocal2Remote(modification_time)); RecvReply(IPC_SET_TIMES); } void HostRemote::SetMode(const std::string &path, mode_t mode) { CheckReady(); SendCommand(IPC_SET_MODE); SendString(CodepageLocal2Remote(path)); SendPOD(mode); RecvReply(IPC_SET_MODE); } void HostRemote::SymlinkCreate(const std::string &link_path, const std::string &link_target) { CheckReady(); SendCommand(IPC_SYMLINK_CREATE); SendString(CodepageLocal2Remote(link_path)); SendString(CodepageLocal2Remote(link_target)); RecvReply(IPC_SYMLINK_CREATE); } void HostRemote::SymlinkQuery(const std::string &link_path, std::string &link_target) { CheckReady(); SendCommand(IPC_SYMLINK_QUERY); SendString(CodepageLocal2Remote(link_path)); RecvReply(IPC_SYMLINK_QUERY); RecvString(link_target); CodepageRemote2Local(link_target); } //////////////////////////////////////// class HostRemoteDirectoryEnumer : public IDirectoryEnumer { std::shared_ptr _conn; bool _complete = false; public: HostRemoteDirectoryEnumer(std::shared_ptr conn, const std::string &path) : _conn(conn) { _conn->BusySet(); } virtual ~HostRemoteDirectoryEnumer() { if (!_complete) try { _conn->SendCommand(IPC_STOP); _conn->RecvReply(IPC_STOP); } catch (std::exception &) { } _conn->BusyReset(); } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (_complete) return false; try { _conn->SendCommand(IPC_DIRECTORY_ENUM); _conn->RecvReply(IPC_DIRECTORY_ENUM); _conn->RecvString(name); if (name.empty()) { _complete = true; return false; } _conn->RecvString(owner); _conn->RecvString(group); _conn->RecvPOD(file_info); _conn->CodepageRemote2Local(name); _conn->CodepageRemote2Local(owner); _conn->CodepageRemote2Local(group); _conn->TimespecRemote2Local(file_info.access_time); _conn->TimespecRemote2Local(file_info.modification_time); _conn->TimespecRemote2Local(file_info.status_change_time); return true; } catch (...) { _complete = true; throw; } } }; std::shared_ptr HostRemote::DirectoryEnum(const std::string &path) { CheckReady(); SendCommand(IPC_DIRECTORY_ENUM); SendString(CodepageLocal2Remote(path)); RecvReply(IPC_DIRECTORY_ENUM); return std::make_shared(shared_from_this(), path); } class HostRemoteFileIO : public IFileReader, public IFileWriter { std::shared_ptr _conn; bool _complete = false, _writing; void EnsureComplete() { if (!_complete) { _complete = true; _conn->SendPOD((size_t)0); // zero length mean stop requested _conn->RecvReply(IPC_STOP); } } public: HostRemoteFileIO(std::shared_ptr conn, bool writing) : _conn(conn), _writing(writing) { } virtual ~HostRemoteFileIO() { try { EnsureComplete(); } catch (std::exception &ex) { fprintf(stderr, "~HostRemoteFileIO(_writing=%d): %s\n", _writing, ex.what()); // _conn->Abort(); } } virtual size_t Read(void *buf, size_t len) { assert(!_writing); if (_complete || len == 0) { return 0; } try { _conn->SendPOD(len); _conn->RecvReply(IPC_FILE_GET); size_t recv_len = len; _conn->RecvPOD(recv_len); if (recv_len == 0) { _complete = true; return 0; } if (recv_len > len) { fprintf(stderr, "HostRemoteFileIO::Read: IPC gonna mad\n"); _conn->Abort(); throw ProtocolError("Read: IPC gonna mad"); } _conn->Recv(buf, recv_len); return recv_len; } catch (...) { _complete = true; throw; } } virtual void Write(const void *buf, size_t len) { assert(_writing); if (len == 0) { return; } if (_complete) { throw std::runtime_error("Write: already complete"); } try { _conn->SendPOD(len); _conn->Send(buf, len); _conn->RecvReply(IPC_FILE_PUT); } catch (...) { _complete = true; throw; } } virtual void WriteComplete() { EnsureComplete(); } }; std::shared_ptr HostRemote::FileGet(const std::string &path, unsigned long long resume_pos) { CheckReady(); SendCommand(IPC_FILE_GET); SendString(CodepageLocal2Remote(path)); SendPOD(resume_pos); RecvReply(IPC_FILE_GET); return std::make_shared(shared_from_this(), false); } std::shared_ptr HostRemote::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { CheckReady(); SendCommand(IPC_FILE_PUT); SendString(CodepageLocal2Remote(path)); SendPOD(mode); SendPOD(size_hint); SendPOD(resume_pos); RecvReply(IPC_FILE_PUT); return std::make_shared(shared_from_this(), true); } void HostRemote::ExecuteCommand(const std::string &working_dir, const std::string &command_line, const std::string &fifo) { CheckReady(); SendCommand(IPC_EXECUTE_COMMAND); SendString(CodepageLocal2Remote(working_dir)); SendString(CodepageLocal2Remote(command_line)); SendString(fifo); RecvReply(IPC_EXECUTE_COMMAND); } bool HostRemote::Alive() { return _peer != 0; } far2l-2.6.5~beta+ds/NetRocks/src/Host/HostRemote.h000066400000000000000000000062471477431623700217130ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "Host.h" #include "IPC.h" #include "FileInformation.h" #include "InitDeinitCmd.h" #include "../SitesConfig.h" class HostRemote : protected IPCEndpoint, public std::enable_shared_from_this, public IHost { friend class HostRemoteDirectoryEnumer; friend class HostRemoteFileIO; std::mutex _mutex; // to protect internal fields std::atomic _aborted{false}; std::unique_ptr _init_deinit_cmd; SiteSpecification _site_specification; Identity _identity; unsigned int _login_mode{0}; std::string _password; std::string _options; unsigned int _codepage{0}; int _timeadjust{0}; std::string _codepage_str; std::wstring _codepage_wstr; timespec _ts_2_remote{}; bool _busy = false; bool _cloning = false; std::atomic _peer{0}; void RecvReply(IPCCommand cmd); bool OnServerIdentityChanged(const std::string &new_identity); const std::string &CodepageLocal2Remote(const std::string &str); void CodepageRemote2Local(std::string &str); const timespec &TimespecLocal2Remote(const timespec &ts); void TimespecRemote2Local(timespec &ts); protected: void BusySet(); void BusyReset(); void AssertNotBusy(); void CheckReady(); void OnBroken(); public: inline HostRemote() {} HostRemote(const SiteSpecification &site_specification); HostRemote(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password); virtual ~HostRemote(); virtual std::shared_ptr Clone(); virtual std::string SiteName(); virtual void GetIdentity(Identity &identity); virtual void ReInitialize(); virtual void Abort(); virtual void GetModes(bool follow_symlink, size_t count, const std::string *pathes, mode_t *modes) noexcept; virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_timem, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); virtual void ExecuteCommand(const std::string &working_dir, const std::string &command_line, const std::string &fifo); virtual bool Alive(); }; far2l-2.6.5~beta+ds/NetRocks/src/Host/HostRemoteBroker.cpp000066400000000000000000000231151477431623700234040ustar00rootroot00000000000000#include #include #include #include #include #include "IPC.h" #include "Protocol/Protocol.h" static const std::string s_empty_string; std::shared_ptr CreateProtocol( const std::string &protocol, // protocol name e.g. "ftp", "sftp", "scp" etc const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, // StringConfig representing various other options, most of which are protocol-specific int fd_ipc_recv // DONT read this fd, use it only errors checking by poll/select (to know that host process exited) ); class HostRemoteBroker : protected IPCEndpoint { int _keepalive = -1; std::string _keepalive_path = "."; std::shared_ptr _protocol; struct { std::string str1, str2, str3; timespec ts1, ts2; FileInformation file_info; unsigned long long ull1, ull2; mode_t mode; bool b; } _args; std::vector _io_buf; void InitConnection(int fd_recv) { std::string protocol, host, username, password, options; unsigned int port, login_mode; RecvString(protocol); if (protocol.empty()) { throw AbortError(); } RecvString(host); RecvPOD(port); RecvPOD(login_mode); RecvString(username); RecvString(password); RecvString(options); _protocol = CreateProtocol(protocol, host, port, username, password, options, fd_recv); if (!_protocol){ throw std::runtime_error(std::string("Failed to create protocol: ").append(protocol)); } } void OnDirectoryEnum() { RecvString(_args.str1); std::shared_ptr enumer = _protocol->DirectoryEnum(_args.str1); _keepalive_path = _args.str1; SendCommand(IPC_DIRECTORY_ENUM); for (;;) { bool cont; try { cont = enumer->Enum(_args.str1, _args.str2, _args.str3, _args.file_info); } catch (ProtocolError &ex) { fprintf(stderr, "OnDirectoryEnum: %s\n", ex.what()); RecvCommand(); SendCommand(IPC_ERROR); SendString(ex.what()); break; } if (cont && _args.str1.empty()) { fprintf(stderr, "OnDirectoryEnum: skipped empty name\n"); continue; } auto cmd = RecvCommand(); SendCommand(cmd); if (cmd != IPC_DIRECTORY_ENUM) { break; } if (!cont) { SendString(s_empty_string); break; } SendString(_args.str1); SendString(_args.str2); SendString(_args.str3); SendPOD(_args.file_info); } } void OnFileGet() { RecvString(_args.str1); RecvPOD(_args.ull1); std::shared_ptr reader = _protocol->FileGet(_args.str1, _args.ull1); SendCommand(IPC_FILE_GET); for (;;) { size_t len = 0; RecvPOD(len); if (len == 0) { SendCommand(IPC_STOP); break; } try { if (_io_buf.size() < len) { _io_buf.resize(len); } len = reader->Read(&_io_buf[0], len); } catch (std::exception &ex) { fprintf(stderr, "OnFileGet: %s\n", ex.what()); SendCommand(IPC_ERROR); SendString(ex.what()); break; } SendCommand(IPC_FILE_GET); SendPOD(len); if (len == 0) { break; } Send(&_io_buf[0], len); } } void OnFilePut() { RecvString(_args.str1); RecvPOD(_args.mode); RecvPOD(_args.ull1); RecvPOD(_args.ull2); std::shared_ptr writer = _protocol->FilePut(_args.str1, _args.mode, _args.ull1, _args.ull2); SendCommand(IPC_FILE_PUT); // Trick to improve IO parallelization: instead of sending status reply on operation, // send preliminary OK and if error will occur - do error reply on next operation. std::string error_str; for (;;) { size_t len = 0; RecvPOD(len); if (_io_buf.size() < len) { _io_buf.resize(len); } if (!error_str.empty()) { if (len) { // still have to fetch buffer to ensure proper IPC sequencing Recv(&_io_buf[0], len); } SendCommand(IPC_ERROR); SendString(error_str); break; } if (!len) { writer->WriteComplete(); SendCommand(IPC_STOP); break; } SendCommand(IPC_FILE_PUT); Recv(&_io_buf[0], len); try { writer->Write(&_io_buf[0], len); } catch (ProtocolError &ex) { fprintf(stderr, "OnFilePut: %s\n", ex.what()); error_str = ex.what(); if (error_str.empty()) error_str = "Unknown error"; } } } void OnGetModes() { bool follow_symlink = true; size_t count = 0; RecvPOD(follow_symlink); RecvPOD(count); std::vector paths(count); std::vector modes(count); for (auto &path : paths) { RecvString(path); } if (!paths.empty()) { _keepalive_path = paths.back(); _protocol->GetModes(follow_symlink, count, paths.data(), modes.data()); } SendCommand(IPC_GET_MODES); for (const auto &mode : modes) { SendPOD(mode); } } void OnKeepAlive() { try { if (g_netrocks_verbosity >= 1) { fprintf(stderr, "OnKeepAlive for '%s'\n", _keepalive_path.c_str()); } _protocol->KeepAlive(_keepalive_path); } catch (std::exception &e) { fprintf(stderr, "OnKeepAlive: <%s> for '%s'\n", e.what(), _keepalive_path.c_str()); _keepalive_path = "."; } } template void OnGetModeOrSize(MethodT pGet) { RecvString(_args.str1); RecvPOD(_args.b); const auto &out = ((*_protocol).*(pGet))(_args.str1, _args.b); SendCommand(C); SendPOD(out); } template void OnDelete(MethodT pDelete) { RecvString(_args.str1); ((*_protocol).*(pDelete))(_args.str1); SendCommand(C); } void OnGetInformation() { RecvString(_args.str1); RecvPOD(_args.b); FileInformation file_info; _protocol->GetInformation(file_info, _args.str1, _args.b); SendCommand(IPC_GET_INFORMATION); SendPOD(file_info); } void OnRename() { RecvString(_args.str1); RecvString(_args.str2); _protocol->Rename(_args.str1, _args.str2); SendCommand(IPC_RENAME); } void OnDirectoryCreate() { RecvString(_args.str1); RecvPOD(_args.mode); _protocol->DirectoryCreate(_args.str1, _args.mode); SendCommand(IPC_DIRECTORY_CREATE); } void OnSetTimes() { RecvString(_args.str1); RecvPOD(_args.ts1); RecvPOD(_args.ts2); _protocol->SetTimes(_args.str1, _args.ts1, _args.ts2); SendCommand(IPC_SET_TIMES); } void OnSetMode() { RecvString(_args.str1); RecvPOD(_args.mode); _protocol->SetMode(_args.str1, _args.mode); SendCommand(IPC_SET_MODE); } void OnSymLinkCreate() { RecvString(_args.str1); RecvString(_args.str2); _protocol->SymlinkCreate(_args.str1, _args.str2); SendCommand(IPC_SYMLINK_CREATE); } void OnSymLinkQuery() { RecvString(_args.str1); _args.str2.clear(); _protocol->SymlinkQuery(_args.str1, _args.str2); SendCommand(IPC_SYMLINK_QUERY); SendString(_args.str2); } void OnExecuteCommand() { RecvString(_args.str1); RecvString(_args.str2); RecvString(_args.str3); _protocol->ExecuteCommand(_args.str1, _args.str2, _args.str3); SendCommand(IPC_EXECUTE_COMMAND); } void OnCommand(IPCCommand c) { switch (c) { case IPC_GET_MODES: OnGetModes(); break; case IPC_GET_MODE: OnGetModeOrSize(&IProtocol::GetMode); break; case IPC_GET_SIZE: OnGetModeOrSize(&IProtocol::GetSize); break; case IPC_GET_INFORMATION: OnGetInformation(); break; case IPC_FILE_DELETE: OnDelete(&IProtocol::FileDelete); break; case IPC_DIRECTORY_DELETE: OnDelete(&IProtocol::DirectoryDelete); break; case IPC_RENAME: OnRename(); break; case IPC_DIRECTORY_CREATE: OnDirectoryCreate(); break; case IPC_SET_TIMES: OnSetTimes(); break; case IPC_SET_MODE: OnSetMode(); break; case IPC_SYMLINK_CREATE: OnSymLinkCreate(); break; case IPC_SYMLINK_QUERY: OnSymLinkQuery(); break; case IPC_DIRECTORY_ENUM: OnDirectoryEnum(); break; case IPC_FILE_GET: OnFileGet(); break; case IPC_FILE_PUT: OnFilePut(); break; case IPC_EXECUTE_COMMAND: OnExecuteCommand(); break; default: throw PipeIPCError("HostRemoteBroker: bad command", (unsigned int)c); } } public: HostRemoteBroker(int fd_recv, int fd_send, int keepalive) : IPCEndpoint(fd_recv, fd_send), _keepalive(keepalive) { SendPOD((uint32_t)IPC_VERSION_MAGIC); SendPOD((pid_t)getpid()); for (;;) try { InitConnection(fd_recv); SendPOD(IPC_PI_OK); break; } catch (PipeIPCError &) { throw; } catch (AbortError &) { throw; } catch (ServerIdentityMismatchError &ex) { SendPOD(IPC_PI_SERVER_IDENTITY_CHANGED); SendString(ex.what()); } catch (ProtocolAuthFailedError &ex) { SendPOD(IPC_PI_AUTHORIZATION_FAILED); SendString(ex.what()); } catch (ProtocolError &ex) { SendPOD(IPC_PI_PROTOCOL_ERROR); SendString(ex.what()); } catch (std::exception &ex) { SendPOD(IPC_PI_GENERIC_ERROR); SendString(ex.what()); } } void Loop() { for (;;) { if (_keepalive > 0) { if (!WaitForRecv(_keepalive * 1000)) { OnKeepAlive(); continue; } } IPCCommand c = RecvCommand(); try { OnCommand(c); } catch (ProtocolUnsupportedError &e) { c = IPC_UNSUPPORTED; SendPOD(c); SendString(e.what()); } catch (ProtocolError &e) { c = IPC_ERROR; SendPOD(c); SendString(e.what()); } } } }; extern "C" int main(int argc, char *argv[]) { if (argc != 4) { fprintf(stderr, "Its a NetRocks protocol broker and must be started by NetRocks only\n"); return -1; } setsid(); //survive terminal death signal(SIGHUP, SIG_IGN); //signal(SIGTERM, SIG_IGN); fprintf(stderr, "%d: HostRemoteBrokerMain: BEGIN\n", getpid()); try { HostRemoteBroker(atoi(argv[1]), atoi(argv[2]), atoi(argv[3])).Loop(); } catch (std::exception &e) { fprintf(stderr, "%d HostRemoteBrokerMain: %s\n", getpid(), e.what()); } fprintf(stderr, "%d: HostRemoteBrokerMain: END\n", getpid()); return 0; } far2l-2.6.5~beta+ds/NetRocks/src/Host/IPC.h000066400000000000000000000014071477431623700202260ustar00rootroot00000000000000#pragma once #include #include #include #include #include "Erroring.h" #include "utils.h" #include "PipeIPC.h" enum IPCCommand { IPC_ERROR = 0, IPC_UNSUPPORTED, IPC_STOP, IPC_RESERVED = 10, IPC_GET_MODE, IPC_GET_MODES, IPC_GET_SIZE, IPC_GET_INFORMATION, IPC_FILE_DELETE, IPC_DIRECTORY_DELETE, IPC_RENAME, IPC_SET_TIMES, IPC_SET_MODE, IPC_SYMLINK_CREATE, IPC_SYMLINK_QUERY, IPC_DIRECTORY_CREATE, IPC_DIRECTORY_ENUM, IPC_FILE_GET, IPC_FILE_PUT, IPC_EXECUTE_COMMAND, }; typedef PipeIPCEndpoint IPCEndpoint; enum IPCProtocolInitStatus { IPC_PI_OK = 0, IPC_PI_SERVER_IDENTITY_CHANGED, IPC_PI_AUTHORIZATION_FAILED, IPC_PI_PROTOCOL_ERROR, IPC_PI_GENERIC_ERROR }; #define IPC_VERSION_MAGIC 0xbabe0001 far2l-2.6.5~beta+ds/NetRocks/src/Host/InitDeinitCmd.cpp000066400000000000000000000155211477431623700226340ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../Erroring.h" #include "InitDeinitCmd.h" #define NRLOCK_PRIVATE "nrlock/prv" #define NRLOCK_GROUP "nrlock" ////////////////////////////////////////////////////////////// InitDeinitCmd::~InitDeinitCmd() { } class InitDeinitCmdImpl : public InitDeinitCmd { struct Cleanup { ~Cleanup() { // cleanup ALL unused private locks, not only own // this is to avoid cache dir pollution by dangling lock files std::vector ids; SharedResource::sEnum(NRLOCK_PRIVATE, ids); for (const auto &id : ids) { SharedResource sr_group(NRLOCK_GROUP, GroupLockID(id)); SharedResource::Writer srw_group(sr_group, 0); if (srw_group.Locked()) { SharedResource sr_id(NRLOCK_PRIVATE, id); if (sr_id.LockWrite(0)) { sr_id.UnlockWrite(); SharedResource::sCleanup(NRLOCK_PRIVATE, id); } } } } } _cleanup; // its first field to be destroyed last, after all SharedResource fields released StringConfig _protocol_options; uint64_t _lock_id; SharedResource _sr_group; std::unique_ptr _sr_private; // created only after _sr_group locked std::unique_ptr _sr_private_rlock; std::string _host, _username, _password; char _port_sz[32]; std::atomic &_aborted; static uint64_t CalcLockID(const std::string &proto, const std::string &host, unsigned int port, const std::string &username, const StringConfig &protocol_options) { // derive lock id from connection's host specification, credentials and creation timestamp (if any) const std::string &nrlock_id_str = StrPrintf("%llx %s:%s@%s:%x", protocol_options.GetHexULL("TS"), proto.c_str(), username.c_str(), host.c_str(), port); return crc64(0, (const unsigned char *)nrlock_id_str.data(), nrlock_id_str.size()); } static uint64_t GroupLockID(uint64_t lock_id) { // more bits makes more uncleanable 'group' lock files // however less 'group' lock files causing more probability of unrelated connections to block each other return lock_id & 0xff; // up to 256 group locks is reasonable amount } void Run(const char *cmd_name, bool singular) { const std::string &cmd = _protocol_options.GetString(cmd_name); if (cmd.empty()) { return; } if (_aborted) { throw AbortError(); } const std::string &extra = _protocol_options.GetString("Extra"); const std::string &stg = InMyCache( StrPrintf("nrstg/%llx", (unsigned long long)_lock_id).c_str()); int child_stdout[2] = {-1, -1}; if (pipe(child_stdout) < 0) { throw std::runtime_error("Can't create pipe"); } MakeFDCloexec(child_stdout[0]); pid_t pid = fork(); if (pid == 0) { dup2(child_stdout[1], 1); dup2(child_stdout[1], 2); close(child_stdout[1]); setenv("HOST", _host.c_str(), 1); setenv("PORT", _port_sz, 1); setenv("USER", _username.c_str(), 1); setenv("PASSWORD", _password.c_str(), 1); setenv("EXTRA", extra.c_str(), 1); setenv("SINGULAR", singular ? "1" : "0", 1); setenv("STORAGE", stg.c_str(), 1); execlp("sh", "sh", "-c", cmd.c_str(), nullptr); int err = errno ? errno : -1; fprintf(stderr, "exec shell error %d\n", err); _exit(err); exit(err); } close(child_stdout[1]); time_t time_limit = time(NULL) + std::max(3, _protocol_options.GetInt("CommandTimeLimit", 30)); char input_buf[0x1000] = {}; // must be greater than 0x100! size_t input_buf_len = 0; bool done = false; for (;;) { fd_set fdr, fde; FD_ZERO(&fdr); FD_ZERO(&fde); FD_SET(child_stdout[0], &fdr); FD_SET(child_stdout[0], &fde); time_t time_now = time(NULL); if (time_now >= time_limit) { break; } struct timeval tv = {std::min((time_t)1, time_limit - time_now), 0}; int sv = select(child_stdout[0] + 1, &fdr, nullptr, &fde, &tv); if (sv < 0) { if (errno == EAGAIN || errno == EINTR) continue; break; } if (FD_ISSET(child_stdout[0], &fdr)) { if (input_buf_len >= sizeof(input_buf)) { input_buf_len = sizeof(input_buf) - 0x100; memcpy(&input_buf[input_buf_len], "...", 3); input_buf_len+= 3; } ssize_t r = read(child_stdout[0], &input_buf[input_buf_len], sizeof(input_buf) - input_buf_len); if (r <= 0) { done = true; break; } input_buf_len+= (size_t)r; } if (FD_ISSET(child_stdout[0], &fde)) { done = true; break; } if (_aborted) { break; } } int status = -1; if (!done) { kill(pid, 9); waitpid(pid, &status, 0); if (_aborted) { throw AbortError(); } throw ProtocolError("Timeout", std::string(input_buf, input_buf_len).c_str(), ETIMEDOUT); } waitpid(pid, &status, 0); if (status != 0) { throw ProtocolError("Error", std::string(input_buf, input_buf_len).c_str(), status); } } bool PrivateMayTakeWriteLock() noexcept { if (!_sr_private->LockWrite(0)) return false; _sr_private->UnlockWrite(); return true; } public: InitDeinitCmdImpl(const std::string &proto, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const StringConfig &protocol_options, std::atomic &aborted) : _protocol_options(protocol_options), _lock_id(CalcLockID(proto, host, port, username, protocol_options)), _sr_group(NRLOCK_GROUP, GroupLockID(_lock_id)), _host(host), _username(username), _password(password), _aborted(aborted) { fprintf(stderr, "InitDeinitCmdImpl(%s:%s@%s:%u): _lock_id=%llx\n", proto.c_str(), username.c_str(), host.c_str(), port, (unsigned long long)_lock_id); snprintf(_port_sz, sizeof(_port_sz) - 1, "%d", port); SharedResource::Writer srw(_sr_group); _sr_private.reset(new SharedResource(NRLOCK_PRIVATE, _lock_id)); Run("Command", PrivateMayTakeWriteLock()); _sr_private_rlock.reset(new SharedResource::Reader(*_sr_private)); } virtual ~InitDeinitCmdImpl() { try { SharedResource::Writer srw(_sr_group); _sr_private_rlock.reset(); Run("CommandDeinit", PrivateMayTakeWriteLock()); _sr_private.reset(); } catch (std::exception &e) { fprintf(stderr, "~InitDeinitCmdImpl: '%s'\n", e.what()); } catch (...) { fprintf(stderr, "~InitDeinitCmdImpl: ???\n"); } } virtual void Abort() { _aborted = true; } }; InitDeinitCmd *InitDeinitCmd::sMake(const std::string &proto, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const StringConfig &protocol_options, std::atomic &aborted) { if (protocol_options.GetString("Command").empty() && protocol_options.GetString("CommandDeinit").empty()) { return nullptr; } return new InitDeinitCmdImpl(proto, host, port, username, password, protocol_options, aborted); } far2l-2.6.5~beta+ds/NetRocks/src/Host/InitDeinitCmd.h000066400000000000000000000005221477431623700222740ustar00rootroot00000000000000#pragma once #include #include "StringConfig.h" struct InitDeinitCmd { static InitDeinitCmd *sMake(const std::string &proto, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const StringConfig &protocol_options, std::atomic &aborted); virtual ~InitDeinitCmd(); }; far2l-2.6.5~beta+ds/NetRocks/src/ImportFarFtpSites.cpp000066400000000000000000000072021477431623700226150ustar00rootroot00000000000000#include #include "Globals.h" #include "SitesConfig.h" #include "StringConfig.h" // TODO: remove this code in year >= 2022 #ifdef WINPORT_REGISTRY #define FTP_PWD_LEN 150 //max encrypted pwd length bool SplitLocationSpecification(const char *specification, std::string &protocol, std::string &host, unsigned int &port, std::string &username, std::string &password, std::string &directory); static std::string DecryptPassword(BYTE Src[FTP_PWD_LEN]) { std::string out; BYTE XorMask = (Src[0]^Src[1]) | 80; int n; //char *Dest = _Dest; //Log(( "DecryptPassword: %02X %02X %02X %02X %02X %02X",Src[0],Src[1],Src[2],Src[3],Src[4],Src[5] )); if(Src[0] && Src[1] && Src[2]) for(n = 2; n < FTP_PWD_LEN; n++) { unsigned char b = (unsigned char)(Src[n] ^ XorMask); if (b == 0 || b == XorMask) break; out+= (char)b; } //Log(( "DecryptPassword: [%s]",_Dest )); return out; } bool ImportFarFtpSites() { HKEY key, host_key; bool out = true; if (WINPORT(RegOpenKeyEx)(HKEY_CURRENT_USER, L"Software/Far2/Plugins/FTP/Hosts", 0, GENERIC_READ, &key) == ERROR_SUCCESS) { TCHAR name[0x100] = {}; for (DWORD i = 0;WINPORT(RegEnumKey)(key, i, name, ARRAYSIZE(name) - 1) == ERROR_SUCCESS; ++i) { if (wcsncmp(name, L"Item", 4) == 0 && WINPORT(RegOpenKeyEx)(key, name, 0, GENERIC_READ, &host_key) == ERROR_SUCCESS) { char host_name[0x1000] = {}, user[0x1000] = {}; BYTE password[0x1000] = {}; DWORD passive_mode = 1, ask_login = 0; DWORD len = sizeof(host_name) - 1; WINPORT(RegQueryValueEx) (host_key, L"HostName", NULL, NULL, (LPBYTE)&host_name[0], &len); fprintf(stderr, "SITE: %s\n", host_name); len = sizeof(user) - 1; WINPORT(RegQueryValueEx) (host_key, L"User", NULL, NULL, (LPBYTE)&user[0], &len); len = sizeof(password) - 1; WINPORT(RegQueryValueEx) (host_key, L"Password", NULL, NULL, (LPBYTE)&password[0], &len); len = sizeof(passive_mode); WINPORT(RegQueryValueEx) (host_key, L"PassiveMode", NULL, NULL, (LPBYTE)&passive_mode, &len); len = sizeof(ask_login); WINPORT(RegQueryValueEx) (host_key, L"AskLogin", NULL, NULL, (LPBYTE)&ask_login, &len); WINPORT(RegCloseKey)(host_key); std::string site = host_name; unsigned int spec_port = 21; std::string spec_protocol, spec_host, spec_username, spec_password, spec_directory; SplitLocationSpecification(site.c_str(), spec_protocol, spec_host, spec_port, spec_username, spec_password, spec_directory); if (spec_host.empty()) { spec_host = site; } if (user[0]) { spec_username = user; } if (password[0]) { spec_password = DecryptPassword(password); } for (;;) { size_t p = site.find('/'); if (p == std::string::npos) break; site[p] = '\\'; } SitesConfigLocation sites_cfg_location; sites_cfg_location.Make("FTP"); if (!sites_cfg_location.Change("FTP")) { out = false; break; } SitesConfig sites_cfg(sites_cfg_location); for (unsigned int i = 1, l = site.size(); !sites_cfg.GetProtocol(site).empty(); ++i) { site.resize(l); site+= StrPrintf(" (%u)", i); } StringConfig sc(""); sc.SetInt("Passive", passive_mode ? 1 : 0); sites_cfg.SetProtocol(site, "ftp"); sites_cfg.SetHost(site, spec_host); sites_cfg.SetUsername(site, spec_username); sites_cfg.SetPassword(site, spec_password); sites_cfg.SetDirectory(site, spec_directory); sites_cfg.SetPort(site, spec_port); sites_cfg.SetLoginMode(site, ask_login ? 0 : 2); sites_cfg.SetProtocolOptions(site, "ftp", sc.Serialize()); } } WINPORT(RegCloseKey)(key); } return out; } #else bool ImportFarFtpSites() { return true; } #endif far2l-2.6.5~beta+ds/NetRocks/src/Location.cpp000066400000000000000000000050421477431623700210000ustar00rootroot00000000000000#include #include "Location.h" #include "SitesConfig.h" bool SplitLocationSpecification(const char *specification, std::string &protocol, std::string &host, unsigned int &port, std::string &username, std::string &password, std::string &directory); /* Examples: //etc/dir/file /dir/in/home/file sftp://server//etc/dir/file sftp://user@server:port/dir/in/home/file */ bool Location::FromString(const std::string &standalone_config, const std::string &str) { server.clear(); url.protocol.clear(); url.host.clear(); url.username.clear(); url.password.clear(); url.port = 0; path.components.clear(); path.absolute = false; std::string directory; if (str.size() > 2 && str[0] == '<') { server_kind = SK_SITE; size_t p = str.find('>', 1); if (p == std::string::npos) { return false; } server = str.substr(1, p - 1); if (server.empty()) { return false; } if (p == str.size() - 1) { SiteSpecification ss(standalone_config, server); directory = SitesConfig(ss.sites_cfg_location).GetDirectory(ss.site); } else if (p < str.size() + 2) { directory = str.substr(p + 2); } } else { server_kind = SK_URL; if (!SplitLocationSpecification(str.c_str(), url.protocol, url.host, url.port, url.username, url.password, directory)) { return false; } server = url.protocol; server+= ':'; if (!url.username.empty()) { server+= url.username; server+= '@'; } server+= url.host; if (url.port) { server+= StrPrintf(":%u", url.port); } } if (directory.empty()) { path.absolute = !str.empty() && str[str.size() - 1] == '/'; } else { PathFromString(directory); } #if 0 fprintf(stderr, "Location::FromString('%s'): server_kind=%d server='%s' directory='%s', result='%s'\n", str.c_str(), server_kind, server.c_str(), directory.c_str(), ToString(true).c_str()); #endif return true; } void Location::PathFromString(const std::string &directory) { path.absolute = !directory.empty() && directory[0] == '/'; path.components.clear(); StrExplode(path.components, directory, "/"); } std::string Location::ToString(bool with_server) const { std::string out; if (with_server) { if (server_kind == SK_SITE) { out+= '<'; } out+= server; if (server_kind == SK_SITE) { out+= '>'; } } if (path.absolute) { out+= '/'; } for (size_t i = 0; i < path.components.size(); ++i) { if (i != 0 || with_server) { out+= '/'; } out+= path.components[i]; } return out; } void Location::Path::ResetToHome() { components.clear(); absolute = false; } far2l-2.6.5~beta+ds/NetRocks/src/Location.h000066400000000000000000000011131477431623700204400ustar00rootroot00000000000000#pragma once #include #include struct Location { enum ServerKind { SK_URL, SK_SITE, } server_kind; struct URL { std::string protocol, host, username, password; unsigned int port; } url; std::string server; // site name or url in form of protocol:username@host:port struct Path { std::vector components; bool absolute; void ResetToHome(); } path; void PathFromString(const std::string &directory); bool FromString(const std::string &standalone_config, const std::string &str); std::string ToString(bool with_server) const; }; far2l-2.6.5~beta+ds/NetRocks/src/NetRocks.cpp000066400000000000000000000170741477431623700207700ustar00rootroot00000000000000#include #include "Globals.h" #include "PluginImpl.h" #include "BackgroundTasks.h" #include "UI/Activities/BackgroundTasksUI.h" #include "Protocol/Protocol.h" #include #include #include //LPCSTR DiskMenuStrings[ 1+FTP_MAXBACKUPS ]; SHAREDSYMBOL void PluginModuleOpen(const char *path) { MB2Wide(path, G.plugin_path); //G.plugin_path = path; // fprintf(stderr, "NetRocks::PluginModuleOpen\n"); } SHAREDSYMBOL int WINAPI _export GetMinFarVersionW(void) { #define MAKEFARVERSION(major,minor) ( ((major)<<16) | (minor)) return MAKEFARVERSION(2, 2); } SHAREDSYMBOL void WINAPI _export SetStartupInfoW(const struct PluginStartupInfo *Info) { G.Startup(Info); // fprintf(stderr, "FSF=%p ExecuteLibrary=%p\n", G.info.FSF, G.info.FSF ? G.info.FSF->ExecuteLibrary : nullptr); } SHAREDSYMBOL HANDLE WINAPI _export OpenFilePluginW(const wchar_t *Name, const unsigned char *Data, int DataSize, int OpMode) { if (!G.IsStarted() || !Name) { return INVALID_HANDLE_VALUE; } // Only user can start config files browsing, avoid doing that during file search etc if ( (OpMode & (~(OPM_PGDN))) != 0) { return INVALID_HANDLE_VALUE; } // Filter out files that has name not suffixed by NETROCKS_EXPORT_SITE_EXTENSION size_t l = wcslen(Name); if (l <= ARRAYSIZE(NETROCKS_EXPORT_SITE_EXTENSION)) { return INVALID_HANDLE_VALUE; } if (wmemcmp(Name + (l + 1 - ARRAYSIZE(NETROCKS_EXPORT_SITE_EXTENSION)), L"" NETROCKS_EXPORT_SITE_EXTENSION, ARRAYSIZE(NETROCKS_EXPORT_SITE_EXTENSION) - 1) != 0) { return INVALID_HANDLE_VALUE; } // Bit deeper check - expecting first line to be section name, // so expecting first non-empty character to be '['. // This BTW throws away any comments.. But NetRocks doesn't write comments. for (int i = 0; i < DataSize; ++i) { if (Data[i] != ' ' && Data[i] != '\t' && Data[i] != '\r' && Data[i] != '\n') { if (Data[i] == '[') { break; } return INVALID_HANDLE_VALUE; } } return new PluginImpl(Name, true, OpMode); } SHAREDSYMBOL HANDLE WINAPI _export OpenPluginW(int OpenFrom, INT_PTR Item) { fprintf(stderr, "NetRocks: OpenPlugin(%d, '0x%lx')\n", OpenFrom, (unsigned long)Item); if (!G.IsStarted())// || OpenFrom != OPEN_COMMANDLINE) return INVALID_HANDLE_VALUE; if (OpenFrom == OPEN_PLUGINSMENU) { if (Item == 1) { BackgroundTasksList(); return INVALID_HANDLE_VALUE; } Item = 0; // static const wchar_t s_curdir_cmd[] = L"file:."; // Item = (INT_PTR)&s_curdir_cmd[0]; } const wchar_t *path = (Item > 0xfff) ? (const wchar_t *)Item : nullptr; try { return new PluginImpl(path); } catch (std::exception &ex) { const std::wstring &tmp_what = MB2Wide(ex.what()); const wchar_t *msg[] = { G.GetMsgWide(MError), tmp_what.c_str(), G.GetMsgWide(MOK)}; G.info.Message(G.info.ModuleNumber, FMSG_WARNING, nullptr, msg, ARRAYSIZE(msg), 1); return INVALID_HANDLE_VALUE; } } SHAREDSYMBOL void WINAPI _export ClosePluginW(HANDLE hPlugin) { delete (PluginImpl *)hPlugin; } SHAREDSYMBOL int WINAPI _export GetFindDataW(HANDLE hPlugin,struct PluginPanelItem **pPanelItem,int *pItemsNumber,int OpMode) { return ((PluginImpl *)hPlugin)->GetFindData(pPanelItem, pItemsNumber, OpMode); } SHAREDSYMBOL void WINAPI _export FreeFindDataW(HANDLE hPlugin,struct PluginPanelItem *PanelItem,int ItemsNumber) { ((PluginImpl *)hPlugin)->FreeFindData(PanelItem, ItemsNumber); } SHAREDSYMBOL int WINAPI _export SetDirectoryW(HANDLE hPlugin,const wchar_t *Dir,int OpMode) { return ((PluginImpl *)hPlugin)->SetDirectory(Dir, OpMode); } SHAREDSYMBOL int WINAPI _export DeleteFilesW(HANDLE hPlugin,struct PluginPanelItem *PanelItem,int ItemsNumber,int OpMode) { return ((PluginImpl *)hPlugin)->DeleteFiles(PanelItem, ItemsNumber, OpMode); } SHAREDSYMBOL int WINAPI _export GetFilesW(HANDLE hPlugin,struct PluginPanelItem *PanelItem, int ItemsNumber,int Move, const wchar_t **DestPath,int OpMode) { return ((PluginImpl *)hPlugin)->GetFiles(PanelItem, ItemsNumber, Move, DestPath ? *DestPath : nullptr, OpMode); } SHAREDSYMBOL int WINAPI _export GetLinkTargetW(HANDLE hPlugin, struct PluginPanelItem *PanelItem, wchar_t *Target, size_t TargetSize, int OpMode) { return ((PluginImpl *)hPlugin)->GetLinkTarget(PanelItem, Target, TargetSize, OpMode); } SHAREDSYMBOL int WINAPI _export PutFilesW(HANDLE hPlugin,struct PluginPanelItem *PanelItem, int ItemsNumber,int Move,const wchar_t *SrcPath, int OpMode) { return ((PluginImpl *)hPlugin)->PutFiles(PanelItem, ItemsNumber, Move, SrcPath, OpMode); } SHAREDSYMBOL int WINAPI _export MakeDirectoryW(HANDLE hPlugin, const wchar_t **Name, int OpMode) { return ((PluginImpl *)hPlugin)->MakeDirectory(Name, OpMode); } SHAREDSYMBOL int WINAPI ProcessEventW(HANDLE hPlugin, int Event, void * Param) { switch (Event) { case FE_COMMAND: if (Param) return ((PluginImpl *)hPlugin)->ProcessEventCommand((const wchar_t *)Param); break; default: ; } return 0; } SHAREDSYMBOL void WINAPI _export ExitFARW() { BackgroundTasksInfo info; GetBackgroundTasksInfo(info); for (const auto &task_info : info) { if (task_info.status == BTS_ACTIVE || task_info.status == BTS_PAUSED) { fprintf(stderr, "NetRocks::ExitFARW: aborting task id=%lu info='%s'\n", task_info.id, task_info.information.c_str()); AbortBackgroundTask(task_info.id); } } while (CountOfPendingBackgroundTasks() != 0) { if (G.info.FSF->DispatchInterThreadCalls() > 0) { usleep(1000); } else { usleep(100000); } } PluginImpl::sOnExiting(); } SHAREDSYMBOL int WINAPI _export MayExitFARW() { const size_t background_tasks_count = CountOfPendingBackgroundTasks(); if ( background_tasks_count != 0 && !ConfirmExitFAR(background_tasks_count).Ask()) { return 0; } return 1; } static std::wstring CombineAllProtocolPrefixes() { std::wstring out = L"net:"; for (auto pi = ProtocolInfoHead(); pi->name; ++pi) { out+= MB2Wide(pi->name); out+= L':'; } return out; } SHAREDSYMBOL void WINAPI _export GetPluginInfoW(struct PluginInfo *Info) { // fprintf(stderr, "NetRocks: GetPluginInfoW\n"); static const wchar_t *s_cfg_strings[2], *s_menu_strings[2]; s_cfg_strings[0] = G.GetMsgWide(MPluginOptionsTitle); s_cfg_strings[1] = G.GetMsgWide(MBackgroundTasksTitle); s_menu_strings[0] = G.GetMsgWide(MTitle); s_menu_strings[1] = G.GetMsgWide(MBackgroundTasksTitle); static const wchar_t *s_disk_menu_strings[] = {L"NetRocks\0"}; static std::wstring s_command_prefixes = CombineAllProtocolPrefixes(); Info->StructSize = sizeof(*Info); Info->Flags = PF_FULLCMDLINE; Info->PluginConfigStrings = s_cfg_strings; Info->PluginConfigStringsNumber = (CountOfAllBackgroundTasks() != 0) ? ARRAYSIZE(s_cfg_strings) : 1; Info->PluginMenuStrings = s_menu_strings; Info->PluginMenuStringsNumber = (CountOfAllBackgroundTasks() != 0) ? ARRAYSIZE(s_menu_strings) : 1; Info->CommandPrefix = s_command_prefixes.c_str(); Info->DiskMenuStrings = s_disk_menu_strings; Info->DiskMenuStringsNumber = ARRAYSIZE(s_disk_menu_strings); } SHAREDSYMBOL void WINAPI _export GetOpenPluginInfoW(HANDLE hPlugin,struct OpenPluginInfo *Info) { ((PluginImpl *)hPlugin)->GetOpenPluginInfo(Info); } SHAREDSYMBOL int WINAPI _export ProcessKeyW(HANDLE hPlugin,int Key,unsigned int ControlState) { return ((PluginImpl *)hPlugin)->ProcessKey(Key, ControlState); } void ConfigurePluginGlobalOptions(); SHAREDSYMBOL int WINAPI _export ConfigureW(int ItemNumber) { if (!G.IsStarted()) return 0; switch (ItemNumber) { case 0: { ConfigurePluginGlobalOptions(); PluginImpl::sOnGlobalSettingsChanged(); } break; case 1: { BackgroundTasksList(); } break; default: { fprintf(stderr, "NetRocks::ConfigureW(%d) - bad item\n", ItemNumber); } } return 1; } far2l-2.6.5~beta+ds/NetRocks/src/Op/000077500000000000000000000000001477431623700171015ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Op/OpBase.cpp000066400000000000000000000105401477431623700207560ustar00rootroot00000000000000#include #include #include "OpBase.h" #include "../Globals.h" #include "../lng.h" OpBase::OpBase(int op_mode, std::shared_ptr base_host, const std::string &base_dir, std::shared_ptr wea_state) : _wea_state(wea_state), _op_mode(op_mode), _base_host(base_host), _base_dir(base_dir) { _state.ao_host = this; } OpBase::~OpBase() { } void OpBase::SetNotifyTitle(int title_lng) { _notify_title_lng = title_lng; } void OpBase::ResetProgressState() { std::lock_guard locker(_state.mtx); _state.stats = ProgressStateStats(); _state.path.clear(); _state.paused = _state.aborting = _state.finished = false; } static void OpBaseExceptionMessage(const std::wstring &what) { // split long string into two (for now) parts if appropriate size_t div_pos = what.rfind(L": "), div_len = 2; if (div_pos == std::string::npos) { div_pos = what.rfind(L" - "); div_len = 3; } if (what.size() > 20 && div_pos != std::string::npos && div_pos != 0 && div_pos + div_len < what.size()) { const auto &what1 = what.substr(0, div_pos); for (div_pos+= div_len; div_pos < what.size() && what[div_pos] == ' '; ++div_pos) { } const auto &what2 = what.substr(div_pos); const wchar_t *msg[] = { G.GetMsgWide(MOperationFailed), what1.c_str(), what2.c_str(), G.GetMsgWide(MOK)}; G.info.Message(G.info.ModuleNumber, FMSG_WARNING, nullptr, msg, ARRAYSIZE(msg), 1); } else { const wchar_t *msg[] = { G.GetMsgWide(MOperationFailed), what.c_str(), G.GetMsgWide(MOK)}; G.info.Message(G.info.ModuleNumber, FMSG_WARNING, nullptr, msg, ARRAYSIZE(msg), 1); } } void *OpBase::ThreadProc() { void *out = this; try { ResetProgressState(); SudoClientRegion sdc_region; Process(); out = nullptr; std::lock_guard locker(_state.mtx); fprintf(stderr, "NetRocks::OpBase('%s'): count=%llu all_total=%llu\n", _base_dir.c_str(), _state.stats.count_total, _state.stats.all_total); } catch (AbortError &) { fprintf(stderr, "NetRocks::OpBase('%s'): aborted\n", _base_dir.c_str()); } catch (std::exception &e) { fprintf(stderr, "NetRocks::OpBase('%s'): ERROR='%s'\n", _base_dir.c_str(), e.what()); bool aborting; { std::lock_guard locker(_state.mtx); aborting = _state.aborting; } if (!aborting) { const std::wstring &what = MB2Wide(e.what()); OpBaseExceptionMessage(what); } } { std::lock_guard locker(_state.mtx); _state.finished = true; } // NOOP_EVENT -> KEY_IDLE -> DN_ENTERIDLE INPUT_RECORD ir = {}; ir.EventType = NOOP_EVENT; DWORD dw = 0; WINPORT(WriteConsoleInput)(0, &ir, 1, &dw); // calling DisplayNotification from here cuz calling it from thread causes deadlock due to // InterThreadCall waits for main thread while main thread waits for thread's completion if (_notify_title_lng != -1 && !IS_SILENT(_op_mode) && G.GetGlobalConfigBool("EnableDesktopNotifications", true)) { std::wstring display_action = G.GetMsgWide((out == nullptr) ? MNotificationSuccess : MNotificationFailed); size_t p = display_action.find(L"()"); if (p != std::wstring::npos) display_action.replace(p, 2, G.GetMsgWide(_notify_title_lng)); G.info.FSF->DisplayNotification(display_action.c_str(), StrMB2Wide(_base_dir).c_str()); } return out; } void OpBase::ForcefullyAbort() { fprintf(stderr, "NetRocks::OpBase('%s')::ForcefullyAbort()\n", _base_dir.c_str()); { std::lock_guard locker(_state.mtx); _state.aborting = true; } _base_host->Abort(); } bool OpBase::WaitThread(unsigned int msec) { for (unsigned int sleep_limit = 200;;) { unsigned int interval = (msec > sleep_limit) ? sleep_limit : msec; if (Threaded::WaitThread(interval)) return true; int cow_result = G.info.FSF->DispatchInterThreadCalls(); if (msec != (unsigned int)-1) { msec-= interval; if (msec == 0) { return false; } } if (cow_result != 0) { fprintf(stderr, "NetRocks::OpBase('%s', %d): DispatchInterThreadCalls returned %d\n", _base_dir.c_str(), _notify_title_lng, cow_result); if (cow_result > 0) { sleep_limit = 1; } } else { sleep_limit = 500; } } } bool OpBase::WaitThreadBeforeShowProgress() { if (WaitThread(IS_SILENT(_op_mode) ? 2000 : 500)) { return true; } // avoid annoying popping up of progress dialog while reading error dialog while (_wea_state->IsShowingUIRightNow()) { if (WaitThread(100)) { return true; } } return false; } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpBase.h000066400000000000000000000021701477431623700204230ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "../Host/Host.h" #include "./Utils/ProgressStateUpdate.h" #include "../UI/Defs.h" #include "../UI/Activities/WhatOnError.h" #include class OpBase : protected Threaded, protected IAbortableOperationsHost { OpBase(const OpBase &) = delete; protected: std::shared_ptr _wea_state; int _notify_title_lng = -1; int _op_mode; std::shared_ptr _base_host; std::string _base_dir; ProgressState _state; void ResetProgressState(); bool WaitThread(unsigned int msec = (unsigned int)-1); bool WaitThreadBeforeShowProgress(); virtual void *ThreadProc(); // Threaded virtual void ForcefullyAbort(); // IAbortableOperationsHost // virtual void Process() = 0; void SetNotifyTitle(int title_lng = -1); public: OpBase(int op_mode, std::shared_ptr base_host, const std::string &base_dir, std::shared_ptr wea_state = std::make_shared()); ~OpBase(); }; #define IS_SILENT(v) ( ((v) & (OPM_FIND|OPM_VIEW|OPM_EDIT|OPM_SILENT)) != 0 ) far2l-2.6.5~beta+ds/NetRocks/src/Op/OpChangeMode.cpp000066400000000000000000000072071477431623700221040ustar00rootroot00000000000000#include "OpChangeMode.h" #include #include "../UI/Activities/ConfirmChangeMode.h" #include "../UI/Activities/SimpleOperationProgress.h" #include "OpGetLinkTarget.h" #include "../Globals.h" OpChangeMode::OpChangeMode(std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *items, int items_count) : OpBase(0, base_host, base_dir), _recurse(false), _mode_set(0), _mode_clear(0) { bool has_dirs = false; mode_t mode_all = 07777, mode_any = 0; for (int i = 0; i < items_count; ++i) { if (items[i].FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { has_dirs = true; /*mode_all = 0; mode_any = 07777; break;*/ } mode_all&= items[i].FindData.dwUnixMode; mode_any|= (items[i].FindData.dwUnixMode & 07777); } std::wstring owner = items[0].Owner ? items[0].Owner : G.GetMsgWide(MOwnerUnknown); std::wstring group = items[0].Group ? items[0].Group : G.GetMsgWide(MGroupUnknown); for (int i = 1; i < items_count; ++i) { if ((items[i].Owner && owner != items[i].Owner) || (!items[i].Owner && owner != G.GetMsgWide(MOwnerUnknown))) { owner = G.GetMsgWide(MOwnerMultiple); break; } } for (int i = 1; i < items_count; ++i) { if ((items[i].Group && group != items[i].Group) || (!items[i].Group && group != G.GetMsgWide(MGroupUnknown))) { group = G.GetMsgWide(MOwnerMultiple); break; } } std::string display_path = base_dir, link_target; FILETIME ftCreationTime = {0}; FILETIME ftLastAccessTime = {0}; FILETIME ftLastWriteTime = {0}; if (items_count == 1) { if (!display_path.empty() && display_path.back() != '/') { display_path+= '/'; } Wide2MB(items->FindData.lpwszFileName, display_path, true); if (items->FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (!OpGetLinkTarget(0, base_host, base_dir, items).Do(link_target)) { link_target.clear(); } } ftCreationTime = items->FindData.ftCreationTime; ftLastAccessTime = items->FindData.ftLastAccessTime; ftLastWriteTime = items->FindData.ftLastWriteTime; } if (!ConfirmChangeMode(items_count, display_path, link_target, owner, group, ftCreationTime, ftLastAccessTime, ftLastWriteTime, has_dirs, mode_all, mode_any).Ask(_recurse, _mode_set, _mode_clear)) { throw AbortError(); } _enumer = std::make_shared(_entries, _base_host, _base_dir, items, items_count, true, _state, _wea_state); } void OpChangeMode::Do() { if (!StartThread()) { ; } else if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_CHANGEMODE, _base_dir, _state); p.Show(); WaitThread(); } } void OpChangeMode::Process() { _enumer->Scan(_recurse); for (const auto &entry : _entries) { ChangeModeOfPath(entry.first, entry.second.mode); ProgressStateUpdate psu(_state); // _state.path = path; _state.stats.count_complete++; } } void OpChangeMode::ChangeModeOfPath(const std::string &path, mode_t prev_mode) { WhatOnErrorWrap(_wea_state, _state, _base_host.get(), path, [&] () mutable { mode_t new_mode = prev_mode; new_mode&= (~_mode_clear); new_mode|= (_mode_set); if (S_ISDIR(prev_mode)) { // hacky workaround: // for directories 'x' means cd-ability, so treat it allowed if if 'r' allowed if (prev_mode & S_IRUSR) { new_mode|= (prev_mode & S_IXUSR); } if (prev_mode & S_IRGRP) { new_mode|= (prev_mode & S_IXGRP); } if (prev_mode & S_IROTH) { new_mode|= (prev_mode & S_IXOTH); } } if (new_mode != prev_mode) { _base_host->SetMode(path.c_str(), new_mode & 07777); } // fprintf(stderr, "%o -> %o '%s'\n", prev_mode, new_mode, path.c_str()); } ); } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpChangeMode.h000066400000000000000000000007201477431623700215420ustar00rootroot00000000000000#pragma once #include "OpBase.h" #include "./Utils/Enumer.h" class OpChangeMode : protected OpBase { virtual void Process(); bool _recurse; mode_t _mode_set, _mode_clear; Path2FileInformation _entries; std::shared_ptr _enumer; void ChangeModeOfPath(const std::string &path, mode_t prev_mode); public: OpChangeMode(std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *items, int items_count); void Do(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpCheckDirectory.cpp000066400000000000000000000031751477431623700230140ustar00rootroot00000000000000#include "OpCheckDirectory.h" #include "../UI/Activities/SimpleOperationProgress.h" OpCheckDirectory::OpCheckDirectory(int op_mode, std::shared_ptr &base_host, const std::string &path, std::shared_ptr &wea_state) : OpBase(op_mode, base_host, path, wea_state), _final_path(path) { } bool OpCheckDirectory::Do(std::string &final_path) { _succeed = false; if (!StartThread()) { ; } else if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_GETMODE, _base_dir, _state); p.Show(); WaitThread(); } if (!_succeed) return false; final_path.swap(_final_path); return true; } void OpCheckDirectory::Process() { WhatOnErrorWrap(_wea_state, _state, _base_host.get(), _final_path, [&] () mutable { auto mode = _base_host->GetMode(_final_path); if (!S_ISDIR(mode)) { fprintf(stderr, "NetRocks: not dir mode=0%o: '%s'\n", mode, _final_path.c_str()); throw std::runtime_error("Not a directory"); } _succeed = true; } , [this] (bool &recovery) mutable { if (_op_mode != 0 || _final_path.empty()) { recovery = false; } if (recovery) { // Translate to 'upper' path, like: // "/foo/bar" -> "/foo" // "/foo" -> "/" // "/" -> "" // "foo/bar" -> "foo" // "foo" -> "" size_t p = _final_path.rfind('/'); if (p == 0) { _final_path.resize((_final_path.size() == 1) ? 0 : 1); } else if (p != std::string::npos) { _final_path.resize(p); } else { _final_path.clear(); } } } , _op_mode == 0 && !_final_path.empty() ); } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpCheckDirectory.h000066400000000000000000000005231477431623700224530ustar00rootroot00000000000000#pragma once #include "OpBase.h" class OpCheckDirectory : protected OpBase { std::string _final_path; volatile bool _succeed; virtual void Process(); public: OpCheckDirectory(int op_mode, std::shared_ptr &base_host, const std::string &path, std::shared_ptr &wea_state); bool Do(std::string &final_path); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpConnect.cpp000066400000000000000000000024641477431623700215030ustar00rootroot00000000000000#include "OpConnect.h" #include "Host/HostRemote.h" #include "../UI/Activities/SimpleOperationProgress.h" static std::shared_ptr CreateRemoteHost(const std::string &standalone_config, const Location &location) { switch (location.server_kind) { case Location::SK_SITE: return std::make_shared(SiteSpecification(standalone_config, location.server)); case Location::SK_URL: return std::make_shared(location.url.protocol, location.url.host, location.url.port, location.url.username, location.url.password); default: ABORT(); } } OpConnect::OpConnect(int op_mode, const std::string &standalone_config, const Location &location) : OpBase(op_mode, CreateRemoteHost(standalone_config,location), location.server) { } std::shared_ptr OpConnect::Do() { _succeed = false; // fprintf(stderr, "Connect START\n"); if (!StartThread()) { ; } else if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_CONNECT, _base_host->SiteName(), _state);//_base_dir p.Show(); WaitThread(); } // fprintf(stderr, "Connect END\n"); return _succeed ? _base_host : std::shared_ptr(); } void OpConnect::Process() { _base_host->ReInitialize(); _succeed = true; // fprintf(stderr, "Connect OK\n"); } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpConnect.h000066400000000000000000000004771477431623700211520ustar00rootroot00000000000000#pragma once #include #include #include "OpBase.h" #include "../Location.h" class OpConnect : protected OpBase { volatile bool _succeed; virtual void Process(); public: OpConnect(int op_mode, const std::string &standalone_config, const Location &location); std::shared_ptr Do(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpEnumDirectory.cpp000066400000000000000000000072101477431623700226750ustar00rootroot00000000000000#include #include "OpEnumDirectory.h" #include "../UI/Activities/Confirm.h" #include "../UI/Activities/SimpleOperationProgress.h" #include "../PooledStrings.h" OpEnumDirectory::OpEnumDirectory(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, PluginPanelItems &result, std::shared_ptr &wea_state) : OpBase(op_mode, base_host, base_dir, wea_state), _result(result) { _initial_result_count = _result.count; std::unique_lock locker(_state.mtx); _initial_count_complete = _state.stats.count_complete; } bool OpEnumDirectory::Do() { if (!StartThread()) { return false; } if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_ENUMDIR, _base_dir, _state); p.Show(); WaitThread(); } return true; } void OpEnumDirectory::Process() { WhatOnErrorWrap(_wea_state, _state, _base_host.get(), _base_dir, [&] () mutable { std::shared_ptr enumer = _base_host->DirectoryEnum(_base_dir); std::string name, owner, group; FileInformation file_info; for (;;) { if (!enumer->Enum(name, owner, group, file_info)) { break; } auto *ppi = _result.Add(name.c_str()); ppi->FindData.nFileSize = file_info.size; ppi->FindData.dwUnixMode = file_info.mode; ppi->FindData.dwFileAttributes = WINPORT(EvaluateAttributesA)(file_info.mode, name.c_str()); ppi->Owner = (wchar_t *)MB2WidePooled(owner); ppi->Group = (wchar_t *)MB2WidePooled(group); WINPORT(FileTime_UnixToWin32)(file_info.access_time, &ppi->FindData.ftLastAccessTime); WINPORT(FileTime_UnixToWin32)(file_info.modification_time, &ppi->FindData.ftLastWriteTime); if (file_info.status_change_time.tv_sec) { // libssh often returns zero attributes->createtime (their bug?) WINPORT(FileTime_UnixToWin32)(file_info.status_change_time, &ppi->FindData.ftCreationTime); } else { file_info.status_change_time = file_info.modification_time; } ProgressStateUpdate psu(_state); _state.stats.count_complete++; } } , [this] (bool &recovery) mutable { recovery = false; _result.Shrink(_initial_result_count); std::unique_lock locker(_state.mtx); _state.stats.count_complete = _initial_count_complete; } ); // Now for those of them which are symlinks check if they point to directory and // set FILE_ATTRIBUTE_DIRECTORY to tell far2l that they're 'enterable' directories // note that not care about possible faults for a reason to do not bother user with // annoying errors if some directories target's will appear inaccessible. std::vector paths; std::vector modes; std::vector pattrs; size_t paths_len = 0; for (int i = _initial_result_count; ; ++i) { if (paths_len >= 1024 || i >= _result.count) { if (!paths.empty()) { _base_host->GetModes(true, paths.size(), paths.data(), modes.data()); for (size_t j = 0; j < paths.size(); ++j) { if (modes[j] != (mode_t)-1 && S_ISDIR(modes[j])) { (*pattrs[j])|= FILE_ATTRIBUTE_DIRECTORY; } } paths.clear(); modes.clear(); pattrs.clear(); paths_len = 0; } if (i >= _result.count) break; } auto &entry = _result.items[i]; if (S_ISLNK(entry.FindData.dwUnixMode)) { paths.emplace_back(_base_dir); if (!_base_dir.empty() && _base_dir.back() != '/') { paths.back()+= '/'; } paths.back()+= Wide2MB(entry.FindData.lpwszFileName); paths_len+= paths.back().size(); modes.emplace_back(~(mode_t)0); pattrs.emplace_back(&entry.FindData.dwFileAttributes); } ProgressStateUpdate psu(_state); // check for pause/abort } } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpEnumDirectory.h000066400000000000000000000006551477431623700223500ustar00rootroot00000000000000#pragma once #include "OpBase.h" #include "../PluginPanelItems.h" class OpEnumDirectory : protected OpBase { PluginPanelItems &_result; unsigned long long _initial_count_complete; int _initial_result_count; virtual void Process(); public: OpEnumDirectory(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, PluginPanelItems &result, std::shared_ptr &wea_state); bool Do(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpExecute.cpp000066400000000000000000000125241477431623700215120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "../Globals.h" #include "OpExecute.h" #include "../UI/Activities/SimpleOperationProgress.h" static int g_fd_ctl = -1; static bool FDCtl_VTSize() { ExecFIFO_CtlMsg m = {}; m.cmd = ExecFIFO_CtlMsg::CMD_PTY_SIZE; m.u.pty_size.cols = 80; m.u.pty_size.rows = 25; struct winsize w = {}; if (ioctl(1, TIOCGWINSZ, &w) == 0) { m.u.pty_size.cols = w.ws_col; m.u.pty_size.rows = w.ws_row; } return (WriteAll(g_fd_ctl, &m, sizeof(m)) == sizeof(m)); } static void OpExecute_SIGWINCH(int) { FDCtl_VTSize(); } static void OpExecute_SignalProxy(int signum) { ExecFIFO_CtlMsg m = {}; m.cmd = ExecFIFO_CtlMsg::CMD_SIGNAL; m.u.signum = signum; WriteAll(g_fd_ctl, &m, sizeof(m)); } static void SetSignalHandler(int sugnum, void (*handler)(int)) { struct sigaction sa = {}; sa.sa_handler = handler; sa.sa_flags = SA_RESTART; sigaction(sugnum, &sa, nullptr); } SHAREDSYMBOL int OpExecute_Shell(int argc, char *argv[]) { if (argc < 1) { fprintf(stderr, "Missing argument\n"); return -1; } std::string fifo = argv[0]; if (argc > 1) { std::string identity = argv[1]; for (auto &c : identity) { if (((unsigned char)c) < 32) { c = ' '; } } identity.insert(0, "\033_far2l#"); identity+= "\a"; if (write(1, identity.c_str(), identity.size()) == -1) { perror("write"); } } try { int fd_stdin = 0, fd_stdout = 1, fd_stderr = 2; TTYRawMode tty_raw_mode(fd_stdin, fd_stdout); FDScope fd_err((fifo + ".err").c_str(), O_RDONLY | O_CLOEXEC); FDScope fd_out((fifo + ".out").c_str(), O_RDONLY | O_CLOEXEC); FDScope fd_in((fifo + ".in").c_str(), O_WRONLY | O_CLOEXEC); FDScope fd_ctl((fifo + ".ctl").c_str(), O_WRONLY | O_CLOEXEC); if (!fd_ctl.Valid() || !fd_in.Valid() || !fd_out.Valid() || !fd_err.Valid()) { throw std::runtime_error("Can't open FIFO"); } g_fd_ctl = fd_ctl; if (!FDCtl_VTSize()) { throw std::runtime_error("Initial FDCtl_VTSize failed"); } SetSignalHandler(SIGWINCH, OpExecute_SIGWINCH); SetSignalHandler(SIGINT, OpExecute_SignalProxy); SetSignalHandler(SIGTSTP, OpExecute_SignalProxy); fd_set fdr, fde; for (;;) { FD_ZERO(&fdr); FD_ZERO(&fde); FD_SET(fd_out, &fdr); FD_SET(fd_err, &fdr); FD_SET(fd_stdin, &fdr); FD_SET(fd_out, &fde); FD_SET(fd_err, &fde); FD_SET(fd_stdin, &fde); int r = select(std::max(fd_stdin, std::max((int)fd_out, (int)fd_err)) + 1, &fdr, nullptr, &fde, NULL); if ( r < 0) { if (errno == EAGAIN || errno == EINTR) continue; throw std::runtime_error("select failed"); } if (FD_ISSET(fd_stdin, &fdr) || FD_ISSET(fd_stdin, &fde)) { if (ReadWritePiece(fd_stdin, fd_in) <= 0) throw std::runtime_error("stdin failed"); } unsigned int out_fails = 0; if (FD_ISSET(fd_out, &fdr) || FD_ISSET(fd_out, &fde)) { switch (ReadWritePiece(fd_out, fd_stdout)) { case 0: out_fails|= 1; break; case -1: throw std::runtime_error("stdout failed"); } } if (FD_ISSET(fd_err, &fdr) || FD_ISSET(fd_err, &fde)) { switch (ReadWritePiece(fd_err, fd_stderr)) { case 0: out_fails|= 2; break; case -1: throw std::runtime_error("stderr failed"); } } if (out_fails) { if (out_fails == 3) { break; } usleep(10000); } } } catch (std::exception &ex) { fprintf(stderr, "OpExecute_Shell: %s\n", ex.what()); g_fd_ctl = -1; return -1; } // fprintf(stderr, "OpExecute_Shell: LEAVE\n"); g_fd_ctl = -1; return ExecCommandFIFO::sReadStatus(fifo); } OpExecute::OpExecute(int op_mode, std::shared_ptr &host, const std::string &dir, const std::string &command) : OpBase(op_mode, host, dir), _command(command) { } OpExecute::~OpExecute() { } void OpExecute::Process() { _base_host->ExecuteCommand(_base_dir, _command, _fifo.FileName()); _status = 0; } void OpExecute::Do() { _status = -1; if (!StartThread()) { ; } else if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_EXECUTE, _base_host->SiteName(), _state);//_base_dir p.Show(); WaitThread(); } if (_status == 0) { std::string fifo_arg = _fifo.FileName(); QuoteCmdArgIfNeed(fifo_arg); IHost::Identity identity; _base_host->GetIdentity(identity); std::string identity_arg = StrPrintf("%s@%s", identity.username.c_str(), identity.host.c_str()); QuoteCmdArgIfNeed(identity_arg); std::wstring args = StrMB2Wide(fifo_arg); args+= L' '; args+= StrMB2Wide(identity_arg); _status = G.info.FSF->ExecuteLibrary(G.plugin_path.c_str(), L"OpExecute_Shell", args.c_str(), EF_NOCMDPRINT); } if (G.GetGlobalConfigBool("EnableDesktopNotifications", true)) { std::wstring display_action = G.GetMsgWide((_status == 0) ? MNotificationSuccess : MNotificationFailed); size_t p = display_action.find(L"()"); if (p != std::wstring::npos) display_action.replace(p, 2, G.GetMsgWide(MCommandNotificationTitle)); // display only first argument of command to avoid leakage of secret information, like passwords etc p = _command.find(' '); std::string first_arg = (p != std::string::npos) ? _command.substr(0, p) : _command; G.info.FSF->DisplayNotification(display_action.c_str(), StrMB2Wide(first_arg).c_str()); } } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpExecute.h000066400000000000000000000006771477431623700211650ustar00rootroot00000000000000#pragma once #include #include #include "../Host/Host.h" #include "Utils/ExecCommandFIFO.hpp" #include "OpBase.h" class OpExecute : protected OpBase { std::string _command; ExecCommandFIFO _fifo; volatile int _status = 0; void CleanupFIFO(); virtual void Process(); public: OpExecute(int op_mode, std::shared_ptr &host, const std::string &dir, const std::string &command); virtual ~OpExecute(); void Do(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpGetLinkTarget.cpp000066400000000000000000000021731477431623700226130ustar00rootroot00000000000000#include "OpGetLinkTarget.h" #include #include "../UI/Activities/ConfirmChangeMode.h" #include "../UI/Activities/SimpleOperationProgress.h" OpGetLinkTarget::OpGetLinkTarget(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *item) : OpBase(op_mode, base_host, base_dir) { _path = base_dir; EnsureSlashAtEnd(_path); Wide2MB(item->FindData.lpwszFileName, _path, true); } bool OpGetLinkTarget::Do(std::wstring &result) { DoInner(); StrMB2Wide(_result, result); return _success; } bool OpGetLinkTarget::Do(std::string &result) { DoInner(); result = _result; return _success; } void OpGetLinkTarget::DoInner() { if (!StartThread()) { ; } else if (IS_SILENT(_op_mode)) { WaitThread(); } else if (!WaitThread(1000)) { SimpleOperationProgress p(SimpleOperationProgress::K_GETLINK, _base_dir, _state); p.Show(); WaitThread(); } } void OpGetLinkTarget::Process() { try { _base_host->SymlinkQuery(_path, _result); _success = true; } catch (std::exception &e) { fprintf(stderr, "OpGetLinkTarget: '%s' for '%s'\n", e.what(), _path.c_str()); } catch (...) { } } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpGetLinkTarget.h000066400000000000000000000006231477431623700222560ustar00rootroot00000000000000#pragma once #include "OpBase.h" #include "./Utils/Enumer.h" class OpGetLinkTarget : protected OpBase { virtual void Process(); std::string _path, _result; bool _success{false}; void DoInner(); public: OpGetLinkTarget(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *item); bool Do(std::string &result); bool Do(std::wstring &result); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpMakeDirectory.cpp000066400000000000000000000022011477431623700226410ustar00rootroot00000000000000#include "OpMakeDirectory.h" #include "../UI/Activities/Confirm.h" #include "../UI/Activities/SimpleOperationProgress.h" OpMakeDirectory::OpMakeDirectory(int op_mode,std::shared_ptr &base_host, const std::string &base_dir, const std::string &dir_name) : OpBase(op_mode, base_host, base_dir), _dir_name(dir_name) { } bool OpMakeDirectory::Do() { if (_base_dir.empty()) { _base_dir = "./"; } else if (_base_dir[_base_dir.size() - 1] != '/') _base_dir+= '/'; if (!StartThread()) { return false; } if (!WaitThreadBeforeShowProgress()) { SimpleOperationProgress p(SimpleOperationProgress::K_CREATEDIR, _dir_name, _state); p.Show(); WaitThread(); } return true; } void OpMakeDirectory::Process() { std::string full_path = _base_dir; full_path+= _dir_name; for (size_t i = _base_dir.size(); i <= full_path.size(); ++i) { if (i == full_path.size() || full_path[i] == '/') { const std::string &component = full_path.substr(0, i); WhatOnErrorWrap(_wea_state, _state, _base_host.get(), _base_dir, [&] () mutable { _base_host->DirectoryCreate(component.c_str(), 0751); } ); } } } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpMakeDirectory.h000066400000000000000000000005151477431623700223140ustar00rootroot00000000000000#pragma once #include "OpBase.h" class OpMakeDirectory : protected OpBase { std::string _dir_name; virtual void Process(); public: OpMakeDirectory(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, const std::string &dir_name); bool Do(); const std::string &DirName() const { return _dir_name; } }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpRemove.cpp000066400000000000000000000035561477431623700213520ustar00rootroot00000000000000#include #include #include "OpRemove.h" #include "../UI/Activities/Confirm.h" #include "../UI/Activities/ComplexOperationProgress.h" OpRemove::OpRemove(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *items, int items_count) : OpBase(op_mode, base_host, base_dir) { _enumer = std::make_shared(_entries, _base_host, _base_dir, items, items_count, false, _state, _wea_state); } bool OpRemove::Do() { if (!IS_SILENT(_op_mode)) { if (!ConfirmRemove(_base_dir).Ask()) { fprintf(stderr, "NetRocks::Remove: cancel\n"); return false; } } if (!StartThread()) { fprintf(stderr, "NetRocks::Remove: start thread error\n"); return false; } if (!WaitThreadBeforeShowProgress()) { RemoveProgress p(_base_dir, _state, _wea_state); p.Show(); WaitThread(); } return true; } void OpRemove::Process() { if (_enumer) { _enumer->Scan(); _enumer.reset(); std::lock_guard locker(_state.mtx); _state.stats.total_start = TimeMSNow(); _state.stats.total_paused = std::chrono::milliseconds::zero(); } for (auto rev_i = _entries.rbegin(); rev_i != _entries.rend(); ++rev_i) { { std::lock_guard locker(_state.mtx); _state.stats.current_start = TimeMSNow(); _state.stats.current_paused = std::chrono::milliseconds::zero(); } WhatOnErrorWrap(_wea_state, _state, _base_host.get(), _base_dir, [&] () mutable { const std::string &subpath = rev_i->first.substr(_base_dir.size()); if (S_ISDIR(rev_i->second.mode)) { _base_host->DirectoryDelete(rev_i->first); } else { _base_host->FileDelete(rev_i->first); } ProgressStateUpdate psu(_state); _state.path = subpath; if (!S_ISDIR(rev_i->second.mode)) _state.stats.all_complete+= rev_i->second.size; _state.stats.count_complete++; } ); } } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpRemove.h000066400000000000000000000005401477431623700210050ustar00rootroot00000000000000#pragma once #include "OpBase.h" #include "./Utils/Enumer.h" class OpRemove : protected OpBase { Path2FileInformation _entries; std::shared_ptr _enumer; virtual void Process(); public: OpRemove(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, struct PluginPanelItem *items, int items_count); bool Do(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/OpXfer.cpp000066400000000000000000000471661477431623700210260ustar00rootroot00000000000000#include #include #include #include "OpXfer.h" #include "../UI/Activities/ConfirmXfer.h" #include "../UI/Activities/ConfirmOverwrite.h" #include "../UI/Activities/WhatOnError.h" #include "../UI/Activities/ComplexOperationProgress.h" #include "../Globals.h" #include "../lng.h" #define BUFFER_SIZE_GRANULARITY 0x8000 #define BUFFER_SIZE_LIMIT 0x1000000 #define BUFFER_SIZE_INITIAL (2 * BUFFER_SIZE_GRANULARITY) #define EXTRA_NEEDED_MODE (S_IRUSR | S_IWUSR) OpXfer::OpXfer(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, std::shared_ptr &dst_host, const std::string &dst_dir, struct PluginPanelItem *items, int items_count, XferKind kind, XferDirection direction) : OpBase(op_mode, base_host, base_dir), _dst_host(dst_host), _dst_dir(dst_dir), _kind(kind), _direction(direction), _io_buf(BUFFER_SIZE_INITIAL, BUFFER_SIZE_GRANULARITY, BUFFER_SIZE_LIMIT), _smart_symlinks_copy(G.GetGlobalConfigBool("SmartSymlinksCopy", true)), _use_of_chmod(G.GetGlobalConfigInt("UseOfChmod", 0)) { _enumer = std::make_shared(_entries, _base_host, _base_dir, items, items_count, true, _state, _wea_state); _diffname_suffix = ".NetRocks@"; _diffname_suffix+= TimeString(TSF_FOR_FILENAME); if (!IS_SILENT(_op_mode)) { _kind = ConfirmXfer(_kind, _direction).Ask(_default_xoa, _dst_dir); if (_kind == XK_NONE) { fprintf(stderr, "NetRocks::Xfer: cancel\n"); throw AbortError(); } } else { _default_xoa = XOA_OVERWRITE_IF_NEWER_OTHERWISE_ASK; } if (_kind == XK_RENAME) { SetNotifyTitle(MNotificationRename); } else { if (_dst_dir.empty()) { _dst_dir = "./"; } else if (_dst_dir[_dst_dir.size() - 1] != '/') _dst_dir+= '/'; if (direction == XD_UPLOAD) { SetNotifyTitle(MNotificationUpload); } else if (direction == XD_DOWNLOAD) { SetNotifyTitle(MNotificationDownload); } else if (direction == XD_CROSSLOAD) { SetNotifyTitle(MNotificationCrossload); } else { throw std::runtime_error("Wrong direction"); } } if (_kind == XK_MOVE) { // Try to use on-site rename operation if destination and source are on same server // and authed under same username. Note that if server host is empty then need // to avoid using of on-site renaming cuz servers may actually be different // except its a file protocol, that means local filesystem IHost::Identity src_identity, dst_identity; _base_host->GetIdentity(src_identity); _dst_host->GetIdentity(dst_identity); if ( (!src_identity.host.empty() || strcasecmp(src_identity.protocol.c_str(), "file") == 0) && src_identity.protocol == dst_identity.protocol && src_identity.host == dst_identity.host && src_identity.port == dst_identity.port && src_identity.username == dst_identity.username) { _on_site_move = true; } } if (!StartThread()) { throw std::runtime_error("Cannot start thread"); } if (!WaitThreadBeforeShowProgress()) { XferProgress p(_kind, _direction, _dst_dir, _state, _wea_state, (_op_mode == 0) ? XferProgress::BM_ALLOW_BACKGROUND : XferProgress::BM_DISALLOW_BACKGROUND); p.Show(); } } OpXfer::~OpXfer() { // sleep(10); WaitThread(); } BackgroundTaskStatus OpXfer::GetStatus() { { std::lock_guard locker(_state.mtx); if (!_state.finished) return _state.paused ? BTS_PAUSED : BTS_ACTIVE; } WaitThread(); return (GetThreadResult() == nullptr) ? BTS_COMPLETE : BTS_ABORTED; } std::string OpXfer::GetInformation() { std::string out; if (!WaitThread( 0 )) { ProgressStateStats stats; { std::lock_guard locker(_state.mtx); stats = _state.stats; } if (stats.all_total != 0) { out+= StrPrintf("%u%% ", (unsigned int)(stats.all_complete * 100ll / stats.all_total)); } else { out+= "... "; } } out+= _base_host->SiteName(); out+= " -> "; out+= _dst_host->SiteName(); return out; } std::string OpXfer::GetDestination(bool &directory) { std::string out = _dst_host->SiteName(); if (out == G.GetMsgMB(MHostLocalName)) { directory = true; return _dst_dir; } directory = false; out+= '/'; out+= _dst_dir; return out; } void OpXfer::Show() { XferProgress p(_kind, _direction, _dst_dir, _state, _wea_state, XferProgress::BM_ALREADY_BACKGROUND); p.Show(); } void OpXfer::Abort() { { std::lock_guard locker(_state.mtx); if (_state.finished) return; _state.aborting = true; } _dst_host->Abort(); _base_host->Abort(); } void OpXfer::Process() { Globals::BackgroundTaskScope bg_task_scope; if (_kind == XK_RENAME) { if (_enumer) { Rename(_enumer->Items()); _enumer.reset(); } } else { if (_enumer) { if (_on_site_move) { std::string path_dst; auto &items = _enumer->Items(); for (auto i = items.begin(); i != items.end();) { try { path_dst = _dst_dir; path_dst+= i->substr(_base_dir.size()); if (IsDstPathExists(path_dst)) { throw std::runtime_error("already exists"); } _base_host->Rename(*i, path_dst); i = items.erase(i); } catch (std::exception &ex) { fprintf(stderr, "NetRocks: on-site move file item %s: '%s' -> '%s'\n", ex.what(), i->c_str(), path_dst.c_str()); ++i; } std::lock_guard locker(_state.mtx); if (_state.aborting) { return; } } } _enumer->Scan(); _enumer.reset(); std::lock_guard locker(_state.mtx); _state.stats.total_start = TimeMSNow(); _state.stats.total_paused = std::chrono::milliseconds::zero(); } Transfer(); } } void OpXfer::Rename(const std::set &items) { if (_dst_dir == "*") return; std::string new_path; size_t star = _dst_dir.find("*"); // its actually name or wildcard for (const auto &original_path : items) { const std::string &original_name = original_path.substr(_base_dir.size()); new_path = _base_dir; if (star == 0) {// *.txt: foo.txt> bar.txt> size_t p = original_name.rfind(_dst_dir[star + 1]); if (p != std::string::npos) { new_path+= original_name.substr(0, p); } else { new_path+= original_name; } new_path+= _dst_dir.substr(star + 1); } else if (star != std::string::npos) { // Foo*Bar: FooHelloBar> new_path+= _dst_dir.substr(0, star); new_path+= original_name; new_path+= _dst_dir.substr(star + 1); } else { new_path+= _dst_dir; } WhatOnErrorWrap(_wea_state, _state, _base_host.get(), original_path + " -> " + new_path, [&] () mutable { _base_host->Rename(original_path, new_path); } ); } } bool OpXfer::IsDstPathExists(const std::string &path) { try { _dst_host->GetMode(path); return true; } catch (std::exception &) { } return false; } void OpXfer::EnsureDstDirExists() { if (IsDstPathExists(_dst_dir)) { return; } for (size_t i = 1; i <= _dst_dir.size(); ++i) { if (i == _dst_dir.size() || _dst_dir[i] == '/') { const std::string &part_dir = _dst_dir.substr(0, i); WhatOnErrorWrap(_wea_state, _state, _dst_host.get(), part_dir, [&] () mutable { if (!IsDstPathExists(part_dir)) { _dst_host->DirectoryCreate(part_dir, 0751); } } ); } } } void OpXfer::Transfer() { EnsureDstDirExists(); std::string path_dst; for (auto &e : _entries) { const std::string &subpath = e.first.substr(_base_dir.size()); path_dst = _dst_dir; path_dst+= subpath; { std::lock_guard lock(_state.mtx); _state.path = subpath; _state.stats.file_complete = 0; _state.stats.file_total = S_ISDIR(e.second.mode) ? 0 : e.second.size; _state.stats.current_start = TimeMSNow(); _state.stats.current_paused = std::chrono::milliseconds::zero(); } FileInformation existing_file_info; bool existing = false; try { _dst_host->GetInformation(existing_file_info, path_dst); existing = true; } catch (std::exception &ex) { (void)ex; } // FIXME: distinguish unexistence of file from IO failure if (S_ISLNK(e.second.mode)) { if (existing || SymlinkCopy(e.first, path_dst)) { if (_kind == XK_MOVE && !existing) { FileDelete(e.first); } ProgressStateUpdate psu(_state); _state.stats.count_complete++; continue; } // if symlink copy failed then fallback to target's content copy WhatOnErrorWrap(_wea_state, _state, _base_host.get(), e.first, [&] () mutable { _base_host->GetInformation(e.second, e.first, true); } ); if (!S_ISREG(e.second.mode) && !S_ISDIR(e.second.mode)) { // don't copy symlink's target if its nor file nor directory fprintf(stderr, "NetRocks: skipped symlink target with mode=0x%x - '%s' \n", e.second.mode, path_dst.c_str()); ProgressStateUpdate psu(_state); _state.stats.count_complete++; _state.stats.count_skips++; continue; } if (S_ISREG(e.second.mode)) { // symlinks are not counted in all_total, need to add size for symlink's target if gonna file-copy it std::lock_guard lock(_state.mtx); _state.stats.all_total+= e.second.size; } } if (S_ISDIR(e.second.mode)) { if (!existing) { DirectoryCopy(path_dst, e.second); } } else { if (existing) { auto xoa = _default_xoa; if (xoa == XOA_OVERWRITE_IF_NEWER_OTHERWISE_ASK) { xoa = (TimeSpecCompare(existing_file_info.modification_time, e.second.modification_time) < 0) ? XOA_OVERWRITE : XOA_ASK; } if (xoa == XOA_ASK) { xoa = ConfirmOverwrite(_kind, _direction, path_dst, e.second.modification_time, e.second.size, existing_file_info.modification_time, existing_file_info.size).Ask(_default_xoa); if (xoa == XOA_CANCEL) { return; } } if (xoa == XOA_OVERWRITE_IF_NEWER) { xoa = (TimeSpecCompare(existing_file_info.modification_time, e.second.modification_time) < 0) ? XOA_OVERWRITE : XOA_SKIP; } if (xoa == XOA_RESUME) { if (existing_file_info.size < e.second.size) { std::lock_guard lock(_state.mtx); _state.stats.all_complete+= existing_file_info.size; _state.stats.file_complete = existing_file_info.size; } else { xoa = XOA_SKIP; } } else if (xoa == XOA_CREATE_DIFFERENT_NAME) { path_dst+= _diffname_suffix; } if (xoa == XOA_SKIP) { std::lock_guard lock(_state.mtx); _state.stats.all_complete+= e.second.size; _state.stats.file_complete+= e.second.size; _state.stats.count_complete++; continue; } } if (_on_site_move) try { _base_host->Rename(e.first, path_dst); std::lock_guard lock(_state.mtx); _state.stats.all_complete+= e.second.size; _state.stats.file_complete+= e.second.size; _state.stats.count_complete++; continue; } catch(std::exception &ex) { fprintf(stderr, "NetRocks: on-site move file error %s: '%s' -> '%s'\n", ex.what(), e.first.c_str(), path_dst.c_str()); } if (FileCopyLoop(e.first, path_dst, e.second)) { CopyAttributes(path_dst, e.second); if (_kind == XK_MOVE) { FileDelete(e.first); } } } ProgressStateUpdate psu(_state); _state.stats.count_complete++; } for (auto rev_i = _entries.rbegin(); rev_i != _entries.rend(); ++rev_i) { if (S_ISDIR(rev_i->second.mode)) { path_dst = _dst_dir; path_dst+= rev_i->first.substr(_base_dir.size()); CopyAttributes(path_dst, rev_i->second); } if (_kind == XK_MOVE) { if (S_ISDIR(rev_i->second.mode)) { WhatOnErrorWrap(_wea_state, _state, _base_host.get(), rev_i->first, [&] () mutable { _base_host->DirectoryDelete(rev_i->first); } ); } } } } void OpXfer::FileDelete(const std::string &path) { WhatOnErrorWrap(_wea_state, _state, _base_host.get(), path, [&] () mutable { _base_host->FileDelete(path); } ); } void OpXfer::CopyAttributes(const std::string &path_dst, const FileInformation &info) { WhatOnErrorWrap(_wea_state, _state, _dst_host.get(), path_dst, [&] () mutable { _dst_host->SetTimes(path_dst.c_str(), info.access_time, info.modification_time); } ); switch (_use_of_chmod) { case 0: // auto if ( (info.mode | EXTRA_NEEDED_MODE) == info.mode) { return; } break; case 1: // always break; case 2: // never return; } fprintf(stderr, "!!!! copy mode !!!\n"); WhatOnErrorWrap(_wea_state, _state, _dst_host.get(), path_dst, [&] () mutable { const mode_t mode = info.mode & 07777; try { _dst_host->SetMode(path_dst.c_str(), mode); } catch (...) { if ((mode & 07000) == 0) { throw; } _dst_host->SetMode(path_dst.c_str(), mode & 00777); } } ); } void OpXfer::EnsureProgressConsistency() { if (_state.stats.file_complete > _state.stats.file_total) { // keep pocker face if file grew while copying _state.stats.all_total+= _state.stats.file_complete - _state.stats.file_total; _state.stats.file_total = _state.stats.file_complete; } } bool OpXfer::FileCopyLoop(const std::string &path_src, const std::string &path_dst, FileInformation &info) { for (IHost *indicted = nullptr;;) try { unsigned long long file_complete; if (indicted) { // retrying... indicted->ReInitialize(); indicted = _dst_host.get(); file_complete = _dst_host->GetSize(path_dst); ProgressStateUpdate psu(_state); _state.stats.file_complete = file_complete; EnsureProgressConsistency(); } else { ProgressStateUpdate psu(_state); file_complete = _state.stats.file_complete; } indicted = _base_host.get(); std::shared_ptr reader = _base_host->FileGet(path_src, file_complete); indicted = _dst_host.get(); std::shared_ptr writer = _dst_host->FilePut(path_dst, (info.mode | EXTRA_NEEDED_MODE) & 07777, info.size, file_complete); if (!_io_buf.Size()) throw std::runtime_error("No buffer - no file"); for (unsigned long long transfer_msec = 0, initial_complete = file_complete;;) { indicted = _base_host.get(); size_t ask_piece = _io_buf.Size(); if (info.size < file_complete + ask_piece && info.size > file_complete) { // use small buffer if gonna read small piece: IO may have small-read-optimized implementation // but ask by one extra byte more to properly detect file being grew while copied ask_piece = (info.size - file_complete) + 1; } std::chrono::milliseconds msec = TimeMSNow(); const size_t piece = reader->Read(_io_buf.Data(), ask_piece); if (piece == 0) { if (file_complete < info.size) { // protocol returned no read error, but trieved less data then expected, only two reasons possible: // - remote file size reduced while copied // - protocol implementation misdetected read failure // so get actual file size, and if it still bigger than retrieved data size then ring-the-bell const auto actual_size = _base_host->GetSize(path_src); if (file_complete < actual_size) { info.size = actual_size; throw std::runtime_error("Retrieved less data than expected"); } info.size = file_complete; } indicted = _dst_host.get(); writer->WriteComplete(); break; } indicted = _dst_host.get(); writer->Write(_io_buf.Data(), piece); file_complete+= piece; const bool fast_complete = (piece < ask_piece && file_complete == info.size); if (fast_complete) { // read returned less than was asked, and position is exactly at file size // - pretty sure its end of file, so don't iterate to next IO to save time, space and Universe // fprintf(stderr, "optimized read completion\n"); writer->WriteComplete(); } transfer_msec+= (TimeMSNow() - msec).count(); if (transfer_msec > 100) { unsigned long rate_avg = (unsigned long)(( (file_complete - initial_complete) * 1000 ) / transfer_msec); unsigned long bufsize_optimal = (rate_avg / 2); unsigned long bufsize_align = bufsize_optimal % BUFFER_SIZE_GRANULARITY; if (bufsize_align) { bufsize_optimal-= bufsize_align; } unsigned long prev_bufsize = _io_buf.Size(); _io_buf.Desire(bufsize_optimal); if (g_netrocks_verbosity > 0 && _io_buf.Size() != prev_bufsize) { fprintf(stderr, "NetRocks: IO buffer size changed to %lu\n", (unsigned long)_io_buf.Size()); } } indicted = nullptr; _wea_state->ResetAutoRetryDelay(); ProgressStateUpdate psu(_state); _state.stats.file_complete = file_complete; _state.stats.all_complete+= piece; EnsureProgressConsistency(); if (fast_complete) { break; } } break; } catch (AbortError &) { throw; } catch (std::exception &ex) { if (indicted == nullptr) { throw; } switch (_wea_state->Query(_state, (_direction == XD_UPLOAD) ? WEK_UPLOAD : ((_direction == XD_DOWNLOAD) ? WEK_DOWNLOAD : WEK_CROSSLOAD) , ex.what(), path_src, indicted->SiteName())) { case WEA_SKIP: { ProgressStateUpdate psu(_state); _state.stats.count_skips++; } return false; case WEA_RETRY: { ProgressStateUpdate psu(_state); _state.stats.count_retries++; } break; default: throw AbortError(); // throw abort to avoid second error warning } } return true; } void OpXfer::DirectoryCopy(const std::string &path_dst, const FileInformation &info) { WhatOnErrorWrap(_wea_state, _state, _dst_host.get(), path_dst, [&] () mutable { _dst_host->DirectoryCreate(path_dst, info.mode | EXTRA_NEEDED_MODE); } ); } bool OpXfer::SymlinkCopy(const std::string &path_src, const std::string &path_dst) { std::string symlink_target; WhatOnErrorWrap(_wea_state, _state, _base_host.get(), path_src, [&] () mutable { symlink_target.clear(); _base_host->SymlinkQuery(path_src, symlink_target); } ); if (symlink_target.empty()) { return false; } std::string orig_symlink_target = symlink_target; if (!_smart_symlinks_copy) { ; } else if (symlink_target[0] == '/') { if (_entries.find(symlink_target) == _entries.end()) { fprintf(stderr, "NetRocks: SymlinkCopy dismiss '%s' [%s]\n", path_src.c_str(), orig_symlink_target.c_str()); return false; } // absolute target is part of copied directories tree: translate it to relative form // /base/dir/copied/sym/link // /some/destination/path/copied/sym/link symlink_target.erase(0, _base_dir.size()); for (size_t i = _base_dir.size(); i < path_dst.size(); ++i) { if (path_dst[i] == '/') { symlink_target.insert(0, "../"); } } } else { // target is relative: check if it doesnt point outside of copied directories tree // ../../../some/relaive/path std::string refined = path_src; size_t p = refined.rfind('/'); refined.resize( (p == std::string::npos) ? 0 : p); for (size_t i = 0, ii = 0; i != symlink_target.size() + 1; ++i) { if (i == symlink_target.size() || symlink_target[i] == '/') { if (i > ii) { const std::string &component = symlink_target.substr(ii, i - ii); if (component == "..") { p = refined.rfind('/'); refined.resize( (p == std::string::npos) ? 0 : p); } else if (component != ".") { if (!refined.empty() && refined[refined.size() - 1] != '/') { refined+= '/'; } refined+= component; } } ii = i + 1; } } if (_entries.find(refined) == _entries.end()) { fprintf(stderr, "NetRocks: SymlinkCopy dismiss '%s' [%s] refined='%s;\n", path_src.c_str(), orig_symlink_target.c_str(), refined.c_str()); return false; } } fprintf(stderr, "NetRocks: SymlinkCopy '%s' [%s] -> '%s' [%s]\n", path_src.c_str(), orig_symlink_target.c_str(), path_dst.c_str(), symlink_target.c_str()); bool created = false; WhatOnErrorWrap(_wea_state, _state, _dst_host.get(), path_dst, [&] () mutable { try { created = true; _dst_host->SymlinkCreate(path_dst, symlink_target); } catch (ProtocolUnsupportedError &) {} } ); return created; } void OpXfer::ForcefullyAbort() { OpBase::ForcefullyAbort(); _dst_host->Abort(); } far2l-2.6.5~beta+ds/NetRocks/src/Op/OpXfer.h000066400000000000000000000031461477431623700204610ustar00rootroot00000000000000#pragma once #include "OpBase.h" #include "./Utils/Enumer.h" #include "./Utils/IOBuffer.h" #include "../UI/Defs.h" #include "../BackgroundTasks.h" class OpXfer : protected OpBase, public IBackgroundTask { Path2FileInformation _entries; std::shared_ptr _enumer; std::shared_ptr _dst_host; std::string _dst_dir, _diffname_suffix; XferOverwriteAction _default_xoa = XOA_ASK; XferKind _kind; XferDirection _direction; IOBuffer _io_buf; bool _smart_symlinks_copy; bool _on_site_move = false; int _use_of_chmod; virtual void Process(); virtual void ForcefullyAbort(); // IAbortableOperationsHost bool IsDstPathExists(const std::string &path); void Rename(const std::set &items); void EnsureDstDirExists(); void Transfer(); void FileDelete(const std::string &path); void DirectoryCopy(const std::string &path_dst, const FileInformation &info); bool SymlinkCopy(const std::string &path_src, const std::string &path_dst); bool FileCopyLoop(const std::string &path_src, const std::string &path_dst, FileInformation &info); void EnsureProgressConsistency(); void CopyAttributes(const std::string &path_dst, const FileInformation &info); public: OpXfer(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, std::shared_ptr &dst_host, const std::string &dst_dir, struct PluginPanelItem *items, int items_count, XferKind kind, XferDirection direction); virtual ~OpXfer(); virtual BackgroundTaskStatus GetStatus(); virtual std::string GetInformation(); virtual std::string GetDestination(bool &directory); virtual void Show(); virtual void Abort(); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/000077500000000000000000000000001477431623700202015ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/Enumer.cpp000066400000000000000000000051141477431623700221410ustar00rootroot00000000000000#include #include #include "Enumer.h" #include "ProgressStateUpdate.h" Enumer::Enumer(Path2FileInformation &result, std::shared_ptr &host, const std::string &dir, const struct PluginPanelItem *items, int items_count, bool no_special_files, ProgressState &state, std::shared_ptr &wea_state) : _result(result), _host(host), _no_special_files(no_special_files), _state(state), _wea_state(wea_state) { ASSERT(dir.empty() || dir[dir.size() - 1] == '/'); std::string item_path; for (int i = 0; i < items_count; ++i) { if (FILENAME_ENUMERABLE(items[i].FindData.lpwszFileName)) { item_path = dir; item_path+= Wide2MB(items[i].FindData.lpwszFileName); _items.emplace(item_path); } } } void Enumer::Scan(bool recurse) { std::string subpath; for (const auto &path : _items) { if (OnScanningPath(path)) { if (recurse) { subpath = path; subpath+= '/'; _scan_depth_limit = 255; ScanItem(subpath); } } } } void Enumer::GetSubitems(const std::string &path, Path2FileInformation &subitems) { WhatOnErrorWrap(_wea_state, _state, _host.get(), path, [&] () mutable { std::shared_ptr enumer = _host->DirectoryEnum(path); std::string name, owner, group; FileInformation file_info; for (;;) { if (!enumer->Enum(name, owner, group, file_info)) { break; } subitems.emplace(name, file_info); ProgressStateUpdate psu(_state); // check for abort/pause } } ); } void Enumer::ScanItem(const std::string &path) { Path2FileInformation subitems; GetSubitems(path, subitems); if (subitems.empty()) return; std::string subpath; for (const auto &e : subitems) { subpath = path; subpath+= e.first; if (OnScanningPath(subpath, &e.second)) { if (_scan_depth_limit) { subpath+= '/'; --_scan_depth_limit; ScanItem(subpath); ++_scan_depth_limit; } else { fprintf(stderr, "NetRocks::Item('%s'): depth limit exhausted\n", subpath.c_str()); } } } } bool Enumer::OnScanningPath(const std::string &path, const FileInformation *file_info) { FileInformation info = {}; if (file_info) { info = *file_info; } else { _host->GetInformation(info, path, false); } if (!S_ISREG(info.mode)) { if (_no_special_files && !S_ISDIR(info.mode) && !S_ISLNK(info.mode)) { return false; } info.size = 0; } if (!_result.emplace(path, info).second) return false; ProgressStateUpdate psu(_state); // _state.path = path; _state.stats.count_total++; if (!S_ISDIR(info.mode)) { _state.stats.all_total+= info.size; return false; } return true; } far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/Enumer.h000066400000000000000000000021071477431623700216050ustar00rootroot00000000000000#pragma once #include #include #include #include #include "../../UI/Defs.h" #include "../../UI/Activities/WhatOnError.h" #include "../../FileInformation.h" #include "../../Host/Host.h" class Enumer { Path2FileInformation &_result; std::shared_ptr _host; std::set _items; bool _no_special_files; ProgressState &_state; std::shared_ptr _wea_state; unsigned int _scan_depth_limit = 0; void GetSubitems(const std::string &path, Path2FileInformation &subitems); void ScanItem(const std::string &path); bool OnScanningPath(const std::string &path, const FileInformation *file_info = nullptr); public: Enumer(Path2FileInformation &result, std::shared_ptr &host, const std::string &dir, const struct PluginPanelItem *items, int items_count, bool no_special_files, ProgressState &state, std::shared_ptr &wea_state); inline const std::set &Items() const { return _items; } inline std::set &Items() { return _items; } void Scan(bool recurse = true); }; far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/ExecCommandFIFO.hpp000066400000000000000000000031661477431623700235470ustar00rootroot00000000000000#pragma once #include #include #include class ExecCommandFIFO { std::string _fifo; void CleanupFIFO() { { FDScope fd_ctl((_fifo + ".ctl").c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC); FDScope fd_in((_fifo + ".in").c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC); FDScope fd_out((_fifo + ".out").c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC); FDScope fd_err((_fifo + ".err").c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC); } unlink((_fifo + ".ctl").c_str()); unlink((_fifo + ".in").c_str()); unlink((_fifo + ".out").c_str()); unlink((_fifo + ".err").c_str()); unlink((_fifo + ".status").c_str()); } public: ExecCommandFIFO() { _fifo = InMyTempFmt("NetRocks/fifo/%lx", (unsigned long)getpid()); CleanupFIFO(); if (mkfifo((_fifo + ".ctl").c_str(), 0700) == -1 ) { throw std::runtime_error("mkfifo ctl"); } if (mkfifo((_fifo + ".in").c_str(), 0700) == -1 ) { CleanupFIFO(); throw std::runtime_error("mkfifo in"); } if (mkfifo((_fifo + ".out").c_str(), 0700) == -1) { CleanupFIFO(); throw std::runtime_error("mkfifo out"); } if (mkfifo((_fifo + ".err").c_str(), 0700) == -1) { CleanupFIFO(); throw std::runtime_error("mkfifo err"); } } virtual ~ExecCommandFIFO() { CleanupFIFO(); } inline const std::string &FileName() const { return _fifo; } static int sReadStatus(const std::string &fifo) { int status = -1; FDScope fd_status((fifo + ".status").c_str(), O_RDONLY | O_CLOEXEC); if (fd_status.Valid()) { ReadAll(fd_status, &status, sizeof(status)); } return status; } int ReadStatus() { return sReadStatus(_fifo); } }; far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/IOBuffer.cpp000066400000000000000000000040751477431623700223540ustar00rootroot00000000000000#include #include #include #include "IOBuffer.h" #define ALIGNMENT ((size_t)0x1000) #define ALIGN_SIZE(sz) ((sz) & (~(ALIGNMENT - 1))) #define LOW_MEM_COUNTDOWN (0x1000) IOBuffer::IOBuffer(size_t initial_size, size_t min_size, size_t max_size) : _min_size(ALIGN_SIZE(min_size)), _max_size(ALIGN_SIZE(max_size)) { if (!_min_size) _min_size = ALIGNMENT; if (_max_size < _min_size) _max_size = _min_size; _size = ALIGN_SIZE(initial_size); if (_size < _min_size) _size = _min_size; if (_size > _max_size) _size = _max_size; if (posix_memalign(&_data, ALIGNMENT, _size) != 0) { _data = malloc(_size); if (_data == nullptr) { _data = malloc(_min_size); if (_data == nullptr) { throw std::runtime_error("IOBuffer - no memory"); } _size = _min_size; } } _capacity = initial_size; } IOBuffer::~IOBuffer() { if (_data) { free(_data); } } bool IOBuffer::IncreaseTo(size_t new_size, bool preserve_data) // tries to increase size of buffer (will not go above max_size) { if (new_size <= _capacity) { _size = new_size; return true; } if (_low_mem_countdown) { --_low_mem_countdown; return false; } void *new_data; if (posix_memalign(&new_data, ALIGNMENT, new_size) != 0) { _low_mem_countdown = LOW_MEM_COUNTDOWN; return false; } if (_data) { if (preserve_data) { memcpy(new_data, _data, _size); } free(_data); } _data = new_data; _size = _capacity = new_size; return true; } bool IOBuffer::Increase(bool preserve_data) { if (_size >= _max_size) return false; const size_t new_size = std::min(ALIGN_SIZE(_size * 3 / 2), _max_size); IncreaseTo(new_size, preserve_data); return true; } bool IOBuffer::Decrease() { if (_size <= _min_size) { return false; } _size = std::max(_size / 2, _min_size); return true; } void IOBuffer::Desire(size_t size, bool preserve_data) { if (size < _size) { _size = std::max(size, _min_size); } else if (size > _size) { size = std::min(size, _max_size); if (size > _size) { IncreaseTo(size, preserve_data); } } } far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/IOBuffer.h000066400000000000000000000016121477431623700220130ustar00rootroot00000000000000#pragma once #include #include class IOBuffer { void *_data = nullptr; size_t _size = 0, _capacity = 0; unsigned int _low_mem_countdown = 0; size_t _min_size, _max_size; IOBuffer(const IOBuffer &) = delete; IOBuffer& operator=(const IOBuffer &) = delete; bool IncreaseTo(size_t new_size, bool preserve_data = true); // tries to increase size of buffer (will not go above max_size) public: IOBuffer(size_t initial_size, size_t min_size, size_t max_size); ~IOBuffer(); inline void *Data() { return _data; } inline size_t Size() { return _size; } bool Increase(bool preserve_data = true); // tries to increase size of buffer (will not go above max_size) bool Decrease(); // tries to decrease size of buffer (will not go below min_size) void Desire(size_t size, bool preserve_data = true); // tries to change size of of buffer (within [min_size..max_size] ) }; far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/ProgressStateUpdate.cpp000066400000000000000000000012001477431623700246460ustar00rootroot00000000000000#include #include #include #include "ProgressStateUpdate.h" #include "Erroring.h" ProgressStateUpdate::ProgressStateUpdate(ProgressState &state) :std::unique_lock(state.mtx) { std::chrono::milliseconds pause_ticks = {}; for (;;) { if (state.aborting) throw AbortError(); if (!state.paused) break; unlock(); if (pause_ticks.count() == 0) { pause_ticks = TimeMSNow(); } usleep(1000000); lock(); } if (pause_ticks.count() != 0) { pause_ticks = TimeMSNow() - pause_ticks; state.stats.total_paused+= pause_ticks; state.stats.current_paused+= pause_ticks; } } far2l-2.6.5~beta+ds/NetRocks/src/Op/Utils/ProgressStateUpdate.h000066400000000000000000000002251477431623700243210ustar00rootroot00000000000000#pragma once #include "../../UI/Defs.h" struct ProgressStateUpdate : std::unique_lock { ProgressStateUpdate(ProgressState &state); }; far2l-2.6.5~beta+ds/NetRocks/src/PluginImpl.cpp000066400000000000000000000661331477431623700213200ustar00rootroot00000000000000#include #include #include #include #include "Globals.h" #include #include "SitesConfig.h" #include "PluginImpl.h" #include "PluginPanelItems.h" #include "PooledStrings.h" #include "ConnectionsPool.h" #include "GetItems.h" #include "Host/HostLocal.h" #include "UI/Settings/SiteConnectionEditor.h" #include "UI/Activities/Confirm.h" #include "Op/OpConnect.h" #include "Op/OpXfer.h" #include "Op/OpRemove.h" #include "Op/OpCheckDirectory.h" #include "Op/OpMakeDirectory.h" #include "Op/OpEnumDirectory.h" #include "Op/OpExecute.h" #include "Op/OpChangeMode.h" #include "Op/OpGetLinkTarget.h" static std::unique_ptr g_conn_pool; class AllNetRocks { std::mutex _mutex; std::map _all; public: void Add(PluginImpl *it) { std::lock_guard locker(_mutex); if (!_all.emplace((void *)it, it).second) { ABORT_MSG("dup entry %p", it); } } void Remove(PluginImpl *it) { std::lock_guard locker(_mutex); _all.erase((void *)it); if (_all.empty()) { PurgePooledStrings(); } } bool GetRemoteOf(void *handle, std::shared_ptr &remote, std::string &site_dir, SitesConfigLocation &sites_cfg_location) { std::lock_guard locker(_mutex); auto i = _all.find(handle); if (i == _all.end()) return false; remote = i->second->_remote; site_dir = i->second->CurrentSiteDir(true); sites_cfg_location = i->second->_sites_cfg_location; return true; } void CloneRemoteOf(void *handle) { std::lock_guard locker(_mutex); auto i = _all.find(handle); if (i != _all.end()) { i->second->_remote = i->second->_remote->Clone(); } } } g_all_netrocks; PluginImpl::PluginImpl(const wchar_t *path, bool path_is_standalone_config, int OpMode) { _cur_dir[0] = _panel_title[0] = _format[0] = 0; _local = std::make_shared(); if (path_is_standalone_config) { _standalone_config = path; _sites_cfg_location = SitesConfigLocation(StrWide2MB(_standalone_config)); } else { if (path && wcsncmp(path, L"net:", 4) == 0) { path+= 4; while (*path == L'/') { ++path; } } if (path && *path) { const std::string &standalone_config = StrWide2MB(_standalone_config); if (_location.FromString(standalone_config, Wide2MB(path))) { _remote = OpConnect(0, standalone_config, _location).Do(); if (!_remote) { throw std::runtime_error(G.GetMsgMB(MCouldNotConnect)); } ValidateLocationDirectory(OpMode); } else if (!_sites_cfg_location.Change(Wide2MB(path))) { throw std::runtime_error(G.GetMsgMB(MWrongPath)); } _wea_state = std::make_shared(); } } g_all_netrocks.Add(this); UpdatePathInfo(); } PluginImpl::~PluginImpl() { DismissRemoteHost(); g_all_netrocks.Remove(this); } void PluginImpl::UpdatePathInfo() { std::wstring tmp; if (_remote) { wcsncpy(_format, StrMB2Wide(_location.server).c_str(), ARRAYSIZE(_format) - 1); wcsncpy(_cur_dir, StrMB2Wide(_location.ToString(true)).c_str(), ARRAYSIZE(_cur_dir) - 1 ); // tmp = _remote->SiteInfo(); // tmp+= '/'; tmp+= _cur_dir; } else { tmp = StrMB2Wide(_sites_cfg_location.TranslateToPath(false)); wcsncpy(_cur_dir, tmp.c_str(), ARRAYSIZE(_cur_dir) - 1); if (!_standalone_config.empty()) { tmp = ExtractFileName(_standalone_config); } else { wcsncpy(_format, L"NetRocks sites", ARRAYSIZE(_format) - 1); if (!tmp.empty()) { if (tmp[tmp.size() - 1] == '/') { tmp.resize(tmp.size() - 1); } tmp.insert(0, L": "); } tmp.insert(0, L"NetRocks sites"); } } if (tmp.size() >= ARRAYSIZE(_panel_title)) { size_t rmlen = 4 + (tmp.size() - ARRAYSIZE(_panel_title)); tmp.replace((tmp.size() - rmlen) / 2 , rmlen, L"..."); } wcscpy(_panel_title, tmp.c_str()); } std::string PluginImpl::CurrentSiteDir(bool with_ending_slash) const { std::string out = _location.ToString(false); if (out.empty()) { out = "."; } if (with_ending_slash) { if (!out.empty() && out[out.size() - 1] != '/') { out+= '/'; } } else if (out.size() > 1 && out[out.size() - 1] == '/') { out.resize(out.size() - 1); } fprintf(stderr, "CurrentSiteDir: out='%s'\n", out.c_str()); return out; } int PluginImpl::GetFindData(PluginPanelItem **pPanelItem, int *pItemsNumber, int OpMode) { fprintf(stderr, "NetRocks::GetFindData '%ls'\n", _cur_dir); PluginPanelItems ppis; try { //_location.path auto *ppi = ppis.Add(L".."); ppi->FindData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; if (_remote == nullptr) { std::vector children; _sites_cfg_location.Enum(children); for (const auto &child : children) { ppi = ppis.Add(child.c_str()); ppi->FindData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; } ppi = ppis.Add(G.GetMsgWide(MCreateSiteConnection)); ppi->FindData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; SitesConfig sc(_sites_cfg_location); const std::vector &sites = sc.EnumSites(); for (const std::string &site : sites) { ppi = ppis.Add(site.c_str()); ppi->FindData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_EXECUTABLE; } } else { OpEnumDirectory(OpMode, _remote, CurrentSiteDir(false), ppis, _wea_state).Do(); //_remote->DirectoryEnum(CurrentSiteDir(false), il, OpMode); } } catch (std::exception &e) { fprintf(stderr, "NetRocks::GetFindData('%ls', %d) ERROR: %s\n", _cur_dir, OpMode, e.what()); return FALSE; } *pPanelItem = ppis.items; *pItemsNumber = ppis.count; ppis.Detach(); return TRUE; } void PluginImpl::FreeFindData(PluginPanelItem *PanelItem, int ItemsNumber) { PluginPanelItems_Free(PanelItem, ItemsNumber); } static bool PreprocessPathTokens(std::vector &components) { for (auto i = components.begin(); i != components.end();) { if (*i == ".") { i = components.erase(i); } else if (*i == "..") { i = components.erase(i); if (i == components.begin()) { return false; } --i; i = components.erase(i); } else { ++i; } } return true; } int PluginImpl::SetDirectory(const wchar_t *Dir, int OpMode) { fprintf(stderr, "PluginImpl::SetDirectory('%ls', %d)\n", Dir, OpMode); StackedDir sd; StackedDirCapture(sd); if (!_remote) { if (_sites_cfg_location.Change(Wide2MB(Dir))) { UpdatePathInfo(); return TRUE; } } _allow_remember_location_dir = (OpMode == 0); SiteSpecification site_specification; if (_location.server_kind == Location::SK_SITE) { site_specification = SiteSpecification(StrWide2MB(_standalone_config), _location.server); } if (*Dir == L'/') { DismissRemoteHost(); do { ++Dir; } while (*Dir == L'/'); if (*Dir == 0) { UpdatePathInfo(); return TRUE; } } if (*Dir && !SetDirectoryInternal(Dir, OpMode)) { StackedDirApply(sd); return FALSE; } UpdatePathInfo(); if (!IS_SILENT(OpMode) && !_remote && site_specification.IsValid()) { G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_ACTIVE, FCTL_REDRAWPANEL, 0, 0); G.info.Control(PANEL_ACTIVE, FCTL_CLEARSELECTION, 0, 0); PanelRedrawInfo ri = {}; const std::wstring &site_w = StrMB2Wide(site_specification.site); GetItems gi(false); for (const auto &item : gi) { if ( (item.FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && site_w == item.FindData.lpwszFileName) { ri.CurrentItem = ri.TopPanelItem = &item - &gi[0]; break; } } G.info.Control(PANEL_ACTIVE, FCTL_REDRAWPANEL, 0, (LONG_PTR)&ri); } return TRUE; } int PluginImpl::SetDirectoryInternal(const wchar_t *Dir, int OpMode) { if (!_remote) { std::string dir_mb = Wide2MB(Dir); if (dir_mb.empty() || dir_mb[0] == '/' || (dir_mb[0] == '~' && (dir_mb.size() == 1 || dir_mb[1] == '/'))) { fprintf(stderr, "SetDirectoryInternal('%ls', 0x%x): wrong path for non-connected state\n", Dir, OpMode); return FALSE; } if (dir_mb[0] != '<') { size_t p = dir_mb.find('/'); if (p != std::string::npos) { dir_mb.insert(p, 1, '>'); } else { dir_mb+= '>'; } dir_mb.insert(0, _sites_cfg_location.TranslateToPath(true)); dir_mb.insert(0, 1, '<'); } Location new_location; if (!new_location.FromString(StrWide2MB(_standalone_config), dir_mb)) { fprintf(stderr, "SetDirectoryInternal('%ls', 0x%x): parse path failed\n", Dir, OpMode); return FALSE; } if (new_location.server_kind == Location::SK_URL && new_location.url.password.empty() && new_location.server_kind == _location.server_kind && new_location.server == _location.server) { new_location.url.password = _location.url.password; } _location = new_location; if (!PreprocessPathTokens(_location.path.components)) { fprintf(stderr, "SetDirectoryInternal('%ls', 0x%x): inconstistent path\n", Dir, OpMode); return FALSE; } if (g_conn_pool) { _remote = g_conn_pool->Get(CurrentConnectionPoolId()); } if (!_remote) { _remote = OpConnect(0, StrWide2MB(_standalone_config), _location).Do(); if (!_remote) { fprintf(stderr, "SetDirectoryInternal('%ls', 0x%x): connect failed\n", Dir, OpMode); return FALSE; } } _wea_state = std::make_shared(); return ValidateLocationDirectory(OpMode) ? TRUE : FALSE; } std::vector components; StrExplode(components, Wide2MB(Dir), "/"); auto saved_path = _location.path; if (!components.empty() && components[0] == "~") { _location.path.ResetToHome(); _location.path.components.insert(_location.path.components.end(), components.begin() + 1, components.end()); } else if (*Dir != L'/') { if (components.empty()) { return TRUE; } _location.path.components.insert(_location.path.components.end(), components.begin(), components.end()); } else { _location.path.components = components; _location.path.absolute = true; } if (!PreprocessPathTokens(_location.path.components)) { fprintf(stderr, "NetRocks::SetDirectoryInternal - close connection due to: '%ls'\n", Dir); DismissRemoteHost(); return TRUE; } if (ValidateLocationDirectory(OpMode)) { return TRUE; } _location.path = saved_path; return FALSE; } bool PluginImpl::ValidateLocationDirectory(int OpMode) { const std::string &dir = _location.ToString(false); fprintf(stderr, "NetRocks::ValidateLocationDirectory - dir: '%s'\n", dir.c_str()); if (dir.empty()) { return true; } std::string final_dir; if (!OpCheckDirectory(OpMode, _remote, dir, _wea_state).Do(final_dir)) { fprintf(stderr, "NetRocks::ValidateLocationDirectory - failed\n"); return false; } if (dir != final_dir) { fprintf(stderr, "NetRocks::ValidateLocationDirectory - final_dir: '%s'\n", final_dir.c_str()); _location.PathFromString(final_dir); } return true; } void PluginImpl::GetOpenPluginInfo(struct OpenPluginInfo *Info) { // fprintf(stderr, "NetRocks::GetOpenPluginInfo: '%ls' \n", &_cur_dir[0]); // snprintf(_panel_title, ARRAYSIZE(_panel_title), // " Inside: %ls@%s ", _dir.c_str(), _name.c_str()); Info->Flags = OPIF_SHOWPRESERVECASE | OPIF_USEHIGHLIGHTING; if (_remote) { IHost::Identity identity; _remote->GetIdentity(identity); const auto *pi = ProtocolInfoLookup(identity.protocol.c_str()); if (pi && pi->inaccurate_timestamps) { Info->Flags|= OPIF_COMPAREFATTIME; } } else { Info->Flags|= OPIF_HL_MARKERS_NOSHOW; // for site connections list always don't show markers // for site connections list don't use current far2l panel modes // - only name column(s) without dependence on panel Align file extensions static struct PanelMode PanelModesArray[10] = { { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N,N,N", .ColumnWidths = L"0,0,0" }, { .ColumnTypes = L"N,N", .ColumnWidths = L"0,0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, { .ColumnTypes = L"N", .ColumnWidths = L"0" }, }; Info->PanelModesArray = PanelModesArray; Info->PanelModesNumber = ARRAYSIZE(PanelModesArray); } Info->HostFile = _standalone_config.empty() ? NULL : _standalone_config.c_str(); Info->CurDir = _cur_dir; Info->Format = _format; Info->PanelTitle = _panel_title; } BackgroundTaskStatus PluginImpl::StartXfer(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, std::shared_ptr &dst_host, const std::string &dst_dir, struct PluginPanelItem *items, int items_count, XferKind kind, XferDirection direction) { BackgroundTaskStatus out = BTS_ABORTED; try { std::shared_ptr task = std::make_shared(op_mode, base_host, base_dir, dst_host, dst_dir, items, items_count, kind, direction); // task->Show(); out = task->GetStatus(); if (out == BTS_ACTIVE || out == BTS_PAUSED) { AddBackgroundTask(task); } } catch (std::exception &ex) { fprintf(stderr, "NetRocks::StartXfer: %s\n", ex.what()); } return out; } BOOL PluginImpl::SitesXfer(const char *dir, struct PluginPanelItem *items, int items_count, bool mv, bool imp) { if (!ConfirmSitesDisposition(imp ? ConfirmSitesDisposition::W_IMPORT : ConfirmSitesDisposition::W_EXPORT, mv).Ask()) { return TRUE; } for (int i = 0; i < items_count; ++i) { if (!FILENAME_ENUMERABLE(items[i].FindData.lpwszFileName)) { continue; } bool its_dir = ((items[i].FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); std::string item_name = Wide2MB(items[i].FindData.lpwszFileName); if (imp) { if (!_sites_cfg_location.Import(dir, item_name, its_dir, mv)) { return FALSE; } } else if ( its_dir || (items[i].FindData.dwFileAttributes & FILE_ATTRIBUTE_EXECUTABLE) != 0) { if (!_sites_cfg_location.Export(dir, item_name, its_dir, mv)) { return FALSE; } } } return TRUE; } int PluginImpl::GetFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int Move, const wchar_t *DestPath, int OpMode) { fprintf(stderr, "NetRocks::GetFiles: _dir='%ls' DestPath='%ls' ItemsNumber=%d OpMode=%d\n", _cur_dir, DestPath, ItemsNumber, OpMode); if (ItemsNumber <= 0) { return FALSE; } std::string dst_dir; if (DestPath) Wide2MB(DestPath, dst_dir); if (!_remote) { return (OpMode == 0) ? SitesXfer(dst_dir.c_str(), PanelItem, ItemsNumber, Move != 0, false) : FALSE; } switch (StartXfer(OpMode, _remote, CurrentSiteDir(true), _local, dst_dir, PanelItem, ItemsNumber, Move ? XK_MOVE : XK_COPY, XD_DOWNLOAD)) { case BTS_ACTIVE: case BTS_PAUSED: _remote = _remote->Clone(); _local = _local->Clone(); case BTS_COMPLETE: return TRUE; case BTS_ABORTED: default: return FALSE; } } int PluginImpl::PutFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int Move, const wchar_t *SrcPath, int OpMode) { const std::string &site_dir = CurrentSiteDir(true); fprintf(stderr, "NetRocks::PutFiles: _dir='%ls' SrcPath='%ls' site_dir='%s' ItemsNumber=%d OpMode=%d\n", _cur_dir, SrcPath, site_dir.c_str(), ItemsNumber, OpMode); if (ItemsNumber <= 0) return FALSE; // std::string src_dir; // if (SrcPath) { // Wide2MB(SrcPath, src_dir); // } else { char dst_dir[PATH_MAX] = {}; sdc_getcwd(dst_dir, PATH_MAX-2); size_t l = strlen(dst_dir); if (l == 0) { strcpy(dst_dir, "./"); } else if (dst_dir[l - 1] != '/') { dst_dir[l] = '/'; dst_dir[l + 1] = 0; } if (!_remote) { return (OpMode == 0) ? SitesXfer(dst_dir, PanelItem, ItemsNumber, Move != 0, true) : FALSE; } switch (StartXfer(OpMode, _local, dst_dir, _remote, CurrentSiteDir(true), PanelItem, ItemsNumber, Move ? XK_MOVE : XK_COPY, XD_UPLOAD)) { case BTS_ACTIVE: case BTS_PAUSED: _remote = _remote->Clone(); _local = _local->Clone(); case BTS_COMPLETE: return TRUE; case BTS_ABORTED: default: return FALSE; } } int PluginImpl::GetLinkTarget(struct PluginPanelItem *PanelItem, wchar_t *Target, size_t TargetSize, int OpMode) { if (!_remote || !PanelItem) { return 0; } std::wstring result; if (!OpGetLinkTarget(OpMode, _remote, CurrentSiteDir(true), PanelItem).Do(result)) { return 0; } wcsncpy(Target, result.c_str(), TargetSize); return result.size() + 1; } int PluginImpl::DeleteFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int OpMode) { fprintf(stderr, "NetRocks::DeleteFiles: _dir='%ls' ItemsNumber=%d\n", _cur_dir, ItemsNumber); if (ItemsNumber <= 0) return FALSE; if (!_remote) { if (!ConfirmSitesDisposition(ConfirmSitesDisposition::W_REMOVE, false).Ask()) return FALSE; SitesConfig sc(_sites_cfg_location); for (int i = 0; i < ItemsNumber; ++i) { const std::string &display_name = Wide2MB(PanelItem[i].FindData.lpwszFileName); if (PanelItem[i].FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { fprintf(stderr, "Removing sites directory: %s\n", display_name.c_str()); _sites_cfg_location.Remove(display_name); } else { fprintf(stderr, "Removing site: %s\n", display_name.c_str()); sc.RemoveSite(display_name); } } return TRUE; } return OpRemove(OpMode, _remote, CurrentSiteDir(true), PanelItem, ItemsNumber).Do() ? TRUE : FALSE; } int PluginImpl::MakeDirectory(const wchar_t **Name, int OpMode) { fprintf(stderr, "NetRocks::MakeDirectory('%ls', 0x%x)\n", Name ? *Name : L"NULL", OpMode); std::string dir_name; if (Name && *Name) { Wide2MB(*Name, dir_name); } if (!IS_SILENT(OpMode)) { dir_name = ConfirmMakeDir(dir_name.c_str()).Ask(); } if (dir_name.empty()) { fprintf(stderr, "NetRocks::MakeDirectory: cancel\n"); return -1; } if (!_remote) { if (!_sites_cfg_location.Make(dir_name)) { return 0; } } else { OpMakeDirectory op(OpMode, _remote, CurrentSiteDir(true), dir_name); if (!op.Do()) { return 0; } dir_name = op.DirName(); } if (Name && !IS_SILENT(OpMode)) { wcsncpy(_mk_dir, StrMB2Wide(dir_name).c_str(), ARRAYSIZE(_mk_dir) - 1); _mk_dir[ARRAYSIZE(_mk_dir) - 1] = 0; *Name = _mk_dir; } return 1; } static bool NoControls(unsigned int ControlState) { return (ControlState & (PKF_CONTROL | PKF_ALT | PKF_SHIFT)) == 0; } int PluginImpl::ProcessKey(int Key, unsigned int ControlState) { // fprintf(stderr, "NetRocks::ProcessKey(0x%x, 0x%x)\n", Key, ControlState); if (Key == VK_RETURN && ControlState == 0 && _remote && G.GetGlobalConfigBool("EnterExecRemotely", true)) { return ByKey_TryExecuteSelected() ? TRUE : FALSE; } if ((Key == VK_RETURN || (Key == VK_NEXT && ControlState == PKF_CONTROL)) && !_remote) { return ByKey_TryEnterSelectedSite() ? TRUE : FALSE; } if ((Key == VK_F5 || Key == VK_F6) && NoControls(ControlState)) { return ByKey_TryCrossload(Key == VK_F6) ? TRUE : FALSE; } if (Key == VK_F4 && !_remote && (ControlState == 0 || ControlState == PKF_SHIFT)) { ByKey_EditSiteConnection(ControlState == PKF_SHIFT); return TRUE; } if (Key == VK_F3 && !_remote) { return TRUE; } if (Key == 'A' && ControlState == PKF_CONTROL) { if (_remote) { ByKey_EditAttributesSelected(); } return TRUE; } if (Key == 'R' && ControlState == PKF_CONTROL && !_remote && g_conn_pool) { g_conn_pool->PurgeAll(); } /* if (Key == VK_F7) { MakeDirectory(); return TRUE; } */ return FALSE; } void PluginImpl::ByKey_EditSiteConnection(bool create_new) { if (g_conn_pool) { g_conn_pool->PurgeAll(); } std::string site; if (!create_new) { GetFocusedItem gfi(this); if (gfi.IsValid()) { if ( (gfi->FindData.dwFileAttributes & FILE_ATTRIBUTE_EXECUTABLE) == 0) { // dont allow editing of directories and return; } Wide2MB(gfi->FindData.lpwszFileName, site); } } SiteConnectionEditor sce(_sites_cfg_location, site); const bool connect_now = sce.Edit(); G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); if (connect_now) { SetDirectoryInternal(StrMB2Wide(sce.DisplayName()).c_str(), 0); } UpdatePathInfo(); G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); } bool PluginImpl::ByKey_TryCrossload(bool mv) { HANDLE plugin = INVALID_HANDLE_VALUE; G.info.Control(PANEL_ACTIVE, FCTL_GETPANELPLUGINHANDLE, 0, (LONG_PTR)(void *)&plugin); if (plugin != (void *)this) return false; G.info.Control(PANEL_PASSIVE, FCTL_GETPANELPLUGINHANDLE, 0, (LONG_PTR)(void *)&plugin); if (plugin == INVALID_HANDLE_VALUE) return false; std::shared_ptr dst_remote; std::string dst_site_dir; SitesConfigLocation sites_cfg_location; if (!g_all_netrocks.GetRemoteOf(plugin, dst_remote, dst_site_dir, sites_cfg_location)) return false; GetItems gi(true); if (gi.empty()) { return true; } if ( _remote) { if (!dst_remote) { // TODO: Show error that need connection fprintf(stderr, "ByKey_TryCrossload(%d): tried to upload to unconnected NetRocks instance\n", mv); return true; } auto state = StartXfer(0, _remote, CurrentSiteDir(true), dst_remote, dst_site_dir, &gi[0], (int)gi.size(), mv ? XK_MOVE : XK_COPY, XD_CROSSLOAD); if (state == BTS_ACTIVE || state == BTS_PAUSED) { _remote = _remote->Clone(); g_all_netrocks.CloneRemoteOf(plugin); } } else { if (dst_remote) { // TODO: Show error that need opened sites list fprintf(stderr, "ByKey_TryCrossload(%d): tried to transfer site to connected NetRocks instance\n", mv); return true; } if (_sites_cfg_location != sites_cfg_location) { if (!ConfirmSitesDisposition(ConfirmSitesDisposition::W_RELOCATE, mv).Ask()) { return true; } for (const auto &item : gi) if ( (item.FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && wcscmp(item.FindData.lpwszFileName, L"..") != 0) { _sites_cfg_location.Transfer(sites_cfg_location, Wide2MB(item.FindData.lpwszFileName), mv); } else if ( (item.FindData.dwFileAttributes & FILE_ATTRIBUTE_EXECUTABLE) != 0) { SitesConfig src(_sites_cfg_location), dst(sites_cfg_location); src.Transfer(dst, Wide2MB(item.FindData.lpwszFileName), mv); } } } G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_ACTIVE, FCTL_REDRAWPANEL, 0, 0); G.info.Control(PANEL_PASSIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_PASSIVE, FCTL_REDRAWPANEL, 0, 0); return true; } bool PluginImpl::ByKey_TryExecuteSelected() { GetFocusedItem gfi(this); if ( (gfi->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 || (gfi->FindData.dwFileAttributes & FILE_ATTRIBUTE_EXECUTABLE) == 0) { return false; } std::wstring cmd = L"./"; cmd+= gfi->FindData.lpwszFileName; QuoteCmdArgIfNeed(cmd); G.info.Control(this, FCTL_SETCMDLINE, 0, (LONG_PTR)cmd.c_str()); if (!ProcessEventCommand(cmd.c_str())) { G.info.Control(this, FCTL_SETCMDLINE, 0, (LONG_PTR)L""); return false; } return true; } bool PluginImpl::ByKey_TryEnterSelectedSite() { GetFocusedItem gfi(this); if ( (gfi->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { return false; } if ( (gfi->FindData.dwFileAttributes & FILE_ATTRIBUTE_EXECUTABLE) == 0) { //&& wcscmp(gfi->FindData.lpwszFileName, G.GetMsgWide(MCreateSiteConnection)) == 0) { ByKey_EditSiteConnection(true); return true; } StackedDir sd; StackedDirCapture(sd); bool out = SetDirectoryInternal(gfi->FindData.lpwszFileName, 0); if (!out) { StackedDirApply(sd); } UpdatePathInfo(); if (out) { G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_ACTIVE, FCTL_CLEARSELECTION, 0, 0); PanelRedrawInfo ri = {}; G.info.Control(PANEL_ACTIVE, FCTL_REDRAWPANEL, 0, (LONG_PTR)&ri); } return true; } void PluginImpl::ByKey_EditAttributesSelected() { GetItems gi(true); if (!gi.empty()) try { OpChangeMode(_remote, CurrentSiteDir(true), &gi[0], (int)gi.size()).Do(); G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); } catch (std::exception &ex) { fprintf(stderr, "ByKey_EditAttributesSelected: %s\n", ex.what()); } } static std::wstring GetCommandArgument(const wchar_t *cmd) { std::wstring out; const wchar_t *arg = wcschr(cmd, ' '); if (arg) { while (*arg == ' ') { ++arg; } out = arg; while (!out.empty() && out[out.size() - 1] == ' ') { out.resize(out.size() - 1); } } return out; } static void FakeExec() { G.info.FSF->Execute(L"true", EF_NOCMDPRINT); } void PluginImpl::StackedDirCapture(StackedDir &sd) { sd.remote = _remote; sd.location = _location; sd.sites_cfg_location = _sites_cfg_location; sd.standalone_config = _standalone_config; } void PluginImpl::StackedDirApply(StackedDir &sd) { if (sd.remote.get() != _remote.get()) { if (sd.remote && sd.remote.use_count() > 1) { // original connection could be used for background download etc _remote = sd.remote->Clone(); } else { _remote = sd.remote; } } _location = sd.location; _sites_cfg_location = sd.sites_cfg_location; _standalone_config = sd.standalone_config; UpdatePathInfo(); } int PluginImpl::ProcessEventCommand(const wchar_t *cmd) { if (wcsstr(cmd, L"exit ") == cmd || wcscmp(cmd, L"exit") == 0) { if (GetCommandArgument(cmd) == L"far") { return FALSE; } DismissRemoteHost(); UpdatePathInfo(); } else if (wcsstr(cmd, L"pushd ") == cmd || wcscmp(cmd, L"pushd") == 0) { StackedDir sd; StackedDirCapture(sd); const std::wstring &dir = GetCommandArgument(cmd); if ((!_remote && (dir.empty() || dir == L".")) || SetDirectoryInternal(dir.empty() ? L"~" : dir.c_str(), 0)) { _dir_stack.emplace_back(sd); FakeExec(); } } else if (wcscmp(cmd, L"popd") == 0) { if (!_dir_stack.empty()) { StackedDirApply(_dir_stack.back()); _dir_stack.pop_back(); UpdatePathInfo(); FakeExec(); } } else if (wcsstr(cmd, L"cd ") == cmd || wcscmp(cmd, L"cd") == 0) { const std::wstring &dir = GetCommandArgument(cmd); if (SetDirectoryInternal(dir.empty() ? L"~" : dir.c_str(), 0)) { FakeExec(); } } else if (_remote) try { OpExecute(0, _remote, CurrentSiteDir(false), Wide2MB(cmd)).Do(); } catch (ProtocolUnsupportedError &) { const wchar_t *msg[] = { G.GetMsgWide(MCommandsNotSupportedTitle), G.GetMsgWide(MCommandsNotSupportedText), G.GetMsgWide(MOK)}; G.info.Message(G.info.ModuleNumber, FMSG_WARNING, nullptr, msg, ARRAYSIZE(msg), 1); } catch (std::exception &ex) { fprintf(stderr, "OpExecute::Do: %s\n", ex.what()); const std::wstring &tmp_what = MB2Wide(ex.what()); const wchar_t *msg[] = { G.GetMsgWide(MError), tmp_what.c_str(), G.GetMsgWide(MOK)}; G.info.Message(G.info.ModuleNumber, FMSG_WARNING, nullptr, msg, ARRAYSIZE(msg), 1); } else { return FALSE; } UpdatePathInfo(); G.info.Control(this, FCTL_SETCMDLINE, 0, (LONG_PTR)L""); G.info.Control(PANEL_ACTIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_ACTIVE, FCTL_REDRAWPANEL, 0, 0); G.info.Control(PANEL_PASSIVE, FCTL_UPDATEPANEL, 0, 0); G.info.Control(PANEL_PASSIVE, FCTL_REDRAWPANEL, 0, 0); return TRUE; } void PluginImpl::DismissRemoteHost() { if (!g_conn_pool) g_conn_pool.reset(new ConnectionsPool); g_conn_pool->Put(CurrentConnectionPoolId(), _remote); if (_allow_remember_location_dir && _location.server_kind == Location::SK_SITE && G.GetGlobalConfigBool("RememberDirectory", false)) { SiteSpecification site_specification(StrWide2MB(_standalone_config), _location.server); SitesConfig sc(site_specification.sites_cfg_location); sc.SetDirectory(site_specification.site.c_str(), _location.ToString(false).c_str()); } _remote.reset(); } std::string PluginImpl::CurrentConnectionPoolId() { std::string out = _location.server; if (!_standalone_config.empty()) { out+= '@'; out+= StrWide2MB(_standalone_config); } return out; } void PluginImpl::sOnExiting() { g_conn_pool.reset(); } void PluginImpl::sOnGlobalSettingsChanged() { if (g_conn_pool) g_conn_pool->OnGlobalSettingsChanged(); } far2l-2.6.5~beta+ds/NetRocks/src/PluginImpl.h000066400000000000000000000053331477431623700207600ustar00rootroot00000000000000#pragma once #include #include #include #include #include "Host/Host.h" #include "UI/Defs.h" #include "UI/Activities/WhatOnError.h" #include "BackgroundTasks.h" #include "Location.h" #include "SitesConfig.h" class PluginImpl { friend class AllNetRocks; wchar_t _panel_title[64], _cur_dir[MAX_PATH], _mk_dir[MAX_PATH], _format[256]; Location _location; bool _allow_remember_location_dir = false; SitesConfigLocation _sites_cfg_location; std::wstring _standalone_config; std::shared_ptr _remote; std::shared_ptr _local; struct StackedDir { std::shared_ptr remote; Location location; SitesConfigLocation sites_cfg_location; std::wstring standalone_config; }; std::deque _dir_stack; std::shared_ptr _wea_state = std::make_shared(); void StackedDirCapture(StackedDir &sd); void StackedDirApply(StackedDir &sd); void UpdatePathInfo(); std::string CurrentSiteDir(bool with_ending_slash) const; void ByKey_EditSiteConnection(bool create_new); bool ByKey_TryCrossload(bool mv); bool ByKey_TryExecuteSelected(); bool ByKey_TryEnterSelectedSite(); void ByKey_EditAttributesSelected(); BackgroundTaskStatus StartXfer(int op_mode, std::shared_ptr &base_host, const std::string &base_dir, std::shared_ptr &dst_host, const std::string &dst_dir, struct PluginPanelItem *items, int items_count, XferKind kind, XferDirection direction); BOOL SitesXfer(const char *dir, struct PluginPanelItem *PanelItem, int ItemsNumber, bool mv, bool imp); int SetDirectoryInternal(const wchar_t *Dir, int OpMode); void DismissRemoteHost(); std::string CurrentConnectionPoolId(); bool ValidateLocationDirectory(int OpMode); public: PluginImpl(const wchar_t *path = nullptr, bool path_is_standalone_config = false, int OpMode = 0); virtual ~PluginImpl(); static void sOnExiting(); static void sOnGlobalSettingsChanged(); int GetFindData(PluginPanelItem **pPanelItem, int *pItemsNumber, int OpMode); void FreeFindData(PluginPanelItem *PanelItem, int ItemsNumber); int SetDirectory(const wchar_t *Dir, int OpMode); void GetOpenPluginInfo(struct OpenPluginInfo *Info); int DeleteFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int OpMode); int GetLinkTarget(struct PluginPanelItem *PanelItem, wchar_t *Target, size_t TargetSize, int OpMode); int GetFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int Move, const wchar_t *DestPath, int OpMode); int PutFiles(struct PluginPanelItem *PanelItem, int ItemsNumber, int Move, const wchar_t *SrcPath, int OpMode); int MakeDirectory(const wchar_t **Name, int OpMode); int ProcessKey(int Key, unsigned int ControlState); int ProcessEventCommand(const wchar_t *cmd); }; far2l-2.6.5~beta+ds/NetRocks/src/PluginPanelItems.cpp000066400000000000000000000040431477431623700224500ustar00rootroot00000000000000#include #include #include "PluginPanelItems.h" static PluginPanelItem *PluginPanelItems_ReAlloc(PluginPanelItem *prev, int capacity) { size_t alloc_size = ((size_t)capacity) * sizeof(PluginPanelItem); if (alloc_size < (size_t)capacity) return nullptr; return (PluginPanelItem *)realloc(prev, alloc_size); } void PluginPanelItems_Free(PluginPanelItem *items, int count) { if (items != nullptr) { for (int i = 0; i < count; ++i) { free(items[i].FindData.lpwszFileName); } free(items); } } PluginPanelItems::~PluginPanelItems() { PluginPanelItems_Free(items, count); } void PluginPanelItems::Detach() { items = nullptr; capacity = count = 0; } void PluginPanelItems::Shrink(int new_count) { ASSERT_MSG(new_count <= count, "new_count=%d while count=%d", new_count, count); for (int i = new_count; i < count; ++i) { free(items[i].FindData.lpwszFileName); } count = new_count; } PluginPanelItem *PluginPanelItems::Add(const wchar_t *name) { if (count == capacity) { if (capacity == std::numeric_limits::max() - 16) throw std::runtime_error("PluginPanelItems: capacity overflow");; int new_capacity = (capacity < (std::numeric_limits::max() / 2) ) ? capacity + capacity/2 + 0x100 : capacity + 16; PluginPanelItem *new_items = PluginPanelItems_ReAlloc(items, new_capacity); if (new_items == nullptr) { new_capacity = capacity + 1; new_items = PluginPanelItems_ReAlloc(items, new_capacity); if (new_items == nullptr) { throw std::runtime_error("PluginPanelItems: can't allocate new items"); } } items = new_items; capacity = new_capacity; } ASSERT_MSG(count <= capacity, "count=%d while capacity=%d", count, capacity); PluginPanelItem *out = &items[count]; ZeroFill(*out); out->FindData.lpwszFileName = wcsdup(name); if (out->FindData.lpwszFileName == nullptr) { throw std::runtime_error("PluginPanelItems: can't allocate filename"); } ++count; return out; } PluginPanelItem *PluginPanelItems::Add(const char *name) { MB2Wide(name, _tmp); return Add(_tmp.c_str()); } far2l-2.6.5~beta+ds/NetRocks/src/PluginPanelItems.h000066400000000000000000000007011477431623700221120ustar00rootroot00000000000000#pragma once #include #include #include #include void PluginPanelItems_Free(PluginPanelItem *items, int count); struct PluginPanelItems { PluginPanelItem *items = nullptr; int count = 0; ~PluginPanelItems(); void Shrink(int new_count); void Detach(); PluginPanelItem *Add(const wchar_t *name); PluginPanelItem *Add(const char *name); private: int capacity = 0; std::wstring _tmp; }; far2l-2.6.5~beta+ds/NetRocks/src/PooledStrings.cpp000066400000000000000000000026401477431623700220250ustar00rootroot00000000000000#include #include #include #include #include "PooledStrings.h" static std::unordered_set g_strings_pool; static std::mutex g_strings_pool_mutex; const char *PooledString(const std::string &s) { if (s.empty()) return ""; std::lock_guard locker(g_strings_pool_mutex); return g_strings_pool.insert(s).first->c_str(); } const char *PooledString(const char *s) { if (!s) return nullptr; if (!*s) return ""; return PooledString(std::string(s)); } const char *PooledString(const wchar_t *s) { return PooledString(Wide2MB(s)); } //////////////////////// static std::unordered_set g_wide_strings_pool; static std::mutex g_wide_strings_pool_mutex; static std::wstring g_wide_strings_pool_tmp; const wchar_t *MB2WidePooled(const char *str) { if (str == nullptr) return nullptr; if (!*str) return L""; std::lock_guard locker(g_wide_strings_pool_mutex); MB2Wide(str, g_wide_strings_pool_tmp); return g_wide_strings_pool.emplace(g_wide_strings_pool_tmp).first->c_str(); } const wchar_t *MB2WidePooled(const std::string &str) { return MB2WidePooled(str.c_str()); } ////////////////////////////////// void PurgePooledStrings() { { std::lock_guard locker(g_strings_pool_mutex); g_strings_pool.clear(); } { std::lock_guard locker(g_wide_strings_pool_mutex); g_wide_strings_pool.clear(); } } far2l-2.6.5~beta+ds/NetRocks/src/PooledStrings.h000066400000000000000000000004531477431623700214720ustar00rootroot00000000000000#pragma once #include const char *PooledString(const char *s); const char *PooledString(const wchar_t *s); const char *PooledString(const std::string &s); const wchar_t *MB2WidePooled(const std::string &str); const wchar_t *MB2WidePooled(const char *str); void PurgePooledStrings(); far2l-2.6.5~beta+ds/NetRocks/src/Protocol/000077500000000000000000000000001477431623700203245ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/000077500000000000000000000000001477431623700207565ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFile.cpp000066400000000000000000000011051477431623700227110ustar00rootroot00000000000000#include "AWSFile.h" AWSFile::AWSFile(std::string name, bool isFile): name(name), isFile(isFile) { modified.tv_sec = 0; modified.tv_nsec = 0; } AWSFile::AWSFile(std::string name, bool isFile, const Aws::Utils::DateTime &date, long long size): name(name), isFile(isFile) { modified.tv_sec = static_cast(date.Seconds()); modified.tv_nsec = 0; this->size = size; } void AWSFile::UpdateModification(const Aws::Utils::DateTime &date) { auto t = static_cast(date.Seconds()); if (t > modified.tv_sec) { modified.tv_sec = t; } } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFile.h000066400000000000000000000005441477431623700223640ustar00rootroot00000000000000#pragma once #include #include class AWSFile { public: std::string name; bool isFile; timespec modified; long long size = 0; AWSFile(std::string name, bool isFile); AWSFile(std::string name, bool isFile, const Aws::Utils::DateTime &date, long long size); void UpdateModification(const Aws::Utils::DateTime &date); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFileReader.cpp000066400000000000000000000033661477431623700240470ustar00rootroot00000000000000#include "AWSFileReader.h" #include AWSFileReader::AWSFileReader( std::shared_ptr client, const std::string& backet, const std::string& key, unsigned long long position, unsigned long long size ): _client(client), _backet(backet), _key(key), _position(position), _size(size) { floatBufferSize = std::min((size - position) / 10, MAX_BUFFER); if (floatBufferSize == 0) { floatBufferSize = std::min(size - position, MAX_BUFFER); } } size_t AWSFileReader::Read(void *buf, size_t len) { if (buffer.empty()) { if (_position + floatBufferSize > _size) { Download(_backet, _key, _position, _size - _position); } else { Download(_backet, _key, _position, floatBufferSize); } } size_t bytesRead = std::min(len, buffer.size()); std::memcpy(buf, buffer.data(), bytesRead); buffer.erase(buffer.begin(), buffer.begin() + bytesRead); _position += bytesRead; return bytesRead; } size_t AWSFileReader::Download(std::string backet, std::string key, size_t offset, size_t len) { Aws::S3::Model::GetObjectRequest request; request.SetBucket(backet); request.SetKey(key); Aws::String range = "bytes=" + std::to_string(offset) + "-" + std::to_string(offset + len - 1); request.SetRange(range); auto outcome = _client->GetObject(request); if (!outcome.IsSuccess()) { throw ProtocolError("Failed to get file: " + outcome.GetError().GetMessage()); } auto &stream = outcome.GetResult().GetBody(); size_t currentSize = buffer.size(); buffer.resize(currentSize + len); stream.read(buffer.data() + currentSize, len); return stream.gcount(); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFileReader.h000066400000000000000000000013701477431623700235050ustar00rootroot00000000000000#pragma once #include "../Protocol.h" #include class AWSFileReader : public IFileReader { private: std::shared_ptr _client; std::string _backet; std::string _key; unsigned long long _position; unsigned long long _size; std::vector buffer; static constexpr unsigned long long MAX_BUFFER = 10 * 1024 * 1024; unsigned long long floatBufferSize; size_t Download(std::string backet, std::string key, size_t offset, size_t len); public: AWSFileReader( std::shared_ptr client, const std::string& backet, const std::string& key, unsigned long long position, unsigned long long size ); virtual size_t Read(void *buf, size_t len); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFileWriter.cpp000066400000000000000000000053751477431623700241230ustar00rootroot00000000000000#include "AWSFileWriter.h" AWSFileWriter::AWSFileWriter(const std::string& bucket, const std::string& key, const std::shared_ptr client) : _bucket(bucket), _key(key), _client(client), partNumber(1) { StartMultipartUpload(); } AWSFileWriter::~AWSFileWriter() { try { CompleteMultipartUpload(); } catch (const std::exception& e) { AbortMultipartUpload(); } } void AWSFileWriter::StartMultipartUpload() { Aws::S3::Model::CreateMultipartUploadRequest request; request.SetBucket(_bucket); request.SetKey(_key); auto outcome = _client->CreateMultipartUpload(request); if (!outcome.IsSuccess()) { throw ProtocolError("Failed upload: " + outcome.GetError().GetMessage()); } uploadId = outcome.GetResult().GetUploadId(); } void AWSFileWriter::Write(const void* buf, size_t len) { const char* data = static_cast(buf); buffer.insert(buffer.end(), data, data + len); if (buffer.size() >= maxPartSize) { UploadPart(); } } void AWSFileWriter::WriteComplete() { CompleteMultipartUpload(); } void AWSFileWriter::UploadPart() { if (buffer.empty()) { return; } Aws::S3::Model::UploadPartRequest request; request.SetBucket(_bucket); request.SetKey(_key); request.SetUploadId(uploadId); request.SetPartNumber(partNumber); auto stream = Aws::MakeShared("S3MultipartWriter"); stream->write(buffer.data(), buffer.size()); stream->flush(); request.SetBody(stream); request.SetContentLength(static_cast(buffer.size())); auto outcome = _client->UploadPart(request); if (!outcome.IsSuccess()) { throw ProtocolError(outcome.GetError().GetMessage()); } Aws::S3::Model::CompletedPart part; part.SetETag(outcome.GetResult().GetETag()); part.SetPartNumber(partNumber); completedParts.push_back(part); ++partNumber; buffer.clear(); } void AWSFileWriter::CompleteMultipartUpload() { if (!buffer.empty()) { UploadPart(); } Aws::S3::Model::CompleteMultipartUploadRequest request; request.SetBucket(_bucket); request.SetKey(_key); request.SetUploadId(uploadId); Aws::S3::Model::CompletedMultipartUpload completedUpload; completedUpload.SetParts(completedParts); request.SetMultipartUpload(completedUpload); auto outcome = _client->CompleteMultipartUpload(request); if (!outcome.IsSuccess()) { throw ProtocolError("Failed: " + outcome.GetError().GetMessage()); } } void AWSFileWriter::AbortMultipartUpload() { Aws::S3::Model::AbortMultipartUploadRequest request; request.SetBucket(_bucket); request.SetKey(_key); request.SetUploadId(uploadId); _client->AbortMultipartUpload(request); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/AWSFileWriter.h000066400000000000000000000020471477431623700235610ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "../Protocol.h" class AWSFileWriter : public IFileWriter { public: AWSFileWriter(const std::string& bucket, const std::string& key, const std::shared_ptr client); ~AWSFileWriter(); private: const std::string _bucket; const std::string _key; std::shared_ptr _client; std::string uploadId; std::vector buffer; const size_t maxPartSize = 5 * 1024 * 1024; int partNumber; std::vector completedParts; void StartMultipartUpload(); void UploadPart(); void AbortMultipartUpload(); void CompleteMultipartUpload(); public: virtual void Write(const void* buf, size_t len); virtual void WriteComplete(); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/ProtocolAWS.cpp000066400000000000000000000115031477431623700236360ustar00rootroot00000000000000#include "ProtocolAWS.h" #include #include std::shared_ptr CreateProtocol(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) { return std::make_shared(host, port, username, password, options); } // Initialize the AWS SDK ProtocolAWS::ProtocolAWS(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &protocol_options) { _repository = std::make_shared(host, port, username, password, protocol_options); } ProtocolAWS::~ProtocolAWS() { } std::string ProtocolAWS::RootedPath(const std::string &path1) { auto path = path1; while (!path.empty() && (path[0] == '/')) { path.erase(0, 1); } while (!path.empty() && (path[path.length() - 1] == '/')) { path.erase(path.length() - 1, 1); } if (path.empty()) { return path; } if (path[0] == '.') { path.erase(0, 1); } while (!path.empty() && (path[0] == '/')) { path.erase(0, 1); } return path; } mode_t ProtocolAWS::GetMode(const std::string &path, bool follow_symlink) { auto f = _repository->GetFileInfo(path); if (f.isFile) { return S_IFREG | DEFAULT_ACCESS_MODE_FILE; } return S_IFDIR | DEFAULT_ACCESS_MODE_DIRECTORY; } unsigned long long ProtocolAWS::GetSize(const std::string &path, bool follow_symlink) { return _repository->GetFileInfo(path).size; } void ProtocolAWS::FileDelete(const std::string &path) { _repository->DeleteFile(RootedPath(path)); } void ProtocolAWS::DirectoryCreate(const std::string &path, mode_t mode) { auto rooted_path = RootedPath(path); if (rooted_path.find('/') < 0) { _repository->CreateBucket(rooted_path); } else { _repository->CreateDirectory(rooted_path); } } class AWSDirectoryEnumer : public IDirectoryEnumer { std::shared_ptr _repository; std::vector ls; public: AWSDirectoryEnumer(std::shared_ptr repository, const std::string &rooted_path) : _repository(repository) { if (rooted_path.empty()) { ls = _repository->ListBuckets(); } else { ls = _repository->ListFolder(rooted_path); } } virtual ~AWSDirectoryEnumer() { } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (ls.empty()) return false; file_info = FileInformation(); auto f = ls.front(); name = f.name; file_info.access_time = f.modified; file_info.modification_time = f.modified; file_info.status_change_time = f.modified; file_info.size = f.size; if (f.isFile) { file_info.mode = S_IFREG | DEFAULT_ACCESS_MODE_FILE; } else { file_info.mode = S_IFDIR | DEFAULT_ACCESS_MODE_DIRECTORY; } ls.erase(ls.begin()); return true; } }; std::shared_ptr ProtocolAWS::DirectoryEnum(const std::string &path) { const std::string &rooted_path = RootedPath(path); return std::shared_ptr(new AWSDirectoryEnumer(_repository, rooted_path)); } std::shared_ptr ProtocolAWS::FileGet(const std::string &path, unsigned long long resume_pos) { return _repository->GetDownloader( RootedPath(path), resume_pos, _repository->GetFileInfo(RootedPath(path)).size ); } std::shared_ptr ProtocolAWS::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { return _repository->GetUploader(RootedPath(path)); } void ProtocolAWS::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { auto rooted_path = RootedPath(path); auto f = _repository->GetFileInfo(rooted_path); file_info = FileInformation(); file_info.access_time = f.modified; file_info.modification_time = file_info.access_time; file_info.status_change_time = file_info.access_time; file_info.size = f.size; if (f.isFile) { file_info.mode = S_IFREG | DEFAULT_ACCESS_MODE_FILE; } else { file_info.mode = S_IFDIR | DEFAULT_ACCESS_MODE_DIRECTORY; } } void ProtocolAWS::DirectoryDelete(const std::string &path) { _repository->DeleteDirectory(RootedPath(path)); } void ProtocolAWS::Rename(const std::string &path_old, const std::string &path_new) { throw ProtocolUnsupportedError("Rename unsupported"); } void ProtocolAWS::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { } void ProtocolAWS::SetMode(const std::string &path, mode_t mode) { } void ProtocolAWS::SymlinkCreate(const std::string &link_path, const std::string &link_target) { throw ProtocolUnsupportedError("Symlink creation unsupported"); } void ProtocolAWS::SymlinkQuery(const std::string &link_path, std::string &link_target) { throw ProtocolUnsupportedError("Symlink unsupported"); }far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/ProtocolAWS.h000066400000000000000000000034171477431623700233100ustar00rootroot00000000000000#pragma once #include #include #include #include "../Protocol.h" #include "S3Repository.h" class ProtocolAWS : public IProtocol, public std::enable_shared_from_this { std::string _host; private: std::shared_ptr _repository; static std::string RootedPath(const std::string &path); public: ProtocolAWS(const std::string &host, unsigned int port,const std::string &username, const std::string &password, const std::string &protocol_options); virtual ~ProtocolAWS(); const std::vector &EnumHosts(); virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/S3Repository.cpp000066400000000000000000000325271477431623700240600ustar00rootroot00000000000000#include "S3Repository.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../Protocol.h" S3Repository::S3Repository(const std::string host, unsigned int port, const std::string accessKey, const std::string secret, const std::string &protocolOptions) { Aws::InitAPI(_options); StringConfig options(protocolOptions); auto useragent = options.GetString("UserAgent"); auto region = options.GetString("Region"); Aws::Client::ClientConfiguration config; if ((port > 0) && (port != 433)) { config.endpointOverride = Trim(host) + ":" + std::to_string(port); } else { config.endpointOverride = Trim(host); } if (options.GetInt("UseProxy", 0) != 0) { config.proxyHost = options.GetString("ProxyHost"); config.proxyPort = options.GetInt("ProxyPort"); if (options.GetInt("AuthProxy", 0) != 0) { config.proxyUserName = options.GetString("ProxyUsername"); config.proxyPassword = options.GetString("ProxyPassword"); } } if (!useragent.empty()) { config.userAgent = useragent; } if (!region.empty()) { config.region = region; } auto endpointProvider = Aws::MakeShared(Aws::Endpoint::DEFAULT_ENDPOINT_PROVIDER_TAG); if (accessKey.empty() && secret.empty()) { auto credentialsProvider = Aws::Auth::DefaultAWSCredentialsProviderChain(); auto credentials = credentialsProvider.GetAWSCredentials(); if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) { throw ProtocolError("Invalid credentals"); } _client = std::make_shared(config, endpointProvider); } else if (accessKey.empty()) { throw ProtocolError("Invalid login/access key"); } else if (secret.empty()) { throw ProtocolError("Invalid password/secret"); } else { Aws::Auth::AWSCredentials credentials(accessKey, secret); _client = std::make_shared(credentials, endpointProvider, config); } } S3Repository::~S3Repository() { Aws::ShutdownAPI(_options); } std::string S3Repository::Trim(const std::string& str) { const std::string unwanted = "/\\ "; auto result = str; size_t start = result.find_first_not_of(unwanted); if (start == std::string::npos) { result.clear(); return result; } result.erase(0, start); size_t end = result.find_last_not_of(unwanted); result.erase(end + 1); return result; } std::string S3Repository::GetS3ErrorTypeString(Aws::S3::S3Errors errorType) { switch (errorType) { case Aws::S3::S3Errors::INCOMPLETE_SIGNATURE: return "INCOMPLETE_SIGNATURE"; case Aws::S3::S3Errors::INTERNAL_FAILURE: return "INTERNAL_FAILURE"; case Aws::S3::S3Errors::INVALID_ACTION: return "INVALID_ACTION"; case Aws::S3::S3Errors::INVALID_CLIENT_TOKEN_ID: return "INVALID_CLIENT_TOKEN_ID"; case Aws::S3::S3Errors::INVALID_PARAMETER_COMBINATION: return "INVALID_PARAMETER_COMBINATION"; case Aws::S3::S3Errors::INVALID_QUERY_PARAMETER: return "INVALID_QUERY_PARAMETER"; case Aws::S3::S3Errors::INVALID_PARAMETER_VALUE: return "INVALID_PARAMETER_VALUE"; case Aws::S3::S3Errors::MISSING_ACTION: return "MISSING_ACTION"; case Aws::S3::S3Errors::MISSING_AUTHENTICATION_TOKEN: return "MISSING_AUTHENTICATION_TOKEN"; case Aws::S3::S3Errors::MISSING_PARAMETER: return "MISSING_PARAMETER"; case Aws::S3::S3Errors::OPT_IN_REQUIRED: return "OPT_IN_REQUIRED"; case Aws::S3::S3Errors::REQUEST_EXPIRED: return "REQUEST_EXPIRED"; case Aws::S3::S3Errors::SERVICE_UNAVAILABLE: return "SERVICE_UNAVAILABLE"; case Aws::S3::S3Errors::THROTTLING: return "THROTTLING"; case Aws::S3::S3Errors::VALIDATION: return "VALIDATION"; case Aws::S3::S3Errors::ACCESS_DENIED: return "ACCESS_DENIED"; case Aws::S3::S3Errors::RESOURCE_NOT_FOUND: return "RESOURCE_NOT_FOUND"; case Aws::S3::S3Errors::UNRECOGNIZED_CLIENT: return "UNRECOGNIZED_CLIENT"; case Aws::S3::S3Errors::MALFORMED_QUERY_STRING: return "MALFORMED_QUERY_STRING"; case Aws::S3::S3Errors::SLOW_DOWN: return "SLOW_DOWN"; case Aws::S3::S3Errors::REQUEST_TIME_TOO_SKEWED: return "REQUEST_TIME_TOO_SKEWED"; case Aws::S3::S3Errors::INVALID_SIGNATURE: return "INVALID_SIGNATURE"; case Aws::S3::S3Errors::SIGNATURE_DOES_NOT_MATCH: return "SIGNATURE_DOES_NOT_MATCH"; case Aws::S3::S3Errors::INVALID_ACCESS_KEY_ID: return "INVALID_ACCESS_KEY_ID"; case Aws::S3::S3Errors::REQUEST_TIMEOUT: return "REQUEST_TIMEOUT"; case Aws::S3::S3Errors::NETWORK_CONNECTION: return "NETWORK_CONNECTION"; case Aws::S3::S3Errors::BUCKET_ALREADY_EXISTS: return "BUCKET_ALREADY_EXISTS"; case Aws::S3::S3Errors::BUCKET_ALREADY_OWNED_BY_YOU: return "BUCKET_ALREADY_OWNED_BY_YOU"; case Aws::S3::S3Errors::ENCRYPTION_TYPE_MISMATCH: return "ENCRYPTION_TYPE_MISMATCH"; case Aws::S3::S3Errors::INVALID_OBJECT_STATE: return "INVALID_OBJECT_STATE"; case Aws::S3::S3Errors::INVALID_REQUEST: return "INVALID_REQUEST"; case Aws::S3::S3Errors::INVALID_WRITE_OFFSET: return "INVALID_WRITE_OFFSET"; case Aws::S3::S3Errors::NO_SUCH_BUCKET: return "NO_SUCH_BUCKET"; case Aws::S3::S3Errors::NO_SUCH_KEY: return "NO_SUCH_KEY"; case Aws::S3::S3Errors::NO_SUCH_UPLOAD: return "NO_SUCH_UPLOAD"; case Aws::S3::S3Errors::OBJECT_ALREADY_IN_ACTIVE_TIER: return "OBJECT_ALREADY_IN_ACTIVE_TIER"; case Aws::S3::S3Errors::OBJECT_NOT_IN_ACTIVE_TIER: return "OBJECT_NOT_IN_ACTIVE_TIER"; case Aws::S3::S3Errors::TOO_MANY_PARTS: return "TOO_MANY_PARTS"; default: return "UNKNOWN_ERROR"; } } ProtocolError S3Repository::ConstructProtocolError(const Aws::Client::AWSError& error, const std::string& context) { std::string errorMessage; if (!context.empty()) { errorMessage += context + ": "; } auto msg = error.GetMessage(); if (msg.empty()) { errorMessage += GetS3ErrorTypeString(error.GetErrorType()); } else { errorMessage += msg; } return ProtocolError(errorMessage); } std::vector S3Repository::ListBuckets() { Aws::S3::Model::ListBucketsRequest request; auto outcome = _client->ListBuckets(); if (outcome.IsSuccess()) { const auto& buckets = outcome.GetResult().GetBuckets(); std::vector ls; for (const auto& bucket : buckets) { ls.push_back(AWSFile(bucket.GetName(), false, bucket.GetCreationDate(), 0)); } return ls; } else { throw ConstructProtocolError(outcome.GetError(), "List buckets"); } } std::vector S3Repository::ListFolder(const std::string &path) { Aws::S3::Model::ListObjectsV2Request request; size_t prefixLen = 0; auto localPath = Path(path); request.SetBucket(localPath.bucket()); if (localPath.hasKey()) { request.SetPrefix(localPath.keyWithSlash()); prefixLen = localPath.keyWithSlash().length(); } auto outcome = _client->ListObjectsV2(request); if (outcome.IsSuccess()) { const auto& contents = outcome.GetResult().GetContents(); std::vector ls; std::map folders; for (const auto& object : contents) { Aws::String key = object.GetKey().substr(prefixLen); size_t last_slash_pos = key.find_last_of("/"); if (last_slash_pos == std::string::npos) { ls.push_back(AWSFile(key, true, object.GetLastModified(), object.GetSize())); } else { size_t pos = key.find('/'); if (pos != Aws::String::npos) { auto dir = key.substr(0, pos); auto result = folders.emplace( dir, AWSFile(dir, false, object.GetLastModified(), object.GetSize()) ); if (!result.second) { result.first->second.size += object.GetSize(); result.first->second.UpdateModification(object.GetLastModified()); } } } } for (const auto& pair : folders) { ls.push_back(pair.second); } return ls; } else { throw ConstructProtocolError(outcome.GetError(), "List dir"); } } bool S3Repository::IsFolder(const Path& localPath) { Aws::S3::Model::ListObjectsV2Request listRequest; listRequest.SetBucket(localPath.bucket()); listRequest.SetPrefix(localPath.keyWithSlash()); listRequest.SetMaxKeys(1); auto listOutcome = _client->ListObjectsV2(listRequest); if (listOutcome.IsSuccess()) { return !listOutcome.GetResult().GetContents().empty(); } return false; } AWSFile S3Repository::GetFileInfo(const std::string &path) { Path localPath(path); if (IsFolder(localPath) || localPath.key().empty()) { Aws::S3::Model::ListObjectsV2Request request; request.SetBucket(localPath.bucket()); request.SetPrefix(localPath.keyWithSlash()); auto outcome = _client->ListObjectsV2(request); if (outcome.IsSuccess()) { auto result = AWSFile(ExtractFileName(localPath.key()), false); const auto& contents = outcome.GetResult().GetContents(); for (const auto& object : contents) { result.size += object.GetSize(); result.UpdateModification(object.GetLastModified()); } return result; } else { throw ConstructProtocolError(outcome.GetError(), "Access denied"); } } Aws::S3::Model::HeadObjectRequest request; request.SetBucket(localPath.bucket()); request.SetKey(localPath.key()); auto outcome = _client->HeadObject(request); if (outcome.IsSuccess()) { const auto& object = outcome.GetResult(); return AWSFile(ExtractFileName(path), true, object.GetLastModified(), object.GetContentLength()); } else { throw ConstructProtocolError(outcome.GetError(), "Access denied"); } } std::string S3Repository::ExtractFileName(const std::string& path) { size_t pos = path.find_last_of('/'); if (pos == std::string::npos) { return path; } return path.substr(pos + 1); } std::shared_ptr S3Repository::GetUploader(const std::string& path) { Path localPath(path); return std::make_shared(localPath.bucket(), localPath.key(), _client); } std::shared_ptr S3Repository::GetDownloader(const std::string& path, unsigned long long position, unsigned long long size) { Path localPath(path); return std::make_shared(_client, localPath.bucket(), localPath.key(), position, size); } void S3Repository::CreateBucket(const std::string& bucket) { Aws::S3::Model::CreateBucketRequest request; request.SetBucket(bucket); auto outcome = _client->CreateBucket(request); if (!outcome.IsSuccess()) { throw ConstructProtocolError(outcome.GetError(), "Create bucket"); } } void S3Repository::CreateDirectory(const std::string& path) { Path localPath(path); Aws::S3::Model::PutObjectRequest request; request.SetBucket(localPath.bucket()); request.SetKey(localPath.keyWithSlash()); request.SetBody(Aws::MakeShared("")); auto outcome = _client->PutObject(request); if (!outcome.IsSuccess()) { throw ConstructProtocolError(outcome.GetError(), "Create dir"); } } void S3Repository::DeleteDirectory(const std::string& path) { Path localPath(path); std::string folderKey = localPath.keyWithSlash(); for (auto val : ListFolder(path)) { Aws::S3::Model::DeleteObjectRequest request; request.SetBucket(localPath.bucket()); request.SetKey(folderKey + val.name); auto outcome = _client->DeleteObject(request); if (!outcome.IsSuccess()) { throw ConstructProtocolError(outcome.GetError()); } } if (localPath.key().empty()) { Aws::S3::Model::DeleteBucketRequest request; request.SetBucket(localPath.bucket()); auto outcome = _client->DeleteBucket(request); if (!outcome.IsSuccess()) { throw ConstructProtocolError(outcome.GetError()); } } } void S3Repository::DeleteFile(const std::string& path) { Path localPath(path); Aws::S3::Model::DeleteObjectRequest request; request.SetBucket(localPath.bucket()); request.SetKey(localPath.key()); auto outcome = _client->DeleteObject(request); if (!outcome.IsSuccess()) { throw ConstructProtocolError(outcome.GetError()); } } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/AWS/S3Repository.h000066400000000000000000000054131477431623700235170ustar00rootroot00000000000000#pragma once #include #include #include #include #include "AWSFile.h" #include "AWSFileWriter.h" #include "AWSFileReader.h" class S3Repository { private: class Path { public: Path(const std::string& path): _bucket(ParseBucket(path)), _key(ParseKey(path)) {} const std::string bucket() const { return _bucket; } const std::string key() const { return _key; } const std::string keyWithSlash() const { return _key + "/"; } bool hasKey() const { return !_key.empty(); } private: const std::string _bucket; const std::string _key; static std::string ParseBucket(const std::string& path) { size_t start = path.find_first_not_of('/'); if (start == std::string::npos) { return ""; } auto pos = path.find('/', start); if (pos == std::string::npos) { return path.substr(start); } return path.substr(start, pos - start); } static std::string ParseKey(const std::string& path) { std::string trimmedPath = path; while (!trimmedPath.empty() && trimmedPath.front() == '/') { trimmedPath.erase(0, 1); } while (!trimmedPath.empty() && trimmedPath.back() == '/') { trimmedPath.pop_back(); } auto pos = trimmedPath.find('/'); if (pos == std::string::npos) { return ""; } return trimmedPath.substr(pos + 1); } }; std::shared_ptr _client; Aws::SDKOptions _options; bool IsFolder(const Path& localPath); static std::string GetS3ErrorTypeString(Aws::S3::S3Errors errorType); static ProtocolError ConstructProtocolError(const Aws::Client::AWSError& error, const std::string& context = ""); static std::string ExtractFileName(const std::string& path); static std::string Trim(const std::string& str); public: S3Repository(const std::string host, unsigned int port, const std::string accessKey, const std::string secret, const std::string &protocol_options); ~S3Repository(); std::vector ListBuckets(); std::vector ListFolder(const std::string &path); AWSFile GetFileInfo(const std::string &path); std::shared_ptr GetDownloader(const std::string& path, unsigned long long position, unsigned long long size); std::shared_ptr GetUploader(const std::string& path); void CreateBucket(const std::string &path); void CreateDirectory(const std::string &path); void DeleteDirectory(const std::string &path); void DeleteFile(const std::string& path); };far2l-2.6.5~beta+ds/NetRocks/src/Protocol/DirectoryEnumCache.cpp000066400000000000000000000103331477431623700245450ustar00rootroot00000000000000#include "DirectoryEnumCache.h" DirectoryEnumCache::DirectoryEnumCache(unsigned int expiration) : _expiration((time_t)expiration) { } bool DirectoryEnumCache::HasValidEntries() { time_t ts = time(NULL); for (auto it = _dir_enum_cache.begin(); it != _dir_enum_cache.end(); ) { if (it->second->ts > ts || it->second->ts + _expiration < ts) { it = _dir_enum_cache.erase(it); } else { return true; } } return false; } void DirectoryEnumCache::Clear() { _dir_enum_cache.clear(); } void DirectoryEnumCache::Remove(const std::string &path, const std::string &name) { auto it = _dir_enum_cache.find(path); if (it == _dir_enum_cache.end()) { return; } time_t ts = time(NULL); if (it->second->ts > ts || it->second->ts + _expiration < ts) { _dir_enum_cache.erase(it); return; } if (it->second->index.empty()) { for (auto entry_it = it->second->begin(); entry_it != it->second->end(); ++entry_it) { it->second->index.emplace(entry_it->name, entry_it); } } auto index_it = it->second->index.find(name); if (index_it != it->second->index.end()) { it->second->erase(index_it->second); it->second->index.erase(index_it); } } void DirectoryEnumCache::Remove(const std::string &path) { auto it = _dir_enum_cache.find(path); if (it != _dir_enum_cache.end()) { _dir_enum_cache.erase(it); } } class CachedDirectoryEnumer : public IDirectoryEnumer { std::shared_ptr _dce_sp; DirectoryEnumCache::DirCacheEntry::const_iterator _it; public: CachedDirectoryEnumer(std::shared_ptr &dce_sp) : _dce_sp(dce_sp) { _it = _dce_sp->begin(); } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (_it == _dce_sp->end()) { return false; } name = _it->name; owner = _it->owner; group = _it->group; file_info = _it->file_info; ++_it; return true; } }; std::shared_ptr DirectoryEnumCache::GetCachedDirectoryEnumer(const std::string &path) { auto it = _dir_enum_cache.find(path); if (it == _dir_enum_cache.end()) { return std::shared_ptr(); } time_t ts = time(NULL); if (it->second->ts > ts || it->second->ts + _expiration < ts) { _dir_enum_cache.erase(it); return std::shared_ptr(); } return std::shared_ptr(new CachedDirectoryEnumer(it->second)); } class CachingWrapperDirectoryEnumer : public IDirectoryEnumer { std::shared_ptr _dce_sp; std::shared_ptr _enumer; bool _enumed_til_the_end = false; public: CachingWrapperDirectoryEnumer(std::shared_ptr &dce_sp, std::shared_ptr &enumer) : _dce_sp(dce_sp), _enumer(enumer) { } virtual ~CachingWrapperDirectoryEnumer() { try { if (!_enumed_til_the_end) { std::string name, owner, group; FileInformation file_info; while (Enum(name, owner, group, file_info)); } _dce_sp->ts = time(NULL); } catch (std::exception &) { _dce_sp->ts = 0; } } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (!_enumer->Enum(name, owner, group, file_info)) { _enumed_til_the_end = true; return false; } _dce_sp->emplace_back(); auto &e = _dce_sp->back(); e.name = name; e.owner = owner; e.group = group; e.file_info = file_info; return true; } }; std::shared_ptr DirectoryEnumCache::GetCachingWrapperDirectoryEnumer(const std::string &path, std::shared_ptr &enumer) { time_t ts = time(NULL); if (_last_expiration_inspection > ts || _last_expiration_inspection + _expiration < ts) { for (auto it = _dir_enum_cache.begin(); it != _dir_enum_cache.end(); ) { if (it->second->ts > ts || it->second->ts + _expiration < ts) { it = _dir_enum_cache.erase(it); } else { ++it; } } _last_expiration_inspection = ts; } auto &dce_sp_ref = _dir_enum_cache[path]; if (dce_sp_ref) { dce_sp_ref->clear(); dce_sp_ref->index.clear(); dce_sp_ref->ts = 0; } else { dce_sp_ref = std::make_shared(); } return std::shared_ptr(new CachingWrapperDirectoryEnumer(dce_sp_ref, enumer)); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/DirectoryEnumCache.h000066400000000000000000000021451477431623700242140ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "Protocol.h" #include "../FileInformation.h" class DirectoryEnumCache { friend class CachedDirectoryEnumer; friend class CachingWrapperDirectoryEnumer; time_t _expiration; struct DirCachedListEntry { std::string name; std::string owner; std::string group; FileInformation file_info; }; struct DirCacheEntry : std::enable_shared_from_this, std::list { time_t ts = 0; std::map index; }; std::map > _dir_enum_cache; time_t _last_expiration_inspection = 0; public: DirectoryEnumCache(unsigned int expiration); bool HasValidEntries(); void Clear(); void Remove(const std::string &path, const std::string &name); void Remove(const std::string &path); std::shared_ptr GetCachedDirectoryEnumer(const std::string &path); std::shared_ptr GetCachingWrapperDirectoryEnumer(const std::string &path, std::shared_ptr &enumer); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/000077500000000000000000000000001477431623700207555ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPConnection.cpp000066400000000000000000000436131477431623700241410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "FTPConnection.h" #define FTP_ENDLINE "\r\n" BaseTransport::~BaseTransport() { } void BaseTransport::Send(const void *data, size_t len) { for (size_t transferred = 0; transferred < len;) { ssize_t r = SendImpl(data, len); if (r <= 0) { throw std::runtime_error(StrPrintf("send errno=%d", errno)); //return transferred ? transferred : r; } transferred+= (size_t)r; } } ssize_t BaseTransport::Recv(void *data, size_t len) { return RecvImpl(data, len); } int BaseTransport::DetachSocket() { return _sock.Detach(); } void BaseTransport::GetPeerAddress(struct sockaddr_in &sin) { socklen_t l = sizeof(sin); if (getpeername(_sock, (struct sockaddr *)&sin, &l) == -1) { throw std::runtime_error(StrPrintf("getpeername errno=%d", errno)); } } void BaseTransport::GetLocalAddress(struct sockaddr_in &sin) { socklen_t l = sizeof(sin); if (getsockname(_sock, (struct sockaddr *)&sin, &l) == -1) { throw std::runtime_error(StrPrintf("getsockname errno=%d", errno)); } } //////////// SocketTransport::SocketTransport(int sock, const StringConfig &protocol_options) { _sock = sock; ApplySocketOptions(protocol_options); } SocketTransport::SocketTransport(struct sockaddr_in &sin, const StringConfig &protocol_options) { Connect(sin, protocol_options); } SocketTransport::SocketTransport(const std::string &host, unsigned int port, const StringConfig &protocol_options) { struct sockaddr_in sin = {0}; sin.sin_family = AF_INET; sin.sin_port = htons(port); if (inet_aton(host.c_str(), &sin.sin_addr) == 0) { struct hostent *he = gethostbyname(host.c_str()); if (!he) { throw std::runtime_error(StrPrintf("gethostbyname('%s') errno=%d", host.c_str(), errno)); } memcpy(&sin.sin_addr, he->h_addr, std::min(sizeof(sin.sin_addr), (size_t)he->h_length)); } Connect(sin, protocol_options); } void SocketTransport::ApplySocketOptions(const StringConfig &protocol_options) { if (protocol_options.GetInt("TcpNoDelay", 1) ) { int nodelay = 1; if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&nodelay, sizeof(nodelay)) == -1) { perror("SocketTransport - TCP_NODELAY"); } } if (protocol_options.GetInt("TcpQuickAck") ) { #ifdef TCP_QUICKACK int quickack = 1; if (setsockopt(_sock, IPPROTO_TCP, TCP_QUICKACK , (void *)&quickack, sizeof(quickack)) == -1) { perror("SocketTransport - TCP_QUICKACK "); } #else fprintf(stderr, "SocketTransport: TCP_QUICKACK requested but not supported\n"); #endif } } void SocketTransport::Connect(struct sockaddr_in &sin, const StringConfig &protocol_options) { _sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (_sock == -1) { throw std::runtime_error(StrPrintf("socket() errno=%d", errno)); } ApplySocketOptions(protocol_options); if (connect(_sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { throw std::runtime_error(StrPrintf("connect() errno=%d", errno)); } } ssize_t SocketTransport::SendImpl(const void *data, size_t len) { return os_call_ssize(send, (int)_sock, data, len, 0); } ssize_t SocketTransport::RecvImpl(void *data, size_t len) { return os_call_ssize(recv, (int)_sock, data, len, 0); } #ifdef HAVE_OPENSSL static int SSLStartup() { int out = OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); return out; } int OpenSSLContext::sNewClientSessionCB(SSL *ssl, SSL_SESSION *session) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L OpenSSLContext *ctx = (OpenSSLContext *)SSL_get_app_data(ssl); if (ctx != NULL && ctx->_session == NULL && session != NULL) { SSL_SESSION_up_ref(session); ctx->_session = session; } #else ABORT(); #endif return 0; } OpenSSLContext::OpenSSLContext(const StringConfig &protocol_options) { static int s_ssl_startup = SSLStartup(); if (!s_ssl_startup) { s_ssl_startup = SSLStartup(); if (!s_ssl_startup) { fprintf(stderr, "TLSTransport: SSLStartup failed\n"); } } _ctx = SSL_CTX_new(SSLv23_client_method()); if (!_ctx) { throw std::runtime_error(StrPrintf("SSL_CTX_new() errno=%d", errno)); } switch (protocol_options.GetInt("EncryptionProtocol", 3)) { case 4: SSL_CTX_set_options(_ctx, SSL_OP_NO_TLSv1_1); case 3: SSL_CTX_set_options(_ctx, SSL_OP_NO_TLSv1); case 2: SSL_CTX_set_options(_ctx, SSL_OP_NO_SSLv3); case 1: SSL_CTX_set_options(_ctx, SSL_OP_NO_SSLv2); case 0: default: ; } #if OPENSSL_VERSION_NUMBER >= 0x10100000L SSL_CTX_sess_set_new_cb(_ctx, sNewClientSessionCB); SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE | SSL_SESS_CACHE_NO_AUTO_CLEAR); #else SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_AUTO_CLEAR); #endif } OpenSSLContext::~OpenSSLContext() { if (_session) { SSL_SESSION_free(_session); } SSL_CTX_free(_ctx); } SSL *OpenSSLContext::NewSSL(int sock) { SSL *ssl = SSL_new(_ctx); if (!ssl) { throw std::runtime_error(StrPrintf("SSL_new() errno=%d", errno)); } #if OPENSSL_VERSION_NUMBER >= 0x10100000L SSL_set_app_data(ssl, this); if (_session) { SSL_set_session(ssl, _session); _session = NULL; // "TLS 1.3 does strongly encourage single-use of resumption ticket" } #else if (_session) { SSL_set_session(ssl, _session); } #endif if (SSL_set_fd(ssl, sock) != 1) { SSL_free(ssl); throw std::runtime_error(StrPrintf("SSL_set_fd() errno=%d", errno)); } if (SSL_connect(ssl) != 1) { SSL_shutdown(ssl); SSL_free(ssl); throw std::runtime_error(StrPrintf("SSL_connect() errno=%d", errno)); } #if OPENSSL_VERSION_NUMBER < 0x10100000L // Doesn't work since TLS1.3 that requires storing session from sNewClientSessionCB // for correct TLS session reuse functionality, however old OpenSSL doesn't have // SSL_SESSION_up_ref that makes it incompatible with TLS1.3 servers that require // session reuse. if (!_session) { _session = SSL_get1_session(ssl); } #endif return ssl; } /// TLSTransport::TLSTransport(std::shared_ptr &ctx, int sock) : _ctx(ctx) { _sock = sock; _ssl = _ctx->NewSSL(sock); } TLSTransport::~TLSTransport() { Shutdown(); } void TLSTransport::Shutdown() { if (_ssl != nullptr) { SSL_shutdown(_ssl); SSL_free(_ssl); _ssl = nullptr; } } ssize_t TLSTransport::SendImpl(const void *data, size_t len) { if (!_ssl) { return -1; } return SSL_write(_ssl, data, len); } ssize_t TLSTransport::RecvImpl(void *data, size_t len) { if (!_ssl) { return -1; } return SSL_read(_ssl, data, len); } int TLSTransport::DetachSocket() { Shutdown(); return BaseTransport::DetachSocket(); } std::string TLSTransport::GetPeerFingerprint() { std::string out; X509 *cert = SSL_get_peer_certificate(_ssl); if (cert) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_size = sizeof(digest); #ifndef OPENSSL_NO_SHA256 if (X509_digest(cert, EVP_sha256(), digest, &digest_size)) #else if (X509_digest(cert, EVP_md5(), digest, &digest_size)) #endif { for (unsigned int i = 0; i != digest_size; ++i) { out+= StrPrintf("%02x", (unsigned int)digest[i]); } } X509_free(cert); } return out; } #endif FTPConnection::FTPConnection(bool implicit_encryption, const std::string &host, unsigned int port, const std::string &options) : _protocol_options(options) { // sleep(30); _batch.pipelining = _protocol_options.GetInt("CommandsPipelining", 0) != 0; if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPConnection(%d, %s, %u) _batch.pipelining=%u\n", implicit_encryption, host.c_str(), port, _batch.pipelining); } if (!port) { port = implicit_encryption ? 990 : 21; } _transport = std::make_shared(host, port, _protocol_options); std::string str; #ifdef HAVE_OPENSSL if (implicit_encryption) { _openssl_ctx = std::make_shared(_protocol_options); } else if (_protocol_options.GetInt("ExplicitEncryption", 0)) { unsigned int banner_code = RecvResponseFromTransport(str); FTPThrowIfBadResponse(str, banner_code, 200, 299); str = "AUTH TLS"; unsigned int reply_code = SendRecvResponse(str); if (reply_code != 234) { if (_protocol_options.GetInt("EncryptionProtocol", 3) < 3) { str = "AUTH SSL"; reply_code = SendRecvResponse(str); if (reply_code == 234) { fprintf(stderr, "FTPConnection: AUTH SSL\n"); } } if (reply_code != 234) { throw ProtocolError(str); } } else { fprintf(stderr, "FTPConnection: AUTH TLS\n"); } _openssl_ctx = std::make_shared(_protocol_options); } if (_openssl_ctx) { auto tls_transport = std::make_shared(_openssl_ctx, _transport->DetachSocket()); const std::string &fingerprint = tls_transport->GetPeerFingerprint(); if (fingerprint != _protocol_options.GetString("ServerIdentity")) throw ServerIdentityMismatchError(fingerprint); _transport = tls_transport; } if (!_openssl_ctx || implicit_encryption) { unsigned int banner_code = RecvResponseFromTransport(str); FTPThrowIfBadResponse(str, banner_code, 200, 299); } #else if (implicit_encryption || _protocol_options.GetInt("ExplicitEncryption", 0)) { throw ProtocolError("Encrypted FTP requires OpenSSL support"); } unsigned int banner_code = RecvResponseFromTransport(str); FTPThrowIfBadResponse(str, banner_code, 200, 299); #endif } FTPConnection::~FTPConnection() { } bool FTPConnection::EnableDataConnectionProtection() { SendRequest("PBSZ 0"); SendRequest("PROT P"); std::string str_pbsz, str_prot; unsigned int code_pbsz = RecvResponse(str_pbsz); unsigned int code_prot = RecvResponse(str_prot); if (code_pbsz != 200) { fprintf(stderr, "FTPConnection::EnableDataConnectionProtection: - '%s'\n", str_pbsz.c_str()); return false; } if (code_prot != 200) { fprintf(stderr, "FTPConnection::EnableDataConnectionProtection: - '%s'\n", str_prot.c_str()); return false; } fprintf(stderr, "FTPConnection::EnableDataConnectionProtection: OK\n"); return true; } unsigned int FTPConnection::RecvResponseFromTransport(std::string &str) { char match[4]; str.clear(); for (size_t cnt = 0, ofs = 0;;) { char ch; do { if (str.size() > 0x100000) { throw std::runtime_error("too long response"); } ssize_t r = _transport->Recv(&ch, sizeof(ch)); if (r <= 0) { throw std::runtime_error(StrPrintf("read response error %d", errno)); } str+= ch; } while (ch != '\n'); size_t p = str.size() - 1; ++cnt; if (cnt == 1 && p - ofs > 3 && str[ofs + 3] == '-') { memcpy(match, str.c_str() + ofs, 3); match[3] = ' '; } else if (cnt == 1 || (p >= 4 && memcmp(str.c_str() + ofs, match, 4) == 0)) { unsigned int out = atoi(str.c_str() + ofs); if (out == 0) { out = atoi(str.c_str()); } return out; } ofs = p + 1; } } void FTPConnection::BatchPerform() { if (_batch.requests.empty()) { return; } if (_batch.pipelining && _batch.requests.size() > 1) { //_batch.responses.reserve(_batch.responses.size() + _batch.requests.size()); _str.clear(); for (const auto &r : _batch.requests) { _str+= r; } _transport->Send(_str.c_str(), _str.size()); for (size_t i = _batch.requests.size(); i != 0; --i) { _batch.responses.emplace_back(); _batch.responses.back().code = RecvResponseFromTransport(_batch.responses.back().str); } } else { for (const auto &r : _batch.requests) { _transport->Send(r.c_str(), r.size()); _batch.responses.emplace_back(); _batch.responses.back().code = RecvResponseFromTransport(_batch.responses.back().str); } } _batch.requests.clear(); } unsigned int FTPConnection::SendRecvResponse(std::string &str) { if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPConnection::SendRecvResponse('%s')\n", str.c_str()); for (const auto &r : _batch.requests) { fprintf(stderr, "FTPConnection::SendRecvResponse: pending request '%s'\n", r.c_str()); } for (const auto &r : _batch.responses) { fprintf(stderr, "FTPConnection::SendRecvResponse: pending response '%s'\n", r.str.c_str()); } } if (!_batch.requests.empty() || !_batch.responses.empty()) { throw std::runtime_error("FTPConnection::SendRecvResponse while batch not empty"); } str+= FTP_ENDLINE; _transport->Send(str.c_str(), str.size()); unsigned int reply_code = RecvResponseFromTransport(str); if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPConnection::SendRecvResponse: response '%s'\n", str.c_str()); } return reply_code; } void FTPConnection::SendRequest(const std::string &str) { _batch.requests.emplace_back(str); _batch.requests.back().append(FTP_ENDLINE); if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPConnection::SendRequest('%s')\n", str.c_str()); } } unsigned int FTPConnection::RecvResponse(std::string &str) { BatchPerform(); if (_batch.responses.empty()) { throw std::runtime_error("FTPConnection::RecvResponse: batch is empty"); } str.swap(_batch.responses.front().str); unsigned int reply_code = _batch.responses.front().code; _batch.responses.pop_front(); if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPConnection::RecvResponse: %s\n", str.c_str()); } return reply_code; } void FTPConnection::RecvResponse(std::string &str, unsigned int reply_ok_min, unsigned int reply_ok_max) { unsigned int reply_code = RecvResponse(str); FTPThrowIfBadResponse(str, reply_code, reply_ok_min, reply_ok_max); } void FTPConnection::SendRecvResponse(std::string &str, unsigned int reply_ok_min, unsigned int reply_ok_max) { unsigned int reply_code = SendRecvResponse(str); FTPThrowIfBadResponse(str, reply_code, reply_ok_min, reply_ok_max); } void FTPConnection::SendRestIfNeeded(unsigned long long rest) { if (rest != 0) { std::string str = StrPrintf("REST %lld", rest); unsigned int reply_code = SendRecvResponse(str); if (reply_code < 300 || reply_code >= 400) { throw ProtocolError(StrPrintf( "REST %lld rejected: %u '%s'", rest, reply_code, str.c_str())); } } } void FTPConnection::DataCommand_PASV(std::shared_ptr &data_transport, const std::string &cmd, unsigned long long rest) { std::string str = "PASV"; SendRecvResponse(str, 200, 299); size_t p = str.find('('); if (p == std::string::npos) { throw ProtocolError(str); } unsigned int v[6]; sscanf(str.c_str() + p + 1,"%u,%u,%u,%u,%u,%u", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]); // doesn't make sense if (_protocol_options.GetInt("RestrictDataPeer", 1) != 0) SendRestIfNeeded(rest); // str = cmd; // str+= FTP_ENDLINE; // _transport->Send(str.c_str(), str.size()); sockaddr_in sin = {0}; sin.sin_family = AF_INET; sin.sin_port = (uint16_t)((v[5] << 8) | v[4]); //sin.sin_addr.s_addr = (uint32_t)((v[3] << 24) | (v[2] << 16) | (v[1] << 8) | (v[0])); struct sockaddr_in peer_sin{}; _transport->GetPeerAddress(peer_sin); sin.sin_addr = peer_sin.sin_addr; data_transport = std::make_shared(sin, _protocol_options); str = cmd; SendRecvResponse(str, 100, 199); // unsigned int reply_code = RecvResponseFromTransport(str); // FTPThrowIfBadResponse(str, reply_code, 100, 199); } void FTPConnection::DataCommand_PORT(std::shared_ptr &data_transport, const std::string &cmd, unsigned long long rest) { sockaddr_in sin = {}; _transport->GetLocalAddress(sin); sin.sin_port = 0; FDScope srv_sock(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)); if (srv_sock == -1) { throw std::runtime_error(StrPrintf("socket() errno=%d", errno)); } if (bind(srv_sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { throw std::runtime_error(StrPrintf("bind() errno=%d", errno)); } if (listen(srv_sock, 1) == -1) { throw std::runtime_error(StrPrintf("bind() errno=%d", errno)); } sockaddr sa = {}; socklen_t l = sizeof(sa); if (getsockname(srv_sock, &sa, &l) == -1) { throw std::runtime_error(StrPrintf("getsockname() errno=%d", errno)); } std::string str = StrPrintf("PORT %u,%u,%u,%u,%u,%u", (unsigned int)(unsigned char)sa.sa_data[2], (unsigned int)(unsigned char)sa.sa_data[3], (unsigned int)(unsigned char)sa.sa_data[4], (unsigned int)(unsigned char)sa.sa_data[5], (unsigned int)(unsigned char)sa.sa_data[0], (unsigned int)(unsigned char)sa.sa_data[1]); SendRecvResponse(str, 200, 299); SendRestIfNeeded(rest); str = cmd; SendRecvResponse(str, 100, 199); for (;;) { l = sizeof(sin); int sc = accept(srv_sock, (struct sockaddr *)&sin, &l); if (sc != -1) { data_transport = std::make_shared(sc, _protocol_options); if (_protocol_options.GetInt("RestrictDataPeer", 1) != 0) { struct sockaddr_in cmd_sin {}, data_sin {}; _transport->GetPeerAddress(cmd_sin); data_transport->GetPeerAddress(data_sin); if (cmd_sin.sin_addr.s_addr != data_sin.sin_addr.s_addr) { fprintf(stderr, "Address mismatch: 0x%x != 0x%x\n", cmd_sin.sin_addr.s_addr, data_sin.sin_addr.s_addr); throw ProtocolError("Unmatched data and command connection address"); } } break; } } } void FTPConnection::EnsureDataConnectionProtection() { #ifdef HAVE_OPENSSL if (_openssl_ctx && !_data_encryption_enabled) { _data_encryption_enabled = EnableDataConnectionProtection(); } #endif } std::shared_ptr FTPConnection::DataCommand(const std::string &cmd, unsigned long long rest) { EnsureDataConnectionProtection(); std::shared_ptr data_transport; if (_protocol_options.GetInt("Passive", 1) == 1) { DataCommand_PASV(data_transport, cmd, rest); } else { DataCommand_PORT(data_transport, cmd, rest); } #ifdef HAVE_OPENSSL if (_data_encryption_enabled) { auto tls_transport = std::make_shared(_openssl_ctx, data_transport->DetachSocket()); if (_protocol_options.GetInt("RestrictDataPeer", 1) != 0) { const std::string &fingerprint = tls_transport->GetPeerFingerprint(); if (fingerprint != _protocol_options.GetString("ServerIdentity")) { throw ProtocolError("Unmatched data and command connection certificates"); } } data_transport = tls_transport; } else if (_openssl_ctx && _protocol_options.GetInt("RestrictDataPeer", 1) != 0) { throw ProtocolError("Unencrypted data connection"); } #endif return data_transport; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPConnection.h000066400000000000000000000074631477431623700236110ustar00rootroot00000000000000#pragma once #include #include #include #ifdef HAVE_OPENSSL # include #endif #include #include #include "../../Erroring.h" struct BaseTransport : public std::enable_shared_from_this { virtual ~BaseTransport(); void Send(const void *data, size_t len); ssize_t Recv(void *data, size_t len); virtual int DetachSocket(); void GetPeerAddress(struct sockaddr_in &sin); void GetLocalAddress(struct sockaddr_in &sin); protected: FDScope _sock; virtual ssize_t SendImpl(const void *data, size_t len) = 0; virtual ssize_t RecvImpl(void *data, size_t len) = 0; }; struct SocketTransport : public BaseTransport { SocketTransport(int sock, const StringConfig &protocol_options); SocketTransport(struct sockaddr_in &sin, const StringConfig &protocol_options); SocketTransport(const std::string &host, unsigned int port, const StringConfig &protocol_options); protected: void Connect(struct sockaddr_in &sin, const StringConfig &protocol_options); void ApplySocketOptions(const StringConfig &protocol_options); virtual ssize_t SendImpl(const void *data, size_t len); virtual ssize_t RecvImpl(void *data, size_t len); }; #ifdef HAVE_OPENSSL struct OpenSSLContext { OpenSSLContext(const StringConfig &protocol_options); virtual ~OpenSSLContext(); SSL *NewSSL(int sock); private: SSL_CTX *_ctx; SSL_SESSION *_session = nullptr; static int sNewClientSessionCB(SSL *ssl, SSL_SESSION *session); }; struct TLSTransport : public BaseTransport { std::shared_ptr _ctx; SSL *_ssl = nullptr; void Shutdown(); public: TLSTransport(std::shared_ptr &ctx, int sock); virtual ~TLSTransport(); virtual ssize_t SendImpl(const void *data, size_t len); virtual ssize_t RecvImpl(void *data, size_t len); virtual int DetachSocket(); std::string GetPeerFingerprint(); }; #endif template static void FTPThrowIfBadResponse(const std::string &str, unsigned int reply_code, unsigned int reply_ok_min, unsigned int reply_ok_max) { if (reply_code < reply_ok_min || reply_code > reply_ok_max) { size_t n = str.size(); while (n > 0 && (str[n - 1] == '\r' || str[n - 1] == '\n')) { --n; } throw E(str.substr(0, n)); } } struct FTPConnection : public std::enable_shared_from_this { std::shared_ptr _transport; StringConfig _protocol_options; std::string _str; struct { struct Response { unsigned int code; std::string str; }; std::deque requests; std::deque responses; bool pipelining = false; } _batch; #ifdef HAVE_OPENSSL std::shared_ptr _openssl_ctx; bool _data_encryption_enabled = false; #endif void BatchPerform(); void DataCommand_PASV(std::shared_ptr &data_transport, const std::string &cmd, unsigned long long rest = 0); void DataCommand_PORT(std::shared_ptr &data_transport, const std::string &cmd, unsigned long long rest = 0); bool EnableDataConnectionProtection(); public: FTPConnection(bool implicit_encryption, const std::string &host, unsigned int port, const std::string &options); virtual ~FTPConnection(); void EnsureDataConnectionProtection(); const StringConfig &ProtocolOptions() const { return _protocol_options; } void SendRequest(const std::string &str); unsigned int RecvResponse(std::string &str); unsigned int SendRecvResponse(std::string &str); unsigned int RecvResponseFromTransport(std::string &str); void RecvResponse(std::string &str, unsigned int reply_ok_min, unsigned int reply_ok_max); void SendRecvResponse(std::string &str, unsigned int reply_ok_min, unsigned int reply_ok_max); void SendRestIfNeeded(unsigned long long rest); std::shared_ptr DataCommand(const std::string &cmd, unsigned long long rest = 0); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPParseLIST.cpp000066400000000000000000000334761477431623700236160ustar00rootroot00000000000000/* ftpparse.c, ftpparse.h: library for parsing FTP LIST responses 20001223 D. J. Bernstein, djb@cr.yp.to http://cr.yp.to/ftpparse.html Commercial use is fine, if you let me know what programs you're using this in. Currently covered formats: EPLF. UNIX ls, with or without gid. Microsoft FTP Service. Windows NT FTP Server. VMS. WFTPD. NetPresenz (Mac). NetWare. MSDOS. Definitely not covered: Long VMS filenames, with information split across two lines. NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories). */ #include #include #include #include #include #include "FTPParseLIST.h" static long totai(long year, long month, long mday) { long result; if (month >= 2) month-= 2; else { month+= 10; --year; } result = (mday - 1) * 10 + 5 + 306 * month; result/= 10; if (result == 365) { year-= 3; result = 1460; } else result+= 365 * (year % 4); year/= 4; result+= 1461 * (year % 25); year/= 25; if (result == 36524) { year-= 3; result = 146096; } else { result+= 36524 * (year % 4); } year/= 4; result+= 146097 * (year - 5); result+= 11017; return result * 86400; } static time_t base; /* time() value on this OS at the beginning of 1970 TAI */ static long now; /* current time */ static int flagnowready = 0; static long currentyear; /* approximation to current year */ static void initnow(void) { long day; long year; struct tm *t; if (flagnowready) { return; } base = 0; t = gmtime(&base); base = -( totai(t->tm_year + 1900, t->tm_mon, t->tm_mday) + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec); /* assumes the right time_t, counting seconds. */ /* base may be slightly off if time_t counts non-leap seconds. */ now = time((time_t *)0) - base; day = now / 86400; if ((now % 86400) < 0) --day; day-= 11017; year = 5 + day / 146097; day = day % 146097; if (day < 0) { day+= 146097; --year; } year*= 4; if (day == 146096) { year+= 3; day = 36524; } else { year+= day / 36524; day%= 36524; } year*= 25; year+= day / 1461; day%= 1461; year*= 4; if (day == 1460) { year+= 3; day = 365; } else { year+= day / 365; day%= 365; } day*= 10; if ((day + 5) / 306 >= 10) ++year; currentyear = year; flagnowready = 1; } /* UNIX ls does not show the year for dates in the last six months. */ /* So we have to guess the year. */ /* Apparently NetWare uses ``twelve months'' instead of ``six months''; ugh. */ /* Some versions of ls also fail to show the year for future dates. */ static long guesstai(long month, long mday) { long year; long t; initnow(); for (year = currentyear - 1; year < currentyear + 100; ++year) { t = totai(year, month, mday); if (now - t < 350 * 86400) return t; } return 0; } static int check(const char *buf, const char *monthname) { if ((buf[0] != monthname[0]) && (buf[0] != monthname[0] - 32)) return 0; if ((buf[1] != monthname[1]) && (buf[1] != monthname[1] - 32)) return 0; if ((buf[2] != monthname[2]) && (buf[2] != monthname[2] - 32)) return 0; return 1; } static const char *months[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}; static int getmonth(const char *buf, int len) { int i; if (len == 3) for (i = 0; i < 12; ++i) if (check(buf, months[i])) return i; return -1; } static long getlong(const char *buf, int len) { long u = 0; while (len-- > 0) u = u * 10 + (*buf++ - '0'); return u; } int ftpparse(struct ftpparse *fp, const char *buf, int len) { int i; int j; int k; int state = 0; long size = 0; long year = 0; long month = 0; long mday = 0; long hour = 0; long minute = 0; fp->name = 0; fp->namelen = 0; fp->flagtrycwd = 0; fp->flagtryretr = 0; fp->sizetype = FTPPARSE_SIZE_UNKNOWN; fp->size = 0; fp->mtimetype = FTPPARSE_MTIME_UNKNOWN; fp->mtime = 0; fp->idtype = FTPPARSE_ID_UNKNOWN; fp->id = 0; fp->idlen = 0; if (len < 2) /* an empty name in EPLF, with no info, could be 2 chars */ return 0; switch (*buf) { /* see http://pobox.com/~djb/proto/eplf.txt */ /* "+i8388621.29609,m824255902,/,\tdev" */ /* "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF" */ case '+': i = 1; for (j = 1; j < len; ++j) { if (buf[j] == 9) { fp->name = buf + j + 1; fp->namelen = len - j - 1; fp->mode|= S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; return 1; } if (buf[j] == ',') { switch (buf[i]) { case '/': fp->flagtrycwd = 1; fp->mode|= S_IFDIR; break; case 'r': fp->flagtryretr = 1; fp->mode|= S_IFREG; break; case 's': fp->sizetype = FTPPARSE_SIZE_BINARY; fp->size = getlong(buf + i + 1, j - i - 1); break; case 'm': fp->mtimetype = FTPPARSE_MTIME_LOCAL; initnow(); fp->mtime = base + getlong(buf + i + 1, j - i - 1); break; case 'i': fp->idtype = FTPPARSE_ID_FULL; fp->id = buf + i + 1; fp->idlen = j - i - 1; } i = j + 1; } } return 0; /* UNIX-style listing, without inum and without blocks */ /* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */ /* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */ /* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */ /* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */ /* Also produced by Microsoft's FTP servers for Windows: */ /* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */ /* "d--------- 1 owner group 0 May 9 19:45 Softlib" */ /* Also WFTPD for MSDOS: */ /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */ /* Also NetWare: */ /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */ /* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */ /* Also NetPresenz for the Mac: */ /* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */ /* "drwxrwxr-x folder 2 May 10 1996 network" */ case 'b': case 'c': case 'd': case 'l': case 'p': case 's': case '-': if (*buf == 'd') { fp->flagtrycwd = 1; fp->mode|= S_IFDIR; } if (*buf == '-') { fp->flagtryretr = 1; fp->mode|= S_IFREG; } if (*buf == 'l') { fp->flagtrycwd = fp->flagtryretr = 1; fp->mode|= S_IFLNK; } state = 1; i = 0; for (j = 1; j < len; ++j) if ((buf[j] == ' ') && (buf[j - 1] != ' ')) { switch (state) { case 1: /* handling perm */ if (len > 1 && buf[1] != '-') fp->mode|= S_IRUSR; if (len > 2 && buf[2] != '-') fp->mode|= S_IWUSR; if (len > 3 && buf[3] != '-') fp->mode|= S_IXUSR; if (len > 4 && buf[4] != '-') fp->mode|= S_IRGRP; if (len > 5 && buf[5] != '-') fp->mode|= S_IWGRP; if (len > 6 && buf[6] != '-') fp->mode|= S_IXGRP; if (len > 7 && buf[7] != '-') fp->mode|= S_IROTH; if (len > 8 && buf[8] != '-') fp->mode|= S_IWOTH; if (len > 9 && buf[9] != '-') fp->mode|= S_IXOTH; state = 2; break; case 2: /* skipping nlink */ state = 3; if ((j - i == 6) && (buf[i] == 'f')) /* for NetPresenz */ state = 4; break; case 3: /* getting owner */ for (k = i; k < len && k - i != sizeof(fp->owner) - 1 && buf[k] != ' '; ++k) { fp->owner[k - i] = buf[k]; } state = 4; break; case 4: /* getting tentative size or group */ for (k = i; k < len && k - i != sizeof(fp->group) - 1 && buf[k] != ' '; ++k) { fp->group[k - i] = buf[k]; } size = getlong(buf + i, j - i); state = 5; break; case 5: /* searching for month, otherwise getting tentative size */ month = getmonth(buf + i, j - i); if (month >= 0) { fp->group[0] = 0; state = 6; } else size = getlong(buf + i, j - i); break; case 6: /* have size and month */ mday = getlong(buf + i, j - i); state = 7; break; case 7: /* have size, month, mday */ if ((j - i == 4) && (buf[i + 1] == ':')) { hour = getlong(buf + i, 1); minute = getlong(buf + i + 2, 2); fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE; initnow(); fp->mtime = base + guesstai(month, mday) + hour * 3600 + minute * 60; } else if ((j - i == 5) && (buf[i + 2] == ':')) { hour = getlong(buf + i, 2); minute = getlong(buf + i + 3, 2); fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE; initnow(); fp->mtime = base + guesstai(month, mday) + hour * 3600 + minute * 60; } else if (j - i >= 4) { year = getlong(buf + i, j - i); fp->mtimetype = FTPPARSE_MTIME_REMOTEDAY; initnow(); fp->mtime = base + totai(year, month, mday); } else return 0; fp->name = buf + j + 1; fp->namelen = len - j - 1; state = 8; break; case 8: /* twiddling thumbs */ break; } i = j + 1; while ((i < len) && (buf[i] == ' ')) ++i; } if (state != 8) return 0; fp->size = size; fp->sizetype = FTPPARSE_SIZE_BINARY; if (*buf == 'l') for (i = 0; i + 3 < fp->namelen; ++i) if (fp->name[i] == ' ') if (fp->name[i + 1] == '-') if (fp->name[i + 2] == '>') if (fp->name[i + 3] == ' ') { fp->namelen = i; break; } /* eliminate extra NetWare spaces */ if ((buf[1] == ' ') || (buf[1] == '[')) if (fp->namelen > 3) if (fp->name[0] == ' ') if (fp->name[1] == ' ') if (fp->name[2] == ' ') { fp->name+= 3; fp->namelen-= 3; } return 1; } /* MultiNet (some spaces removed from examples) */ /* "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)" */ /* "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)" */ /* and non-MultiNet VMS: */ /* "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)" */ for (i = 0; i < len; ++i) if (buf[i] == ';') break; if (i < len) { fp->name = buf; fp->namelen = i; if (i > 4) if (buf[i - 4] == '.') if (buf[i - 3] == 'D') if (buf[i - 2] == 'I') if (buf[i - 1] == 'R') { fp->namelen-= 4; fp->flagtrycwd = 1; fp->mode|= S_IFDIR; } if (!fp->flagtrycwd) { fp->flagtryretr = 1; fp->mode|= S_IFREG; } while (buf[i] != ' ') if (++i == len) return 0; while (buf[i] == ' ') if (++i == len) return 0; while (buf[i] != ' ') if (++i == len) return 0; while (buf[i] == ' ') if (++i == len) return 0; j = i; while (buf[j] != '-') if (++j == len) return 0; mday = getlong(buf + i, j - i); while (buf[j] == '-') if (++j == len) return 0; i = j; while (buf[j] != '-') if (++j == len) return 0; month = getmonth(buf + i, j - i); if (month < 0) return 0; while (buf[j] == '-') if (++j == len) return 0; i = j; while (buf[j] != ' ') if (++j == len) return 0; year = getlong(buf + i, j - i); while (buf[j] == ' ') if (++j == len) return 0; i = j; while (buf[j] != ':') if (++j == len) return 0; hour = getlong(buf + i, j - i); while (buf[j] == ':') if (++j == len) return 0; i = j; while ((buf[j] != ':') && (buf[j] != ' ')) if (++j == len) return 0; minute = getlong(buf + i, j - i); fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE; initnow(); fp->mtime = base + totai(year, month, mday) + hour * 3600 + minute * 60; fp->mode|= S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; return 1; } /* MSDOS format */ /* 04-27-00 09:09PM licensed */ /* 07-18-00 10:16AM pub */ /* 04-14-00 03:47PM 589 readme.htm */ if ((*buf >= '0') && (*buf <= '9')) { i = 0; j = 0; while (buf[j] != '-') if (++j == len) return 0; month = getlong(buf + i, j - i) - 1; while (buf[j] == '-') if (++j == len) return 0; i = j; while (buf[j] != '-') if (++j == len) return 0; mday = getlong(buf + i, j - i); while (buf[j] == '-') if (++j == len) return 0; i = j; while (buf[j] != ' ') if (++j == len) return 0; year = getlong(buf + i, j - i); if (year < 50) year+= 2000; if (year < 1000) year+= 1900; while (buf[j] == ' ') if (++j == len) return 0; i = j; while (buf[j] != ':') if (++j == len) return 0; hour = getlong(buf + i, j - i); while (buf[j] == ':') if (++j == len) return 0; i = j; while ((buf[j] != 'A') && (buf[j] != 'P')) if (++j == len) return 0; minute = getlong(buf + i, j - i); if (hour == 12) hour = 0; if (buf[j] == 'A') if (++j == len) return 0; if (buf[j] == 'P') { hour+= 12; if (++j == len) return 0; } if (buf[j] == 'M') if (++j == len) return 0; while (buf[j] == ' ') if (++j == len) return 0; if (buf[j] == '<') { fp->flagtrycwd = 1; fp->mode|= S_IFDIR; while (buf[j] != ' ') if (++j == len) return 0; } else { i = j; while (buf[j] != ' ') if (++j == len) return 0; fp->size = getlong(buf + i, j - i); fp->sizetype = FTPPARSE_SIZE_BINARY; fp->flagtryretr = 1; fp->mode|= S_IFREG; } while (buf[j] == ' ') if (++j == len) return 0; fp->name = buf + j; fp->namelen = len - j; fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE; initnow(); fp->mtime = base + totai(year, month, mday) + hour * 3600 + minute * 60; fp->mode|= S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; return 1; } /* Some useless lines, safely ignored: */ /* "Total of 11 Files, 10966 Blocks." (VMS) */ /* "total 14786" (UNIX) */ /* "DISK$ANONFTP:[ANONYMOUS]" (VMS) */ /* "Directory DISK$PCSA:[ANONYM]" (VMS) */ return 0; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPParseLIST.h000066400000000000000000000033621477431623700232520ustar00rootroot00000000000000#ifndef FTPPARSE_H #define FTPPARSE_H /* ftpparse(&fp,buf,len) tries to parse one line of LIST output. The line is an array of len characters stored in buf. It should not include the terminating CR LF; so buf[len] is typically CR. If ftpparse() can't find a filename, it returns 0. If ftpparse() can find a filename, it fills in fp and returns 1. fp is a struct ftpparse, defined below. The name is an array of fp.namelen characters stored in fp.name; fp.name points somewhere within buf. */ struct ftpparse { const char *name; /* not necessarily 0-terminated */ int namelen; int flagtrycwd; /* 0 if cwd is definitely pointless, 1 otherwise */ int flagtryretr; /* 0 if retr is definitely pointless, 1 otherwise */ int sizetype; long size; /* number of octets */ int mtimetype; time_t mtime; /* modification time */ int idtype; const char *id; /* not necessarily 0-terminated */ int idlen; mode_t mode; char owner[0x100]; char group[0x100]; } ; #define FTPPARSE_SIZE_UNKNOWN 0 #define FTPPARSE_SIZE_BINARY 1 /* size is the number of octets in TYPE I */ #define FTPPARSE_SIZE_ASCII 2 /* size is the number of octets in TYPE A */ #define FTPPARSE_MTIME_UNKNOWN 0 #define FTPPARSE_MTIME_LOCAL 1 /* time is correct */ #define FTPPARSE_MTIME_REMOTEMINUTE 2 /* time zone and secs are unknown */ #define FTPPARSE_MTIME_REMOTEDAY 3 /* time zone and time of day are unknown */ /* When a time zone is unknown, it is assumed to be GMT. You may want to use localtime() for LOCAL times, along with an indication that the time is correct in the local time zone, and gmtime() for REMOTE* times. */ #define FTPPARSE_ID_UNKNOWN 0 #define FTPPARSE_ID_FULL 1 /* unique identifier for files on this FTP server */ int ftpparse(struct ftpparse *, const char *, int); #endif far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPParseMLST.cpp000066400000000000000000000164421477431623700236140ustar00rootroot00000000000000#include #include #include #include "FTPParseMLST.h" #include "../../Erroring.h" #include static const char match__unix_mode[] = "UNIX.mode"; static const char match__unix_uid[] = "UNIX.uid"; static const char match__unix_gid[] = "UNIX.gid"; static const char match__type[] = "type"; static const char match__type_os_unix[] = "type=OS.unix"; static const char match__perm[] = "perm"; static const char match__size[] = "size"; static const char match__sizd[] = "sizd"; static const char match__modify[] = "modify"; static const char match__create[] = "create"; static const char match__file[] = "file"; static const char match__dir[] = "dir"; static const char match__cdir[] = "cdir"; static const char match__pdir[] = "pdir"; #define MATCH_SUBSTR(str, len, match) (sizeof(match) == (len) + 1 && CaseIgnoreEngStrMatch(str, match, sizeof(match) - 1)) static time_t GetDefaultTime() { static time_t s_out = time(NULL); return s_out; } static void ParseTime(timespec &ts, const char *str, size_t len) { /* time-val = 14DIGIT [ "." 1*DIGIT ] The leading, mandatory, fourteen digits are to be interpreted as, in order from the leftmost, four digits giving the year, with a range of 1000--9999, two digits giving the month of the year, with a range of 01--12, two digits giving the day of the month, with a range of 01--31, two digits giving the hour of the day, with a range of 00--23, two digits giving minutes past the hour, with a range of 00--59, and finally, two digits giving seconds past the minute, with a range of 00--60 (with 60 being used only at a leap second). Years The optional digits, which are preceded by a period, give decimal fractions of a second. These may be given to whatever precision is appropriate to the circumstance, however implementations MUST NOT add precision to time-vals where that precision does not exist in the underlying value being transmitted. Symbolically, a time-val may be viewed as: YYYYMMDDHHMMSS.sss */ struct tm t{}; if (len >= 4) { t.tm_year = DecToULong(str, 4) - 1900; if (len >= 6) { t.tm_mon = DecToULong(str + 4, 2) - 1; if (len >= 8) { t.tm_mday = DecToULong(str + 6, 2); if (len >= 10) { t.tm_hour = DecToULong(str + 8, 2); if (len >= 12) { t.tm_min = DecToULong(str + 10, 2); if (len >= 14) { t.tm_sec = DecToULong(str + 12, 2); } } } } } } ts.tv_sec = mktime(&t); if (len > 15 && str[14] == '.') { size_t deci_len = std::min(len - 15, (size_t)9); ts.tv_nsec = DecToULong(str + 15, deci_len); for (size_t i = deci_len; i < 9; ++i) { ts.tv_nsec*= 10; } } else { ts.tv_nsec = 0; } } bool ParseMLsxLine(const char *line, const char *end, FileInformation &file_info, uid_t *uid, gid_t *gid, std::string *name, std::string *lnkto) { file_info = FileInformation(); bool has_mode = false, has_type = false, has_any_known = false, has_modify = false, has_create = false; const char *perm = nullptr, *perm_end = nullptr; for (; line != end && *line == ' '; ++line) {;} const char *fact, *eq; for (fact = eq = line; line != end; ++line) if (*line == '=') { eq = line; } else if (*line ==';') { if (eq > fact) { const size_t name_len = eq - fact; const size_t value_len = line - (eq + 1); if (MATCH_SUBSTR(fact, name_len, match__unix_mode)) { file_info.mode|= strtol(eq + 1, nullptr, (*(eq + 1) == '0') ? 8 : 10); has_any_known = has_mode = true; } else if (MATCH_SUBSTR(fact, name_len, match__unix_uid)) { if (uid) *uid = strtol(eq + 1, nullptr, 10); has_any_known = true; } else if (MATCH_SUBSTR(fact, name_len, match__unix_gid)) { if (gid) *gid = strtol(eq + 1, nullptr, 10); has_any_known = true; } else if (MATCH_SUBSTR(fact, name_len, match__create)) { ParseTime(file_info.status_change_time, eq + 1, value_len); has_any_known = has_create = true; } else if (MATCH_SUBSTR(fact, name_len, match__modify)) { ParseTime(file_info.modification_time, eq + 1, value_len); has_any_known = has_modify = true; } else if (MATCH_SUBSTR(fact, name_len, match__type)) { if (MATCH_SUBSTR(eq + 1, value_len, match__file)) { file_info.mode|= S_IFREG; has_type = true; has_any_known = true; } else if (MATCH_SUBSTR(eq + 1, value_len, match__dir) || MATCH_SUBSTR(eq + 1, value_len, match__cdir) || MATCH_SUBSTR(eq + 1, value_len, match__pdir)) { file_info.mode|= S_IFDIR; has_type = true; has_any_known = true; } else if (g_netrocks_verbosity > 0) { fprintf(stderr, "ParseMLsxLine: unknown type='%s'\n", std::string(eq + 1, value_len).c_str()); } } else if (MATCH_SUBSTR(fact, name_len, match__perm)) { perm = eq + 1; perm_end = line; has_any_known = true; } else if (MATCH_SUBSTR(fact, name_len, match__size) || MATCH_SUBSTR(fact, name_len, match__sizd)) { file_info.size = strtol(eq + 1, nullptr, 10); has_any_known = true; } else if (MATCH_SUBSTR(fact, name_len, match__type_os_unix)) { if (value_len >= 6 && CaseIgnoreEngStrMatch(eq + 1, "slink:", 6)) { file_info.mode|= S_IFLNK; has_type = true; has_any_known = true; if (lnkto) { lnkto->assign(eq + 1 + 6, value_len - 6); } } } } fact = line + 1; } if (name) { if (fact != end && *fact == ' ') { ++fact; } if (fact != end) { name->assign(fact, end - fact); } else { name->clear(); } } if (!has_type) { if (perm) { if (CaseIgnoreEngStrChr('c', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('l', perm, perm_end - perm) != nullptr) { file_info.mode|= S_IFDIR; } else { file_info.mode|= S_IFREG; } } else { file_info.mode|= S_IFREG; // last resort } } // fprintf(stderr, "type:%s\n", ((file_info.mode & S_IFMT) == S_IFDIR) ? "DIR" : "FILE"); if (!has_mode) { if (perm) { if (CaseIgnoreEngStrChr('r', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('l', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('e', perm, perm_end - perm) != nullptr) { file_info.mode|= 0444; if ((file_info.mode & S_IFMT) == S_IFDIR) { file_info.mode|= 0111; } } if (CaseIgnoreEngStrChr('w', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('a', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('c', perm, perm_end - perm) != nullptr) { file_info.mode|= 0220; } if (CaseIgnoreEngStrChr('x', perm, perm_end - perm) != nullptr || CaseIgnoreEngStrChr('e', perm, perm_end - perm) != nullptr) { file_info.mode|= 0111; } } else if ((file_info.mode & S_IFMT) == S_IFDIR) { file_info.mode|= DEFAULT_ACCESS_MODE_DIRECTORY; // last resort } else { file_info.mode|= DEFAULT_ACCESS_MODE_FILE; // last resort } } if (has_modify) { file_info.access_time.tv_sec = file_info.modification_time.tv_sec; if (!has_create) { file_info.status_change_time.tv_sec = file_info.modification_time.tv_sec; } } else if (has_create) { file_info.access_time.tv_sec = file_info.status_change_time.tv_sec; if (!has_modify) { file_info.modification_time.tv_sec = file_info.status_change_time.tv_sec; } } else { file_info.status_change_time.tv_sec = file_info.modification_time.tv_sec = file_info.access_time.tv_sec = GetDefaultTime(); } return has_any_known; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/FTPParseMLST.h000066400000000000000000000003571477431623700232570ustar00rootroot00000000000000#pragma once #include "../../FileInformation.h" bool ParseMLsxLine(const char *line, const char *end, FileInformation &file_info, uid_t *uid = nullptr, gid_t *gid = nullptr, std::string *name = nullptr, std::string *lnkto = nullptr); far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/ProtocolFTP.cpp000066400000000000000000000471161477431623700236450ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "ProtocolFTP.h" #include "FTPParseMLST.h" #include "FTPParseLIST.h" std::shared_ptr CreateProtocol(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) { return std::make_shared(protocol, host, port, username, password, options); } ProtocolFTP::ProtocolFTP(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options) : _conn(std::make_shared( (strcasecmp(protocol.c_str(), "ftps") == 0), host, port, options)), _dir_enum_cache(10) { _conn->EnsureDataConnectionProtection(); _str.assign("USER ").append(username); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code >= 300 && reply_code <= 399) { _str.assign("PASS ").append(password); reply_code = _conn->SendRecvResponse(_str); } FTPThrowIfBadResponse(_str, reply_code, 200, 299); _cmd.list_ = _conn->ProtocolOptions().GetString("ListCommand", ""); if (_conn->ProtocolOptions().GetInt("MLSDMLST", 1) != 0) { _str.assign("FEAT"); reply_code = _conn->SendRecvResponse(_str); if (reply_code >= 200 && reply_code < 299) { if (_str.find("MLST") != std::string::npos) _cmd.mlst = "MLST"; if (_str.find("MLSD") != std::string::npos) _cmd.mlsd = "MLSD"; } } _conn->SendRequest("TYPE I"); _conn->SendRequest("PWD"); _conn->RecvResponse(_str); if (!RecvPwdResponse()) { _conn->SendRequest("XPWD"); if (RecvPwdResponse()) { _cmd.pwd = "XPWD"; _cmd.cwd = "XCWD"; _cmd.mkd = "XMKD"; _cmd.rmd = "XRMD"; } else { fprintf(stderr, "ProtocolFTP: RecvPwdResponse failed - ITS VERY BAD\n"); } } fprintf(stderr, "ProtocolFTP: %s %s %s\n", _cmd.pwd, _cmd.mlst ? _cmd.mlst : "", _cmd.mlsd ? _cmd.mlsd : ""); _cwd.home = _cwd.path; if (_cwd.home[_cwd.home.size() - 1] != '/') { _cwd.home+= '/'; } } ProtocolFTP::~ProtocolFTP() { } static void PathExplode(std::vector &parts, const std::string &path) { parts.clear(); StrExplode(parts, path, "/"); for (auto it = parts.begin(); it != parts.end();) { if (it->empty() || *it == ".") { it = parts.erase(it); } else { ++it; } } } bool ProtocolFTP::RecvPwdResponse() { _str.clear(); unsigned int reply = _conn->RecvResponse(_str); if (reply != 257) { fprintf(stderr, "ProtocolFTP::RecvPwdResponse: reply=%u '%s'\n", reply, _str.c_str()); return false; } size_t b = _str.find('\"'); size_t e = _str.rfind('\"'); if (b == std::string::npos || b == e) { fprintf(stderr, "ProtocolFTP::RecvPwdResponse: bad quotes '%s'\n", _str.c_str()); return false; } if (g_netrocks_verbosity > 1) { fprintf(stderr, "ProtocolFTP::RecvPwdResponse: '%s'\n", _str.c_str()); } _str.resize(e); _str.erase(0, b + 1); if ('/' != *_str.c_str()) { _str.insert(0, "/"); } _cwd.path.swap(_str); PathExplode(_cwd.parts, _cwd.path); return true; } std::string ProtocolFTP::SplitPathAndNavigate(const std::string &path_name, bool allow_empty_name_part) { if ('/' != *path_name.c_str()) { std::string full_path_name = _cwd.home; full_path_name+= path_name; return SplitPathAndNavigate(full_path_name, allow_empty_name_part); } std::string name_part; std::vector parts; PathExplode(parts, path_name); if (!parts.empty()) { name_part = parts.back(); parts.pop_back(); } else if (!allow_empty_name_part) { throw ProtocolError( StrPrintf( "ProtocolFTP::SplitPathAndNavigate('%s') - empty path_name\n", path_name.c_str())); // fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate('%s') - empty path_name\n", path_name.c_str()); // return std::string(); } if (g_netrocks_verbosity > 0) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate('%s') prev='%s'\n", path_name.c_str(), _cwd.path.c_str()); } if (parts != _cwd.parts) { // first try most compatible way - sequence of CDUP followed by sequence of CWD size_t i; for (i = 0; (i != parts.size() && i != _cwd.parts.size() && parts[i] == _cwd.parts[i]); ++i) { ; } if (g_netrocks_verbosity > 1) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate: i=%lu _cwd.parts.size()=%lu parts.size()=%lu\n", (unsigned long)i, (unsigned long)_cwd.parts.size(), (unsigned long)parts.size()); } for (size_t j = i; j != _cwd.parts.size(); ++j) { _conn->SendRequest(_cmd.cdup); } for (size_t j = i; j != parts.size(); ++j) { _str.assign(_cmd.cwd).append(" ").append(parts[j]); _conn->SendRequest(_str); } _conn->SendRequest(_cmd.pwd); bool ok = true; // fetch all responses except PWD for (size_t j = 0, jj = (_cwd.parts.size() - i) + (parts.size() - i); j != jj; ++j) { try { _conn->RecvResponse(_str, 200, 299); if (g_netrocks_verbosity > 1) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate: reply='%s'\n", _str.c_str()); } } catch (std::exception &e) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate('%s') - %s, part %lu of %lu\n", path_name.c_str(), e.what(), (unsigned long)j, (unsigned long)jj); ok = false; } } std::string fail_str; if (!ok) { // alternative way: CWD-to-home followed by CWD-where-we-want-to-be _conn->RecvResponse(_str); // skip PWD response for now, will send another one ok = true; // try other way: reset to home and apply full path _conn->SendRequest(_cmd.cwd); _str.assign(_cmd.cwd).append(" "); for (const auto &part : parts) { _str.append(part).append(1, '/'); } _conn->SendRequest(_str); _conn->SendRequest(_cmd.pwd); try { _conn->RecvResponse(_str, 200, 299); if (g_netrocks_verbosity > 1) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate: homey CWD reply='%s'\n", _str.c_str()); } } catch (std::exception &e) { ok = false; fail_str = e.what(); fprintf(stderr, "SplitPathAndNavigate('%s') homey CWD - %s\n", path_name.c_str(), e.what()); } try { _conn->RecvResponse(_str, 200, 299); if (g_netrocks_verbosity > 1) { fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate: full CWD reply='%s'\n", _str.c_str()); } } catch (std::exception &e) { ok = false; if (fail_str.empty()) { fail_str = e.what(); } fprintf(stderr, "ProtocolFTP::SplitPathAndNavigate('%s') full CWD - %s\n", path_name.c_str(), e.what()); } } if (!RecvPwdResponse()) { _cwd.parts = parts; _cwd.path.clear(); for (const auto &part : parts) { _cwd.path.append(1, '/').append(part); } } if (!ok) { // nothing worked, bail out with error throw ProtocolError(fail_str); } } return name_part; } std::string ProtocolFTP::PathAsRelative(const std::string &path) { std::vector parts; PathExplode(parts, path); size_t i; for (i = 0; (i < parts.size() && i < _cwd.parts.size() && parts[i] == _cwd.parts[i]); ++i) { ; } std::string out; for (size_t j = i; j < _cwd.parts.size(); ++j) { if (out.empty()) { out+= ".."; } else { out+= "/.."; } } for (size_t j = i; j < parts.size(); ++j) { if (!out.empty()) { out+= '/'; } out+= parts[j]; } return out; } void ProtocolFTP::MLst(const std::string &path, FileInformation &file_info, uid_t *uid, gid_t *gid, std::string *lnkto) { _str.assign(_cmd.mlst).append(" ").append(path); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code >= 500 && reply_code <= 504) { throw ProtocolError(_str); } std::vector lines; StrExplode(lines, _str, "\n"); if (reply_code != 250) { throw ProtocolError(_str); } const std::string &line = lines[ (lines.size() > 1) ? 1 : 0 ]; if (!ParseMLsxLine(line.c_str(), line.c_str() + line.size(), file_info, uid, gid, nullptr, lnkto)) { throw ProtocolError("MLst response uninformative"); } } void ProtocolFTP::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { const std::string &name_part = SplitPathAndNavigate(path, true); if (name_part.empty()) { file_info = FileInformation(); file_info.mode = S_IFDIR | DEFAULT_ACCESS_MODE_DIRECTORY; return; } if (_cmd.mlst != nullptr) { MLst(name_part, file_info); } else { std::shared_ptr enumer = NavigatedDirectoryEnum(); std::string enum_name, enum_owner, enum_group; do { if (!enumer->Enum(enum_name, enum_owner, enum_group, file_info)) { throw ProtocolError("File not found"); } } while (name_part != enum_name); } if (follow_symlink && S_ISLNK(file_info.mode)) { _str.assign(_cmd.size).append(" ").append(name_part); unsigned int reply_code = _conn->SendRecvResponse(_str); file_info.mode&= ~S_IFMT; file_info.mode|= S_IFDIR; if (reply_code == 213) { size_t p = _str.find(' '); if (p != std::string::npos) { while (p < _str.size() && _str[p] == ' ') { ++p; } file_info.size = atol(_str.c_str() + p); file_info.mode&= ~S_IFMT; file_info.mode|= S_IFREG; } } } } mode_t ProtocolFTP::GetMode(const std::string &path, bool follow_symlink) { FileInformation file_info; GetInformation(file_info, path, follow_symlink); return file_info.mode; } unsigned long long ProtocolFTP::GetSize(const std::string &path, bool follow_symlink) { FileInformation file_info; GetInformation(file_info, path, follow_symlink); return file_info.size; } void ProtocolFTP::SimpleDispositionCommand(const char *cmd, const std::string &path) { const std::string &name_part = SplitPathAndNavigate(path); _str.assign(cmd).append(" ").append(name_part); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code < 200 || reply_code > 299) { throw ProtocolError(_str); } if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } } void ProtocolFTP::FileDelete(const std::string &path) { SimpleDispositionCommand(_cmd.dele, path); } void ProtocolFTP::DirectoryDelete(const std::string &path) { SimpleDispositionCommand(_cmd.rmd, path); } void ProtocolFTP::DirectoryCreate(const std::string &path, mode_t mode) { SimpleDispositionCommand(_cmd.mkd, path); SetMode(path, mode); } void ProtocolFTP::Rename(const std::string &path_old, const std::string &path_new) { const std::string &name_old = SplitPathAndNavigate(path_old); const std::string &path_new_relative = PathAsRelative(path_new); _str.assign(_cmd.rnfr).append(" ").append(name_old); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code != 350) { FTPThrowIfBadResponse(_str, reply_code, 200, 299); } _str.assign(_cmd.rnto).append(" ").append(path_new_relative); reply_code = _conn->SendRecvResponse(_str); FTPThrowIfBadResponse(_str, reply_code, 200, 299); if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } SplitPathAndNavigate(path_new); if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } } void ProtocolFTP::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { if (_cmd.mfmt == nullptr) { return; } const std::string &name_part = SplitPathAndNavigate(path); struct tm t {}; if (gmtime_r(&modification_time.tv_sec, &t) == NULL) { return; } //MFMT YYYYMMDDHHMMSS path, where: //YYYY - the 4-digit year //MM - the 2-digit month //DD - the 2-digit day of the month //HH - the hour in 24-hour format //MM - the minute //SS - the seconds _str = StrPrintf("%s %04u%02u%02u%02u%02u%02u %s", _cmd.mfmt, (unsigned int)t.tm_year + 1900, (unsigned int)t.tm_mon, (unsigned int)t.tm_mday, (unsigned int)t.tm_hour, (unsigned int)t.tm_min, (unsigned int)t.tm_sec, name_part.c_str()); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code < 200 || reply_code > 299) { if (reply_code >= 500 && reply_code <= 504) { fprintf(stderr, "ProtocolFTP::SetTimes('%s'): '%s' - assume unsupported\n", path.c_str(), _str.c_str()); _cmd.mfmt = nullptr; } else { throw ProtocolError(_str); } } else if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } } void ProtocolFTP::SetMode(const std::string &path, mode_t mode) { if (_cmd.chmod == nullptr) { return; } const std::string &name_part = SplitPathAndNavigate(path); _str = StrPrintf("%s %03o %s", _cmd.chmod, (unsigned int)mode, name_part.c_str()); unsigned int reply_code = _conn->SendRecvResponse(_str); if (reply_code < 200 || reply_code > 299) { if (reply_code >= 500 && reply_code <= 504) { fprintf(stderr, "ProtocolFTP::SetMode('%s', %03o): '%s' - assume unsupported\n", path.c_str(), mode, _str.c_str()); _cmd.chmod = nullptr; } else { throw ProtocolError(_str); } } else if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } } void ProtocolFTP::SymlinkCreate(const std::string &link_path, const std::string &link_target) { throw ProtocolUnsupportedError("Symlink creation unsupported"); } void ProtocolFTP::SymlinkQuery(const std::string &link_path, std::string &link_target) { if (_cmd.mlst != nullptr) { const std::string &name_part = SplitPathAndNavigate(link_path, true); if (!name_part.empty()) { FileInformation file_info; MLst(name_part, file_info, nullptr, nullptr, &link_target); return; } } link_target.clear(); } class FTPDataCommand { protected: std::shared_ptr _conn; std::shared_ptr _data_transport; void EnsureFinalized() { _data_transport.reset(); if (_conn) { std::shared_ptr conn = _conn; _conn.reset(); std::string str; conn->RecvResponseFromTransport(str); if (g_netrocks_verbosity > 1) { fprintf(stderr, "FTPDataCommand::EnsureFinalized: response '%s'\n", str.c_str()); } } } public: FTPDataCommand(std::shared_ptr &conn, std::shared_ptr &data_transport) : _conn(conn), _data_transport(data_transport) { } virtual ~FTPDataCommand() { try { EnsureFinalized(); } catch (std::exception &e) { fprintf(stderr, "~FTPDataCommand: %s\n", e.what()); } } }; class FTPBaseDirectoryEnumer : protected FTPDataCommand, public IDirectoryEnumer { std::string _read_buffer; protected: virtual bool OnParseLine(const char *buf, size_t len, std::string &name, std::string &owner, std::string &group, FileInformation &file_info) = 0; public: using FTPDataCommand::FTPDataCommand; virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (!_data_transport) { return false; } for (;;) { for (;;) { size_t p = _read_buffer.find('\n'); if (p == std::string::npos) { break; } if (p > 0 && _read_buffer[p - 1] == '\r') { --p; } bool line_parsed = OnParseLine(_read_buffer.c_str(), p, name, owner, group, file_info); do { ++p; } while ( p < _read_buffer.size() && (_read_buffer[p] == '\r' || _read_buffer[p] == '\n')); _read_buffer.erase(0, p); if (line_parsed) { return true; } } if (_read_buffer.size() > 0x100000) { throw ProtocolError("Too long line in MLSD response"); } char buf[0x1000]; ssize_t r = _data_transport->Recv(buf, sizeof(buf)); if (r <= 0) { EnsureFinalized(); return false; } _read_buffer.append(buf, r); } } }; class FTPDirectoryEnumerMLSD : public FTPBaseDirectoryEnumer { protected: virtual bool OnParseLine(const char *buf, size_t len, std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { uid_t uid = 0; gid_t gid = 0; if (!ParseMLsxLine(buf, buf + len, file_info, &uid, &gid, &name, nullptr)) { return false; } if (!FILENAME_ENUMERABLE(name.c_str())) { return false; } owner = StrPrintf("uid:%lu", (unsigned long)uid); group = StrPrintf("gid:%lu", (unsigned long)gid); return true; } public: using FTPBaseDirectoryEnumer::FTPBaseDirectoryEnumer; }; class FTPDirectoryEnumerLIST : public FTPBaseDirectoryEnumer { time_t default_mtime = time(NULL); protected: virtual bool OnParseLine(const char *buf, size_t len, std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { struct ftpparse fp{}; if (ftpparse(&fp, buf, (int)len) != 1 || !fp.name || !fp.namelen) { return false; } name.assign(fp.name, fp.namelen); if (!FILENAME_ENUMERABLE(name.c_str())) { return false; } file_info = FileInformation(); file_info.mode = fp.mode; file_info.size = fp.size; if (fp.flagtryretr) { file_info.mode|= S_IXUSR | S_IXGRP | S_IXOTH; } owner = fp.owner; group = fp.group; file_info.access_time.tv_sec = file_info.status_change_time.tv_sec = file_info.modification_time.tv_sec = (fp.mtimetype == FTPPARSE_MTIME_UNKNOWN) ? default_mtime : fp.mtime; return true; } public: using FTPBaseDirectoryEnumer::FTPBaseDirectoryEnumer; }; std::shared_ptr ProtocolFTP::DirectoryEnum(const std::string &path) { SplitPathAndNavigate(path + "/*"); return NavigatedDirectoryEnum(); } std::shared_ptr ProtocolFTP::NavigatedDirectoryEnum() { std::shared_ptr enumer = _dir_enum_cache.GetCachedDirectoryEnumer(_cwd.path); if (enumer) { if (g_netrocks_verbosity > 0) { fprintf(stderr, "Cached enum '%s'\n", _cwd.path.c_str()); } return enumer; } // const std::string &name_part = SplitPathAndNavigate(path); if (_cmd.mlsd != nullptr) { std::shared_ptr data_transport = _conn->DataCommand(std::string(_cmd.mlsd)); enumer = std::shared_ptr(new FTPDirectoryEnumerMLSD(_conn, data_transport)); } else { if (_cmd.list_.empty()) { try { std::shared_ptr tmp_data_transport = _conn->DataCommand("LIST -la"); auto tmp_enumer = std::shared_ptr(new FTPDirectoryEnumerLIST(_conn, tmp_data_transport)); std::string tmp_name, tmp_owner, tmp_group; FileInformation tmp_file_info; tmp_enumer->Enum(tmp_name, tmp_owner, tmp_group, tmp_file_info); _cmd.list_ = "LIST -la"; } catch (std::exception &) { _cmd.list_ = "LIST"; } fprintf(stderr, "NR/FTP - list cmd: '%s'\n", _cmd.list_.c_str()); } std::shared_ptr data_transport = _conn->DataCommand(_cmd.list_); enumer = std::shared_ptr(new FTPDirectoryEnumerLIST(_conn, data_transport)); } if (g_netrocks_verbosity > 0) { fprintf(stderr, "Caching enum '%s'\n", _cwd.path.c_str()); } enumer = _dir_enum_cache.GetCachingWrapperDirectoryEnumer(_cwd.path, enumer); return enumer; } class FTPFileIO : protected FTPDataCommand, public IFileReader, public IFileWriter { public: using FTPDataCommand::FTPDataCommand; virtual size_t Read(void *buf, size_t len) { return _data_transport->Recv(buf, len); } virtual void Write(const void *buf, size_t len) { return _data_transport->Send(buf, len); } virtual void WriteComplete() { EnsureFinalized(); } }; std::shared_ptr ProtocolFTP::FileGet(const std::string &path, unsigned long long resume_pos) { const std::string &name_part = SplitPathAndNavigate(path); _str.assign(_cmd.retr).append(" ").append(name_part); std::shared_ptr data_transport = _conn->DataCommand(_str, resume_pos); return std::make_shared(_conn, data_transport); } std::shared_ptr ProtocolFTP::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { const std::string &name_part = SplitPathAndNavigate(path); if (_dir_enum_cache.HasValidEntries()) { _dir_enum_cache.Remove(_cwd.path); } _str.assign(_cmd.stor).append(" ").append(name_part); std::shared_ptr data_transport = _conn->DataCommand(_str, resume_pos); return std::make_shared(_conn, data_transport); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FTP/ProtocolFTP.h000066400000000000000000000061201477431623700233000ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "../Protocol.h" #include "../DirectoryEnumCache.h" #include "FTPConnection.h" class ProtocolFTP : public IProtocol, public std::enable_shared_from_this { std::shared_ptr _conn; DirectoryEnumCache _dir_enum_cache; struct { // command codes or nullptr if assumed that command is not supported const char *pwd = "PWD"; const char *cwd = "CWD"; const char *cdup = "CDUP"; const char *mkd = "MKD"; const char *rmd = "RMD"; const char *size = "SIZE"; const char *mfmt = "MFMT"; const char *chmod = "CHMOD"; const char *dele = "DELE"; const char *rnfr = "RNFR"; const char *rnto = "RNTO"; std::string list_;// = "LIST"; const char *mlsd = nullptr; // set to non-NULL during connection init const char *mlst = nullptr; // set to non-NULL during connection init const char *retr = "RETR"; const char *stor = "STOR"; } _cmd; struct { std::vector parts; std::string path; std::string home; } _cwd; std::string _str; bool RecvPwdResponse(); // bool RecvPwdAndRememberAsCwd(); std::string SplitPathAndNavigate(const std::string &path_name, bool allow_empty_name_part = false); std::string PathAsRelative(const std::string &path); void MLst(const std::string &path, FileInformation &file_info, uid_t *uid = nullptr, gid_t *gid = nullptr, std::string *lnkto = nullptr); std::shared_ptr NavigatedDirectoryEnum(); void SimpleDispositionCommand(const char *cmd, const std::string &path); public: ProtocolFTP(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &protocol_options); virtual ~ProtocolFTP(); const std::vector &EnumHosts(); std::string RootedPath(const std::string &path); virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/File/000077500000000000000000000000001477431623700212035ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/File/ProtocolFile.cpp000066400000000000000000000011011477431623700243010ustar00rootroot00000000000000#include #include #include "ProtocolFile.h" std::shared_ptr CreateProtocol(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) { return std::make_shared(host, port, username, password, options); } ProtocolFile::ProtocolFile(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options) { } ProtocolFile::~ProtocolFile() { } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/File/ProtocolFile.h000066400000000000000000000004431477431623700237560ustar00rootroot00000000000000#pragma once #include #include "../../Host/HostLocal.h" class ProtocolFile : public HostLocal { public: ProtocolFile(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options); virtual ~ProtocolFile(); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FileStatsOverride.cpp000066400000000000000000000053201477431623700244260ustar00rootroot00000000000000#include "FileStatsOverride.h" FileStatsOverride::~FileStatsOverride() { } void FileStatsOverride::Cleanup(const std::string &path) { if (!path.empty() && path.back() == '/') Cleanup(path.substr(0, path.size() - 1)); else _path2ovrst.erase(path); } void FileStatsOverride::Rename(std::string old_path, std::string new_path) { if (!old_path.empty() && old_path.back() == '/') old_path.pop_back(); if (!new_path.empty() && new_path.back() == '/') new_path.pop_back(); if (old_path == new_path) { return; } auto it = _path2ovrst.find(old_path); if (it != _path2ovrst.end()) { _path2ovrst[new_path] = it->second; _path2ovrst.erase(it); } } void FileStatsOverride::OverrideTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { OverridenStats &ovrst = _path2ovrst[path]; ovrst.access_time = access_time; ovrst.modification_time = modification_time; } void FileStatsOverride::OverrideMode(const std::string &path, mode_t mode) { _path2ovrst[path].mode = mode; } const FileStatsOverride::OverridenStats *FileStatsOverride::Lookup(const std::string &path) const { if (!path.empty() && path.back() == '/') return Lookup(path.substr(0, path.size() - 1)); if (path.empty()) return nullptr; auto it = _path2ovrst.find(path); return (it != _path2ovrst.end()) ? &it->second : nullptr; } void FileStatsOverride::FilterFileInformation(const std::string &path, FileInformation &file_info) const { const auto *ovrst = Lookup(path); if (ovrst) { if (ovrst->mode) { file_info.mode = ovrst->mode; } if (ovrst->access_time.tv_sec) { file_info.access_time = ovrst->access_time; } if (ovrst->modification_time.tv_sec) { file_info.modification_time = ovrst->modification_time; } } } void FileStatsOverride::FilterFileMode(const std::string &path, mode_t &mode) const { const auto *ovrst = Lookup(path); if (ovrst && ovrst->mode) { mode = ovrst->mode; } } /////////////// DirectoryEnumerWithFileStatsOverride::DirectoryEnumerWithFileStatsOverride( const FileStatsOverride &file_stats_override, std::shared_ptr enumer, const std::string &path) : _file_stats_override(file_stats_override), _enumer(enumer), _path(path) { if (!_path.empty() && _path.back() != '/') { _path+= '/'; } _path_len = _path.size(); } DirectoryEnumerWithFileStatsOverride::~DirectoryEnumerWithFileStatsOverride() { } bool DirectoryEnumerWithFileStatsOverride::Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (!_enumer->Enum(name, owner, group, file_info)) { return false; } _path.replace(_path_len, _path.size() - _path_len, name); _file_stats_override.FilterFileInformation(_path, file_info); return true; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/FileStatsOverride.h000066400000000000000000000026231477431623700240760ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "Protocol.h" class FileStatsOverride { public: ~FileStatsOverride(); struct OverridenStats { timespec access_time{}; timespec modification_time{}; mode_t mode{}; }; bool NonEmpty() const { return !_path2ovrst.empty(); } void Cleanup(const std::string &path); void Rename(std::string old_path, std::string new_path); void OverrideTimes(const std::string &path, const timespec &access_time, const timespec &modification_time); void OverrideMode(const std::string &path, mode_t mode); void FilterFileInformation(const std::string &path, FileInformation &file_info) const; void FilterFileMode(const std::string &path, mode_t &mode) const; private: std::map _path2ovrst; const OverridenStats *Lookup(const std::string &path) const; }; class DirectoryEnumerWithFileStatsOverride : public IDirectoryEnumer { const FileStatsOverride &_file_stats_override; std::shared_ptr _enumer; std::string _path; size_t _path_len{}; public: DirectoryEnumerWithFileStatsOverride(const FileStatsOverride &file_stats_override, std::shared_ptr enumer, const std::string &path); virtual ~DirectoryEnumerWithFileStatsOverride(); virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/NFS/000077500000000000000000000000001477431623700207525ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/NFS/ProtocolNFS.cpp000066400000000000000000000365071477431623700236410ustar00rootroot00000000000000#include #include #include #include #include #include "ProtocolNFS.h" #include #include std::shared_ptr CreateProtocol(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) { return std::make_shared(host, port, username, password, options); } //////////////////////////// ProtocolNFS::ProtocolNFS(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options) : _nfs(std::make_shared()), _host(host) { StringConfig protocol_options(options); _nfs->ctx = nfs_init_context(); if (!_nfs->ctx) { throw ProtocolError("Create context error", errno); } if (protocol_options.GetInt("Override", 0) != 0) { #ifdef LIBNFS_FEATURE_READAHEAD const std::string &host = protocol_options.GetString("Host"); const std::string &groups_str = protocol_options.GetString("Groups"); uint32_t uid = (uint32_t)protocol_options.GetInt("UID", 65534); uint32_t gid = (uint32_t)protocol_options.GetInt("GID", 65534); std::vector groups; for (size_t i = 0, j = 0; i <= groups_str.size(); ++i) { if (i == groups_str.size() || !isdigit(groups_str[i])) { const std::string &grp = groups_str.substr(j, i - j); for (size_t k = 0; k < grp.size(); ++k) { if (isdigit(grp[k])) { groups.emplace_back(atoi(grp.c_str() + k)); break; } } j = i + 1; } } struct AUTH *auth = libnfs_authunix_create(host.c_str(), uid, gid, groups.size(), groups.empty() ? nullptr : &groups[0]); if (auth) { nfs_set_auth(_nfs->ctx, auth); // owned by context: libnfs_auth_destroy(auth); } #else fprintf(stderr, "Your libnfs doesnt support credentials override\n"); #endif } } ProtocolNFS::~ProtocolNFS() { } static int RootedPathLevel(const std::string &rooted_path) { // 0 on "nfs://" // 1 on "nfs://server" or "nfs://server/" // 2 on "nfs://server/export" or "nfs://server/export/" //etc if (rooted_path.size() < 6) return -1; if (rooted_path.size() == 6) return 0; int out = 1; for (size_t i = 6; i < rooted_path.size(); ++i) { if (rooted_path[i] == '/' && i + 1 < rooted_path.size() && rooted_path[i + 1] != '/') { ++out; } } return out; } std::string ProtocolNFS::RootedPath(const std::string &path) { std::string out = "nfs://"; out+= _host; if (!_host.empty() && !_mount.empty()) { out+= '/'; out+= _mount; } if (!path.empty() && path != "." && path[0] != '/' && (!_host.empty() || !_mount.empty())) { out+= '/'; } if (path != "." && path != "/.") { out+= path; // out+= "?auto-traverse-mounts=1"; } for (size_t i = 7; i < out.size();) { if (out[i] == '.' && out[i - 1] == '/' && (i + 1 == out.size() || out[i + 1] == '/')) { out.erase(i - 1, 2); --i; } else { ++i; } } return out; } void ProtocolNFS::RootedPathToMounted(std::string &path) { size_t p = path.find('/', 6); if (p == std::string::npos) throw ProtocolError("Cannot mount path without export", path.c_str()); if (_nfs->mounted_path.empty() || path.size() < _nfs->mounted_path.size() || memcmp(path.c_str(), _nfs->mounted_path.c_str(), _nfs->mounted_path.size()) != 0 || (path.size() > _nfs->mounted_path.size() && path[_nfs->mounted_path.size()] != '/')) { std::string server = path.substr(6, p - 6); std::string exported = path.substr(p); int rc = nfs_mount(_nfs->ctx, server.c_str(), exported.c_str()); if (rc != 0) throw ProtocolError("Mount error", rc); _nfs->mounted_path = path; } path.erase(0, _nfs->mounted_path.size()); if (path.empty()) path = "/"; } std::string ProtocolNFS::MountedRootedPath(const std::string &path) { std::string out = RootedPath(path); if (RootedPathLevel(out) <= 1) { throw ProtocolError("Path is not mountable"); } RootedPathToMounted(out); return out; } mode_t ProtocolNFS::GetMode(const std::string &path, bool follow_symlink) { std::string xpath = RootedPath(path); if (RootedPathLevel(xpath) <= 1) { return S_IFDIR | 0755; } fprintf(stderr, "ProtocolNFS::GetMode rooted_path='%s'\n", xpath.c_str()); RootedPathToMounted(xpath); #ifdef LIBNFS_FEATURE_READAHEAD struct nfs_stat_64 s = {}; int rc = follow_symlink ? nfs_stat64(_nfs->ctx, xpath.c_str(), &s) : nfs_lstat64(_nfs->ctx, xpath.c_str(), &s); auto out = s.nfs_mode; #else struct stat s = {}; int rc = nfs_stat(_nfs->ctx, xpath.c_str(), &s); auto out = s.st_mode; #endif fprintf(stderr, "GetMode result %d\n", rc); if (rc != 0) throw ProtocolError("Get mode error", rc); return out; } unsigned long long ProtocolNFS::GetSize(const std::string &path, bool follow_symlink) { std::string xpath = RootedPath(path); if (RootedPathLevel(xpath) <= 1) { return 0; } fprintf(stderr, "ProtocolNFS::GetSize rooted_path='%s'\n", xpath.c_str()); RootedPathToMounted(xpath); #ifdef LIBNFS_FEATURE_READAHEAD struct nfs_stat_64 s = {}; int rc = follow_symlink ? nfs_stat64(_nfs->ctx, xpath.c_str(), &s) : nfs_lstat64(_nfs->ctx, xpath.c_str(), &s); auto out = s.nfs_size; #else struct stat s = {}; int rc = nfs_stat(_nfs->ctx, xpath.c_str(), &s); auto out = s.st_size; #endif if (rc != 0) throw ProtocolError("Get size error", rc); return out; } void ProtocolNFS::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { std::string xpath = RootedPath(path); if (RootedPathLevel(xpath) <= 1) { file_info = FileInformation(); file_info.mode = S_IFDIR | 0755; return; } fprintf(stderr, "ProtocolNFS::GetInformation rooted_path='%s'\n", xpath.c_str()); RootedPathToMounted(xpath); #ifdef LIBNFS_FEATURE_READAHEAD struct nfs_stat_64 s = {}; int rc = follow_symlink ? nfs_stat64(_nfs->ctx, xpath.c_str(), &s) : nfs_lstat64(_nfs->ctx, xpath.c_str(), &s); if (rc != 0) throw ProtocolError("Get info error", rc); file_info.access_time.tv_sec = s.nfs_atime; file_info.access_time.tv_nsec = s.nfs_atime_nsec; file_info.modification_time.tv_sec = s.nfs_mtime; file_info.modification_time.tv_nsec = s.nfs_mtime_nsec; file_info.status_change_time.tv_sec = s.nfs_ctime; file_info.status_change_time.tv_nsec = s.nfs_ctime_nsec; file_info.mode = s.nfs_mode; file_info.size = s.nfs_size; #else struct stat s = {}; int rc = nfs_stat(_nfs->ctx, xpath.c_str(), &s); if (rc != 0) throw ProtocolError("Get info error", rc); file_info.access_time = s.st_atim; file_info.modification_time = s.st_mtim; file_info.status_change_time = s.st_ctim; file_info.size = s.st_size; file_info.mode = s.st_mode; #endif } void ProtocolNFS::FileDelete(const std::string &path) { int rc = nfs_unlink(_nfs->ctx, MountedRootedPath(path).c_str()); if (rc != 0) throw ProtocolError("Delete file error", rc); } void ProtocolNFS::DirectoryDelete(const std::string &path) { int rc = nfs_rmdir(_nfs->ctx, MountedRootedPath(path).c_str()); if (rc != 0) throw ProtocolError("Delete directory error", rc); } void ProtocolNFS::DirectoryCreate(const std::string &path, mode_t mode) { int rc = nfs_mkdir(_nfs->ctx, MountedRootedPath(path).c_str());//, mode); if (rc != 0) throw ProtocolError("Create directory error", rc); } void ProtocolNFS::Rename(const std::string &path_old, const std::string &path_new) { int rc = nfs_rename(_nfs->ctx, MountedRootedPath(path_old).c_str(), MountedRootedPath(path_new).c_str()); if (rc != 0) throw ProtocolError("Rename error", rc); } void ProtocolNFS::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { struct timeval times[2] = {}; times[0].tv_sec = access_time.tv_sec; times[0].tv_usec = suseconds_t(access_time.tv_nsec / 1000); times[1].tv_sec = modification_time.tv_sec; times[1].tv_usec = suseconds_t(modification_time.tv_nsec / 1000); int rc = nfs_utimes(_nfs->ctx, MountedRootedPath(path).c_str(), times); if (rc != 0) throw ProtocolError("Set times error", rc); } void ProtocolNFS::SetMode(const std::string &path, mode_t mode) { int rc = nfs_chmod(_nfs->ctx, MountedRootedPath(path).c_str(), mode); if (rc != 0) throw ProtocolError("Set mode error", rc); } void ProtocolNFS::SymlinkCreate(const std::string &link_path, const std::string &link_target) { int rc = nfs_symlink(_nfs->ctx, MountedRootedPath(link_target).c_str(), MountedRootedPath(link_path).c_str()); if (rc != 0) throw ProtocolError("Symlink create error", rc); } void ProtocolNFS::SymlinkQuery(const std::string &link_path, std::string &link_target) { char buf[0x1001] = {}; int rc = nfs_readlink(_nfs->ctx, MountedRootedPath(link_path).c_str(), buf, sizeof(buf) - 1); if (rc != 0) throw ProtocolError("Symlink query error", rc); link_target = buf; } class NFSDirectoryEnumer : public IDirectoryEnumer { std::shared_ptr _nfs; struct nfsdir *_dir = nullptr; public: NFSDirectoryEnumer(std::shared_ptr &nfs, const std::string &path) : _nfs(nfs) { //const std::string &dir_path = protocol->RootedPath(path) int rc = nfs_opendir(_nfs->ctx, path.c_str(), &_dir); if (rc != 0 || _dir == nullptr) { _dir = nullptr; throw ProtocolError("Directory open error", rc); } } virtual ~NFSDirectoryEnumer() { if (_dir != nullptr) { nfs_closedir(_nfs->ctx, _dir); } } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { for (;;) { struct nfsdirent *de = nfs_readdir(_nfs->ctx, _dir); if (de == nullptr) { return false; } if (!de->name || !FILENAME_ENUMERABLE(de->name)) { continue; } name = de->name; file_info.access_time.tv_sec = de->atime.tv_sec; file_info.modification_time.tv_sec = de->mtime.tv_sec; file_info.status_change_time.tv_sec = de->ctime.tv_sec; #ifdef LIBNFS_FEATURE_READAHEAD owner = StrPrintf("UID:%u", de->uid); group = StrPrintf("GID:%u", de->gid); file_info.access_time.tv_nsec = de->atime_nsec; file_info.modification_time.tv_nsec = de->mtime_nsec; file_info.status_change_time.tv_nsec = de->ctime_nsec; #else owner.clear(); group.clear(); #endif file_info.mode = de->mode; file_info.size = de->size; switch (de->type) { case NF3REG: file_info.mode|= S_IFREG; break; case NF3DIR: file_info.mode|= S_IFDIR; break; case NF3BLK: file_info.mode|= S_IFBLK; break; case NF3CHR: file_info.mode|= S_IFCHR; break; case NF3LNK: file_info.mode|= S_IFLNK; break; case NF3SOCK: file_info.mode|= S_IFSOCK; break; case NF3FIFO: file_info.mode|= S_IFIFO; break; } return true; } } }; struct NFSContainersEnumer : IDirectoryEnumer { protected: std::set _names; virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) { if (_names.empty()) { return false; } name = *_names.begin(); owner.clear(); group.clear(); file_info = FileInformation(); file_info.mode|= S_IFDIR | DEFAULT_ACCESS_MODE_DIRECTORY; _names.erase(_names.begin()); return true; } }; struct NFSServersEnumer : NFSContainersEnumer { NFSServersEnumer(std::shared_ptr &nfs) { if (nfs->srv2exports.empty()) { struct nfs_server_list *servers = nfs_find_local_servers(); fprintf(stderr, "NFSServersEnumer: %p\n", servers); if (servers != nullptr) { std::set empty; struct nfs_server_list *server = servers; do { nfs->srv2exports.emplace(server->addr, empty); server = server->next; } while (server != nullptr); free_nfs_srvr_list(servers); } } for (const auto &i : nfs->srv2exports) { _names.emplace(i.first); } } }; struct NFSExportsEnumer : NFSContainersEnumer { NFSExportsEnumer(std::shared_ptr &nfs, const std::string &rooted_path) { if (rooted_path.size() <= 6) { fprintf(stderr, "NFSExportsEnumer('%s'): path too short\n", rooted_path.c_str()); return; } std::string server = rooted_path.substr(6); while (!server.empty() && server[server.size() - 1] == '/') { server.resize(server.size() - 1); } if (server.empty()) { fprintf(stderr, "NFSExportsEnumer('%s'): no server in path\n", rooted_path.c_str()); return; } auto &cached_exports = nfs->srv2exports[server]; if (cached_exports.empty()) { struct exportnode *en_list = mount_getexports(server.c_str()); fprintf(stderr, "NFSExportsEnumer('%s'): %p\n", server.c_str(), en_list); if (en_list != nullptr) { struct exportnode *en = en_list; do { cached_exports.emplace((*en->ex_dir == '/') ? en->ex_dir + 1 : en->ex_dir); en = en->ex_next; } while (en != nullptr); mount_free_export_list(en_list); } } _names = cached_exports; } }; std::shared_ptr ProtocolNFS::DirectoryEnum(const std::string &path) { std::string xpath = RootedPath(path); fprintf(stderr, "ProtocolNFS::DirectoryEnum: rooted_path='%s'\n", xpath.c_str()); switch (RootedPathLevel(xpath)) { case 0: return std::shared_ptr(new NFSServersEnumer(_nfs)); case 1: return std::shared_ptr(new NFSExportsEnumer(_nfs, xpath)); default: RootedPathToMounted(xpath); return std::shared_ptr(new NFSDirectoryEnumer(_nfs, xpath)); } } class NFSFileIO : public IFileReader, public IFileWriter { std::shared_ptr _nfs; struct nfsfh *_file = nullptr; public: NFSFileIO(std::shared_ptr &nfs, const std::string &path, int flags, mode_t mode, unsigned long long resume_pos) : _nfs(nfs) { fprintf(stderr, "TRying to open path: '%s'\n", path.c_str()); int rc = (flags & O_CREAT) ? nfs_creat(_nfs->ctx, path.c_str(), mode & (~O_CREAT), &_file) : nfs_open(_nfs->ctx, path.c_str(), flags, &_file); if (rc != 0 || _file == nullptr) { _file = nullptr; throw ProtocolError("Failed to open file", rc); } if (resume_pos) { uint64_t current_offset = resume_pos; int rc = nfs_lseek(_nfs->ctx, _file, resume_pos, SEEK_SET, ¤t_offset); if (rc < 0) { nfs_close(_nfs->ctx, _file); _file = nullptr; throw ProtocolError("Failed to seek file", rc); } } } virtual ~NFSFileIO() { if (_file != nullptr) { nfs_close(_nfs->ctx, _file); } } virtual size_t Read(void *buf, size_t len) { #ifdef LIBNFS_API_V2 const auto rc = nfs_read(_nfs->ctx, _file, (char *)buf, len); #else const auto rc = nfs_read(_nfs->ctx, _file, len, (char *)buf); #endif if (rc < 0) throw ProtocolError("Read file error", errno); // uncomment to simulate connection stuck if ( (rand()%100) == 0) sleep(60); return (size_t)rc; } virtual void Write(const void *buf, size_t len) { if (len > 0) for (;;) { #ifdef LIBNFS_API_V2 const auto rc = nfs_write(_nfs->ctx, _file, (const char *)buf, len); #else const auto rc = nfs_write(_nfs->ctx, _file, len, (const char *)buf); #endif if (rc <= 0) throw ProtocolError("Write file error", errno); if ((size_t)rc >= len) break; len-= (size_t)rc; buf = (const char *)buf + rc; } } virtual void WriteComplete() { // what? } }; std::shared_ptr ProtocolNFS::FileGet(const std::string &path, unsigned long long resume_pos) { return std::make_shared(_nfs, MountedRootedPath(path), O_RDONLY, 0, resume_pos); } std::shared_ptr ProtocolNFS::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { return std::make_shared(_nfs, MountedRootedPath(path), O_WRONLY | O_CREAT | (resume_pos ? 0 : O_TRUNC), mode, resume_pos); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/NFS/ProtocolNFS.h000066400000000000000000000043111477431623700232720ustar00rootroot00000000000000#pragma once #include #include #include #include #include "../Protocol.h" extern "C" { #include //#include #include #include } struct NFSConnection { std::map > srv2exports; struct nfs_context *ctx = nullptr; std::string mounted_path; NFSConnection() = default; ~NFSConnection() { if (ctx != nullptr) { nfs_destroy_context(ctx); } } private: NFSConnection(const NFSConnection &) = delete; }; class ProtocolNFS : public IProtocol { std::shared_ptr _nfs; std::string _host, _mount; std::string RootedPath(const std::string &path); void RootedPathToMounted(std::string &path); std::string MountedRootedPath(const std::string &path); public: ProtocolNFS(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &protocol_options); virtual ~ProtocolNFS(); virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/Protocol.cpp000066400000000000000000000035311477431623700226330ustar00rootroot00000000000000#include #include "Protocol.h" void ConfigureProtocolSFTP(std::string &options); void ConfigureProtocolSCP(std::string &options); void ConfigureProtocolFTP(std::string &options); void ConfigureProtocolFTPS(std::string &options); void ConfigureProtocolSMB(std::string &options); void ConfigureProtocolNFS(std::string &options); void ConfigureProtocolWebDAV(std::string &options); void ConfigureProtocolWebDAVs(std::string &options); void ConfigureProtocolFile(std::string &options); void ConfigureProtocolSHELL(std::string &options); void ConfigureProtocolAWS(std::string &options); static ProtocolInfo s_protocols[] = { #ifdef HAVE_SFTP { "sftp", "NetRocks-SFTP", 22, true, true, true, ConfigureProtocolSFTP}, { "scp", "NetRocks-SFTP", 22, true, true, true, ConfigureProtocolSCP}, #endif { "shell", "NetRocks-SHELL", 22, true, true, true, ConfigureProtocolSHELL}, { "ftp", "NetRocks-FTP", 21, true, true, true, ConfigureProtocolFTP}, #ifdef HAVE_OPENSSL { "ftps", "NetRocks-FTP", 990, true, true, true, ConfigureProtocolFTPS}, #endif #ifdef HAVE_SMB { "smb", "NetRocks-SMB", -1, false, true, false, ConfigureProtocolSMB}, #endif #ifdef HAVE_NFS { "nfs", "NetRocks-NFS", -1, false, false, false, ConfigureProtocolNFS}, #endif #ifdef HAVE_WEBDAV { "dav", "NetRocks-WebDAV", 80, true, true, true, ConfigureProtocolWebDAV}, { "davs", "NetRocks-WebDAV", 443, true, true, true, ConfigureProtocolWebDAVs}, #endif #ifdef HAVE_AWS { "aws", "NetRocks-AWS", 443, true, true, false, ConfigureProtocolAWS}, #endif { "file", "NetRocks-FILE", 0, false, true, false, nullptr}, { } }; const ProtocolInfo *ProtocolInfoHead() { return &s_protocols[0]; } const ProtocolInfo *ProtocolInfoLookup(const char *name) { for (const ProtocolInfo *pi = ProtocolInfoHead(); pi->name; ++pi) { if (strcasecmp(name, pi->name) == 0) { return pi; } } return nullptr; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/Protocol.h000066400000000000000000000072131477431623700223010ustar00rootroot00000000000000#pragma once #include #if defined(__FreeBSD__) || defined(__DragonFly__) # include #endif #include "Erroring.h" #include "FileInformation.h" struct ExecFIFO_CtlMsg { enum Cmd { CMD_PTY_SIZE = 0, CMD_SIGNAL } cmd; union { struct { unsigned int cols; unsigned int rows; } pty_size; unsigned int signum; } u; }; // all methods of this interfaces are NOT thread-safe unless explicitly marked as MT-safe // all methods may throw exceptions from ../Erroring.h to indicate connectivity/authorization/etc problems struct IFileReader { virtual ~IFileReader() {} virtual size_t Read(void *buf, size_t len) = 0; }; struct IFileWriter { virtual ~IFileWriter() {} virtual void Write(const void *buf, size_t len) = 0; /// optional call used to ensure that all data written (flush, gracefully close connection etc) but may be not called virtual void WriteComplete() = 0; }; struct IDirectoryEnumer { virtual ~IDirectoryEnumer() {} virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) = 0; }; struct IProtocol { virtual ~IProtocol() {} /* default implementation */ virtual void KeepAlive(const std::string &path_to_check) { GetMode(path_to_check); } /* optimized and not-throwing version of GetMode for mass-query of modes */ virtual void GetModes(bool follow_symlink, size_t count, const std::string *paths, mode_t *modes) noexcept { for (size_t i = 0; i < count; ++i) try { modes[i] = GetMode(paths[i], follow_symlink); } catch (...) { modes[i] = ~(mode_t)0; } } virtual mode_t GetMode(const std::string &path, bool follow_symlink = true) = 0; virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true) = 0; virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true) = 0; virtual void FileDelete(const std::string &path) = 0; virtual void DirectoryDelete(const std::string &path) = 0; virtual void DirectoryCreate(const std::string &path, mode_t mode) = 0; virtual void Rename(const std::string &path_old, const std::string &path_new) = 0; virtual void SetTimes(const std::string &path, const timespec &access_timem, const timespec &modification_time) = 0; virtual void SetMode(const std::string &path, mode_t mode) = 0; virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target) = 0; virtual void SymlinkQuery(const std::string &link_path, std::string &link_target) = 0; virtual std::shared_ptr DirectoryEnum(const std::string &path) = 0; virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0) = 0; virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0) = 0; virtual void ExecuteCommand(const std::string &working_dir, const std::string &command_line, const std::string &fifo) { throw ProtocolUnsupportedError(""); } }; #define FILENAME_ENUMERABLE(PSZ) ((PSZ)[0] != 0 && ((PSZ)[0] != '.' || ((PSZ)[1] != 0 && ((PSZ)[1] != '.' || (PSZ)[2] != 0)) )) struct ProtocolInfo { const char *name; const char *broker; int default_port; // -1 if port cannot be represented/changed bool require_server; // false if protocol can be instantiated with empty server bool support_creds; // false if protocol doesnt support username:password authentication bool inaccurate_timestamps; // true if should use OPIF_COMPAREFATTIME flag when opened file in such protocol void (*Configure)(std::string &options); }; const ProtocolInfo *ProtocolInfoHead(); const ProtocolInfo *ProtocolInfoLookup(const char *name); far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/000077500000000000000000000000001477431623700211735ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Helpers/000077500000000000000000000000001477431623700225755ustar00rootroot00000000000000far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Helpers/remote.sh000077500000000000000000000376131477431623700244410ustar00rootroot00000000000000# This script is compactized when sent: # All comments and empty lines are discarded. # Tokens started by SHELLVAR_ and SHELLFCN_ are renamed to shorter names. SAVED_PS1=$PS1;SAVED_PS2=$PS2;SAVED_PS2=$PS3;SAVED_PS4=$PS4;SAVED_PC=$PROMPT_COMMAND;export PS1=;export PS2=;export PS3=;export PS4=;export PROMPT_COMMAND=; #SAVED_STTY=`stty -g` #stty -opost -echo if [ "$0" = "bash" ] || [ "$0" = "-bash" ]; then bind 'set enable-bracketed-paste off'; fi export FISH=far2l export LANG=C export LC_TIME=C export LS_COLORS= export SHELLVAR_LOG=/dev/null #export SHELLVAR_LOG=/tmp/nr-shell.log SHELLFCN_SEND_ERROR_AND_RESYNC() { SHELLVAR_ERRCNT=`expr $SHELLVAR_ERRCNT + 1` ERRID="$$-$(date)-$SHELLVAR_ERRCNT" echo "resync.req: $1:$ERRID" >>$SHELLVAR_LOG echo;echo "+ERROR:$1:$ERRID" while true; do $SHELLVAR_READ_FN STR || exit echo "resync.rpl: $STR" >>$SHELLVAR_LOG [ "$STR" = "SHELL_RESYNCHRONIZATION_ERROR:$1:$ERRID" ] && break done } SHELLFCN_GET_INFO_INNER() { # $1 - path # $2 - nonempty if follow symlink # $3 - stat format # $4 - find format # $5 - optional ls filter if [ -n "$SHELLVAR_STAT" ]; then if [ -n "$2" ]; then stat -L --format="$3" "$1" else stat --format="$3" "$1" fi elif [ -n "$SHELLVAR_FIND" ]; then if [ -n "$2" ]; then find -H "$1" -mindepth 0 -maxdepth 0 -printf "$4" else find "$1" -mindepth 0 -maxdepth 0 -printf "$4" fi else SHELLVAR_SELECTED_LS_ARGS=$SHELLVAR_LS_ARGS [ -n "$2" ] && SHELLVAR_SELECTED_LS_ARGS=$SHELLVAR_LS_ARGS_FOLLOW if [ -n "$5" ]; then ls -d $SHELLVAR_SELECTED_LS_ARGS "$1" | grep $SHELLVAR_GREP_ARGS '^[^cbt]' | ( $SHELLVAR_READ_FN $5; echo $y ) else ls -d $SHELLVAR_SELECTED_LS_ARGS "$1" | grep $SHELLVAR_GREP_ARGS '^[^cbt]' fi fi } SHELLFCN_GET_SIZE() { SHELLVAR_INFO=`SHELLFCN_GET_INFO_INNER "$1" '1' "$SHELLVAR_STAT_FMT_SIZE" "$SHELLVAR_FIND_FMT_SIZE" 'n n n n y n' 2>>$SHELLVAR_LOG` [ -n "$SHELLVAR_INFO" ] || SHELLVAR_INFO=0 echo "$SHELLVAR_INFO" } SHELLFCN_GET_INFO() { SHELLVAR_OUT=`SHELLFCN_GET_INFO_INNER "$@" 2>>$SHELLVAR_LOG` if [ -n "$SHELLVAR_OUT" ]; then echo "+OK:$SHELLVAR_OUT" else SHELLVAR_OUT=`SHELLFCN_GET_INFO_INNER "$@" 2>&1` echo "+ERROR:$SHELLVAR_OUT" fi } SHELLFCN_CMD_ENUM() { if [ -n "$SHELLVAR_STAT" ]; then stat --format="$SHELLVAR_STAT_FMT" "$SHELLVAR_ARG"/* "$SHELLVAR_ARG"/.* 2>>$SHELLVAR_LOG elif [ -n "$SHELLVAR_FIND" ]; then find -H "$SHELLVAR_ARG" -mindepth 1 -maxdepth 1 -printf "$SHELLVAR_FIND_FMT" 2>>$SHELLVAR_LOG else ls $SHELLVAR_LS_ARGS_FOLLOW "$SHELLVAR_ARG" 2>>$SHELLVAR_LOG | grep $SHELLVAR_GREP_ARGS '^[^cbt]' fi } SHELLFCN_CMD_INFO_SINGLE() { SHELLFCN_GET_INFO "$SHELLVAR_ARG" "$1" "$2" "$3" "$4" } SHELLFCN_CMD_INFO_MULTI() { # !!! This is a directory with symlinks listing bottleneck !!! # TODO: Rewrite so instead of querying files one-by-one do grouped queries with one stat per several files while [ $SHELLVAR_ARG -gt 0 ]; do $SHELLVAR_READ_FN SHELLVAR_PATH1 SHELLVAR_PATH2 SHELLVAR_PATH3 SHELLVAR_PATH4 SHELLVAR_PATH5 SHELLVAR_PATH6 SHELLVAR_PATH7 SHELLVAR_PATH8 || exit [ $SHELLVAR_ARG -ge 1 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH1" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 2 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH2" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 3 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH3" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 4 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH4" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 5 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH5" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 6 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH6" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 7 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH7" "$1" "$2" "$3" "$4" [ $SHELLVAR_ARG -ge 8 ] && SHELLFCN_GET_INFO "$SHELLVAR_PATH8" "$1" "$2" "$3" "$4" SHELLVAR_ARG=`expr $SHELLVAR_ARG - 8` done } SHELLFCN_CHOOSE_BLOCK() { # $1 - size want to write # $2 - position want to seek (so chosen block size will be its divider) SHELLVAR_BLOCK=1 [ $1 -ge 16 ] && [ `expr $2 % 16` -eq 0 ] && SHELLVAR_BLOCK=16 [ $1 -ge 64 ] && [ `expr $2 % 64` -eq 0 ] && SHELLVAR_BLOCK=64 [ $1 -ge 512 ] && [ `expr $2 % 512` -eq 0 ] && SHELLVAR_BLOCK=512 [ $1 -ge 4096 ] && [ `expr $2 % 4096` -eq 0 ] && SHELLVAR_BLOCK=4096 [ $1 -ge 65536 ] && [ `expr $2 % 65536` -eq 0 ] && SHELLVAR_BLOCK=65536 } SHELLFCN_CMD_READ() { $SHELLVAR_READ_FN SHELLVAR_OFFSET || exit SHELLVAR_SIZE=`SHELLFCN_GET_SIZE "$SHELLVAR_ARG"` if [ ! -n "$SHELLVAR_SIZE" ]; then echo '+FAIL' return fi SHELLVAR_STATE= if [ -n "$SHELLVAR_DD" ]; then while true; do SHELLVAR_REMAIN=`expr $SHELLVAR_SIZE - $SHELLVAR_OFFSET` $SHELLVAR_READ_FN SHELLVAR_STATE || exit if [ "$SHELLVAR_STATE" = 'abort' ]; then echo '+ABORTED' $SHELLVAR_READ_FN SHELLVAR_STATE || exit break fi if [ $SHELLVAR_REMAIN -le 0 ]; then echo '+DONE' $SHELLVAR_READ_FN SHELLVAR_STATE || exit break fi SHELLFCN_CHOOSE_BLOCK $SHELLVAR_REMAIN $SHELLVAR_OFFSET SHELLVAR_PIECE=$SHELLVAR_REMAIN [ $SHELLVAR_PIECE -gt 8388608 ] && SHELLVAR_PIECE=8388608 CNT=`expr $SHELLVAR_PIECE / $SHELLVAR_BLOCK` SHELLVAR_PIECE=`expr $CNT '*' $SHELLVAR_BLOCK` echo '+NEXT:'$SHELLVAR_PIECE dd iflag=fullblock skip=`expr $SHELLVAR_OFFSET / $SHELLVAR_BLOCK` count=$CNT bs=$SHELLVAR_BLOCK if="${SHELLVAR_ARG}" 2>>$SHELLVAR_LOG ERR=$? if [ $ERR -ne 0 ]; then # its unknown how much data was actually read, so send $SHELLVAR_PIECE of zeroes followed with ERROR statement # unless its client aborted transfer by sending CtrlC [ "$SHELLVAR_STATE" = 'abort' ] || dd iflag=fullblock count=$CNT bs=$SHELLVAR_BLOCK if=/dev/zero 2>>$SHELLVAR_LOG SHELLFCN_SEND_ERROR_AND_RESYNC "$ERR" break fi SHELLVAR_OFFSET=`expr $SHELLVAR_OFFSET + $SHELLVAR_PIECE` done elif [ $SHELLVAR_OFFSET -eq 0 ]; then # no dd? fallback to cat, if can $SHELLVAR_READ_FN SHELLVAR_STATE || exit echo '+NEXT:'$SHELLVAR_SIZE cat "$SHELLVAR_ARG" 2>>$SHELLVAR_LOG echo '+DONE' $SHELLVAR_READ_FN SHELLVAR_STATE || exit else echo '+FAIL' fi } SHELLFCN_WRITE_BY_DD() { # $1 - size # $2 - offset # $3 - file SHELLFCN_CHOOSE_BLOCK $1 $2 SHELLVAR_DDCNT=`expr $1 / $SHELLVAR_BLOCK` SHELLVAR_DDPIECE=`expr $SHELLVAR_DDCNT '*' $SHELLVAR_BLOCK` SHELLVAR_DDSEEK=`expr $2 '/' $SHELLVAR_BLOCK` dd iflag=fullblock seek=$SHELLVAR_DDSEEK count=$SHELLVAR_DDCNT bs=$SHELLVAR_BLOCK of="$3" 2>>$SHELLVAR_LOG RV=$? if [ $RV -eq 0 ] && [ $SHELLVAR_DDPIECE -ne $1 ]; then SHELLFCN_WRITE_BY_DD `expr $1 - $SHELLVAR_DDPIECE` `expr $2 + $SHELLVAR_DDPIECE` "$3" RV=$? fi return $RV } SHELLFCN_WRITE_BY_HEAD() { # $1 - size # $2 - offset # $3 - file SHELLVAR_WBH_SIZE_EXPECTED=`expr $1 + $2` SHELLVAR_WBH_SIZE_REMAIN=$1 # echo "HEADING $1 bytes at $2" >>$SHELLVAR_LOG # echo "HEADED $1 bytes at $2 size before=$(SIZE_BEFORE) after=$(SHELLFCN_GET_SIZE "$3") " >>$SHELLVAR_LOG while true; do if [ $2 -eq 0 ]; then head -c $SHELLVAR_WBH_SIZE_REMAIN >"$3" 2>>$SHELLVAR_LOG SHELLVAR_WBH_RV=$? else head -c $SHELLVAR_WBH_SIZE_REMAIN >>"$3" 2>>$SHELLVAR_LOG SHELLVAR_WBH_RV=$? fi SHELLVAR_WBH_SIZE=$(SHELLFCN_GET_SIZE "$3") [ $SHELLVAR_WBH_RV -ne 0 ] || return $SHELLVAR_WBH_RV [ $SHELLVAR_WBH_SIZE -ge $SHELLVAR_WBH_SIZE_EXPECTED ] && return 0 SHELLVAR_WBH_SIZE_REMAIN=`expr $SHELLVAR_WBH_SIZE_EXPECTED - $SHELLVAR_WBH_SIZE` done } SHELLFCN_WRITE_BY_BASE64() { # $1 - size (ignored as whole line will be read) # $2 - offset # $3 - file $SHELLVAR_READ_FN B64LN if [ $2 -eq 0 ]; then echo "$B64LN" | base64 -d >"$3" 2>>$SHELLVAR_LOG else echo "$B64LN" | base64 -d >>"$3" 2>>$SHELLVAR_LOG fi } SHELLFCN_CMD_WRITE() { $SHELLVAR_READ_FN SHELLVAR_OFFSET || exit if [ -n "$SHELLVAR_DD" ] || [ -n "$SHELLVAR_HEAD" ] || [ -n "$SHELLVAR_BASE64" ]; then SHELLFCN_WRITE=SHELLFCN_WRITE_BY_BASE64 [ -n "$SHELLVAR_HEAD" ] && SHELLFCN_WRITE=SHELLFCN_WRITE_BY_HEAD [ -n "$SHELLVAR_DD" ] && SHELLFCN_WRITE=SHELLFCN_WRITE_BY_DD if ! [ -n "$SHELLVAR_DD" ] && [ $SHELLVAR_OFFSET -ne 0 ] && ! truncate --size="$SHELLVAR_OFFSET" "$SHELLVAR_ARG" >>$SHELLVAR_LOG 2>&1 ; then SHELLFCN_SEND_ERROR_AND_RESYNC "$?" # avoid further writings SHELLVAR_ARG=/dev/null else echo '+OK' fi NSEQ=1 while true; do while true; do $SHELLVAR_READ_FN SEQ SHELLVAR_SIZE || exit [ "$SHELLVAR_SIZE" = '' ] || break done if [ "$SEQ" = '.' ]; then # have to create/truncate file if there was no data written [ $NSEQ -eq 1 ] && ( touch "$SHELLVAR_ARG"; truncate --size="$SHELLVAR_OFFSET" "$SHELLVAR_ARG" ) >>$SHELLVAR_LOG 2>&1 break fi if [ $NSEQ -eq $SEQ ] && $SHELLFCN_WRITE $SHELLVAR_SIZE $SHELLVAR_OFFSET "$SHELLVAR_ARG"; then echo '+OK' SHELLVAR_OFFSET=`expr $SHELLVAR_OFFSET + $SHELLVAR_SIZE` NSEQ=`expr $NSEQ + 1` else SHELLFCN_SEND_ERROR_AND_RESYNC "SEQ=$SEQ NSEQ=$NSEQ $?" # avoid further writings SHELLVAR_ARG=/dev/null fi done else SHELLFCN_SEND_ERROR_AND_RESYNC "$?" fi } SHELLFCN_CMD_REMOVE_FILE() { SHELLVAR_OUT=`unlink "$SHELLVAR_ARG" 2>&1 || rm -f "$SHELLVAR_ARG" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_CMD_REMOVE_DIR() { SHELLVAR_OUT=`rmdir "$SHELLVAR_ARG" 2>&1 || rm -f "$SHELLVAR_ARG" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_CMD_CREATE_DIR() { $SHELLVAR_READ_FN SHELLVAR_ARG_MODE || exit SHELLVAR_OUT=`mkdir -m "$SHELLVAR_ARG_MODE" "$SHELLVAR_ARG" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_CMD_RENAME() { $SHELLVAR_READ_FN SHELLVAR_ARG_DEST || exit SHELLVAR_OUT=`mv "$SHELLVAR_ARG" "$SHELLVAR_ARG_DEST" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_READLINK_BY_LS() { # lrwxrwxrwx 1 root root 7 Feb 11 2023 /bin -> usr/bin ls -d $SHELLVAR_LS_ARGS "$1" 2>/dev/null | ( $SHELLVAR_READ_FN W1 W2 W3 W4 W5 W6 W7 W8 W9 W10 W11 W12 W13 W14 W15 RV=$? if [ $RV -eq 0 ]; then [ "$W14" = '->' ] && echo "$W15" [ "$W13" = '->' ] && echo "$W14" [ "$W12" = '->' ] && echo "$W13" [ "$W11" = '->' ] && echo "$W12" [ "$W10" = '->' ] && echo "$W11" [ "$W9" = '->' ] && echo "$W10" [ "$W8" = '->' ] && echo "$W9" [ "$W7" = '->' ] && echo "$W8" fi return $RV ) } SHELLFCN_CMD_READ_SYMLINK() { SHELLVAR_OUT=`$SHELLVAR_READLINK_FN "$SHELLVAR_ARG" 2>>$SHELLVAR_LOG` RV=$? if [ $RV -eq 0 ]; then echo "+OK:$SHELLVAR_OUT" else SHELLVAR_OUT=`$SHELLVAR_READLINK_FN "$SHELLVAR_ARG" 2>&1` echo "+ERROR:$SHELLVAR_OUT" fi } SHELLFCN_CMD_MAKE_SYMLINK() { $SHELLVAR_READ_FN SHELLVAR_ARG_DEST || exit SHELLVAR_OUT=`ln -s "$SHELLVAR_ARG_DEST" "$SHELLVAR_ARG" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_CMD_SET_MODE() { $SHELLVAR_READ_FN SHELLVAR_ARG_MODE || exit SHELLVAR_OUT=`chmod "$SHELLVAR_ARG_MODE" "$SHELLVAR_ARG" 2>&1` RV=$? [ $RV -ne 0 ] && echo "+ERROR:$SHELLVAR_OUT" } SHELLFCN_CMD_EXECUTE() { $SHELLVAR_READ_FN SHELLVAR_ARG_TOKEN SHELLVAR_ARG_WD || exit if cd "$SHELLVAR_ARG_WD"; then eval $SHELLVAR_ARG RV=$? echo;echo "$SHELLVAR_ARG_TOKEN:$RV" else echo;echo "$SHELLVAR_ARG_TOKEN:-1" fi } ### # NAME:MODE:SIZE:ACCESS:MODIFY:STATUS:USER:GROUP SHELLVAR_STAT_FMT='%n %f %s %X %Y %Z %U %G' SHELLVAR_FIND_FMT='%f %M %s %A@ %T@ %C@ %u %g\n' SHELLVAR_STAT_FMT_INFO='%f %s %X %Y %Z' SHELLVAR_FIND_FMT_INFO='%M %s %A@ %T@ %C@\n' SHELLVAR_STAT_FMT_SIZE='%s' SHELLVAR_FIND_FMT_SIZE='%s\n' SHELLVAR_STAT_FMT_MODE='%f' SHELLVAR_FIND_FMT_MODE='%M\n' SHELLVAR_READLINK_FN=SHELLFCN_READLINK_BY_LS SHELLVAR_FIND= SHELLVAR_STAT= SHELLVAR_LS_ARGS='-l -A' SHELLVAR_DD= SHELLVAR_HEAD= SHELLVAR_WRITE_BLOCK= SHELLVAR_BASE64= SHELLVAR_ERRCNT=0 SHELLVAR_GREP_ARGS= SHELLVAR_READ_FN=read if echo XXX | read -s XXX >/dev/null 2>&1; then SHELLVAR_READ_FN='read -s' fi if which readlink >>$SHELLVAR_LOG 2>&1; then SHELLVAR_READLINK_FN=readlink fi if echo foo | grep --color=never foo >>$SHELLVAR_LOG 2>&1; then SHELLVAR_GREP_ARGS='--color=never' fi if find -H . -mindepth 0 -maxdepth 0 -printf "$SHELLVAR_FIND_FMT" >>$SHELLVAR_LOG 2>&1; then SHELLVAR_FIND=Y fi if stat -L --format="$SHELLVAR_STAT_FMT" . >>$SHELLVAR_LOG 2>&1; then SHELLVAR_STAT=Y fi if ls -f $SHELLVAR_LS_ARGS . >>$SHELLVAR_LOG 2>&1; then SHELLVAR_LS_ARGS="-f $SHELLVAR_LS_ARGS" fi if ls -d -H . >>$SHELLVAR_LOG 2>&1; then SHELLVAR_LS_ARGS_FOLLOW="$SHELLVAR_LS_ARGS -H" elif ls -d -L . >>$SHELLVAR_LOG 2>&1; then SHELLVAR_LS_ARGS_FOLLOW="$SHELLVAR_LS_ARGS -L" else SHELLVAR_LS_ARGS_FOLLOW="$SHELLVAR_LS_ARGS" fi # using dd requires also expr command if dd iflag=fullblock skip=16 count=16 bs=16 if=/dev/zero 2>>$SHELLVAR_LOG >/dev/null && [ `expr 1 + 2` = '3' ] ; then SHELLVAR_DD=Y fi if [ "`echo 'helloworld' | ( head -c 5 > /dev/null 2>>$SHELLVAR_LOG; cat )`" = world ]; then SHELLVAR_HEAD=Y elif [ "`echo 'helloworld' | head -c 5 2>>$SHELLVAR_LOG`" = hello ]; then # head is here but it reads stdin with buffering, so let # peer know about this so it will pad each unaligned send with \n SHELLVAR_WRITE_BLOCK=`head -c 131072 /dev/zero | ( head -c 1 > /dev/null; cat ) | wc -c` SHELLVAR_WRITE_BLOCK=`expr 131072 - $SHELLVAR_WRITE_BLOCK` [ $SHELLVAR_WRITE_BLOCK -le 65536 ] && [ $SHELLVAR_WRITE_BLOCK -gt 1 ] && SHELLVAR_HEAD=Y SHELLVAR_WRITE_BLOCK="$SHELLVAR_WRITE_BLOCK*`expr 2097152 / $SHELLVAR_WRITE_BLOCK`" fi [ "`echo aGVsbG8K | base64 -d 2>>$SHELLVAR_LOG`" = hello ] && SHELLVAR_BASE64=Y #debug #SHELLVAR_STAT= #SHELLVAR_FIND= #SHELLVAR_DD= #SHELLVAR_HEAD= #SHELLVAR_BASE64= #echo "SHELLVAR_LS_ARGS=$SHELLVAR_LS_ARGS" SHELLVAR_FEATS= if [ -n "$SHELLVAR_STAT" ]; then SHELLVAR_FEATS="${SHELLVAR_FEATS}STAT " elif [ -n "$SHELLVAR_FIND" ]; then SHELLVAR_FEATS="${SHELLVAR_FEATS}FIND " else SHELLVAR_FEATS="${SHELLVAR_FEATS}LS " fi if [ -n "$SHELLVAR_DD" ]; then SHELLVAR_FEATS="${SHELLVAR_FEATS}READ_RESUME WRITE_RESUME " elif [ -n "$SHELLVAR_HEAD" ]; then SHELLVAR_FEATS="${SHELLVAR_FEATS}READ WRITE_RESUME " [ -n "$SHELLVAR_WRITE_BLOCK" ] && SHELLVAR_FEATS="${SHELLVAR_FEATS}WRITE_BLOCK=$SHELLVAR_WRITE_BLOCK " elif [ -n "$SHELLVAR_BASE64" ]; then SHELLVAR_FEATS="${SHELLVAR_FEATS}READ WRITE_RESUME WRITE_BASE64 " else SHELLVAR_FEATS="${SHELLVAR_FEATS}READ " fi echo;echo;echo; SHELLVAR_NOPROMPT= while true; do [ ! -n "$SHELLVAR_NOPROMPT" ] && echo && echo '>(((^>' SHELLVAR_NOPROMPT= $SHELLVAR_READ_FN SHELLVAR_CMD SHELLVAR_ARG || exit case "$SHELLVAR_CMD" in feats ) echo "FEATS ${SHELLVAR_FEATS} SHELL.FAR2L";; enum ) SHELLFCN_CMD_ENUM;; linfo ) SHELLFCN_CMD_INFO_SINGLE '0' "$SHELLVAR_STAT_FMT_INFO" "$SHELLVAR_FIND_FMT_INFO" '';; info ) SHELLFCN_CMD_INFO_SINGLE '1' "$SHELLVAR_STAT_FMT_INFO" "$SHELLVAR_FIND_FMT_INFO" '';; lsize ) SHELLFCN_CMD_INFO_SINGLE '0' "$SHELLVAR_STAT_FMT_SIZE" "$SHELLVAR_FIND_FMT_SIZE" 'n n n n y n';; size ) SHELLFCN_CMD_INFO_SINGLE '1' "$SHELLVAR_STAT_FMT_SIZE" "$SHELLVAR_FIND_FMT_SIZE" 'n n n n y n';; lmode ) SHELLFCN_CMD_INFO_SINGLE '0' "$SHELLVAR_STAT_FMT_MODE" "$SHELLVAR_FIND_FMT_MODE" 'y n n n n n';; mode ) SHELLFCN_CMD_INFO_SINGLE '1' "$SHELLVAR_STAT_FMT_MODE" "$SHELLVAR_FIND_FMT_MODE" 'y n n n n n';; lmodes ) SHELLFCN_CMD_INFO_MULTI '0' "$SHELLVAR_STAT_FMT_MODE" "$SHELLVAR_FIND_FMT_MODE" 'y n n n n n';; modes ) SHELLFCN_CMD_INFO_MULTI '1' "$SHELLVAR_STAT_FMT_MODE" "$SHELLVAR_FIND_FMT_MODE" 'y n n n n n';; read ) SHELLFCN_CMD_READ;; write ) SHELLFCN_CMD_WRITE;; rmfile ) SHELLFCN_CMD_REMOVE_FILE;; rmdir ) SHELLFCN_CMD_REMOVE_DIR;; mkdir ) SHELLFCN_CMD_CREATE_DIR;; rename ) SHELLFCN_CMD_RENAME;; chmod ) SHELLFCN_CMD_SET_MODE;; rdsym ) SHELLFCN_CMD_READ_SYMLINK;; mksym ) SHELLFCN_CMD_MAKE_SYMLINK;; exec ) SHELLFCN_CMD_EXECUTE;; # Special cases: casual 'abort' or 'cont' could be from cancelled read operation, so silently skip them abort ) echo 'Odd abort' >>$SHELLVAR_LOG; SHELLVAR_NOPROMPT=Y;; cont ) echo 'Odd cont' >>$SHELLVAR_LOG; SHELLVAR_NOPROMPT=Y;; noop ) ;; exit ) echo '73!'; exit 0; break;; # Another special case - if its part of initial sequence supposed to be sent to shell # - lets mimic shell's response so negotiation sequence will continue. # sleep 3 ensures 'fishy' prompt will be printed at the right time moment echo ) echo; echo 'far2l is ready for fishing'; sleep 3;; * ) echo "Bad CMD='$SHELLVAR_CMD'" >>$SHELLVAR_LOG; echo "??? '$SHELLVAR_CMD'";; esac done far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Helpers/ways.ini000066400000000000000000000023031477431623700242570ustar00rootroot00000000000000[SSH] Command="ssh $OPT0 -p "$PORT" "$USER@$HOST" "$OPT1"" OPT0_Name="Compression" OPT0_Items="{No:}|Yes:-C" OPT1_Name="Login with sudo" OPT1_Items="{No:echo 'far2l is ready for fishing'; HISTCONTROL=ignorespace sh}|Yes:sudo -S sh -c 'echo far2l is ready for fishing; HISTCONTROL=ignorespace sh'" [SSH/LOGIN] "far2l is ready for fishing\n"=^ "*password: "="$PASSWORD\n" "*Enter passphrase for key*"="$PASSWORD\n" "*password for user: "="$PASSWORD\r\n" "Are you sure*"="yes\n" "Host key verification failed"=^STDERR "Permission denied*\n"=^AUTH "Sorry, try again.*\n"=^AUTH [SERIAL] Serial="$HOST" OPT0_Name="Baudrate" OPT0_Items="50|150|300|600|1200|2400|4800|9600|19200|38400|57600|{115200}|230400|460800|500000|576000|921600|1000000|1152000|1500000|2000000|2500000|3000000|3500000|4000000" OPT1_Name="Flow control" OPT1_Items="{None}|Xon/Xoff|RTS/CTS" OPT2_Name="Data bits" OPT2_Items="5|6|7|{8}" OPT3_Name="Stop bits" OPT3_Items="{1}|2" OPT4_Name="Parity" OPT4_Items="{None}|Odd|Even" [SERIAL/LOGIN] ""="\n [ -n "$SAVED_STTY" ] || SAVED_STTY=`stty -g`; stty raw -echo; ( echo 'echo; echo; echo; echo; echo far2l is ready for fishing'; cat ) | sh | cat; stty $SAVED_STTY\necho\n" "far2l is ready for fishing\n"=^ far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Parse.cpp000066400000000000000000000112551477431623700227550ustar00rootroot00000000000000#include #include #include #include "Parse.h" #include "../ShellParseUtils.h" #include "../Protocol.h" static std::string FetchTail(std::string &line) { return ShellParseUtils::ExtractStringTail(line, " \t"); } static std::string FetchHead(std::string &line) { return ShellParseUtils::ExtractStringHead(line, " \t"); } //// static void SHELLParseLSPermissions(FileInfo &fi, const char *line, size_t line_len) { // drwxr-xr-x 0 0 fi.mode = ShellParseUtils::Str2Mode(line, line_len); for (size_t i = line_len, j = line_len; i--;) { if (line[i] == ' ' || line[i] == '.') { if (j > i) { if (fi.group.empty()) { fi.group.assign(&line[i] + 1, j - i - 1); } else { fi.owner.assign(&line[i] + 1, j - i - 1); break; } j = i; } } } } static unsigned int ParseModeByStatOrFindLine(const std::string &s) { bool hexadec = !s.empty(); for (const auto &c : s) { if ( (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { hexadec = false; break; } } if (hexadec) { return strtoul(s.c_str(), nullptr, 16); } return ShellParseUtils::Str2Mode(s.c_str(), s.size()); } static bool SHELLParseEnumByStatOrFindLine(FileInfo &fi, std::string line) { StrTrim(line, " \t\n"); // NAME MODE SIZE TIME_ACC TIME_MOD TIME_ST OWNER GROUP //. 41ed 4096 1694886719 1691156361 1691156361 root root fi.group = FetchTail(line); fi.owner = FetchTail(line); fi.status_change_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.modification_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.access_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.size = (uint64_t)strtoull(FetchTail(line).c_str(), nullptr, 10); fi.mode = ParseModeByStatOrFindLine(FetchTail(line)); StrTrim(line, " \t\n"); const size_t p = line.rfind('/'); if (p != std::string::npos) { fi.path = line.substr(p + 1); } else { fi.path.swap(line); } return !fi.path.empty() && fi.path != "." && fi.path != ".."; } void SHELLParseEnumByStatOrFind(std::vector &files, const std::vector &lines) { for (const auto &line : lines) { if (!line.empty()) { files.emplace_back(); if (!SHELLParseEnumByStatOrFindLine(files.back(), line)) { files.pop_back(); } } } } void SHELLParseInfoByStatOrFind(FileInformation &fi, std::string &line) { StrTrim(line, " \t\n"); // MODE SIZE TIME_ACC TIME_MOD TIME_ST // 41ed 4096 1694886719 1691156361 1691156361 fi.status_change_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.modification_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.access_time = timespec{(time_t)strtoull(FetchTail(line).c_str(), nullptr, 10), 0}; fi.size = (uint64_t)strtoull(FetchTail(line).c_str(), nullptr, 10); fi.mode = ParseModeByStatOrFindLine(FetchTail(line)); } uint64_t SHELLParseSizeByStatOrFind(std::string &line) { StrTrim(line, " \t\n"); return (uint64_t)strtoull(FetchTail(line).c_str(), nullptr, 10); } uint32_t SHELLParseModeByStatOrFind(std::string &line) { StrTrim(line, " \t\n"); return ParseModeByStatOrFindLine(FetchTail(line)); } //////////////// // drwxr-xr-x 2 root root 73728 Sep 16 06:59 "/bin" void SHELLParseEnumByLS(std::vector &files, const std::vector &lines) { for (std::string line : lines) { files.emplace_back(); auto &fi = files.back(); StrTrim(line, " \t\n"); if (!ShellParseUtils::ParseLineFromLS(line, fi.path, fi.owner, fi.group, fi.access_time, fi.modification_time, fi.status_change_time, fi.size, fi.mode) || !FILENAME_ENUMERABLE(fi.path)) { files.pop_back(); } } } void SHELLParseInfoByLS(FileInformation &fi, std::string &line) { StrTrim(line, " \t\n"); std::string name, owner, group; ShellParseUtils::ParseLineFromLS(line, name, owner, group, fi.access_time, fi.modification_time, fi.status_change_time, fi.size, fi.mode); } uint64_t SHELLParseSizeByLS(std::string &line) { StrTrim(line, " \t\n"); return (uint64_t)strtoull(line.c_str(), nullptr, 10); } uint32_t SHELLParseModeByLS(std::string &line) { StrTrim(line, " \t\n"); return ParseModeByStatOrFindLine(line); } void AppendTrimmedLines(std::string &s, const std::vector &lines) { for (auto line : lines) { StrTrim(line, " \t\n\r"); if (!line.empty()) { if (!s.empty()) { s+= '\n'; } s+= line; } } } void Substitute(std::string &str, const char *pattern, const std::string &replacement) { for (size_t ofs = 0; ofs < str.size();) { const size_t p = str.find(pattern, ofs); if (p == std::string::npos) { break; } str.replace(p, strlen(pattern), replacement); ofs = p + replacement.size(); } } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Parse.h000066400000000000000000000020131477431623700224120ustar00rootroot00000000000000#pragma once #include #include #include #include "../../FileInformation.h" #include struct FileInfo { mode_t mode{0}; unsigned long long size{0}; std::string owner; std::string group; std::string path; timespec access_time{}, modification_time{}, status_change_time{}; }; void SHELLParseEnumByStatOrFind(std::vector &files, const std::vector &lines); void SHELLParseInfoByStatOrFind(FileInformation &fi, std::string &line); uint64_t SHELLParseSizeByStatOrFind(std::string &line); uint32_t SHELLParseModeByStatOrFind(std::string &line); void SHELLParseEnumByLS(std::vector &files, const std::vector &lines); void SHELLParseInfoByLS(FileInformation &fi, std::string &line); uint64_t SHELLParseSizeByLS(std::string &line); uint32_t SHELLParseModeByLS(std::string &line); void AppendTrimmedLines(std::string &s, const std::vector &lines); void Substitute(std::string &str, const char *pattern, const std::string &replacement); far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/ProtocolSHELL.cpp000066400000000000000000000523421477431623700242760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "ProtocolSHELL.h" #include "WayToShellConfig.h" #include "Request.h" #include "Parse.h" #include "RemoteSh.h" #include #include #include // для getenv #define GREETING_MSG "far2l is ready for fishing" static std::vector s_prompt{">(((^>\n"}; static std::vector s_prompt_or_error{">(((^>\n", "+ERROR:*\n"}; static std::vector s_ok_or_error{"+OK:*\n", "+ERROR:*\n"}; static std::vector s_read_replies{"+NEXT:*\n", "+DONE\n", "+FAIL\n", "+ABORTED\n", "+ERROR:*\n"}; static std::vector s_write_replies{"+OK\n", "+ERROR:*\n"}; static const char s_prefix_error[] = "+ERROR:"; static const char s_prefix_ok[] = "+OK:"; std::shared_ptr CreateProtocol(const std::string &protocol, const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) { return std::make_shared(host, port, username, password, options, fd_ipc_recv); } static uint64_t GetIntegerBeforeStatusReplyLine(const std::string &data, size_t pos) { size_t prev_line_start = pos; if (prev_line_start) do { --prev_line_start; } while (prev_line_start && data[prev_line_start] != '\n'); if (data[prev_line_start] == '\n') { ++prev_line_start; } return (uint64_t)strtoull(data.c_str() + prev_line_start, nullptr, 10); } //////////////////////////// void ProtocolSHELL::SubstituteCreds(std::string &str) { Substitute(str, "$HOST", _host); Substitute(str, "$PORT", ToDec(_port)); Substitute(str, "$USER", _username); Substitute(str, "$PASSWORD", _password); } void ProtocolSHELL::OpenWay() { _way.reset(); WayToShellConfig cfg("SHELL/ways.ini", _way_name); if (!cfg.command.empty()) { SubstituteCreds(cfg.command); } if (!cfg.serial.empty()) { SubstituteCreds(cfg.serial); } _way = std::make_shared(_fd_ipc_recv, cfg, _protocol_options); fprintf(stderr, "[SHELL] WAY OPENED\n"); } void ProtocolSHELL::PerformLogin() { KeyFileReadSection rules("SHELL/ways.ini", _way_name + "/LOGIN"); if (rules.empty()) { fprintf(stderr, "[SHELL] LOGIN NOT REQUIRED\n"); return; } std::vector replies; for (const auto &it : rules) { if (it.first.empty()) { // special case - initial string std::string line = it.second; SubstituteCreds(line); _way->Send(line); } else { replies.emplace_back(it.first.c_str()); } } if (replies.empty()) { fprintf(stderr, "[SHELL] NON-INTERACTIVE LOGIN DONE\n"); return; } auto wr = _way->WaitReply(replies); for (;;) { std::string reply = rules.GetString(replies[wr.index]); if (reply == "^") { break; } if (reply == "^STDERR" || reply == "^STDOUT" || reply == "^STD") { std::string s; if (reply == "^STDERR" || reply == "^STD") { AppendTrimmedLines(s, wr.stderr_lines); } if (reply == "^STDOUT" || reply == "^STD") { AppendTrimmedLines(s, wr.stdout_lines); } throw ProtocolError(s); } if (reply == "^AUTH") { throw ProtocolAuthFailedError(); } SubstituteCreds(reply); wr = _way->SendAndWaitReply(reply, replies); } fprintf(stderr, "[SHELL] INTERACTIVE LOGIN DONE\n"); } void ProtocolSHELL::ParseFeatsLine(const std::string &feats_line) { _feats.using_stat = (feats_line.find(" STAT ") != std::string::npos); _feats.using_find = (feats_line.find(" FIND ") != std::string::npos); _feats.using_ls = (feats_line.find(" LS ") != std::string::npos); if (feats_line.find(" READ_RESUME ") != std::string::npos) { _feats.support_read = _feats.support_read_resume = true; } if (!_feats.support_read && feats_line.find(" READ ") != std::string::npos) { _feats.support_read = true; } if (feats_line.find(" WRITE_RESUME ") != std::string::npos) { _feats.support_write = _feats.support_write_resume = true; } if (feats_line.find(" WRITE_BASE64 ") != std::string::npos) { _feats.support_write = _feats.require_write_base64 = true; } if (!_feats.support_write && feats_line.find(" WRITE ") != std::string::npos) { _feats.support_write = true; } size_t p = feats_line.find(" WRITE_BLOCK="); if (p != std::string::npos) { size_t require_write_block = atoi(feats_line.c_str() + p + 13); _feats.require_write_block = require_write_block; const size_t px = feats_line.find('*', p + 13); if (px != std::string::npos && px < feats_line.find(' ', p + 13)) { _feats.limit_max_blocks = atoi(feats_line.c_str() + px + 1); } } } void ProtocolSHELL::Initialize() { _way_name = _protocol_options.GetString("Way"); if (_way_name.empty()) { // defaults to very first root section in config file WaysToShell ways("SHELL/ways.ini"); if (!ways.empty()) { _way_name = ways.front(); } if (_way_name.empty()) { throw ProtocolError("Way not specified"); } } fprintf(stderr, "[SHELL] INITIALIZE: '%s'\n", _way_name.c_str()); if (g_netrocks_verbosity > 0) { fprintf(stderr, "[SHELL] host: '%s'\n", _host.c_str()); fprintf(stderr, "[SHELL] port: %i\n", _port); fprintf(stderr, "[SHELL] username: '%s'\n", _username.c_str()); if (g_netrocks_verbosity > 1) { fprintf(stderr, "[SHELL] password: {%lu}\n", _password.size()); } } OpenWay(); PerformLogin(); auto wr = _way->SendAndWaitReply(LoadRemoteSh("SHELL/remote.sh"), s_prompt); fprintf(stderr, "[SHELL] REMOTE READY\n"); wr = _way->SendAndWaitReply("feats .\n", s_prompt); if (wr.stdout_lines.size() > 1) { for (size_t i = wr.stdout_lines.size() - 1; i--; ) { //INFO ${INFO} SHELL.FAR2L auto &feats_line = wr.stdout_lines[i]; if (StrStartsFrom(feats_line, "FEATS ") && StrEndsBy(feats_line, " SHELL.FAR2L\n")) { ParseFeatsLine(feats_line); break; } } } fprintf(stderr, "[SHELL] stat=%u find=%u ls=%u read=%u r/resume:%u write:%u w/resume:%u w/base64:%u w/block:%u*%u\n", _feats.using_stat, _feats.using_find, _feats.using_ls, _feats.support_read, _feats.support_read_resume, _feats.support_write, _feats.support_write_resume, _feats.require_write_base64, _feats.require_write_block, _feats.limit_max_blocks); } ProtocolSHELL::ProtocolSHELL(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &options, int fd_ipc_recv) : _fd_ipc_recv(fd_ipc_recv), _protocol_options(options), _host(host), _port(port), _username(username), _password(password) { if (_username.empty()) { const char *user = getenv("USER"); _username = (user && *user) ? user : "root"; } Initialize(); } ProtocolSHELL::~ProtocolSHELL() { fprintf(stderr, "[SHELL] ProtocolSHELL::%s\n", __FUNCTION__); _exec_cmd.reset(); } void ProtocolSHELL::FinalizeExecCmd() { if (_exec_cmd) { const bool broken = _exec_cmd->IsBroken(); _exec_cmd.reset(); if (broken) { fprintf(stderr, "[SHELL] BROKEN\n"); // Rather stupid workaround: just restart _way. // This may cause some amusements, // like auth errors in inconvinient moment of life. // But world is not ideal. _way.reset(); Initialize(); } } } void ProtocolSHELL::KeepAlive(const std::string &path_to_check) { if (_exec_cmd) { if (!_exec_cmd->KeepAlive()) { FinalizeExecCmd(); } } if (!_exec_cmd) { _way->SendAndWaitReply("noop .\n", s_prompt); } } void ProtocolSHELL::GetModes(bool follow_symlink, size_t count, const std::string *pathes, mode_t *modes) noexcept { if (count) try { FinalizeExecCmd(); fprintf(stderr, "[SHELL] ProtocolSHELL::GetModes follow_symlink=%d count=%lu\n", follow_symlink, (unsigned long)count); Request request(follow_symlink ? "modes " : "lmodes "); request.AddFmt("%lu\n", (unsigned long)count); for (size_t i = 0; i < count;) { switch (std::min(count - i, (size_t)8)) { case 8: request.Add(pathes[i++], ' '); // fallthrough case 7: request.Add(pathes[i++], ' '); // fallthrough case 6: request.Add(pathes[i++], ' '); // fallthrough case 5: request.Add(pathes[i++], ' '); // fallthrough case 4: request.Add(pathes[i++], ' '); // fallthrough case 3: request.Add(pathes[i++], ' '); // fallthrough case 2: request.Add(pathes[i++], ' '); // fallthrough case 1: request.Add(pathes[i++], '\n'); break; } } auto wr = _way->SendAndWaitReply(request, s_prompt); ASSERT(wr.stdout_lines.size()); for (size_t i = count, j = wr.stdout_lines.size() - 1; i && j;) { auto &line = wr.stdout_lines[--j]; if (StrStartsFrom(line, s_prefix_ok)) { line.erase(0, sizeof(s_prefix_ok) - 1); if (_feats.using_stat || _feats.using_find) { modes[--i] = SHELLParseModeByStatOrFind(line); } else { modes[--i] = SHELLParseModeByLS(line); } }else if (StrStartsFrom(line, s_prefix_error)) { modes[--i] = ~(mode_t)0; } else { fprintf(stderr, "[SHELL] ProtocolSHELL::GetModes: skip - %s\n", line.c_str()); } } } catch (...) { fprintf(stderr, "[SHELL] ProtocolSHELL::GetModes excpt\n"); for (size_t i = 0; i != count; ++i) { modes[i] = ~(mode_t)0; } } } void ProtocolSHELL::GetSingleFileInfo(const char *what, const std::string &path) { fprintf(stderr, "[SHELL] ProtocolSHELL::%s: %s for '%s'\n", __FUNCTION__, what, path.c_str()); FinalizeExecCmd(); const auto &wr = _way->SendAndWaitReply( Request(what).Add(path, '\n'), s_prompt ); size_t i = wr.stdout_lines.size(); if (i--) while (i--) { const auto &line = wr.stdout_lines[i]; size_t n = line.size(); while (line[n - 1] == '\r' || line[n - 1] == '\n') { --n; } if (StrStartsFrom(line, s_prefix_ok)) { _single_line_info.assign(line.data() + sizeof(s_prefix_ok) - 1, n - (sizeof(s_prefix_ok) - 1)); return; } if (StrStartsFrom(line, s_prefix_error)) { _single_line_info.assign(line.data() + sizeof(s_prefix_error) - 1, n - (sizeof(s_prefix_error) - 1)); throw ProtocolError(what, _single_line_info.c_str()); } } throw ProtocolError(what, "unreplied"); } mode_t ProtocolSHELL::GetMode(const std::string &path, bool follow_symlink) { GetSingleFileInfo(follow_symlink ? "mode " : "lmode ", path); if (_feats.using_stat || _feats.using_find) { return SHELLParseModeByStatOrFind(_single_line_info); } return SHELLParseModeByLS(_single_line_info); } unsigned long long ProtocolSHELL::GetSize(const std::string &path, bool follow_symlink) { GetSingleFileInfo(follow_symlink ? "size " : "lsize ", path); if (_feats.using_stat || _feats.using_find) { return SHELLParseSizeByStatOrFind(_single_line_info); } return SHELLParseSizeByLS(_single_line_info); } void ProtocolSHELL::GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink) { GetSingleFileInfo(follow_symlink ? "info " : "linfo ", path); if (_feats.using_stat || _feats.using_find) { SHELLParseInfoByStatOrFind(file_info, _single_line_info); } else { SHELLParseInfoByLS(file_info, _single_line_info); } } void ProtocolSHELL::SendAndWaitPromptOrError(const char *what, const std::string &request) { fprintf(stderr, "[SHELL] ProtocolSHELL::%s - %s\n", __FUNCTION__, what); FinalizeExecCmd(); auto wr = _way->SendAndWaitReply(request, s_prompt_or_error); if (wr.index != 0) { _way->WaitReply(s_prompt); auto &line = (wr.output_type == STDERR) ? wr.stderr_lines.back() : wr.stdout_lines.back(); StrTrim(line, " \t\r\n"); if (StrStartsFrom(line, s_prefix_error)) { line.erase(0, sizeof(s_prefix_error) - 1); } if (line.empty()) { line = "failed"; } throw ProtocolError(what, line.c_str()); } } void ProtocolSHELL::FileDelete(const std::string &path) { SendAndWaitPromptOrError("rmfile", Request("rmfile ").Add(path, '\n') ); } void ProtocolSHELL::DirectoryDelete(const std::string &path) { SendAndWaitPromptOrError("rmdir", Request("rmdir ").Add(path, '\n') ); } void ProtocolSHELL::DirectoryCreate(const std::string &path, mode_t mode) { SendAndWaitPromptOrError("mkdir", Request("mkdir ").Add(path, '\n').AddFmt("0%o\n", mode & 0777) ); } void ProtocolSHELL::Rename(const std::string &path_old, const std::string &path_new) { SendAndWaitPromptOrError("rename", Request("rename ").Add(path_old, '\n').Add(path_new, '\n') ); } void ProtocolSHELL::SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time) { fprintf(stderr, "[SHELL] ProtocolSHELL::%s\n", __FUNCTION__); FinalizeExecCmd(); } void ProtocolSHELL::SetMode(const std::string &path, mode_t mode) { SendAndWaitPromptOrError("chmod", Request("chmod ").Add(path, '\n').AddFmt("0%o\n", mode & 0777) ); } void ProtocolSHELL::SymlinkCreate(const std::string &link_path, const std::string &link_target) { SendAndWaitPromptOrError("mksym", Request("mksym ").Add(link_path, '\n').Add(link_target, '\n') ); } void ProtocolSHELL::SymlinkQuery(const std::string &link_path, std::string &link_target) { GetSingleFileInfo("rdsym ", link_path); link_target.swap(_single_line_info); } class SHELLDirectoryEnumer : public IDirectoryEnumer { private: std::shared_ptr _way; std::vector _files; size_t _index = 0; public: SHELLDirectoryEnumer(std::shared_ptr &app, const std::string &path, const RemoteFeats &feats) : _way(app) { auto wr = _way->SendAndWaitReply( Request("enum ").Add(path, '\n'), s_prompt_or_error ); if (wr.index != 0) { _way->WaitReply(s_prompt); throw ProtocolError("dir query error"); } if (feats.using_stat || feats.using_find) { wr.stdout_lines.pop_back(); // get rid of reply SHELLParseEnumByStatOrFind(_files, wr.stdout_lines); } else { wr.stdout_lines.pop_back(); // get rid of reply SHELLParseEnumByLS(_files, wr.stdout_lines); } fprintf(stderr, "[SHELL] LIST READ\n"); } virtual bool Enum(std::string &name, std::string &owner, std::string &group, FileInformation &file_info) override { const FileInfo *file; do { if (_index >= _files.size()) { return false; } file = &_files[_index++]; name = file->path; if (S_ISLNK(file->mode)) { size_t p = name.rfind(" -> "); if (p != std::string::npos) { name.resize(p); } } // if (_unquote && name.size() > 2 && name.front() == '\"' && name.back() == '\"') { // name.pop_back(); // name.erase(0, 1); // } } while (!FILENAME_ENUMERABLE(name)); owner = file->owner; group = file->group; file_info.access_time = file->access_time; file_info.modification_time = file->modification_time; file_info.status_change_time = file->status_change_time; file_info.size = file->size; file_info.mode = file->mode; return true; } }; std::shared_ptr ProtocolSHELL::DirectoryEnum(const std::string &path) { fprintf(stderr, "[SHELL] Enum '%s'\n", path.c_str()); FinalizeExecCmd(); return std::shared_ptr(new SHELLDirectoryEnumer(_way, path, _feats)); } class SHELLFileReader : public IFileReader { std::shared_ptr _way; size_t _chunk {0}; bool _finished{false}; bool _aborting{false}; void RecvChunkHeader() { WaitResult wr = _way->SendAndWaitReply(_aborting ? "abort\n" : "cont\n", s_read_replies); //{"+NEXT:*\n", "+DONE\n", "+FAIL\n", "+ABORTED\n", "+ERROR:*\n"}; if (wr.index == 4) { std::string resync = wr.stdout_lines.back(); resync.replace(0, 1, "SHELL_RESYNCHRONIZATION_"); _way->Send(resync); _finished = true; throw ProtocolError("read error"); } if (wr.index == 0) { _chunk = (size_t)strtoul(wr.stdout_lines.back().c_str() + 6, nullptr, 10); } else { _chunk = 0; } if (!_chunk) { _finished = true; } } public: SHELLFileReader(std::shared_ptr &app, const std::string &path, unsigned long long resume_pos) : _way(app) { _way->Send( Request("read ").Add(path, '\n').AddFmt("%llu\n", resume_pos).Add("cont", '\n') ); } virtual ~SHELLFileReader() { _aborting = true; try { while (!_finished) { char tmp[0x1000]; Read(tmp, sizeof(tmp)); } } catch (std::exception &e) { fprintf(stderr, "[SHELL] ~SHELLFileReader: read %s\n", e.what()); } catch (...) { fprintf(stderr, "[SHELL] ~SHELLFileReader: read ...\n"); } try { _way->WaitReply(s_prompt); } catch (std::exception &e) { fprintf(stderr, "[SHELL] ~SHELLFileReader: prompt %s\n", e.what()); } catch (...) { fprintf(stderr, "[SHELL] ~SHELLFileReader: prompt ...\n"); } } virtual size_t Read(void *buf, size_t len) { size_t rv = 0; while (rv < len && !_finished) { if (!_chunk) { RecvChunkHeader(); if (_finished) { break; } } const size_t piece = std::min(len - rv, _chunk); _way->ReadStdout((char *)buf + rv, piece); rv+= piece; _chunk-= piece; } return rv; } }; class SHELLFileWriter : public IFileWriter { std::shared_ptr _way; RemoteFeats _feats; unsigned int _pending_replies{0}; bool _write_completed{false}; std::vector _padded_data; size_t _seq{0}; void WaitReply() { --_pending_replies; const auto &wr = _way->WaitReply(s_write_replies); // {"+OK\n", "+ERROR:*\n"}; if (wr.index == 1) { std::string resync = wr.stdout_lines.back(); resync.replace(0, 1, "SHELL_RESYNCHRONIZATION_"); _way->Send(resync); throw ProtocolError("write error"); } } public: SHELLFileWriter(std::shared_ptr &app, const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos, const RemoteFeats &feats) : _way(app), _feats(feats) { _way->Send( Request("write ").Add(path, '\n').AddFmt("%llu\n", resume_pos) ); ++_pending_replies; } virtual ~SHELLFileWriter() { if (!_write_completed) try { _way->Send(". .\n"); } catch (std::exception &e) { fprintf(stderr, "[SHELL] ~SHELLFileWriter: dot %s\n", e.what()); } catch (...) { fprintf(stderr, "[SHELL] ~SHELLFileWriter: dot ...\n"); } while (_pending_replies) try { WaitReply(); } catch (std::exception &e) { fprintf(stderr, "[SHELL] ~SHELLFileWriter: reply %s\n", e.what()); } catch (...) { fprintf(stderr, "[SHELL] ~SHELLFileWriter: reply ...\n"); } try { _way->WaitReply(s_prompt); } catch (std::exception &e) { fprintf(stderr, "[SHELL] ~SHELLFileReader: prompt %s\n", e.what()); } catch (...) { fprintf(stderr, "[SHELL] ~SHELLFileReader: prompt ...\n"); } } virtual void WriteComplete() { if (!_write_completed) { _write_completed = true; _way->Send(". .\n"); } while (_pending_replies) { WaitReply(); } } virtual void Write(const void *buf, size_t len) { if (_feats.require_write_base64) { std::string line; for (size_t ofs = 0; ofs < len; ) { // avoid too long lines cuz receiver should fit it into env variable const size_t piece = std::min(size_t(0x1000), len - ofs); line.clear(); base64_encode(line, (const unsigned char *)buf + ofs, piece); line+= '\n'; WritePiece(piece, line.data(), line.size()); ofs+= piece; } } else if (_feats.require_write_block > 1) { size_t ofs = 0; for (;;) { size_t piece = ((len - ofs) / _feats.require_write_block) * _feats.require_write_block; if (piece == 0) { break; } if (_feats.limit_max_blocks && piece > _feats.require_write_block * _feats.limit_max_blocks) { piece = _feats.require_write_block * _feats.limit_max_blocks; } WritePiece(piece, (const char *)buf + ofs, piece); ofs+= piece; } _padded_data.resize(_feats.require_write_block); // Pad data with extra spaces finalized with \n and with amount that ensures // total sent size aligned by require_write_block plus extra require_write_block. // This is to workaround issue with head of busybox that consumes more than it was // asked for due to it uses stdin that has internal buffer. // Note that remote sh will handle this by skipping empty lines got from read until // getting unempty line with subsequent directive. if (ofs < len) { const size_t piece = len - ofs; memcpy(_padded_data.data(), (const char *)buf + ofs, piece); memset(_padded_data.data() + piece, ' ', _feats.require_write_block - piece); WritePiece(piece, _padded_data.data(), _padded_data.size()); } memset(_padded_data.data(), ' ', _padded_data.size()); // No need as read skips leading spaces so they can arrive as part of subsequent directive // _padded_data.back() = '\n'; _way->Send(_padded_data.data(), _padded_data.size()); } else { WritePiece(len, (const char *)buf, len); } } void WritePiece(size_t target_len, const char *data, size_t len) { while (_pending_replies > 1) { WaitReply(); } ++_seq; _way->Send(StrPrintf("%lu %lu\n", (unsigned long)_seq, (unsigned long)target_len)); _way->Send(data, len); ++_pending_replies; } }; std::shared_ptr ProtocolSHELL::FileGet(const std::string &path, unsigned long long resume_pos) { FinalizeExecCmd(); if (!_feats.support_read) { throw ProtocolUnsupportedError("read unsupported"); } if (resume_pos != 0 && !_feats.support_read_resume) { throw ProtocolUnsupportedError("read-resume unsupported"); } return std::make_shared(_way, path, resume_pos); } std::shared_ptr ProtocolSHELL::FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos) { FinalizeExecCmd(); if (!_feats.support_write) { throw ProtocolUnsupportedError("write unsupported"); } if (resume_pos != 0 && !_feats.support_write_resume) { throw ProtocolUnsupportedError("write-resume unsupported"); } return std::make_shared(_way, path, mode, size_hint, resume_pos, _feats); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/ProtocolSHELL.h000066400000000000000000000072241477431623700237420ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include "../Protocol.h" #include "WayToShell.h" struct RemoteFeats { uint32_t require_write_block; // typically few Kbs uint32_t limit_max_blocks; bool using_stat : 1; bool using_find : 1; bool using_ls : 1; bool support_read : 1; bool support_read_resume : 1; bool support_write : 1; bool support_write_resume : 1; bool require_write_base64 : 1; }; class ProtocolSHELL : public IProtocol { class ExecCmd : protected Threaded { bool _broken{false}; std::string _working_dir; std::string _command_line; std::string _fifo; FDScope _fdinout, _fderr; int _kickass[2] {-1, -1}; struct MarkerTrack { std::string marker; bool done{false}; int status{-1}; void Inspect(const char *append, ssize_t &len); private: std::string _tail; bool _matched{false}; } _marker_track; void Abort(); void OnReadFDCtl(int fd); void IOLoop(); virtual void *ThreadProc(); public: ExecCmd(std::shared_ptr &app, const std::string &working_dir, const std::string &command_line, const std::string &fifo); ~ExecCmd(); bool KeepAlive(); inline bool IsBroken() const { return _broken; } }; std::shared_ptr _way; pid_t _pid{0}; RemoteFeats _feats{}; int _fd_ipc_recv{-1}; StringConfig _protocol_options; std::string _host; unsigned int _port{0}; std::string _username; std::string _password; std::string _way_name; std::unique_ptr _exec_cmd; std::string _single_line_info; void SubstituteCreds(std::string &str); void OpenWay(); void PerformLogin(); void ParseFeatsLine(const std::string &feats_line); void FinalizeExecCmd(); void Initialize(); void GetSingleFileInfo(const char *what, const std::string &path); void SendAndWaitPromptOrError(const char *what, const std::string &request); public: ProtocolSHELL(const std::string &host, unsigned int port, const std::string &username, const std::string &password, const std::string &protocol_options, int fd_ipc_recv); virtual ~ProtocolSHELL(); virtual void KeepAlive(const std::string &path_to_check); virtual void GetModes(bool follow_symlink, size_t count, const std::string *pathes, mode_t *modes) noexcept; virtual mode_t GetMode(const std::string &path, bool follow_symlink = true); virtual unsigned long long GetSize(const std::string &path, bool follow_symlink = true); virtual void GetInformation(FileInformation &file_info, const std::string &path, bool follow_symlink = true); virtual void FileDelete(const std::string &path); virtual void DirectoryDelete(const std::string &path); virtual void DirectoryCreate(const std::string &path, mode_t mode); virtual void Rename(const std::string &path_old, const std::string &path_new); virtual void SetTimes(const std::string &path, const timespec &access_time, const timespec &modification_time); virtual void SetMode(const std::string &path, mode_t mode); virtual void SymlinkCreate(const std::string &link_path, const std::string &link_target); virtual void SymlinkQuery(const std::string &link_path, std::string &link_target); virtual std::shared_ptr DirectoryEnum(const std::string &path); virtual std::shared_ptr FileGet(const std::string &path, unsigned long long resume_pos = 0); virtual std::shared_ptr FilePut(const std::string &path, mode_t mode, unsigned long long size_hint, unsigned long long resume_pos = 0); virtual void ExecuteCommand(const std::string &working_dir, const std::string &command_line, const std::string &fifo); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/ProtocolSHELL_ExecCmd.cpp000066400000000000000000000152761477431623700256730ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "ProtocolSHELL.h" #include "Request.h" // very poor for now void ProtocolSHELL::ExecCmd::OnReadFDCtl(int fd) { ExecFIFO_CtlMsg m; if (ReadAll(fd, &m, sizeof(m)) != sizeof(m)) { throw std::runtime_error("ctl read failed"); } switch (m.cmd) { case ExecFIFO_CtlMsg::CMD_PTY_SIZE: { // m.u.pty_size.cols, m.u.pty_size.rows } break; case ExecFIFO_CtlMsg::CMD_SIGNAL: { if (m.u.signum == SIGKILL || m.u.signum == SIGINT || m.u.signum == SIGQUIT) { char c = 0; if (os_call_ssize(write, _kickass[1], (const void*)&c, sizeof(c)) != 1) { perror("~ExecFIFO_CtlMsg::CMD_SIGNAL: write kickass"); } } // SendSignal(m.u.signum); } break; } } void ProtocolSHELL::ExecCmd::MarkerTrack::Inspect(const char *append, ssize_t &len) { size_t tail_len_before = _tail.size(); _tail.append(append, len); if (_matched) { len = 0; } for (;;) { size_t p = _tail.find(marker); if (p == std::string::npos) { break; } if (!_matched) { _matched = true; len = p - tail_len_before; ASSERT(len >= 0); status = atoi(_tail.c_str() + p + marker.size()); } else { done = true; break; } } if (_tail.size() > marker.size()) { _tail.erase(0, _tail.size() - marker.size()); } } static void WriteAllCRLF(int fd, char *data, size_t len) { for (size_t i = 0, j = 0; ; ++i) { if (i == len || data[i] == '\n') { WriteAll(fd, &data[j], i - j); if (i != len) { WriteAll(fd, "\r\n", 2); } if (i == len) { break; } j = i + 1; } } } void ProtocolSHELL::ExecCmd::IOLoop() { FDScope fd_err(open((_fifo + ".err").c_str(), O_WRONLY | O_CLOEXEC)); FDScope fd_out(open((_fifo + ".out").c_str(), O_WRONLY | O_CLOEXEC)); FDScope fd_in(open((_fifo + ".in").c_str(), O_RDONLY | O_CLOEXEC)); FDScope fd_ctl(open((_fifo + ".ctl").c_str(), O_RDONLY | O_CLOEXEC)); if (!fd_err.Valid() || !fd_out.Valid() || !fd_in.Valid() || !fd_ctl.Valid()) throw ProtocolError("fifo"); // get PTY size that is sent immediately OnReadFDCtl(fd_ctl); MakeFDNonBlocking(_fdinout); const int maxfd = std::max((int)fd_in, std::max((int)fd_ctl, std::max((int)_fdinout, std::max((int)_fderr, _kickass[0])))); for (unsigned int idle = 0; !_marker_track.done;) { char buf[0x1000]; fd_set fdr, fde; FD_ZERO(&fdr); FD_ZERO(&fde); FD_SET(fd_in, &fdr); // data from local side FD_SET(fd_ctl, &fdr); FD_SET(_fdinout, &fdr); // data from remote side FD_SET(_fderr, &fdr); // data from remote side FD_SET(_kickass[0], &fdr); FD_SET(fd_in, &fde); FD_SET(fd_ctl, &fde); FD_SET(_fdinout, &fde); FD_SET(_fderr, &fde); FD_SET(_kickass[0], &fdr); struct timeval tv = {0, (idle > 1000) ? 100000 : ((idle > 0) ? 10000 : 0)}; if (idle < 100000) { ++idle; } int r = select(maxfd + 1, &fdr, nullptr, &fde, &tv); if ( r < 0) { if (errno == EAGAIN || errno == EINTR) continue; throw std::runtime_error("select failed"); } if (FD_ISSET(_kickass[0], &fde)) { throw std::runtime_error("kickass exception"); } if (FD_ISSET(_kickass[0], &fdr)) { char c = 0; if (os_call_ssize(read, _kickass[0], (void*)&c, sizeof(c)) != 1) { throw std::runtime_error("kickass read failed"); } if (c == 0) { throw std::runtime_error("kickass-driven exit"); } // TODO: somehow keepalive } if (FD_ISSET(fd_in, &fdr)) { ssize_t rlen = read(fd_in, buf, sizeof(buf)); if (rlen <= 0) { if (errno != EAGAIN) { throw std::runtime_error("fd_in read failed"); } } else { WriteAll(_fdinout, buf, rlen); for (ssize_t i = 0; i < rlen; ++i) { if (buf[i] == 0x03 || buf[i] == 0x1c) { // hack: handle Ctrl+C by force exiting IO loop, this will leave whole connection in broken state // but its better than not to react at all leaving user with unresponsive command _marker_track.done = true; _marker_track.status = (buf[i] == 0x1c) ? 0 : SIGINT; _broken = true; } } idle = 0; } } if (FD_ISSET(fd_ctl, &fdr)) { OnReadFDCtl(fd_ctl); idle = 0; } if (FD_ISSET(_fderr, &fdr)) { ssize_t rlen = read(_fderr, buf, sizeof(buf)); if (rlen <= 0) { if (errno != EAGAIN) { throw std::runtime_error("_fderr read failed"); } } else { WriteAllCRLF(fd_err, buf, rlen); idle = 0; } } if (FD_ISSET(_fdinout, &fdr)) { ssize_t rlen = read(_fdinout, buf, sizeof(buf)); if (rlen <= 0) { if (errno != EAGAIN) { throw std::runtime_error("_fdinout read failed"); } } else { _marker_track.Inspect(buf, rlen); WriteAllCRLF(fd_out, buf, rlen); idle = 0; } } } FDScope fd_status(open((_fifo + ".status").c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600)); if (fd_status.Valid()) { WriteAll(fd_status, &_marker_track.status, sizeof(_marker_track.status)); } } void *ProtocolSHELL::ExecCmd::ThreadProc() { try { fprintf(stderr, "[SHELL] ShellExecutedCommand: ENTERING [%s]\n", _command_line.c_str()); IOLoop(); fprintf(stderr, "[SHELL] ShellExecutedCommand: LEAVING\n"); } catch (std::exception &ex) { fprintf(stderr, "[SHELL] ShellExecutedCommand: %s\n", ex.what()); } return nullptr; } ProtocolSHELL::ExecCmd::ExecCmd(std::shared_ptr &way, const std::string &working_dir, const std::string &command_line, const std::string &fifo) : _working_dir(working_dir), _command_line(command_line), _fifo(fifo) { if (pipe_cloexec(_kickass) == -1) { throw ProtocolError("pipe", errno); } MakeFDNonBlocking(_kickass[1]); int fdinout, fderr; way->GetDescriptors(fdinout, fderr); _fdinout = fdinout; _fderr = fderr; RandomStringAppend(_marker_track.marker, 32, 32, RNDF_ALNUM); way->Send( Request("exec ").Add(command_line, '\n').Add(_marker_track.marker, ' ').Add(working_dir, '\n')); _marker_track.marker.insert(0, 1, '\n'); _marker_track.marker.append(1, ':'); if (!StartThread()) { CheckedCloseFDPair(_kickass); way->WaitReply({">(((^>\n"}); throw std::runtime_error("start thread"); } } ProtocolSHELL::ExecCmd::~ExecCmd() { try { Abort(); } catch(...) { } } void ProtocolSHELL::ExecCmd::Abort() { if (!WaitThread(0)) { char c = 0; if (os_call_ssize(write, _kickass[1], (const void*)&c, sizeof(c)) != 1) { perror("~ShellExecutedCommand: write kickass"); } WaitThread(); } } bool ProtocolSHELL::ExecCmd::KeepAlive() { return !WaitThread(0); } void ProtocolSHELL::ExecuteCommand(const std::string &working_dir, const std::string &command_line, const std::string &fifo) { _exec_cmd.reset(); _exec_cmd.reset(new ExecCmd(_way, working_dir, command_line, fifo)); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/RemoteSh.cpp000066400000000000000000000044031477431623700234260ustar00rootroot00000000000000#include #include #include #include #include #include "../../Erroring.h" #include "RemoteSh.h" #define COMPACTIZE_REMOTE_SH class CompactizedTokens { std::map _m; std::string _out; public: CompactizedTokens(char heading) : _out(1, heading) { } const std::string &Compactize(const std::string &token) { auto ir = _m.emplace(token, _m.size()); size_t n = ir.first->second; _out.resize(1); while (n) { const char c = 'a' + (n % (1 + 'z' - 'a')); n/= (1 + 'z' - 'a'); _out+= c; } return _out; } size_t Count() const { return _m.size(); } }; std::string LoadRemoteSh(const char *helper) { std::ifstream helper_ifs; helper_ifs.open(helper); std::string out, tmp_str; if (!helper_ifs.is_open() ) { throw ProtocolError("can't open helper", helper, errno); } CompactizedTokens comp_funcs('F'), comp_vars('V'); size_t orig_len = 0; while (std::getline(helper_ifs, tmp_str)) { orig_len+= tmp_str.size() + 1; // do some compactization StrTrim(tmp_str); #ifdef COMPACTIZE_REMOTE_SH // skip no-code lines unless enabled logging (to keep line numbers) if (tmp_str.empty() || tmp_str.front() == '#') { if (g_netrocks_verbosity <= 0) { continue; } tmp_str.clear(); } // rename tokens named for compactization for (;;) { CompactizedTokens *ct; size_t p = tmp_str.find("SHELLVAR_"); if (p == std::string::npos) { p = tmp_str.find("SHELLFCN_"); if (p == std::string::npos) { break; } ct = &comp_funcs; } else { ct = &comp_vars; } size_t e = p + 9; while (e < tmp_str.size() && (isalnum(tmp_str[e]) || tmp_str[e] == '_')) { ++e; } tmp_str.replace(p, e - p, ct->Compactize(tmp_str.substr(p, e - p))); } #endif out+= ' '; // prepend each line with space to avoid history pollution (as HISTCONTROL=ignorespace) out+= tmp_str; out+= '\n'; } fprintf(stderr, "[SHELL] %s('%s'): %lu -> %lu bytes, renamed %lu funcs and %lu vars\n", __FUNCTION__, helper, (unsigned long)orig_len, (unsigned long)out.size(), (unsigned long)comp_funcs.Count(), (unsigned long)comp_vars.Count()); if (g_netrocks_verbosity > 2) { fprintf(stderr, "---\n"); fprintf(stderr, "%s\n", out.c_str()); fprintf(stderr, "---\n"); } return out; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/RemoteSh.h000066400000000000000000000001161477431623700230700ustar00rootroot00000000000000#pragma once #include std::string LoadRemoteSh(const char *helper); far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Request.cpp000066400000000000000000000012621477431623700233300ustar00rootroot00000000000000#include #include "Request.h" Request::Request(const char *cmd_ending) : _request(cmd_ending) { } Request &Request::Add(const std::string &arg, char ending) { for (size_t ofs = 0; ofs < arg.size();) { const size_t p = arg.find_first_of(" \t\r\n\\", ofs); if (p == std::string::npos) { _request.append(arg.data() + ofs, arg.size() - ofs); break; } _request.append(arg.data() + ofs, p - ofs); _request+= '\\'; _request+= arg[p]; ofs = p + 1; } _request+= ending; return *this; } Request &Request::AddFmt(const char *fmt_ending, ...) { va_list args; va_start(args, fmt_ending); _request+= StrPrintfV(fmt_ending, args); va_end(args); return *this; } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/Request.h000066400000000000000000000006461477431623700230020ustar00rootroot00000000000000#pragma once #include #include class Request { std::string _request; public: Request(const char *cmd_ending); // expecting ending at the end of cmd Request &Add(const std::string &arg, char ending); // appends escaped arg and ending Request &AddFmt(const char *cmd_ending, ...); // expecting ending inside and not escaped! inline operator const std::string &() const { return _request; } }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/WayToShell.cpp000066400000000000000000000410021477431623700237270ustar00rootroot00000000000000#include "WayToShell.h" #include "Parse.h" #include #include #include #include // Для работы с терминальными атрибутами #include #include #include #include #include "../../Erroring.h" // use 'export NETROCKS_VERBOSE=1' or '..=2' to see protocol dumps from DebugStr static void DebugStr(const char *info, const char *str, size_t len) { std::string tmp; for (size_t i = 0; i <= len; ++i) { if (i == len) { ; // nothing } else if ((unsigned char)str[i] < 32) { tmp+= StrPrintf("{%02x}", (unsigned char)str[i]); if (str[i] == '\r' && i + 1 < len && str[i + 1] == '\n') { continue; } } else { tmp+= str[i]; } if (!tmp.empty() && (i == len || str[i] == '\r' || str[i] == '\n')) { std::cerr << info << ": " << tmp << std::endl; tmp.clear(); } } } static void DebugStr(const char *info, const std::string &str) { DebugStr(info, str.c_str(), str.size()); } static void ForkSafePrint(const char *str) { if (write(STDOUT_FILENO, str, strlen(str)) <= 0) { perror("write"); } } WayToShell::WayToShell(int fd_ipc_recv, const WayToShellConfig &cfg, const StringConfig &protocol_options) : _fd_ipc_recv(fd_ipc_recv) { if (!cfg.command.empty()) { LaunchCommand(cfg, protocol_options); } else if (!cfg.serial.empty()) { OpenSerialPort(cfg, protocol_options); } else { throw ProtocolError("Command or port not specified"); } } // That long echo after exit makes things work smoother, dunno why. // And it really needs to be rather long static const char s_exit_cmd[] = "\nexit\necho =================================\n"; WayToShell::~WayToShell() { try { while (FinalRead(100)) { usleep(1000); } if (write(_master_fd, s_exit_cmd, sizeof(s_exit_cmd) - 1) == sizeof(s_exit_cmd) - 1) { FinalRead(1000); fprintf(stderr, "~WayToShell: exit delivered\n"); } else { perror("~WayToShell: write exit"); } } catch (...) { fprintf(stderr, "~WayToShell: exit exception\n"); } CheckedCloseFD(_master_fd); CheckedCloseFDPair(_stderr_pipe); if (_pid != (pid_t)-1) { // Проверяем, завершен ли дочерний процесс int status; if (waitpid(_pid, &status, WNOHANG) == 0) { // Если нет, отправляем сигнал для завершения kill(_pid, SIGTERM); waitpid(_pid, &status, 0); // Ожидаем завершения } std::cerr << "Child exited with status " << status << '\n'; } } bool WayToShell::FinalRead(int tmout) { struct pollfd pfd{}; pfd.fd = _master_fd; pfd.events = POLLIN; const int r = os_call_int(poll, &pfd, (nfds_t)1, tmout); return (r > 0 && RecvPolledFD(pfd, STDOUT)); } void WayToShell::LaunchCommand(const WayToShellConfig &cfg, const StringConfig &protocol_options) { std::string command = cfg.command; for (unsigned i = cfg.options.size(); i--;) { // reversed order for proper replacement Substitute(command, StrPrintf("$OPT%u", i).c_str(), cfg.OptionValue(i, protocol_options)); } if (g_netrocks_verbosity > 0) { fprintf(stderr, "WayToShell::LaunchCommand: '%s'\n", command.c_str()); } if (pipe(_stderr_pipe) < 0) { throw ProtocolError("pipe error", errno); } struct Argv : std::vector { ~Argv() { for (auto &arg : *this) { free(arg); } } } argv; Environment::ExplodeCommandLine ecl(command); for (const auto &arg : ecl) { argv.emplace_back(strdup(arg.c_str())); } argv.emplace_back(nullptr); _pid = MakePTYAndFork(_master_fd); if (_pid == (pid_t)-1) { throw ProtocolError("PTY error", errno); } if (_pid == 0) { CheckedCloseFD(_stderr_pipe[0]); // close read end dup2(_stderr_pipe[1], STDERR_FILENO); // redirect stderr to the pipe CheckedCloseFD(_stderr_pipe[1]); // close write end, as it's now duplicated setenv("LANG", "C", 1); setenv("TERM", "xterm-mono", 1); unsetenv("COLORFGBG"); // Child process // Получаем текущие атрибуты терминала struct termios tios{}; if (tcgetattr(STDIN_FILENO, &tios) == -1) { ForkSafePrint("tcgetattr failed\n"); _exit(-1); } // disable echo, CR/LF translation and other nasty things cfmakeraw(&tios); // Применяем новые атрибуты if (tcsetattr(STDIN_FILENO, TCSANOW, &tios) == -1) { ForkSafePrint("tcsetattr failed\n"); _exit(-1); } // ssh should terminate if terminal closed signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGPIPE, SIG_DFL); execvp(*argv.data(), argv.data()); ForkSafePrint("execlp failed\n"); _exit(-1); } // Parent process CheckedCloseFD(_stderr_pipe[1]); // close write end // Now you can read from _stderr_pipe[0] to get the stderr output // and from _master_fd to get the stdout output MakeFDNonBlocking(_master_fd); MakeFDNonBlocking(_stderr_pipe[0]); } void WayToShell::OpenSerialPort(const WayToShellConfig &cfg, const StringConfig &protocol_options) { const auto &opt_baudrate = cfg.OptionValue(0, protocol_options); const auto &opt_flowctrl = cfg.OptionValue(1, protocol_options); const auto &opt_databits = cfg.OptionValue(2, protocol_options); const auto &opt_stopbits = cfg.OptionValue(3, protocol_options); const auto &opt_parity = cfg.OptionValue(4, protocol_options); speed_t baudrate; switch (atoi(opt_baudrate.c_str())) { case 50: baudrate = B50; break; case 150: baudrate = B150; break; case 300: baudrate = B300; break; case 600: baudrate = B600; break; case 1200: baudrate = B1200; break; case 2400: baudrate = B2400; break; case 4800: baudrate = B4800; break; case 9600: baudrate = B9600; break; case 19200: baudrate = B19200; break; case 38400: baudrate = B38400; break; case 57600: baudrate = B57600; break; case 115200: baudrate = B115200; break; #ifdef B230400 case 230400: baudrate = B230400; break; #endif #ifdef B460800 case 460800: baudrate = B460800; break; #endif #ifdef B500000 case 500000: baudrate = B500000; break; #endif #ifdef B576000 case 576000: baudrate = B576000; break; #endif #ifdef B921600 case 921600: baudrate = B921600; break; #endif #ifdef B1000000 case 1000000: baudrate = B1000000; break; #endif #ifdef B1152000 case 1152000: baudrate = B1152000; break; #endif #ifdef B1500000 case 1500000: baudrate = B1500000; break; #endif #ifdef B2000000 case 2000000: baudrate = B2000000; break; #endif #ifdef B2500000 case 2500000: baudrate = B2500000; break; #endif #ifdef B3000000 case 3000000: baudrate = B3000000; break; #endif #ifdef B3500000 case 3500000: baudrate = B3500000; break; #endif #ifdef B4000000 case 4000000: baudrate = B4000000; break; #endif default: throw ProtocolError("Bad baudrate", opt_baudrate.c_str()); } _master_fd = open(cfg.serial.c_str(), O_RDWR); if (_master_fd == -1) { throw ProtocolError("open port error", cfg.serial.c_str(), errno); } struct termios tios{}; if (tcgetattr(_master_fd, &tios) != 0) { const int err = errno; close(_master_fd); throw ProtocolError("tcgetattr error", cfg.serial.c_str(), err); } tios.c_cflag&= ~PARENB; tios.c_cflag&= ~CSTOPB; tios.c_cflag&= ~CSIZE; tios.c_cflag&= ~CRTSCTS; tios.c_cflag|= CREAD | CLOCAL; tios.c_lflag&= ~ISIG; tios.c_lflag&= ~ICANON; tios.c_lflag&= ~ECHO; tios.c_lflag&= ~ECHOE; tios.c_lflag&= ~ECHONL; tios.c_iflag&= ~(IXON | IXOFF | IXANY); tios.c_iflag&= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); tios.c_oflag &= ~OPOST; tios.c_oflag &= ~ONLCR; tios.c_cc[VTIME] = 0; tios.c_cc[VMIN] = 1; switch (atoi(opt_databits.c_str())) { case 5: tios.c_cflag|= CS5; break; case 6: tios.c_cflag|= CS6; break; case 7: tios.c_cflag|= CS7; break; case 8: tios.c_cflag|= CS8; break; default: close(_master_fd); throw ProtocolError("wrong databits", opt_databits.c_str()); } if (opt_flowctrl == "Xon/Xoff") { tios.c_iflag|= (IXON | IXOFF); } else if (opt_flowctrl == "RTS/CTS") { tios.c_cflag|= CRTSCTS; } else if (opt_flowctrl != "None") { close(_master_fd); throw ProtocolError("wrong flowctrl", opt_flowctrl.c_str()); } if (opt_stopbits == "2") { tios.c_cflag|= CSTOPB; } else if (opt_stopbits != "1") { close(_master_fd); throw ProtocolError("wrong stopbits", opt_stopbits.c_str()); } if (opt_parity == "Odd") { tios.c_cflag|= PARENB; tios.c_cflag|= PARODD; } else if (opt_parity == "Even") { tios.c_cflag|= PARENB; tios.c_cflag&= ~PARODD; } else if (opt_parity != "None") { close(_master_fd); throw ProtocolError("wrong parity", opt_parity.c_str()); } // cfmakeraw(&tios); cfsetispeed(&tios, baudrate); cfsetospeed(&tios, baudrate); if (tcsetattr(_master_fd, TCSANOW, &tios) != 0) { const int err = errno; close(_master_fd); throw ProtocolError("tcsetattr error", cfg.serial.c_str(), err); } if (pipe(_stderr_pipe) < 0) { // create dummy pipe, just for genericity const int err = errno; close(_master_fd); throw ProtocolError("pipe error", err); } MakeFDNonBlocking(_master_fd); MakeFDNonBlocking(_stderr_pipe[0]); } void WayToShell::GetDescriptors(int &fdinout, int &fderr) { fdinout = (_master_fd != -1) ? dup(_master_fd) : -1; fderr = (_stderr_pipe[0] != -1) ? dup(_stderr_pipe[0]) : -1; } void WayToShell::ThrowIfAppExited() { if (_pid != (pid_t)-1) { // usleep(10000); if (waitpid(_pid, &_pid_status, WNOHANG) == _pid) { _pid = (pid_t)-1; throw ProtocolError("client app exited", _pid_status); } } } bool WayToShell::RecvPolledFD(struct pollfd &fd, enum OutputType output_type) { if (!(fd.revents & POLLIN)) { return false; } std::vector &data = (output_type == STDERR) ? _stderr_data : _stdout_data; const size_t ofs = data.size(); const size_t piece = 4096; data.resize(data.size() + piece); ssize_t n = os_call_ssize(read, fd.fd, (void *)(data.data() + ofs), piece); if (n <= 0) { fprintf(stderr, " !!! error reading pty n=%ld err=%d\n", n, errno); data.resize(ofs); throw ProtocolError("error reading pty", (output_type == STDERR) ? "stderr" : "stdout", errno); } data.resize(ofs + n); // if (g_netrocks_verbosity > 0) { // DebugStr((output_type == STDERR) ? "STDERR" : "STDOUT", data.data() + ofs, n); // } return true; } // if data is non-NULL then send that data, optionally recving incoming stuff // if data is NULL then recv at least some incoming stuff unsigned WayToShell::Xfer(const char *data, size_t len) { unsigned out = 0; while ((!data && out == 0) || len) { struct pollfd fds[3]{}; fds[0].fd = _master_fd; fds[0].events = (data && len) ? POLLIN | POLLOUT : POLLIN; fds[1].fd = _stderr_pipe[0]; fds[1].events = POLLIN; fds[2].fd = _fd_ipc_recv; fds[2].events = 0; // this poll'ed only for errors const int r = os_call_int(poll, &fds[0], (nfds_t)3, 10000); if (r < 0) { throw ProtocolError("error polling pty", errno); } if (r == 0) { ThrowIfAppExited(); } else { if (RecvPolledFD(fds[0], STDOUT)) { out|= STDOUT; } if (RecvPolledFD(fds[1], STDERR)) { out|= STDERR; } if (data && len && (fds[0].revents & POLLOUT) != 0) { const ssize_t wr = write(_master_fd, data, len); if (wr >= 0) { len-= wr; if (len != 0) { data+= wr; } } else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { throw ProtocolError("pty write error", errno); } } if ( (fds[0].revents & (POLLERR | POLLHUP)) != 0 || (fds[1].revents & (POLLERR | POLLHUP)) != 0) { throw ProtocolError("pty disrupted", errno); } if ((fds[2].revents & (POLLERR | POLLHUP)) != 0) { throw ProtocolError("ipc disrupted", errno); } } } return out; } void WayToShell::RecvSomeStdout() { try { while ((Xfer() & STDOUT) == 0) { ; } } catch (std::exception &e) { if (_stderr_data.empty()) { throw; } std::string stderr_str(_stderr_data.data(), _stderr_data.size()); throw ProtocolError(e.what(), stderr_str.c_str()); } } static ssize_t DispatchExpectedReply(std::vector &lines, std::vector &incoming, const std::vector &expected_replies) { // fetch incoming data and pack it into lines only until found match with expected reply for (size_t left = 0, right = 0;;) { if (right == incoming.size() || incoming[right] == '\r' || incoming[right] == '\n' || incoming[right] == 0) { if (lines.empty() || lines.back().back() == '\n') { lines.emplace_back(incoming.data() + left, right - left); } else { lines.back().append(incoming.data() + left, right - left); } const bool append_lf = (right != incoming.size()); if (right + 1 < incoming.size() && incoming[right] == '\r' && incoming[right + 1] == '\n') { right+= 2; } else if (right < incoming.size()) { right++; } left = right; if (!lines.back().empty()) { if (append_lf) { lines.back()+= '\n'; } if (g_netrocks_verbosity > 1) { std::cerr << "Line '" << lines.back() << "' " << std::endl; } for (size_t index = 0; index != expected_replies.size(); ++index) { if (MatchWildcard(lines.back().c_str(), expected_replies[index])) { if (g_netrocks_verbosity > 2) { std::cerr << "Matched '" << expected_replies[index] << "' " << std::endl; } incoming.erase(incoming.begin(), incoming.begin() + right); return index; } if (g_netrocks_verbosity > 2) { std::cerr << "NOT Matched '" << expected_replies[index] << "' " << std::endl; } } } else { lines.pop_back(); } if (right == incoming.size()) { incoming.clear(); return -1; } } else { ++right; } } } WaitResult WayToShell::SendAndWaitReplyInner(const std::string &send_str, const std::vector &expected_replies) { if (g_netrocks_verbosity > 2) { for (size_t index = 0; index != expected_replies.size(); ++index) { std::cerr << "Expect '" << expected_replies[index] << "' " << std::endl; } } WaitResult wr; try { for (bool first = true;;first = false) { unsigned xrv; if (first) { xrv = STDOUT | STDERR; if (!send_str.empty()) { xrv|= Xfer(send_str.c_str(), send_str.size()); } } else { xrv = Xfer(); } if (xrv & STDOUT) { wr.index = DispatchExpectedReply(wr.stdout_lines, _stdout_data, expected_replies); if (wr.index != -1) { wr.output_type = STDOUT; break; } } if (xrv & STDERR) { wr.index = DispatchExpectedReply(wr.stderr_lines, _stderr_data, expected_replies); if (wr.index != -1) { wr.output_type = STDERR; break; } } } } catch (std::exception &e) { std::string stderr_str; AppendTrimmedLines(stderr_str, wr.stderr_lines); if (!_stderr_data.empty()) { stderr_str+= '\n'; stderr_str.append(_stderr_data.data(), _stderr_data.size()); } if (stderr_str.empty()) { throw; } throw ProtocolError(e.what(), stderr_str.c_str()); } if (g_netrocks_verbosity > 0 && !wr.stdout_lines.empty()) { for (const auto &line : wr.stdout_lines) { DebugStr("STDOUT.RPL", line); } } if (g_netrocks_verbosity > 0 && !wr.stderr_lines.empty()) { for (const auto &line : wr.stderr_lines) { DebugStr("STDERR.RPL", line); } } return wr; } WaitResult WayToShell::SendAndWaitReply(const std::string &send_str, const std::vector &expected_replies, bool hide_in_log) { if (g_netrocks_verbosity > 0) { if (hide_in_log) { DebugStr("SEND.HIDE", "???"); } else { DebugStr("SEND", send_str); } } return SendAndWaitReplyInner(send_str, expected_replies); } WaitResult WayToShell::WaitReply(const std::vector &expected_replies) { return SendAndWaitReplyInner(std::string(), expected_replies); } void WayToShell::Send(const char *data, size_t len) { if (g_netrocks_verbosity > 1) { DebugStr("SEND.BLOB", data, len); } else if (g_netrocks_verbosity > 0) { DebugStr("SEND.BLOB", StrPrintf("{%lu}", (unsigned long)len)); } Xfer(data, len); } void WayToShell::Send(const char *data) { const size_t len = strlen(data); if (g_netrocks_verbosity > 0) { DebugStr("SEND.PSZ", data, len); } Xfer(data, len); } void WayToShell::Send(const std::string &line) { if (g_netrocks_verbosity > 0) { DebugStr("SEND.STR", line); } Xfer(line.c_str(), line.size()); } void WayToShell::ReadStdout(void *buffer, size_t len) { for (size_t ofs = 0;;) { size_t piece = std::min(len - ofs, _stdout_data.size()); if (piece) { memcpy((char *)buffer + ofs, _stdout_data.data(), piece); _stdout_data.erase(_stdout_data.begin(), _stdout_data.begin() + piece); ofs+= piece; } if (ofs == len) { break; } RecvSomeStdout(); } if (g_netrocks_verbosity > 1) { DebugStr("STDOUT.BLOB", (const char *)buffer, len); } else if (g_netrocks_verbosity > 0) { const auto &info = StrPrintf("{%lu}", (unsigned long)len); DebugStr("STDOUT.BLOB", info); } } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/WayToShell.h000066400000000000000000000033041477431623700233770ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "WayToShellConfig.h" #include "Request.h" enum OutputType { STDBAD = 0, STDOUT = 1, STDERR = 2 }; struct WaitResult { std::vector stdout_lines; std::vector stderr_lines; ssize_t index{-1}; OutputType output_type{STDBAD}; }; class WayToShell { int _fd_ipc_recv; int _master_fd{-1}; int _stderr_pipe[2]{-1, -1}; pid_t _pid{(pid_t)-1}; int _pid_status{0}; std::vector _stdout_data, _stderr_data; void ThrowIfAppExited(); bool RecvPolledFD(struct pollfd &fd, enum OutputType output_type); unsigned Xfer(const char *data = nullptr, size_t len = 0); void RecvSomeStdout(); WaitResult SendAndWaitReplyInner(const std::string &send_str, const std::vector &expected_replies); bool FinalRead(int tmout); void LaunchCommand(const WayToShellConfig &cfg, const StringConfig &protocol_options); void OpenSerialPort(const WayToShellConfig &cfg, const StringConfig &protocol_options); public: WayToShell(int fd_ipc_recv, const WayToShellConfig &cfg, const StringConfig &protocol_options); virtual ~WayToShell(); void GetDescriptors(int &fdinout, int &fderr); void Send(const char *data, size_t len); void Send(const char *data); void Send(const std::string &line); void ReadStdout(void *buffer, size_t len); WaitResult WaitReply(const std::vector &expected_replies); WaitResult SendAndWaitReply(const std::string &send_str, const std::vector &expected_replies, bool hide_in_log = false); }; far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/WayToShellConfig.cpp000066400000000000000000000033051477431623700250610ustar00rootroot00000000000000/// NB: This file linked both into NetRocks plugin binary and into SHELL protocol broker executable #include "WayToShellConfig.h" #include #include WaysToShell::WaysToShell(const std::string &ways_ini) { std::vector::operator =(KeyFileReadHelper(ways_ini).EnumSectionsAt("")); } WaysToShell::~WaysToShell() { } // WayToShellConfig::WayToShellConfig(const std::string &ways_ini, const std::string &way_name) { KeyFileReadSection kf(ways_ini, way_name); command = kf.GetString("Command"); serial = kf.GetString("Serial"); std::string values; std::vector exploded_values; for (unsigned i = 0;;++i) { values = kf.GetString(StrPrintf("OPT%u_Items", i)); if (values.empty()) { break; } options.emplace_back(); auto &opt = options.back(); opt.name = kf.GetString(StrPrintf("OPT%u_Name", i)); exploded_values.clear(); StrExplode(exploded_values, values, "|"); for (size_t v = 0; v < exploded_values.size(); ++v) { auto &ev = exploded_values[v]; if (StrStartsFrom(ev, '{') && StrEndsBy(ev, '}')) { ev.resize(ev.size() - 1); ev.erase(0, 1); opt.def = v; } opt.items.emplace_back(); auto &item = opt.items.back(); const size_t p = ev.find(':'); if (p != std::string::npos) { item.info = ev.substr(0, p); item.value = ev.substr(p + 1); } else { item.value = item.info = ev; } } } } WayToShellConfig::~WayToShellConfig() { } std::string WayToShellConfig::OptionValue(unsigned index, const StringConfig &protocol_options) const { ASSERT(index < options.size()); const auto &opt = options[index]; return protocol_options.GetString(StrPrintf("OPT%u", index).c_str(), opt.items[opt.def].value.c_str()); } far2l-2.6.5~beta+ds/NetRocks/src/Protocol/SHELL/WayToShellConfig.h000066400000000000000000000011661477431623700245310ustar00rootroot00000000000000#pragma once #include #include #include struct WaysToShell : std::vector { WaysToShell(const std::string &ways_ini); ~WaysToShell(); }; struct WayToShellConfig { struct Option { struct Item { std::string info; std::string value; }; std::string name; std::vector items; size_t def{0}; }; std::vector