diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..8721673 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,225 @@ +name: android + +on: [push] + +jobs: + use_install_qt: + name: use install qt android_${{matrix.os}}-qt${{matrix.qt_version}}-${{matrix.VCPKG_TARGET_TRIPLET}}-${{matrix.BUILD_TYPE}} + + strategy: + matrix: + BUILD_TYPE: [Release] + ANDROID_ABI: [armeabi-v7a, x86] + ANDROID_PLATFORM: [android-28] + os: [Ubuntu-18.04] + qt_version: [5.12.12] + include: + - VCPKG_TARGET_TRIPLET: arm-android + qt_arch: android_armv7 + ANDROID_ABI: armeabi-v7a + + - VCPKG_TARGET_TRIPLET: x86-android + qt_arch: android_x86 + ANDROID_ABI: x86 + + runs-on: ${{ matrix.os }} + + env: + SOURCE_DIR: ${{github.workspace}}/.cache/source + TOOSL_DIR: ${{github.workspace}}/.cache/tools + INSTALL_DIR: ${{github.workspace}}/.cache/install + QFtpServer_VERSION: v0.0.18 + VCPKGGITCOMMITID: faed44dfa013088fe1910908a8a65887622f412f + qt_modules: qtwebengine + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: snickerbockers/submodules-init@v4 + + - name: make directory + run: | + cmake -E make_directory ${{env.SOURCE_DIR}} + cmake -E make_directory ${{env.TOOSL_DIR}} + cmake -E make_directory ${{env.INSTALL_DIR}} + + - name: Cache Qt + id: cache-qt + uses: actions/cache@v1 # not v2! + with: + path: ${{env.TOOSL_DIR}}/qt + key: android_${{matrix.os}}-qt${{matrix.qt_version}}-${{matrix.VCPKG_TARGET_TRIPLET}}-${{matrix.BUILD_TYPE}}-2021-11-03 + + - name: Install Qt + # You may pin to the exact commit or the version. + # uses: jurplel/install-qt-action@a962fb91949c8e846a3e4a944d50e7ef5cc3a28a + uses: jurplel/install-qt-action@v2.14.0 + with: + # Directory to install Qt + dir: ${{env.TOOSL_DIR}}/qt # optional + # Version of Qt to install + version: ${{matrix.qt_version}} # optional, default is 5.15.2 + # Host platform + #host: linux # optional + # Target platform for build + target: android # optional, default is desktop + # Architecture for Windows/Android + arch: ${{matrix.qt_arch}} # optional + # Whether or not to install Qt dependencies on Linux + #install-deps: true # optional, default is true + # Additional Qt modules to install + modules: ${{env.qt_modules}} # optional + # Force a Qt mirror in case the default is not working + #mirror: # optional + # Whether or not to actually download Qt + cached: ${{ steps.cache-qt.outputs.cache-hit }} # optional, default is false + # Whether or not to automatically run setup-python to find a valid python version. + #setup-python: false # optional, default is true + # Qt tools to download--specify comma-separated argument lists which are themselves separated by spaces: ,, + #tools: # optional + # Whether or not to actually install Qt or just the tools from the tools argument + #tools-only: false # optional, default is false + # Version of aqtinstall to use in case of issues + #aqtversion: # optional, default is ==0.10.0 + # Version of py7zr to use in case of issues + #py7zrversion: # optional, default is ==0.11.0 + # Any extra arguments to append to the back + #extra: # optional + + - name: run-vcpkg + uses: lukka/run-vcpkg@v7.4 + with: + # Indicates whether to only setup vcpkg (i.e. installing it and setting the environment variables VCPKG_ROOT, RUNVCPK_VCPKG_ROOT), without installing any port. + #setupOnly: # optional + #vcpkgGitURL: https://github.com/KangLin/vcpkg.git + vcpkgGitCommitId: ${{env.VCPKGGITCOMMITID}} + vcpkgDirectory: ${{ runner.workspace }}/vcpkg/ + # Since the cache must be invalidated when content of the vcpkg.json file changes, let's + # compute its hash and append this to the computed cache's key. + appendedCacheKey: android_${{matrix.os}}-qt${{matrix.qt_version}}-${{matrix.VCPKG_TARGET_TRIPLET}}-${{matrix.BUILD_TYPE}}-${{env.VCPKGGITCOMMITID}} + additionalCachedPaths: $VCPKG_ROOT/installed + vcpkgTriplet: '${{ matrix.VCPKG_TARGET_TRIPLET }}' + vcpkgArguments: 'zlib openssl' + + - name: Cache installed + uses: actions/cache@v2 + id: cache-installed + with: + path: | + ${{env.INSTALL_DIR}} + key: cache-installed-android_${{matrix.os}}-qt${{matrix.qt_version}}-${{matrix.VCPKG_TARGET_TRIPLET}}-${{matrix.BUILD_TYPE}} + + - name: build log4qt + working-directory: ${{env.SOURCE_DIR}} + env: + QT_ROOT: ${{env.Qt5_Dir}} + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/Log4Qt ]; then + git clone -b v1.5.1 "https://github.com/MEONMedical/Log4Qt.git" + cd Log4Qt + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} \ + -DQt5_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DQt5Core_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Core \ + -DQt5Network_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Network \ + -DQt5Concurrent_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Concurrent \ + -DQt5Test_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Test \ + -DCMAKE_INSTALL_PREFIX=${{env.INSTALL_DIR}} \ + -DCMAKE_PREFIX_PATH=${{env.QT_ROOT}} \ + -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=${{matrix.ANDROID_ABI}} \ + -DANDROID_PLATFORM=${{matrix.ANDROID_PLATFORM}} + cmake --build . --config ${{matrix.BUILD_TYPE}} + cmake --build . --config ${{matrix.BUILD_TYPE}} --target install/strip + fi + - name: git clone RabbitCommon + working-directory: ${{env.SOURCE_DIR}} + run: git clone https://github.com/KangLin/RabbitCommon.git + + - name: build QFtpServer + env: + RabbitCommon_DIR: ${{env.SOURCE_DIR}}/RabbitCommon + QT_ROOT: ${{env.Qt5_Dir}} + STOREPASS: ${{secrets.STOREPASS}} + run: | + cmake -E make_directory ${{github.workspace}}/build + cd ${{github.workspace}}/build + cmake ${{github.workspace}} \ + -DCMARK_SHARED=OFF \ + -DCMARK_TESTS=OFF \ + -DCMARK_STATIC=ON \ + -DBUILD_FREERDP=ON \ + -DLog4Qt_DIR=${{env.INSTALL_DIR}}/lib/cmake/Log4Qt \ + -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} \ + -DQT_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DQt5_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DQt5Core_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Core \ + -DQt5Gui_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Gui \ + -DQt5Widgets_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Widgets \ + -DQt5Xml_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Xml \ + -DQt5Sql_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Sql \ + -DQt5Positioning_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Positioning \ + -DQt5Network_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Network \ + -DQt5Multimedia_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Multimedia \ + -DQt5LinguistTools_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5LinguistTools \ + -DQt5Qml_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Qml \ + -DQt5Quick_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Quick \ + -DQt5QuickWidgets_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5QuickWidgets \ + -DQt5AndroidExtras_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5AndroidExtras \ + -DQt5Concurrent_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Concurrent \ + -DQt5Test_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5Test \ + -DQt5WebEngineWidgets_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5WebEngineWidgets \ + -DCMAKE_INSTALL_PREFIX=${{env.INSTALL_DIR}} \ + -DCMAKE_PREFIX_PATH="${{env.QT_ROOT}}:${{env.INSTALL_DIR}}" \ + -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=${{matrix.ANDROID_ABI}} \ + -DANDROID_PLATFORM=${{matrix.ANDROID_PLATFORM}} + cmake --build . --config ${{ matrix.BUILD_TYPE }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target install-runtime + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target APK + + - name: Package + if: ${{matrix.BUILD_TYPE == 'Release'}} + working-directory: ${{env.INSTALL_DIR}} + env: + QT_ROOT: ${{env.Qt5_Dir}} + RabbitCommon_DIR: ${{env.SOURCE_DIR}}/RabbitCommon + run: | + APK_FILE=`find . -name "install-release-signed.apk"` + APK_NAME=QFtpServer_${{matrix.ANDROID_ABI}}_${QFtpServer_VERSION}.apk + mv -f ${APK_FILE} ${APK_NAME} + APK_FILE=${APK_NAME} + cp ${{github.workspace}}/Update/update_android.xml . + MD5=`md5sum ${APK_FILE} | awk '{print $1}'` + echo "MD5:${MD5}" + sed -i "s/.*${QFtpServer_VERSION}.*Release rabbit remote control ${QFtpServer_VERSION}.*`date`.*${BUILD_ARCH}.*${MD5}.*<:https\://github.com/KangLin/QFtpServer/releases/download/${QFtpServer_VERSION}/${APK_NAME}<:g" update_android.xml + sed -i "s/.*${QFtpServer_VERSION},, + #tools: # optional + # Whether or not to actually install Qt or just the tools from the tools argument + #tools-only: false # optional, default is false + # Version of aqtinstall to use in case of issues + #aqtversion: # optional, default is ==0.10.0 + # Version of py7zr to use in case of issues + #py7zrversion: # optional, default is ==0.11.0 + # Any extra arguments to append to the back + #extra: # optional + + - name: build log4qt + working-directory: ${{env.SOURCE_DIR}} + run: | + IF NOT EXIST ${{env.INSTALL_DIR}}\lib\cmake\Log4Qt ( + git clone https://github.com/MEONMedical/Log4Qt.git + cd Log4Qt + git checkout -b eb4b952a886ab03d24e896e38df015cf0ff5bd7a eb4b952a886ab03d24e896e38df015cf0ff5bd7a + cmake -E make_directory build + cd build + cmake .. ^ + -A ${{matrix.CMAKE_GENERATOR_PLATFORM}} ^ + -T ${{matrix.VCPKG_PLATFORM_TOOLSET}} ^ + -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} ^ + -DCMAKE_INSTALL_PREFIX=${{env.INSTALL_DIR}} ^ + -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake + cmake --build . --config ${{matrix.BUILD_TYPE}} + cmake --build . --config ${{matrix.BUILD_TYPE}} --target install + ) + + - name: build QFtpServer + env: + RabbitCommon_DIR: ${{env.SOURCE_DIR}}/RabbitCommon + run: | + cmake -E make_directory ${{github.workspace}}/build + cd ${{github.workspace}}/build + cmake ${{github.workspace}} ^ + -A ${{matrix.CMAKE_GENERATOR_PLATFORM}} ^ + -T ${{matrix.VCPKG_PLATFORM_TOOLSET}} ^ + -DCMARK_SHARED=OFF ^ + -DCMARK_TESTS=OFF ^ + -DCMARK_STATIC=ON ^ + -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} ^ + -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/build/install ^ + -DBUILD_QUIWidget=OFF ^ + -DLog4Qt_DIR=${{env.INSTALL_DIR}}/lib/cmake/Log4Qt ^ + -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake + cmake --build . --config ${{matrix.BUILD_TYPE}} + + - name: Package + if: ${{ matrix.BUILD_TYPE == 'Release' }} + working-directory: ${{github.workspace}}\build + run: | + cmake --build . --config ${{matrix.BUILD_TYPE}} --target install-runtime + move /Y install\plugins\*crypto*.dll install\bin + move /Y install\plugins\*ssl*.dll install\bin + copy /Y ${{env.INSTALL_DIR}}\bin\*.dll install\bin + copy /Y ${{env.INSTALL_DIR}}\lib\*.dll install\bin + copy /Y ${{env.RUNVCPKG_VCPKG_ROOT}}\installed\${{env.RUNVCPKG_VCPKG_TRIPLET_OUT}}\bin\*.dll install\bin + 7z a QFtpServer_windows_${{env.QFtpServer_VERSION}}.zip .\install\* + makensis Install.nsi + if "5.12.12" == "${{matrix.qt_version}}" ( + ${{github.workspace}}\build\install\bin\QFtpServerApp.exe ^ + -f "update_windows.xml" ^ + --pf QFtpServer_setup_${{env.QFtpServer_VERSION}}.exe ^ + -m "${{env.QFtpServer_VERSION}}" + ) + + - name: update + uses: actions/upload-artifact@v2 + with: + name: qftpserver_${{ matrix.os }}_msvc_qt${{matrix.qt_version}} + path: | + ${{github.workspace}}\build\QFtpServer_setup_${{env.QFtpServer_VERSION}}.exe + if: ${{ matrix.BUILD_TYPE == 'Release' }} + + - name: Upload To Github Release + if: ${{ matrix.BUILD_TYPE == 'Release' && '5.12.12' == matrix.qt_version && startsWith(github.ref, 'refs/tags/') }} + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + prerelease: true + files: | + ${{github.workspace}}\build\QFtpServer_windows_${{env.QFtpServer_VERSION}}.zip + ${{github.workspace}}\build\QFtpServer_setup_${{env.QFtpServer_VERSION}}.exe + ${{github.workspace}}\build\update_windows.xml diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..7e7ad0a --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,243 @@ +name: ubuntu + +on: [push] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + build: + strategy: + matrix: + os: [Ubuntu-20.04, Ubuntu-18.04] + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: [Release, Debug] + + runs-on: ${{matrix.os}} + env: + BUILD_TYPE: ${{ matrix.BUILD_TYPE }} + SOURCE_DIR: ${{github.workspace}}/.cache/source + TOOSL_DIR: ${{github.workspace}}/.cache/tools + INSTALL_DIR: ${{github.workspace}}/.cache/install_ubuntu_2022_02_14 + QFtpServer_VERSION: 0.0.18 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: make_directory + run: | + cmake -E make_directory ${{env.SOURCE_DIR}} + cmake -E make_directory ${{env.TOOSL_DIR}} + cmake -E make_directory ${{env.INSTALL_DIR}} + + - name: git clone RabbitCommon + working-directory: ${{env.SOURCE_DIR}} + run: git clone https://github.com/KangLin/RabbitCommon.git + + - name: apt helper action + uses: ryankurte/action-apt@v0.2.0 + with: + # architectures to pass to dpkg --add-architecture + #architectures: # optional + # apt packages to install + packages: xvfb xpra libglu1-mesa-dev libpulse-mainloop-glib0 cmake build-essential libusb-1.0-0-dev freerdp2-dev libvncserver-dev libpixman-1-dev libssh-dev libtelnet-dev libqxmpp-dev debhelper dh-systemd fakeroot graphviz doxygen qttools5-dev qttools5-dev-tools qtbase5-dev qtbase5-dev-tools qtmultimedia5-dev qtlocation5-dev libqt5svg5-dev libutf8proc-dev libqtermwidget5-0-dev libpam0g-dev libxkbcommon-dev libxkbcommon-x11-dev libx11-xcb-dev libx11-dev libxfixes-dev qtwebengine5-dev + + - name: Cache installed + uses: actions/cache@v2 + id: cache-installed + with: + path: | + ${{env.INSTALL_DIR}} + key: cache-installed-ubuntu-${{matrix.os}}-${{matrix.BUILD_TYPE}} + +# - name: build FreeRDP +# working-directory: ${{env.SOURCE_DIR}} +# run: | +# if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/FreeRDP2 ]; then +# git clone -b 2.5.0 https://github.com/FreeRDP/FreeRDP.git +# cd FreeRDP +# git submodule update --init --recursive +# cmake -E make_directory build +# cd build +# cmake .. \ +# -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} \ +# -DCMAKE_INSTALL_PREFIX="${{env.INSTALL_DIR}}" \ +# -DWITH_SERVER=ON +# cmake --build . --config ${{matrix.BUILD_TYPE}} +# cmake --build . --config ${{matrix.BUILD_TYPE}} --target install/strip +# fi + + - name: build RabbitVNC + working-directory: ${{env.SOURCE_DIR}} + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/RabbitVNC ]; then + git clone https://github.com/KangLin/RabbitVNC.git + cd ${{env.SOURCE_DIR}}/RabbitVNC + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_TYPE }} \ + -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \ + -DBUILD_TESTS=OFF -DBUILD_VIEWER=OFF + cmake --build . --config ${{ matrix.BUILD_TYPE }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target install/strip + fi + + - name: build tigervnc + working-directory: ${{env.SOURCE_DIR}} + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/tigervnc ]; then + git clone https://github.com/KangLin/tigervnc.git + cd ${{env.SOURCE_DIR}}/tigervnc + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_TYPE }} \ + -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \ + -DBUILD_TESTS=OFF -DBUILD_VIEWER=OFF + cmake --build . --config ${{ matrix.BUILD_TYPE }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target install/strip + fi + + - name: build libdatachannel + working-directory: ${{env.SOURCE_DIR}} + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/libdatachannel ]; then + git clone -b v0.15.5 https://github.com/paullouisageneau/libdatachannel.git + cd libdatachannel + git submodule update --init --recursive + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_TYPE }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target install/strip + fi + + - name: build log4qt + working-directory: ${{env.SOURCE_DIR}} + env: + QT_ROOT: /usr/lib/`uname -m`-linux-gnu/qt5 + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/Log4Qt ]; then + git clone -b v1.5.1 "https://github.com/MEONMedical/Log4Qt.git" + cd Log4Qt + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}} \ + -DQT_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DQt5_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DCMAKE_INSTALL_PREFIX=${{env.INSTALL_DIR}} \ + -DCMAKE_PREFIX_PATH=${{env.QT_ROOT}} + cmake --build . --config ${{matrix.BUILD_TYPE}} + cmake --build . --config ${{matrix.BUILD_TYPE}} --target install/strip + fi + + - name: build QtService + working-directory: ${{env.SOURCE_DIR}} + env: + QT_ROOT: /usr/lib/`uname -m`-linux-gnu/qt5 + run: | + if [ ! -d ${{env.INSTALL_DIR}}/lib/cmake/QtService ]; then + git clone https://github.com/KangLin/qt-solutions.git + cd qt-solutions/qtservice + git submodule update --init --recursive + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_TYPE }} \ + -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \ + -DQT_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 \ + -DQt5_DIR=${{env.QT_ROOT}}/lib/cmake/Qt5 + cmake --build . --config ${{ matrix.BUILD_TYPE }} + cmake --build . --config ${{ matrix.BUILD_TYPE }} --target install/strip + fi + + - name: build_debpackage.sh + working-directory: ${{github.workspace}} + env: + RabbitCommon_DIR: ${{env.SOURCE_DIR}}/RabbitCommon + RabbitVNC_DIR: ${{ env.INSTALL_DIR }}/lib/cmake/RabbitVNC + LibDataChannel_DIR: ${{ env.INSTALL_DIR }}/lib/cmake/LibDataChannel + QtService_DIR: ${{ env.INSTALL_DIR }}/lib/cmake/QtService + Log4Qt_DIR: ${{env.INSTALL_DIR}}/lib/cmake/Log4Qt +# FreeRDP_DIR: ${{env.INSTALL_DIR}}/lib/cmake/FreeRDP2 +# FreeRDP-Client_DIR: ${{env.INSTALL_DIR}}/lib/cmake/FreeRDP-Client2 +# WinPR_DIR: ${{env.INSTALL_DIR}}/lib/cmake/WinPR2 +# FreeRDP-Shadow_DIR: ${{env.INSTALL_DIR}}/lib/cmake/FreeRDP-Shadow2 +# FreeRDP-Server_DIR: ${{env.INSTALL_DIR}}/lib/cmake/FreeRDP-Server2 + run: | + if [ ${{matrix.BUILD_TYPE}} == 'Release' ]; then + export BUILD_TERMINAL=OFF + else + export tigervnc_DIR=${{ env.INSTALL_DIR }}/lib/cmake/tigervnc + fi + ./build_debpackage.sh /usr/lib/`uname -m`-linux-gnu/qt5 + cp ../qftpserver_${{env.QFtpServer_VERSION}}_amd64.deb qftpserver_${{env.QFtpServer_VERSION}}_amd64_${{matrix.os}}.deb + +# - name: update xml file +# if: ${{ matrix.BUILD_TYPE == 'Release' }} +# working-directory: ${{github.workspace}} +# run: | +# #sudo apt update -y -qq +# #sudo apt-get install -y -qq xvfb xpra +# sudo Xvfb :999 -ac & +# export DISPLAY=:999.0 +# echo "Install rabbit remote control ......" +# sudo dpkg -i qftpserver_${{env.QFtpServer_VERSION}}_amd64_${{ matrix.os }}.deb +# echo "Update update_linux.xml ......" +# /opt/QFtpServer/bin/QFtpServerApp -f "`pwd`/update_linux.xml" --pf qftpserver_${{env.QFtpServer_VERSION}}_amd64_${{ matrix.os }}.deb -m "v${{env.QFtpServer_VERSION}}" + + - name: update + uses: actions/upload-artifact@v2 + with: + name: qftpserver_${{ matrix.os }} + path: | + qftpserver_*.deb + if: ${{ matrix.BUILD_TYPE == 'Release' }} + + - name: Upload To Github Release + if: ${{ matrix.BUILD_TYPE == 'Release' && startsWith(github.ref, 'refs/tags/') }} + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + prerelease: true + files: | + ${{github.workspace}}/qftpserver_*.deb + +#- name: Release +# uses: fnkr/github-action-ghr@v1 +# if: startsWith(github.ref, 'refs/tags/') +# env: +# GHR_COMPRESS: xz +# GHR_PATH: ${{github.workspace}}/qftpserver_*.deb +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + +# - name: Upload To Github Release +# # You may pin to the exact commit or the version. +# # uses: xresloader/upload-to-github-release@3ad14478ee4fb9bd65e84c6d496a7d6862892f43 +# uses: xresloader/upload-to-github-release@v1.3.2 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# with: +# # The files or file patterns to upload. You can upload multiple files by split them by semicolon. You can use the glob pattern to find the files. +# file: ${{github.workspace}}/qftpserver_*.deb;${{github.workspace}}/update_linux.xml # default is +# # The files or file patterns to delete before uploading new files. You can delete multiple files by split them by semicolon. You can use the glob pattern to find the files. +# #delete_file: # optional, default is +# # If you need to overwrite existing files, add overwrite: true to the with. +# overwrite: true # optional, default is false +# # The resultant deployment is a draft Release that only repository collaborators can see. This gives you an opportunity to examine and edit the draft release. +# draft: true # optional, default is true +# # The resultant deployment is a Prerelease. This gives you an opportunity to examine and edit the prerelease. +# prerelease: true # optional, default is false +# # With tags: true, your Releases deployment will trigger if and only if the build is a tagged build. +# tags: true # optional, default is false +# # Only work on these branches(splited by semicolon), set to nothing to accept all branches. +# branches: master # optional, default is +# # With verbose: true, be verbose. +# verbose: true # optional, default is false +# # Set custom tag name. +# #tag_name: 'v${{env.QFtpServer_VERSION}}' +# # If try to update latest release. +# #update_latest_release: true # optional, default is false +# if: ${{ matrix.BUILD_TYPE == 'Release' && startsWith(github.ref, 'refs/tags/') }} diff --git a/.gitignore b/.gitignore index 5439c79..679e442 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # This file is used to ignore files which are generated # ---------------------------------------------------------------------------- - +*.user *~ *.autosave *.a @@ -33,7 +33,8 @@ Thumbs.db *.rc /.qmake.cache /.qmake.stash - +build* +build*/ # qtcreator generated files *.pro.user* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..aa6f4b7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "QFtpServer/android/android_lib"] + path = QFtpServer/android/android_lib + url = https://github.com/KangLin/QtAndroidUtils.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..401825a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +sudo: required +dist: xenial +language: cpp + +cache: + - apt + - directories: + +compiler: + - g++ + +os: + - unix + +addons: + ssh_known_hosts: + - github.com + - frs.sourceforge.net + +env: + global: + - SOURCE_DIR=$TRAVIS_BUILD_DIR + - RABBITIM_USE_REPOSITORIES="TRUE" + - QMAKE=qmake + + matrix: + - BUILD_TARGERT="unix" QT_VERSION_DIR=512 QT_VERSION=5.12.3 DOWNLOAD_QT=FALSE + - BUILD_TARGERT="unix" QT_VERSION_DIR=511 QT_VERSION=5.11.3 DOWNLOAD_QT=FALSE + - BUILD_TARGERT="unix" BUILD_ARCH="unix" QT_VERSION_DIR=5.12 QT_VERSION=5.12.10 DOWNLOAD_QT=TRUE + - BUILD_TARGERT="unix" BUILD_ARCH="unix" QT_VERSION_DIR=5.9 QT_VERSION=5.9.9 DOWNLOAD_QT=TRUE + + - BUILD_TARGERT="android" BUILD_ARCH="armeabi-v7a" ANDROID_ARM_NEON=ON QT_VERSION_DIR=5.12 QT_VERSION=5.12.10 ANDROID_API=android-27 DOWNLOAD_QT=TRUE + - BUILD_TARGERT="android" BUILD_ARCH="x86" QT_VERSION_DIR=5.12 QT_VERSION=5.12.10 ANDROID_API=android-27 DOWNLOAD_QT=TRUE + +before_install: + - echo "TRAVIS_OS_NAME=${TRAVIS_OS_NAME}" + - sudo apt-get install -y -qq xvfb xpra + - sudo Xvfb :99 -ac & + - export DISPLAY=:99.0 + - mkdir ${SOURCE_DIR}/Tools + +install: + - cd ${SOURCE_DIR} + - bash ci/build-install-tools.sh > /dev/null + +before_script: + - cd ${SOURCE_DIR} + +script: + - bash ${SOURCE_DIR}/ci/build.sh ${SOURCE_DIR} + +notifications: + email: kl222@126.com + on_success: never # [always|never|change] + on_failure: always + +#deploy: +# - provider: releases +# api_key: +# secure: ALZWWdQlmi0gJ5Gs9gjX4waV4JYvZAFogN6oUCJayWYkCvME/z1QSxMERuZhRM0F4K2Kt8LWK2ozp18OEtonNQJ81ZELbZRYMQwl4C3Cmfhnywyn4UgiU3CLEQ3Dn25iLqHy7QhaS6BWyISxVQxrsEhFkc0tLDODwk45tibiniIpTtBtTB9Tx85p8FEfdOHUCNxhVtW9zF0LjdWxD569HEzpKDi8c4naNCDVvbBA9r2voQqt3NkNsCNz10vxyV+xO8uJIs1/K5Mj6Gnd+UaUtSDnVTUKCZatcge5OGpu9TcYbH5yLuhlTifVtYmhmK3YeAmte6uNlD6WYIdpSTMJLQ8Lgc2WE+2mj0G16afWtY9ajTqVUlJEa1R078gCnFG4YeYjUYIZhFq/u0U16Ga95RvHAZlQNVcyDLb/Mwt6QyJJdWRNHzsm1sY6mwhXKRwAMGvPl9RTWl6JJdFT5wcPYIzPL9+Y6k6ZhkdI6kWEzA7HYUbA1mLiF6Q8h+ezKnb10KiPEAFU4NNUmf3RKMwolRSTN/TkiZCRIVsPKnkLgS5LzTf6s0rGBZ2g5vWo1fDsigDPBJRE27742pPDXXMt82CySThq3lKJFwQsGObLg1z2H8XbvDkImVIVOK98oW2a/7aXX28/0LjWE2tdV83p1ho5DvyTlGZio+fqk0qmfsw= +# file: "$TRAVIS_BUILD_DIR/../qftpserver_${VERSION}_amd64.deb " +# skip_cleanup: true +# on: +# tags: true +# condition: "$BUILD_TARGERT=unix && $QT_VERSION_DIR=512 && $DOWNLOAD_QT=FALSE" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5f0c3a3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,143 @@ +cmake_minimum_required(VERSION 3.21) + +project(QFtpServer) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +#set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "verbose") +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +if(POLICY CMP0083) + cmake_policy(SET CMP0083 NEW) +endif() + +if(POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) +endif() + +if(POLICY CMP0021) + cmake_policy(SET CMP0021 NEW) +endif() + +# Find Git Version Patch +IF(EXISTS "${CMAKE_SOURCE_DIR}/.git") + if(NOT GIT) + SET(GIT $ENV{GIT}) + endif() + if(NOT GIT) + FIND_PROGRAM(GIT NAMES git git.exe git.cmd) + endif() + IF(GIT) + EXECUTE_PROCESS( + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${GIT} describe --tags + OUTPUT_VARIABLE GIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + EXECUTE_PROCESS( + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${GIT} rev-parse --short HEAD + OUTPUT_VARIABLE GIT_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF() +ENDIF() +if(NOT GIT_VERSION) + SET(BUILD_VERSION "v0.0.16") +else() + set(BUILD_VERSION ${GIT_VERSION}) +endif() +message("BUILD_VERSION ${PROJECT_NAME}:${BUILD_VERSION}; GIT_VERSION: ${GIT_VERSION} GIT_REVISION: ${GIT_REVISION}") +#set(VERSION ${BUILD_VERSION}) + +#打开 qt 编译工具 +SET(CMAKE_AUTOUIC ON) +SET(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTORCC ON) +SET(CMAKE_VERBOSE_MAKEFILE ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core) +if(QT_FOUND) + SET(QT_COMPONENTS Core) + if(ANDROID AND (Qt${QT_VERSION_MAJOR}_VERSION VERSION_LESS 6.0.0)) + LIST(APPEND QT_COMPONENTS AndroidExtras) + endif() + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${QT_COMPONENTS}) + message("QT_VERSION:${Qt${QT_VERSION_MAJOR}_VERSION}") + if(Qt${QT_VERSION_MAJOR}_VERSION VERSION_LESS 5.10.0 AND ANDROID) + message(FATAL_ERROR "Qt must great 5.10.0") + endif() + if(Qt${QT_VERSION_MAJOR}_FOUND) + FOREACH(_COMPONENT ${QT_COMPONENTS}) + list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::${_COMPONENT}) + ENDFOREACH() + endif() + get_filename_component(QT_INSTALL_DIR "${Qt${QT_VERSION_MAJOR}_DIR}/../../.." ABSOLUTE) + message("QT_INSTALL_DIR:${QT_INSTALL_DIR}") + message("QT_LIBRARIES:${QT_LIBRARIES}") +endif() + +IF(MSVC) + # This option is to enable the /MP switch for Visual Studio 2005 and above compilers + OPTION(WIN32_USE_MP "Set to ON to build with the /MP option (Visual Studio 2005 and above)." ON) + MARK_AS_ADVANCED(WIN32_USE_MP) + IF(WIN32_USE_MP) + #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + add_compile_options(/MP) + ENDIF(WIN32_USE_MP) + add_compile_options("$<$:/utf-8>") + add_compile_options("$<$:/utf-8>") +ENDIF(MSVC) + +SET(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libs") +if (BUILD_SHARED_LIBS) + add_definitions(-DBUILD_SHARED_LIBS) + if (CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW) + # Just setting CMAKE_POSITION_INDEPENDENT_CODE should be enough to set + # -fPIC for GCC but sometimes it still doesn't get set, so make sure it + # does. + add_definitions("-fPIC") + endif() + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +else(BUILD_SHARED_LIBS) + add_definitions(-DQT_STATICPLUGIN) +endif(BUILD_SHARED_LIBS) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) +include(GenerateExportHeader) +include(CheckIncludeFile) +include(CheckIncludeFileCXX) +include(CheckFunctionExists) + +#CHECK_INCLUDE_FILE_CXX("string" HAVE_STRING_H) +#check_include_file("math.h" HAVE_MATH_H) + +#check_function_exists("fabs" HAVE_FABS) + +set(BUILD_PLATFORM "${CMAKE_SYSTEM_NAME}") +# ---------------------------------------------------------------------------- +# Detect compiler and target platform architecture +# ---------------------------------------------------------------------------- +if(NOT ANDROID) + if(X86_64 OR CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BUILD_ARCH x86_64) + elseif(X86 OR CMAKE_SIZEOF_VOID_P EQUAL 4) + set(BUILD_ARCH x86) + endif() +else() + set(BUILD_ARCH ${CMAKE_SYSTEM_PROCESSOR}) +endif() + +add_subdirectory(QFtpServerLib) +option(WITH_APP "Build application" ON) +if(WITH_APP) + add_subdirectory(QFtpServer) + add_subdirectory(QFtpServerCommandLine) +endif() +#option(WITH_TEST "Build tests" OFF) +#if(WITH_TEST) +# add_subdirectory(QFtpServerTests) +#endif() + diff --git a/QFtpServer.pro b/QFtpServer.pro index a7e9ec5..46651a9 100644 --- a/QFtpServer.pro +++ b/QFtpServer.pro @@ -12,3 +12,10 @@ equals(QT_MAJOR_VERSION, 4) { } else { message(Unit tests project is available only for Qt4 because it depends on QFtp) } + +OTHER_FILES += README.md \ + .travis.yml \ + appveyor.yml \ + ci/* \ + tag.sh \ + CMakeLists.txt diff --git a/QFtpServer/CMakeLists.txt b/QFtpServer/CMakeLists.txt new file mode 100644 index 0000000..093c4fa --- /dev/null +++ b/QFtpServer/CMakeLists.txt @@ -0,0 +1,40 @@ +# Author: Kang Lin + +project(QFtpServer) + +SET(QT_COMPONENTS Gui Widgets) +if(ANDROID AND (Qt${QT_VERSION_MAJOR}_VERSION VERSION_LESS 6.0.0)) + SET(QT_COMPONENTS ${QT_COMPONENTS} AndroidExtras) +endif() +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${QT_COMPONENTS}) +if(Qt${QT_VERSION_MAJOR}_FOUND) + FOREACH(_COMPONENT ${QT_COMPONENTS}) + list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::${_COMPONENT}) + ENDFOREACH() +endif() +message("${PROJECT_NAME} QT_LIBRARIES:${QT_LIBRARIES}") + +set(HEADER_FILES + mainwindow.h + ftpthread.h + debuglogdialog.h + ) + +set(SOURCE_FILES + main.cpp + mainwindow.cpp + debuglogdialog.cpp + ftpthread.cpp + ) + +set(UI_FILES + mainwindow.ui + debuglogdialog.ui + ) + +set(RCC_FILES + res.qrc) + +add_executable(${PROJECT_NAME} ${HEADER_FILES} ${SOURCE_FILES} ${UI_FILES} ${RCC_FILES}) +target_link_libraries(${PROJECT_NAME} QFtpServerLib ${QT_LIBRARIES}) +install(TARGETS ${PROJECT_NAME}) diff --git a/QFtpServer/QFtpServer.pro b/QFtpServer/QFtpServer.pro index ef8d8b3..5d96f43 100644 --- a/QFtpServer/QFtpServer.pro +++ b/QFtpServer/QFtpServer.pro @@ -22,10 +22,13 @@ symbian:TARGET.CAPABILITY += NetworkServices # CONFIG += mobility # MOBILITY += -SOURCES += main.cpp mainwindow.cpp \ - debuglogdialog.cpp +SOURCES += main.cpp \ + mainwindow.cpp \ + debuglogdialog.cpp \ + ftpthread.cpp HEADERS += mainwindow.h \ - debuglogdialog.h + ftpthread.h \ + debuglogdialog.h FORMS += mainwindow.ui \ debuglogdialog.ui QT += network @@ -41,9 +44,10 @@ RESOURCES += \ OTHER_FILES += \ cacert.pem \ - android/AndroidManifest.xml + CMakeLists.txt -ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +# android +android:include(android/android.pri) DISTFILES += diff --git a/QFtpServer/android/AndroidManifest.xml b/QFtpServer/android/AndroidManifest.xml index 9badde3..8408cd7 100644 --- a/QFtpServer/android/AndroidManifest.xml +++ b/QFtpServer/android/AndroidManifest.xml @@ -1,40 +1,83 @@ - - - - + + + + + + + + + + + + + + + + + - + + + + - + - + - - - + + + + + - - - + + + + - + + + + + + + + + + + + + + + + + - - - - - - + diff --git a/QFtpServer/android/android.pri b/QFtpServer/android/android.pri new file mode 100644 index 0000000..92ff8a1 --- /dev/null +++ b/QFtpServer/android/android.pri @@ -0,0 +1,8 @@ +#qthelp://org.qt-project.qtdoc.5120/qtdoc/deployment-android.html +ANDROID_PACKAGE_SOURCE_DIR = $$PWD + +include($$PWD/android_lib/android/android.pri) +OTHER_FILES += \ + $$PWD/AndroidManifest.xml \ + $$PWD/build.gradle \ + $$PWD/settings.gradle diff --git a/QFtpServer/android/android_lib b/QFtpServer/android/android_lib new file mode 160000 index 0000000..ff4544d --- /dev/null +++ b/QFtpServer/android/android_lib @@ -0,0 +1 @@ +Subproject commit ff4544d83063419492066f12930f4130abc98443 diff --git a/QFtpServer/android/build.gradle b/QFtpServer/android/build.gradle new file mode 100644 index 0000000..ae92655 --- /dev/null +++ b/QFtpServer/android/build.gradle @@ -0,0 +1,58 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.0' + } +} + +repositories { + google() + jcenter() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':android_lib/android/QtAndroidUtilsModule') +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion androidBuildToolsVersion + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + lintOptions { + abortOnError false + } +} diff --git a/QFtpServer/android/res/drawable-hdpi/close_high.png b/QFtpServer/android/res/drawable-hdpi/close_high.png new file mode 100644 index 0000000..737f5b8 Binary files /dev/null and b/QFtpServer/android/res/drawable-hdpi/close_high.png differ diff --git a/QFtpServer/android/res/drawable-hdpi/icon.png b/QFtpServer/android/res/drawable-hdpi/icon.png index acba56f..9f5f734 100644 Binary files a/QFtpServer/android/res/drawable-hdpi/icon.png and b/QFtpServer/android/res/drawable-hdpi/icon.png differ diff --git a/QFtpServer/android/res/drawable-ldpi/close_down.png b/QFtpServer/android/res/drawable-ldpi/close_down.png new file mode 100644 index 0000000..1337954 Binary files /dev/null and b/QFtpServer/android/res/drawable-ldpi/close_down.png differ diff --git a/QFtpServer/android/res/drawable-ldpi/icon.png b/QFtpServer/android/res/drawable-ldpi/icon.png index acba56f..9f5f734 100644 Binary files a/QFtpServer/android/res/drawable-ldpi/icon.png and b/QFtpServer/android/res/drawable-ldpi/icon.png differ diff --git a/QFtpServer/android/res/drawable-mdpi/close_normal.png b/QFtpServer/android/res/drawable-mdpi/close_normal.png new file mode 100644 index 0000000..9420fe5 Binary files /dev/null and b/QFtpServer/android/res/drawable-mdpi/close_normal.png differ diff --git a/QFtpServer/android/res/drawable-mdpi/icon.png b/QFtpServer/android/res/drawable-mdpi/icon.png index acba56f..9f5f734 100644 Binary files a/QFtpServer/android/res/drawable-mdpi/icon.png and b/QFtpServer/android/res/drawable-mdpi/icon.png differ diff --git a/QFtpServer/android/res/drawable-nodpi/close_normal.png b/QFtpServer/android/res/drawable-nodpi/close_normal.png new file mode 100644 index 0000000..9420fe5 Binary files /dev/null and b/QFtpServer/android/res/drawable-nodpi/close_normal.png differ diff --git a/QFtpServer/android/res/drawable-nodpi/icon.png b/QFtpServer/android/res/drawable-nodpi/icon.png new file mode 100644 index 0000000..9f5f734 Binary files /dev/null and b/QFtpServer/android/res/drawable-nodpi/icon.png differ diff --git a/QFtpServer/android/settings.gradle b/QFtpServer/android/settings.gradle new file mode 100644 index 0000000..e7c8bf0 --- /dev/null +++ b/QFtpServer/android/settings.gradle @@ -0,0 +1 @@ +include ':android_lib/android/QtAndroidUtilsModule' diff --git a/QFtpServer/ftpthread.cpp b/QFtpServer/ftpthread.cpp new file mode 100644 index 0000000..a38e331 --- /dev/null +++ b/QFtpServer/ftpthread.cpp @@ -0,0 +1,35 @@ +// Author: Kang Lin (kl222@126.com) + +#include "ftpthread.h" +#include "ftpserver.h" + +CFtpThread::CFtpThread(QString szPath, + int nPort, + QString szUser, + QString szPassword, + bool bReadOnly, + QObject *parent) + : QThread(parent), + m_szPath(szPath), + m_nPort(nPort), + m_szUser(szUser), + m_szPassword(szPassword), + m_bReadOnly(bReadOnly) +{ +} + +void CFtpThread::run() +{ + CFtpServer server(nullptr, m_szPath, m_nPort, m_szUser, + m_szPassword, m_bReadOnly); + bool check = connect(&server, SIGNAL(newPeerIp(const QString&)), + this, SIGNAL(sigNewPeerIp(const QString&))); + Q_ASSERT(check); + if (server.Listening()) { + emit sigMessage("Listening at " + CFtpServer::lanIp()); + } else { + emit sigMessage("Not listening"); + } + + exec(); +} diff --git a/QFtpServer/ftpthread.h b/QFtpServer/ftpthread.h new file mode 100644 index 0000000..4c41209 --- /dev/null +++ b/QFtpServer/ftpthread.h @@ -0,0 +1,34 @@ +// Author: Kang Lin (kl222@126.com) + +#ifndef CBACKGROUNDTHREAD_H_KL_2021_4_4 +#define CBACKGROUNDTHREAD_H_KL_2021_4_4 + +#include + +class CFtpThread : public QThread +{ + Q_OBJECT + +public: + explicit CFtpThread(QString szPath, + int nPort, + QString szUser = QString(), + QString szPassword = QString(), + bool bReadOnly = false, + QObject *parent = nullptr); +signals: + void sigMessage(const QString &msg); + void sigNewPeerIp(const QString &ip); + +protected: + virtual void run() override; + +private: + QString m_szPath; + int m_nPort; + QString m_szUser; + QString m_szPassword; + bool m_bReadOnly; +}; + +#endif // CBACKGROUNDTHREAD_H_KL_2021_4_4 diff --git a/QFtpServer/main.cpp b/QFtpServer/main.cpp index e8eb407..aac768d 100644 --- a/QFtpServer/main.cpp +++ b/QFtpServer/main.cpp @@ -1,11 +1,16 @@ #include "mainwindow.h" #include +#if defined(Q_OS_ANDROID) +#include +#endif int main(int argc, char *argv[]) { QApplication app(argc, argv); - +#if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + QtAndroid::hideSplashScreen(); +#endif // Needed for QSettings. app.setOrganizationName("CodeThesis"); app.setApplicationName("QFtpServer"); diff --git a/QFtpServer/mainwindow.cpp b/QFtpServer/mainwindow.cpp index ab544a8..adcbb28 100644 --- a/QFtpServer/mainwindow.cpp +++ b/QFtpServer/mainwindow.cpp @@ -3,22 +3,43 @@ #include "ftpserver.h" #include "debuglogdialog.h" -#include +#if defined(Q_OS_ANDROID) + #include "AndroidUtils.h" + #include "AndroidDirectory.h" +#endif + +#include #include #include +#include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), ui(new Ui::MainWindow) + : QMainWindow(parent), + ui(new Ui::MainWindow), + server(nullptr), + m_TrayIcon(this), + m_TrayIconMenu(this) { ui->setupUi(this); - + + m_nCloseType = UNKNOW; + ui->lineEditPort->setValidator(new QIntValidator(1, 65535, this)); #if defined(Q_OS_ANDROID) + ui->checkBoxDisableLockScreen->checkState() == Qt::Checked + ? CAndroidUtils::PowerWakeLock() : CAndroidUtils::PowerWakeLock(false); + CAndroidUtils::InitPermissions(); + connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), + this, SLOT(slotApplicationStateChanged(Qt::ApplicationState))); // Fix for the bug android keyboard bug - see // http://stackoverflow.com/q/21074012/492336. foreach (QLineEdit *lineEdit, findChildren()) { - connect(lineEdit, SIGNAL(editingFinished()), QGuiApplication::inputMethod(), SLOT(hide())); + connect(lineEdit, SIGNAL(editingFinished()), + QGuiApplication::inputMethod(), SLOT(hide())); } #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) @@ -33,18 +54,40 @@ MainWindow::MainWindow(QWidget *parent) #else // The exit button is needed only for Android. Hide it for other builds. ui->pushButtonExit->hide(); + ui->checkBoxDisableLockScreen->hide(); #endif // Q_OS_ANDROID // Set window icon. setWindowIcon(QIcon(":/icons/appicon")); - + if(QSystemTrayIcon::isSystemTrayAvailable()) + { + bool check = connect(&m_TrayIcon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + SLOT(slotTrayIconActive(QSystemTrayIcon::ActivationReason))); + Q_ASSERT(check); + QAction* pExit = m_TrayIconMenu.addAction( + QIcon(":/icons/close"), + tr("Exit"), + this, + SLOT(slotActionExit(bool))); + Q_ASSERT(pExit); + m_TrayIcon.setContextMenu(&m_TrayIconMenu); + m_TrayIcon.setToolTip(tr("QFtpServer")); + m_TrayIcon.setIcon(this->windowIcon()); + m_TrayIcon.show(); + } + loadSettings(); - server = 0; + startServer(); } MainWindow::~MainWindow() { + if(server) + { + server->quit(); + } saveSettings(); delete ui; } @@ -100,7 +143,7 @@ void MainWindow::showExpanded() #elif defined(Q_WS_MAEMO_5) || defined (Q_OS_ANDROID) showMaximized(); #else - show(); + showNormal(); #endif } @@ -119,10 +162,19 @@ void MainWindow::loadSettings() ui->lineEditPort->setText(settings.value("settings/port", defaultPort).toString()); ui->lineEditUserName->setText(settings.value("settings/username", "admin").toString()); ui->lineEditPassword->setText(settings.value("settings/password", "qt").toString()); - ui->lineEditRootPath->setText(settings.value("settings/rootpath", QDir::rootPath()).toString()); + QString rootPath = QDir::rootPath(); +#ifdef Q_OS_ANDROID + QDir root(CAndroidDirectory::GetExternalStorageDirectory()); + if(root.exists()) + rootPath = CAndroidDirectory::GetExternalStorageDirectory(); +#endif + ui->lineEditRootPath->setText(settings.value("settings/rootpath", rootPath).toString()); + //ui->lineEditRootPath->setText(settings.value("settings/rootpath", QDir::rootPath()).toString()); ui->checkBoxAnonymous->setChecked(settings.value("settings/anonymous", false).toBool()); ui->checkBoxReadOnly->setChecked(settings.value("settings/readonly", false).toBool()); - ui->checkBoxOnlyOneIpAllowed->setChecked(settings.value("settings/oneip", true).toBool()); +#ifndef Q_OS_ANDROID + m_nCloseType = settings.value("settings/closetype", UNKNOW).toInt(); +#endif } void MainWindow::saveSettings() @@ -134,7 +186,9 @@ void MainWindow::saveSettings() settings.setValue("settings/rootpath", ui->lineEditRootPath->text()); settings.setValue("settings/anonymous", ui->checkBoxAnonymous->isChecked()); settings.setValue("settings/readonly", ui->checkBoxReadOnly->isChecked()); - settings.setValue("settings/oneip", ui->checkBoxOnlyOneIpAllowed->isChecked()); +#ifndef Q_OS_ANDROID + settings.setValue("settings/closetype", m_nCloseType); +#endif } void MainWindow::startServer() @@ -145,15 +199,28 @@ void MainWindow::startServer() userName = ui->lineEditUserName->text(); password = ui->lineEditPassword->text(); } - delete server; - server = new FtpServer(this, ui->lineEditRootPath->text(), ui->lineEditPort->text().toInt(), userName, - password, ui->checkBoxReadOnly->isChecked(), ui->checkBoxOnlyOneIpAllowed->isChecked()); - connect(server, SIGNAL(newPeerIp(QString)), SLOT(onPeerIpChanged(QString))); - if (server->isListening()) { - ui->statusBar->showMessage("Listening at " + FtpServer::lanIp()); - } else { - ui->statusBar->showMessage("Not listening"); + + if(server) + { + server->quit(); + } + server = new CFtpThread(ui->lineEditRootPath->text(), + ui->lineEditPort->text().toInt(), + userName, + password, + ui->checkBoxReadOnly->isChecked()); + bool check = connect(server, SIGNAL(sigNewPeerIp(const QString&)), + this, SLOT(onPeerIpChanged(const QString&))); + Q_ASSERT(check); + check = connect(server, SIGNAL(sigMessage(const QString&)), + this, SLOT(onMessage(const QString&))); + Q_ASSERT(check); + // Delete server + check = connect(server, SIGNAL(finished()), + server, SLOT(deleteLater())); + Q_ASSERT(check); + server->start(); } void MainWindow::on_pushButtonRestartServer_clicked() @@ -191,6 +258,11 @@ void MainWindow::onPeerIpChanged(const QString &peerIp) ui->statusBar->showMessage("Connected to " + peerIp); } +void MainWindow::onMessage(const QString& msg) +{ + ui->statusBar->showMessage(msg); +} + void MainWindow::on_pushButtonShowDebugLog_clicked() { DebugLogDialog *dlg = new DebugLogDialog; @@ -199,7 +271,116 @@ void MainWindow::on_pushButtonShowDebugLog_clicked() dlg->showExpanded(); } +void MainWindow::slotTrayIconActive(QSystemTrayIcon::ActivationReason e) +{ + if(QSystemTrayIcon::Trigger == e) + { + showExpanded(); + } +} + +#if !defined(Q_OS_ANDROID) +void MainWindow::closeEvent(QCloseEvent *e) +{ + if(EXIT & m_nCloseType) + { + m_nCloseType &= ~EXIT; + return; + } + switch(m_nCloseType) + { + case UNKNOW: + { + QMessageBox msg(QMessageBox::Question, + tr("Close"), + tr("Is closed the programe or hidden programe windows?"), + QMessageBox::Close | QMessageBox::Yes | QMessageBox::Cancel, + this); + + msg.setButtonText(QMessageBox::Yes, tr("Hidden")); + msg.setButtonText(QMessageBox::Cancel, tr("Cancel")); + msg.setDefaultButton(QMessageBox::Close); + #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QCheckBox cbSave(tr("Save the setting"), &msg); + msg.setCheckBox(&cbSave); + #endif + int nRet = msg.exec(); + switch(nRet) + { + case QMessageBox::Yes: + { + this->hide(); + m_nCloseType = HIDE; + e->ignore(); + break; + } + case QMessageBox::Cancel: + { + e->ignore(); + break; + } + default: + m_nCloseType = CLOSE; + } + + #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + if(!msg.checkBox()->isChecked()) + m_nCloseType = UNKNOW; + #endif + + break; + } + case HIDE: + { + this->hide(); + e->ignore(); + break; + } + } +} +#endif + +void MainWindow::slotActionExit(bool checked) +{ + Q_UNUSED(checked); + on_pushButtonExit_clicked(); +} + void MainWindow::on_pushButtonExit_clicked() { + m_nCloseType |= EXIT; close(); } + +#if defined(Q_OS_ANDROID) +void MainWindow::slotApplicationStateChanged(Qt::ApplicationState state) +{ + qDebug() << "stats: " << state << " closetype: " << m_nCloseType; + if(EXIT & m_nCloseType) + { + m_nCloseType &= ~EXIT; + return; + } + + if(Qt::ApplicationState::ApplicationInactive == state) + m_Notification.Show(tr("QFtpServer is running in backgroup"), + tr("QFtpServer"), + 0, + QImage(":/icons/appicon"), + QImage(":/icons/appicon") + ); + if(Qt::ApplicationState::ApplicationActive == state) + m_Notification.Cancel(); +} +#endif + +#if defined (Q_OS_ANDROID) +void MainWindow::on_checkBoxDisableLockScreen_stateChanged(int arg1) +{ + if(Qt::Checked == arg1) + CAndroidUtils::PowerWakeLock(); + if(Qt::Unchecked == arg1) { + CAndroidUtils::PowerWakeLock(false); + } +} +#endif diff --git a/QFtpServer/mainwindow.h b/QFtpServer/mainwindow.h index e2d56fc..9cd712e 100644 --- a/QFtpServer/mainwindow.h +++ b/QFtpServer/mainwindow.h @@ -2,12 +2,20 @@ #define MAINWINDOW_H #include +#include +#include +#include +#if defined(Q_OS_ANDROID) + #include "Notification.h" +#endif + +#include "ftpthread.h" namespace Ui { class MainWindow; } -class FtpServer; +class CFtpServer; class MainWindow : public QMainWindow { @@ -29,20 +37,29 @@ class MainWindow : public QMainWindow private slots: void on_pushButtonRestartServer_clicked(); - void on_toolButtonBrowse_clicked(); - void onPeerIpChanged(const QString &peerIp); - + void onMessage(const QString &msg); void on_pushButtonShowDebugLog_clicked(); - void on_pushButtonExit_clicked(); - + + // System tray icon + void slotTrayIconActive(QSystemTrayIcon::ActivationReason e); + void slotActionExit(bool checked); +#if defined(Q_OS_ANDROID) + void slotApplicationStateChanged(Qt::ApplicationState state); + void on_checkBoxDisableLockScreen_stateChanged(int arg1); +#else + +protected: + virtual void closeEvent(QCloseEvent *e); +#endif + private: Ui::MainWindow *ui; // This is the FTP server object. - FtpServer *server; + CFtpThread *server; // Load the stored settings. void loadSettings(); @@ -52,6 +69,21 @@ private slots: // Restart the FTP server. void startServer(); + + // System tray icon + QSystemTrayIcon m_TrayIcon; + QMenu m_TrayIconMenu; + enum _CLOSE_TYPE + { + UNKNOW = 0, + CLOSE = 1, + HIDE = 2, + EXIT = 4 + }; + int m_nCloseType; +#if defined(Q_OS_ANDROID) + CNotification m_Notification; +#endif }; #endif // MAINWINDOW_H diff --git a/QFtpServer/mainwindow.ui b/QFtpServer/mainwindow.ui index b0b8fbf..064208a 100644 --- a/QFtpServer/mainwindow.ui +++ b/QFtpServer/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 206 - 344 + 381 @@ -18,20 +18,6 @@ - - - - ... - - - - - - - Port: - - - @@ -39,30 +25,6 @@ - - - - Restart FTP Server - - - - - - - Root path: - - - - - - - Allow anonymous login - - - - - - @@ -70,6 +32,13 @@ + + + + Debug Console + + + @@ -77,10 +46,13 @@ - - + + + + + - Debug Console + Allow anonymous login @@ -91,17 +63,10 @@ - - - - Only one IP allowed - - - - Qt::Vertical + Qt::Orientation::Vertical @@ -125,6 +90,50 @@ + + + + + 0 + 0 + + + + Disable lock screen + + + false + + + + + + + ... + + + + + + + Restart FTP Server + + + + + + + Port: + + + + + + + Root path: + + + diff --git a/QFtpServer/res.qrc b/QFtpServer/res.qrc index 8536ed9..5e7fe53 100644 --- a/QFtpServer/res.qrc +++ b/QFtpServer/res.qrc @@ -1,5 +1,6 @@ android/res/drawable-hdpi/icon.png + android/res/drawable-hdpi/close_high.png diff --git a/QFtpServerCommandLine/CMakeLists.txt b/QFtpServerCommandLine/CMakeLists.txt new file mode 100644 index 0000000..5c7f2ab --- /dev/null +++ b/QFtpServerCommandLine/CMakeLists.txt @@ -0,0 +1,20 @@ +# Author: Kang Lin + +project(QFtpServerCommandLine) + +SET(_COMPONENTS Core Network) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${_COMPONENTS}) +if(Qt${QT_VERSION_MAJOR}_FOUND) + FOREACH(_COMPONENT ${_COMPONENTS}) + list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::${_COMPONENT}) + ENDFOREACH() +endif() +message("${PROJECT_NAME} QT_LIBRARIES:${QT_LIBRARIES}") + +set(SOURCE_FILES + main.cpp + ) + +add_executable(${PROJECT_NAME} ${SOURCE_FILES}) +target_link_libraries(${PROJECT_NAME} QFtpServerLib ${QT_LIBRARIES}) +install(TARGETS ${PROJECT_NAME}) diff --git a/QFtpServerCommandLine/QFtpServerCommandLine.pro b/QFtpServerCommandLine/QFtpServerCommandLine.pro index 57a7c3c..7fb5653 100644 --- a/QFtpServerCommandLine/QFtpServerCommandLine.pro +++ b/QFtpServerCommandLine/QFtpServerCommandLine.pro @@ -16,3 +16,6 @@ else:unix: LIBS += -L$$OUT_PWD/../QFtpServerLib/ -lQFtpServerLib INCLUDEPATH += $$PWD/../QFtpServerLib DEPENDPATH += $$PWD/../QFtpServerLib + +# android +android:include($$PWD/../QFtpServer/android/android.pri) diff --git a/QFtpServerCommandLine/main.cpp b/QFtpServerCommandLine/main.cpp index 1d4ca1b..19e57ca 100644 --- a/QFtpServerCommandLine/main.cpp +++ b/QFtpServerCommandLine/main.cpp @@ -3,10 +3,20 @@ #include #include #include +#if defined(Q_OS_ANDROID) + #include "AndroidUtils.h" +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + #include +#endif QChar getRandomChar() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + return QChar('a' + (QRandomGenerator::securelySeeded().generate()%('z'-'a'))); +#else return QChar('a' + (qrand()%('z'-'a'))); +#endif } QString getRandomString(int n) @@ -21,18 +31,23 @@ QString getRandomString(int n) int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - +#if defined(Q_OS_ANDROID) + CAndroidUtils::InitPermissions(); +#endif // Seed the random numbers. +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QRandomGenerator::securelySeeded().seed(QTime::currentTime().msec()); +#else qsrand(QTime::currentTime().msec()); - +#endif const QString &userName = getRandomString(3); const QString &password = getRandomString(3); const QString &rootPath = QDir::currentPath(); // *TODO: Allow using port 0. - FtpServer server(&a, rootPath, 2121, userName, password, false, false); - if (server.isListening()) { - qDebug() << QString("Listening at %1:2121").arg(FtpServer::lanIp()).toStdString().c_str(); + CFtpServer server(&a, rootPath, 2121, userName, password, false); + if (server.Listening()) { + qDebug() << QString("Listening at %1:2121").arg(CFtpServer::lanIp()).toStdString().c_str(); qDebug() << QString("User: %1").arg(userName).toStdString().c_str(); qDebug() << QString("Password: %1").arg(password).toStdString().c_str(); return a.exec(); diff --git a/QFtpServerLib/CMakeLists.txt b/QFtpServerLib/CMakeLists.txt new file mode 100644 index 0000000..8500c2b --- /dev/null +++ b/QFtpServerLib/CMakeLists.txt @@ -0,0 +1,83 @@ +# Author: Kang Lin + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "verbose") + +project(QFtpServerLib LANGUAGES CXX) + +SET(QT_LIBRARIES "") +SET(LIB_QT_COMPONENTS Core Network) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${LIB_QT_COMPONENTS}) +if(Qt${QT_VERSION_MAJOR}_FOUND) + FOREACH(_COMPONENT ${LIB_QT_COMPONENTS}) + list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::${_COMPONENT}) + ENDFOREACH() +endif() +message("${PROJECT_NAME} QT_LIBRARIES:${QT_LIBRARIES}") + +set(INSTALL_HEADER_FILES + ftpserver.h + qftpserverlib_global.h + ) + +set(HEADER_FILES + ${INSTALL_HEADER_FILES} + dataconnection.h + ftpcommand.h + ftpcontrolconnection.h + ftplistcommand.h + ftpretrcommand.h + ftpstorcommand.h + sslserver.h + ) + +set(SOURCE_FILES + dataconnection.cpp + ftpcommand.cpp + ftpcontrolconnection.cpp + ftplistcommand.cpp + ftpretrcommand.cpp + ftpserver.cpp + ftpstorcommand.cpp + sslserver.cpp + ) + +set(RCC_FILES certificates.qrc) + +add_library(${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES} ${RCC_FILES}) +target_link_libraries(${PROJECT_NAME} PUBLIC ${QT_LIBRARIES}) +target_compile_definitions(${PROJECT_NAME} PRIVATE QFTPSERVERLIB_LIBRARY) +target_include_directories(${PROJECT_NAME} PUBLIC + "$") +set_target_properties(${PROJECT_NAME} PROPERTIES + PUBLIC_HEADER "${INSTALL_HEADER_FILES}" # Install head files + VERSION ${BUILD_VERSION} + ) + +export(TARGETS ${PROJECT_NAME} + APPEND FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ) +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/QFtpServer + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/QFtpServer + ) +install(EXPORT ${PROJECT_NAME}Targets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/${PROJECT_NAME}Config.cmake.in" + "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" + IMMEDIATE @ONLY) +install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + RENAME ${PROJECT_NAME}Config.cmake) + +write_basic_package_version_file( + "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${BUILD_VERSION} + COMPATIBILITY AnyNewerVersion) +install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") diff --git a/QFtpServerLib/QFtpServerLib.pro b/QFtpServerLib/QFtpServerLib.pro index b7d2992..eb52449 100644 --- a/QFtpServerLib/QFtpServerLib.pro +++ b/QFtpServerLib/QFtpServerLib.pro @@ -24,7 +24,7 @@ SOURCES += \ sslserver.cpp HEADERS +=\ - qftpserverlib_global.h \ + qftpserverlib_global.h \ dataconnection.h \ ftpcommand.h \ ftpcontrolconnection.h \ @@ -41,3 +41,5 @@ unix { RESOURCES += \ certificates.qrc + +OTHER_FILES += CMakeLists.txt diff --git a/QFtpServerLib/ftpcontrolconnection.cpp b/QFtpServerLib/ftpcontrolconnection.cpp index 7af56e2..4f2ddf7 100644 --- a/QFtpServerLib/ftpcontrolconnection.cpp +++ b/QFtpServerLib/ftpcontrolconnection.cpp @@ -14,6 +14,9 @@ #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + #include +#endif FtpControlConnection::FtpControlConnection(QObject *parent, QSslSocket *socket, const QString &rootPath, const QString &userName, const QString &password, bool readOnly) : QObject(parent) @@ -137,7 +140,11 @@ QString FtpControlConnection::toLocalPath(const QString &fileName) const // Note we do this **before** prepending the root path, in order to avoid // "jailbreaking" out of the "chroot". QStringList components; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + foreach (const QString &component, localPath.split('/', Qt::SkipEmptyParts)) { +#else foreach (const QString &component, localPath.split('/', QString::SkipEmptyParts)) { +#endif if (component == "..") { if (!components.isEmpty()) { components.pop_back(); @@ -256,10 +263,21 @@ void FtpControlConnection::port(const QString &addressAndPort) // PORT h1,h2,h3,h4,p1,p2 // Get IP and port. + QString hostName; + int port = 0; +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QRegularExpression re("\\s*(\\d+,\\d+,\\d+,\\d+),(\\d+),(\\d+)"); + QRegularExpressionMatch match = re.match(addressAndPort); + if (match.hasMatch()) { + hostName = match.captured(1).replace(',', '.'); + port = match.captured(2).toInt() * 256 + match.captured(3).toInt(); + } +#else QRegExp exp("\\s*(\\d+,\\d+,\\d+,\\d+),(\\d+),(\\d+)"); exp.indexIn(addressAndPort); - QString hostName = exp.cap(1).replace(',', '.'); - int port = exp.cap(2).toInt() * 256 + exp.cap(3).toInt(); + hostName = exp.cap(1).replace(',', '.'); + port = exp.cap(2).toInt() * 256 + exp.cap(3).toInt(); +#endif dataConnection->scheduleConnectToHost(hostName, port, encryptDataConnection); reply("200 Command okay."); } diff --git a/QFtpServerLib/ftpserver.cpp b/QFtpServerLib/ftpserver.cpp index 2d35570..829d7e2 100644 --- a/QFtpServerLib/ftpserver.cpp +++ b/QFtpServerLib/ftpserver.cpp @@ -1,37 +1,80 @@ +#include +#include +#include +#include + #include "ftpserver.h" #include "ftpcontrolconnection.h" #include "sslserver.h" -#include -#include -#include +static Q_LOGGING_CATEGORY(log, "FtpServer") +CFtpServer::CFtpServer(QObject *parent, const QString &rootPath, quint16 port, + const QString &userName, const QString &password, + bool readOnly) : QObject(parent) + , m_pFilter(this) + , m_nPort(port) + , m_szUserName(userName) + , m_szPassword(password) + , m_szRootPath(rootPath) + , m_bReadOnly(readOnly) +{ +} + +CFtpServer::~CFtpServer() +{ + qDebug(log) << Q_FUNC_INFO; + foreach (auto s, m_pServer) { + if(s) { + s->close(); + delete s; + } + } +} + +bool CFtpServer::Listening(const QHostAddress &addr) +{ + bool bRet = false; + auto pServer = new SslServer(this); + if(!pServer) { + qCritical(log) << "new SslServer fail. no memery"; + return false; + } + + bRet = pServer->listen(addr, m_nPort); + if(!bRet) + { + qCritical(log) << "Server listen at" << m_nPort << " fail:" << pServer->errorString(); + delete pServer; + return false; + } + qDebug(log) << "Server listen at" << addr.toString() << "Port:" << m_nPort; + bRet = connect(pServer, SIGNAL(newConnection()), this, SLOT(startNewControlConnection())); + Q_ASSERT(bRet); + m_pServer.insert(pServer); + return true; +} -FtpServer::FtpServer(QObject *parent, const QString &rootPath, int port, const QString &userName, const QString &password, bool readOnly, bool onlyOneIpAllowed) : - QObject(parent) +bool CFtpServer::Listening(const QList &addr) { - server = new SslServer(this); - // In Qt4, QHostAddress::Any listens for IPv4 connections only, but as of - // Qt5, it now listens on all available interfaces, and - // QHostAddress::AnyIPv4 needs to be used if we want only IPv4 connections. -#if QT_VERSION >= 0x050000 - server->listen(QHostAddress::AnyIPv4, port); -#else - server->listen(QHostAddress::Any, port); -#endif - connect(server, SIGNAL(newConnection()), this, SLOT(startNewControlConnection())); - this->userName = userName; - this->password = password; - this->rootPath = rootPath; - this->readOnly = readOnly; - this->onlyOneIpAllowed = onlyOneIpAllowed; + bool bRet = false; + foreach (auto a, addr) { + bool b = Listening(a); + if(b) + bRet = b; + } + return bRet; } -bool FtpServer::isListening() +bool CFtpServer::isListening() { - return server->isListening(); + foreach (auto s, m_pServer) { + if(s->isListening()) + return true; + } + return false; } -QString FtpServer::lanIp() +QString CFtpServer::lanIp() { foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) { if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) { @@ -41,25 +84,44 @@ QString FtpServer::lanIp() return ""; } -void FtpServer::startNewControlConnection() +void CFtpServer::startNewControlConnection() { - QSslSocket *socket = (QSslSocket *) server->nextPendingConnection(); + SslServer* pServer = qobject_cast(sender()); + QSslSocket* pSocket = qobject_cast(pServer->nextPendingConnection()); + if(!pSocket) { + qCritical() << "The socket is nullptr"; + return; + } // If this is not a previously encountered IP emit the newPeerIp signal. - QString peerIp = socket->peerAddress().toString(); + QString peerIp = pSocket->peerAddress().toString(); qDebug() << "connection from" << peerIp; - if (!encounteredIps.contains(peerIp)) { - // If we don't allow more than one IP for the client, we close - // that connection. - if (onlyOneIpAllowed && !encounteredIps.isEmpty()) { - delete socket; - return; - } - - emit newPeerIp(peerIp); - encounteredIps.insert(peerIp); + bool bFilter = false; + if(m_pFilter) + bFilter = m_pFilter->onFilter(pSocket); + if(bFilter) + { + qDebug(log) << "Filte connection from IP:" << peerIp << "Port:" << pSocket->peerPort(); + delete pSocket; + return; } // Create a new FTP control connection on this socket. - new FtpControlConnection(this, socket, rootPath, userName, password, readOnly); + new FtpControlConnection(this, pSocket, m_szRootPath, m_szUserName, m_szPassword, m_bReadOnly); +} + +bool CFtpServer::onFilter(QSslSocket *socket) +{ + if(!socket) return true; + QString ip = socket->peerAddress().toString(); + if (!m_EncounteredIps.contains(ip)) { + emit newPeerIp(ip); + m_EncounteredIps.insert(ip); + } + return false; +} + +void CFtpServer::SetFilter(CFtpServerFilter *handler) +{ + m_pFilter = handler; } diff --git a/QFtpServerLib/ftpserver.h b/QFtpServerLib/ftpserver.h index 6223810..6296fc5 100644 --- a/QFtpServerLib/ftpserver.h +++ b/QFtpServerLib/ftpserver.h @@ -3,20 +3,39 @@ #include #include +#include #include class SslServer; +class QSslSocket; + +class QFTPSERVERLIBSHARED_EXPORT CFtpServerFilter { +public: + virtual bool onFilter(QSslSocket *socket) = 0; +}; // The ftp server. Listens on a port, and starts a new control connection each // time it gets connected. -class QFTPSERVERLIBSHARED_EXPORT FtpServer : public QObject +class QFTPSERVERLIBSHARED_EXPORT CFtpServer : public QObject, CFtpServerFilter { Q_OBJECT public: - explicit FtpServer(QObject *parent, const QString &rootPath, int port = 21, - const QString &userName = QString(), const QString &password = QString(), - bool readOnly = false, bool onlyOneIpAllowed = false); + explicit CFtpServer(QObject *parent, const QString &rootPath, quint16 port = 21, + const QString &userName = QString(), const QString &password = QString(), + bool readOnly = false); + ~CFtpServer(); + bool Listening(const QHostAddress& addr = + // In Qt4, QHostAddress::Any listens for IPv4 connections only, but as of + // Qt5, it now listens on all available interfaces, and + // QHostAddress::AnyIPv4 needs to be used if we want only IPv4 connections. + #if QT_VERSION >= 0x050000 + QHostAddress::AnyIPv4 + #else + QHostAddress::Any + #endif + ); + bool Listening(const QList& addr); // Whether or not the server is listening for incoming connections. If it // is not currently listening then there was an error - probably no @@ -27,6 +46,9 @@ class QFTPSERVERLIBSHARED_EXPORT FtpServer : public QObject // Get the LAN IP of the host, e.g. "192.168.1.10". static QString lanIp(); + void SetFilter(CFtpServerFilter* handler); + virtual bool onFilter(QSslSocket *socket) override; + signals: // A connection from a new IP has been established. This signal is emitted // when the FTP server is connected by a new IP. The new IP will then be @@ -39,32 +61,29 @@ private slots: void startNewControlConnection(); private: + CFtpServerFilter* m_pFilter; + quint16 m_nPort; // If both username and password are empty, it means anonymous mode - any // username and password combination will be accepted. - QString userName; - QString password; + QString m_szUserName; + QString m_szPassword; // The root path is the virtual root directory of the FTP. It works like // chroot (see http://en.wikipedia.org/wiki/Chroot). The FTP server will // not access files or folders that are not in this folder or any of its // subfolders. - QString rootPath; + QString m_szRootPath; - // The SSL server listen for incoming connections. - SslServer *server; + // The SSL m_pServer listen for incoming connections. + QSet m_pServer; // All the IPs that this FTP server object has encountered in its lifetime. // See the signal newPeerIp. - QSet encounteredIps; + QSet m_EncounteredIps; // Whether or not the server is in read-only mode. In read-only mode the // server will not create, modify or delete any files or directories. - bool readOnly; - - // Causes the server to remember the first IP that connects to it, and then - // refuse connections from any other IP. This makes sense because a mobile - // phone is unlikely to be used from 2 places at once. - bool onlyOneIpAllowed; + bool m_bReadOnly; }; #endif // FTPSERVER_H diff --git a/QFtpServerTests/CMakeLists.txt b/QFtpServerTests/CMakeLists.txt new file mode 100644 index 0000000..d1f36a9 --- /dev/null +++ b/QFtpServerTests/CMakeLists.txt @@ -0,0 +1,21 @@ +# Author: Kang Lin + +cmake_minimum_required(VERSION 3.5) + +project(tst_qftpservertests) + +SET(QT_COMPONENTS Test) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${QT_COMPONENTS}) +if(Qt${QT_VERSION_MAJOR}_FOUND) + FOREACH(_COMPONENT ${QT_COMPONENTS}) + list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::${_COMPONENT}) + ENDFOREACH() +endif() +message("QT_LIBRARIES:${QT_LIBRARIES}") + +set(SOURCE_FILES + tst_qftpservertests.cpp + ) + +add_executable(${PROJECT_NAME} ${SOURCE_FILES}) +target_link_libraries(${PROJECT_NAME} QFtpServerLib ${QT_LIBRARIES}) diff --git a/QFtpServerTests/tst_qftpservertests.cpp b/QFtpServerTests/tst_qftpservertests.cpp index 54ee73a..9af4dbb 100644 --- a/QFtpServerTests/tst_qftpservertests.cpp +++ b/QFtpServerTests/tst_qftpservertests.cpp @@ -51,7 +51,7 @@ class Arrange rootPath = "/tmp/ftpservertest/"; removeRecursively(rootPath); QDir().mkpath(rootPath); - server.reset(new FtpServer(0, rootPath, port, params->userName, params->password)); + server.reset(new CFtpServer(0, rootPath, port, params->userName, params->password)); // Create the FTP client (and optionally login). client.reset(new QFtp()); diff --git a/README.md b/README.md index 1ededd6..a974319 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,62 @@ QFtpServer - an FTP server written in Qt ---------------------------------------- +I based it on [QFtpServer](https://github.com/sashoalm/QFtpServer) +and used it for [RabbitRemoteControl](https://github.com/KangLin/RabbitRemoteControl) + ### Description -QFtpServer is an event-driven server which is written entirely in Qt. It is non-blocking and supports multiple connections. +QFtpServer is an event-driven server which is written entirely in Qt. +It is non-blocking and supports multiple connections. + +This project started because I couldn't find a free FTP server for my Symbian +phone so I decided to try and make one of my own. I've tested it against +Filezilla, Chrome, Firefox, Safari, Windows Explorer, and others. -This project started because I couldn't find a free FTP server for my Symbian phone so I decided to try and make one of my own. I've tested it against Filezilla, Chrome, Firefox, Safari, Windows Explorer, and others. +### QFtpServerLib -### Features +#### Features It supports: * Active connections (PORT command). * Passive connections (PASV command). -* Random access for files (APPE, RETR), so stopped uploads and downloads can be continued. +* Random access for files (APPE, RETR), so stopped uploads and downloads can + be continued. * Secure FTP, also known as FTP-ES which uses SSL/TLS sockets. It doesn't support: * IPv6. * FTPS (this standard is obsolete and FTP-ES should be used instead). * Multiple user accounts. +* Securely bind network card interfaces and filter interfaces. + +#### Example usage: + + server = new CFtpServer(this, "/ftp/root/path", 21, "MyUserName", + "MyPassword", false); + server->Listening(); -### Example usage: - server = new FtpServer(this, "/ftp/root/path", 21, "MyUserName", "MyPassword", false, false); +### QFtpServer + +#### Features + +It supports: + ++ Windows ++ linux ++ android ++ Symbian phone + +### Build + + git clone --recursive https://github.com/sashoalm/QFtpServer.git + cd QFtpServer + cmake -E make_directory build + cd build + cmake .. -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=...... + cmake --build . --config Release --parallel $(nproc) + cmake --build . --config Release --target install + diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c6f6d06 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,139 @@ +#TODO: Change version +version: 'v0.3.2.{build}' + +environment: + matrix: + ##### android ######## + - BUILD_TARGERT: android + BUILD_ARCH: "armeabi-v7a" + ANDROID_ARM_NEON: ON + QT_VERSION_DIR: 5.12 + QT_VERSION: 5.12.10 + ANDROID_API: android-27 + TOOLCHAIN_VERSION: 4.9 + + ##### msvc 2013 ######## + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 12 + QT_ROOT: C:\Qt\5.6\msvc2013 + BUILD_ARCH: x86 + + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 12 + QT_ROOT: C:\Qt\5.6\msvc2013_64 + BUILD_ARCH: x64 + + ##### msvc 2015 ######## + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.10\msvc2015 + BUILD_ARCH: x86 + + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.10\msvc2015_64 + BUILD_ARCH: x64 + + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.6\msvc2015 + BUILD_ARCH: x86 + + - BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.6\msvc2015_64 + BUILD_ARCH: x64 + + ##### msvc 2017 ######## + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.12\msvc2017 + BUILD_ARCH: x86 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.12\msvc2017_64 + BUILD_ARCH: x64 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.10\msvc2017_64 + BUILD_ARCH: x64 + + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_ROOT: C:\Qt\5.9\msvc2017_64 + BUILD_ARCH: x64 + + ##### mingw ######## + - BUILD_TARGERT: windows_mingw + TOOLCHAIN_VERSION: 492_32 + QT_ROOT: C:\Qt\5.6\mingw49_32 + BUILD_ARCH: x86 + + - BUILD_TARGERT: windows_mingw + TOOLCHAIN_VERSION: 530_32 + QT_ROOT: C:\Qt\5.10\mingw53_32 + BUILD_ARCH: x86 + + - BUILD_TARGERT: windows_mingw + TOOLCHAIN_VERSION: 730_64 + QT_ROOT: C:\Qt\5.12\mingw73_64 + BUILD_ARCH: x64 + +matrix: + fast_finish: false + +init: + - set varch=%BUILD_ARCH% + - if "%BUILD_ARCH%" == "x64" set varch=amd64 + - if "%BUILD_TARGERT%" == "windows_msvc" if %TOOLCHAIN_VERSION% LSS 15 (call "C:\Program Files (x86)\Microsoft Visual Studio %TOOLCHAIN_VERSION%.0\VC\vcvarsall.bat" %varch%) else (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %varch%) + - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS% + - echo PROCESSOR_IDENTIFIER=%PROCESSOR_IDENTIFIER% + - if NOT "%QT_ROOT%" == "NO" for /f "delims=" %%i in ('%QT_ROOT%/bin/qmake -query QT_VERSION') do (set QT_VERSION=%%i) + - echo QT_ROOT=%QT_ROOT% + - echo QT_VERSION=%QT_VERSION% + - del C:\msys64\usr\bin\link.exe + +install: + - C:\msys64\autorebase.bat + - C:\msys64\usr\bin\pacman.exe -S --noconfirm unzip + #- if "%BUILD_TARGERT%"=="windows_mingw" set PATH=C:\Qt\Tools\mingw%TOOLCHAIN_VERSION%_32\bin;%PATH% + - if "%BUILD_TARGERT%" == "windows_mingw" set MSYSTEM=MINGW32 #设置编译系统为MINGW32 + +before_build: + - cd %APPVEYOR_BUILD_FOLDER% + - git submodule update --init --recursive + - if "%BUILD_TARGERT%" == "android" C:\msys64\usr\bin\bash.exe %APPVEYOR_BUILD_FOLDER%\ci\build-install-tools-windows.sh + +build_script: + - cd %APPVEYOR_BUILD_FOLDER% + - C:\msys64\usr\bin\bash.exe %APPVEYOR_BUILD_FOLDER%\ci\build.sh + +artifacts: + - path: QFtpServer_*.apk + +test: off + +#See:https://www.appveyor.com/docs/deployment/github/ +deploy: + - provider: GitHub + #release: QFtpServer$(appveyor_build_version) + description: 'Release QFtpServer $(APPVEYOR_REPO_TAG_NAME) on windows' + #token : https://github.com/settings/tokens + #password encrypt: https://ci.appveyor.com/tools/encrypt + auth_token: + secure: gSHXb8kHAivRhgV7Qn1iN7ljt6wAuCKaqm1emdrT3utkG4RjNLU6pB8aNDqY7cnl + #artifact: QFtpServer$(BUILD_TARGERT)$(TOOLCHAIN_VERSION)_$(BUILD_ARCH)_$(QT_VERSION)_$(appveyor_build_version) + #draft: true + #prerelease: true + on: + BUILD_TARGERT: windows_msvc + TOOLCHAIN_VERSION: 14 + QT_VERSION: 5.6.3 + BUILD_ARCH: x86 + appveyor_repo_tag: true # deploy on tag push only diff --git a/ci/build-install-tools-windows.sh b/ci/build-install-tools-windows.sh new file mode 100644 index 0000000..d1d48f4 --- /dev/null +++ b/ci/build-install-tools-windows.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +set -ev + +SOURCE_DIR="`pwd`" +echo $SOURCE_DIR +TOOLS_DIR=${SOURCE_DIR}/Tools +echo ${TOOLS_DIR} + +if [ "$BUILD_TARGERT" = "android" ]; then + export ANDROID_SDK_ROOT=${TOOLS_DIR}/android-sdk + export ANDROID_NDK_ROOT=${TOOLS_DIR}/android-ndk + export JAVA_HOME="/C/Program Files (x86)/Java/jdk1.8.0" + export PATH=${TOOLS_DIR}/apache-ant/bin:$JAVA_HOME:$PATH +else + exit 0 +fi + +if [ ! -d "${TOOLS_DIR}" ]; then + mkdir ${TOOLS_DIR} +fi +cd ${TOOLS_DIR} + +# Install qt:https://github.com/benlau/qtci +if [ -n "${QT_VERSION}" ]; then + QT_DIR=C:/projects/${APPVEYOR_PROJECT_NAME}/Tools/Qt/${QT_VERSION} + if [ ! -d "${QT_DIR}" ]; then + wget -c --no-check-certificate -nv http://download.qt.io/official_releases/qt/${QT_VERSION_DIR}/${QT_VERSION}/qt-opensource-windows-x86-${QT_VERSION}.exe + bash ${SOURCE_DIR}/ci/qt-installer.sh qt-opensource-windows-x86-${QT_VERSION}.exe ${QT_DIR} + rm qt-opensource-windows-x86-${QT_VERSION}.exe + fi +fi + +cd ${TOOLS_DIR} + +#wget -c -nv http://apache.fayea.com//ant/binaries/apache-ant-1.10.1-bin.tar.gz +#tar xzf apache-ant-1.10.1-bin.tar.gz +#rm -f apache-ant-1.10.1-bin.tar.gz +#mv apache-ant-1.10.1 apache-ant + +cd ${TOOLS_DIR} + +#Download android sdk +if [ ! -d "${TOOLS_DIR}/android-sdk" ]; then + cd ${TOOLS_DIR} + + ANDROID_STUDIO_VERSION=191.5900203 + wget -c -nv https://dl.google.com/dl/android/studio/ide-zips/3.5.1.0/android-studio-ide-${ANDROID_STUDIO_VERSION}-windows.zip + unzip -q android-studio-ide-${ANDROID_STUDIO_VERSION}-windows.zip + rm android-studio-ide-${ANDROID_STUDIO_VERSION}-windows.zip + export JAVA_HOME=${TOOLS_DIR}/android-studio/jre + export PATH=${JAVA_HOME}/bin:$PATH + + ANDROID_SDK_VERSION=4333796 + wget -c -nv https://dl.google.com/android/repository/sdk-tools-windows-${ANDROID_SDK_VERSION}.zip + mkdir android-sdk + cd android-sdk + mv ../sdk-tools-windows-${ANDROID_SDK_VERSION}.zip . + unzip -q sdk-tools-windows-${ANDROID_SDK_VERSION}.zip + rm sdk-tools-windows-${ANDROID_SDK_VERSION}.zip + + echo "Install sdk and ndk ......" + if [ -n "${ANDROID_API}" ]; then + PLATFORMS="platforms;${ANDROID_API}" + else + PLATFORMS="platforms" + fi + if [ -z "${BUILD_TOOS_VERSION}" ]; then + BUILD_TOOS_VERSION="28.0.3" + fi + (sleep 5 ; num=0 ; while [ $num -le 5 ] ; do sleep 1 ; num=$(($num+1)) ; printf 'y\r\n' ; done ) \ + | ./tools/bin/sdkmanager.bat "platform-tools" "build-tools;${BUILD_TOOS_VERSION}" "${PLATFORMS}" "ndk-bundle" + cd ${TOOLS_DIR} +fi + +cd ${SOURCE_DIR} diff --git a/ci/build-install-tools.sh b/ci/build-install-tools.sh new file mode 100644 index 0000000..00afccf --- /dev/null +++ b/ci/build-install-tools.sh @@ -0,0 +1,192 @@ +#!/bin/bash + +set -e +SOURCE_DIR="`pwd`" +echo $SOURCE_DIR +TOOLS_DIR=${SOURCE_DIR}/Tools + +function function_install_yasm() +{ + #Install yasm + mkdir -p ${TOOLS_DIR}/src + cd ${TOOLS_DIR}/src + wget -c -nv http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz + tar xzf yasm-1.3.0.tar.gz + cd yasm-1.3.0/ + ./configure > /dev/null && sudo make install -j2 > /dev/null + cd ${SOURCE_DIR} +} + +function function_common() +{ + cd ${TOOLS_DIR} + #Install new cmake + #if [ "cmake" = "${QMAKE}" ]; then + # if [ ! -d "`pwd`/cmake" ]; then + # wget -nv --no-check-certificate http://www.cmake.org/files/v3.6/cmake-3.6.1-Linux-x86_64.tar.gz + # tar xzf cmake-3.6.1-Linux-x86_64.tar.gz + # mv cmake-3.6.1-Linux-x86_64 cmake + # fi + #fi + + # Install qt:https://github.com/benlau/qtci + if [ "$DOWNLOAD_QT" = "TRUE" ]; then + QT_DIR=`pwd`/Qt/${QT_VERSION} + if [ ! -d "${QT_DIR}" ]; then + if [ "${QT_VERSION}" = "5.6.3" ]; then + wget -c --no-check-certificate -nv http://download.qt.io/official_releases/qt/${QT_VERSION_DIR}/${QT_VERSION}/qt-opensource-linux-x64-android-${QT_VERSION}.run + bash ${SOURCE_DIR}/ci/qt-installer.sh qt-opensource-linux-x64-android-${QT_VERSION}.run ${QT_DIR} + rm qt-opensource-linux-x64-android-${QT_VERSION}.run + else + wget -c --no-check-certificate -nv http://download.qt.io/official_releases/qt/${QT_VERSION_DIR}/${QT_VERSION}/qt-opensource-linux-x64-${QT_VERSION}.run + bash ${SOURCE_DIR}/ci/qt-installer.sh qt-opensource-linux-x64-${QT_VERSION}.run ${QT_DIR} + rm qt-opensource-linux-x64-${QT_VERSION}.run + fi + fi + fi +} + +function install_android() +{ + cd ${TOOLS_DIR} + if [ ! -d "`pwd`/android-sdk" ]; then + ANDROID_STUDIO_VERSION=191.5900203 + wget -c -nv https://dl.google.com/dl/android/studio/ide-zips/3.5.1.0/android-studio-ide-${ANDROID_STUDIO_VERSION}-linux.tar.gz + tar xzf android-studio-ide-${ANDROID_STUDIO_VERSION}-linux.tar.gz + export JAVA_HOME=`pwd`/android-studio/jre + export PATH=${JAVA_HOME}/bin:$PATH + wget -c -nv https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip + mkdir android-sdk + cd android-sdk + cp ../sdk-tools-linux-4333796.zip . + unzip -q sdk-tools-linux-4333796.zip + echo "Install sdk and ndk ......" + if [ -n "${ANDROID_API}" ]; then + PLATFORMS="platforms;${ANDROID_API}" + else + PLATFORMS="platforms" + fi + if [ -z "${BUILD_TOOS_VERSION}" ]; then + BUILD_TOOS_VERSION="28.0.3" + fi + (sleep 5 ; num=0 ; while [ $num -le 5 ] ; do sleep 1 ; num=$(($num+1)) ; printf 'y\r\n' ; done ) \ + | ./tools/bin/sdkmanager "platform-tools" "build-tools;${BUILD_TOOS_VERSION}" "${PLATFORMS}" "ndk-bundle" + if [ ! -d ${TOOLS_DIR}/android-ndk ]; then + ln -s ${TOOLS_DIR}/android-sdk/ndk-bundle ${TOOLS_DIR}/android-ndk + fi + fi +} + +function install_android_sdk_and_ndk() +{ + cd ${SOURCE_DIR}/Tools + + #Install android ndk + if [ ! -d "`pwd`/android-ndk" ]; then + if [ "$QT_VERSION_DIR" = "5.8" ]; then + wget -c -nv http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin + chmod u+x android-ndk-r10e-linux-x86_64.bin + ./android-ndk-r10e-linux-x86_64.bin > /dev/null + mv android-ndk-r10e android-ndk + rm android-ndk-r10e-linux-x86_64.bin + else + NDK_VERSION=r20 + wget -c -nv https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux-x86_64.zip + unzip android-ndk-${NDK_VERSION}-linux-x86_64.zip + mv android-ndk-${NDK_VERSION} android-ndk + rm android-ndk-${NDK_VERSION}-linux-x86_64.zip + fi + fi + + cd ${TOOLS_DIR} + + #Download android sdk + if [ ! -d "`pwd`/android-sdk" ]; then + SDK_VERSION=r24.4.1 + wget -c -nv https://dl.google.com/android/android-sdk_${SDK_VERSION}-linux.tgz + tar xf android-sdk_${SDK_VERSION}-linux.tgz + mv android-sdk-linux android-sdk + rm android-sdk_${SDK_VERSION}-linux.tgz + (sleep 5 ; while true ; do sleep 1 ; printf 'y\r\n' ; done ) \ + | android-sdk/tools/android update sdk -u -t tool,${ANDROID_API},extra,platform,platform-tools,build-tools-28.0.3,build-tools-28.0.2 + fi +} + +function function_android() +{ + cd ${TOOLS_DIR} + + sudo apt-get update -y -qq + #sudo apt-get install -qq -y openjdk-11-jdk + # install oracle jdk + #sudo add-apt-repository ppa:linuxuprising/java -y + #sudo apt update + #(sleep 5 ; while true ; do sleep 1 ; printf '\r\n' ; done ) | sudo apt install oracle-java11-installer -qq -y + + #sudo apt install oracle-java11-set-default -qq -y + + install_android + + sudo apt-get install ant -qq -y + sudo apt-get install libicu-dev -qq -y + sudo apt-get install -y -qq libglu1-mesa-dev \ + libxkbcommon-x11-dev + + function_common + cd ${SOURCE_DIR} +} + +function function_unix() +{ + #Install yasm + #function_install_yasm + + if [ "$DOWNLOAD_QT" != "TRUE" ]; then + #See: https://launchpad.net/~beineri + sudo add-apt-repository ppa:beineri/opt-qt-${QT_VERSION}-`lsb_release -c|awk '{print $2}'` -y + fi + + sudo apt-get update -y -qq + sudo apt-get install debhelper fakeroot -y -qq + sudo apt-get install -y -qq libglu1-mesa-dev \ + libxkbcommon-x11-dev + + if [ "$DOWNLOAD_QT" != "TRUE" ]; then + sudo apt-get install -y -qq qt${QT_VERSION_DIR}base \ + qt${QT_VERSION_DIR}tools + fi + function_common + + cd ${SOURCE_DIR} +} + +function function_mingw() +{ + #Install yasm + #function_install_yasm + + cd ${SOURCE_DIR} + if [ "true" == "$RABBITIM_BUILD_THIRDLIBRARY" ]; then + export RABBITIM_BUILD_CROSS_HOST=i686-w64-mingw32 #i586-mingw32msvc + fi + + function_common + cd ${SOURCE_DIR} +} + +case ${BUILD_TARGERT} in + android) + function_android + ;; + unix) + function_unix + ;; + windows_mingw) + function_mingw + ;; + *) + echo "There aren't ${BUILD_TARGERT}" + ;; +esac + +cd ${SOURCE_DIR} diff --git a/ci/build.sh b/ci/build.sh new file mode 100644 index 0000000..1500c65 --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,147 @@ +#!/bin/bash +set -e + +SOURCE_DIR=`pwd` +if [ -n "$1" ]; then + SOURCE_DIR=$1 +fi +TOOLS_DIR=${SOURCE_DIR}/Tools +cd ${SOURCE_DIR} + +if [ "$BUILD_TARGERT" = "android" ]; then + export ANDROID_SDK_ROOT=${TOOLS_DIR}/android-sdk + export ANDROID_NDK_ROOT=${TOOLS_DIR}/android-ndk + if [ -n "$APPVEYOR" ]; then + #export JAVA_HOME="/C/Program Files (x86)/Java/jdk1.8.0" + export ANDROID_NDK_ROOT=${TOOLS_DIR}/android-sdk/ndk-bundle + fi + #if [ "$TRAVIS" = "true" ]; then + #export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + #fi + export JAVA_HOME=${TOOLS_DIR}/android-studio/jre + + case $BUILD_ARCH in + arm*) + export QT_ROOT=${TOOLS_DIR}/Qt/${QT_VERSION}/${QT_VERSION}/android_armv7 + ;; + x86) + export QT_ROOT=${TOOLS_DIR}/Qt/${QT_VERSION}/${QT_VERSION}/android_x86 + ;; + esac + export PATH=${TOOLS_DIR}/apache-ant/bin:$JAVA_HOME/bin:$PATH + export ANDROID_SDK=${ANDROID_SDK_ROOT} + export ANDROID_NDK=${ANDROID_NDK_ROOT} + if [ -z "${BUILD_TOOS_VERSION}" ]; then + export BUILD_TOOS_VERSION="28.0.3" + fi +fi + +if [ "${BUILD_TARGERT}" = "unix" ]; then + if [ "$DOWNLOAD_QT" = "TRUE" ]; then + QT_DIR=${TOOLS_DIR}/Qt/${QT_VERSION} + export QT_ROOT=${QT_DIR}/${QT_VERSION}/gcc_64 + else + #source /opt/qt${QT_VERSION_DIR}/bin/qt${QT_VERSION_DIR}-env.sh + export QT_ROOT=/opt/qt${QT_VERSION_DIR} + fi + export PATH=$QT_ROOT/bin:$PATH + export LD_LIBRARY_PATH=$QT_ROOT/lib/i386-linux-gnu:$QT_ROOT/lib:$LD_LIBRARY_PATH + export PKG_CONFIG_PATH=$QT_ROOT/lib/pkgconfig:$PKG_CONFIG_PATH +fi + +if [ "$BUILD_TARGERT" != "windows_msvc" ]; then + RABBIT_MAKE_JOB_PARA="-j`cat /proc/cpuinfo |grep 'cpu cores' |wc -l`" #make 同时工作进程参数 + if [ "$RABBIT_MAKE_JOB_PARA" = "-j1" ];then + RABBIT_MAKE_JOB_PARA="" + fi +fi + +if [ "$BUILD_TARGERT" = "windows_mingw" \ + -a -n "$APPVEYOR" ]; then + export PATH=/C/Qt/Tools/mingw${TOOLCHAIN_VERSION}/bin:$PATH +fi +TARGET_OS=`uname -s` +case $TARGET_OS in + MINGW* | CYGWIN* | MSYS*) + export PKG_CONFIG=/c/msys64/mingw32/bin/pkg-config.exe + RABBIT_BUILD_HOST="windows" + if [ "$BUILD_TARGERT" = "android" ]; then + ANDROID_NDK_HOST=windows-x86_64 + if [ ! -d $ANDROID_NDK/prebuilt/${ANDROID_NDK_HOST} ]; then + ANDROID_NDK_HOST=windows + fi + fi + ;; + Linux* | Unix*) + ;; + *) + ;; +esac + +export PATH=${QT_ROOT}/bin:$PATH +echo "PATH:$PATH" +echo "PKG_CONFIG:$PKG_CONFIG" +cd ${SOURCE_DIR} +mkdir -p build_${BUILD_TARGERT} +cd build_${BUILD_TARGERT} + +case ${BUILD_TARGERT} in + windows_msvc) + MAKE=nmake + ;; + windows_mingw) + if [ "${RABBIT_BUILD_HOST}"="windows" ]; then + MAKE="mingw32-make ${RABBIT_MAKE_JOB_PARA}" + fi + ;; + *) + MAKE="make ${RABBIT_MAKE_JOB_PARA}" + ;; +esac + +if [ -n "$appveyor_build_version" -a -z "$VERSION" ]; then + export VERSION="v2.0.1" +fi +if [ -z "$VERSION" ]; then + export VERSION="v2.0.1" +fi + +if [ "ON" = "${STATIC}" ]; then + CONFIG_PARA="CONFIG*=static" +fi +if [ "${BUILD_TARGERT}" = "android" ]; then + ${QT_ROOT}/bin/qmake ${SOURCE_DIR} \ + "CONFIG+=release" ${CONFIG_PARA} + + $MAKE + $MAKE install INSTALL_ROOT=`pwd`/android-build +else + ${QT_ROOT}/bin/qmake ${SOURCE_DIR} \ + "CONFIG+=release" ${CONFIG_PARA}\ + PREFIX=`pwd`/install + + $MAKE + echo "$MAKE install ...." + $MAKE install +fi + +if [ "${BUILD_TARGERT}" = "android" ]; then + ${QT_ROOT}/bin/androiddeployqt \ + --input `pwd`/QFtpServer/android-libQFtpServer.so-deployment-settings.json \ + --output `pwd`/android-build \ + --android-platform ${ANDROID_API} \ + --gradle + APK_FILE=`find . -name "android-build-debug.apk"` + mv -f ${APK_FILE} $SOURCE_DIR/QFtpServer_${VERSION}.apk + APK_FILE=$SOURCE_DIR/QFtpServer_${VERSION}.apk + if [ "$TRAVIS_TAG" != "" \ + -a "$BUILD_ARCH"="armeabi-v7a" \ + -a "$QT_VERSION"="5.12.10" ]; then + + export UPLOADTOOL_BODY="Release QFtpServer_${VERSION}" + #export UPLOADTOOL_PR_BODY= + wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh + chmod u+x upload.sh + ./upload.sh ${APK_FILE} + fi +fi diff --git a/ci/qt-installer.sh b/ci/qt-installer.sh new file mode 100644 index 0000000..a3f91c7 --- /dev/null +++ b/ci/qt-installer.sh @@ -0,0 +1,267 @@ +#!/bin/bash + +#http://stackoverflow.com/questions/25105269/silent-install-qt-run-installer-on-ubuntu-server +#http://doc.qt.io/qtinstallerframework/noninteractive.html +#参考:https://github.com/benlau/qtci +# https://github.com/rabits/dockerfiles +# https://github.com/mjscosta/qt-silent-installer + +set -e #quit on error + +if [ $# -lt 2 ]; +then + echo qt-installer.sh qt-installer-file output_path + exit -1 +fi + +function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } +function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } +function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } +function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } + +export PATH=$PATH:$PWD +export WORKDIR=$PWD +INSTALLER=$1 +OUTPUT=$2 +SCRIPT="$(mktemp /tmp/tmp.XXXXXXXXX)" +LIST_PACKAGES=0 + +case $BUILD_ARCH in + unix) + PACKAGES=gcc_64 + ;; + arm*|x86*) + if version_ge $QT_VERSION_DIR 5.14 ; then + PACKAGES=android + else + case $BUILD_ARCH in + arm*) + PACKAGES=android_armv7 + ;; + x86*) + PACKAGES=android_x86 + ;; + esac + fi + ;; + *) + echo "Aach[$BUILD_ARCH] don't suppoert" + ;; +esac + +if [ -n "$QT_CI_PACKAGES" ]; then + PACKAGES=$QT_CI_PACKAGES +fi + +cat < $SCRIPT +function abortInstaller() +{ + installer.setDefaultPageVisible(QInstaller.Introduction, false); + installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); + installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); + installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false); + installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); + installer.setDefaultPageVisible(QInstaller.PerformInstallation, false); + installer.setDefaultPageVisible(QInstaller.LicenseCheck, false); + var abortText = "" + qsTr("Installation failed:") + ""; + var error_list = installer.value("component_errors").split(";;;"); + abortText += "
    "; + // ignore the first empty one + for (var i = 0; i < error_list.length; ++i) { + if (error_list[i] !== "") { + log(error_list[i]); + abortText += "
  • " + error_list[i] + "
  • " + } + } + abortText += "
"; + installer.setValue("FinishedText", abortText); +} +function log() { + var msg = ["QTCI: "].concat([].slice.call(arguments)); + console.log(msg.join(" ")); +} +function printObject(object) { + var lines = []; + for (var i in object) { + lines.push([i, object[i]].join(" ")); + } + log(lines.join(",")); +} +var status = { + widget: null, + finishedPageVisible: false, + installationFinished: false +} +function tryFinish() { + if (status.finishedPageVisible && status.installationFinished) { + if (status.widget.LaunchQtCreatorCheckBoxForm) { + // Disable this checkbox for minimal platform + status.widget.LaunchQtCreatorCheckBoxForm.launchQtCreatorCheckBox.setChecked(false); + } + if (status.widget.RunItCheckBox) { + // LaunchQtCreatorCheckBoxForm may not work for newer versions. + status.widget.RunItCheckBox.setChecked(false); + } + log("Press Finish Button"); + gui.clickButton(buttons.FinishButton); + } +} +function Controller() { + installer.installationFinished.connect(function() { + status.installationFinished = true; + gui.clickButton(buttons.NextButton); + tryFinish(); + }); + installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes); + installer.setMessageBoxAutomaticAnswer("installationErrorWithRetry", QMessageBox.Ignore); + installer.setMessageBoxAutomaticAnswer("XcodeError", QMessageBox.Ok); + + // Allow to cancel installation for arguments --list-packages + installer.setMessageBoxAutomaticAnswer("cancelInstallation", QMessageBox.Yes); +} +Controller.prototype.WelcomePageCallback = function() { + log("Welcome Page"); + gui.clickButton(buttons.NextButton); + var widget = gui.currentPageWidget(); + /* + Online installer 3.0.6 + - It must disconnect the completeChanged callback after used, otherwise it will click the 'next' button on another pages + */ + var callback = function() { + gui.clickButton(buttons.NextButton); + widget.completeChanged.disconnect(callback); + } + widget.completeChanged.connect(callback); +} +Controller.prototype.CredentialsPageCallback = function() { + + var login = installer.environmentVariable("QT_USER"); + var password = installer.environmentVariable("QT_PASSWORD"); + if (login === "" || password === "") { + gui.clickButton(buttons.CommitButton); + } + + var widget = gui.currentPageWidget(); + widget.loginWidget.EmailLineEdit.setText(login); + widget.loginWidget.PasswordLineEdit.setText(password); + gui.clickButton(buttons.CommitButton); +} +Controller.prototype.ObligationsPageCallback = function() { + var page = gui.pageWidgetByObjectName("ObligationsPage"); + page.obligationsAgreement.setChecked(true); + page.completeChanged(); + gui.clickButton(buttons.NextButton); +} +Controller.prototype.ComponentSelectionPageCallback = function() { + log("ComponentSelectionPageCallback"); + function list_packages() { + var components = installer.components(); + log("Available components: " + components.length); + var packages = ["Packages: "]; + for (var i = 0 ; i < components.length ;i++) { + packages.push(components[i].name); + } + log(packages.join(" ")); + } + + if ($LIST_PACKAGES) { + list_packages(); + gui.clickButton(buttons.CancelButton); + return; + } + log("Select components"); + function trim(str) { + return str.replace(/^ +/,"").replace(/ *$/,""); + } + var widget = gui.currentPageWidget(); + var packages = trim("$PACKAGES").split(","); + if (packages.length > 0 && packages[0] !== "") { + widget.deselectAll(); + var components = installer.components(); + var allfound = true; + for (var i in packages) { + var pkg = trim(packages[i]); + var found = false; + for (var j in components) { + if (components[j].name === pkg) { + found = true; + break; + } + } + if (!found) { + allfound = false; + log("ERROR: Package " + pkg + " not found."); + } else { + log("Select " + pkg); + widget.selectComponent(pkg); + } + } + if (!allfound) { + list_packages(); + // TODO: figure out how to set non-zero exit status. + gui.clickButton(buttons.CancelButton); + return; + } + } else { + log("Use default component list"); + } + gui.clickButton(buttons.NextButton); +} +Controller.prototype.IntroductionPageCallback = function() { + log("Introduction Page"); + log("Retrieving meta information from remote repository"); + + /* + Online installer 3.0.6 + - Don't click buttons.NextButton directly. It will skip the componenet selection. + */ + + if (installer.isOfflineOnly()) { + gui.clickButton(buttons.NextButton); + } +} +Controller.prototype.TargetDirectoryPageCallback = function() { + log("Set target installation page: $OUTPUT"); + var widget = gui.currentPageWidget(); + if (widget != null) { + widget.TargetDirectoryLineEdit.setText("$OUTPUT"); + } + + gui.clickButton(buttons.NextButton); +} +Controller.prototype.LicenseAgreementPageCallback = function() { + log("Accept license agreement"); + var widget = gui.currentPageWidget(); + if (widget != null) { + widget.AcceptLicenseRadioButton.setChecked(true); + } + gui.clickButton(buttons.NextButton); +} +Controller.prototype.ReadyForInstallationPageCallback = function() { + log("Ready to install"); + // Bug? If commit button pressed too quickly finished callback might not show the checkbox to disable running qt creator + // Behaviour started around 5.10. You don't actually have to press this button at all with those versions, even though gui.isButtonEnabled() returns true. + + gui.clickButton(buttons.CommitButton, 200); +} +Controller.prototype.PerformInstallationPageCallback = function() { + log("PerformInstallationPageCallback"); + gui.clickButton(buttons.CommitButton); +} +Controller.prototype.FinishedPageCallback = function() { + log("FinishedPageCallback"); + var widget = gui.currentPageWidget(); + // Bug? Qt 5.9.5 and Qt 5.9.6 installer show finished page before the installation completed + // Don't press "finishButton" immediately + + status.finishedPageVisible = true; + status.widget = widget; + tryFinish(); +} +EOF + +chmod u+x $1 +#显示log +$1 -v --script $SCRIPT +#不显示log +#$1 --script $SCRIPT diff --git a/cmake/QFtpServerLibConfig.cmake.in b/cmake/QFtpServerLibConfig.cmake.in new file mode 100644 index 0000000..efc529a --- /dev/null +++ b/cmake/QFtpServerLibConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/") + +include(CMakeFindDependencyMacro) +find_dependency(Qt@QT_VERSION_MAJOR@ COMPONENTS @LIB_QT_COMPONENTS@) +include("${CMAKE_CURRENT_LIST_DIR}/QFtpServerLibTargets.cmake") diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..a2dee19 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,57 @@ +# Author: Kang Lin(kl222@126.com) + +# Use: Please add the follow code to CMakeLists.txt + +# # Install runtime target +# add_custom_target(install-runtime +# COMMAND +# "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=Runtime +# -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake" +# ) +# # Uninstall runtime target +# add_custom_target(uninstall-runtime +# COMMAND +# "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=Runtime +# -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" +# ) +# # Create will be delete files +# CONFIGURE_FILE( +# "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" +# "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" +# IMMEDIATE @ONLY) +# # Create unistall target +# ADD_CUSTOM_TARGET(uninstall +# "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" +# DEPENDS uninstall-runtime) + + +if(CMAKE_INSTALL_COMPONENT) + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/${CMAKE_INSTALL_MANIFEST}") + MESSAGE(WARNING "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/${CMAKE_INSTALL_MANIFEST}\"") +ELSE() + + FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/${CMAKE_INSTALL_MANIFEST}" files) + STRING(REGEX REPLACE "\n" ";" files "${files}") + FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"${file}\"") + IF(EXISTS "${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF("${rm_retval}" STREQUAL 0) + ELSE("${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"") + ENDIF("${rm_retval}" STREQUAL 0) + ELSE(EXISTS "${file}") + MESSAGE(STATUS "File \"${file}\" does not exist.") + ENDIF(EXISTS "${file}") + ENDFOREACH(file) + +ENDIF() diff --git a/tag.sh b/tag.sh new file mode 100755 index 0000000..2ee7682 --- /dev/null +++ b/tag.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +SOURCE_DIR=`pwd` + +if [ -n "$1" ]; then + VERSION=`git describe --tags` + if [ -z "$VERSION" ]; then + VERSION=` git rev-parse HEAD` + fi + + echo "Current verion: $VERSION, The version to will be set: $1" + echo "Please check the follow list:" + echo " - Test is ok ?" + echo " - Translation is ok ?" + echo " - Setup file is ok ?" + echo " - Update_*.xml is ok ?" + + read -t 30 -p "Be sure to input Y, not input N: " INPUT + if [ "$INPUT" != "Y" -a "$INPUT" != "y" ]; then + exit 0 + fi + git tag -a $1 -m "Release $1" +fi + +VERSION=`git describe --tags` +if [ -z "$VERSION" ]; then + VERSION=`git rev-parse --short HEAD` +fi + +sed -i "s/export VERSION=.*/export VERSION=\"${VERSION}\"/g" ${SOURCE_DIR}/ci/build.sh + +DEBIAN_VERSION=`echo ${VERSION}|cut -d "v" -f 2` +sed -i "s/[0-9]\+\.[0-9]\+\.[0-9]\+/${DEBIAN_VERSION}/g" ${SOURCE_DIR}/README*.md +sed -i "s/[0-9]\+\.[0-9]\+\.[0-9]\+/${DEBIAN_VERSION}/g" ${SOURCE_DIR}/QFtpServer/android/AndroidManifest.xml +#sed -i "s/[0-9]\+\.[0-9]\+\.[0-9]\+/${DEBIAN_VERSION}/g" ${SOURCE_DIR}/QFtpServer/android/android_lib/android/AndroidManifest.xml + +MAJOR_VERSION=`echo ${DEBIAN_VERSION}|cut -d "." -f 1` +#sed -i "s/android:versionCode=.*android/android:versionCode=\"${MAJOR_VERSION}\" android/g" ${SOURCE_DIR}/QFtpServer/android/android_lib/android/AndroidManifest.xml +sed -i "s/android:versionCode=.*android/android:versionCode=\"${MAJOR_VERSION}\" android/g" ${SOURCE_DIR}/QFtpServer/android/AndroidManifest.xml + +if [ -n "$1" ]; then + git add . + git commit -m "Release $1" + git push + git tag -d $1 + git tag -a $1 -m "Release $1" + git push origin :refs/tags/$1 + git push origin $1 +fi