├── www └── upnp │ ├── presentation │ └── about.html │ ├── icons │ └── dev-icon-48x48.png │ ├── descr │ ├── root.xml │ ├── X_MS_MediaReceiverRegistrar.xml │ ├── ConnectionManager.xml │ ├── ContentDirectory.wdsl │ └── ContentDirectory.xml │ └── control │ └── ContentDirectory.php ├── .gitmodules ├── .editorconfig ├── ssdpd.workspace ├── config.h.cmake ├── nginx ├── nginx-upnp-server.conf ├── nginx-upnp-full.conf └── nginx-upnp-handler.conf ├── freebsd └── ssdpd ├── src ├── CMakeLists.txt └── ssdpd.c ├── dist.sh ├── .github ├── FUNDING.yml └── workflows │ ├── build-ubuntu-latest.yml │ └── build-macos-latest.yml ├── LICENSE ├── ssdpd.conf ├── ssdpd.conf.debug ├── readme.md ├── test.txt ├── CMakeLists.txt ├── ssdpd.project └── php └── upnp-server.conf /www/upnp/presentation/about.html: -------------------------------------------------------------------------------- 1 | By Rozhuk Ivan. 2 | 3 | rozhuk.im@gmail.com 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/liblcb"] 2 | path = src/liblcb 3 | url = https://github.com/rozhuk-im/liblcb.git 4 | -------------------------------------------------------------------------------- /www/upnp/icons/dev-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rozhuk-im/ssdpd/HEAD/www/upnp/icons/dev-icon-48x48.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: https://EditorConfig.org 2 | ### Rozhuk Ivan 2024 3 | ### https://spec.editorconfig.org/ 4 | ### 5 | 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | 11 | # Unix-style newlines with a newline ending every file 12 | [*] 13 | indent_style = tab 14 | indent_size = 4 15 | tab_width = 8 16 | end_of_line = lf 17 | charset = utf-8 18 | # spelling_language 19 | trim_trailing_whitespace = true 20 | insert_final_newline = true 21 | -------------------------------------------------------------------------------- /ssdpd.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /config.h.cmake: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H_IN__ 2 | #define __CONFIG_H_IN__ 3 | 4 | 5 | /*--------------------------------------------------------------------*/ 6 | /* Package information. */ 7 | #define PACKAGE @PROJECT_NAME@ 8 | #define VERSION PACKAGE_VERSION 9 | #define PACKAGE_NAME "@PACKAGE_NAME@" 10 | #define PACKAGE_VERSION "@PACKAGE_VERSION@" 11 | #define PACKAGE_URL "@PACKAGE_URL@" 12 | #define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" 13 | #define PACKAGE_STRING "@PACKAGE_STRING@" 14 | #define PACKAGE_DESCRIPTION "@PACKAGE_DESCRIPTION@" 15 | 16 | 17 | #endif /* __CONFIG_H_IN__ */ 18 | -------------------------------------------------------------------------------- /nginx/nginx-upnp-server.conf: -------------------------------------------------------------------------------- 1 | ### Rozhuk Ivan 2009.04 - 2017 2 | ### nginx configuration file 3 | ### PHP UPnP HTTP server 4 | 5 | 6 | server { 7 | listen *:80 default_server rcvbuf=2m sndbuf=2m accept_filter=httpready so_keepalive=30m::10; 8 | listen [::]:80 default_server rcvbuf=2m sndbuf=2m accept_filter=httpready so_keepalive=30m::10 ipv6only=on; 9 | server_name ""; 10 | 11 | index index.html; 12 | root @WWWDIR@; 13 | access_log /var/log/nginx-access.log main buffer=64k; 14 | error_log /var/log/nginx-error.log; # debug 15 | 16 | include @CONFDIR@/nginx-upnp-handler.conf; 17 | } 18 | -------------------------------------------------------------------------------- /freebsd/ssdpd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### Rozhuk Ivan 2013 - 2017 3 | ### startup script file for ssdpd 4 | ### 5 | 6 | 7 | # PROVIDE: ssdpd 8 | # REQUIRE: DAEMON 9 | # BEFORE: LOGIN 10 | # KEYWORD: shutdown 11 | 12 | . /etc/rc.subr 13 | 14 | name="ssdpd" 15 | rcvar=ssdpd_enable 16 | 17 | load_rc_config $name 18 | 19 | : ${ssdpd_enable="NO"} 20 | : ${ssdpd_command="@CMAKE_INSTALL_PREFIX@/bin/ssdpd"} 21 | : ${ssdpd_cfgfile="@CONFDIR@/ssdpd.conf"} 22 | : ${ssdpd_pidfile="@RUNDIR@/ssdpd.pid"} 23 | : ${ssdpd_user="www"} 24 | : ${ssdpd_group="www"} 25 | : ${ssdpd_chroot=""} 26 | : ${ssdpd_chdir=""} 27 | 28 | 29 | command=${ssdpd_command} 30 | command_args="-d -c ${ssdpd_cfgfile} -P ${ssdpd_pidfile}" 31 | 32 | pidfile="${ssdpd_chroot}${ssdpd_pidfile}" 33 | required_dirs=${ssdpd_chroot} 34 | required_files="${ssdpd_chroot}${command}" 35 | 36 | 37 | run_rc_command "$1" 38 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(SSDPD_BIN ssdpd.c 3 | liblcb/src/net/socket.c 4 | liblcb/src/net/socket_address.c 5 | liblcb/src/net/socket_options.c 6 | liblcb/src/net/utils.c 7 | liblcb/src/proto/http.c 8 | liblcb/src/proto/upnp_ssdp.c 9 | liblcb/src/threadpool/threadpool.c 10 | liblcb/src/threadpool/threadpool_msg_sys.c 11 | liblcb/src/threadpool/threadpool_task.c 12 | liblcb/src/utils/cmd_line_daemon.c 13 | liblcb/src/utils/sys.c 14 | liblcb/src/utils/buf_str.c 15 | liblcb/src/utils/info.c 16 | liblcb/src/utils/xml.c) 17 | 18 | add_executable(ssdpd ${SSDPD_BIN}) 19 | target_compile_definitions(ssdpd PRIVATE -DSOCKET_XML_CONFIG) 20 | set_target_properties(ssdpd PROPERTIES LINKER_LANGUAGE C) 21 | target_link_libraries(ssdpd ${CMAKE_REQUIRED_LIBRARIES} ${CMAKE_EXE_LINKER_FLAGS}) 22 | 23 | install(TARGETS ssdpd RUNTIME DESTINATION bin) 24 | -------------------------------------------------------------------------------- /dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TAR=${2:-"tar"} 4 | 5 | if [ $# -lt 1 ] ; then 6 | echo "Usage: dist.sh [tar_command]" 7 | exit 1 8 | fi 9 | 10 | FILE_NAME=$1 11 | PREFIX=`basename $FILE_NAME | sed -e 's/\.tar.*$//'` 12 | 13 | OUT="" 14 | while true ; do 15 | __mktemp=`which mktemp` 16 | if [ F"$__mktemp" != "F" ] ; then 17 | OUT=`$__mktemp /tmp/files-XXXXXXXX` 18 | break 19 | else 20 | OUT="/tmp/files-`strings -7 /dev/urandom | head -1 | sed -e 's/[^[:alnum:]]//g'`" 21 | fi 22 | if [ ! -f "$OUT" ] ; then 23 | break 24 | fi 25 | done 26 | 27 | git ls-files > $OUT 28 | SUBMODULES=`git submodule | cut -d ' ' -f 3` 29 | 30 | for sub in $SUBMODULES ; do 31 | (cd $sub && git ls-files | sed -e "s|^|$sub/|" >> $OUT) 32 | done 33 | 34 | ${TAR} -c --exclude='.[^/]*' --exclude='*.xz' --exclude='*.gz' --no-recursion --transform "s|^|$PREFIX/|" -a -T $OUT -v -f $FILE_NAME 35 | rm -f $OUT 36 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [rozhuk-im] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | #polar: # Replace with a single Polar username 13 | buy_me_a_coffee: rojuc # Replace with a single Buy Me a Coffee username 14 | custom: ['https://paypal.me/rojuc'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2011 - 2018, Rozhuk Ivan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /ssdpd.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 6 9 | 10 | 11 | 12 | 64 13 | 64 14 | 1 15 | 1 16 | 1 17 | 18 | 19 | yes 20 | yes 21 | yes 22 | 23 | 24 | 25 | 26 | @WWWDIR@/upnp/descr/root.xml 27 | 1800 28 | 10 29 | 30 | 31 | lo0 32 | 33 | 34 | 35 | 36 | lan0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ssdpd.conf.debug: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 6 9 | 10 | 11 | 12 | 64 13 | 64 14 | 1 15 | 1 16 | 1 17 | 18 | 19 | yes 20 | yes 21 | yes 22 | 23 | 24 | 25 | 26 | /home/rim/docs/Progs/ssdpd/www/upnp/descr/root.xml 27 | 1800 28 | 10 29 | 30 | 31 | lo0 32 | 33 | 34 | 35 | 36 | lan0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /www/upnp/descr/root.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 1 7 | 0 8 | 9 | 10 | urn:schemas-upnp-org:device:MediaServer:1 11 | Web Media Server 12 | Rozhuk Ivan 13 | http://www.netlab.linkpc.net 14 | UPnP/DLNA media service for sharing content. 15 | Web UPnP sevice 16 | 1.0 17 | http://netlab.dhis.org/wiki/en:software:ssdpd:index 18 | 1 19 | uuid:99c11c71-ba2a-56ee-6fd1-830ff56f149f 20 | 000000000001 21 | DMS-1.50 22 | 23 | 24 | image/png 25 | 48 26 | 48 27 | 8 28 | /upnp/icons/dev-icon-48x48.png 29 | 30 | 31 | 32 | 33 | urn:schemas-upnp-org:service:ContentDirectory:1 34 | urn:upnp-org:serviceId:ContentDirectory 35 | /upnp/descr/ContentDirectory.xml 36 | /upnp/control/ContentDirectory.php 37 | /upnp/subscribe/ContentDirectory 38 | 39 | 40 | urn:schemas-upnp-org:service:ConnectionManager:1 41 | urn:upnp-org:serviceId:ConnectionManager 42 | /upnp/descr/ConnectionManager.xml 43 | /upnp/control/ConnectionManager.php 44 | /upnp/subscribe/ConnectionManager 45 | 46 | 47 | urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1 48 | urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar 49 | /upnp/descr/X_MS_MediaReceiverRegistrar.xml 50 | /upnp/control/X_MS_MediaReceiverRegistrar.php 51 | /upnp/subscribe/X_MS_MediaReceiverRegistrar 52 | 53 | 54 | /upnp/presentation/about.html 55 | 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/build-ubuntu-latest.yml: -------------------------------------------------------------------------------- 1 | name: build-ubuntu-latest 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | 10 | jobs: 11 | build: 12 | # The CMake configure and build commands are platform agnostic and should work equally 13 | # well on Windows or Mac. You can convert this to a matrix build if you need 14 | # cross-platform coverage. 15 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | compiler: [gcc, clang] 20 | include: 21 | - compiler: gcc 22 | cc: gcc 23 | cxx: g++ 24 | - compiler: clang 25 | cc: clang 26 | cxx: clang++ 27 | steps: 28 | - uses: actions/checkout@v2 29 | with: 30 | submodules: 'recursive' 31 | 32 | - name: Install libraries 33 | run: | 34 | sudo apt-get update 35 | sudo apt-get install cmake libcunit1 libcunit1-doc libcunit1-dev 36 | 37 | - name: Create Build Environment 38 | # Some projects don't allow in-source building, so create a separate build directory 39 | # We'll use this as our working directory for all subsequent commands 40 | run: cmake -E make_directory ${{github.workspace}}/build 41 | 42 | - name: Configure CMake 43 | # Use a bash shell so we can use the same syntax for environment variable 44 | # access regardless of the host operating system 45 | shell: bash 46 | working-directory: ${{github.workspace}}/build 47 | # Note the current convention is to use the -S and -B options here to specify source 48 | # and build directories, but this is only available with CMake 3.13 and higher. 49 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 50 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DENABLE_TESTS=1 51 | 52 | - name: Build 53 | working-directory: ${{github.workspace}}/build 54 | shell: bash 55 | # Execute the build. You can specify a specific target with "--target " 56 | run: cmake --build . --config $BUILD_TYPE -j 16 57 | 58 | - name: Test 59 | working-directory: ${{github.workspace}}/build 60 | shell: bash 61 | # Execute tests defined by the CMake configuration. 62 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 63 | run: ctest -C $BUILD_TYPE -j 16 --output-on-failure 64 | -------------------------------------------------------------------------------- /nginx/nginx-upnp-full.conf: -------------------------------------------------------------------------------- 1 | ### Rozhuk Ivan 2009.04-2024 2 | ### nginx configuration file 3 | ### PHP UPnP nginx HTTP server 4 | 5 | 6 | user www www; 7 | daemon on; 8 | worker_processes auto; 9 | worker_priority 0; 10 | worker_cpu_affinity auto; 11 | pid @RUNDIR@/nginx.pid; 12 | 13 | master_process on; 14 | timer_resolution 100ms; 15 | pcre_jit on; 16 | 17 | 18 | # If your nginx build with dynamic modules (DSO) then you need 19 | # uncomment this and set correct path. 20 | #load_module /usr/local/libexec/nginx/ngx_http_headers_more_filter_module.so; 21 | 22 | 23 | events { 24 | worker_connections 65535; 25 | multi_accept on; 26 | } 27 | 28 | 29 | http { 30 | include mime.types; 31 | uninitialized_variable_warn on; 32 | 33 | resolver 127.0.0.1; 34 | resolver_timeout 8s; 35 | 36 | log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'; 37 | log_format full_log '$remote_addr - $remote_user [$time_local] "$http_host" "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'; 38 | #access_log /var/log/nginx.access.log main buffer=64k; 39 | log_not_found on; 40 | log_subrequest off; 41 | open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2; 42 | 43 | client_body_in_file_only off; 44 | client_body_in_single_buffer off; 45 | #client_body_buffer_size 8k; 46 | client_body_temp_path /tmp/nginx/ 1 2; 47 | client_body_timeout 30s; 48 | client_max_body_size 8m; 49 | client_header_timeout 30s; 50 | client_header_buffer_size 2k; 51 | large_client_header_buffers 256 64k; 52 | 53 | msie_padding off; 54 | msie_refresh off; 55 | 56 | if_modified_since exact; 57 | 58 | merge_slashes on; 59 | underscores_in_headers off; 60 | port_in_redirect on; 61 | recursive_error_pages off; 62 | chunked_transfer_encoding off; 63 | 64 | server_tokens on; 65 | 66 | sendfile off; 67 | sendfile_max_chunk 64m; 68 | aio off; 69 | read_ahead 128m; 70 | output_buffers 128 64k; 71 | directio off; 72 | tcp_nopush on; 73 | tcp_nodelay off; 74 | send_lowat 0; 75 | keepalive_timeout 65 60; 76 | send_timeout 60s; 77 | reset_timedout_connection on; 78 | 79 | open_file_cache max=1000 inactive=20s; 80 | open_file_cache_valid 30s; 81 | open_file_cache_min_uses 2; 82 | open_file_cache_errors on; 83 | 84 | server_name_in_redirect on; 85 | server_names_hash_max_size 512; 86 | server_names_hash_bucket_size 32; 87 | 88 | # GZip module 89 | gzip off; 90 | 91 | # SSI module 92 | ssi off; 93 | ssi_silent_errors off; 94 | 95 | include @CONFDIR@/nginx-upnp-server.conf; 96 | } 97 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-latest.yml: -------------------------------------------------------------------------------- 1 | name: build-macos-latest 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | 10 | jobs: 11 | build: 12 | # The CMake configure and build commands are platform agnostic and should work equally 13 | # well on Windows or Mac. You can convert this to a matrix build if you need 14 | # cross-platform coverage. 15 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | runs-on: macos-latest 17 | strategy: 18 | matrix: 19 | compiler: [gcc, clang] 20 | include: 21 | - compiler: gcc 22 | cc: gcc 23 | cxx: g++ 24 | - compiler: clang 25 | cc: clang 26 | cxx: clang++ 27 | steps: 28 | - uses: actions/checkout@v2 29 | with: 30 | submodules: 'recursive' 31 | 32 | - name: Install libraries 33 | run: | 34 | checkPkgAndInstall() 35 | { 36 | while [ $# -ne 0 ] 37 | do 38 | rm -f '/usr/local/bin/2to3' 39 | if brew ls --versions $1 ; then 40 | brew upgrade $1 41 | else 42 | brew install $1 43 | fi 44 | shift 45 | done 46 | } 47 | checkPkgAndInstall cunit 48 | 49 | - name: Create Build Environment 50 | # Some projects don't allow in-source building, so create a separate build directory 51 | # We'll use this as our working directory for all subsequent commands 52 | run: cmake -E make_directory ${{github.workspace}}/build 53 | 54 | - name: Configure CMake 55 | # Use a bash shell so we can use the same syntax for environment variable 56 | # access regardless of the host operating system 57 | shell: bash 58 | working-directory: ${{github.workspace}}/build 59 | # Note the current convention is to use the -S and -B options here to specify source 60 | # and build directories, but this is only available with CMake 3.13 and higher. 61 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 62 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DENABLE_TESTS=1 63 | 64 | - name: Build 65 | working-directory: ${{github.workspace}}/build 66 | shell: bash 67 | # Execute the build. You can specify a specific target with "--target " 68 | run: cmake --build . --config $BUILD_TYPE -j 16 69 | 70 | - name: Test 71 | working-directory: ${{github.workspace}}/build 72 | shell: bash 73 | # Execute tests defined by the CMake configuration. 74 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 75 | run: ctest -C $BUILD_TYPE -j 16 --output-on-failure 76 | -------------------------------------------------------------------------------- /www/upnp/descr/X_MS_MediaReceiverRegistrar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 1 6 | 0 7 | 8 | 9 | 10 | IsAuthorized 11 | 12 | 13 | DeviceID 14 | in 15 | A_ARG_TYPE_DeviceID 16 | 17 | 18 | Result 19 | out 20 | A_ARG_TYPE_Result 21 | 22 | 23 | 24 | 25 | RegisterDevice 26 | 27 | 28 | RegistrationReqMsg 29 | in 30 | A_ARG_TYPE_RegistrationReqMsg 31 | 32 | 33 | RegistrationRespMsg 34 | out 35 | A_ARG_TYPE_RegistrationRespMsg 36 | 37 | 38 | 39 | 40 | IsValidated 41 | 42 | 43 | DeviceID 44 | in 45 | A_ARG_TYPE_DeviceID 46 | 47 | 48 | Result 49 | out 50 | A_ARG_TYPE_Result 51 | 52 | 53 | 54 | 55 | 56 | 57 | A_ARG_TYPE_DeviceID 58 | string 59 | 60 | 61 | A_ARG_TYPE_Result 62 | int 63 | 64 | 65 | A_ARG_TYPE_RegistrationReqMsg 66 | bin.base64 67 | 68 | 69 | A_ARG_TYPE_RegistrationRespMsg 70 | bin.base64 71 | 72 | 73 | AuthorizationGrantedUpdateID 74 | ui4 75 | 76 | 77 | AuthorizationDeniedUpdateID 78 | ui4 79 | 80 | 81 | ValidationSucceededUpdateID 82 | ui4 83 | 84 | 85 | ValidationRevokedUpdateID 86 | ui4 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /nginx/nginx-upnp-handler.conf: -------------------------------------------------------------------------------- 1 | ### Rozhuk Ivan 2009.04 - 2017 2 | ### nginx configuration file 3 | ### PHP UPnP HTTP request handler 4 | 5 | 6 | # Files sharing and listing. 7 | location ^~ /upnpdata/ { 8 | root @WWWDIR@; 9 | 10 | send_timeout 24h; 11 | 12 | # Allow only local nets by default. 13 | allow 10.0.0.0/8; 14 | allow 169.254.0.0/16; 15 | allow 172.16.0.0/12; 16 | allow 192.168.0.0/16; 17 | deny all; 18 | 19 | access_log off; 20 | autoindex on; 21 | autoindex_exact_size on; 22 | autoindex_localtime on; 23 | 24 | # Customize responce HTTP headers. 25 | server_tokens off; 26 | more_set_headers 'Server: nginx/$nginx_version DLNADOC/1.50 UPnP/1.0 @PACKAGE_NAME@-PHP-Media-server/@PACKAGE_VERSION@'; 27 | add_header TransferMode.DLNA.ORG 'Streaming'; 28 | #add_header ContentFeatures.DLNA.ORG 'DLNA.ORG_OP=11'; 29 | # avi: add_header ContentFeatures.DLNA.ORG 'DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=017000 00000000000000000000000000'; 30 | add_header ContentFeatures.DLNA.ORG 'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000'; 31 | } 32 | 33 | # UPnP SUBSCRIBE/UNSUBSCRIBE handle 34 | location ^~ /upnp/subscribe/ { 35 | access_log off; 36 | server_tokens off; 37 | 38 | if ($request_method = SUBSCRIBE) { 39 | # Customize responce HTTP headers. 40 | more_set_headers 'Server: nginx/$nginx_version DLNADOC/1.50 UPnP/1.0 @PACKAGE_NAME@-PHP-Media-server/@PACKAGE_VERSION@'; 41 | add_header Pragma 'no-cache'; 42 | add_header SID 'uuid:7CF21CB0-2266-47BE-A608-3CC1F5210BB4'; 43 | add_header Timeout 'Second-1800'; 44 | 45 | return 200; 46 | } 47 | if ($request_method = UNSUBSCRIBE) { 48 | # Customize responce HTTP headers. 49 | more_set_headers 'Server: nginx/$nginx_version DLNADOC/1.50 UPnP/1.0 @PACKAGE_NAME@-PHP-Media-server/@PACKAGE_VERSION@'; 50 | add_header Pragma 'no-cache'; 51 | 52 | return 200; 53 | } 54 | } 55 | 56 | # php for: UPnP 57 | location ^~ /upnp/control/ { 58 | root @WWWDIR@; 59 | 60 | # Allow only local nets by default. 61 | allow 10.0.0.0/8; 62 | allow 169.254.0.0/16; 63 | allow 172.16.0.0/12; 64 | allow 192.168.0.0/16; 65 | deny all; 66 | 67 | access_log off; 68 | 69 | try_files $fastcgi_script_name = 404; 70 | include fastcgi_params; 71 | fastcgi_pass unix:@RUNDIR@/php-fcgi-upnp-server.sock; 72 | fastcgi_connect_timeout 30s; 73 | fastcgi_read_timeout 600s; 74 | fastcgi_send_timeout 600s; 75 | fastcgi_ignore_client_abort off; 76 | #fastcgi_cache_valid any 10s; 77 | fastcgi_intercept_errors on; 78 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 79 | 80 | # Customize responce HTTP headers. 81 | server_tokens off; 82 | more_set_headers 'Server: nginx/$nginx_version DLNADOC/1.50 UPnP/1.0 @PACKAGE_NAME@-PHP-Media-server/@PACKAGE_VERSION@'; 83 | add_header EXT ' '; # REQUIRED for backwards compatibility with UPnP 1.0. (Header field name only; no field value.) 84 | } 85 | 86 | # Max cache for static files 87 | location ^~ /upnp/ { 88 | root @WWWDIR@; 89 | 90 | access_log off; 91 | 92 | # Customize responce HTTP headers. 93 | expires 30d; 94 | add_header Last-Modified $date_gmt; 95 | } 96 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SSDPd 2 | 3 | [![Build-macOS-latest Actions Status](https://github.com/rozhuk-im/ssdpd/workflows/build-macos-latest/badge.svg)](https://github.com/rozhuk-im/ssdpd/actions) 4 | [![Build-Ubuntu-latest Actions Status](https://github.com/rozhuk-im/ssdpd/workflows/build-ubuntu-latest/badge.svg)](https://github.com/rozhuk-im/ssdpd/actions) 5 | [![Build-PHP Status](https://scrutinizer-ci.com/g/rozhuk-im/ssdpd/badges/build.png?b=master)](https://scrutinizer-ci.com/g/rozhuk-im/ssdpd/build-status/master) 6 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/rozhuk-im/ssdpd/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/rozhuk-im/ssdpd/?branch=master) 7 | 8 | 9 | Rozhuk Ivan 2013-2025 10 | 11 | SSDPd - Announces UPnP/DLNA device across network. 12 | You can use PHP script, nginx config and static files to 13 | build your own UPnP/DLNA server. 14 | 15 | 16 | ## Licence 17 | BSD licence. 18 | Website: http://www.netlab.linkpc.net/wiki/en:software:ssdpd:index 19 | 20 | 21 | ## Donate 22 | Support the author 23 | * **GitHub Sponsors:** [!["GitHub Sponsors"](https://camo.githubusercontent.com/220b7d46014daa72a2ab6b0fcf4b8bf5c4be7289ad4b02f355d5aa8407eb952c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d53706f6e736f722d6661666266633f6c6f676f3d47697448756225323053706f6e736f7273)](https://github.com/sponsors/rozhuk-im)
24 | * **Buy Me A Coffee:** [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/rojuc)
25 | * **PayPal:** [![PayPal](https://srv-cdn.himpfen.io/badges/paypal/paypal-flat.svg)](https://paypal.me/rojuc)
26 | * **Bitcoin (BTC):** `1AxYyMWek5vhoWWRTWKQpWUqKxyfLarCuz`
27 | 28 | 29 | ## Features 30 | * can act as UPnP/DLNA ContentDirectory to share multimedia content 31 | * can announce remote UPnP/DLNA devices 32 | 33 | 34 | ## Compilation and Installation 35 | ``` 36 | sudo apt-get install build-essential git cmake fakeroot 37 | git clone --recursive https://github.com/rozhuk-im/ssdpd.git 38 | cd ssdpd 39 | mkdir build 40 | cd build 41 | cmake .. 42 | make -j 43 | ``` 44 | 45 | 46 | UPnP/DLNA PHP server requires 47 | 1. nginx with headers_more. 48 | 2. PHP with fpm, fileinfo, soap, xml. 49 | 50 | 51 | ## Run tests 52 | ``` 53 | mkdir -p build 54 | cd build 55 | cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=1 .. 56 | cmake --build . --config Release -j 16 57 | ctest -C Release --output-on-failure -j 16 58 | ``` 59 | 60 | ## Usage 61 | ``` 62 | ssdpd [-d] [-v] [-c file] 63 | [-p PID file] [-u uid|usr -g gid|grp] 64 | -h usage (this screen) 65 | -d become daemon 66 | -c file config file 67 | -p PID file file name to store PID 68 | -u uid|user change uid 69 | -g gid|group change gid 70 | -v verboce 71 | ``` 72 | 73 | 74 | ## Setup 75 | 76 | ### ssdpd 77 | Copy %%ETCDIR%%/ssdpd.conf.sample to %%ETCDIR%%/ssdpd.conf 78 | then replace lan0 with your network interface name. 79 | Add more sections if needed. 80 | Remove IPv4/IPv6 lines if not needed. 81 | 82 | Add to /etc/rc.conf: 83 | ``` 84 | ssdpd_enable="YES" 85 | ``` 86 | 87 | Run: 88 | ``` 89 | service ssdpd restart 90 | ``` 91 | 92 | 93 | 94 | ### PHP UPnP server 95 | 96 | #### PHP 97 | Add to /etc/rc.conf: 98 | ``` 99 | php_fpm_enable="YES" 100 | ``` 101 | 102 | Run: 103 | ``` 104 | service php-fpm restart 105 | ``` 106 | 107 | 108 | #### nginx 109 | If nginx will serve only upnp then you can: 110 | ``` 111 | ln -f -s %%ETCDIR%%/nginx-upnp-full.conf %%CMAKE_INSTALL_PREFIX%%/etc/nginx/nginx.conf 112 | ``` 113 | If nginx build with DSO (dynamic modules load) then you need 114 | uncomment "load_module" and set correth path to module. 115 | 116 | Or add to existing nginx.conf following line: 117 | include %%ETCDIR%%/nginx-upnp-handler.conf; 118 | some where in existing http/server section. 119 | 120 | Add to /etc/rc.conf: 121 | ``` 122 | nginx_enable="YES" 123 | ``` 124 | 125 | Run: 126 | ``` 127 | service nginx restart 128 | ``` 129 | 130 | 131 | #### Data 132 | Place shared data in: %%DATADIR%%/www/upnpdata 133 | or make in as simlink on existing data. 134 | Make sure that permissions allow www access. 135 | 136 | 137 | ### Firewall 138 | ### ssdpd 139 | Allow all IPv4 with options proto igmp. 140 | Allow all udp dst port 1900. 141 | 142 | ### PHP UPnP server 143 | Allow in tcp dst port 80. 144 | -------------------------------------------------------------------------------- /test.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | M-SEARCH * HTTP/1.1 4 | HOST: 239.255.255.250:1900 5 | MAN: "ssdp:discover" 6 | MX: 1 7 | ST: ssdp:all 8 | USER-AGENT: OS/version UPnP/1.1 product/version 9 | 10 | 11 | 12 | SUBSCRIBE /udp/236.3.6.9:1234 HTTP/1.1 13 | User-Agent: curl/7.24.0 (amd64-portbld-freebsd9.1) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.7 libidn/1.26 14 | Host: 172.16.0.254:80 15 | Accept: */* 16 | 17 | 18 | 19 | 20 | POST /upnp/control/ContentDirectory HTTP/1.0 21 | HOST: 172.16.0.254:80 22 | CONTENT-LENGTH: 439 23 | CONTENT-TYPE: text/xml;charset="utf-8" 24 | USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV]UE40D6100/1.0 25 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" 26 | 27 | iptv/private/iptv_btk.m3uBrowseDirectChildren*01 28 | 29 | 30 | 31 | 32 | POST /control/89c11c71-ba2a-56ee-6fd1-830ff56f149f/ContentDirectory HTTP/1.1 33 | HOST: 172.16.0.254:51900 34 | CONTENT-LENGTH: 415 35 | CONTENT-TYPE: text/xml;charset="utf-8" 36 | USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV]UE40D6100/1.0 37 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" 38 | 39 | 0BrowseDirectChildren*01 40 | 41 | 42 | 43 | POST /upnp/control/ContentDirectory.php HTTP/1.0 44 | HOST: 172.16.0.254:80 45 | CONTENT-LENGTH: 656 46 | CONTENT-TYPE: text/xml;charset="utf-8" 47 | USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV]UE40D6100/1.0 48 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" 49 | 50 | VBrowseDirectChildren@id,@parentID,@restricted,dc:title,upnp:class,res,dc:date,@childCount,sec:CaptionInfo,sec:CaptionInfoEx, sec:dcmInfo, sec:MetaFileInfo,res@resolution,res@size,upnp:genre,dc:date,upnp:album,res@duration,upnp:albumArtURI,res@bitrate,dc:creator030 51 | 52 | 53 | 54 | 55 | POST /upnp/control/ContentDirectory.php HTTP/1.1 56 | HOST:172.16.0.254:80 57 | Accept: */* 58 | CONTENT-LENGTH: 313 59 | CONTENT-TYPE: text/xml; charset="utf-8" 60 | USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV] Samsung 8 Series (65)/1.0 UPnP/1.0 61 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#GetSearchCapabilities" 62 | Connection: close 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | POST /upnp/control/ContentDirectory.php HTTP/1.1 76 | HOST: 172.16.0.254:80 77 | CONTENT-LENGTH: 485 78 | CONTENT-TYPE: text/xml; charset="utf-8" 79 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:3#Browse" 80 | USER-AGENT: 6.1.7601 2/Service Pack 1, UPnP/1.0, Portable SDK for UPnP devices/1.6.18 81 | 82 | 83 | 84 | 0 85 | BrowseDirectChildren 86 | id,dc:title,res,sec:CaptionInfo,sec:CaptionInfoEx 87 | 0 88 | 0 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | POST /upnp/control/ContentDirectory.php HTTP/1.1 98 | HOST:172.16.0.254:80 99 | Accept: */* 100 | CONTENT-LENGTH: 445 101 | CONTENT-TYPE: text/xml; charset="utf-8" 102 | USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV] Samsung 8 Series (65)/1.0 UPnP/1.0 103 | SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" 104 | Connection: close 105 | 106 | 0BrowseMetadata*00 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /www/upnp/descr/ConnectionManager.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | 8 | 9 | GetProtocolInfo 10 | 11 | 12 | Source 13 | out 14 | SourceProtocolInfo 15 | 16 | 17 | Sink 18 | out 19 | SinkProtocolInfo 20 | 21 | 22 | 23 | 24 | PrepareForConnection 25 | 26 | 27 | RemoteProtocolInfo 28 | in 29 | A_ARG_TYPE_ProtocolInfo 30 | 31 | 32 | PeerConnectionManager 33 | in 34 | A_ARG_TYPE_ConnectionManager 35 | 36 | 37 | PeerConnectionID 38 | in 39 | A_ARG_TYPE_ConnectionID 40 | 41 | 42 | Direction 43 | in 44 | A_ARG_TYPE_Direction 45 | 46 | 47 | ConnectionID 48 | out 49 | A_ARG_TYPE_ConnectionID 50 | 51 | 52 | AVTransportID 53 | out 54 | A_ARG_TYPE_AVTransportID 55 | 56 | 57 | RcsID 58 | out 59 | A_ARG_TYPE_RcsID 60 | 61 | 62 | 63 | 64 | ConnectionComplete 65 | 66 | 67 | ConnectionID 68 | in 69 | A_ARG_TYPE_ConnectionID 70 | 71 | 72 | 73 | 74 | GetCurrentConnectionIDs 75 | 76 | 77 | ConnectionIDs 78 | out 79 | CurrentConnectionIDs 80 | 81 | 82 | 83 | 84 | GetCurrentConnectionInfo 85 | 86 | 87 | ConnectionID 88 | in 89 | A_ARG_TYPE_ConnectionID 90 | 91 | 92 | RcsID 93 | out 94 | A_ARG_TYPE_RcsID 95 | 96 | 97 | AVTransportID 98 | out 99 | A_ARG_TYPE_AVTransportID 100 | 101 | 102 | ProtocolInfo 103 | out 104 | A_ARG_TYPE_ProtocolInfo 105 | 106 | 107 | PeerConnectionManager 108 | out 109 | A_ARG_TYPE_ConnectionManager 110 | 111 | 112 | PeerConnectionID 113 | out 114 | A_ARG_TYPE_ConnectionID 115 | 116 | 117 | Direction 118 | out 119 | A_ARG_TYPE_Direction 120 | 121 | 122 | Status 123 | out 124 | A_ARG_TYPE_ConnectionStatus 125 | 126 | 127 | 128 | 129 | 130 | 131 | SourceProtocolInfo 132 | string 133 | 134 | 135 | SinkProtocolInfo 136 | string 137 | 138 | 139 | CurrentConnectionIDs 140 | string 141 | 142 | 143 | A_ARG_TYPE_ConnectionStatus 144 | string 145 | 146 | OK 147 | ContentFormatMismatch 148 | InsufficientBandwidth 149 | UnreliableChannel 150 | Unknown 151 | 152 | 153 | 154 | A_ARG_TYPE_ConnectionManager 155 | string 156 | 157 | 158 | A_ARG_TYPE_Direction 159 | string 160 | 161 | Input 162 | Output 163 | 164 | 165 | 166 | A_ARG_TYPE_ProtocolInfo 167 | string 168 | 169 | 170 | A_ARG_TYPE_ConnectionID 171 | i4 172 | 173 | 174 | A_ARG_TYPE_AVTransportID 175 | i4 176 | 177 | 178 | A_ARG_TYPE_RcsID 179 | i4 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Cmake configuration file 3 | # 4 | 5 | ############################# INITIAL SECTION ########################## 6 | cmake_minimum_required(VERSION 3.20) 7 | 8 | project(ssdpd C) 9 | 10 | set(PACKAGE_VERSION_MAJOR 1) 11 | set(PACKAGE_VERSION_MINOR 9) 12 | set(PACKAGE_VERSION_PATCH 0) 13 | 14 | set(PACKAGE_NAME "${PROJECT_NAME}") 15 | set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}") 16 | set(PACKAGE_URL "https://github.com/rozhuk-im/ssdpd") 17 | set(PACKAGE_BUGREPORT "https://github.com/rozhuk-im/ssdpd") 18 | set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") 19 | set(PACKAGE_DESCRIPTION "SSDP announce daemon for UPnP 1.1") 20 | set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}") 21 | 22 | ############################# OPTIONS SECTION ########################## 23 | 24 | option(INSTALL_PHP_MEDIA_SERVER "Install PHP UPnP/DLNA Media server [default: OFF]" OFF) 25 | option(ENABLE_TESTS "Enable tests [default: OFF]" OFF) 26 | if (ENABLE_TESTS) 27 | # Enable testing functionality. 28 | enable_testing() 29 | set(ENABLE_LIBLCB_TESTS TRUE) 30 | endif() 31 | 32 | # Now CMAKE_INSTALL_PREFIX is a base prefix for everything. 33 | if (NOT CONFDIR) 34 | set(CONFDIR "${CMAKE_INSTALL_PREFIX}/etc/ssdpd") 35 | endif() 36 | 37 | if (NOT RUNDIR) 38 | set(RUNDIR "/var/run") 39 | endif() 40 | 41 | if (NOT WWWDIR) 42 | set(WWWDIR "${CMAKE_INSTALL_PREFIX}/share/ssdpd/www") 43 | endif() 44 | 45 | 46 | ############################# INCLUDE SECTION ########################## 47 | 48 | #include(CheckIncludeFiles) 49 | include(CheckFunctionExists) 50 | include(CheckSymbolExists) 51 | include(CheckCCompilerFlag) 52 | 53 | find_library(PTHREAD_LIBRARY pthread) 54 | list(APPEND CMAKE_REQUIRED_LIBRARIES ${PTHREAD_LIBRARY}) 55 | 56 | ############################# MACRO SECTION ############################ 57 | macro(try_c_flag prop flag) 58 | # Try flag once on the C compiler 59 | check_c_compiler_flag("-Werror ${flag}" C_FLAG_${prop}) 60 | if (C_FLAG_${prop}) 61 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") 62 | endif() 63 | endmacro() 64 | 65 | macro(try_linker_flag prop flag) 66 | # Check with the C compiler 67 | set(CMAKE_REQUIRED_FLAGS ${flag}) 68 | check_c_compiler_flag(${flag} LINKER_FLAG_${prop}) 69 | set(CMAKE_REQUIRED_FLAGS "") 70 | if (LINKER_FLAG_${prop}) 71 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}") 72 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${flag}") 73 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${flag}") 74 | endif() 75 | endmacro() 76 | 77 | function(install_if_not_exists src dest destname suffix) 78 | if (NOT IS_ABSOLUTE "${src}") 79 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 80 | endif() 81 | if (NOT IS_ABSOLUTE "${dest}") 82 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 83 | endif() 84 | get_filename_component(src_name "${src}" NAME) 85 | get_filename_component(dest_name "${destname}" NAME) 86 | if (NOT EXISTS "${dest}/${dest_name}${suffix}") 87 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${dest_name}${suffix}") 88 | configure_file(${src} ${src_tmp} @ONLY) 89 | install(FILES ${src_tmp} DESTINATION ${dest}) 90 | endif() 91 | endfunction() 92 | 93 | function(install_script src dest) 94 | if (NOT IS_ABSOLUTE "${src}") 95 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 96 | endif() 97 | if (NOT IS_ABSOLUTE "${dest}") 98 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 99 | endif() 100 | get_filename_component(src_name "${src}" NAME) 101 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${src_name}") 102 | configure_file(${src} ${src_tmp} @ONLY) 103 | install(PROGRAMS ${src_tmp} DESTINATION ${dest}) 104 | endfunction() 105 | 106 | function(install_config src dest) 107 | if (NOT IS_ABSOLUTE "${src}") 108 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 109 | endif() 110 | if (NOT IS_ABSOLUTE "${dest}") 111 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 112 | endif() 113 | get_filename_component(src_name "${src}" NAME) 114 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${src_name}") 115 | configure_file(${src} ${src_tmp} @ONLY) 116 | install(FILES ${src_tmp} DESTINATION ${dest}) 117 | endfunction() 118 | 119 | ############################# CONFIG SECTION ########################### 120 | 121 | # Prefer local include dirs to system ones. 122 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/" 123 | "${CMAKE_CURRENT_SOURCE_DIR}/src" 124 | "${CMAKE_CURRENT_SOURCE_DIR}/src/liblcb/include" 125 | "${CMAKE_BINARY_DIR}/src") 126 | 127 | set(TAR "tar") 128 | 129 | # Platform specific configuration. 130 | if (CMAKE_SYSTEM_NAME MATCHES "^.*BSD$|DragonFly") 131 | add_definitions(-D_BSD_SOURCE -DFREEBSD) 132 | message(STATUS "Configuring for BSD system") 133 | include_directories("/usr/local/include/") 134 | set(TAR "gtar") 135 | endif() 136 | 137 | if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") 138 | add_definitions(-D_BSD_SOURCE -DDARWIN) 139 | message(STATUS "Configuring for Darwin") 140 | set(TAR "gnutar") 141 | endif() 142 | 143 | 144 | if (CMAKE_SYSTEM_NAME STREQUAL "Linux") 145 | add_definitions(-D_GNU_SOURCE -DLINUX -D__USE_GNU=1) 146 | message(STATUS "Configuring for Linux") 147 | if (BUILD_CPU_MODE STREQUAL "32") 148 | add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE) 149 | endif() 150 | 151 | list(APPEND CMAKE_REQUIRED_LIBRARIES rt) 152 | endif() 153 | 154 | 155 | try_c_flag(PIPE "-pipe") 156 | try_c_flag(NO_DEL_NULL_PTR_CHKS "-fno-delete-null-pointer-checks") 157 | 158 | 159 | if (CMAKE_BUILD_TYPE MATCHES "Debug") 160 | # Process with warn flags. 161 | try_c_flag(W "-W") 162 | try_c_flag(WALL "-Wall") 163 | try_c_flag(WEVERYTHING "-Weverything") 164 | if (NOT C_FLAG_WEVERYTHING) 165 | try_c_flag(WPOINTER "-Wpointer-arith") 166 | try_c_flag(WSTRICT_PROTOTYPES "-Wstrict-prototypes") 167 | try_c_flag(PEDANTIC "-pedantic") 168 | try_c_flag(WNULL_DEREFERENCE "-Wnull-dereference") 169 | try_c_flag(WDUPLICATED_COND "-Wduplicated-cond") 170 | try_c_flag(WIMPLICIT_FALLTHROUGH "-Wimplicit-fallthrough") 171 | endif() 172 | 173 | try_c_flag(WCAST_FN_TYPE_STRICT "-Wno-cast-function-type-strict") 174 | try_c_flag(WCAST_QUAL "-Wno-cast-qual") 175 | try_c_flag(WDOCUMENTATION "-Wno-documentation") 176 | try_c_flag(WDOC_UNKNOWN_CMD "-Wno-documentation-unknown-command") 177 | try_c_flag(WPADDED "-Wno-padded") 178 | #try_c_flag(WPOINTER_SIGN "-Wno-pointer-sign") 179 | #try_c_flag(WRESERVED_ID_MACRO "-Wno-reserved-id-macro") 180 | try_c_flag(WRESERVED_IDENTIFIER "-Wno-reserved-identifier") 181 | #try_c_flag(WSIGN_COMPARE "-Wno-sign-compare") 182 | try_c_flag(WSWITCH_ENUM "-Wno-switch-enum") 183 | #try_c_flag(WUNUSED_CONST "-Wno-unused-const-variable") 184 | #try_c_flag(WUNUSED_FUNCTION "-Wno-unused-function") 185 | #try_c_flag(WUNUSED_PARAM "-Wno-unused-parameter") 186 | #try_c_flag(WUNUSED_VAR "-Wno-unused-variable") 187 | try_c_flag(WUNSAFE_BUFFER_USAGE "-Wno-unsafe-buffer-usage") 188 | #try_c_flag(WVARIADIC_MACROS "-Wno-variadic-macros") 189 | #try_c_flag(WGNU_ZERO_VAR_MACRO_ARGS "-Wno-gnu-zero-variadic-macro-arguments") 190 | try_c_flag(WZERO_LENGTH_ARRAY "-Wno-zero-length-array") 191 | 192 | set(CMAKE_INSTALL_DO_STRIP FALSE) 193 | set(CMAKE_C_OPT_FLAGS "-g3 -ggdb -O0") 194 | message(STATUS "Adding -DDEBUG to definitions.") 195 | add_definitions(-DDEBUG) 196 | else() 197 | set(CMAKE_INSTALL_DO_STRIP TRUE) 198 | message(STATUS "Adding -DNDEBUG to definitions.") 199 | add_definitions(-DNDEBUG) 200 | endif() 201 | 202 | message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode.") 203 | message(STATUS "CMAKE_INSTALL_DO_STRIP is ${CMAKE_INSTALL_DO_STRIP}.") 204 | 205 | 206 | if (NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro) 207 | try_c_flag(STD11 "-std=c11") 208 | if (NOT C_FLAG_STD11) 209 | try_c_flag(STD99 "-std=c99") 210 | endif() 211 | endif() 212 | 213 | 214 | # Hardening flags 215 | try_c_flag(FORTIFY_SOURCE2 "-D_FORTIFY_SOURCE=2") 216 | try_c_flag(FSTACK_PROTECTOR_ALL "-fstack-protector-all") 217 | try_c_flag(FSANITIZE_SAFE_STACK "-fsanitize=safe-stack") 218 | try_c_flag(FSANITIZE_CFI "-fsanitize=cfi") 219 | try_c_flag(MRETPOLINE "-mretpoline") 220 | try_c_flag(MFUNCTION_RETURN "-mfunction-return=thunk") 221 | try_c_flag(MINDIRECT_BRANCH "-mindirect-branch=thunk") 222 | try_c_flag(FWRAPV "-fwrapv") 223 | try_c_flag(FPIE "-fPIE") 224 | if (C_FLAG_FPIE) 225 | try_linker_flag(PIE "-pie") 226 | endif() 227 | try_linker_flag(RETPOLINEPLT "-Wl,-z,retpolineplt") 228 | try_linker_flag(ZRELRO "-Wl,-z,relro") 229 | try_linker_flag(ZNOW "-Wl,-z,now") 230 | try_linker_flag(ZNOEXECSTACK "-Wl,-z,noexecstack") 231 | 232 | 233 | 234 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_OPT_FLAGS}") 235 | # Silently strip whitespace 236 | string(STRIP "${CMAKE_C_FLAGS}" CMAKE_C_FLAGS) 237 | string(STRIP "${CMAKE_EXE_LINKER_FLAGS}" CMAKE_EXE_LINKER_FLAGS) 238 | string(STRIP "${CMAKE_MODULE_LINKER_FLAGS}" CMAKE_MODULE_LINKER_FLAGS) 239 | string(STRIP "${CMAKE_SHARED_LINKER_FLAGS}" CMAKE_SHARED_LINKER_FLAGS) 240 | 241 | configure_file(config.h.cmake src/config.h) 242 | add_definitions(-DHAVE_CONFIG_H) 243 | 244 | ################################ SUBDIRS SECTION ####################### 245 | 246 | include(src/liblcb/CMakeLists.txt) 247 | add_subdirectory(src) 248 | 249 | if (ENABLE_TESTS) 250 | #add_subdirectory(tests) 251 | endif() 252 | 253 | ############################ TARGETS SECTION ########################### 254 | 255 | add_custom_target(dist ${CMAKE_CURRENT_SOURCE_DIR}/dist.sh 256 | "${CMAKE_BINARY_DIR}/${PACKAGE_TARNAME}.tar.xz" "${TAR}" 257 | COMMENT "Create source distribution" 258 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 259 | 260 | ##################### INSTALLATION ##################################### 261 | 262 | # Configs 263 | install(CODE "FILE(MAKE_DIRECTORY ${CONFDIR})") 264 | 265 | install_if_not_exists(ssdpd.conf ${CONFDIR} "ssdpd.conf" ".sample") 266 | 267 | if (CMAKE_SYSTEM_NAME MATCHES "^.*BSD$|DragonFly") 268 | install_script(freebsd/ssdpd "${CMAKE_INSTALL_PREFIX}/etc/rc.d/") 269 | endif() 270 | 271 | # Install PHP UPnP/DLNA Media server 272 | if (INSTALL_PHP_MEDIA_SERVER) 273 | install(DIRECTORY "www/upnp" DESTINATION ${WWWDIR}) 274 | install(DIRECTORY DESTINATION "${WWWDIR}/upnpdata") 275 | install_config("nginx/nginx-upnp-full.conf" ${CONFDIR}) 276 | install_config("nginx/nginx-upnp-server.conf" ${CONFDIR}) 277 | install_config("nginx/nginx-upnp-handler.conf" ${CONFDIR}) 278 | install_config("php/upnp-server.conf" "${CMAKE_INSTALL_PREFIX}/etc/php-fpm.d/") 279 | endif() 280 | 281 | -------------------------------------------------------------------------------- /ssdpd.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ../ssdpd 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | mkdir -p $(IntermediateDirectory) 157 | echo '#define PACKAGE_NAME "ssdpd"' > $(IntermediateDirectory)/config.h 158 | echo '#define PACKAGE_VERSION "1.0.0"' >> $(IntermediateDirectory)/config.h 159 | echo '#define PACKAGE_DESCRIPTION "SSDP announce daemon for UPnP 1.1"' >> $(IntermediateDirectory)/config.h 160 | echo '#define PACKAGE_URL "https://github.com/rozhuk-im/ssdpd"' >> $(IntermediateDirectory)/config.h 161 | echo '#define PACKAGE_STRING PACKAGE_NAME" "PACKAGE_VERSION' >> $(IntermediateDirectory)/config.h 162 | 163 | 164 | 165 | ./configure --enable-debug 166 | make clean 167 | make 168 | make clean 169 | ./configure --enable-debug 170 | make 171 | 172 | 173 | 174 | None 175 | $(ProjectPath) 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | mkdir $(ProjectPath)/$(ConfigurationName) && 206 | cd $(ProjectPath)/$(ConfigurationName) && 207 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=true .. 208 | 209 | 210 | 211 | rm -rf $(ProjectPath)/$(ConfigurationName) 212 | make -C $(ProjectPath)/$(ConfigurationName) -j`getconf NPROCESSORS_ONLN` 213 | 214 | 215 | 216 | None 217 | $(WorkspacePath) 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /php/upnp-server.conf: -------------------------------------------------------------------------------- 1 | ;;; Rozhuk Ivan 2014 - 2017 2 | ;;; FPM Configuration pool config 3 | ;;; 4 | 5 | [upnp-server] 6 | ;prefix = /path/to/pools/$pool 7 | 8 | user = www 9 | group = www 10 | 11 | listen = @RUNDIR@/php-fcgi-upnp-server.sock 12 | listen.owner = www 13 | listen.group = www 14 | listen.mode = 1777 15 | listen.backlog = -1 16 | ;listen.acl_users = 17 | ;listen.acl_groups = 18 | listen.allowed_clients = 127.0.0.1 19 | 20 | process.priority = 15 21 | 22 | pm = dynamic 23 | pm.max_children = 4 24 | pm.start_servers = 1 25 | pm.min_spare_servers = 1 26 | pm.max_spare_servers = 3 27 | pm.process_idle_timeout = 300s; 28 | pm.max_requests = 1000 29 | 30 | ;pm.status_path = /status 31 | 32 | ;ping.path = /ping 33 | ;ping.response = pong 34 | 35 | ;access.log = log/$pool.access.log 36 | ;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" 37 | ;slowlog = log/$pool.log.slow 38 | ;request_slowlog_timeout = 0 39 | ;request_terminate_timeout = 0 40 | 41 | catch_workers_output = yes 42 | 43 | ;rlimit_files = 1024 44 | ;rlimit_core = 0 45 | 46 | ;chroot = 47 | chdir = /tmp 48 | 49 | security.limit_extensions = .php .php3 .php4 .php5 50 | 51 | ; Default Value: clean env 52 | clear_env = yes 53 | env[HOSTNAME] = $HOSTNAME 54 | env[PATH] = /usr/local/bin:/usr/bin:/bin 55 | env[TMP] = /tmp 56 | env[TMPDIR] = /tmp 57 | env[TEMP] = /tmp 58 | 59 | 60 | ; Additional php.ini defines, specific to this pool of workers. These settings 61 | ; overwrite the values previously defined in the php.ini. The directives are the 62 | ; same as the PHP SAPI: 63 | ; php_value/php_flag - you can set classic ini defines which can 64 | ; be overwritten from PHP call 'ini_set'. 65 | ; php_admin_value/php_admin_flag - these directives won't be overwritten by 66 | ; PHP call 'ini_set' 67 | ; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. 68 | 69 | ;;;;;;;;;;;;;;;;;;;; 70 | ; php.ini Options ; 71 | ;;;;;;;;;;;;;;;;;;;; 72 | php_admin_value[user_ini.filename] = 73 | php_admin_value[user_ini.cache_ttl] = 0 74 | 75 | ;;;;;;;;;;;;;;;;;;;; 76 | ; Language Options ; 77 | ;;;;;;;;;;;;;;;;;;;; 78 | ;php_admin_flag[engine] = on 79 | php_admin_flag[short_open_tag] = off 80 | php_value[precision] = 14 81 | php_value[output_buffering] = 32768 82 | php_value[output_handler] = 83 | php_value[url_rewriter.tags] = "form=" 84 | php_value[url_rewriter.hosts] = 85 | php_flag[zlib.output_compression] = off 86 | php_value[zlib.output_compression_level] = 7 87 | php_flag[implicit_flush] = off 88 | php_value[unserialize_callback_func] = 89 | php_value[serialize_precision] = -1 90 | ;php_admin_value[open_basedir] = 91 | php_admin_value[disable_functions] = 92 | php_admin_value[disable_classes] = 93 | php_admin_flag[ignore_user_abort] = off 94 | php_admin_value[realpath_cache_size] = 8M 95 | php_admin_value[realpath_cache_ttl] = 120 96 | php_admin_flag[zend.enable_gc] = on 97 | php_flag[zend.multibyte] = off 98 | ;php_value[zend.script_encoding] = 99 | 100 | ;;;;;;;;;;;;;;;;; 101 | ; Miscellaneous ; 102 | ;;;;;;;;;;;;;;;;; 103 | php_admin_flag[expose_php] = off 104 | 105 | ;;;;;;;;;;;;;;;;;;; 106 | ; Resource Limits ; 107 | ;;;;;;;;;;;;;;;;;;; 108 | php_admin_value[max_execution_time] = 30 109 | php_admin_value[max_input_time] = 60 110 | php_admin_value[max_input_nesting_level] = 64 111 | php_value[max_input_vars] = 1000 112 | php_admin_value[memory_limit] = 128M 113 | 114 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 115 | ; Error handling and logging ; 116 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 117 | php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT 118 | php_admin_value[display_errors] = off 119 | php_admin_flag[display_startup_errors] = on 120 | php_admin_flag[log_errors] = on 121 | php_admin_value[log_errors_max_len] = 8192 122 | php_admin_flag[ignore_repeated_errors] = off 123 | php_admin_flag[ignore_repeated_source] = off 124 | php_admin_flag[report_memleaks] = on 125 | php_admin_flag[report_zend_debug] = off 126 | php_admin_flag[track_errors] = on 127 | ;php_admin_flag[xmlrpc_errors] = on 128 | ;php_admin_value[xmlrpc_error_number] = 0 129 | php_admin_flag[html_errors] = off 130 | ;php_admin_value[docref_root] = 131 | ;php_admin_value[docref_ext] = ".html" 132 | php_admin_value[error_prepend_string] = 133 | php_admin_value[error_append_string] = 134 | php_admin_value[error_log] = syslog 135 | ;php_admin_flag[windows.show_crt_warning] = off 136 | 137 | ;;;;;;;;;;;;;;;;; 138 | ; Data Handling ; 139 | ;;;;;;;;;;;;;;;;; 140 | php_value[arg_separator.output] = "&" 141 | php_value[arg_separator.input] = "&" 142 | php_value[variables_order] = "GPCS" 143 | php_value[request_order] = "GP" 144 | php_admin_flag[register_argc_argv] = on 145 | php_admin_flag[auto_globals_jit] = on 146 | php_admin_flag[enable_post_data_reading] = on 147 | php_admin_value[post_max_size] = 0 148 | php_admin_value[auto_prepend_file] = 149 | php_admin_value[auto_append_file] = 150 | php_admin_value[default_mimetype] = "text/html" 151 | php_admin_value[default_charset] = "UTF-8" 152 | php_value[internal_encoding] = 153 | php_value[input_encoding] = 154 | php_value[output_encoding] = 155 | 156 | ;;;;;;;;;;;;;;;;;;;;;;;;; 157 | ; Paths and Directories ; 158 | ;;;;;;;;;;;;;;;;;;;;;;;;; 159 | ;php_admin_value[include_path] = 160 | php_admin_value[doc_root] = 161 | php_admin_value[user_dir] = 162 | ;php_admin_value[extension_dir] = "ext" 163 | php_admin_value[sys_temp_dir] = "/tmp" 164 | php_admin_flag[enable_dl] = off 165 | php_admin_flag[cgi.force_redirect] = on 166 | php_admin_flag[cgi.nph] = off 167 | php_admin_value[cgi.redirect_status_env] = 168 | php_admin_flag[cgi.fix_pathinfo] = on 169 | php_admin_flag[cgi.discard_path] = on 170 | ;php_admin_flag[fastcgi.impersonate] = off 171 | php_admin_flag[fastcgi.logging] = on 172 | php_admin_flag[cgi.rfc2616_headers] = off 173 | ;php_admin_flag[cgi.check_shebang_line] = off 174 | 175 | ;;;;;;;;;;;;;;;; 176 | ; File Uploads ; 177 | ;;;;;;;;;;;;;;;; 178 | php_admin_flag[file_uploads] = off 179 | 180 | ;;;;;;;;;;;;;;;;;; 181 | ; Fopen wrappers ; 182 | ;;;;;;;;;;;;;;;;;; 183 | php_admin_flag[allow_url_fopen] = on 184 | php_admin_flag[allow_url_include] = off 185 | php_value[from] = "john@doe.com" 186 | php_value[user_agent] = 187 | php_value[default_socket_timeout] = 60 188 | php_flag[auto_detect_line_endings] = off 189 | 190 | ;;;;;;;;;;;;;;;;;;; 191 | ; Module Settings ; 192 | ;;;;;;;;;;;;;;;;;;; 193 | ;php_flag[cli_server.color] = on 194 | 195 | php_value[date.timezone] = "UTC" 196 | ;php_value[date.default_latitude] = 197 | ;php_value[date.default_longitude] = 198 | ;php_value[date.sunrise_zenith] = 199 | ;php_value[date.sunset_zenith] = 200 | 201 | ;php_admin_value[filter.default] = unsafe_raw 202 | ;php_admin_value[filter.default_flags] = 203 | 204 | ;php_value[iconv.input_encoding] = 205 | ;php_value[iconv.internal_encoding] = 206 | ;php_value[iconv.output_encoding] = 207 | 208 | ;php_admin_value[intl.default_locale] = 209 | ;php_admin_value[intl.error_level] = E_WARNING 210 | ;php_admin_value[intl.use_exceptions] = 211 | 212 | php_value[pcre.backtrack_limit] = 100000 213 | php_value[pcre.recursion_limit] = 100000 214 | php_flag[pcre.jit] = off 215 | 216 | php_admin_value[session.save_handler] = files 217 | php_admin_value[session.save_path] = "/tmp" 218 | php_admin_flag[session.use_strict_mode] = on 219 | php_admin_flag[session.use_cookies] = on 220 | ;php_admin_value[session.cookie_secure] = 221 | php_admin_flag[session.use_only_cookies] = on 222 | php_value[session.name] = PHPSESSID 223 | php_flag[session.auto_start] = off 224 | php_value[session.cookie_lifetime] = 0 225 | php_value[session.cookie_path] = / 226 | php_value[session.cookie_domain] = 227 | php_flag[session.cookie_httponly] = on 228 | php_admin_value[session.serialize_handler] = php 229 | php_admin_value[session.gc_probability] = 1 230 | php_admin_value[session.gc_divisor] = 1000 231 | php_admin_value[session.gc_maxlifetime] = 3600 232 | php_value[session.referer_check] = 233 | php_value[session.cache_limiter] = nocache 234 | php_value[session.cache_expire] = 180 235 | php_admin_flag[session.use_trans_sid] = off 236 | php_admin_value[session.sid_length] = 128 237 | php_admin_value[session.trans_sid_tags] = "a=href,area=href,frame=src,form=" 238 | php_admin_value[session.trans_sid_hosts] = 239 | php_admin_value[session.sid_bits_per_character] = 5 240 | php_admin_flag[session.upload_progress.enabled] = on 241 | php_admin_flag[session.upload_progress.cleanup] = on 242 | php_admin_value[session.upload_progress.prefix] = "upload_progress_" 243 | php_admin_value[session.upload_progress.name] = "PHP_SESSION_UPLOAD_PROGRESS" 244 | php_value[session.upload_progress.freq] = "1%" 245 | php_value[session.upload_progress.min_freq] = "1" 246 | php_admin_flag[session.lazy_write] = on 247 | 248 | php_admin_value[zend.assertions] = -1 249 | php_admin_flag[assert.active] = on 250 | php_admin_flag[assert.exception] = on 251 | php_admin_flag[assert.warning] = on 252 | php_admin_flag[assert.bail] = off 253 | php_admin_value[assert.callback] = 0 254 | php_admin_value[assert.quiet_eval] = 0 255 | 256 | php_value[mbstring.language] = 257 | php_value[mbstring.internal_encoding] = 258 | php_value[mbstring.http_input] = 259 | php_value[mbstring.http_output] = 260 | php_flag[mbstring.encoding_translation] = off 261 | php_value[mbstring.detect_order] = auto 262 | php_value[mbstring.substitute_character] = none 263 | php_value[mbstring.func_overload] = 0 264 | php_flag[mbstring.strict_detection] = on 265 | ;php_value[mbstring.http_output_conv_mimetype] = none 266 | 267 | php_flag[gd.jpeg_ignore_warning] = on 268 | 269 | ;php_admin_value[exif.encode_unicode] = ISO-8859-15 270 | ;php_admin_value[exif.decode_unicode_motorola] = UCS-2BE 271 | ;php_admin_value[exif.decode_unicode_intel] = UCS-2LE 272 | ;php_admin_value[exif.encode_jis] = 273 | ;php_admin_value[exif.decode_jis_motorola] = JIS 274 | ;php_admin_value[exif.decode_jis_intel] = JIS 275 | 276 | ;php_admin_value[tidy.default_config] = /usr/local/lib/php/default.tcfg 277 | php_flag[tidy.clean_output] = off 278 | 279 | php_flag[soap.wsdl_cache_enabled] = on 280 | php_admin_value[soap.wsdl_cache_dir] = "/tmp" 281 | php_value[soap.wsdl_cache_ttl] = 86400 282 | php_value[soap.wsdl_cache] = 2 283 | php_value[soap.wsdl_cache_limit] = 1024 284 | 285 | php_value[sysvshm.init_mem] = 16384 286 | 287 | php_admin_flag[opcache.enable] = on 288 | php_admin_flag[opcache.enable_cli] = off 289 | php_admin_value[opcache.memory_consumption] = 256 290 | php_admin_value[opcache.interned_strings_buffer]= 16 291 | php_admin_value[opcache.max_accelerated_files] = 16384 292 | php_admin_value[opcache.max_wasted_percentage] = 5 293 | php_admin_flag[opcache.use_cwd] = on 294 | php_flag[opcache.validate_timestamps] = off 295 | php_value[opcache.revalidate_freq] = 4 296 | php_admin_flag[opcache.revalidate_path] = on 297 | php_admin_flag[opcache.save_comments] = on 298 | php_admin_flag[opcache.fast_shutdown] = off 299 | php_admin_flag[opcache.enable_file_override] = off 300 | php_value[opcache.optimization_level] = 0xffffffff 301 | ;php_admin_flag[opcache.opcache.inherited_hack] = on 302 | ;php_admin_flag[opcache.dups_fix] = off 303 | php_value[opcache.blacklist_filename] = 304 | php_value[opcache.max_file_size] = 0 305 | php_admin_value[opcache.consistency_checks] = 0 306 | php_admin_value[opcache.force_restart_timeout] = 60 307 | php_admin_value[opcache.error_log] = "stderr" 308 | php_admin_value[opcache.log_verbosity_level] = 2 309 | ;php_admin_value[opcache.preferred_memory_model]= 310 | ;php_admin_flag[opcache.protect_memory] = on 311 | php_value[opcache.restrict_api] = 312 | ;php_admin_value[opcache.mmap_base] = 313 | ;php_admin_value[opcache.file_cache] = 314 | php_admin_flag[opcache.file_cache_only] = off 315 | php_admin_flag[opcache.file_cache_consistency_checks]= on 316 | php_admin_flag[opcache.file_cache_fallback] = on 317 | php_admin_flag[opcache.huge_code_pages] = on 318 | php_admin_flag[opcache.validate_permission] = off 319 | php_admin_flag[opcache.validate_root] = off 320 | 321 | -------------------------------------------------------------------------------- /www/upnp/descr/ContentDirectory.wdsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /src/ssdpd.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011-2024 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include /* snprintf, fprintf */ 38 | #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ 39 | #include /* malloc, exit */ 40 | #include /* close, write, sysconf */ 41 | #include /* open, fcntl */ 42 | #include /* SIGNAL constants. */ 43 | #include 44 | 45 | #include "utils/macro.h" 46 | #include "utils/mem_utils.h" 47 | #include "utils/str2num.h" 48 | #include "utils/xml.h" 49 | #include "utils/buf_str.h" 50 | #include "utils/sys.h" 51 | #include "utils/cmd_line_daemon.h" 52 | #include "net/socket_address.h" 53 | #include "net/utils.h" 54 | #include "proto/upnp_ssdp.h" 55 | #include "config.h" 56 | 57 | #define CFG_FILE_MAX_SIZE (1024 * 1024) 58 | 59 | 60 | #define SSDPD_CFG_CALC_VAL_COUNT(args...) \ 61 | xml_calc_tag_count_args(cfg_file_buf, cfg_file_buf_size, \ 62 | (const uint8_t*)"ssdpd", ##args) 63 | #define SSDPD_CFG_GET_VAL_DATA(next_pos, data, data_size, args...) \ 64 | xml_get_val_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 65 | NULL, NULL, data, data_size, (const uint8_t*)"ssdpd", ##args) 66 | #define SSDPD_CFG_GET_VAL_UINT(next_pos, val_ret, args...) \ 67 | xml_get_val_uint32_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 68 | val_ret, (const uint8_t*)"ssdpd", ##args) 69 | #define SSDPD_CFG_GET_VAL_SIZE(next_pos, val_ret, args...) \ 70 | xml_get_val_size_t_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 71 | val_ret, (const uint8_t*)"ssdpd", ##args) 72 | 73 | 74 | int 75 | main(int argc, char *argv[]) { 76 | int error; 77 | tp_p tp = NULL; 78 | upnp_ssdp_p upnp_ssdp = NULL; /* UPnP SSDP server. */ 79 | cmd_line_data_t cmd_line_data; 80 | 81 | 82 | if (0 != cmd_line_parse(argc, argv, &cmd_line_data)) { 83 | cmd_line_usage(PACKAGE_DESCRIPTION, PACKAGE_VERSION, 84 | "Rozhuk Ivan ", 85 | PACKAGE_URL); 86 | return (0); 87 | } 88 | if (0 != cmd_line_data.daemon) { 89 | make_daemon(); 90 | openlog(PACKAGE_NAME, 91 | (LOG_NDELAY | LOG_PID | ((0 != cmd_line_data.verbose) ? LOG_PERROR : 0)), 92 | LOG_DAEMON); 93 | } else { 94 | openlog(PACKAGE_NAME, 95 | (LOG_NDELAY | LOG_PID | LOG_PERROR), LOG_USER); 96 | } 97 | setlogmask(LOG_UPTO(cmd_line_data.log_level)); 98 | 99 | { /* Process config file. */ 100 | const uint8_t *data, *ptm, *cur_pos, *cur_svc_pos, *cur_if_pos; 101 | const uint8_t *ann_data, *svc_data; 102 | uint8_t *cfg_file_buf, *cfg_dev_buf; 103 | size_t cfg_file_buf_size, cfg_dev_buf_size; 104 | size_t tm, data_size, ann_data_size, svc_data_size; 105 | const uint8_t *uuid, *domain_name, *type; 106 | size_t domain_name_size, type_size; 107 | uint32_t ver, config_id, max_age, ann_interval; 108 | upnp_ssdp_dev_p dev; 109 | const uint8_t *if_name, *url4, *url6; 110 | size_t if_name_size, url4_size, url6_size; 111 | upnp_ssdp_settings_t ssdp_st; 112 | uint8_t url4_buf[1024], url6_buf[1024]; 113 | struct sockaddr_storage addr; 114 | tp_settings_t tp_s; 115 | 116 | error = read_file(cmd_line_data.cfg_file_name, 0, 0, 0, 117 | CFG_FILE_MAX_SIZE, &cfg_file_buf, &cfg_file_buf_size); 118 | if (0 != error) { 119 | SYSLOG_ERR(LOG_CRIT, error, "Config read_file()."); 120 | goto err_out; 121 | } 122 | if (0 != xml_get_val_args(cfg_file_buf, cfg_file_buf_size, 123 | NULL, NULL, NULL, NULL, NULL, 124 | (const uint8_t*)"ssdpd", NULL)) { 125 | syslog(LOG_CRIT, "Config file XML format invalid."); 126 | goto err_out; 127 | } 128 | 129 | /* Log level. */ 130 | if (0 == SSDPD_CFG_GET_VAL_UINT(NULL, (uint32_t*)&cmd_line_data.log_level, 131 | "log", "level", NULL)) { 132 | setlogmask(LOG_UPTO(cmd_line_data.log_level)); 133 | } 134 | syslog(LOG_NOTICE, PACKAGE_STRING": started!"); 135 | #ifdef DEBUG 136 | syslog(LOG_INFO, "Build: "__DATE__" "__TIME__", DEBUG."); 137 | #else 138 | syslog(LOG_INFO, "Build: "__DATE__" "__TIME__", Release."); 139 | #endif 140 | syslog(LOG_INFO, "CPU count: %d.", get_cpu_count()); 141 | syslog(LOG_INFO, "Descriptor table size: %d (max files).", getdtablesize()); 142 | 143 | /* Thread pool settings. */ 144 | tp_settings_def(&tp_s); 145 | tp_s.flags = 0; 146 | tp_s.threads_max = 1; 147 | error = tp_create(&tp_s, &tp); 148 | if (0 != error) { 149 | SYSLOG_ERR(LOG_CRIT, error, "tp_create()."); 150 | goto err_out; 151 | } 152 | tp_threads_create(tp, 1); /* XXX exit rewrite. */ 153 | 154 | 155 | /* SSDP receiver. */ 156 | if (0 == SSDPD_CFG_CALC_VAL_COUNT("announceList", "announce", NULL)) { 157 | syslog(LOG_NOTICE, "No announce devices specified, nothink to do..."); 158 | goto err_out; 159 | } 160 | /* Default values. */ 161 | upnp_ssdp_def_settings(&ssdp_st); 162 | /* Read from config. */ 163 | if (0 == SSDPD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 164 | "skt", NULL)) { 165 | skt_opts_xml_load(data, data_size, 166 | UPNP_SSDP_S_SKT_OPTS_LOAD_MASK, &ssdp_st.skt_opts); 167 | } 168 | ssdp_st.flags = 0; 169 | if (0 == SSDPD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 170 | "fEnableIPv4", NULL)) { 171 | yn_set_flag32(data, data_size, UPNP_SSDP_S_F_IPV4, 172 | &ssdp_st.flags); 173 | } 174 | if (0 == SSDPD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 175 | "fEnableIPv6", NULL)) { 176 | yn_set_flag32(data, data_size, UPNP_SSDP_S_F_IPV6, 177 | &ssdp_st.flags); 178 | } 179 | if (0 == SSDPD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 180 | "fEnableByebye", NULL)) { 181 | yn_set_flag32(data, data_size, UPNP_SSDP_S_F_BYEBYE, 182 | &ssdp_st.flags); 183 | } 184 | if (0 == SSDPD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 185 | "httpServer", NULL) && 186 | 0 != data_size && 187 | (sizeof(ssdp_st.http_server) - 1) > data_size) { 188 | ssdp_st.http_server_size = data_size; 189 | memcpy(ssdp_st.http_server, data, data_size); 190 | } 191 | /* Create SSDP receiver. */ 192 | error = upnp_ssdp_create(tp, &ssdp_st, &upnp_ssdp); 193 | if (0 != error) { 194 | SYSLOG_ERR(LOG_CRIT, error, "upnp_ssdp_create()."); 195 | return (error); 196 | } 197 | 198 | /* Add Devices to announce on selected ifaces. */ 199 | cur_pos = NULL; 200 | while (0 == SSDPD_CFG_GET_VAL_DATA(&cur_pos, &ann_data, &ann_data_size, 201 | "announceList", "announce", NULL)) { 202 | if (0 == xml_calc_tag_count_args(ann_data, ann_data_size, 203 | (const uint8_t*)"ifList", "if", NULL)) 204 | continue; /* No network interfaces specified. */ 205 | if (0 != xml_get_val_args(ann_data, ann_data_size, NULL, 206 | NULL, NULL, &data, &data_size, 207 | (const uint8_t*)"xmlDevDescr", NULL)) 208 | continue; /* No xml file with UPnP dev description. */ 209 | error = read_file((const char*)data, data_size, 0, 0, 210 | CFG_FILE_MAX_SIZE, &cfg_dev_buf, &cfg_dev_buf_size); 211 | if (0 != error) { 212 | SYSLOG_ERR(LOG_ERR, error, "xmlDevDescr read_file()."); 213 | continue; 214 | } 215 | /* Load device options and add. */ 216 | if (0 != xml_get_val_args(cfg_dev_buf, cfg_dev_buf_size, 217 | NULL, NULL, NULL, &uuid, &tm, 218 | (const uint8_t*)"root", "device", "UDN", NULL) || 219 | (5 + 36) != tm) { /* 5 = "uuid:", 36 = UPNP_UUID_SIZE */ 220 | syslog(LOG_ERR, "Invalid device UUID."); 221 | free(cfg_dev_buf); 222 | continue; 223 | } 224 | uuid += 5; /* Skip "uuid:". */ 225 | if (0 != xml_get_val_args(cfg_dev_buf, cfg_dev_buf_size, 226 | NULL, NULL, NULL, &data, &data_size, 227 | (const uint8_t*)"root", "device", "deviceType", NULL)) { 228 | no_dev_type: 229 | syslog(LOG_ERR, "No deviceType."); 230 | free(cfg_dev_buf); 231 | continue; 232 | } 233 | /* Parce: "urn:schemas-upnp-org:device:MediaServer:3". */ 234 | domain_name = (data + 4); /* Skip "urn:". */ 235 | ptm = mem_chr_off(5, data, data_size, ':'); 236 | if (NULL == ptm) 237 | goto no_dev_type; 238 | domain_name_size = (size_t)(ptm - domain_name); 239 | type = (ptm + 8); /* Skip ":device:". */ 240 | ptm = mem_chr_ptr(type, data, data_size, ':'); 241 | if (NULL == ptm) 242 | goto no_dev_type; 243 | type_size = (size_t)(ptm - type); 244 | ptm += 1; /* Skip ":". */ 245 | ver = ustr2u32(ptm, (size_t)(data_size - (size_t)(ptm - data))); 246 | 247 | config_id = 1; /* XXX not read. */ 248 | max_age = 0; 249 | ann_interval = 0; 250 | xml_get_val_uint32_args(ann_data, ann_data_size, NULL, 251 | &max_age, (const uint8_t*)"maxAge", NULL); 252 | xml_get_val_uint32_args(ann_data, ann_data_size, NULL, 253 | &ann_interval, (const uint8_t*)"interval", NULL); 254 | /* Add device. */ 255 | error = upnp_ssdp_dev_add(upnp_ssdp, (const char*)uuid, 256 | (const char*)domain_name, domain_name_size, 257 | (const char*)type, type_size, ver, 258 | (uint32_t)time(NULL), config_id, max_age, 259 | ann_interval, &dev); 260 | if (0 != error) { 261 | SYSLOG_ERR(LOG_ERR, error, "upnp_ssdp_dev_add()."); 262 | free(cfg_dev_buf); 263 | continue; 264 | } 265 | /* Load services options and add. */ 266 | cur_svc_pos = NULL; 267 | while (0 == xml_get_val_args(cfg_dev_buf, cfg_dev_buf_size, 268 | &cur_svc_pos, NULL, NULL, &svc_data, &svc_data_size, 269 | (const uint8_t*)"root", "device", "serviceList", "service", NULL)) { 270 | if (0 != xml_get_val_args(svc_data, svc_data_size, NULL, 271 | NULL, NULL, &data, &data_size, 272 | (const uint8_t*)"serviceType", NULL)) { 273 | no_svc_type: 274 | syslog(LOG_ERR, "No serviceType."); 275 | continue; 276 | } 277 | /* Parse: "urn:schemas-upnp-org:service:ContentDirectory:3". */ 278 | domain_name = (data + 4); /* Skip "urn:". */ 279 | ptm = mem_chr_off(5, data, data_size, ':'); 280 | if (NULL == ptm) 281 | goto no_svc_type; 282 | domain_name_size = (size_t)(ptm - domain_name); 283 | type = (ptm + 9); /* Skip ":service:". */ 284 | ptm = mem_chr_ptr(type, data, data_size, ':'); 285 | if (NULL == ptm) 286 | goto no_svc_type; 287 | type_size = (size_t)(ptm - type); 288 | ptm += 1; /* Skip ":". */ 289 | ver = ustr2u32(ptm, (size_t)(data_size - (size_t)(ptm - data))); 290 | /* Add service to device. */ 291 | error = upnp_ssdp_svc_add(dev, 292 | (const char*)domain_name, domain_name_size, 293 | (const char*)type, type_size, ver); 294 | if (0 != error) { 295 | SYSLOG_ERR(LOG_ERR, error, "upnp_ssdp_svc_add()."); 296 | continue; 297 | } 298 | } 299 | free(cfg_dev_buf); 300 | /* UPnP Device and services added, now add network interfaces. */ 301 | cur_if_pos = NULL; 302 | while (0 == xml_get_val_args(ann_data, ann_data_size, &cur_if_pos, 303 | NULL, NULL, &data, &data_size, 304 | (const uint8_t*)"ifList", "if", NULL)) { 305 | url4_size = 0; 306 | url6_size = 0; 307 | xml_get_val_args(data, data_size, NULL, NULL, NULL, 308 | &url4, &url4_size, 309 | (const uint8_t*)"DevDescrURL4", NULL); 310 | xml_get_val_args(data, data_size, NULL, NULL, NULL, 311 | &url6, &url6_size, 312 | (const uint8_t*)"DevDescrURL6", NULL); 313 | if (0 != xml_get_val_args(data, data_size, NULL, NULL, NULL, 314 | &if_name, &if_name_size, 315 | (const uint8_t*)"ifName", NULL) || 316 | (0 == url4_size && 0 == url6_size)) { 317 | syslog(LOG_ERR, "Bad device network interface."); 318 | continue; 319 | } 320 | /* Autoreplace NULL addr to if addr. */ 321 | if (14 < url4_size && /* http://0.0.0.0... */ 322 | 0 == mem_cmp_cstr("0.0.0.0", (url4 + 7)) && 323 | (':' == url4[14] || '\\' == url4[14])) { 324 | syslog(LOG_INFO, "Autoreplace NULL IPv4 addr to if addr."); 325 | error = get_if_addr_by_name((const char*)if_name, 326 | if_name_size, AF_INET, &addr); 327 | if (0 != error) { 328 | SYSLOG_ERR(LOG_ERR, error, "get_if_addr_by_name()."); 329 | continue; 330 | } 331 | error = sa_addr_to_str(&addr, 332 | (char*)(url4_buf + 7), 333 | (sizeof(url4_buf) - 8), &tm); 334 | if (0 != error) { 335 | SYSLOG_ERR(LOG_ERR, error, "sa_addr_to_str()."); 336 | continue; 337 | } 338 | if (sizeof(url4_buf) > (url4_size + (tm - 7))) { 339 | memcpy(url4_buf, "http://", 7); 340 | memcpy((url4_buf + 7 + tm), 341 | (url4 + 14), 342 | (url4_size - 14)); 343 | url4 = url4_buf; 344 | url4_size += (tm - 7); 345 | url4_buf[url4_size] = 0; 346 | syslog(LOG_INFO, "%s", (const char*)url4); 347 | } else { 348 | syslog(LOG_ERR, "URL to long, not enough buf space."); 349 | url4 = NULL; 350 | url4_size = 0; 351 | } 352 | } 353 | if (13 < url6_size && /* http://[::]... */ 354 | 0 == mem_cmp_cstr("[::]", (url6 + 7)) && 355 | (':' == url6[11] || '\\' == url6[11])) { 356 | syslog(LOG_INFO, "Autoreplace NULL IPv6 addr to if addr."); 357 | error = get_if_addr_by_name((const char*)if_name, 358 | if_name_size, AF_INET6, &addr); 359 | if (0 != error) { 360 | SYSLOG_ERR(LOG_ERR, error, "get_if_addr_by_name()."); 361 | continue; 362 | } 363 | error = sa_addr_to_str(&addr, 364 | (char*)(url6_buf + 7), 365 | (sizeof(url6_buf) - 8), &tm); 366 | if (0 != error) { 367 | SYSLOG_ERR(LOG_ERR, error, "sa_addr_to_str()."); 368 | continue; 369 | } 370 | if (sizeof(url6_buf) > (url6_size + (tm - 7))) { 371 | memcpy(url6_buf, "http://", 7); 372 | memcpy((url6_buf + 7 + tm), 373 | (url6 + 11), 374 | (url6_size - 11)); 375 | url6 = url6_buf; 376 | url6_size += (tm - 4); 377 | url6_buf[url6_size] = 0; 378 | syslog(LOG_INFO, "%s", (const char*)url6); 379 | } else { 380 | syslog(LOG_ERR, "URL to long, not enough buf space."); 381 | url6 = NULL; 382 | url6_size = 0; 383 | } 384 | } 385 | /* Add service to device. */ 386 | error = upnp_ssdp_dev_if_add(upnp_ssdp, dev, 387 | (const char*)if_name, if_name_size, 388 | (const char*)url4, url4_size, 389 | (const char*)url6, url6_size); 390 | if (0 != error) { 391 | SYSLOG_ERR(LOG_WARNING, error, "upnp_ssdp_dev_if_add()."); 392 | continue; 393 | } 394 | } 395 | } 396 | free(cfg_file_buf); 397 | upnp_ssdp_send_notify(upnp_ssdp); 398 | } /* Done with config. */ 399 | 400 | if (0 == upnp_ssdp_root_dev_count(upnp_ssdp) || 401 | 0 == upnp_ssdp_if_count(upnp_ssdp)) { 402 | syslog(LOG_NOTICE, "No announce devices specified or no network ifaces, nothink to do..."); 403 | goto err_out; 404 | } 405 | 406 | tp_signal_handler_add_tp(tp); 407 | signal_install(tp_signal_handler); 408 | 409 | write_pid(cmd_line_data.pid_file_name); /* Store pid to file. */ 410 | set_user_and_group(cmd_line_data.pw_uid, cmd_line_data.pw_gid); /* Drop rights. */ 411 | 412 | /* Receive and process packets. */ 413 | tp_thread_attach_first(tp); 414 | tp_shutdown_wait(tp); 415 | 416 | err_out: 417 | /* Deinitialization... */ 418 | upnp_ssdp_destroy(upnp_ssdp); 419 | if (NULL != cmd_line_data.pid_file_name) { 420 | unlink(cmd_line_data.pid_file_name); /* Remove pid file. */ 421 | } 422 | tp_destroy(tp); 423 | syslog(LOG_NOTICE, "Exiting."); 424 | closelog(); 425 | 426 | return (error); 427 | } 428 | 429 | -------------------------------------------------------------------------------- /www/upnp/descr/ContentDirectory.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | 8 | 9 | GetSearchCapabilities 10 | 11 | 12 | SearchCaps 13 | out 14 | SearchCapabilities 15 | 16 | 17 | 18 | 19 | GetSortCapabilities 20 | 21 | 22 | SortCaps 23 | out 24 | SortCapabilities 25 | 26 | 27 | 28 | 29 | GetSortExtensionCapabilities 30 | 31 | 32 | SortExtensionCaps 33 | out 34 | SortExtensionCapabilities 35 | 36 | 37 | 38 | 39 | GetFeatureList 40 | 41 | 42 | FeatureList 43 | out 44 | FeatureList 45 | 46 | 47 | 48 | 49 | GetSystemUpdateID 50 | 51 | 52 | Id 53 | out 54 | SystemUpdateID 55 | 56 | 57 | 58 | 59 | GetServiceResetToken 60 | 61 | 62 | ResetToken 63 | out 64 | ServiceResetToken 65 | 66 | 67 | 68 | 69 | Browse 70 | 71 | 72 | ObjectID 73 | in 74 | A_ARG_TYPE_ObjectID 75 | 76 | 77 | BrowseFlag 78 | in 79 | A_ARG_TYPE_BrowseFlag 80 | 81 | 82 | Filter 83 | in 84 | A_ARG_TYPE_Filter 85 | 86 | 87 | StartingIndex 88 | in 89 | A_ARG_TYPE_Index 90 | 91 | 92 | RequestedCount 93 | in 94 | A_ARG_TYPE_Count 95 | 96 | 97 | SortCriteria 98 | in 99 | A_ARG_TYPE_SortCriteria 100 | 101 | 102 | Result 103 | out 104 | A_ARG_TYPE_Result 105 | 106 | 107 | NumberReturned 108 | out 109 | A_ARG_TYPE_Count 110 | 111 | 112 | TotalMatches 113 | out 114 | A_ARG_TYPE_Count 115 | 116 | 117 | UpdateID 118 | out 119 | A_ARG_TYPE_UpdateID 120 | 121 | 122 | 123 | 124 | Search 125 | 126 | 127 | ContainerID 128 | in 129 | A_ARG_TYPE_ObjectID 130 | 131 | 132 | SearchCriteria 133 | in 134 | A_ARG_TYPE_SearchCriteria 135 | 136 | 137 | Filter 138 | in 139 | A_ARG_TYPE_Filter 140 | 141 | 142 | StartingIndex 143 | in 144 | A_ARG_TYPE_Index 145 | 146 | 147 | RequestedCount 148 | in 149 | A_ARG_TYPE_Count 150 | 151 | 152 | SortCriteria 153 | in 154 | A_ARG_TYPE_SortCriteria 155 | 156 | 157 | Result 158 | out 159 | A_ARG_TYPE_Result 160 | 161 | 162 | NumberReturned 163 | out 164 | A_ARG_TYPE_Count 165 | 166 | 167 | TotalMatches 168 | out 169 | A_ARG_TYPE_Count 170 | 171 | 172 | UpdateID 173 | out 174 | A_ARG_TYPE_UpdateID 175 | 176 | 177 | 178 | 179 | CreateObject 180 | 181 | 182 | ContainerID 183 | in 184 | A_ARG_TYPE_ObjectID 185 | 186 | 187 | Elements 188 | in 189 | A_ARG_TYPE_Result 190 | 191 | 192 | ObjectID 193 | out 194 | A_ARG_TYPE_ObjectID 195 | 196 | 197 | Result 198 | out 199 | A_ARG_TYPE_Result 200 | 201 | 202 | 203 | 204 | DestroyObject 205 | 206 | 207 | ObjectID 208 | in 209 | A_ARG_TYPE_ObjectID 210 | 211 | 212 | 213 | 214 | UpdateObject 215 | 216 | 217 | ObjectID 218 | in 219 | A_ARG_TYPE_ObjectID 220 | 221 | 222 | CurrentTagValue 223 | in 224 | A_ARG_TYPE_TagValueList 225 | 226 | 227 | NewTagValue 228 | in 229 | A_ARG_TYPE_TagValueList 230 | 231 | 232 | 233 | 234 | MoveObject 235 | 236 | 237 | ObjectID 238 | in 239 | A_ARG_TYPE_ObjectID 240 | 241 | 242 | NewParentID 243 | in 244 | A_ARG_TYPE_ObjectID 245 | 246 | 247 | NewObjectID 248 | out 249 | A_ARG_TYPE_ObjectID 250 | 251 | 252 | 253 | 254 | ImportResource 255 | 256 | 257 | SourceURI 258 | in 259 | A_ARG_TYPE_URI 260 | 261 | 262 | DestinationURI 263 | in 264 | A_ARG_TYPE_URI 265 | 266 | 267 | TransferID 268 | out 269 | A_ARG_TYPE_TransferID 270 | 271 | 272 | 273 | 274 | ExportResource 275 | 276 | 277 | SourceURI 278 | in 279 | A_ARG_TYPE_URI 280 | 281 | 282 | DestinationURI 283 | in 284 | A_ARG_TYPE_URI 285 | 286 | 287 | TransferID 288 | out 289 | A_ARG_TYPE_TransferID 290 | 291 | 292 | 293 | 294 | StopTransferResource 295 | 296 | 297 | TransferID 298 | in 299 | A_ARG_TYPE_TransferID 300 | 301 | 302 | 303 | 304 | DeleteResource 305 | 306 | 307 | ResourceURI 308 | in 309 | A_ARG_TYPE_URI 310 | 311 | 312 | 313 | 314 | GetTransferProgress 315 | 316 | 317 | TransferID 318 | in 319 | A_ARG_TYPE_TransferID 320 | 321 | 322 | TransferStatus 323 | out 324 | A_ARG_TYPE_TransferStatus 325 | 326 | 327 | TransferLength 328 | out 329 | A_ARG_TYPE_TransferLength 330 | 331 | 332 | TransferTotal 333 | out 334 | A_ARG_TYPE_TransferTotal 335 | 336 | 337 | 338 | 339 | CreateReference 340 | 341 | 342 | ContainerID 343 | in 344 | A_ARG_TYPE_ObjectID 345 | 346 | 347 | ObjectID 348 | in 349 | A_ARG_TYPE_ObjectID 350 | 351 | 352 | NewID 353 | out 354 | A_ARG_TYPE_ObjectID 355 | 356 | 357 | 358 | 359 | FreeFormQuery 360 | 361 | 362 | ContainerID 363 | in 364 | A_ARG_TYPE_ObjectID 365 | 366 | 367 | CDSView 368 | in 369 | A_ARG_TYPE_CDSView 370 | 371 | 372 | QueryRequest 373 | in 374 | A_ARG_TYPE_QueryRequest 375 | 376 | 377 | QueryResult 378 | out 379 | A_ARG_TYPE_QueryResult 380 | 381 | 382 | UpdateID 383 | out 384 | A_ARG_TYPE_UpdateID 385 | 386 | 387 | 388 | 389 | GetFreeFormQueryCapabilities 390 | 391 | 392 | FFQCapabilities 393 | out 394 | A_ARG_TYPE_FFQCapabilities 395 | 396 | 397 | 398 | 399 | X_GetFeatureList_DISABLED 400 | 401 | 402 | FeatureList 403 | out 404 | A_ARG_TYPE_Featurelist 405 | 406 | 407 | 408 | 409 | X_SetBookmark 410 | 411 | 412 | CategoryType 413 | in 414 | A_ARG_TYPE_CategoryType 415 | 416 | 417 | RID 418 | in 419 | A_ARG_TYPE_RID 420 | 421 | 422 | ObjectID 423 | in 424 | A_ARG_TYPE_ObjectID 425 | 426 | 427 | PosSecond 428 | in 429 | A_ARG_TYPE_PosSec 430 | 431 | 432 | 433 | 434 | 435 | 436 | SearchCapabilities 437 | string 438 | 439 | 440 | SortCapabilities 441 | string 442 | 443 | 444 | SortExtensionCapabilities 445 | string 446 | 447 | 448 | SystemUpdateID 449 | ui4 450 | 451 | 452 | ContainerUpdateIDs 453 | string 454 | 455 | 456 | ServiceResetToken 457 | string 458 | 459 | 460 | LastChange 461 | string 462 | 463 | 464 | TransferIDs 465 | string 466 | 467 | 468 | FeatureList 469 | string 470 | 471 | 472 | A_ARG_TYPE_ObjectID 473 | string 474 | 475 | 476 | A_ARG_TYPE_Result 477 | string 478 | 479 | 480 | A_ARG_TYPE_SearchCriteria 481 | string 482 | 483 | 484 | A_ARG_TYPE_BrowseFlag 485 | string 486 | 487 | BrowseMetadata 488 | BrowseDirectChildren 489 | 490 | 491 | 492 | A_ARG_TYPE_Filter 493 | string 494 | 495 | 496 | A_ARG_TYPE_SortCriteria 497 | string 498 | 499 | 500 | A_ARG_TYPE_Index 501 | ui4 502 | 503 | 504 | A_ARG_TYPE_Count 505 | ui4 506 | 507 | 508 | A_ARG_TYPE_UpdateID 509 | ui4 510 | 511 | 512 | A_ARG_TYPE_TransferID 513 | ui4 514 | 515 | 516 | A_ARG_TYPE_TransferStatus 517 | string 518 | 519 | COMPLETED 520 | ERROR 521 | IN_PROGRESS 522 | STOPPED 523 | 524 | 525 | 526 | A_ARG_TYPE_TransferLength 527 | string 528 | 529 | 530 | A_ARG_TYPE_TransferTotal 531 | string 532 | 533 | 534 | A_ARG_TYPE_TagValueList 535 | string 536 | 537 | 538 | A_ARG_TYPE_URI 539 | uri 540 | 541 | 542 | A_ARG_TYPE_CDSView 543 | ui4 544 | 545 | 546 | A_ARG_TYPE_QueryRequest 547 | string 548 | 549 | 550 | A_ARG_TYPE_QueryResult 551 | string 552 | 553 | 554 | A_ARG_TYPE_FFQCapabilities 555 | string 556 | 557 | 558 | A_ARG_TYPE_Featurelist 559 | string 560 | 561 | 562 | A_ARG_TYPE_CategoryType 563 | ui4 564 | 565 | 566 | A_ARG_TYPE_RID 567 | ui4 568 | 569 | 570 | A_ARG_TYPE_PosSec 571 | ui4 572 | 573 | 574 | 575 | -------------------------------------------------------------------------------- /www/upnp/control/ContentDirectory.php: -------------------------------------------------------------------------------- 1 | 4 | * All rights reserved. 5 | * 6 | * Subject to the following obligations and disclaimer of warranty, use and 7 | * redistribution of this software, in source or object code forms, with or 8 | * without modifications are expressly permitted by Whistle Communications; 9 | * provided, however, that: 10 | * 1. Any and all reproductions of the source or object code must include the 11 | * copyright notice above and the following disclaimer of warranties; and 12 | * 2. No rights are granted, in any manner or form, to use Whistle 13 | * Communications, Inc. trademarks, including the mark "WHISTLE 14 | * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 15 | * such appears in the above copyright notice or in the software. 16 | * 17 | * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 18 | * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 19 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 20 | * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 22 | * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 23 | * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 24 | * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 25 | * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 26 | * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 27 | * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 | * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 33 | * OF SUCH DAMAGE. 34 | * 35 | * Author: Rozhuk Ivan 36 | * 37 | */ 38 | 39 | /* xml-SOAP MediaServer/ContentDirectory:3 for UPnP/DLNA */ 40 | /* http://upnp.org/specs/av/UPnP-av-ContentDirectory-v3-Service.pdf */ 41 | 42 | 43 | /* Config.*/ 44 | $basedir = dirname(__FILE__).'/../../upnpdata'; /* File system path. */ 45 | $baseurl = '/upnpdata'; /* WEB URL path. */ 46 | 47 | date_default_timezone_set('UTC'); 48 | 49 | 50 | /* File types. */ 51 | $file_class = array( 52 | 'm3u' => 'object.container.storageFolder', 53 | 'xspf' => 'object.container.storageFolder', 54 | 'xml' => 'object.container.storageFolder', 55 | 56 | 'bmp' => 'object.item.imageItem.photo', 57 | 'gif' => 'object.item.imageItem.photo', 58 | 'ico' => 'object.item.imageItem.photo', 59 | 'png' => 'object.item.imageItem.photo', 60 | 'jpe' => 'object.item.imageItem.photo', 61 | 'jpg' => 'object.item.imageItem.photo', 62 | 'jpeg' => 'object.item.imageItem.photo', 63 | 'tif' => 'object.item.imageItem.photo', 64 | 'tiff' => 'object.item.imageItem.photo', 65 | 'svg' => 'object.item.imageItem.photo', 66 | 'svgz' => 'object.item.imageItem.photo', 67 | 68 | 'flac' => 'object.item.audioItem.musicTrack', 69 | 'mp3' => 'object.item.audioItem.musicTrack', 70 | 'wav' => 'object.item.audioItem.musicTrack', 71 | 'wma' => 'object.item.audioItem.musicTrack', 72 | 73 | 'flv' => 'object.item.videoItem', 74 | 'f4v' => 'object.item.videoItem', 75 | '3g2' => 'object.item.videoItem', 76 | '3gp' => 'object.item.videoItem', 77 | '3gp2' => 'object.item.videoItem', 78 | '3gpp' => 'object.item.videoItem', 79 | 'asf' => 'object.item.videoItem', 80 | 'asx' => 'object.item.videoItem', 81 | 'avi' => 'object.item.videoItem.movie', 82 | 'dat' => 'object.item.videoItem', 83 | 'iso' => 'object.item.videoItem', 84 | 'm2t' => 'object.item.videoItem', 85 | 'm2ts' => 'object.item.videoItem', 86 | 'm2v' => 'object.item.videoItem', 87 | 'm4v' => 'object.item.videoItem', 88 | 'mp2v' => 'object.item.videoItem', 89 | 'mp4' => 'object.item.videoItem', 90 | 'mp4v' => 'object.item.videoItem', 91 | 'mpe' => 'object.item.videoItem', 92 | 'mpeg' => 'object.item.videoItem', 93 | 'mpg' => 'object.item.videoItem', 94 | 'mod' => 'object.item.videoItem', 95 | 'mov' => 'object.item.videoItem', 96 | 'mkv' => 'object.item.videoItem.videoBroadcast', 97 | 'mts' => 'object.item.videoItem', 98 | 'ogg' => 'object.item.videoItem', 99 | 'swf' => 'object.item.videoItem', 100 | 'vob' => 'object.item.videoItem', 101 | 'ts' => 'object.item.videoItem', 102 | 'webm' => 'object.item.videoItem', 103 | 'wm' => 'object.item.videoItem', 104 | 'wmv' => 'object.item.videoItem', 105 | 'wmx' => 'object.item.videoItem', 106 | ); 107 | 108 | /* MIME types. */ 109 | $mime_types = array( 110 | 'txt' => 'text/plain', 111 | 'htm' => 'text/html', 112 | 'html' => 'text/html', 113 | 'php' => 'text/html', 114 | 'css' => 'text/css', 115 | 'js' => 'application/javascript', 116 | 'json' => 'application/json', 117 | 'xml' => 'application/xml', 118 | 'swf' => 'application/x-shockwave-flash', 119 | 120 | /* Images. */ 121 | 'png' => 'image/png', 122 | 'jpe' => 'image/jpeg', 123 | 'jpeg' => 'image/jpeg', 124 | 'jpg' => 'image/jpeg', 125 | 'gif' => 'image/gif', 126 | 'bmp' => 'image/bmp', 127 | 'ico' => 'image/vnd.microsoft.icon', 128 | 'tiff' => 'image/tiff', 129 | 'tif' => 'image/tiff', 130 | 'svg' => 'image/svg+xml', 131 | 'svgz' => 'image/svg+xml', 132 | 133 | /* Audio. */ 134 | 'flac' => 'audio/ogg', 135 | 'mp3' => 'audio/mpeg', 136 | 'wav' => 'audio/vnd.wave', 137 | 'wma' => 'audio/x-ms-wma', 138 | 139 | /* Video. */ 140 | '3gp' => 'video/3gpp', 141 | '3gpp' => 'video/3gpp', 142 | '3g2' => 'video/3gpp2', 143 | '3gpp2' => 'video/3gpp2', 144 | 'flv' => 'video/x-flv', 145 | 'qt' => 'video/quicktime', 146 | 'ogg' => 'video/ogg', 147 | 'mov' => 'video/mpeg', 148 | 'mp4' => 'video/mp4', 149 | 'mkv' => 'video/x-mkv', 150 | 'm2ts' => 'video/MP2T', 151 | 'ts' => 'video/MP2T', 152 | 'webm' => 'video/webm', 153 | ); 154 | 155 | 156 | /* Auto variables. */ 157 | 158 | /* "urn:schemas-upnp-org:service:ContentDirectory:1#Browse" */ 159 | $http_hdr_soapact = $_SERVER['HTTP_SOAPACTION']; 160 | $soap_shemas = strpos($http_hdr_soapact, 'urn:schemas-upnp-org:service:ContentDirectory:'); 161 | if (false === $soap_shemas) 162 | return (500); 163 | $soap_service_ver = substr($http_hdr_soapact, ($soap_shemas + 46), 1); 164 | $soap_service_func = substr($http_hdr_soapact, ($soap_shemas + 48), -1); 165 | 166 | 167 | if (substr($basedir, -1, 1) !== '/') { 168 | $basedir = $basedir.'/'; 169 | } 170 | $baseurl = implode('/', array_map('rawurlencode', explode('/', $baseurl))); 171 | $baseurlpatch = 'http://'.$_SERVER['HTTP_HOST'].$baseurl; 172 | if ('/' !== substr($baseurlpatch, -1, 1)) { 173 | $baseurlpatch = $baseurlpatch.'/'; 174 | } 175 | /** 176 | * Apply workaround for the libxml PHP bugs: 177 | * @link https://bugs.php.net/bug.php?id=62577 178 | * @link https://bugs.php.net/bug.php?id=64938 179 | */ 180 | if (function_exists('libxml_disable_entity_loader')) { 181 | libxml_disable_entity_loader(false); 182 | } 183 | 184 | # $server = new SoapServer(null, array('uri' => "urn:schemas-upnp-org:service:ContentDirectory:3")); 185 | $server = new SoapServer(dirname(__FILE__)."/../descr/ContentDirectory.wdsl", 186 | array('cache_wsdl' => WSDL_CACHE_MEMORY, 187 | 'soap_version' => SOAP_1_2, 188 | 'trace' => true 189 | )); 190 | 191 | 192 | function xml_encode($string) { 193 | 194 | return (str_replace( 195 | array("&", "<", ">", /*'"',*/ "'"), 196 | array("&", "<", ">", /*""",*/ "'"), 197 | $string)); 198 | } 199 | 200 | function xml_decode($string) { 201 | 202 | return (str_replace( 203 | array("&", "<", ">", """, "'"), 204 | array("&", "<", ">", '"', "'"), 205 | $string)); 206 | } 207 | 208 | 209 | function upnp_url_encode($url) { 210 | 211 | if ('http://' !== substr($url, 0, 7) || 212 | false === ($url_path_off = strrpos($url, '/', 8))) 213 | return (implode('/', array_map('rawurlencode', explode('/', $url)))); 214 | //return (xml_encode(implode('/', array_map('rawurlencode', explode('/', $url))))); 215 | //return (xml_encode($url)); 216 | //return (' '', 288 | 'NumberReturned' => 0, 289 | 'TotalMatches' => 0, 290 | 'UpdateID' => $UpdateID)); 291 | } 292 | $date = upnp_date(filectime($filename), 1); 293 | if (is_writable($filename)) { 294 | $Restricted = '0'; 295 | } else { 296 | $Restricted = '1'; 297 | } 298 | 299 | while (!feof($fd)) { /* Read the file line by line... */ 300 | $buffer = trim(fgets($fd)); 301 | if (false === strpos($buffer, '#EXTINF:')) { /* Skip empty/bad lines. */ 302 | continue; 303 | } 304 | $entry = trim(fgets($fd)); 305 | if (false === strpos($entry, '://')) 306 | continue; 307 | /* Ok, item matched and may be returned. */ 308 | $TotalMatches++; 309 | if (0 < $StartingIndex && 310 | $TotalMatches < $StartingIndex) 311 | continue; /* Skip first items. */ 312 | if (0 < $RequestedCount && 313 | $NumberReturned >= $RequestedCount) 314 | continue; /* Do not add more than requested. */ 315 | $NumberReturned++; 316 | /* Add item to result. */ 317 | $title = xml_encode(trim(substr($buffer, (strpos($buffer, ',') + 1)))); 318 | //$en_entry = upnp_url_encode($entry); 319 | $en_entry = xml_encode($entry); 320 | $iclass = upnp_get_class($entry, 'object.item.videoItem.videoBroadcast'); 321 | $mimetype = 'video/mpeg'; 322 | if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */ 323 | $Result = $Result. 324 | "". 325 | "$title". 326 | 'object.container.storageFolder'. 327 | ''; 328 | } else { 329 | $Result = $Result. 330 | "". 331 | "$title". 332 | "$date". 333 | "$iclass". 334 | "$en_entry". 335 | ''; 336 | } 337 | } 338 | fclose($fd); 339 | 340 | $Result = $Result.''; 341 | return (array('Result' => $Result, 342 | 'NumberReturned' => $NumberReturned, 343 | 'TotalMatches' => $TotalMatches, 344 | 'UpdateID' => $UpdateID)); 345 | } 346 | 347 | 348 | function upnp_mime_content_type($filename) { 349 | global $mime_types; 350 | 351 | $def = 'video/mpeg'; 352 | 353 | if (!isset($filename)) 354 | return ($def); 355 | $dot = strrpos($filename, '.'); 356 | if (false === $dot) 357 | return ($def); 358 | $ext = strtolower(substr($filename, ($dot + 1))); 359 | if (array_key_exists($ext, $mime_types)) { 360 | return ($mime_types[$ext]); 361 | } elseif (function_exists('finfo_open')) { 362 | $finfo = finfo_open(FILEINFO_MIME); 363 | $mimetype = finfo_file($finfo, $filename); 364 | finfo_close($finfo); 365 | return ($mimetype); 366 | } 367 | 368 | return ($def); 369 | } 370 | 371 | /* Format: 372 | * 0: 2005-02-10 373 | * 1: 2004-05-08T10:00:00 374 | * */ 375 | function upnp_date($timedate, $format) { 376 | $res = date('Y-m-d', $timedate); 377 | 378 | if (1 === $format) { 379 | $res = $res.'T'.date('H:i:s', $timedate); 380 | } 381 | 382 | return ($res); 383 | } 384 | 385 | 386 | function browse_metadata($filename, $dir, $ObjectID, $UpdateID, $Result) { 387 | 388 | /* Is file/dir exist? */ 389 | $stat = stat($filename); 390 | if (false === $stat) { /* No such file/dir. */ 391 | return (array('Result' => '', 392 | 'NumberReturned' => 0, 393 | 'TotalMatches' => 0, 394 | 'UpdateID' => $UpdateID)); 395 | } 396 | 397 | /* Collect data. */ 398 | if (is_writable($filename)) { 399 | $WriteStatus = 'WRITABLE'; 400 | $Restricted = '0'; 401 | } else { 402 | $WriteStatus = 'NOT_WRITABLE'; 403 | $Restricted = '1'; 404 | } 405 | $basefilename = basename($dir); 406 | if ('0' === $ObjectID) { 407 | $title = 'root'; 408 | $ParentID = '-1'; 409 | } else { 410 | $title = xml_encode($basefilename); 411 | $ParentID = upnp_url_encode(dirname($dir)); 412 | } 413 | 414 | if (is_dir($filename)) { /* Dir. */ 415 | $StorageTotal = disk_total_space($filename); 416 | $StorageFree = disk_free_space($filename); 417 | $StorageUsed = ($StorageTotal - $StorageFree); 418 | $ChildCount = (count(scandir($filename)) - 2); 419 | $Result = $Result. 420 | "". 421 | "$title". 422 | 'object.container.storageFolder'. 423 | "$StorageTotal". 424 | "$StorageFree". 425 | "$StorageUsed". 426 | "$WriteStatus"; 427 | if ('0' === $ObjectID) { 428 | $Result = $Result. 429 | 'object.item.audioItem'. 430 | 'object.item.imageItem'. 431 | 'object.item.videoItem'; 432 | } 433 | $Result = $Result.''; 434 | } else { /* File or playlist. */ 435 | $iclass = upnp_get_class($basefilename, 'object.item.videoItem'); 436 | if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */ 437 | $ChildCount = m3u_calc_items_count($filename); 438 | $Result = $Result. 439 | "". 440 | "$title". 441 | 'object.container.storageFolder'. 442 | ''; 443 | } else { 444 | $date = upnp_date(filectime($filename), 1); 445 | $size = filesize($filename); 446 | $mimetype = upnp_mime_content_type($filename); 447 | $Result = $Result. 448 | "". 449 | "$title". 450 | "$date". 451 | "$iclass". 452 | "$ObjectID". 453 | ''; 454 | } 455 | } 456 | $Result = $Result.''; 457 | return (array('Result' => $Result, 458 | 'NumberReturned' => 1, 459 | 'TotalMatches' => 1, 460 | 'UpdateID' => $UpdateID)); 461 | } 462 | 463 | 464 | /* ContentDirectory funcs */ 465 | 466 | function GetSearchCapabilities() { 467 | // 'upnp:class'; /* dc:title,upnp:class,upnp:artist */ 468 | //$SearchCaps = 'dc:creator,dc:date,dc:title,upnp:album,upnp:actor,upnp:artist,upnp:class,upnp:genre,@id,@parentID,@refID'; 469 | $SearchCaps = 'dc:title'; 470 | 471 | return ($SearchCaps); 472 | } 473 | 474 | 475 | function GetSortCapabilities() { 476 | $SortCaps = 'dc:title'; 477 | /* dc:title,upnp:genre,upnp:album,dc:creator,res@size, 478 | * res@duration,res@bitrate,dc:publisher, 479 | * upnp:originalTrackNumber,dc:date,upnp:producer,upnp:rating, 480 | * upnp:actor,upnp:director,dc:description 481 | */ 482 | 483 | return ($SortCaps); 484 | } 485 | 486 | 487 | function GetSortExtensionCapabilities() { 488 | $SortExtensionCaps = ''; 489 | 490 | return ($SortExtensionCaps); 491 | } 492 | 493 | 494 | function GetFeatureList() { 495 | $FeatureList = ''; 496 | 497 | return ($FeatureList); 498 | } 499 | 500 | 501 | function GetSystemUpdateID() { 502 | $Id = '1'; 503 | 504 | return ($Id); 505 | } 506 | 507 | 508 | function GetServiceResetToken() { 509 | $ResetToken = '1'; 510 | 511 | return ($ResetToken); 512 | } 513 | 514 | 515 | function Browse($ObjectID, $BrowseFlag, $Filter, $StartingIndex, 516 | $RequestedCount, $SortCriteria) { 517 | global $basedir, $baseurl, $baseurlpatch; 518 | $Result = ''; 527 | $NumberReturned = 0; 528 | $TotalMatches = 0; 529 | $UpdateID = 1; 530 | 531 | /* Check input param. */ 532 | if (isset($ObjectID)) { 533 | $ObjectID_len = strlen($ObjectID); 534 | if ((1 === $ObjectID_len || ( 535 | 3 === $ObjectID_len && ( 536 | '_T' === substr($ObjectID, 1, 2) || 537 | '_D' === substr($ObjectID, 1, 2) || 538 | '_L' === substr($ObjectID, 1, 2)))) && ( 539 | '0' === substr($ObjectID, 0, 1) || 540 | 'A' === substr($ObjectID, 0, 1) || 541 | 'I' === substr($ObjectID, 0, 1) || 542 | 'V' === substr($ObjectID, 0, 1) || 543 | 'P' === substr($ObjectID, 0, 1) || 544 | 'T' === substr($ObjectID, 0, 1))) { /* V, I, A, P, T - from X_GetFeatureList() */ 545 | $ObjectID = '0'; 546 | $dir = ''; 547 | } else { 548 | $dir = rawurldecode(xml_decode($ObjectID)); 549 | if ('/' !== substr($dir, -1, 1)) { 550 | $dir = $dir.'/'; 551 | } 552 | /* Sec check: .. in path */ 553 | $dotdotdir = ''; 554 | $dirnames = explode('/', $dir); 555 | $dirnames_size = sizeof($dirnames); 556 | for ($di = 0; $di < $dirnames_size; $di++) { 557 | if ('.' === $dirnames[$di]) 558 | continue; 559 | if ('..' === $dirnames[$di]) { 560 | $dir = ''; 561 | break; 562 | } 563 | if ($dirnames_size >= $di) { 564 | $dotdotdir = $dotdotdir.$dirnames[$di].'/'; 565 | } 566 | } 567 | $dir = $dotdotdir; 568 | if ('/' === substr($dir, 0, 1) /*|| !is_dir($basedir.$dir)*/) { 569 | $dir = ''; 570 | } 571 | /* Remove tail slash from file name. */ 572 | if (!is_dir($basedir.$dir) && 573 | '/' === substr($dir, -1, 1)) { 574 | $dir = substr($dir, 0, -1); 575 | } 576 | } 577 | } else { 578 | $ObjectID = '0'; 579 | $dir = ''; 580 | } 581 | 582 | if ('BrowseMetadata' === $BrowseFlag) { 583 | return (browse_metadata($basedir.$dir, $dir, $ObjectID, 584 | $UpdateID, $Result)); 585 | } 586 | 587 | if (!isset($StartingIndex)) { 588 | $StartingIndex = 0; 589 | } 590 | if (!isset($RequestedCount)) { 591 | $RequestedCount = 0; 592 | } 593 | 594 | if (!is_dir($basedir.$dir)) { /* Play list file? */ 595 | return (m3u_browse($basedir.$dir, $ObjectID, 596 | $StartingIndex, $RequestedCount, $UpdateID, $Result)); 597 | } 598 | 599 | /* Scan directory and add to play list.*/ 600 | $entries = scandir($basedir.$dir); 601 | /* Add dirs to play list. */ 602 | foreach ($entries as $entry) { 603 | $filename = $basedir.$dir.$entry; 604 | if ('.' === substr($entry, 0, 1) || 605 | !is_dir($filename)) /* Skip files. */ 606 | continue; 607 | /* Ok, item matched and may be returned. */ 608 | $TotalMatches++; 609 | if (0 < $StartingIndex && 610 | $TotalMatches < $StartingIndex) 611 | continue; /* Skip first items. */ 612 | if (0 < $RequestedCount && 613 | $NumberReturned >= $RequestedCount) 614 | continue; /* Do not add more than requested. */ 615 | $NumberReturned++; 616 | /* Add item to result. */ 617 | if (is_writable($filename)) { 618 | $Restricted = '0'; 619 | } else { 620 | $Restricted = '1'; 621 | } 622 | $title = xml_encode($entry); 623 | $en_entry = upnp_url_encode($dir.$entry); 624 | $ChildCount = (count(scandir($filename)) - 2); 625 | $Result = $Result. 626 | "". 627 | "$title". 628 | 'object.container.storageFolder'. 629 | ''; 630 | } 631 | /* Add files to play list. */ 632 | foreach ($entries as $entry) { 633 | $filename = $basedir.$dir.$entry; 634 | if (is_dir($filename)) /* Skip dirs. */ 635 | continue; 636 | $iclass = upnp_get_class($entry, null); 637 | if (null === $iclass) /* Skip unsupported file type. */ 638 | continue; 639 | /* Ok, item matched and may be returned. */ 640 | $TotalMatches++; 641 | if (0 < $StartingIndex && 642 | $TotalMatches < $StartingIndex) 643 | continue; /* Skip first items. */ 644 | if (0 < $RequestedCount && 645 | $NumberReturned >= $RequestedCount) 646 | continue; /* Do not add more than requested. */ 647 | $NumberReturned++; 648 | /* Add item to result. */ 649 | if (is_writable($filename)) { 650 | $Restricted = '0'; 651 | } else { 652 | $Restricted = '1'; 653 | } 654 | $title = xml_encode($entry); 655 | $en_entry = upnp_url_encode($dir.$entry); 656 | if ('object.container.storageFolder' === $iclass) { /* Play list as folder! */ 657 | $ChildCount = m3u_calc_items_count($filename); 658 | $Result = $Result. 659 | "". 660 | "$title". 661 | 'object.container.storageFolder'. 662 | ''; 663 | } else { 664 | $date = upnp_date(filectime($filename), 1); 665 | $size = filesize($filename); 666 | $mimetype = upnp_mime_content_type($filename); 667 | $res_info_ex = ''; 668 | $Result = $Result. 669 | "". 670 | "$title". 671 | "$date". 672 | "$iclass"; 673 | if ('object.item.imageItem' === substr($iclass, 0, 21)) { 674 | $Result = $Result. 675 | "$baseurlpatch$en_entry". 676 | "$baseurlpatch$en_entry"; 677 | $img_info = getimagesize($filename); 678 | if (false !== $img_info) { 679 | $res_info_ex = ' resolution="'.$img_info[0].'x'.$img_info[1].'"'; 680 | } 681 | } 682 | $Result = $Result. 683 | "$baseurlpatch$en_entry". 684 | ''; 685 | } 686 | } 687 | 688 | $Result = $Result.''; 689 | return (array('Result' => $Result, 690 | 'NumberReturned' => $NumberReturned, 691 | 'TotalMatches' => $TotalMatches, 692 | 'UpdateID' => $UpdateID)); 693 | } 694 | 695 | 696 | function Search($ContainerID, $SearchCriteria, $Filter, $StartingIndex, 697 | $RequestedCount, $SortCriteria) { 698 | global $basedir, $baseurl, $baseurlpatch; 699 | $Result = ''; 708 | $NumberReturned = 0; 709 | $TotalMatches = 0; 710 | $UpdateID = 1; 711 | 712 | $Result = $Result.''; 713 | 714 | return (array('Result' => $Result, 715 | 'NumberReturned' => $NumberReturned, 716 | 'TotalMatches' => $TotalMatches, 717 | 'UpdateID' => $UpdateID)); 718 | } 719 | 720 | 721 | function CreateObject($ContainerID, $Elements) { 722 | $ObjectID = ''; 723 | $Result = ''; 724 | 725 | return (array('ObjectID' => $ObjectID, 726 | 'Result' => $Result)); 727 | } 728 | 729 | 730 | function DestroyObject($ObjectID) { 731 | } 732 | 733 | 734 | function UpdateObject($ObjectID, $CurrentTagValue, $NewTagValue) { 735 | } 736 | 737 | 738 | function MoveObject($ObjectID, $NewParentID, $NewObjectID) { 739 | } 740 | 741 | 742 | /* Samsung private. */ 743 | function X_GetFeatureList() { 744 | $FeatureList = 745 | ''. 746 | ''. 751 | ''. 752 | ''. 753 | ''. 754 | ''. 755 | ''. 756 | ''. 757 | ''. 758 | ''; 759 | 760 | return ($FeatureList); 761 | } 762 | 763 | function X_SetBookmark($CategoryType, $RID, $ObjectID, $PosSecond) { 764 | /* Return: 765 | * BM=number of seconds 766 | * HH:MM:SS 767 | */ 768 | } 769 | 770 | 771 | 772 | /* Process request. */ 773 | 774 | $request_body = @file_get_contents('php://input'); 775 | if (is_string($request_body) === false) { 776 | header($_SERVER["SERVER_PROTOCOL"]." 400 Bad request"); 777 | die(); 778 | } 779 | try { 780 | $server->addFunction(array('GetSearchCapabilities', 781 | 'GetSortCapabilities', 782 | 'GetSortExtensionCapabilities', 783 | 'GetFeatureList', 784 | 'GetSystemUpdateID', 785 | 'GetServiceResetToken', 786 | 'Browse', 787 | 'Search', 788 | 'CreateObject', 789 | 'DestroyObject', 790 | 'UpdateObject', 791 | 'MoveObject', 792 | 'X_GetFeatureList', 793 | 'X_SetBookmark' 794 | )); 795 | ob_start(); 796 | /* Type checking done before, it is safe to ignore type warning here. */ 797 | $server->handle(/** @scrutinizer ignore-type */ $request_body); 798 | $soapXml = ob_get_clean(); 799 | } catch (Exception $e) { 800 | $server->fault($e->getCode(), $e->getMessage()); 801 | throw $e; 802 | } 803 | 804 | 805 | /* Post processing. */ 806 | 807 | 808 | function get_resp_tag_name($sxml) { 809 | $tag_st = strpos($sxml, '', $tag_st); 814 | if (false === $tag_end) 815 | return (false); 816 | return (substr($sxml, $tag_st, ($tag_end - $tag_st))); 817 | } 818 | 819 | function get_tag_ns($req, $tag) { 820 | $rreq = strrev($req); 821 | $ns_st = strpos($rreq, strrev(":$tag>")); 822 | if (false === $ns_st) 823 | return (false); 824 | $ns_st += (strlen($tag) + 2); 825 | $ns_end = strpos($rreq, '/<', $ns_st); 826 | if (false === $ns_end) 827 | return (false); 828 | return (strrev(substr($rreq, $ns_st, ($ns_end - $ns_st)))); 829 | } 830 | 831 | function tag_ns_replace($req, $sxml, $tag, $ns = false) { 832 | if (false === $tag) 833 | return ($sxml); 834 | if (false === $ns) 835 | $ns = get_tag_ns($req, $tag); 836 | if (false === $ns) 837 | return ($sxml); 838 | while ($tag_st = strpos($sxml, "', $tag_st); 840 | if (false === $tag_end) 841 | return ($sxml); 842 | $old_tag_data = substr($sxml, $tag_st, ($tag_end - $tag_st)); 843 | $new_tag_data = str_replace('SOAP-ENV', $ns, $old_tag_data); 844 | $sxml = str_replace($old_tag_data, $new_tag_data, $sxml); 845 | } 846 | return (str_replace("", "", $sxml)); 847 | } 848 | 849 | 850 | /* Add encodingStyle attr. */ 851 | $soapXml = str_replace(' 867 | --------------------------------------------------------------------------------