├── 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 | [](https://github.com/rozhuk-im/ssdpd/actions)
4 | [](https://github.com/rozhuk-im/ssdpd/actions)
5 | [](https://scrutinizer-ci.com/g/rozhuk-im/ssdpd/build-status/master)
6 | [](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:** [](https://github.com/sponsors/rozhuk-im)
24 | * **Buy Me A Coffee:** [](https://www.buymeacoffee.com/rojuc)
25 | * **PayPal:** [](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("", "$ns:$tag>", $sxml));
847 | }
848 |
849 |
850 | /* Add encodingStyle attr. */
851 | $soapXml = str_replace('
867 |
--------------------------------------------------------------------------------