Initial public commit of Plex Media Player

This commit is contained in:
Tobias Hieta 2015-10-13 11:05:27 +02:00
commit 3d4859f1b1
321 changed files with 33472 additions and 0 deletions

47
.clang-format Normal file
View 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
View 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
View File

41
BuildQT5-windows.md Normal file
View 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
View 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)

View 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)

View 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()

View 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)

View File

@ -0,0 +1,3 @@
if(CPACK_GENERATOR MATCHES "IFW")
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
endif()

View 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)

View 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@)

View 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()

View 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)

View 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
)

View 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
View 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)

View 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
View 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")

View 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
)

View 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
)

View 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
)

View File

View 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)

View 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()

View 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()

View 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})

View 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)

View 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)

View 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})

View 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)

View 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)

View 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)

View File

@ -0,0 +1,2 @@
set(WEB_CLIENT_VERSION a1def70)
set(WEB_CLIENT_HASH 16d2ef0a5b46fb6746aed6d3f3f7234d463f2c36)

View 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
View 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
View 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
View 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>

View 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

Binary file not shown.

View 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
View File

@ -0,0 +1,4 @@
[Paths]
Plugins = ../PlugIns
Imports = ../Resources/qml
Qml2Imports = ../Resources/qml

BIN
bundle/win/Plex.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,2 @@
set QT_OPENGL=angle
start PlexMediaPlayer.exe

1
bundle/win/iconres.rc Normal file
View File

@ -0,0 +1 @@
IDI_ICON ICON Plex.ico

7
bundle/win/qt.conf Normal file
View File

@ -0,0 +1,7 @@
[Paths]
Prefix = .
Plugins = .
Binaries = .
Imports = .
Qml2Imports = .
LibraryExecutables = .

15
bundle/win/shortcut.qs Normal file
View 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
View 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
View 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
View 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
View 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.

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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);
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

10
external/qhttpserver/docs/pages/examples.dox vendored Executable file
View 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
View 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
*/

View 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();
}

View 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;
};

View 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
View File

@ -0,0 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += \
helloworld\
greeting\
bodydata\

View 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();
}

View 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);
};

View 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

View 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();
}

View 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);
};

View 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
View 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

View 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>

View 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
View 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>

View 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
View 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
View 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
View 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);
}
}

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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

File diff suppressed because it is too large Load Diff

44
external/qhttpserver/http-parser/url_parser.c vendored Executable file
View 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;
}

View 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
View 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
View File

@ -0,0 +1,8 @@
CONFIG += ordered
TEMPLATE = subdirs
SUBDIRS += src \
examples
examples.depends = src

293
external/qhttpserver/src/qhttpconnection.cpp vendored Executable file
View 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
View 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
View 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
View 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