mirror of
https://github.com/jellyfin/jellyfin-media-player.git
synced 2024-11-22 21:49:56 +00:00
Initial public commit of Plex Media Player
This commit is contained in:
commit
3d4859f1b1
47
.clang-format
Normal file
47
.clang-format
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 100
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
DerivePointerBinding: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: true
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 60
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
PointerBindsToType: true
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: false
|
||||
Standard: Cpp03
|
||||
IndentWidth: 2
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Allman
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterControlStatementKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 0
|
||||
...
|
||||
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
*.pro.user*
|
||||
dependencies/*
|
||||
CMakeLists.txt.user
|
||||
CMakeLists.txt.user.*
|
||||
build/*
|
||||
.idea
|
||||
.install_pkg
|
||||
.openelec-unpack
|
||||
QtCreatorDeployment.txt
|
||||
vagrant/ubuntu-x86_64/.vagrant
|
||||
vagrant/windows-x86_64/.vagrant
|
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
41
BuildQT5-windows.md
Normal file
41
BuildQT5-windows.md
Normal file
@ -0,0 +1,41 @@
|
||||
# How-To build Qt 5.5.0 on Windows
|
||||
This procedure was ran on a freshly installed Win7 64 bits.
|
||||
|
||||
This procedure assumes that the build root is `C:\qt\` but it can be changed in the beginning of each `.bat` file
|
||||
|
||||
1. Install Visual Studio 2013
|
||||
- Install [Visual Studio 2013 Update 4](http://www.microsoft.com/fr-fr/download/details.aspx?id=44921)
|
||||
- Install [Windows SDK 8](https://msdn.microsoft.com/en-us/windows/desktop/hh852363.aspx)
|
||||
- Install [ActivePerl](http://www.activestate.com/activeperl/downloads)
|
||||
- Install [Python 2.7.10](https://www.python.org/downloads/release/python-2710)
|
||||
- Install the following gnutools:
|
||||
- [Bison](http://gnuwin32.sourceforge.net/downlinks/bison.php)
|
||||
- [GPerf](http://gnuwin32.sourceforge.net/downlinks/gperf.php)
|
||||
- [Flex](http://gnuwin32.sourceforge.net/downlinks/flex.php)
|
||||
- Grab [ICU sources](http://download.icu-project.org/files/icu4c/55.1/icu4c-55_1-src.zip) and unpack them in `C:\qt\icu`
|
||||
- Grab [Openssl 1.0.2d](https://www.openssl.org/source/) and unpack them in `C:\qt\openssl`
|
||||
- Grab [Qt Sources](http://download.qt.io/official_releases/qt/5.5/5.5.0/single/qt-everywhere-opensource-src-5.5.0.zip) and unpack them in `C:\qt\qt5`
|
||||
- drop the three `.bat` files in the steps below in `C:\qt\`
|
||||
- Open `C:\qt\icu\source\allionone\allinone.sln` with VS2013, you will be prompted for project conversion. Once the project is converted, just close it.
|
||||
- run `c:\qt\buildicu.bat`
|
||||
- run `c:\qt\buildopenssl.bat`
|
||||
- run `c:\qt\buildqt.bat`
|
||||
|
||||
The qt build will land in `C:\qt\qt5\build`
|
||||
|
||||
|
||||
# buildicu.bat
|
||||
```
|
||||
set BUILD_ROOT=C:\qt
REM Set up \Microsoft Visual Studio 2013, where <arch> is \c amd64, \c x86, etc.
CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
REM set compiler to multicore
set CL=/MP
REM Build icu
cd %BUILD_ROOT%\icu
msbuild source\allinone\allinone.sln /m /target:Build /property:Configuration=Release;Platform=x64
msbuild source\allinone\allinone.sln /m /target:Build /property:Configuration=Debug;Platform=x64
|
||||
cd ..
|
||||
```
|
||||
|
||||
# buildopenssl.bat
|
||||
```
|
||||
set BUILD_ROOT=C:\qt
CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
cd openssl
set CL=/MP
perl Configure VC-WIN64A --prefix=%BUILD_ROOT%\openssl\build
call ms\do_win64a
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak install
cd ..
|
||||
```
|
||||
|
||||
# buildqt.bat
|
||||
```
|
||||
set BUILD_ROOT=C:\qt
REM Set up \Microsoft Visual Studio 2013, where <arch> is \c amd64, \c x86, etc.
CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
REM set compiler to multicore
set CL=/MP
REM Add ICU dirs to the proper path (include, libs , bin)
set INCLUDE=%INCLUDE%;%BUILD_ROOT%\icu\include
set LIB=%LIB%;%BUILD_ROOT%\icu\lib64
set PATH=%PATH%;%BUILD_ROOT%\icu\bin64
REM Add OpenSSL dirs to the proper path (include, libs , bin)
set INCLUDE=%INCLUDE%;%BUILD_ROOT%\openssl\build\include
set LIB=%LIB%;%BUILD_ROOT%\openssl\build\lib
set PATH=%PATH%;%BUILD_ROOT%\openssl\build\bin
REM Add Pyhton dirs to the proper path (include, libs , bin)
set PATH=%PATH%;c:\Python27\
REM Add GunWindirs to the proper path (include, libs , bin)
set INCLUDE=%INCLUDE%;%BUILD_ROOT%\GnuWin32\include
set LIB=%LIB%;%BUILD_ROOT%\GnuWin32\lib
set PATH=%PATH%;=%BUILD_ROOT%\GnuWin32\bin
SET QT_ROOT=%BUILD_ROOT%\qt5
SET PATH=%QT_ROOT%\qtbase\bin;%PATH%
SET QMAKESPEC=win32-msvc2013
cd %QT_ROOT%
CALL configure -prefix %QT_ROOT%\build -icu -opengl dynamic -release -nomake examples -opensource -confirm-license -no-gif -qt-libpng -qt-libjpeg -openssl -qt-pcre -no-cups -no-dbus -skip qtwebkit -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwebkit-examples -skip qtquick1 -skip qt3d
nmake
nmake install
cd ..
|
||||
```
|
59
CMakeLists.txt
Normal file
59
CMakeLists.txt
Normal file
@ -0,0 +1,59 @@
|
||||
project(PlexMediaPlayer CXX C)
|
||||
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
|
||||
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
if (POLICY CMP0058)
|
||||
cmake_policy(SET CMP0058 NEW)
|
||||
endif()
|
||||
cmake_policy(SET CMP0017 NEW)
|
||||
|
||||
option(OPENELEC "Make an OpenELEC build" OFF)
|
||||
|
||||
if (OPENELEC)
|
||||
add_definitions(-DKONVERGO_OPENELEC=1)
|
||||
Message(STATUS "Making an OpenELEC build")
|
||||
endif(OPENELEC)
|
||||
|
||||
option(BUILD_TARGET "Destination target for the build" "")
|
||||
|
||||
if (BUILD_TARGET STREQUAL "RPI")
|
||||
add_definitions(-DTARGET_RPI=1)
|
||||
set(RPI_LIBS bcm_host)
|
||||
Message(STATUS "Build for Raspberry PI target")
|
||||
endif(BUILD_TARGET STREQUAL "RPI")
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/")
|
||||
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
|
||||
|
||||
add_definitions(-DQS_LOG_LINE_NUMBERS -DQHTTPSERVER_EXPORT)
|
||||
|
||||
include(DependencyConfiguration)
|
||||
include(VersionConfiguration)
|
||||
include(NameConfiguration)
|
||||
include(utils)
|
||||
include(QtConfiguration)
|
||||
include(PlayerConfiguration)
|
||||
include(InputConfiguration)
|
||||
include(FindBreakpad)
|
||||
include(BreakpadSymbols)
|
||||
|
||||
if(APPLE)
|
||||
include(AppleConfiguration)
|
||||
elseif(WIN32)
|
||||
include(Win32Configuration)
|
||||
elseif(UNIX AND (NOT APPLE))
|
||||
include(LinuxConfiguration)
|
||||
endif(APPLE)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
include(GCCConfiguration)
|
||||
endif(CMAKE_COMPILER_IS_GNUCC)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNINGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNINGS}")
|
||||
|
||||
add_subdirectory(external)
|
||||
add_subdirectory(src)
|
||||
|
||||
include(CPackConfiguration)
|
21
CMakeModules/AppleConfiguration.cmake
Normal file
21
CMakeModules/AppleConfiguration.cmake
Normal file
@ -0,0 +1,21 @@
|
||||
find_library(FOUNDATION Foundation)
|
||||
find_library(APPKIT AppKit)
|
||||
find_library(IOKIT IOKit)
|
||||
find_library(COCOA Cocoa)
|
||||
find_Library(CARBON Carbon)
|
||||
find_library(SECURITY Security)
|
||||
|
||||
set(OS_LIBS ${FOUNDATION} ${APPKIT} ${IOKIT} ${COCOA} ${SECURITY} ${CARBON} spmediakeytap)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.9")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.9")
|
||||
set(WARNINGS "-Wall")
|
||||
|
||||
set(HAVE_UPDATER 1)
|
||||
find_program(UPDATER_PATH updater HINTS ${CMAKE_FIND_ROOT_PATH}/update_installer/ NO_DEFAULT_PATH)
|
||||
if(${UPDATER_PATH} MATCHES "UPDATER_PATH-NOTFOUND")
|
||||
set(HAVE_UPDATER 0)
|
||||
message(STATUS "will build without the updater")
|
||||
endif(${UPDATER_PATH} MATCHES "UPDATER_PATH-NOTFOUND")
|
||||
|
||||
set(INSTALL_BIN_DIR .)
|
||||
set(INSTALL_RESOURCE_DIR Contents/Resources)
|
35
CMakeModules/BreakpadSymbols.cmake
Normal file
35
CMakeModules/BreakpadSymbols.cmake
Normal file
@ -0,0 +1,35 @@
|
||||
OPTION(GENERATE_SYMBOLS "Should we generate symbols for binaries?" ON)
|
||||
function(dumpsyms target symfile)
|
||||
find_program(DUMP_SYMS dump_syms HINTS /usr/bin/ ${DEPENDENCY_ROOT}/bin)
|
||||
if(GENERATE_SYMBOLS AND NOT DUMP_SYMS)
|
||||
message(STATUS "dump_syms not found")
|
||||
endif()
|
||||
if(GENERATE_SYMBOLS AND DUMP_SYMS)
|
||||
|
||||
if(APPLE)
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND dsymutil -o ${MAIN_NAME}.dSYM $<TARGET_FILE:${MAIN_TARGET}>
|
||||
COMMENT Generating ${MAIN_NAME}.dSYM
|
||||
BYPRODUCTS ${MAIN_NAME}.dSYM/Contents/Resources/DWARF/${target} ${MAIN_NAME}.dSYM/Contents/Info.plist
|
||||
)
|
||||
endif(APPLE)
|
||||
|
||||
unset(COMPRESS)
|
||||
find_program(COMPRESS_XZ xz)
|
||||
find_program(COMPRESS_BZ bzip2)
|
||||
if(COMPRESS_XZ)
|
||||
set(COMPRESS_EXT xz)
|
||||
set(COMPRESS ${COMPRESS_XZ})
|
||||
elseif(COMPRESS_BZ)
|
||||
set(COMPRESS_EXT bz2)
|
||||
set(COMPRESS ${COMPRESS_BZ})
|
||||
endif(COMPRESS_XZ)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${target} POST_BUILD
|
||||
BYPRODUCTS ${symfile}.${COMPRESS_EXT}
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/scripts/dump-syms.sh "${DUMP_SYMS}" "${COMPRESS}" "$<TARGET_FILE:${target}>" "${symfile}.${COMPRESS_EXT}"
|
||||
)
|
||||
install(FILES ${symfile}.${COMPRESS_EXT} DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif(GENERATE_SYMBOLS AND DUMP_SYMS)
|
||||
endfunction()
|
63
CMakeModules/CPackConfiguration.cmake
Normal file
63
CMakeModules/CPackConfiguration.cmake
Normal file
@ -0,0 +1,63 @@
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Plex Media Player")
|
||||
set(CPACK_PACKAGE_VENDOR "Plex")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_NANO})
|
||||
|
||||
if(APPLE)
|
||||
set(CPACK_SYSTEM_NAME "macosx-x86_64")
|
||||
elseif(WIN32)
|
||||
set(CPACK_SYSTEM_NAME "windows-x86")
|
||||
else()
|
||||
set(CPACK_SYSTEM_NAME linux-${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
set(CPACK_PACKAGE_FILE_NAME "PlexMediaPlayer-${VERSION_STRING}-${CPACK_SYSTEM_NAME}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "PlexMediaPlayer-${VERSION_STRING}-src")
|
||||
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "PlexMediaPlayer")
|
||||
set(CPACK_STRIP_FILES 1)
|
||||
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND CPACK_GENERATOR "IFW")
|
||||
endif(WIN32)
|
||||
|
||||
# config IFW
|
||||
set(CPACK_IFW_FRAMEWORK_VERSION 2.0.1)
|
||||
set(CPACK_IFW_PACKAGE_NAME "Plex Media Player")
|
||||
set(CPACK_IFW_PACKAGE_START_MENU_DIRECTORY "Plex Media Player")
|
||||
set(CPACK_IFW_PACKAGE_TITLE "Plex Media Player Installer")
|
||||
set(CPACK_IFW_PACKAGE_PUBLISHER "Plex")
|
||||
set(CPACK_IFW_PRODUCT_URL "https://plex.tv")
|
||||
set(CPACK_IFW_PACKAGE_ICON ${CMAKE_SOURCE_DIR}/bundle/win/Plex.ico)
|
||||
set(CPACK_IFW_PACKAGE_WINDOW_ICON ${CMAKE_SOURCE_DIR}/resources/images/icon.png)
|
||||
|
||||
if(APPLE)
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||
endif(APPLE)
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/CMakeModules/CPackGeneratedConfig.cmake.in ${CMAKE_BINARY_DIR}/CPackGeneratedConfig.cmake)
|
||||
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackGeneratedConfig.cmake)
|
||||
|
||||
include(CPack)
|
||||
|
||||
cpack_add_component(Core DISPLAY_NAME "Plex Media Player" DESCRIPTION "Plex Media Player (Core Application)" REQUIRED)
|
||||
|
||||
if(WIN32)
|
||||
FILE(TO_CMAKE_PATH ${DEPENDENCY_ROOT} tmp)
|
||||
install(FILES ${tmp}/bin/mpv-1.dll DESTINATION .)
|
||||
install(FILES ${tmp}/lib/SDL2.dll DESTINATION .)
|
||||
install(FILES ${tmp}/lib/libcec.dll DESTINATION .)
|
||||
if(IS_DIRECTORY ${CMAKE_BINARY_DIR}/extradlls)
|
||||
file(GLOB EXTRADLLS ${CMAKE_BINARY_DIR}/extradlls/*.dll)
|
||||
install(FILES ${EXTRADLLS} DESTINATION .)
|
||||
endif()
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/bundle/win/qt.conf DESTINATION .)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/bundle/win/PlexMediaPlayer-angle.bat DESTINATION .)
|
||||
#add_custom_command(TARGET package POST_BUILD COMMAND ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd ${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.exe WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
# group/component configuration
|
||||
message(STATUS configure IFW)
|
||||
include(CPackIFW)
|
||||
cpack_ifw_configure_component(Core PRIORITY 1 SCRIPT ${CMAKE_SOURCE_DIR}/bundle/win/shortcut.qs)
|
||||
endif(WIN32)
|
3
CMakeModules/CPackGeneratedConfig.cmake.in
Normal file
3
CMakeModules/CPackGeneratedConfig.cmake.in
Normal file
@ -0,0 +1,3 @@
|
||||
if(CPACK_GENERATOR MATCHES "IFW")
|
||||
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
|
||||
endif()
|
17
CMakeModules/CompleteBundle.cmake
Normal file
17
CMakeModules/CompleteBundle.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
if(APPLE)
|
||||
set(SCRIPT CompleteBundleOSX)
|
||||
elseif(WIN32)
|
||||
set(SCRIPT CompleteBundleWin)
|
||||
endif(APPLE)
|
||||
|
||||
option(CODE_SIGN "code sign the app" OFF)
|
||||
if(CODE_SIGN)
|
||||
set(DO_SIGN 1)
|
||||
else(CODE_SIGN)
|
||||
set(DO_SIGN 0)
|
||||
endif(CODE_SIGN)
|
||||
|
||||
if(SCRIPT)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/CMakeModules/${SCRIPT}.cmake.in ${SCRIPT}.cmake @ONLY)
|
||||
install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/${SCRIPT}.cmake)
|
||||
endif(SCRIPT)
|
69
CMakeModules/CompleteBundleOSX.cmake.in
Normal file
69
CMakeModules/CompleteBundleOSX.cmake.in
Normal file
@ -0,0 +1,69 @@
|
||||
set(app "@EXE@")
|
||||
|
||||
list(APPEND BINS "Contents/Resources/updater")
|
||||
list(APPEND BINS "Contents/Resources/@HELPER_NAME@")
|
||||
|
||||
set(args ${app})
|
||||
list(APPEND args "-verbose=2")
|
||||
list(APPEND args "-qmldir=@SOURCE_ROOT@/src/ui")
|
||||
foreach(BIN ${BINS})
|
||||
list(APPEND args "-executable=${app}/${BIN}")
|
||||
endforeach(BIN ${BINS})
|
||||
|
||||
execute_process(
|
||||
COMMAND @QTROOT@/bin/macdeployqt ${args}
|
||||
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
|
||||
#set(ENTITLEMENTS --entitlements @SOURCE_ROOT@/bundle/osx/Konvergo.entitlements)
|
||||
set(CODESIGN codesign ${ENTITLEMENTS} --force --sign "Developer ID Application: Plex Inc.")
|
||||
|
||||
macro(sign_binary BIN)
|
||||
message(STATUS "Signing: ${BIN}")
|
||||
execute_process(
|
||||
COMMAND ${CODESIGN} "${BIN}"
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to sign ${BIN}")
|
||||
endif(NOT ${result} EQUAL 0)
|
||||
endmacro(sign_binary BIN)
|
||||
|
||||
if(@DO_SIGN@)
|
||||
|
||||
# we need to sign the webengine helper before the framework
|
||||
# add --entitlements @SOURCE_ROOT@/bundle/osx/WebEngine.entitlements when we want to sandbox
|
||||
set(WEB_PROC "${CMAKE_INSTALL_PREFIX}/${app}/Contents/Frameworks/QtWebEngineCore.framework/Versions/Current/Helpers/QtWebEngineProcess.app")
|
||||
sign_binary(${WEB_PROC})
|
||||
|
||||
file(GLOB_RECURSE LIBS
|
||||
FOLLOW_SYMLINKS
|
||||
"${CMAKE_INSTALL_PREFIX}/${app}/*.dylib"
|
||||
)
|
||||
file(GLOB FRAMEWORKS FOLLOW_SYMLINKS LIST_DIRECTORIES true "${CMAKE_INSTALL_PREFIX}/${app}/Contents/Frameworks/*")
|
||||
foreach(LIB ${LIBS} ${FRAMEWORKS})
|
||||
sign_binary(${LIB})
|
||||
endforeach(LIB ${LIBS})
|
||||
|
||||
foreach(BIN ${BINS})
|
||||
sign_binary(${CMAKE_INSTALL_PREFIX}/${app}/${BIN})
|
||||
endforeach(BIN ${BINS})
|
||||
|
||||
sign_binary(${CMAKE_INSTALL_PREFIX}/${app})
|
||||
|
||||
message("Verifing signature")
|
||||
execute_process(
|
||||
COMMAND codesign --verbose=4 --verify "${CMAKE_INSTALL_PREFIX}/@EXE@"
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to verify binary!")
|
||||
endif(NOT ${result} EQUAL 0)
|
||||
execute_process(
|
||||
COMMAND spctl --verbose=4 --assess --type execute "${CMAKE_INSTALL_PREFIX}/@EXE@"
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to verify binary!")
|
||||
endif(NOT ${result} EQUAL 0)
|
||||
endif(@DO_SIGN@)
|
10
CMakeModules/CompleteBundleWin.cmake.in
Normal file
10
CMakeModules/CompleteBundleWin.cmake.in
Normal file
@ -0,0 +1,10 @@
|
||||
execute_process(COMMAND @QTROOT@/bin/windeployqt.exe --qmldir @SOURCE_ROOT@/src/ui ${CMAKE_INSTALL_PREFIX}/PlexMediaPlayer.exe)
|
||||
|
||||
if(@DO_SIGN@)
|
||||
file(GLOB_RECURSE EXES ${CMAKE_INSTALL_PREFIX}/*.exe ${CMAKE_INSTALL_PREFIX}/*.dll)
|
||||
set(ENV{errorlevel} 1)
|
||||
foreach(e ${EXES})
|
||||
message("-- Signing: ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd ${e}")
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd "${e}" RESULT_VARIABLE RES)
|
||||
endforeach()
|
||||
endif()
|
28
CMakeModules/DependencyConfiguration.cmake
Normal file
28
CMakeModules/DependencyConfiguration.cmake
Normal file
@ -0,0 +1,28 @@
|
||||
find_package(PkgConfig)
|
||||
option(DISABLE_BUNDLED_DEPS "Disable the bundled deps on certain platforms" OFF)
|
||||
|
||||
if(APPLE AND NOT DISABLE_BUNDLED_DEPS)
|
||||
set(DEFAULT_ROOT "${CMAKE_SOURCE_DIR}/dependencies/konvergo-depends-darwin-x86_64-release")
|
||||
set(DEPENDENCY_ROOT ${DEFAULT_ROOT} CACHE PATH "Path where the deps are located")
|
||||
endif(APPLE AND NOT DISABLE_BUNDLED_DEPS)
|
||||
|
||||
if(DEPENDENCY_ROOT)
|
||||
message(STATUS "Going to use bundled deps in directory: ${DEPENDENCY_ROOT}")
|
||||
set(CMAKE_FIND_ROOT_PATH ${DEPENDENCY_ROOT})
|
||||
set(CMAKE_PREFIX_PATH ${DEPENDENCY_ROOT})
|
||||
set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig)
|
||||
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE)
|
||||
include_directories(${CMAKE_FIND_ROOT_PATH}/include)
|
||||
else(DEPENDENCY_ROOT)
|
||||
message(STATUS "Not using bundled deps")
|
||||
endif(DEPENDENCY_ROOT)
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
# on windows we need to download the updater binary seperately
|
||||
if(WIN32)
|
||||
file(DOWNLOAD https://nightlies.plex.tv/directdl/plex-dependencies/konvergo-qt/updater.exe ${CMAKE_BINARY_DIR}/updater.exe
|
||||
SHOW_PROGRESS
|
||||
EXPECTED_HASH SHA1=d3b4f70d6542fa42c8edd2b9b93fd0916bf20f07
|
||||
TLS_VERIFY OFF)
|
||||
endif(WIN32)
|
69
CMakeModules/FindBreakpad.cmake
Normal file
69
CMakeModules/FindBreakpad.cmake
Normal file
@ -0,0 +1,69 @@
|
||||
###############################################################################
|
||||
# CMake module to search for the mpv libraries.
|
||||
#
|
||||
# WARNING: This module is experimental work in progress.
|
||||
#
|
||||
# Based one FindVLC.cmake by:
|
||||
# Copyright (c) 2011 Michael Jansen <info@michael-jansen.biz>
|
||||
# Modified by Tobias Hieta <tobias@hieta.se>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
### Global Configuration Section
|
||||
#
|
||||
SET(_BREAKPAD_REQUIRED_VARS BREAKPAD_INCLUDE_DIR BREAKPAD_LIBRARY)
|
||||
|
||||
#
|
||||
### BREAKPAD uses pkgconfig.
|
||||
#
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_BREAKPAD QUIET breakpad-client)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
#
|
||||
### Look for the include files.
|
||||
#
|
||||
find_path(
|
||||
BREAKPAD_INCLUDE_DIR
|
||||
NAMES google_breakpad/common/breakpad_types.h
|
||||
PATH_SUFFIXES breakpad
|
||||
HINTS
|
||||
${PC_BREAKPAD_INCLUDEDIR}
|
||||
${PC_BREAKPAD_INCLUDE_DIRS} # Unused for BREAKPAD but anyway
|
||||
DOC "BREAKPAD include directory"
|
||||
)
|
||||
mark_as_advanced(BREAKPAD_INCLUDE_DIR)
|
||||
set(BREAKPAD_INCLUDE_DIRS ${BREAKPAD_INCLUDE_DIR})
|
||||
|
||||
#
|
||||
### Look for the libraries (BREAKPAD and BREAKPADsore)
|
||||
#
|
||||
find_library(
|
||||
BREAKPAD_LIBRARY
|
||||
NAMES breakpad_client
|
||||
HINTS
|
||||
${PC_BREAKPAD_LIBDIR}
|
||||
${PC_BREAKPAD_LIBRARY_DIRS} # Unused for BREAKPAD but anyway
|
||||
PATH_SUFFIXES lib${LIB_SUFFIX}
|
||||
)
|
||||
get_filename_component(_BREAKPAD_LIBRARY_DIR ${BREAKPAD_LIBRARY} PATH)
|
||||
mark_as_advanced(BREAKPAD_LIBRARY)
|
||||
|
||||
set(BREAKPAD_LIBRARY_DIRS _BREAKPAD_CORE_LIBRARY_DIR _BREAKPAD_LIBRARY_DIR)
|
||||
list(REMOVE_DUPLICATES BREAKPAD_LIBRARY_DIRS)
|
||||
mark_as_advanced(BREAKPAD_LIBRARY_DIRS)
|
||||
|
||||
#
|
||||
### Check if everything was found and if the version is sufficient.
|
||||
#
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
BREAKPAD
|
||||
REQUIRED_VARS ${_BREAKPAD_REQUIRED_VARS}
|
||||
VERSION_VAR BREAKPAD_VERSION_STRING
|
||||
)
|
||||
|
26
CMakeModules/FindCEC.cmake
Normal file
26
CMakeModules/FindCEC.cmake
Normal file
@ -0,0 +1,26 @@
|
||||
if(CEC_INCLUDE_DIR)
|
||||
# Already in cache, be silent
|
||||
set(CEC_FIND_QUIETLY TRUE)
|
||||
endif(CEC_INCLUDE_DIR)
|
||||
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_CEC QUIET libcec>=2.0)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
Find_Path(CEC_INCLUDE_DIR
|
||||
NAMES cec.h
|
||||
PATHS /usr/include usr/local/include
|
||||
PATH_SUFFIXES libcec
|
||||
HINTS ${_CEC_INCLUDEDIR}
|
||||
)
|
||||
|
||||
Find_Library(CEC_LIBRARY
|
||||
NAMES cec
|
||||
PATHS /usr/lib usr/local/lib
|
||||
HINTS ${_CEC_LIBDIR}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CEC DEFAULT_MSG CEC_LIBRARY CEC_INCLUDE_DIR)
|
||||
|
||||
|
17
CMakeModules/FindDL.cmake
Normal file
17
CMakeModules/FindDL.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
# - find where dlopen and friends are located.
|
||||
# DL_FOUND - system has dynamic linking interface available
|
||||
# DL_INCLUDE_DIR - where dlfcn.h is located.
|
||||
# DL_LIBRARIES - libraries needed to use dlopen
|
||||
|
||||
include(CheckFunctionExists)
|
||||
|
||||
find_path(DL_INCLUDE_DIR NAMES dlfcn.h)
|
||||
find_library(DL_LIBRARIES NAMES dl)
|
||||
if(DL_LIBRARIES)
|
||||
set(DL_FOUND)
|
||||
else(DL_LIBRARIES)
|
||||
check_function_exists(dlopen DL_FOUND)
|
||||
# If dlopen can be found without linking in dl then dlopen is part
|
||||
# of libc, so don't need to link extra libs.
|
||||
set(DL_LIBRARIES "")
|
||||
endif(DL_LIBRARIES)
|
73
CMakeModules/FindGLES2.cmake
Normal file
73
CMakeModules/FindGLES2.cmake
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright (c) 2012, Guillermo A. Amaral B. (gamaral) <g@maral.me>.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is part of Marshmallow Game Engine.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
# EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# The views and conclusions contained in the software and documentation are
|
||||
# those of the authors and should not be interpreted as representing official
|
||||
# policies, either expressed or implied, of the project as a whole.
|
||||
#
|
||||
###############################################################################
|
||||
# Find GLES2
|
||||
###############################################################################
|
||||
#
|
||||
# GLES2_FOUND
|
||||
# GLES2_INCLUDE_DIR
|
||||
# GLES2_LIBRARY
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
find_path(GLES2_INCLUDE_DIR GLES2/gl2.h
|
||||
HINTS $ENV{GLES2DIR}
|
||||
PATH_SUFFIXES include
|
||||
PATHS ~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/usr/X11R6
|
||||
/opt/local
|
||||
/opt/vc
|
||||
/opt
|
||||
)
|
||||
|
||||
find_library(GLES2_LIBRARY
|
||||
GLESv2
|
||||
HINTS $ENV{GLES2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS ~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/usr/X11R6
|
||||
/opt/local
|
||||
/opt/vc
|
||||
/opt
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLES2 DEFAULT_MSG GLES2_LIBRARY)
|
||||
|
||||
mark_as_advanced(GLES2_LIBRARY GLES2_INCLUDE_DIR)
|
||||
|
||||
set(OPENGL_FOUND ${GLES2_FOUND})
|
311
CMakeModules/FindICU.cmake
Normal file
311
CMakeModules/FindICU.cmake
Normal file
@ -0,0 +1,311 @@
|
||||
# This module can find the International Components for Unicode (ICU) Library
|
||||
#
|
||||
# Requirements:
|
||||
# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args)
|
||||
#
|
||||
# The following variables will be defined for your use:
|
||||
# - ICU_FOUND : were all of your specified components found (include dependencies)?
|
||||
# - ICU_INCLUDE_DIRS : ICU include directory
|
||||
# - ICU_LIBRARIES : ICU libraries
|
||||
# - ICU_VERSION : complete version of ICU (x.y.z)
|
||||
# - ICU_MAJOR_VERSION : major version of ICU
|
||||
# - ICU_MINOR_VERSION : minor version of ICU
|
||||
# - ICU_PATCH_VERSION : patch version of ICU
|
||||
# - ICU_<COMPONENT>_FOUND : were <COMPONENT> found? (FALSE for non specified component if it is not a dependency)
|
||||
#
|
||||
# For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways:
|
||||
# - run cmake with -DICU_ROOT=<PATH>
|
||||
# - define an environment variable with the same name before running cmake
|
||||
# With cmake-gui, before pressing "Configure":
|
||||
# 1) Press "Add Entry" button
|
||||
# 2) Add a new entry defined as:
|
||||
# - Name: ICU_ROOT
|
||||
# - Type: choose PATH in the selection list
|
||||
# - Press "..." button and select the root installation of ICU
|
||||
#
|
||||
# Example Usage:
|
||||
#
|
||||
# 1. Copy this file in the root of your project source directory
|
||||
# 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt:
|
||||
# set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
||||
# 3. Finally call find_package() once, here are some examples to pick from
|
||||
#
|
||||
# Require ICU 4.4 or later
|
||||
# find_package(ICU 4.4 REQUIRED)
|
||||
#
|
||||
# if(ICU_FOUND)
|
||||
# include_directories(${ICU_INCLUDE_DIRS})
|
||||
# add_executable(myapp myapp.c)
|
||||
# target_link_libraries(myapp ${ICU_LIBRARIES})
|
||||
# endif(ICU_FOUND)
|
||||
|
||||
#=============================================================================
|
||||
# Copyright (c) 2011-2013, julp
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#=============================================================================
|
||||
|
||||
########## Private ##########
|
||||
if(NOT DEFINED ICU_PUBLIC_VAR_NS)
|
||||
set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables
|
||||
endif(NOT DEFINED ICU_PUBLIC_VAR_NS)
|
||||
if(NOT DEFINED ICU_PRIVATE_VAR_NS)
|
||||
set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables
|
||||
endif(NOT DEFINED ICU_PRIVATE_VAR_NS)
|
||||
if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS)
|
||||
set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables
|
||||
endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS)
|
||||
|
||||
function(icudebug _VARNAME)
|
||||
if(${ICU_PUBLIC_VAR_NS}_DEBUG)
|
||||
if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
|
||||
message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}")
|
||||
else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
|
||||
message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = <UNDEFINED>")
|
||||
endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
|
||||
endif(${ICU_PUBLIC_VAR_NS}_DEBUG)
|
||||
endfunction(icudebug)
|
||||
|
||||
set(${ICU_PRIVATE_VAR_NS}_ROOT "")
|
||||
if(DEFINED ENV{ICU_ROOT})
|
||||
set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}")
|
||||
endif(DEFINED ENV{ICU_ROOT})
|
||||
if (DEFINED ICU_ROOT)
|
||||
set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}")
|
||||
endif(DEFINED ICU_ROOT)
|
||||
|
||||
set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES )
|
||||
set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES )
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64")
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64")
|
||||
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin")
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib")
|
||||
|
||||
set(${ICU_PRIVATE_VAR_NS}_COMPONENTS )
|
||||
# <icu component name> <library name 1> ... <library name N>
|
||||
macro(icu_declare_component _NAME)
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME})
|
||||
set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN})
|
||||
endmacro(icu_declare_component)
|
||||
|
||||
icu_declare_component(data icudata)
|
||||
icu_declare_component(uc icuuc) # Common and Data libraries
|
||||
icu_declare_component(i18n icui18n icuin) # Internationalization library
|
||||
icu_declare_component(io icuio ustdio) # Stream and I/O Library
|
||||
icu_declare_component(le icule) # Layout library
|
||||
icu_declare_component(lx iculx) # Paragraph Layout library
|
||||
|
||||
########## Public ##########
|
||||
set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE)
|
||||
set(${ICU_PUBLIC_VAR_NS}_LIBRARIES )
|
||||
set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS )
|
||||
set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "")
|
||||
set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "")
|
||||
set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "")
|
||||
set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "")
|
||||
set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "")
|
||||
set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "")
|
||||
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS})
|
||||
string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
|
||||
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro
|
||||
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
|
||||
|
||||
# Check components
|
||||
if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least
|
||||
set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
|
||||
else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
|
||||
list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
|
||||
list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
|
||||
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
|
||||
if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
|
||||
message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}")
|
||||
endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
|
||||
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
|
||||
endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
|
||||
|
||||
# Includes
|
||||
find_path(
|
||||
${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
|
||||
NAMES unicode/utypes.h utypes.h
|
||||
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
|
||||
PATH_SUFFIXES "include"
|
||||
DOC "Include directories for ICU"
|
||||
)
|
||||
|
||||
if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
|
||||
########## <part to keep synced with tests/version/CMakeLists.txt> ##########
|
||||
if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4
|
||||
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
|
||||
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[
|
||||
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
|
||||
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[
|
||||
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
|
||||
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3
|
||||
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
|
||||
else()
|
||||
message(FATAL_ERROR "ICU version header not found")
|
||||
endif()
|
||||
|
||||
if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3
|
||||
# [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0)
|
||||
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1")
|
||||
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}")
|
||||
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0")
|
||||
elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*")
|
||||
#
|
||||
# Since version 4.9.1, ICU release version numbering was totaly changed, see:
|
||||
# - http://site.icu-project.org/download/49
|
||||
# - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU
|
||||
#
|
||||
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
|
||||
string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
|
||||
string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
|
||||
elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[
|
||||
# [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0
|
||||
set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if
|
||||
if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$")
|
||||
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
|
||||
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}")
|
||||
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0")
|
||||
elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
|
||||
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
|
||||
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}")
|
||||
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "failed to detect ICU version")
|
||||
endif()
|
||||
set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}")
|
||||
########## </part to keep synced with tests/version/CMakeLists.txt> ##########
|
||||
|
||||
# Check dependencies (implies pkg-config)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
|
||||
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP})
|
||||
pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET)
|
||||
|
||||
if(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
|
||||
foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES})
|
||||
string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY})
|
||||
list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY})
|
||||
endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY)
|
||||
endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
|
||||
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
|
||||
list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
# Check libraries
|
||||
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
|
||||
set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES )
|
||||
set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES )
|
||||
foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}})
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}")
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d")
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}")
|
||||
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d")
|
||||
endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME)
|
||||
|
||||
find_library(
|
||||
${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
|
||||
NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES}
|
||||
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
|
||||
PATH_SUFFIXES ${_ICU_LIB_SUFFIXES}
|
||||
DOC "Release libraries for ICU"
|
||||
)
|
||||
find_library(
|
||||
${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
|
||||
NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES}
|
||||
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
|
||||
PATH_SUFFIXES ${_ICU_LIB_SUFFIXES}
|
||||
DOC "Debug libraries for ICU"
|
||||
)
|
||||
|
||||
string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
|
||||
if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found
|
||||
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE)
|
||||
set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE)
|
||||
else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found
|
||||
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE)
|
||||
if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug
|
||||
set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}")
|
||||
elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release
|
||||
set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}")
|
||||
else() # both found
|
||||
set(
|
||||
${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
|
||||
optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}
|
||||
debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}})
|
||||
endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
|
||||
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
|
||||
|
||||
# Try to find out compiler flags
|
||||
find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT})
|
||||
if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
|
||||
|
||||
# Check find_package arguments
|
||||
include(FindPackageHandleStandardArgs)
|
||||
if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
|
||||
find_package_handle_standard_args(
|
||||
${ICU_PUBLIC_VAR_NS}
|
||||
REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
|
||||
VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION
|
||||
)
|
||||
else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
|
||||
find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
|
||||
endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
|
||||
else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
|
||||
if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
|
||||
message(FATAL_ERROR "Could not find ICU include directory")
|
||||
endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
|
||||
endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
|
||||
${ICU_PUBLIC_VAR_NS}_LIBRARIES
|
||||
)
|
||||
|
||||
# IN (args)
|
||||
icudebug("FIND_COMPONENTS")
|
||||
icudebug("FIND_REQUIRED")
|
||||
icudebug("FIND_QUIETLY")
|
||||
icudebug("FIND_VERSION")
|
||||
# OUT
|
||||
# Found
|
||||
icudebug("FOUND")
|
||||
icudebug("UC_FOUND")
|
||||
icudebug("I18N_FOUND")
|
||||
icudebug("IO_FOUND")
|
||||
icudebug("LE_FOUND")
|
||||
icudebug("LX_FOUND")
|
||||
icudebug("DATA_FOUND")
|
||||
# Flags
|
||||
icudebug("C_FLAGS")
|
||||
icudebug("CPP_FLAGS")
|
||||
icudebug("CXX_FLAGS")
|
||||
icudebug("C_SHARED_FLAGS")
|
||||
icudebug("CPP_SHARED_FLAGS")
|
||||
icudebug("CXX_SHARED_FLAGS")
|
||||
# Linking
|
||||
icudebug("INCLUDE_DIRS")
|
||||
icudebug("LIBRARIES")
|
||||
# Version
|
||||
icudebug("MAJOR_VERSION")
|
||||
icudebug("MINOR_VERSION")
|
||||
icudebug("PATCH_VERSION")
|
||||
icudebug("VERSION")
|
57
CMakeModules/FindIconv.cmake
Normal file
57
CMakeModules/FindIconv.cmake
Normal file
@ -0,0 +1,57 @@
|
||||
# - Try to find Iconv
|
||||
# Once done this will define
|
||||
#
|
||||
# ICONV_FOUND - system has Iconv
|
||||
# ICONV_INCLUDE_DIR - the Iconv include directory
|
||||
# ICONV_LIBRARIES - Link these to use Iconv
|
||||
# ICONV_SECOND_ARGUMENT_IS_CONST - the second argument for iconv() is const
|
||||
#
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||
# Already in cache, be silent
|
||||
SET(ICONV_FIND_QUIETLY TRUE)
|
||||
ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||
|
||||
FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
|
||||
|
||||
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c)
|
||||
|
||||
IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||
SET(ICONV_FOUND TRUE)
|
||||
ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES})
|
||||
IF(ICONV_FOUND)
|
||||
check_cxx_source_compiles("
|
||||
#include <iconv.h>
|
||||
int main(){
|
||||
iconv_t conv = 0;
|
||||
const char* in = 0;
|
||||
size_t ilen = 0;
|
||||
char* out = 0;
|
||||
size_t olen = 0;
|
||||
iconv(conv, &in, &ilen, &out, &olen);
|
||||
return 0;
|
||||
}
|
||||
" ICONV_SECOND_ARGUMENT_IS_CONST )
|
||||
ENDIF(ICONV_FOUND)
|
||||
set(CMAKE_REQUIRED_INCLUDES)
|
||||
set(CMAKE_REQUIRED_LIBRARIES)
|
||||
|
||||
IF(ICONV_FOUND)
|
||||
IF(NOT ICONV_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}")
|
||||
ENDIF(NOT ICONV_FIND_QUIETLY)
|
||||
ELSE(ICONV_FOUND)
|
||||
IF(Iconv_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find Iconv")
|
||||
ENDIF(Iconv_FIND_REQUIRED)
|
||||
ENDIF(ICONV_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
ICONV_INCLUDE_DIR
|
||||
ICONV_LIBRARIES
|
||||
ICONV_SECOND_ARGUMENT_IS_CONST
|
||||
)
|
80
CMakeModules/FindMPV.cmake
Normal file
80
CMakeModules/FindMPV.cmake
Normal file
@ -0,0 +1,80 @@
|
||||
###############################################################################
|
||||
# CMake module to search for the mpv libraries.
|
||||
#
|
||||
# WARNING: This module is experimental work in progress.
|
||||
#
|
||||
# Based one FindVLC.cmake by:
|
||||
# Copyright (c) 2011 Michael Jansen <info@michael-jansen.biz>
|
||||
# Modified by Tobias Hieta <tobias@hieta.se>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
### Global Configuration Section
|
||||
#
|
||||
SET(_MPV_REQUIRED_VARS MPV_INCLUDE_DIR MPV_LIBRARY)
|
||||
|
||||
#
|
||||
### MPV uses pkgconfig.
|
||||
#
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_MPV QUIET mpv)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
if(PC_MPV_FOUND)
|
||||
#
|
||||
### Look for the include files.
|
||||
#
|
||||
find_path(
|
||||
MPV_INCLUDE_DIR
|
||||
NAMES mpv/client.h
|
||||
HINTS
|
||||
${PC_MPV_INCLUDEDIR}
|
||||
${PC_MPV_INCLUDE_DIRS} # Unused for MPV but anyway
|
||||
DOC "MPV include directory"
|
||||
)
|
||||
|
||||
#
|
||||
### Look for the libraries
|
||||
#
|
||||
set(_MPV_LIBRARY_NAMES mpv)
|
||||
if(PC_MPV_LIBRARIES)
|
||||
set(_MPV_LIBRARY_NAMES ${PC_MPV_LIBRARIES})
|
||||
endif(PC_MPV_LIBRARIES)
|
||||
|
||||
foreach(l ${_MPV_LIBRARY_NAMES})
|
||||
find_library(
|
||||
MPV_LIBRARY_${l}
|
||||
NAMES ${l}
|
||||
HINTS
|
||||
${PC_MPV_LIBDIR}
|
||||
${PC_MPV_LIBRARY_DIRS} # Unused for MPV but anyway
|
||||
PATH_SUFFIXES lib${LIB_SUFFIX}
|
||||
)
|
||||
list(APPEND MPV_LIBRARY ${MPV_LIBRARY_${l}})
|
||||
endforeach()
|
||||
get_filename_component(_MPV_LIBRARY_DIR ${MPV_LIBRARY_mpv} PATH)
|
||||
mark_as_advanced(MPV_LIBRARY)
|
||||
|
||||
set(MPV_LIBRARY_DIRS _MPV_LIBRARY_DIR)
|
||||
list(REMOVE_DUPLICATES MPV_LIBRARY_DIRS)
|
||||
|
||||
endif()
|
||||
|
||||
mark_as_advanced(MPV_INCLUDE_DIR)
|
||||
mark_as_advanced(MPV_LIBRARY_DIRS)
|
||||
set(MPV_INCLUDE_DIRS ${MPV_INCLUDE_DIR})
|
||||
|
||||
#
|
||||
### Check if everything was found and if the version is sufficient.
|
||||
#
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
MPV
|
||||
REQUIRED_VARS ${_MPV_REQUIRED_VARS}
|
||||
VERSION_VAR MPV_VERSION_STRING
|
||||
)
|
||||
|
69
CMakeModules/FindSDL2.cmake
Normal file
69
CMakeModules/FindSDL2.cmake
Normal file
@ -0,0 +1,69 @@
|
||||
###############################################################################
|
||||
# CMake module to search for the SDL libraries.
|
||||
#
|
||||
# WARNING: This module is experimental work in progress.
|
||||
#
|
||||
# Based one FindVLC.cmake by:
|
||||
# Copyright (c) 2011 Michael Jansen <info@michael-jansen.biz>
|
||||
# Modified by Tobias Hieta <tobias@hieta.se>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
### Global Configuration Section
|
||||
#
|
||||
SET(_SDL2_REQUIRED_VARS SDL2_INCLUDE_DIR SDL2_LIBRARY)
|
||||
|
||||
#
|
||||
### SDL uses pkgconfig.
|
||||
#
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_SDL QUIET sdl2)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
#
|
||||
### Look for the include files.
|
||||
#
|
||||
find_path(
|
||||
SDL2_INCLUDE_DIR
|
||||
NAMES SDL.h
|
||||
PATH_SUFFIXES SDL2
|
||||
HINTS
|
||||
${PC_SDL2_INCLUDEDIR}
|
||||
${PC_SDL2_INCLUDE_DIRS} # Unused for SDL but anyway
|
||||
DOC "SDL2 include directory"
|
||||
)
|
||||
mark_as_advanced(SDL2_INCLUDE_DIR)
|
||||
set(SDL2_INCLUDE_DIRS ${SDL_INCLUDE_DIR})
|
||||
|
||||
#
|
||||
### Look for the libraries (SDL and SDLsore)
|
||||
#
|
||||
find_library(
|
||||
SDL2_LIBRARY
|
||||
NAMES SDL2
|
||||
HINTS
|
||||
${PC_SDL2_LIBDIR}
|
||||
${PC_SDL2_LIBRARY_DIRS} # Unused for SDL but anyway
|
||||
PATH_SUFFIXES lib${LIB_SUFFIX}
|
||||
)
|
||||
get_filename_component(_SDL2_LIBRARY_DIR "${SDL2_LIBRARY}" PATH)
|
||||
mark_as_advanced(SDL2_LIBRARY)
|
||||
|
||||
set(SDL2_LIBRARY_DIRS _SDL2_LIBRARY_DIR)
|
||||
list(REMOVE_DUPLICATES SDL2_LIBRARY_DIRS)
|
||||
mark_as_advanced(SDL2_LIBRARY_DIRS)
|
||||
|
||||
#
|
||||
### Check if everything was found and if the version is sufficient.
|
||||
#
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
SDL2
|
||||
REQUIRED_VARS ${_SDL2_REQUIRED_VARS}
|
||||
VERSION_VAR SDL2_VERSION_STRING
|
||||
)
|
||||
|
0
CMakeModules/GCCConfiguration.cmake
Normal file
0
CMakeModules/GCCConfiguration.cmake
Normal file
13
CMakeModules/GetDate.cmake
Normal file
13
CMakeModules/GetDate.cmake
Normal file
@ -0,0 +1,13 @@
|
||||
MACRO (TODAY RESULT)
|
||||
IF (WIN32)
|
||||
#EXECUTE_PROCESS(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE ${RESULT})
|
||||
#string(REGEX REPLACE "(..)/(..)/(....).*" "\\3\\2\\1" ${RESULT} ${${RESULT}})
|
||||
set(${RESULT})
|
||||
ELSEIF(UNIX)
|
||||
EXECUTE_PROCESS(COMMAND "date" "+%d/%m/%Y" OUTPUT_VARIABLE ${RESULT})
|
||||
string(REGEX REPLACE "(..)/(..)/(....).*" "\\3\\2\\1" ${RESULT} ${${RESULT}})
|
||||
ELSE (WIN32)
|
||||
MESSAGE(SEND_ERROR "date not implemented")
|
||||
SET(${RESULT} 000000)
|
||||
ENDIF (WIN32)
|
||||
ENDMACRO (TODAY)
|
123
CMakeModules/GetGitRevisionDescription.cmake
Normal file
123
CMakeModules/GetGitRevisionDescription.cmake
Normal file
@ -0,0 +1,123 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}")
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
endwhile()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake"
|
||||
@ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
38
CMakeModules/GetGitRevisionDescription.cmake.in
Normal file
38
CMakeModules/GetGitRevisionDescription.cmake.in
Normal file
@ -0,0 +1,38 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
set(HEAD_HASH "${HEAD_REF}")
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
71
CMakeModules/InputConfiguration.cmake
Normal file
71
CMakeModules/InputConfiguration.cmake
Normal file
@ -0,0 +1,71 @@
|
||||
OPTION(ENABLE_SDL2 "Enable SDL2 for joystick handling" ON)
|
||||
if(ENABLE_SDL2)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND)
|
||||
list(APPEND ENABLED_INPUTS SDL2)
|
||||
|
||||
if(NOT WIN32)
|
||||
find_package(Iconv)
|
||||
|
||||
if(NOT ICONV_FOUND)
|
||||
unset(SDL2_FOUND)
|
||||
endif(NOT ICONV_FOUND)
|
||||
|
||||
find_package(DL)
|
||||
|
||||
if(NOT DL_FOUND)
|
||||
unset(SDL2_FOUND)
|
||||
endif(NOT DL_FOUND)
|
||||
|
||||
list(APPEND SDL2_LIBRARY ${ICONV_LIBRARIES} ${DL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_package(Iconv)
|
||||
|
||||
if(NOT ICONV_FOUND)
|
||||
unset(SDL2_FOUND)
|
||||
endif(NOT ICONV_FOUND)
|
||||
|
||||
list(APPEND SDL2_LIBRARY ${ICONV_LIBRARIES})
|
||||
find_library(FORCEFEEDBACK ForceFeedback)
|
||||
find_library(CARBON Carbon)
|
||||
list(APPEND SDL2_LIBRARY ${FORCEFEEDBACK} ${CARBON})
|
||||
endif(APPLE)
|
||||
|
||||
if(SDL2_FOUND)
|
||||
add_definitions(-DHAVE_SDL)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
set(EXTRA_LIBS ${SDL2_LIBRARY})
|
||||
endif(SDL2_FOUND)
|
||||
|
||||
endif(SDL2_FOUND)
|
||||
|
||||
endif(ENABLE_SDL2)
|
||||
|
||||
OPTION(ENABLE_CEC "Enable HDMI/CEC support with libCEC" ON)
|
||||
if(ENABLE_CEC)
|
||||
find_package(CEC)
|
||||
if(CEC_FOUND)
|
||||
list(APPEND ENABLED_INPUTS CEC)
|
||||
add_definitions(-DHAVE_CEC)
|
||||
include_directories(${CEC_INCLUDE_DIR})
|
||||
set(EXTRA_LIBS ${EXTRA_LIBS} ${CEC_LIBRARY})
|
||||
endif(CEC_FOUND)
|
||||
|
||||
endif(ENABLE_CEC)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
OPTION(ENABLE_LIRC "Enable LIRC for Linux IR handling" ON)
|
||||
if(ENABLE_LIRC)
|
||||
list(APPEND ENABLED_INPUTS LIRC)
|
||||
add_definitions(-DHAVE_LIRC)
|
||||
endif(ENABLE_LIRC)
|
||||
endif(UNIX AND NOT APPLE)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND ENABLED_INPUTS "AppleRemote")
|
||||
endif(APPLE)
|
||||
|
||||
string(REPLACE ";" " " _STR "${ENABLED_INPUTS}")
|
||||
message(STATUS "Enabled Inputs: " ${_STR})
|
14
CMakeModules/LinuxConfiguration.cmake
Normal file
14
CMakeModules/LinuxConfiguration.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
find_package(X11)
|
||||
if(X11_FOUND AND X11_Xrandr_FOUND)
|
||||
include_directories(X11_X11_INCLUDE_PATH X11_Xrandr_INCLUDE_PATH)
|
||||
set(X11XRANDR_FOUND 1)
|
||||
add_definitions(-DUSE_X11XRANDR)
|
||||
endif()
|
||||
|
||||
if (NOT BUILD_TARGET STREQUAL "RPI")
|
||||
set(USE_X11POWER ON)
|
||||
add_definitions(-DUSE_X11POWER)
|
||||
endif()
|
||||
|
||||
set(INSTALL_BIN_DIR bin)
|
||||
set(INSTALL_RESOURCE_DIR share)
|
16
CMakeModules/NameConfiguration.cmake
Normal file
16
CMakeModules/NameConfiguration.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
set(HELPER_TARGET PMPHelper)
|
||||
set(MAIN_TARGET PlexMediaPlayer)
|
||||
|
||||
# Name of the output binary, defaults are only used on Linux
|
||||
set(HELPER_NAME pmphelper)
|
||||
set(MAIN_NAME plexmediaplayer)
|
||||
|
||||
if(APPLE)
|
||||
set(HELPER_NAME "PMP Helper")
|
||||
set(MAIN_NAME "Plex Media Player")
|
||||
elseif(WIN32)
|
||||
set(HELPER_NAME "PMPHelper")
|
||||
set(MAIN_NAME "PlexMediaPlayer")
|
||||
endif(APPLE)
|
||||
|
||||
configure_file(src/shared/Names.cpp.in src/shared/Names.cpp @ONLY)
|
16
CMakeModules/PlayerConfiguration.cmake
Normal file
16
CMakeModules/PlayerConfiguration.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
# We want OpenGL or OpenGLES2
|
||||
find_package(OpenGL)
|
||||
if(NOT OPENGL_FOUND)
|
||||
find_package(GLES2)
|
||||
if(NOT GLES2_FOUND)
|
||||
message(FATAL_ERROR "OpenGL or GLES2 is required")
|
||||
else(NOT GLES2_FOUND)
|
||||
set(OPENGL_LIBS ${GLES2_LIBRARY})
|
||||
endif(NOT GLES2_FOUND)
|
||||
else(NOT OPENGL_FOUND)
|
||||
set(OPENGL_LIBS ${OPENGL_gl_LIBRARY})
|
||||
endif(NOT OPENGL_FOUND)
|
||||
|
||||
find_package(MPV REQUIRED)
|
||||
|
||||
include_directories(${MPV_INCLUDE_DIR})
|
57
CMakeModules/QtConfiguration.cmake
Normal file
57
CMakeModules/QtConfiguration.cmake
Normal file
@ -0,0 +1,57 @@
|
||||
set(QTROOT "/usr/local/Qt/Qt5.5" CACHE PATH "Root of the QT binaries.")
|
||||
set(REQUIRED_QT_VERSION "5.5.0")
|
||||
|
||||
set(QTCONFIGROOT ${QTROOT}/lib/cmake/Qt5)
|
||||
set(components Core Network WebChannel Qml Quick Xml WebEngine)
|
||||
|
||||
if(OPENELEC)
|
||||
set(components ${components} DBus)
|
||||
endif(OPENELEC)
|
||||
|
||||
foreach(COMP ${components})
|
||||
set(mod Qt5${COMP})
|
||||
|
||||
# look for the config files in the QtConfigRoot defined above
|
||||
set(${mod}_DIR ${QTCONFIGROOT}${COMP})
|
||||
|
||||
# look for the actual package
|
||||
find_package(${mod} ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
|
||||
include_directories(${${mod}_INCLUDE_DIRS})
|
||||
if(OPENELEC)
|
||||
include_directories(${${mod}_PRIVATE_INCLUDE_DIRS})
|
||||
endif(OPENELEC)
|
||||
|
||||
list(APPEND QT5_LIBRARIES ${${mod}_LIBRARIES})
|
||||
list(APPEND QT5_CFLAGS ${${mod}_EXECUTABLE_COMPILE_FLAGS})
|
||||
endforeach(COMP ${components})
|
||||
|
||||
list(REMOVE_DUPLICATES QT5_CFLAGS)
|
||||
|
||||
message(STATUS "Qt version: ${Qt5Core_VERSION_STRING}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QT5_CFLAGS}")
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${Qt5WebEngine_INCLUDE_DIRS};${Qt5WebEngine_PRIVATE_INCLUDE_DIRS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${QT5_LIBRARIES})
|
||||
|
||||
OPTION(SKIP_QT_TEST "Skip tests for required Qt features" OFF)
|
||||
|
||||
if(NOT SKIP_QT_TEST)
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("
|
||||
#include <private/qquickwebengineview_p.h>
|
||||
#include <QColor>
|
||||
int main()
|
||||
{
|
||||
QQuickWebEngineView* view = new QQuickWebEngineView(NULL);
|
||||
view->setBackgroundColor(QColor(\"transparent\"));
|
||||
}
|
||||
" WebEngineBackgroundProperty)
|
||||
|
||||
if(NOT WebEngineBackgroundProperty)
|
||||
message(FATAL_ERROR "QQuickWebEngineView doesn't have the background property."
|
||||
"This will break video playback. As of Qt 5.5 you need to manually patch and build Qt to get this property."
|
||||
"With the release of Qt5.6 it will no longer be required. See qt-patches/README for more details.")
|
||||
endif(NOT WebEngineBackgroundProperty)
|
||||
|
||||
endif(NOT SKIP_QT_TEST)
|
30
CMakeModules/VersionConfiguration.cmake
Normal file
30
CMakeModules/VersionConfiguration.cmake
Normal file
@ -0,0 +1,30 @@
|
||||
# Get the current date.
|
||||
include(GetDate)
|
||||
include(WebClientVariables)
|
||||
today(CURRENT_DATE)
|
||||
|
||||
# Get git revision version
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(REFSPEC FULL_GIT_REVISION)
|
||||
if(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND")
|
||||
set(GIT_REVISION "git")
|
||||
else(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND")
|
||||
string(SUBSTRING ${FULL_GIT_REVISION} 0 8 GIT_REVISION)
|
||||
endif(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND")
|
||||
|
||||
# Get the build number if available
|
||||
if(DEFINED ENV{BUILD_NUMBER})
|
||||
set(VERSION_BUILD "$ENV{BUILD_NUMBER}")
|
||||
set(VERSION_BUILD_NR "$ENV{BUILD_NUMBER}")
|
||||
else()
|
||||
set(VERSION_BUILD "dev")
|
||||
set(VERSION_BUILD_NR "0")
|
||||
endif()
|
||||
|
||||
set(VERSION_MAJOR 1)
|
||||
set(VERSION_MINOR 0)
|
||||
set(VERSION_NANO 0)
|
||||
|
||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_NANO}.${VERSION_BUILD}-${GIT_REVISION}")
|
||||
set(CANONICAL_VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_NANO}-${GIT_REVISION}")
|
||||
configure_file(src/Version.cpp.in src/Version.cpp)
|
36
CMakeModules/WebClientResources.cmake
Normal file
36
CMakeModules/WebClientResources.cmake
Normal file
@ -0,0 +1,36 @@
|
||||
include(WebClientVariables)
|
||||
|
||||
option(SKIP_WEB_CLIENT "Skip downloading the web client" OFF)
|
||||
|
||||
if(NOT SKIP_WEB_CLIENT)
|
||||
set(WEB_CLIENT_CPP web-client-${WEB_CLIENT_VERSION}.cpp)
|
||||
set(WEB_CLIENT_URL https://nightlies.plex.tv/directdl/plex-web-client-plexmediaplayer/master/plex-web-client-konvergo-${WEB_CLIENT_VERSION}.cpp.bz2)
|
||||
|
||||
message(STATUS "web-client version: ${WEB_CLIENT_VERSION}")
|
||||
|
||||
file(
|
||||
DOWNLOAD ${WEB_CLIENT_URL} ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2
|
||||
EXPECTED_HASH SHA1=${WEB_CLIENT_HASH}
|
||||
TIMEOUT 100
|
||||
SHOW_PROGRESS
|
||||
TLS_VERIFY ON
|
||||
)
|
||||
|
||||
find_program(BUNZIP2 bunzip2${CMAKE_EXECUTABLE_SUFFIX})
|
||||
if(${BUNZIP2} MATCHES NOT_FOUND)
|
||||
message(FATAL_ERROR "Can't fid bunzip2")
|
||||
endif(${BUNZIP2} MATCHES NOT_FOUND)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WEB_CLIENT_CPP}
|
||||
COMMAND ${BUNZIP2} -k -f ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2
|
||||
COMMENT "Unpacking: ${WEB_CLIENT_CPP}.bz2"
|
||||
)
|
||||
|
||||
add_custom_target(UnpackWebClientResource
|
||||
DEPENDS ${WEB_CLIENT_CPP}
|
||||
)
|
||||
else(NOT SKIP_WEB_CLIENT)
|
||||
message(WARNING "Skipping web-client, you will not a functioning end product")
|
||||
endif(NOT SKIP_WEB_CLIENT)
|
2
CMakeModules/WebClientVariables.cmake
Normal file
2
CMakeModules/WebClientVariables.cmake
Normal file
@ -0,0 +1,2 @@
|
||||
set(WEB_CLIENT_VERSION a1def70)
|
||||
set(WEB_CLIENT_HASH 16d2ef0a5b46fb6746aed6d3f3f7234d463f2c36)
|
14
CMakeModules/Win32Configuration.cmake
Normal file
14
CMakeModules/Win32Configuration.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
set(INSTALL_BIN_DIR .)
|
||||
set(INSTALL_RESOURCE_DIR resources)
|
||||
set(HAVE_UPDATER 1)
|
||||
|
||||
|
||||
# Add install rules for required system runtimes such as MSVCRxx.dll
|
||||
SET (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON)
|
||||
INCLUDE(InstallRequiredSystemLibraries)
|
||||
IF (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
|
||||
INSTALL(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
|
||||
DESTINATION ${INSTALL_BIN_DIR}
|
||||
PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ
|
||||
COMPONENT Runtime)
|
||||
ENDIF (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
|
70
CMakeModules/utils.cmake
Normal file
70
CMakeModules/utils.cmake
Normal file
@ -0,0 +1,70 @@
|
||||
#############################################################
|
||||
function(set_bundle_dir)
|
||||
set(args SOURCES DEST EXCLUDE)
|
||||
include(CMakeParseArguments)
|
||||
cmake_parse_arguments(BD "" "" "${args}" ${ARGN})
|
||||
|
||||
foreach(_BDIR ${BD_SOURCES})
|
||||
file(GLOB _DIRCONTENTS ${_BDIR}/*)
|
||||
foreach(_BDFILE ${_DIRCONTENTS})
|
||||
get_filename_component(_BDFILE_NAME ${_BDFILE} NAME)
|
||||
|
||||
set(PROCESS_FILE 1)
|
||||
foreach(EX_FILE ${BD_EXCLUDE})
|
||||
string(REGEX MATCH ${EX_FILE} DID_MATCH ${_BDFILE})
|
||||
if(NOT "${DID_MATCH}" STREQUAL "")
|
||||
set(PROCESS_FILE 0)
|
||||
endif(NOT "${DID_MATCH}" STREQUAL "")
|
||||
endforeach(EX_FILE ${BD_EXCLUDE})
|
||||
|
||||
if(PROCESS_FILE STREQUAL "1")
|
||||
if(IS_DIRECTORY ${_BDFILE})
|
||||
set_bundle_dir(SOURCES ${_BDFILE} DEST ${BD_DEST}/${_BDFILE_NAME} EXCLUDE ${BD_EXCLUDE})
|
||||
else(IS_DIRECTORY ${_BDFILE})
|
||||
#message("set_bundle_dir : setting package_location ${_BDFILE} = ${BD_DEST}")
|
||||
set_source_files_properties(${_BDFILE} PROPERTIES MACOSX_PACKAGE_LOCATION ${BD_DEST})
|
||||
get_property(BUNDLED_FILES GLOBAL PROPERTY CONFIG_BUNDLED_FILES)
|
||||
set_property(GLOBAL PROPERTY CONFIG_BUNDLED_FILES ${BUNDLED_FILES} ${_BDFILE})
|
||||
|
||||
string(REPLACE "/" "\\\\" GNAME ${BD_DEST})
|
||||
source_group(${GNAME} FILES ${_BDFILE})
|
||||
endif(IS_DIRECTORY ${_BDFILE})
|
||||
endif()
|
||||
endforeach(_BDFILE ${_DIRCONTENTS})
|
||||
endforeach(_BDIR ${BD_SOURCES})
|
||||
endfunction(set_bundle_dir)
|
||||
|
||||
#############################################################
|
||||
macro(find_all_sources DIRECTORY VARIABLE)
|
||||
aux_source_directory(${DIRECTORY} ${VARIABLE})
|
||||
file(GLOB headers ${DIRECTORY}/*h)
|
||||
list(APPEND ${VARIABLE} ${headers})
|
||||
endmacro()
|
||||
|
||||
#############################################################
|
||||
# function to collect all the sources from sub-directories
|
||||
# into a single list
|
||||
function(add_sources)
|
||||
get_property(is_defined GLOBAL PROPERTY SRCS_LIST DEFINED)
|
||||
if(NOT is_defined)
|
||||
define_property(GLOBAL PROPERTY SRCS_LIST
|
||||
BRIEF_DOCS "List of source files"
|
||||
FULL_DOCS "List of source files to be compiled in one library")
|
||||
endif()
|
||||
# make absolute paths
|
||||
set(SRCS)
|
||||
foreach(s IN LISTS ARGN)
|
||||
if(NOT IS_ABSOLUTE "${s}")
|
||||
get_filename_component(s "${s}" ABSOLUTE)
|
||||
endif()
|
||||
list(APPEND SRCS "${s}")
|
||||
endforeach()
|
||||
|
||||
string(REPLACE ${CMAKE_SOURCE_DIR}/src/ "" SUBDIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
string(TOLOWER ${SUBDIR} SUBDIR)
|
||||
string(REPLACE "/" "\\\\" LIBNAME ${SUBDIR})
|
||||
source_group(${LIBNAME} FILES ${SRCS})
|
||||
|
||||
# add it to the global list.
|
||||
set_property(GLOBAL APPEND PROPERTY SRCS_LIST ${SRCS})
|
||||
endfunction(add_sources)
|
52
README.md
Normal file
52
README.md
Normal file
@ -0,0 +1,52 @@
|
||||
## Building
|
||||
|
||||
You need:
|
||||
|
||||
* Qt 5.6 alpha
|
||||
* cmake 3.0 or newer
|
||||
* ninja is recommended for building
|
||||
|
||||
Special Qt requirements:
|
||||
|
||||
* On Windows, you must apply qt-patches/0003-Always-enable-viewport-stuff.patch for
|
||||
correct window scaling. Applying the patches in qt-patches/qt-5.6-alpha/ fixes
|
||||
some stability issues.
|
||||
* On OSX, you should apply qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch
|
||||
to improve the user experience in fullscreen.
|
||||
* You can try to use Qt 5.5, but then you also need to apply the following patches:
|
||||
qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch
|
||||
qt-patches/0004-qtwebengine-transparency-window-creation.patch
|
||||
Without them, video playback will not work.
|
||||
|
||||
Get dependencies:
|
||||
|
||||
* scripts/fetch-binaries.py -p darwin-x86_64
|
||||
|
||||
If you're happy just building from the command line then run CMake for the ninja build tool:
|
||||
|
||||
* mkdir build ; cd build
|
||||
* cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DQTROOT=/path/to/qt -DCMAKE_INSTALL_PREFIX=output ..
|
||||
|
||||
Build (ninja):
|
||||
|
||||
* ninja
|
||||
|
||||
Make a distributable package:
|
||||
|
||||
* ninja install (be patient, it's slow)
|
||||
|
||||
Or if you prefer working in Xcode, run CMake for the xcode build):
|
||||
|
||||
* mkdir build ; cd build
|
||||
* cmake -GXcode -DQTROOT=/path/to/qt ..
|
||||
|
||||
|
||||
### Debugging Web Client
|
||||
|
||||
You can run a locally hosted development version of the web app within the Konvergo application. If the main app window is open you can also run Chrome side by side to debug.
|
||||
|
||||
* Run the `grunt server:konvergo` from the `web-client` submodule. This will run a dev version of the web client
|
||||
* Update the `starturl` in `~/Library/Application Support/Plex Media Player/Plex Media Player.conf` to point to `http://localhost:3333/app/dev-konvergo.html`
|
||||
* Run the `Plex Media Player.app`
|
||||
* Tail the `~/Library/Logs/Plex Media Player/Plex Media Player.log`, optionally grepped with `WebClient` to see `console.log`s
|
||||
* Open Chrome and point to `http://localhost:3333/app/dev-konvergo.html`. This should open a Qt channel to the main `Plex Media Player.app` and function as normal - but with the ability to add breakpoints and inspect code
|
40
bundle/osx/Info.plist.in
Normal file
40
bundle/osx/Info.plist.in
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.9</string>
|
||||
</dict>
|
||||
</plist>
|
12
bundle/osx/Konvergo.entitlements
Normal file
12
bundle/osx/Konvergo.entitlements
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
BIN
bundle/osx/Plex.icns
Normal file
BIN
bundle/osx/Plex.icns
Normal file
Binary file not shown.
10
bundle/osx/WebEngine.entitlements
Normal file
10
bundle/osx/WebEngine.entitlements
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
4
bundle/osx/qt.conf
Normal file
4
bundle/osx/qt.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[Paths]
|
||||
Plugins = ../PlugIns
|
||||
Imports = ../Resources/qml
|
||||
Qml2Imports = ../Resources/qml
|
BIN
bundle/win/Plex.ico
Normal file
BIN
bundle/win/Plex.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
2
bundle/win/PlexMediaPlayer-angle.bat
Normal file
2
bundle/win/PlexMediaPlayer-angle.bat
Normal file
@ -0,0 +1,2 @@
|
||||
set QT_OPENGL=angle
|
||||
start PlexMediaPlayer.exe
|
1
bundle/win/iconres.rc
Normal file
1
bundle/win/iconres.rc
Normal file
@ -0,0 +1 @@
|
||||
IDI_ICON ICON Plex.ico
|
7
bundle/win/qt.conf
Normal file
7
bundle/win/qt.conf
Normal file
@ -0,0 +1,7 @@
|
||||
[Paths]
|
||||
Prefix = .
|
||||
Plugins = .
|
||||
Binaries = .
|
||||
Imports = .
|
||||
Qml2Imports = .
|
||||
LibraryExecutables = .
|
15
bundle/win/shortcut.qs
Normal file
15
bundle/win/shortcut.qs
Normal file
@ -0,0 +1,15 @@
|
||||
function Component()
|
||||
{
|
||||
// default constructor
|
||||
}
|
||||
|
||||
Component.prototype.createOperations = function()
|
||||
{
|
||||
component.createOperations();
|
||||
|
||||
if (systemInfo.productType === "windows") {
|
||||
component.addOperation("CreateShortcut", "@TargetDir@/PlexMediaPlayer.exe", "@StartMenuDir@/Plex Media Player.lnk");
|
||||
component.addOperation("CreateShortcut", "@TargetDir@/PlexMediaPlayer-angle.bat", "@StartMenuDir@/Plex Media Player (DirectX).lnk");
|
||||
component.addOperation("CreateShortcut", "@TargetDir@/maintenancetool.exe", "@StartMenuDir@/Maintain Plex Media Player.lnk");
|
||||
}
|
||||
}
|
7
external/CMakeLists.txt
vendored
Normal file
7
external/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
add_subdirectory(qhttpserver)
|
||||
add_subdirectory(qslog)
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(plistparser)
|
||||
add_subdirectory(SPMediaKeyTap)
|
||||
endif(APPLE)
|
9
external/SPMediaKeyTap/CMakeLists.txt
vendored
Normal file
9
external/SPMediaKeyTap/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
# SPMediaKeyTap uses some deprecated methods, no need to fix that right now, so let's supress the warnings.
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
|
||||
|
||||
add_library(spmediakeytap STATIC
|
||||
SPMediaKeyTap.h SPMediaKeyTap.m
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
|
||||
)
|
8
external/SPMediaKeyTap/LICENSE
vendored
Normal file
8
external/SPMediaKeyTap/LICENSE
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Copyright (c) 2011, Joachim Bengtsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
12
external/SPMediaKeyTap/README.md
vendored
Normal file
12
external/SPMediaKeyTap/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
SPMediaKeyTap
|
||||
=============
|
||||
|
||||
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple is not exposing any APIs allowing third-parties to join in on this collaboration.
|
||||
|
||||
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
|
||||
|
||||
In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use `SPMediaKeyTap`, and how you handle the semi-private `NSEvent` subtypes involved in media keys, including on how to fall back to non-event tap handling of these events.
|
||||
|
||||
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
|
||||
|
||||
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.
|
30
external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
vendored
Normal file
30
external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SPInvocationGrabber : NSObject {
|
||||
id _object;
|
||||
NSInvocation *_invocation;
|
||||
int frameCount;
|
||||
char **frameStrings;
|
||||
BOOL backgroundAfterForward;
|
||||
BOOL onMainAfterForward;
|
||||
BOOL waitUntilDone;
|
||||
}
|
||||
-(id)initWithObject:(id)obj;
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
@property (readonly, retain, nonatomic) id object;
|
||||
@property (readonly, retain, nonatomic) NSInvocation *invocation;
|
||||
@property BOOL backgroundAfterForward;
|
||||
@property BOOL onMainAfterForward;
|
||||
@property BOOL waitUntilDone;
|
||||
-(void)invoke; // will release object and invocation
|
||||
-(void)printBacktrace;
|
||||
-(void)saveBacktrace;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
-(id)nextRunloop;
|
||||
-(id)inBackground;
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
@end
|
127
external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
vendored
Normal file
127
external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
#import "NSObject+SPInvocationGrabbing.h"
|
||||
#import <execinfo.h>
|
||||
|
||||
#pragma mark Invocation grabbing
|
||||
@interface SPInvocationGrabber ()
|
||||
@property (readwrite, retain, nonatomic) id object;
|
||||
@property (readwrite, retain, nonatomic) NSInvocation *invocation;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SPInvocationGrabber
|
||||
- (id)initWithObject:(id)obj;
|
||||
{
|
||||
return [self initWithObject:obj stacktraceSaving:YES];
|
||||
}
|
||||
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
{
|
||||
self.object = obj;
|
||||
|
||||
if(saveStack)
|
||||
[self saveBacktrace];
|
||||
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
free(frameStrings);
|
||||
self.object = nil;
|
||||
self.invocation = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
@synthesize invocation = _invocation, object = _object;
|
||||
|
||||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
||||
- (void)runInBackground;
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
@try {
|
||||
[self invoke];
|
||||
}
|
||||
@finally {
|
||||
[pool drain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation {
|
||||
[anInvocation retainArguments];
|
||||
anInvocation.target = _object;
|
||||
self.invocation = anInvocation;
|
||||
|
||||
if(backgroundAfterForward)
|
||||
[NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil];
|
||||
else if(onMainAfterForward)
|
||||
[self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone];
|
||||
}
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
|
||||
NSMethodSignature *signature = [super methodSignatureForSelector:inSelector];
|
||||
if (signature == NULL)
|
||||
signature = [_object methodSignatureForSelector:inSelector];
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
- (void)invoke;
|
||||
{
|
||||
|
||||
@try {
|
||||
[_invocation invoke];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
|
||||
[self printBacktrace];
|
||||
printf("\n");
|
||||
[e raise];
|
||||
}
|
||||
|
||||
self.invocation = nil;
|
||||
self.object = nil;
|
||||
}
|
||||
|
||||
-(void)saveBacktrace;
|
||||
{
|
||||
void *backtraceFrames[128];
|
||||
frameCount = backtrace(&backtraceFrames[0], 128);
|
||||
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
|
||||
}
|
||||
-(void)printBacktrace;
|
||||
{
|
||||
for(int x = 3; x < frameCount; x++) {
|
||||
if(frameStrings[x] == NULL) { break; }
|
||||
printf("%s\n", frameStrings[x]);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
{
|
||||
return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
|
||||
}
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
{
|
||||
id grabber = [self grab];
|
||||
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
|
||||
return grabber;
|
||||
}
|
||||
- (id)nextRunloop;
|
||||
{
|
||||
return [self invokeAfter:0];
|
||||
}
|
||||
-(id)inBackground;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.backgroundAfterForward = YES;
|
||||
return grabber;
|
||||
}
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.onMainAfterForward = YES;
|
||||
grabber.waitUntilDone = !async;
|
||||
return grabber;
|
||||
}
|
||||
|
||||
@end
|
43
external/SPMediaKeyTap/SPMediaKeyTap.h
vendored
Normal file
43
external/SPMediaKeyTap/SPMediaKeyTap.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
// http://overooped.com/post/2593597587/mediakeys
|
||||
|
||||
#define SPSystemDefinedEventMediaKeys 8
|
||||
|
||||
@interface SPMediaKeyTap : NSObject {
|
||||
EventHandlerRef _app_switching_ref;
|
||||
EventHandlerRef _app_terminating_ref;
|
||||
CFMachPortRef _eventPort;
|
||||
CFRunLoopSourceRef _eventPortSource;
|
||||
CFRunLoopRef _tapThreadRL;
|
||||
BOOL _shouldInterceptMediaKeyEvents;
|
||||
id _delegate;
|
||||
// The app that is frontmost in this list owns media keys
|
||||
NSMutableArray *_mediaKeyAppList;
|
||||
}
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap;
|
||||
-(void)startWatchingMediaKeys;
|
||||
-(void)stopWatchingMediaKeys;
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPMediaKeyTapDelegate)
|
||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
||||
extern NSString *kIgnoreMediaKeysDefaultsKey;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
346
external/SPMediaKeyTap/SPMediaKeyTap.m
vendored
Normal file
346
external/SPMediaKeyTap/SPMediaKeyTap.m
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
// Copyright (c) 2010 Spotify AB
|
||||
#import "SPMediaKeyTap.h"
|
||||
#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule
|
||||
|
||||
@interface SPMediaKeyTap ()
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
-(void)startWatchingAppSwitching;
|
||||
-(void)stopWatchingAppSwitching;
|
||||
-(void)eventTapThread;
|
||||
@end
|
||||
static SPMediaKeyTap *singleton = nil;
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
||||
|
||||
|
||||
// Inspired by http://gist.github.com/546311
|
||||
|
||||
@implementation SPMediaKeyTap
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setup and teardown
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
{
|
||||
_delegate = delegate;
|
||||
[self startWatchingAppSwitching];
|
||||
singleton = self;
|
||||
_mediaKeyAppList = [NSMutableArray new];
|
||||
_tapThreadRL=nil;
|
||||
_eventPort=nil;
|
||||
_eventPortSource=nil;
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
[self stopWatchingMediaKeys];
|
||||
[self stopWatchingAppSwitching];
|
||||
[_mediaKeyAppList release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(void)startWatchingAppSwitching;
|
||||
{
|
||||
// Listen to "app switched" event, so that we don't intercept media keys if we
|
||||
// weren't the last "media key listening" app to be active
|
||||
EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched };
|
||||
OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref);
|
||||
assert(err == noErr);
|
||||
|
||||
eventType.eventKind = kEventAppTerminated;
|
||||
err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref);
|
||||
assert(err == noErr);
|
||||
}
|
||||
-(void)stopWatchingAppSwitching;
|
||||
{
|
||||
if(!_app_switching_ref) return;
|
||||
RemoveEventHandler(_app_switching_ref);
|
||||
_app_switching_ref = NULL;
|
||||
}
|
||||
|
||||
-(void)startWatchingMediaKeys;{
|
||||
// Prevent having multiple mediaKeys threads
|
||||
[self stopWatchingMediaKeys];
|
||||
|
||||
[self setShouldInterceptMediaKeyEvents:YES];
|
||||
|
||||
// Add an event tap to intercept the system defined media key events
|
||||
_eventPort = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(NX_SYSDEFINED),
|
||||
tapEventCallback,
|
||||
self);
|
||||
assert(_eventPort != NULL);
|
||||
|
||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||
assert(_eventPortSource != NULL);
|
||||
|
||||
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
|
||||
[NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil];
|
||||
}
|
||||
-(void)stopWatchingMediaKeys;
|
||||
{
|
||||
// TODO<nevyn>: Shut down thread, remove event tap port and source
|
||||
|
||||
if(_tapThreadRL){
|
||||
CFRunLoopStop(_tapThreadRL);
|
||||
_tapThreadRL=nil;
|
||||
}
|
||||
|
||||
if(_eventPort){
|
||||
CFMachPortInvalidate(_eventPort);
|
||||
CFRelease(_eventPort);
|
||||
_eventPort=nil;
|
||||
}
|
||||
|
||||
if(_eventPortSource){
|
||||
CFRelease(_eventPortSource);
|
||||
_eventPortSource=nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
|
||||
return NO;
|
||||
#else
|
||||
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
|
||||
return
|
||||
![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey]
|
||||
&& floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
[[NSBundle mainBundle] bundleIdentifier], // your app
|
||||
@"com.spotify.client",
|
||||
@"com.apple.iTunes",
|
||||
@"com.apple.QuickTimePlayerX",
|
||||
@"com.apple.quicktimeplayer",
|
||||
@"com.apple.iWork.Keynote",
|
||||
@"com.apple.iPhoto",
|
||||
@"org.videolan.vlc",
|
||||
@"com.apple.Aperture",
|
||||
@"com.plexsquared.Plex",
|
||||
@"com.soundcloud.desktop",
|
||||
@"org.niltsh.MPlayerX",
|
||||
@"com.ilabs.PandorasHelper",
|
||||
@"com.mahasoftware.pandabar",
|
||||
@"com.bitcartel.pandorajam",
|
||||
@"org.clementine-player.clementine",
|
||||
@"fm.last.Last.fm",
|
||||
@"fm.last.Scrobbler",
|
||||
@"com.beatport.BeatportPro",
|
||||
@"com.Timenut.SongKey",
|
||||
@"com.macromedia.fireworks", // the tap messes up their mouse input
|
||||
@"at.justp.Theremin",
|
||||
@"ru.ya.themblsha.YandexMusic",
|
||||
@"com.jriver.MediaCenter18",
|
||||
@"com.jriver.MediaCenter19",
|
||||
@"com.jriver.MediaCenter20",
|
||||
@"co.rackit.mate",
|
||||
@"com.ttitt.b-music",
|
||||
@"com.beardedspice.BeardedSpice",
|
||||
@"com.plug.Plug",
|
||||
@"com.plug.Plug2",
|
||||
@"com.netease.163music",
|
||||
nil
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
{
|
||||
BOOL shouldIntercept = NO;
|
||||
@synchronized(self) {
|
||||
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
||||
}
|
||||
return shouldIntercept;
|
||||
}
|
||||
|
||||
-(void)pauseTapOnTapThread:(BOOL)yeahno;
|
||||
{
|
||||
CGEventTapEnable(self->_eventPort, yeahno);
|
||||
}
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
{
|
||||
BOOL oldSetting;
|
||||
@synchronized(self) {
|
||||
oldSetting = _shouldInterceptMediaKeyEvents;
|
||||
_shouldInterceptMediaKeyEvents = newSetting;
|
||||
}
|
||||
if(_tapThreadRL && oldSetting != newSetting) {
|
||||
id grab = [self grab];
|
||||
[grab pauseTapOnTapThread:newSetting];
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO];
|
||||
CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma mark -
|
||||
#pragma mark Event tap callbacks
|
||||
|
||||
// Note: method called on background thread
|
||||
|
||||
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
SPMediaKeyTap *self = refcon;
|
||||
|
||||
if(type == kCGEventTapDisabledByTimeout) {
|
||||
NSLog(@"Media key event tap was disabled by timeout");
|
||||
CGEventTapEnable(self->_eventPort, TRUE);
|
||||
return event;
|
||||
} else if(type == kCGEventTapDisabledByUserInput) {
|
||||
// Was disabled manually by -[pauseTapOnTapThread]
|
||||
return event;
|
||||
}
|
||||
NSEvent *nsEvent = nil;
|
||||
@try {
|
||||
nsEvent = [NSEvent eventWithCGEvent:event];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
||||
assert(0);
|
||||
return event;
|
||||
}
|
||||
|
||||
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
|
||||
return event;
|
||||
|
||||
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
|
||||
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT)
|
||||
return event;
|
||||
|
||||
if (![self shouldInterceptMediaKeyEvents])
|
||||
return event;
|
||||
|
||||
[nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent:
|
||||
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
|
||||
[pool drain];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// event will have been retained in the other thread
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event {
|
||||
[event autorelease];
|
||||
|
||||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
||||
}
|
||||
|
||||
|
||||
-(void)eventTapThread;
|
||||
{
|
||||
_tapThreadRL = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
#pragma mark Task switching callbacks
|
||||
|
||||
NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
|
||||
NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
|
||||
|
||||
|
||||
|
||||
-(void)mediaKeyAppListChanged;
|
||||
{
|
||||
if([_mediaKeyAppList count] == 0) return;
|
||||
|
||||
/*NSLog(@"--");
|
||||
int i = 0;
|
||||
for (NSValue *psnv in _mediaKeyAppList) {
|
||||
ProcessSerialNumber psn; [psnv getValue:&psn];
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
NSLog(@"%d: %@", i++, bundleIdentifier);
|
||||
}*/
|
||||
|
||||
ProcessSerialNumber mySerial, topSerial;
|
||||
GetCurrentProcess(&mySerial);
|
||||
[[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial];
|
||||
|
||||
Boolean same;
|
||||
OSErr err = SameProcess(&mySerial, &topSerial, &same);
|
||||
[self setShouldInterceptMediaKeyEvents:(err == noErr && same)];
|
||||
|
||||
}
|
||||
-(void)appIsNowFrontmost:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
|
||||
NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey];
|
||||
if(![whitelistIdentifiers containsObject:bundleIdentifier]) return;
|
||||
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[_mediaKeyAppList insertObject:psnv atIndex:0];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
-(void)appTerminated:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber newSerial;
|
||||
GetFrontProcess(&newSerial);
|
||||
|
||||
[self appIsNowFrontmost:newSerial];
|
||||
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber deadPSN;
|
||||
|
||||
GetEventParameter(
|
||||
evt,
|
||||
kEventParamProcessID,
|
||||
typeProcessSerialNumber,
|
||||
NULL,
|
||||
sizeof(deadPSN),
|
||||
NULL,
|
||||
&deadPSN
|
||||
);
|
||||
|
||||
|
||||
[self appTerminated:deadPSN];
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
@end
|
8
external/plistparser/CMakeLists.txt
vendored
Normal file
8
external/plistparser/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
include_directories(.)
|
||||
|
||||
add_library(plistparser STATIC
|
||||
plistparser.cpp
|
||||
plistparser.h
|
||||
plistserializer.cpp
|
||||
plistserializer.h
|
||||
)
|
22
external/plistparser/LICENSE
vendored
Normal file
22
external/plistparser/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Gist: this is an MIT license. Act accordingly (basically, do whatever you want).
|
||||
It would be nice to get an email from you if you use this, but if not that's also cool.
|
||||
|
||||
Copyright (c) 2010 Reilly Watson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
98
external/plistparser/plistparser.cpp
vendored
Normal file
98
external/plistparser/plistparser.cpp
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
// Own includes
|
||||
#include "plistparser.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QDomNode>
|
||||
#include <QDomDocument>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
QVariant PListParser::parsePList(QIODevice *device) {
|
||||
QVariantMap result;
|
||||
QDomDocument doc;
|
||||
QString errorMessage;
|
||||
int errorLine;
|
||||
int errorColumn;
|
||||
bool success = doc.setContent(device, false, &errorMessage, &errorLine, &errorColumn);
|
||||
if (!success) {
|
||||
qDebug() << "PListParser Warning: Could not parse PList file!";
|
||||
qDebug() << "Error message: " << errorMessage;
|
||||
qDebug() << "Error line: " << errorLine;
|
||||
qDebug() << "Error column: " << errorColumn;
|
||||
return result;
|
||||
}
|
||||
QDomElement root = doc.documentElement();
|
||||
if (root.attribute(QStringLiteral("version"), QStringLiteral("1.0")) != QLatin1String("1.0")) {
|
||||
qDebug() << "PListParser Warning: plist is using an unknown format version, parsing might fail unexpectedly";
|
||||
}
|
||||
return parseElement(root.firstChild().toElement());
|
||||
}
|
||||
|
||||
QVariant PListParser::parseElement(const QDomElement &e) {
|
||||
QString tagName = e.tagName();
|
||||
QVariant result;
|
||||
if (tagName == QLatin1String("dict")) {
|
||||
result = parseDictElement(e);
|
||||
}
|
||||
else if (tagName == QLatin1String("array")) {
|
||||
result = parseArrayElement(e);
|
||||
}
|
||||
else if (tagName == QLatin1String("string")) {
|
||||
result = e.text();
|
||||
}
|
||||
else if (tagName == QLatin1String("data")) {
|
||||
result = QByteArray::fromBase64(e.text().toUtf8());
|
||||
}
|
||||
else if (tagName == QLatin1String("integer")) {
|
||||
result = e.text().toInt();
|
||||
}
|
||||
else if (tagName == QLatin1String("real")) {
|
||||
result = e.text().toFloat();
|
||||
}
|
||||
else if (tagName == QLatin1String("true")) {
|
||||
result = true;
|
||||
}
|
||||
else if (tagName == QLatin1String("false")) {
|
||||
result = false;
|
||||
}
|
||||
else if (tagName == QLatin1String("date")) {
|
||||
result = QDateTime::fromString(e.text(), Qt::ISODate);
|
||||
}
|
||||
else {
|
||||
qDebug() << "PListParser Warning: Invalid tag found: " << e.tagName() << e.text();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantList PListParser::parseArrayElement(const QDomElement& element) {
|
||||
QVariantList result;
|
||||
QDomNodeList children = element.childNodes();
|
||||
for (int i = 0; i < children.count(); i++) {
|
||||
QDomNode child = children.at(i);
|
||||
QDomElement e = child.toElement();
|
||||
if (!e.isNull()) {
|
||||
result.append(parseElement(e));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap PListParser::parseDictElement(const QDomElement& element) {
|
||||
QVariantMap result;
|
||||
QDomNodeList children = element.childNodes();
|
||||
QString currentKey;
|
||||
for (int i = 0; i < children.count(); i++) {
|
||||
QDomNode child = children.at(i);
|
||||
QDomElement e = child.toElement();
|
||||
if (!e.isNull()) {
|
||||
QString tagName = e.tagName();
|
||||
if (tagName == QLatin1String("key")) {
|
||||
currentKey = e.text();
|
||||
}
|
||||
else if (!currentKey.isEmpty()) {
|
||||
result[currentKey] = parseElement(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
18
external/plistparser/plistparser.h
vendored
Normal file
18
external/plistparser/plistparser.h
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
// Qt includes
|
||||
#include <QIODevice>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QVariantMap>
|
||||
#include <QDomElement>
|
||||
|
||||
class PListParser {
|
||||
public:
|
||||
static QVariant parsePList(QIODevice *device);
|
||||
private:
|
||||
static QVariant parseElement(const QDomElement &e);
|
||||
static QVariantList parseArrayElement(const QDomElement& node);
|
||||
static QVariantMap parseDictElement(const QDomElement& element);
|
||||
};
|
||||
|
83
external/plistparser/plistserializer.cpp
vendored
Normal file
83
external/plistparser/plistserializer.cpp
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Own includes
|
||||
#include "plistserializer.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QDomElement>
|
||||
#include <QDomDocument>
|
||||
#include <QDate>
|
||||
#include <QDateTime>
|
||||
|
||||
static QDomElement textElement(QDomDocument& doc, const char *tagName, QString contents) {
|
||||
QDomElement tag = doc.createElement(QString::fromLatin1(tagName));
|
||||
tag.appendChild(doc.createTextNode(contents));
|
||||
return tag;
|
||||
}
|
||||
|
||||
static QDomElement serializePrimitive(QDomDocument &doc, const QVariant &variant) {
|
||||
QDomElement result;
|
||||
if (variant.type() == QVariant::Bool) {
|
||||
result = doc.createElement(variant.toBool() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||
}
|
||||
else if (variant.type() == QVariant::Date) {
|
||||
result = textElement(doc, "date", variant.toDate().toString(Qt::ISODate));
|
||||
}
|
||||
else if (variant.type() == QVariant::DateTime) {
|
||||
result = textElement(doc, "date", variant.toDateTime().toString(Qt::ISODate));
|
||||
}
|
||||
else if (variant.type() == QVariant::ByteArray) {
|
||||
result = textElement(doc, "data", QString::fromLatin1(variant.toByteArray().toBase64()));
|
||||
}
|
||||
else if (variant.type() == QVariant::String) {
|
||||
result = textElement(doc, "string", variant.toString());
|
||||
}
|
||||
else if (variant.type() == QVariant::Int) {
|
||||
result = textElement(doc, "integer", QString::number(variant.toInt()));
|
||||
}
|
||||
else if (variant.canConvert(QVariant::Double)) {
|
||||
QString num;
|
||||
num.setNum(variant.toDouble());
|
||||
result = textElement(doc, "real", num);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QDomElement PListSerializer::serializeElement(QDomDocument &doc, const QVariant &variant) {
|
||||
if (variant.type() == QVariant::Map) {
|
||||
return serializeMap(doc, variant.toMap());
|
||||
}
|
||||
else if (variant.type() == QVariant::List) {
|
||||
return serializeList(doc, variant.toList());
|
||||
}
|
||||
else {
|
||||
return serializePrimitive(doc, variant);
|
||||
}
|
||||
}
|
||||
|
||||
QDomElement PListSerializer::serializeList(QDomDocument &doc, const QVariantList &list) {
|
||||
QDomElement element = doc.createElement(QStringLiteral("array"));
|
||||
foreach(QVariant item, list) {
|
||||
element.appendChild(serializeElement(doc, item));
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
QDomElement PListSerializer::serializeMap(QDomDocument &doc, const QVariantMap &map) {
|
||||
QDomElement element = doc.createElement(QStringLiteral("dict"));
|
||||
QList<QString> keys = map.keys();
|
||||
foreach(QString key, keys) {
|
||||
QDomElement keyElement = textElement(doc, "key", key);
|
||||
element.appendChild(keyElement);
|
||||
element.appendChild(serializeElement(doc, map[key]));
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
QString PListSerializer::toPList(const QVariant &variant) {
|
||||
QDomDocument document(QStringLiteral("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\""));
|
||||
document.appendChild(document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"")));
|
||||
QDomElement plist = document.createElement(QStringLiteral("plist"));
|
||||
plist.setAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
|
||||
document.appendChild(plist);
|
||||
plist.appendChild(serializeElement(document, variant));
|
||||
return document.toString();
|
||||
}
|
20
external/plistparser/plistserializer.h
vendored
Normal file
20
external/plistparser/plistserializer.h
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
// Qt includes
|
||||
#include <QIODevice>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QVariantMap>
|
||||
#include <QDomElement>
|
||||
#include <QDomDocument>
|
||||
#include <QString>
|
||||
|
||||
class PListSerializer {
|
||||
public:
|
||||
static QString toPList(const QVariant &variant);
|
||||
private:
|
||||
static QDomElement serializeElement(QDomDocument &doc, const QVariant &variant);
|
||||
static QDomElement serializeMap(QDomDocument &doc, const QVariantMap &map);
|
||||
static QDomElement serializeList(QDomDocument &doc, const QVariantList &list);
|
||||
};
|
||||
|
30
external/qhttpserver/.gitignore
vendored
Executable file
30
external/qhttpserver/.gitignore
vendored
Executable file
@ -0,0 +1,30 @@
|
||||
# Folders
|
||||
build
|
||||
lib
|
||||
|
||||
# Generated
|
||||
Makefile
|
||||
*.o
|
||||
moc_*
|
||||
|
||||
# Docs
|
||||
docs/html
|
||||
|
||||
# Build folders
|
||||
*/debug
|
||||
*/release
|
||||
*/*/debug
|
||||
*/*/release
|
||||
|
||||
# Visual studio
|
||||
*.suo
|
||||
*.ncb
|
||||
*.user
|
||||
*.pdb
|
||||
*.idb
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.lib
|
||||
*.sln
|
||||
*.rc
|
8
external/qhttpserver/CMakeLists.txt
vendored
Normal file
8
external/qhttpserver/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
aux_source_directory(src HTTP_SRC)
|
||||
|
||||
include_directories(http-parser)
|
||||
add_library(http-parser STATIC http-parser/http_parser.c)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
add_library(qhttpserver STATIC ${HTTP_SRC} ${PARSER_SRC})
|
||||
target_link_libraries(qhttpserver http-parser)
|
19
external/qhttpserver/LICENSE
vendored
Executable file
19
external/qhttpserver/LICENSE
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
Copyright (C) 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
72
external/qhttpserver/README.md
vendored
Executable file
72
external/qhttpserver/README.md
vendored
Executable file
@ -0,0 +1,72 @@
|
||||
QHttpServer
|
||||
===========
|
||||
|
||||
A Qt HTTP Server - because hard-core programmers write web-apps in C++ :)
|
||||
|
||||
It uses Joyent's [HTTP Parser](http://github.com/joyent/http-parser) and is asynchronous and does not require any inheritance.
|
||||
|
||||
QHttpServer is available under the MIT License.
|
||||
|
||||
**NOTE: QHttpServer is NOT fully HTTP compliant right now! DO NOT use it for
|
||||
anything complex**
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Requires Qt 4 or Qt 5.
|
||||
|
||||
qmake && make && su -c 'make install'
|
||||
|
||||
To link to your projects put this in your project's qmake project file
|
||||
|
||||
LIBS += -lqhttpserver
|
||||
|
||||
By default, the installation prefix is /usr/local. To change that to /usr,
|
||||
for example, run:
|
||||
|
||||
qmake -r PREFIX=/usr
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Include the headers
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
Create a server, and connect to the signal for new requests
|
||||
|
||||
QHttpServer *server = new QHttpServer;
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
handler, SLOT(handle(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
// let's go
|
||||
server->listen(8080);
|
||||
|
||||
In the handler, you may dispatch on routes or do whatever other things
|
||||
you want. See the API documentation for what information
|
||||
is provided about the request via the QHttpRequest object.
|
||||
|
||||
To send data back to the browser and end the request:
|
||||
|
||||
void Handler::handle(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
resp->setHeader("Content-Length", 11);
|
||||
resp->writeHead(200); // everything is OK
|
||||
resp->write("Hello World");
|
||||
resp->end();
|
||||
}
|
||||
|
||||
The server and request/response objects emit various signals
|
||||
and have guarantees about memory management. See the API documentation for
|
||||
these.
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
Feel free to file issues, branch and send pull requests. If you plan to work on a major feature (say WebSocket support), please run it by me first by filing an issue! qhttpserver has a narrow scope and API and I'd like to keep it that way, so a thousand line patch that implements the kitchen sink is unlikely to be accepted.
|
||||
|
||||
- Nikhil Marathe (maintainer)
|
||||
|
||||
Everybody who has ever contributed shows up in [Contributors](https://github.com/nikhilm/qhttpserver/graphs/contributors).
|
7
external/qhttpserver/TODO
vendored
Executable file
7
external/qhttpserver/TODO
vendored
Executable file
@ -0,0 +1,7 @@
|
||||
* Expect & Continue stuff
|
||||
* Chunked encoding support
|
||||
* Only copy over public headers etc.
|
||||
* connection object should connect to QHttpResponse::destroyed()
|
||||
and stop pushing data into it or whatever if the object is destroyed.
|
||||
* response object should keep track of emitting done() and not accept writes after that
|
||||
* handle encoding in response write and end
|
2314
external/qhttpserver/docs/Doxyfile
vendored
Executable file
2314
external/qhttpserver/docs/Doxyfile
vendored
Executable file
File diff suppressed because it is too large
Load Diff
10
external/qhttpserver/docs/pages/examples.dox
vendored
Executable file
10
external/qhttpserver/docs/pages/examples.dox
vendored
Executable file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
|
||||
@example helloworld/helloworld.cpp
|
||||
@example helloworld/helloworld.h
|
||||
@example greeting/greeting.cpp
|
||||
@example greeting/greeting.h
|
||||
@example bodydata/bodydata.cpp
|
||||
@example bodydata/bodydata.h
|
||||
|
||||
*/
|
105
external/qhttpserver/docs/pages/main-page.dox
vendored
Executable file
105
external/qhttpserver/docs/pages/main-page.dox
vendored
Executable file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
|
||||
@mainpage %QHttpServer Documentation
|
||||
|
||||
@section introduction Introduction
|
||||
|
||||
%QHttpServer is a easy to use, fast and light-weight
|
||||
HTTP Server suitable for C++ web applications backed
|
||||
by Qt. Since C++ web applications are pretty uncommon
|
||||
the market for this project is pretty low.
|
||||
|
||||
But integrating this with a module like QtScript and using it to write
|
||||
JavaScript web applications is a tempting possibility, and something that
|
||||
I demonstrated at <a href="http://conf.kde.in">conf.kde.in 2011</a>. The slides
|
||||
can be found on <a href="https://github.com/nikhilm/confkdein11">Github</a>.
|
||||
|
||||
%QHttpServer uses a signal-slots based mechanism
|
||||
for all communication, so no inheritance is required.
|
||||
It tries to be as asynchronous as possible, to the
|
||||
extent that request body data is also delivered as and
|
||||
when it is received over the socket via signals. This
|
||||
kind of programming may take some getting used to.
|
||||
|
||||
%QHttpServer is backed by <a href="http://github.com/ry/http-parser">Ryan
|
||||
Dahl's secure and fast http parser</a> which makes it streaming
|
||||
till the lowest level.
|
||||
|
||||
@section usage Usage
|
||||
|
||||
Using %QHttpServer is very simple. Simply create a QHttpServer,
|
||||
connect a slot to the newRequest() signal and use the request and
|
||||
response objects.
|
||||
See the QHttpServer class documentation for an example.
|
||||
|
||||
@section memorymanagement Memory Management
|
||||
|
||||
The QHttpRequest and QHttpResponse deletion policies
|
||||
are such.
|
||||
|
||||
QHttpRequest is <b>never</b> deleted by %QHttpServer.
|
||||
Since it is not possible to determine till what point the application
|
||||
may want access to its data, it is up to the application to delete it.
|
||||
A recommended way to handle this is to create a new responder object for
|
||||
every request and to delete the request in that object's destructor. The
|
||||
object itself can be deleted by connecting to QHttpResponse's done()
|
||||
slot as explained below.
|
||||
|
||||
You should <b>not</b> delete the QHttpRequest object until it
|
||||
has emitted an QHttpRequest::end() signal.
|
||||
|
||||
QHttpResponse queues itself up for auto-deletion once the application
|
||||
calls its end() method. Once the data has been flushed to the underlying
|
||||
socket, the object will emit a QHttpResponse::done() signal before queueing itself up
|
||||
for deletion. You should <b>not</b> interact with the response
|
||||
object once it has emitted QHttpResponse::done() although actual deletion does not
|
||||
happen until QHttpResponse::destroyed() is emitted.
|
||||
QHttpResponse::done() serves as a useful way to handle memory management of the
|
||||
application itself. For example:
|
||||
|
||||
@code
|
||||
|
||||
MyApp::MyApp() : QObject()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(...)), this, SLOT(handle(...)));
|
||||
s.listen(8080);
|
||||
}
|
||||
|
||||
void MyApp::handle(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
if (request->path() == x) // Match a route
|
||||
new Responder(request, response);
|
||||
else
|
||||
new PageNotFound(request, response);
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
Responder::Responder(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
m_request = request;
|
||||
|
||||
connect(request, SIGNAL(end()), response, SLOT(end()));
|
||||
|
||||
// Once the request is complete, the response is sent.
|
||||
// When the response ends, it deletes itself
|
||||
// the Responder object connects to done()
|
||||
// which will lead to it being deleted
|
||||
// and this will delete the request.
|
||||
// So all 3 are properly deleted.
|
||||
connect(response, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||
|
||||
response->writeHead(200);
|
||||
response->write("Quitting soon");
|
||||
}
|
||||
|
||||
Responder::~Responder()
|
||||
{
|
||||
delete m_request;
|
||||
m_request = 0;
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
76
external/qhttpserver/examples/bodydata/bodydata.cpp
vendored
Executable file
76
external/qhttpserver/examples/bodydata/bodydata.cpp
vendored
Executable file
@ -0,0 +1,76 @@
|
||||
#include "bodydata.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// BodyData
|
||||
|
||||
BodyData::BodyData()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void BodyData::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
new Responder(req, resp);
|
||||
}
|
||||
|
||||
/// Responder
|
||||
|
||||
Responder::Responder(QHttpRequest *req, QHttpResponse *resp)
|
||||
: m_req(req)
|
||||
, m_resp(resp)
|
||||
{
|
||||
QRegExp exp("^/user/([a-z]+$)");
|
||||
if (exp.indexIn(req->path()) == -1)
|
||||
{
|
||||
resp->writeHead(403);
|
||||
resp->end(QByteArray("You aren't allowed here!"));
|
||||
/// @todo There should be a way to tell request to stop streaming data
|
||||
return;
|
||||
}
|
||||
|
||||
resp->setHeader("Content-Type", "text/html");
|
||||
resp->writeHead(200);
|
||||
|
||||
QString name = exp.capturedTexts()[1];
|
||||
QString bodyStart = tr("<html><head><title>BodyData App</title></head><body><h1>Hello %1!</h1><p>").arg(name);
|
||||
resp->write(bodyStart.toUtf8());
|
||||
|
||||
connect(req, SIGNAL(data(const QByteArray&)), this, SLOT(accumulate(const QByteArray&)));
|
||||
connect(req, SIGNAL(end()), this, SLOT(reply()));
|
||||
connect(m_resp, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
Responder::~Responder()
|
||||
{
|
||||
}
|
||||
|
||||
void Responder::accumulate(const QByteArray &data)
|
||||
{
|
||||
m_resp->write(data);
|
||||
}
|
||||
|
||||
void Responder::reply()
|
||||
{
|
||||
m_resp->end(QByteArray("</p></body></html>"));
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
BodyData bodydata;
|
||||
app.exec();
|
||||
}
|
39
external/qhttpserver/examples/bodydata/bodydata.h
vendored
Executable file
39
external/qhttpserver/examples/bodydata/bodydata.h
vendored
Executable file
@ -0,0 +1,39 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
/// BodyData
|
||||
|
||||
class BodyData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BodyData();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
||||
|
||||
/// Responder
|
||||
|
||||
class Responder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Responder(QHttpRequest *req, QHttpResponse *resp);
|
||||
~Responder();
|
||||
|
||||
signals:
|
||||
void done();
|
||||
|
||||
private slots:
|
||||
void accumulate(const QByteArray &data);
|
||||
void reply();
|
||||
|
||||
private:
|
||||
QScopedPointer<QHttpRequest> m_req;
|
||||
QHttpResponse *m_resp;
|
||||
};
|
19
external/qhttpserver/examples/bodydata/bodydata.pro
vendored
Executable file
19
external/qhttpserver/examples/bodydata/bodydata.pro
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
TARGET = bodydata
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = bodydata.cpp
|
||||
HEADERS = bodydata.h
|
5
external/qhttpserver/examples/examples.pro
vendored
Executable file
5
external/qhttpserver/examples/examples.pro
vendored
Executable file
@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
helloworld\
|
||||
greeting\
|
||||
bodydata\
|
48
external/qhttpserver/examples/greeting/greeting.cpp
vendored
Executable file
48
external/qhttpserver/examples/greeting/greeting.cpp
vendored
Executable file
@ -0,0 +1,48 @@
|
||||
#include "greeting.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// Greeting
|
||||
|
||||
Greeting::Greeting()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void Greeting::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
QRegExp exp("^/user/([a-z]+)$");
|
||||
if( exp.indexIn(req->path()) != -1 )
|
||||
{
|
||||
resp->setHeader("Content-Type", "text/html");
|
||||
resp->writeHead(200);
|
||||
|
||||
QString name = exp.capturedTexts()[1];
|
||||
QString body = tr("<html><head><title>Greeting App</title></head><body><h1>Hello %1!</h1></body></html>");
|
||||
resp->end(body.arg(name).toUtf8());
|
||||
}
|
||||
else
|
||||
{
|
||||
resp->writeHead(403);
|
||||
resp->end(QByteArray("You aren't allowed here!"));
|
||||
}
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
Greeting greeting;
|
||||
app.exec();
|
||||
}
|
16
external/qhttpserver/examples/greeting/greeting.h
vendored
Executable file
16
external/qhttpserver/examples/greeting/greeting.h
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// Greeting
|
||||
|
||||
class Greeting : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Greeting();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
19
external/qhttpserver/examples/greeting/greeting.pro
vendored
Executable file
19
external/qhttpserver/examples/greeting/greeting.pro
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
TARGET = greeting
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = greeting.cpp
|
||||
HEADERS = greeting.h
|
37
external/qhttpserver/examples/helloworld/helloworld.cpp
vendored
Executable file
37
external/qhttpserver/examples/helloworld/helloworld.cpp
vendored
Executable file
@ -0,0 +1,37 @@
|
||||
#include "helloworld.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// HelloWorld
|
||||
|
||||
HelloWorld::HelloWorld()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void HelloWorld::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
Q_UNUSED(req);
|
||||
|
||||
QByteArray body = "Hello World";
|
||||
resp->setHeader("Content-Length", QString::number(body.size()));
|
||||
resp->writeHead(200);
|
||||
resp->end(body);
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
HelloWorld hello;
|
||||
app.exec();
|
||||
}
|
16
external/qhttpserver/examples/helloworld/helloworld.h
vendored
Executable file
16
external/qhttpserver/examples/helloworld/helloworld.h
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// HelloWorld
|
||||
|
||||
class HelloWorld : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HelloWorld();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
19
external/qhttpserver/examples/helloworld/helloworld.pro
vendored
Executable file
19
external/qhttpserver/examples/helloworld/helloworld.pro
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
TARGET = helloworld
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = helloworld.cpp
|
||||
HEADERS = helloworld.h
|
28
external/qhttpserver/http-parser/.gitignore
vendored
Executable file
28
external/qhttpserver/http-parser/.gitignore
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
/out/
|
||||
core
|
||||
tags
|
||||
*.o
|
||||
test
|
||||
test_g
|
||||
test_fast
|
||||
bench
|
||||
url_parser
|
||||
parsertrace
|
||||
parsertrace_g
|
||||
*.mk
|
||||
*.Makefile
|
||||
*.so.*
|
||||
*.a
|
||||
|
||||
|
||||
# Visual Studio uglies
|
||||
*.suo
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
*.opensdf
|
||||
*.ncrunchsolution*
|
||||
*.sdf
|
||||
*.vsp
|
||||
*.psess
|
8
external/qhttpserver/http-parser/.mailmap
vendored
Normal file
8
external/qhttpserver/http-parser/.mailmap
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# update AUTHORS with:
|
||||
# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS
|
||||
Ryan Dahl <ry@tinyclouds.org>
|
||||
Salman Haq <salman.haq@asti-usa.com>
|
||||
Simon Zimmermann <simonz05@gmail.com>
|
||||
Thomas LE ROUX <thomas@november-eleven.fr> LE ROUX Thomas <thomas@procheo.fr>
|
||||
Thomas LE ROUX <thomas@november-eleven.fr> Thomas LE ROUX <thomas@procheo.fr>
|
||||
Fedor Indutny <fedor@indutny.com>
|
13
external/qhttpserver/http-parser/.travis.yml
vendored
Normal file
13
external/qhttpserver/http-parser/.travis.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
script:
|
||||
- "make"
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
- "irc.freenode.net#node-ci"
|
67
external/qhttpserver/http-parser/AUTHORS
vendored
Executable file
67
external/qhttpserver/http-parser/AUTHORS
vendored
Executable file
@ -0,0 +1,67 @@
|
||||
# Authors ordered by first contribution.
|
||||
Ryan Dahl <ry@tinyclouds.org>
|
||||
Jeremy Hinegardner <jeremy@hinegardner.org>
|
||||
Sergey Shepelev <temotor@gmail.com>
|
||||
Joe Damato <ice799@gmail.com>
|
||||
tomika <tomika_nospam@freemail.hu>
|
||||
Phoenix Sol <phoenix@burninglabs.com>
|
||||
Cliff Frey <cliff@meraki.com>
|
||||
Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
|
||||
Santiago Gala <sgala@apache.org>
|
||||
Tim Becker <tim.becker@syngenio.de>
|
||||
Jeff Terrace <jterrace@gmail.com>
|
||||
Ben Noordhuis <info@bnoordhuis.nl>
|
||||
Nathan Rajlich <nathan@tootallnate.net>
|
||||
Mark Nottingham <mnot@mnot.net>
|
||||
Aman Gupta <aman@tmm1.net>
|
||||
Tim Becker <tim.becker@kuriositaet.de>
|
||||
Sean Cunningham <sean.cunningham@mandiant.com>
|
||||
Peter Griess <pg@std.in>
|
||||
Salman Haq <salman.haq@asti-usa.com>
|
||||
Cliff Frey <clifffrey@gmail.com>
|
||||
Jon Kolb <jon@b0g.us>
|
||||
Fouad Mardini <f.mardini@gmail.com>
|
||||
Paul Querna <pquerna@apache.org>
|
||||
Felix Geisendörfer <felix@debuggable.com>
|
||||
koichik <koichik@improvement.jp>
|
||||
Andre Caron <andre.l.caron@gmail.com>
|
||||
Ivo Raisr <ivosh@ivosh.net>
|
||||
James McLaughlin <jamie@lacewing-project.org>
|
||||
David Gwynne <loki@animata.net>
|
||||
Thomas LE ROUX <thomas@november-eleven.fr>
|
||||
Randy Rizun <rrizun@ortivawireless.com>
|
||||
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
|
||||
Simon Zimmermann <simonz05@gmail.com>
|
||||
Erik Dubbelboer <erik@dubbelboer.com>
|
||||
Martell Malone <martellmalone@gmail.com>
|
||||
Bertrand Paquet <bpaquet@octo.com>
|
||||
BogDan Vatra <bogdan@kde.org>
|
||||
Peter Faiman <peter@thepicard.org>
|
||||
Corey Richardson <corey@octayn.net>
|
||||
Tóth Tamás <tomika_nospam@freemail.hu>
|
||||
Cam Swords <cam.swords@gmail.com>
|
||||
Chris Dickinson <christopher.s.dickinson@gmail.com>
|
||||
Uli Köhler <ukoehler@btronik.de>
|
||||
Charlie Somerville <charlie@charliesomerville.com>
|
||||
Patrik Stutz <patrik.stutz@gmail.com>
|
||||
Fedor Indutny <fedor.indutny@gmail.com>
|
||||
runner <runner.mei@gmail.com>
|
||||
Alexis Campailla <alexis@janeasystems.com>
|
||||
David Wragg <david@wragg.org>
|
||||
Vinnie Falco <vinnie.falco@gmail.com>
|
||||
Alex Butum <alexbutum@linux.com>
|
||||
Rex Feng <rexfeng@gmail.com>
|
||||
Alex Kocharin <alex@kocharin.ru>
|
||||
Mark Koopman <markmontymark@yahoo.com>
|
||||
Helge Heß <me@helgehess.eu>
|
||||
Alexis La Goutte <alexis.lagoutte@gmail.com>
|
||||
George Miroshnykov <george.miroshnykov@gmail.com>
|
||||
Maciej Małecki <me@mmalecki.com>
|
||||
Marc O'Morain <github.com@marcomorain.com>
|
||||
Jeff Pinner <jpinner@twitter.com>
|
||||
Timothy J Fontaine <tjfontaine@gmail.com>
|
||||
Akagi201 <akagi201@gmail.com>
|
||||
Romain Giraud <giraud.romain@gmail.com>
|
||||
Jay Satiro <raysatiro@yahoo.com>
|
||||
Arne Steen <Arne.Steen@gmx.de>
|
||||
Kjell Schubert <kjell.schubert@gmail.com>
|
4
external/qhttpserver/http-parser/CONTRIBUTIONS
vendored
Executable file
4
external/qhttpserver/http-parser/CONTRIBUTIONS
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
Contributors must agree to the Contributor License Agreement before patches
|
||||
can be accepted.
|
||||
|
||||
http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
|
23
external/qhttpserver/http-parser/LICENSE-MIT
vendored
Executable file
23
external/qhttpserver/http-parser/LICENSE-MIT
vendored
Executable file
@ -0,0 +1,23 @@
|
||||
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
|
||||
Igor Sysoev.
|
||||
|
||||
Additional changes are licensed under the same terms as NGINX and
|
||||
copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
183
external/qhttpserver/http-parser/README.md
vendored
Executable file
183
external/qhttpserver/http-parser/README.md
vendored
Executable file
@ -0,0 +1,183 @@
|
||||
HTTP Parser
|
||||
===========
|
||||
|
||||
[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)
|
||||
|
||||
This is a parser for HTTP messages written in C. It parses both requests and
|
||||
responses. The parser is designed to be used in performance HTTP
|
||||
applications. It does not make any syscalls nor allocations, it does not
|
||||
buffer data, it can be interrupted at anytime. Depending on your
|
||||
architecture, it only requires about 40 bytes of data per message
|
||||
stream (in a web server that is per connection).
|
||||
|
||||
Features:
|
||||
|
||||
* No dependencies
|
||||
* Handles persistent streams (keep-alive).
|
||||
* Decodes chunked encoding.
|
||||
* Upgrade support
|
||||
* Defends against buffer overflow attacks.
|
||||
|
||||
The parser extracts the following information from HTTP messages:
|
||||
|
||||
* Header fields and values
|
||||
* Content-Length
|
||||
* Request method
|
||||
* Response status code
|
||||
* Transfer-Encoding
|
||||
* HTTP version
|
||||
* Request URL
|
||||
* Message body
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
One `http_parser` object is used per TCP connection. Initialize the struct
|
||||
using `http_parser_init()` and set the callbacks. That might look something
|
||||
like this for a request parser:
|
||||
```c
|
||||
http_parser_settings settings;
|
||||
settings.on_url = my_url_callback;
|
||||
settings.on_header_field = my_header_field_callback;
|
||||
/* ... */
|
||||
|
||||
http_parser *parser = malloc(sizeof(http_parser));
|
||||
http_parser_init(parser, HTTP_REQUEST);
|
||||
parser->data = my_socket;
|
||||
```
|
||||
|
||||
When data is received on the socket execute the parser and check for errors.
|
||||
|
||||
```c
|
||||
size_t len = 80*1024, nparsed;
|
||||
char buf[len];
|
||||
ssize_t recved;
|
||||
|
||||
recved = recv(fd, buf, len, 0);
|
||||
|
||||
if (recved < 0) {
|
||||
/* Handle error. */
|
||||
}
|
||||
|
||||
/* Start up / continue the parser.
|
||||
* Note we pass recved==0 to signal that EOF has been received.
|
||||
*/
|
||||
nparsed = http_parser_execute(parser, &settings, buf, recved);
|
||||
|
||||
if (parser->upgrade) {
|
||||
/* handle new protocol */
|
||||
} else if (nparsed != recved) {
|
||||
/* Handle error. Usually just close the connection. */
|
||||
}
|
||||
```
|
||||
|
||||
HTTP needs to know where the end of the stream is. For example, sometimes
|
||||
servers send responses without Content-Length and expect the client to
|
||||
consume input (for the body) until EOF. To tell http_parser about EOF, give
|
||||
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
|
||||
can still be encountered during an EOF, so one must still be prepared
|
||||
to receive them.
|
||||
|
||||
Scalar valued message information such as `status_code`, `method`, and the
|
||||
HTTP version are stored in the parser structure. This data is only
|
||||
temporally stored in `http_parser` and gets reset on each new message. If
|
||||
this information is needed later, copy it out of the structure during the
|
||||
`headers_complete` callback.
|
||||
|
||||
The parser decodes the transfer-encoding for both requests and responses
|
||||
transparently. That is, a chunked encoding is decoded before being sent to
|
||||
the on_body callback.
|
||||
|
||||
|
||||
The Special Problem of Upgrade
|
||||
------------------------------
|
||||
|
||||
HTTP supports upgrading the connection to a different protocol. An
|
||||
increasingly common example of this is the Web Socket protocol which sends
|
||||
a request like
|
||||
|
||||
GET /demo HTTP/1.1
|
||||
Upgrade: WebSocket
|
||||
Connection: Upgrade
|
||||
Host: example.com
|
||||
Origin: http://example.com
|
||||
WebSocket-Protocol: sample
|
||||
|
||||
followed by non-HTTP data.
|
||||
|
||||
(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
|
||||
information the Web Socket protocol.)
|
||||
|
||||
To support this, the parser will treat this as a normal HTTP message without a
|
||||
body, issuing both on_headers_complete and on_message_complete callbacks. However
|
||||
http_parser_execute() will stop parsing at the end of the headers and return.
|
||||
|
||||
The user is expected to check if `parser->upgrade` has been set to 1 after
|
||||
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
|
||||
offset by the return value of `http_parser_execute()`.
|
||||
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
During the `http_parser_execute()` call, the callbacks set in
|
||||
`http_parser_settings` will be executed. The parser maintains state and
|
||||
never looks behind, so buffering the data is not necessary. If you need to
|
||||
save certain data for later usage, you can do that from the callbacks.
|
||||
|
||||
There are two types of callbacks:
|
||||
|
||||
* notification `typedef int (*http_cb) (http_parser*);`
|
||||
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
|
||||
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
|
||||
Callbacks: (requests only) on_url,
|
||||
(common) on_header_field, on_header_value, on_body;
|
||||
|
||||
Callbacks must return 0 on success. Returning a non-zero value indicates
|
||||
error to the parser, making it exit immediately.
|
||||
|
||||
In case you parse HTTP message in chunks (i.e. `read()` request line
|
||||
from socket, parse, read half headers, parse, etc) your data callbacks
|
||||
may be called more than once. Http-parser guarantees that data pointer is only
|
||||
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
||||
buffer to avoid copying memory around if this fits your application.
|
||||
|
||||
Reading headers may be a tricky task if you read/parse headers partially.
|
||||
Basically, you need to remember whether last header callback was field or value
|
||||
and apply the following logic:
|
||||
|
||||
(on_header_field and on_header_value shortened to on_h_*)
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| State (prev. callback) | Callback | Description/action |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
|
||||
| | | into it |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| value | on_h_field | New header started. |
|
||||
| | | Copy current name,value buffers to headers |
|
||||
| | | list and allocate new buffer for new name |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| field | on_h_field | Previous name continues. Reallocate name |
|
||||
| | | buffer and append callback data to it |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| field | on_h_value | Value for current header started. Allocate |
|
||||
| | | new buffer and copy callback data to it |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| value | on_h_value | Value continues. Reallocate value buffer |
|
||||
| | | and append callback data to it |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
|
||||
|
||||
Parsing URLs
|
||||
------------
|
||||
|
||||
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
|
||||
Users of this library may wish to use it to parse URLs constructed from
|
||||
consecutive `on_url` callbacks.
|
||||
|
||||
See examples of reading in headers:
|
||||
|
||||
* [partial example](http://gist.github.com/155877) in C
|
||||
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
|
||||
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript
|
111
external/qhttpserver/http-parser/bench.c
vendored
Normal file
111
external/qhttpserver/http-parser/bench.c
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/* Copyright Fedor Indutny. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#include "http_parser.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static const char data[] =
|
||||
"POST /joyent/http-parser HTTP/1.1\r\n"
|
||||
"Host: github.com\r\n"
|
||||
"DNT: 1\r\n"
|
||||
"Accept-Encoding: gzip, deflate, sdch\r\n"
|
||||
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
|
||||
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/39.0.2171.65 Safari/537.36\r\n"
|
||||
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
|
||||
"image/webp,*/*;q=0.8\r\n"
|
||||
"Referer: https://github.com/joyent/http-parser\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n";
|
||||
static const size_t data_len = sizeof(data) - 1;
|
||||
|
||||
static int on_info(http_parser* p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int on_data(http_parser* p, const char *at, size_t length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static http_parser_settings settings = {
|
||||
.on_message_begin = on_info,
|
||||
.on_headers_complete = on_info,
|
||||
.on_message_complete = on_info,
|
||||
.on_header_field = on_data,
|
||||
.on_header_value = on_data,
|
||||
.on_url = on_data,
|
||||
.on_status = on_data,
|
||||
.on_body = on_data
|
||||
};
|
||||
|
||||
int bench(int iter_count, int silent) {
|
||||
struct http_parser parser;
|
||||
int i;
|
||||
int err;
|
||||
struct timeval start;
|
||||
struct timeval end;
|
||||
float rps;
|
||||
|
||||
if (!silent) {
|
||||
err = gettimeofday(&start, NULL);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < iter_count; i++) {
|
||||
size_t parsed;
|
||||
http_parser_init(&parser, HTTP_REQUEST);
|
||||
|
||||
parsed = http_parser_execute(&parser, &settings, data, data_len);
|
||||
assert(parsed == data_len);
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
err = gettimeofday(&end, NULL);
|
||||
assert(err == 0);
|
||||
|
||||
fprintf(stdout, "Benchmark result:\n");
|
||||
|
||||
rps = (float) (end.tv_sec - start.tv_sec) +
|
||||
(end.tv_usec - start.tv_usec) * 1e-6f;
|
||||
fprintf(stdout, "Took %f seconds to run\n", rps);
|
||||
|
||||
rps = (float) iter_count / rps;
|
||||
fprintf(stdout, "%f req/sec\n", rps);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
|
||||
for (;;)
|
||||
bench(5000000, 1);
|
||||
return 0;
|
||||
} else {
|
||||
return bench(5000000, 0);
|
||||
}
|
||||
}
|
160
external/qhttpserver/http-parser/contrib/parsertrace.c
vendored
Normal file
160
external/qhttpserver/http-parser/contrib/parsertrace.c
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
|
||||
*
|
||||
* Additional changes are licensed under the same terms as NGINX and
|
||||
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Dump what the parser finds to stdout as it happen */
|
||||
|
||||
#include "http_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int on_message_begin(http_parser* _) {
|
||||
(void)_;
|
||||
printf("\n***MESSAGE BEGIN***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_headers_complete(http_parser* _) {
|
||||
(void)_;
|
||||
printf("\n***HEADERS COMPLETE***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_message_complete(http_parser* _) {
|
||||
(void)_;
|
||||
printf("\n***MESSAGE COMPLETE***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_url(http_parser* _, const char* at, size_t length) {
|
||||
(void)_;
|
||||
printf("Url: %.*s\n", (int)length, at);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_header_field(http_parser* _, const char* at, size_t length) {
|
||||
(void)_;
|
||||
printf("Header field: %.*s\n", (int)length, at);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_header_value(http_parser* _, const char* at, size_t length) {
|
||||
(void)_;
|
||||
printf("Header value: %.*s\n", (int)length, at);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int on_body(http_parser* _, const char* at, size_t length) {
|
||||
(void)_;
|
||||
printf("Body: %.*s\n", (int)length, at);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage(const char* name) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s $type $filename\n"
|
||||
" type: -x, where x is one of {r,b,q}\n"
|
||||
" parses file as a Response, reQuest, or Both\n",
|
||||
name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
enum http_parser_type file_type;
|
||||
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
char* type = argv[1];
|
||||
if (type[0] != '-') {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
switch (type[1]) {
|
||||
/* in the case of "-", type[1] will be NUL */
|
||||
case 'r':
|
||||
file_type = HTTP_RESPONSE;
|
||||
break;
|
||||
case 'q':
|
||||
file_type = HTTP_REQUEST;
|
||||
break;
|
||||
case 'b':
|
||||
file_type = HTTP_BOTH;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
char* filename = argv[2];
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (file == NULL) {
|
||||
perror("fopen");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_length = ftell(file);
|
||||
if (file_length == -1) {
|
||||
perror("ftell");
|
||||
goto fail;
|
||||
}
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
char* data = malloc(file_length);
|
||||
if (fread(data, 1, file_length, file) != (size_t)file_length) {
|
||||
fprintf(stderr, "couldn't read entire file\n");
|
||||
free(data);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
http_parser_settings settings;
|
||||
memset(&settings, 0, sizeof(settings));
|
||||
settings.on_message_begin = on_message_begin;
|
||||
settings.on_url = on_url;
|
||||
settings.on_header_field = on_header_field;
|
||||
settings.on_header_value = on_header_value;
|
||||
settings.on_headers_complete = on_headers_complete;
|
||||
settings.on_body = on_body;
|
||||
settings.on_message_complete = on_message_complete;
|
||||
|
||||
http_parser parser;
|
||||
http_parser_init(&parser, file_type);
|
||||
size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
|
||||
free(data);
|
||||
|
||||
if (nparsed != (size_t)file_length) {
|
||||
fprintf(stderr,
|
||||
"Error: %s (%s)\n",
|
||||
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
|
||||
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
fail:
|
||||
fclose(file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
46
external/qhttpserver/http-parser/contrib/url_parser.c
vendored
Normal file
46
external/qhttpserver/http-parser/contrib/url_parser.c
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
#include "http_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
dump_url (const char *url, const struct http_parser_url *u)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
|
||||
for (i = 0; i < UF_MAX; i++) {
|
||||
if ((u->field_set & (1 << i)) == 0) {
|
||||
printf("\tfield_data[%u]: unset\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n",
|
||||
i,
|
||||
u->field_data[i].off,
|
||||
u->field_data[i].len,
|
||||
u->field_data[i].len,
|
||||
url + u->field_data[i].off);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
struct http_parser_url u;
|
||||
int len, connect, result;
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Syntax : %s connect|get url\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
len = strlen(argv[2]);
|
||||
connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||
|
||||
result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||
if (result != 0) {
|
||||
printf("Parse error : %d\n", result);
|
||||
return result;
|
||||
}
|
||||
printf("Parse ok, result : \n");
|
||||
dump_url(argv[2], &u);
|
||||
return 0;
|
||||
}
|
2429
external/qhttpserver/http-parser/http_parser.c
vendored
Executable file
2429
external/qhttpserver/http-parser/http_parser.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
111
external/qhttpserver/http-parser/http_parser.gyp
vendored
Executable file
111
external/qhttpserver/http-parser/http_parser.gyp
vendored
Executable file
@ -0,0 +1,111 @@
|
||||
# This file is used with the GYP meta build system.
|
||||
# http://code.google.com/p/gyp/
|
||||
# To build try this:
|
||||
# svn co http://gyp.googlecode.com/svn/trunk gyp
|
||||
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
|
||||
# ./out/Debug/test
|
||||
{
|
||||
'target_defaults': {
|
||||
'default_configuration': 'Debug',
|
||||
'configurations': {
|
||||
# TODO: hoist these out and put them somewhere common, because
|
||||
# RuntimeLibrary MUST MATCH across the entire project
|
||||
'Debug': {
|
||||
'defines': [ 'DEBUG', '_DEBUG' ],
|
||||
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'RuntimeLibrary': 1, # static debug
|
||||
},
|
||||
},
|
||||
},
|
||||
'Release': {
|
||||
'defines': [ 'NDEBUG' ],
|
||||
'cflags': [ '-Wall', '-Wextra', '-O3' ],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'RuntimeLibrary': 0, # static release
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
},
|
||||
'VCLibrarianTool': {
|
||||
},
|
||||
'VCLinkerTool': {
|
||||
'GenerateDebugInformation': 'true',
|
||||
},
|
||||
},
|
||||
'conditions': [
|
||||
['OS == "win"', {
|
||||
'defines': [
|
||||
'WIN32'
|
||||
],
|
||||
}]
|
||||
],
|
||||
},
|
||||
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'http_parser',
|
||||
'type': 'static_library',
|
||||
'include_dirs': [ '.' ],
|
||||
'direct_dependent_settings': {
|
||||
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
||||
'include_dirs': [ '.' ],
|
||||
},
|
||||
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
||||
'sources': [ './http_parser.c', ],
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
# Compile as C++. http_parser.c is actually C99, but C++ is
|
||||
# close enough in this case.
|
||||
'CompileAs': 2,
|
||||
},
|
||||
},
|
||||
}]
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
'target_name': 'http_parser_strict',
|
||||
'type': 'static_library',
|
||||
'include_dirs': [ '.' ],
|
||||
'direct_dependent_settings': {
|
||||
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
||||
'include_dirs': [ '.' ],
|
||||
},
|
||||
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
||||
'sources': [ './http_parser.c', ],
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
# Compile as C++. http_parser.c is actually C99, but C++ is
|
||||
# close enough in this case.
|
||||
'CompileAs': 2,
|
||||
},
|
||||
},
|
||||
}]
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
'target_name': 'test-nonstrict',
|
||||
'type': 'executable',
|
||||
'dependencies': [ 'http_parser' ],
|
||||
'sources': [ 'test.c' ]
|
||||
},
|
||||
|
||||
{
|
||||
'target_name': 'test-strict',
|
||||
'type': 'executable',
|
||||
'dependencies': [ 'http_parser_strict' ],
|
||||
'sources': [ 'test.c' ]
|
||||
}
|
||||
]
|
||||
}
|
342
external/qhttpserver/http-parser/http_parser.h
vendored
Executable file
342
external/qhttpserver/http-parser/http_parser.h
vendored
Executable file
@ -0,0 +1,342 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 5
|
||||
#define HTTP_PARSER_VERSION_PATCH 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
||||
#include <BaseTsd.h>
|
||||
#include <stddef.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
|
||||
* the effective limit on the size of the header, define the macro
|
||||
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
|
||||
*/
|
||||
#ifndef HTTP_MAX_HEADER_SIZE
|
||||
# define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be called arbitrarily
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* webdav */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
/* subversion */ \
|
||||
XX(16, REPORT, REPORT) \
|
||||
XX(17, MKACTIVITY, MKACTIVITY) \
|
||||
XX(18, CHECKOUT, CHECKOUT) \
|
||||
XX(19, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(20, MSEARCH, M-SEARCH) \
|
||||
XX(21, NOTIFY, NOTIFY) \
|
||||
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(24, PATCH, PATCH) \
|
||||
XX(25, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(26, MKCALENDAR, MKCALENDAR) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_CONNECTION_UPGRADE = 1 << 3
|
||||
, F_TRAILING = 1 << 4
|
||||
, F_UPGRADE = 1 << 5
|
||||
, F_SKIPBODY = 1 << 6
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned int state : 7; /* enum state from http_parser.c */
|
||||
unsigned int header_state : 8; /* enum header_state from http_parser.c */
|
||||
unsigned int index : 8; /* index into current matcher */
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned int status_code : 16; /* responses only */
|
||||
unsigned int method : 8; /* requests only */
|
||||
unsigned int http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned int upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_status;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Returns the library version. Bits 16-23 contain the major version number,
|
||||
* bits 8-15 the minor version number and bits 0-7 the patch level.
|
||||
* Usage example:
|
||||
*
|
||||
* unsigned long version = http_parser_version();
|
||||
* unsigned major = (version >> 16) & 255;
|
||||
* unsigned minor = (version >> 8) & 255;
|
||||
* unsigned patch = version & 255;
|
||||
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
|
||||
*/
|
||||
unsigned long http_parser_version(void);
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings);
|
||||
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
3852
external/qhttpserver/http-parser/test.c
vendored
Executable file
3852
external/qhttpserver/http-parser/test.c
vendored
Executable file
File diff suppressed because it is too large
Load Diff
44
external/qhttpserver/http-parser/url_parser.c
vendored
Executable file
44
external/qhttpserver/http-parser/url_parser.c
vendored
Executable file
@ -0,0 +1,44 @@
|
||||
#include "http_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
dump_url (const char *url, const struct http_parser_url *u)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
|
||||
for (i = 0; i < UF_MAX; i++) {
|
||||
if ((u->field_set & (1 << i)) == 0) {
|
||||
printf("\tfield_data[%u]: unset\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
|
||||
i,
|
||||
u->field_data[i].off,
|
||||
u->field_data[i].len,
|
||||
u->field_data[i].len,
|
||||
url + u->field_data[i].off);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
if (argc != 3) {
|
||||
printf("Syntax : %s connect|get url\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
struct http_parser_url u;
|
||||
int len = strlen(argv[2]);
|
||||
int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||
|
||||
int result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||
if (result != 0) {
|
||||
printf("Parse error : %d\n", result);
|
||||
return result;
|
||||
}
|
||||
printf("Parse ok, result : \n");
|
||||
dump_url(argv[2], &u);
|
||||
return 0;
|
||||
}
|
46
external/qhttpserver/konvergo-error.patch
vendored
Normal file
46
external/qhttpserver/konvergo-error.patch
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
diff --git a/external/qhttpserver/src/qhttpserver.cpp b/external/qhttpserver/src/qhttpserver.cpp
|
||||
index 07e4a85..507645d 100755
|
||||
--- a/external/qhttpserver/src/qhttpserver.cpp
|
||||
+++ b/external/qhttpserver/src/qhttpserver.cpp
|
||||
@@ -115,6 +115,7 @@ bool QHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||
if (couldBindToPort) {
|
||||
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
} else {
|
||||
+ m_errorString = m_tcpServer->errorString();
|
||||
delete m_tcpServer;
|
||||
m_tcpServer = NULL;
|
||||
}
|
||||
@@ -126,6 +127,11 @@ bool QHttpServer::listen(quint16 port)
|
||||
return listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
+QString QHttpServer::errorString() const
|
||||
+{
|
||||
+ return m_errorString;
|
||||
+}
|
||||
+
|
||||
void QHttpServer::close()
|
||||
{
|
||||
if (m_tcpServer)
|
||||
diff --git a/external/qhttpserver/src/qhttpserver.h b/external/qhttpserver/src/qhttpserver.h
|
||||
index a3cb74b..4adb391 100755
|
||||
--- a/external/qhttpserver/src/qhttpserver.h
|
||||
+++ b/external/qhttpserver/src/qhttpserver.h
|
||||
@@ -79,6 +79,9 @@ public:
|
||||
@sa listen(const QHostAddress&, quint16) */
|
||||
bool listen(quint16 port);
|
||||
|
||||
+ /// Return the last error encountered.
|
||||
+ QString errorString() const;
|
||||
+
|
||||
/// Stop the server and listening for new connections.
|
||||
void close();
|
||||
signals:
|
||||
@@ -94,6 +97,7 @@ private slots:
|
||||
|
||||
private:
|
||||
QTcpServer *m_tcpServer;
|
||||
+ QString m_errorString;
|
||||
};
|
||||
|
||||
#endif
|
3
external/qhttpserver/qhttpserver.pri
vendored
Executable file
3
external/qhttpserver/qhttpserver.pri
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
isEmpty(PREFIX):PREFIX = /usr/local
|
||||
isEmpty(LIBDIR):LIBDIR = $${PREFIX}/lib
|
||||
isEmpty(INCLUDEDIR):INCLUDEDIR = $${PREFIX}/include
|
8
external/qhttpserver/qhttpserver.pro
vendored
Executable file
8
external/qhttpserver/qhttpserver.pro
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
CONFIG += ordered
|
||||
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += src \
|
||||
examples
|
||||
|
||||
examples.depends = src
|
293
external/qhttpserver/src/qhttpconnection.cpp
vendored
Executable file
293
external/qhttpserver/src/qhttpconnection.cpp
vendored
Executable file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "http_parser.h"
|
||||
#include "qhttprequest.h"
|
||||
#include "qhttpresponse.h"
|
||||
|
||||
/// @cond nodoc
|
||||
|
||||
QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_socket(socket),
|
||||
m_parser(0),
|
||||
m_parserSettings(0),
|
||||
m_request(0),
|
||||
m_transmitLen(0),
|
||||
m_transmitPos(0)
|
||||
{
|
||||
m_parser = (http_parser *)malloc(sizeof(http_parser));
|
||||
http_parser_init(m_parser, HTTP_REQUEST);
|
||||
|
||||
m_parserSettings = new http_parser_settings();
|
||||
m_parserSettings->on_message_begin = MessageBegin;
|
||||
m_parserSettings->on_url = Url;
|
||||
m_parserSettings->on_header_field = HeaderField;
|
||||
m_parserSettings->on_header_value = HeaderValue;
|
||||
m_parserSettings->on_headers_complete = HeadersComplete;
|
||||
m_parserSettings->on_body = Body;
|
||||
m_parserSettings->on_message_complete = MessageComplete;
|
||||
|
||||
m_parser->data = this;
|
||||
|
||||
connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
|
||||
connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateWriteCount(qint64)));
|
||||
}
|
||||
|
||||
QHttpConnection::~QHttpConnection()
|
||||
{
|
||||
m_socket = 0;
|
||||
|
||||
free(m_parser);
|
||||
m_parser = 0;
|
||||
|
||||
delete m_parserSettings;
|
||||
m_parserSettings = 0;
|
||||
}
|
||||
|
||||
void QHttpConnection::socketDisconnected()
|
||||
{
|
||||
deleteLater();
|
||||
|
||||
if (m_request) {
|
||||
if (m_request->successful())
|
||||
return;
|
||||
|
||||
m_request->setSuccessful(false);
|
||||
Q_EMIT m_request->end();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::updateWriteCount(qint64 count)
|
||||
{
|
||||
Q_ASSERT(m_transmitPos + count <= m_transmitLen);
|
||||
|
||||
m_transmitPos += count;
|
||||
|
||||
if (m_transmitPos == m_transmitLen)
|
||||
{
|
||||
m_transmitLen = 0;
|
||||
m_transmitPos = 0;
|
||||
Q_EMIT allBytesWritten();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::parseRequest()
|
||||
{
|
||||
Q_ASSERT(m_parser);
|
||||
|
||||
while (m_socket->bytesAvailable()) {
|
||||
QByteArray arr = m_socket->readAll();
|
||||
http_parser_execute(m_parser, m_parserSettings, arr.constData(), arr.size());
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::write(const QByteArray &data)
|
||||
{
|
||||
m_socket->write(data);
|
||||
m_transmitLen += data.size();
|
||||
}
|
||||
|
||||
void QHttpConnection::flush()
|
||||
{
|
||||
m_socket->flush();
|
||||
}
|
||||
|
||||
void QHttpConnection::waitForBytesWritten()
|
||||
{
|
||||
m_socket->waitForBytesWritten();
|
||||
}
|
||||
|
||||
void QHttpConnection::responseDone()
|
||||
{
|
||||
QHttpResponse *response = qobject_cast<QHttpResponse *>(QObject::sender());
|
||||
if (response->m_last)
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
/* URL Utilities */
|
||||
#define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
|
||||
|
||||
#define GET_FIELD(data, info, field) \
|
||||
QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
|
||||
|
||||
#define CHECK_AND_GET_FIELD(data, info, field) \
|
||||
(HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
|
||||
|
||||
QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
|
||||
url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
|
||||
// Port is dealt with separately since it is available as an integer.
|
||||
url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
|
||||
#else
|
||||
if (HAS_URL_FIELD(urlInfo, UF_QUERY)) {
|
||||
url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off,
|
||||
urlInfo.field_data[UF_QUERY].len));
|
||||
}
|
||||
#endif
|
||||
url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
|
||||
url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
|
||||
|
||||
if (HAS_URL_FIELD(urlInfo, UF_PORT))
|
||||
url.setPort(urlInfo.port);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
#undef CHECK_AND_SET_FIELD
|
||||
#undef GET_FIELD
|
||||
#undef HAS_URL_FIELD
|
||||
|
||||
/********************
|
||||
* Static Callbacks *
|
||||
*******************/
|
||||
|
||||
int QHttpConnection::MessageBegin(http_parser *parser)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
theConnection->m_currentHeaders.clear();
|
||||
theConnection->m_currentUrl.clear();
|
||||
theConnection->m_currentUrl.reserve(128);
|
||||
|
||||
// The QHttpRequest should not be parented to this, since it's memory
|
||||
// management is the responsibility of the user of the library.
|
||||
theConnection->m_request = new QHttpRequest(theConnection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeadersComplete(http_parser *parser)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
/** set method **/
|
||||
theConnection->m_request->setMethod(static_cast<QHttpRequest::HttpMethod>(parser->method));
|
||||
|
||||
/** set version **/
|
||||
theConnection->m_request->setVersion(
|
||||
QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
|
||||
|
||||
/** get parsed url **/
|
||||
struct http_parser_url urlInfo;
|
||||
int r = http_parser_parse_url(theConnection->m_currentUrl.constData(),
|
||||
theConnection->m_currentUrl.size(),
|
||||
parser->method == HTTP_CONNECT, &urlInfo);
|
||||
Q_ASSERT(r == 0);
|
||||
Q_UNUSED(r);
|
||||
|
||||
theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo));
|
||||
|
||||
// Insert last remaining header
|
||||
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
|
||||
theConnection->m_currentHeaderValue;
|
||||
theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
|
||||
|
||||
/** set client information **/
|
||||
theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString();
|
||||
theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort();
|
||||
|
||||
QHttpResponse *response = new QHttpResponse(theConnection);
|
||||
if (parser->http_major < 1 || parser->http_minor < 1)
|
||||
response->m_keepAlive = false;
|
||||
|
||||
connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
|
||||
connect(response, SIGNAL(done()), theConnection, SLOT(responseDone()));
|
||||
|
||||
// we are good to go!
|
||||
Q_EMIT theConnection->newRequest(theConnection->m_request, response);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::MessageComplete(http_parser *parser)
|
||||
{
|
||||
// TODO: do cleanup and prepare for next request
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
theConnection->m_request->setSuccessful(true);
|
||||
Q_EMIT theConnection->m_request->end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
theConnection->m_currentUrl.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
// insert the header we parsed previously
|
||||
// into the header map
|
||||
if (!theConnection->m_currentHeaderField.isEmpty() &&
|
||||
!theConnection->m_currentHeaderValue.isEmpty()) {
|
||||
// header names are always lower-cased
|
||||
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
|
||||
theConnection->m_currentHeaderValue;
|
||||
// clear header value. this sets up a nice
|
||||
// feedback loop where the next time
|
||||
// HeaderValue is called, it can simply append
|
||||
theConnection->m_currentHeaderField = QString();
|
||||
theConnection->m_currentHeaderValue = QString();
|
||||
}
|
||||
|
||||
QString fieldSuffix = QString::fromLatin1(at, length);
|
||||
theConnection->m_currentHeaderField += fieldSuffix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
QString valueSuffix = QString::fromLatin1(at, length);
|
||||
theConnection->m_currentHeaderValue += valueSuffix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
Q_EMIT theConnection->m_request->data(QByteArray(at, length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @endcond
|
85
external/qhttpserver/src/qhttpconnection.h
vendored
Executable file
85
external/qhttpserver/src/qhttpconnection.h
vendored
Executable file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_CONNECTION
|
||||
#define Q_HTTP_CONNECTION
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// @cond nodoc
|
||||
|
||||
class QHTTPSERVER_API QHttpConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHttpConnection(QTcpSocket *socket, QObject *parent = 0);
|
||||
virtual ~QHttpConnection();
|
||||
|
||||
void write(const QByteArray &data);
|
||||
void flush();
|
||||
void waitForBytesWritten();
|
||||
|
||||
Q_SIGNALS:
|
||||
void newRequest(QHttpRequest *, QHttpResponse *);
|
||||
void allBytesWritten();
|
||||
|
||||
private Q_SLOTS:
|
||||
void parseRequest();
|
||||
void responseDone();
|
||||
void socketDisconnected();
|
||||
void updateWriteCount(qint64);
|
||||
|
||||
private:
|
||||
static int MessageBegin(http_parser *parser);
|
||||
static int Url(http_parser *parser, const char *at, size_t length);
|
||||
static int HeaderField(http_parser *parser, const char *at, size_t length);
|
||||
static int HeaderValue(http_parser *parser, const char *at, size_t length);
|
||||
static int HeadersComplete(http_parser *parser);
|
||||
static int Body(http_parser *parser, const char *at, size_t length);
|
||||
static int MessageComplete(http_parser *parser);
|
||||
|
||||
private:
|
||||
QTcpSocket *m_socket;
|
||||
http_parser *m_parser;
|
||||
http_parser_settings *m_parserSettings;
|
||||
|
||||
// Since there can only be one request at any time even with pipelining.
|
||||
QHttpRequest *m_request;
|
||||
|
||||
QByteArray m_currentUrl;
|
||||
// The ones we are reading in from the parser
|
||||
HeaderHash m_currentHeaders;
|
||||
QString m_currentHeaderField;
|
||||
QString m_currentHeaderValue;
|
||||
|
||||
// Keep track of transmit buffer status
|
||||
qint64 m_transmitLen;
|
||||
qint64 m_transmitPos;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
#endif
|
96
external/qhttpserver/src/qhttprequest.cpp
vendored
Executable file
96
external/qhttpserver/src/qhttprequest.cpp
vendored
Executable file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttprequest.h"
|
||||
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
QHttpRequest::QHttpRequest(QHttpConnection *connection, QObject *parent)
|
||||
: QObject(parent), m_connection(connection), m_url("http://localhost/"), m_success(false)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpRequest::~QHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
QString QHttpRequest::header(const QString &field)
|
||||
{
|
||||
return m_headers.value(field.toLower(), "");
|
||||
}
|
||||
|
||||
const HeaderHash &QHttpRequest::headers() const
|
||||
{
|
||||
return m_headers;
|
||||
}
|
||||
|
||||
const QString &QHttpRequest::httpVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
const QUrl &QHttpRequest::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
const QString QHttpRequest::path() const
|
||||
{
|
||||
return m_url.path();
|
||||
}
|
||||
|
||||
const QString QHttpRequest::methodString() const
|
||||
{
|
||||
return MethodToString(method());
|
||||
}
|
||||
|
||||
QHttpRequest::HttpMethod QHttpRequest::method() const
|
||||
{
|
||||
return m_method;
|
||||
}
|
||||
|
||||
const QString &QHttpRequest::remoteAddress() const
|
||||
{
|
||||
return m_remoteAddress;
|
||||
}
|
||||
|
||||
quint16 QHttpRequest::remotePort() const
|
||||
{
|
||||
return m_remotePort;
|
||||
}
|
||||
|
||||
void QHttpRequest::storeBody()
|
||||
{
|
||||
connect(this, SIGNAL(data(const QByteArray &)), this, SLOT(appendBody(const QByteArray &)),
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
QString QHttpRequest::MethodToString(HttpMethod method)
|
||||
{
|
||||
int index = staticMetaObject.indexOfEnumerator("HttpMethod");
|
||||
return staticMetaObject.enumerator(index).valueToKey(method);
|
||||
}
|
||||
|
||||
void QHttpRequest::appendBody(const QByteArray &body)
|
||||
{
|
||||
m_body.append(body);
|
||||
}
|
202
external/qhttpserver/src/qhttprequest.h
vendored
Executable file
202
external/qhttpserver/src/qhttprequest.h
vendored
Executable file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_REQUEST
|
||||
#define Q_HTTP_REQUEST
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QMetaEnum>
|
||||
#include <QMetaType>
|
||||
#include <QUrl>
|
||||
|
||||
/// The QHttpRequest class represents the header and body data sent by the client.
|
||||
/** The requests header data is available immediately. Body data is streamed as
|
||||
it comes in via the data() signal. As a consequence the application's request
|
||||
callback should ensure that it connects to the data() signal before control
|
||||
returns back to the event loop. Otherwise there is a risk of some data never
|
||||
being received by the application.
|
||||
|
||||
The class is <b>read-only</b>. */
|
||||
class QHTTPSERVER_API QHttpRequest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(HeaderHash headers READ headers)
|
||||
Q_PROPERTY(QString remoteAddress READ remoteAddress)
|
||||
Q_PROPERTY(quint16 remotePort READ remotePort)
|
||||
Q_PROPERTY(QString method READ method)
|
||||
Q_PROPERTY(QUrl url READ url)
|
||||
Q_PROPERTY(QString path READ path)
|
||||
Q_PROPERTY(QString httpVersion READ httpVersion)
|
||||
|
||||
Q_ENUMS(HttpMethod)
|
||||
|
||||
/// @cond nodoc
|
||||
friend class QHttpConnection;
|
||||
/// @endcond
|
||||
|
||||
public:
|
||||
virtual ~QHttpRequest();
|
||||
|
||||
/// Request method enumeration.
|
||||
/** @note Taken from http_parser.h -- make sure to keep synced */
|
||||
enum HttpMethod {
|
||||
HTTP_DELETE = 0,
|
||||
HTTP_GET,
|
||||
HTTP_HEAD,
|
||||
HTTP_POST,
|
||||
HTTP_PUT,
|
||||
// pathological
|
||||
HTTP_CONNECT,
|
||||
HTTP_OPTIONS,
|
||||
HTTP_TRACE,
|
||||
// webdav
|
||||
HTTP_COPY,
|
||||
HTTP_LOCK,
|
||||
HTTP_MKCOL,
|
||||
HTTP_MOVE,
|
||||
HTTP_PROPFIND,
|
||||
HTTP_PROPPATCH,
|
||||
HTTP_SEARCH,
|
||||
HTTP_UNLOCK,
|
||||
// subversion
|
||||
HTTP_REPORT,
|
||||
HTTP_MKACTIVITY,
|
||||
HTTP_CHECKOUT,
|
||||
HTTP_MERGE,
|
||||
// upnp
|
||||
HTTP_MSEARCH,
|
||||
HTTP_NOTIFY,
|
||||
HTTP_SUBSCRIBE,
|
||||
HTTP_UNSUBSCRIBE,
|
||||
// RFC-5789
|
||||
HTTP_PATCH,
|
||||
HTTP_PURGE
|
||||
};
|
||||
|
||||
/// The method used for the request.
|
||||
HttpMethod method() const;
|
||||
|
||||
/// Returns the method string for the request.
|
||||
/** @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */
|
||||
const QString methodString() const;
|
||||
|
||||
/// The complete URL for the request.
|
||||
/** This includes the path and query string.
|
||||
@sa path() */
|
||||
const QUrl &url() const;
|
||||
|
||||
/// The path portion of the query URL.
|
||||
/** @sa url() */
|
||||
const QString path() const;
|
||||
|
||||
/// The HTTP version of the request.
|
||||
/** @return A string in the form of "x.x" */
|
||||
const QString &httpVersion() const;
|
||||
|
||||
/// Return all the headers sent by the client.
|
||||
/** This returns a reference. If you want to store headers
|
||||
somewhere else, where the request may be deleted,
|
||||
make sure you store them as a copy.
|
||||
@note All header names are <b>lowercase</b>
|
||||
so that Content-Length becomes content-length etc. */
|
||||
const HeaderHash &headers() const;
|
||||
|
||||
/// Get the value of a header.
|
||||
/** Headers are stored as lowercase so the input @c field will be lowercased.
|
||||
@param field Name of the header field
|
||||
@return Value of the header or empty string if not found. */
|
||||
QString header(const QString &field);
|
||||
|
||||
/// IP Address of the client in dotted decimal format.
|
||||
const QString &remoteAddress() const;
|
||||
|
||||
/// Outbound connection port for the client.
|
||||
quint16 remotePort() const;
|
||||
|
||||
/// Request body data, empty for non POST/PUT requests.
|
||||
/** @sa storeBody() */
|
||||
const QByteArray &body() const
|
||||
{
|
||||
return m_body;
|
||||
}
|
||||
|
||||
/// If this request was successfully received.
|
||||
/** Set before end() has been emitted, stating whether
|
||||
the message was properly received. This is false
|
||||
until the receiving the full request has completed. */
|
||||
bool successful() const
|
||||
{
|
||||
return m_success;
|
||||
}
|
||||
|
||||
/// Utility function to make this request store all body data internally.
|
||||
/** If you call this when the request is received via QHttpServer::newRequest()
|
||||
the request will take care of storing the body data for you.
|
||||
Once the end() signal is emitted you can access the body data with
|
||||
the body() function.
|
||||
|
||||
If you wish to handle incoming data yourself don't call this function
|
||||
and see the data() signal.
|
||||
@sa data() body() */
|
||||
void storeBody();
|
||||
|
||||
Q_SIGNALS:
|
||||
/// Emitted when new body data has been received.
|
||||
/** @note This may be emitted zero or more times
|
||||
depending on the request type.
|
||||
@param data Received data. */
|
||||
void data(const QByteArray &data);
|
||||
|
||||
/// Emitted when the request has been fully received.
|
||||
/** @note The no more data() signals will be emitted after this. */
|
||||
void end();
|
||||
|
||||
private Q_SLOTS:
|
||||
void appendBody(const QByteArray &body);
|
||||
|
||||
private:
|
||||
QHttpRequest(QHttpConnection *connection, QObject *parent = 0);
|
||||
|
||||
static QString MethodToString(HttpMethod method);
|
||||
|
||||
void setMethod(HttpMethod method) { m_method = method; }
|
||||
void setVersion(const QString &version) { m_version = version; }
|
||||
void setUrl(const QUrl &url) { m_url = url; }
|
||||
void setHeaders(const HeaderHash headers) { m_headers = headers; }
|
||||
void setSuccessful(bool success) { m_success = success; }
|
||||
|
||||
QHttpConnection *m_connection;
|
||||
HeaderHash m_headers;
|
||||
HttpMethod m_method;
|
||||
QUrl m_url;
|
||||
QString m_version;
|
||||
QString m_remoteAddress;
|
||||
quint16 m_remotePort;
|
||||
QByteArray m_body;
|
||||
bool m_success;
|
||||
};
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user