1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-09 06:10:41 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Stephan Jauernick
80f4880f88 Merge branch 'devel-stephan48-libigloo-osc' into 'devel-phschafft'
Devel stephan48 libigloo osc

See merge request xiph/icecast-server!16
2024-01-23 10:47:00 +00:00
Philipp Schafft
64ab166225 Fix: Skip loading of geoip database if none is given 2024-01-22 10:38:17 +00:00
Philipp Schafft
1b7db528f4 Feature: Added basic way to export Icecast's internally used IDs as a valuefile 2024-01-21 17:26:31 +00:00
Stephan Jauernick
5eb7ca123c use more descriptive commit info for osc commit 2024-01-20 23:13:43 +00:00
Gilou
ed1ef192f0 Rewrite CI to prepare for -betaX release, and future 2.5.x on OBS 2024-01-20 23:12:21 +00:00
Stephan Jauernick
7d33cd96fd DNU - hint at docu change regarding CI and submodules 2024-01-20 23:12:21 +00:00
Stephan Jauernick
8248a4f87c DNU WIP ci changes 2024-01-20 23:12:21 +00:00
Stephan Jauernick
e768c5782a WIP ci changes 2024-01-20 23:12:21 +00:00
Philipp Schafft
e3487e4c22 Feature: Added UUID interpolation for dump files 2024-01-20 04:25:19 +00:00
Philipp Schafft
6752809c20 Merge branch 'feature-geoip' into devel-phschafft 2024-01-20 02:13:38 +00:00
Philipp Schafft
e63fa6f191 Update: Updated XML to JSON mapping for <geoip> 2024-01-20 01:56:50 +00:00
Philipp Schafft
f193eab36a Feature: Render geoip data in a nicer way to JSON 2024-01-20 01:56:47 +00:00
Philipp Schafft
fe0d8598f4 Feature: Report geoip result on admin interface 2024-01-20 00:16:07 +00:00
Philipp Schafft
f9fccf24ce Feature: Include geocoordinates in geoip lookup 2024-01-20 00:15:57 +00:00
Philipp Schafft
5e66d22e36 Feature: Added per country geoip statistics 2024-01-20 00:14:55 +00:00
Philipp Schafft
1963562d58 Feature: Added basic geoip lookup 2024-01-20 00:14:51 +00:00
Philipp Schafft
2952a29c1e Fix: Converted source->format->contenttype into a igloo string pool string 2024-01-19 23:55:31 +00:00
44 changed files with 1150 additions and 220 deletions

View File

@ -1,28 +1,14 @@
# Build Icecast - test # Build Icecast - test
variables:
BASE_IMAGE_REGISTRY: registry.gitlab.xiph.org/xiph/icecast-docker-images
default: default:
image: alpine:3.15 image: "$BASE_IMAGE_REGISTRY/alpine-icecast-server:20240114141404"
tags: tags:
- docker - docker
- linux - linux
.TemplateAlpine:
before_script:
- set -xe
- apk update
- cat /etc/os*
- apk add musl-dev git make gcc automake autoconf libtool curl ca-certificates
- apk add curl-dev libogg-dev libvorbis-dev libxslt-dev libxml2-dev libc6-compat rhash-dev
# gzip required because busybox gzip does not understand best / zip for make dist
- apk add gzip zip tar
# Required for tests
#- apk add curl ffmpeg
# Create user to run tests
#- adduser -s /bin/sh -D -H icecast
make_dist: make_dist:
extends: .TemplateAlpine
variables: variables:
GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_STRATEGY: recursive
LIBIGLOO_PACKAGE_PREFIX: "alpine-libigloo-bins" LIBIGLOO_PACKAGE_PREFIX: "alpine-libigloo-bins"
@ -33,6 +19,7 @@ make_dist:
script: script:
- set -xe # yes i know the scripts will be merged... but for sanities sake - set -xe # yes i know the scripts will be merged... but for sanities sake
- cat /etc/os*
- ./autogen.sh - ./autogen.sh
- mkdir EXTRA-LIBS - mkdir EXTRA-LIBS
- export PKG_CONFIG_PATH=`pwd`/EXTRA-LIBS/usr/local/lib/pkgconfig - export PKG_CONFIG_PATH=`pwd`/EXTRA-LIBS/usr/local/lib/pkgconfig
@ -49,6 +36,7 @@ make_dist:
- ./configure || cat config.log - ./configure || cat config.log
- make - make
- make dist - make dist
- tree
# Tests # Tests
#- su -c "./tests/admin-tests.sh" icecast #- su -c "./tests/admin-tests.sh" icecast
@ -57,29 +45,8 @@ make_dist:
untracked: true untracked: true
expire_in: 1 week expire_in: 1 week
upload_package:
extends: .TemplateAlpine
stage: deploy
variables:
ICECAST_SERVER_VERSION: "2.4.99.3"
ICECAST_SERVER_PREFIX: "alpine-icecast-server-bins"
script:
- make install DESTDIR=`pwd`/_install_base/
- cd _install_base/
# see above
- export ICECAST_SERVER_BASE=$ICECAST_SERVER_PREFIX-$CI_COMMIT_REF_NAME
- export ICECAST_SERVER_ARCHIVE=$ICECAST_SERVER_BASE.tar.gz
- export ICECAST_SERVER_VERSION_WITH_SUFFIX=$ICECAST_SERVER_VERSION-$CI_COMMIT_REF_NAME
- tar -cvzf ../$ICECAST_SERVER_ARCHIVE *
- cd ..
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file $ICECAST_SERVER_ARCHIVE "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/$ICECAST_SERVER_PREFIX/$ICECAST_SERVER_VERSION_WITH_SUFFIX/$ICECAST_SERVER_ARCHIVE"'
needs:
- "make_dist"
upload_dist: upload_dist:
image: "$BASE_IMAGE_REGISTRY/alpine-osc:20240114141818"
only: only:
- master - master
- devel - devel
@ -96,6 +63,7 @@ upload_dist:
- ./ci/osc/handle-osc-upload.sh - ./ci/osc/handle-osc-upload.sh
upload_dist_release: upload_dist_release:
image: "$BASE_IMAGE_REGISTRY/alpine-osc:20240114141818"
only: only:
- tags - tags

View File

@ -1,6 +1,8 @@
Icecast 2 - README Icecast 2 - README
--------------------------------------------------------------------- ---------------------------------------------------------------------
TODO: add info regarding forks and expected repo layout in gitlab/*blub*
[![Build Status](https://travis-ci.org/xiph/Icecast-Server.svg?branch=master)](https://travis-ci.org/xiph/Icecast-Server) [![Build Status](https://travis-ci.org/xiph/Icecast-Server.svg?branch=master)](https://travis-ci.org/xiph/Icecast-Server)
Icecast is a streaming media server which currently supports _WebM_ and Icecast is a streaming media server which currently supports _WebM_ and

View File

@ -25,6 +25,7 @@
<th>Role</th> <th>Role</th>
<th>Sec. connected</th> <th>Sec. connected</th>
<th>User Agent</th> <th>User Agent</th>
<th>Location</th>
<th class="actions">Action</th> <th class="actions">Action</th>
</tr> </tr>
</thead> </thead>
@ -36,6 +37,10 @@
<td><xsl:value-of select="role" /></td> <td><xsl:value-of select="role" /></td>
<td><xsl:value-of select="connected" /></td> <td><xsl:value-of select="connected" /></td>
<td><xsl:value-of select="useragent" /></td> <td><xsl:value-of select="useragent" /></td>
<td>
<xsl:value-of select="geoip/country/@iso-alpha-2" />
<xsl:if test="geoip/location/@latitude and geoip/location/@longitude">&#160;<a href="https://www.openstreetmap.org/?mlat={geoip/location/@latitude}&amp;mlon={geoip/location/@longitude}&amp;zoom=7">On OSM</a></xsl:if>
</td>
<td class="actions"> <td class="actions">
<a class="critical" href="/admin/ui/confirmkillclient.xsl?mount={../@mount}&amp;id={id}">Kick</a> <a class="critical" href="/admin/ui/confirmkillclient.xsl?mount={../@mount}&amp;id={id}">Kick</a>
<a href="/admin/moveclients.xsl?mount={../@mount}&amp;id={id}">Move</a> <a href="/admin/moveclients.xsl?mount={../@mount}&amp;id={id}">Move</a>

View File

@ -9,14 +9,16 @@ These are defined in the ci/osc/ for releases and nightlies in their respective
To make a new release please call the magic version changer as following from the repo root: To make a new release please call the magic version changer as following from the repo root:
``` ```
export ICECAST_BETA_VERSION=3 ci/create-changelog-and-set-versions.sh "2.4.99.4" "_VERSION_ARCHIVE_" "2.4.99.4" "now" "Stephan Jauernick <info@stephan-jauernick.de>" "Preparing for 2.5.0"
ci/create-changelog-and-set-versions.sh "$ICECAST_BETA_VERSION" "2.4.99.$ICECAST_BETA_VERSION" "2.5 beta$ICECAST_BETA_VERSION" "25-beta-$ICECAST_BETA_VERSION" "2.5-beta$ICECAST_BETA_VERSION" "_VERSION_ARCHIVE_" "2.4.99.$ICECAST_BETA_VERSION" "now" "Stephan Jauernick <info@stephan-jauernick.de>" "Preparing for 2.5 beta$ICECAST_BETA_VERSION aka 2.4.99.$ICECAST_BETA_VERSION" "icecast" "mingw32-icecast" "mingw32-icecast-installer" ci/create-changelog-and-set-versions.sh "2.5.0" "_VERSION_ARCHIVE_" "2.5.0" "now" "Stephan Jauernick <info@stephan-jauernick.de>" "2.5 is great"
``` ```
Please adapt the "ICECAST_BETA_VERSION", the Author, the Date(now; please enter a valid ISO8601 Date if needed) and the Message as needed. Please adapt the Author, the Date(now; please enter a valid ISO8601 Date if needed) and the Message as needed.
This script/mechanism will update all the version references and then show you a git status/diff. Please check all changes and commit them as needed. This script/mechanism will update all the version references and then show you a git status/diff. Please check all changes and commit them as needed.
For now, a X.Y.Z.B release will set the versions for a X.Y+1 beta B release, this could be changed to allow patch pre-release.
After tagging and uploading the release will be picked up by gitlabs CI and a release will be pushed to OBS. After tagging and uploading the release will be picked up by gitlabs CI and a release will be pushed to OBS.
# Nightlies # Nightlies

View File

@ -1,20 +1,56 @@
#!/bin/bash -xe #!/bin/bash -xe
BETA_VERSION=${1:?Missing Beta Version, Use 2}; shift # Script to handle version handling for releases manually or through the CI
SHORT_VERSION=${1:?Missing Short Version, Use 2.4.99.2}; shift # SHORT_VERSION (first parameter) will be used for most places
STRANGE_VERSION=${1:?Missing Strange Version, Use '2.5 beta2'}; shift # It should be MAJOR.minor.patch to prepare an actual release
HTML_VERSION=${1:?Missing HTML Version, Use '25-beta-2'}; shift # or MAJOR.minor.patch.extra to prepare for the next minor release
WIN32_VERSION=${1:?Missing Win32 Version, Use '2.5-beta2'}; shift # extra will then be the number of the beta release
ARCHIVE_VERSION=${1:?Missing Archive Version, Use '2.4.99.2' for ci or _VERSION_ARCHIVE_ for release}; shift # - in configure.ac, for the actual version used for building
CI_VERSION=${1:?Missing CI Version - Use 2.4.99.2+bla}; shift # - to build the other version numbers such as :
DATE=`date --date=${1:?ISO DATE or now} --iso-8601=seconds`; shift # - Documentation link for the current version
# - Windows installer "displayed version" and others
SHORT_VERSION=${1:?Missing Short Version, Use 2.5.0 or 2.5.0.1}; shift
# ARCHIVE_VERSION is used as an info to differentiate releases from CI builds
# For releases, it should be set to _VERSION_ARCHIVE_
# For CI, something resembling the SHORT_VERSION
ARCHIVE_VERSION=${1:?Missing Archive Version, Use '2.5.0' for ci or _VERSION_ARCHIVE_ for release}; shift
# CI_VERSION is used mostly in changelogs and comments about the release
# should be set to something like SHORT_VERSION
CI_VERSION=${1:?Missing CI Version - Use 2.4.99.2+bla without any -}; shift
# Date used throughout for changelogs, and tags where we need a timestamp
DATE=$(LC_TIME=C date --utc --iso-8601=seconds --date="${1:?ISO DATE or now}"); shift
# Author (Email, or Name <email>) in changelogs and comments
AUTHOR=${1:?Mail Address}; shift AUTHOR=${1:?Mail Address}; shift
# Short description to be added to changelogs
TEXT=${1:?Release Text}; shift TEXT=${1:?Release Text}; shift
ICECAST_PROJECT=${1:?Icecast OSC Project Name}; shift
W32_ICECAST_PROJECT=${1:?Icecast W32 OSC Project Name}; shift
W32_ICECAST_INSTALLER_PROJECT=${1:?Icecast W32 Installer OSC Project Name}; shift
OSC_BASE_DIR=osc_tmp if [[ "$SHORT_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(\.([0-9]+))?$ ]]; then
MAJOR=${BASH_REMATCH[1]}
MINOR=${BASH_REMATCH[2]}
PATCH=${BASH_REMATCH[3]}
EXTRA=${BASH_REMATCH[5]}
LABEL=${EXTRA:+beta}
if [[ -n "$EXTRA" ]]; then
((MINOR++))
fi
else
echo "Sorry, that version does not comply to our ^([0-9]+)\.([0-9]+)\.([0-9]+)(\.([0-9]+))?$ regex, use something like 2.5.0 or 2.5.0.1"
exit 3
fi
# M.m or M.m -LABEL
STRANGE_VERSION="${MAJOR}.${MINOR}${EXTRA:+ $LABEL$EXTRA}"
# Mm, or Mm-beta-1
HTML_VERSION="${MAJOR}${MINOR}${EXTRA:+-$LABEL-$EXTRA}"
# M.m or M.m-LABEL
WIN32_VERSION="${MAJOR}.${MINOR}${EXTRA:+-$LABEL$EXTRA}"
# Name of the package names in OBS for each part. Can be overridden.
ICECAST_PROJECT=${ICECAST_PROJECT:-icecast}
W32_ICECAST_PROJECT=${W32_ICECAST_PROJECT:-mingw32-icecast}
W32_ICECAST_INSTALLER_PROJECT=${W32_ICECAST_INSTALLER_PROJECT:-mingw32-icecast-installer}
OSC_BASE_DIR="${OSC_TMP:-osc_tmp}"
# upon release we modify the templates - in ci we modiy temporary files # upon release we modify the templates - in ci we modiy temporary files
if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then
@ -23,29 +59,28 @@ fi
pushd $OSC_BASE_DIR pushd $OSC_BASE_DIR
sed -i "1s#^#icecast2 ($CI_VERSION-1) UNRELEASED; urgency=medium\n\n * $TEXT\n\n -- $AUTHOR `date --date=$DATE +"%a, %d %b %Y %H:%M:%S %z"`\n\n#" $ICECAST_PROJECT/debian/changelog # Set changelog for Debian, required. Version will be set to $CI_VERSION-1
sed -i "1s#^#icecast2 ($CI_VERSION-1) UNRELEASED; urgency=medium\n\n * $TEXT\n\n -- $AUTHOR $(LC_TIME=C date --date="$DATE" +"%a, %d %b %Y %H:%M:%S %z")\n\n#" "$ICECAST_PROJECT/debian/changelog"
# Set .spec to build on OBS, common changelog, version set to $ARCHIVE_VERSION
for i in "$ICECAST_PROJECT/$ICECAST_PROJECT.spec" "$W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec" "$W32_ICECAST_PROJECT/$W32_ICECAST_PROJECT.spec"; do for i in "$ICECAST_PROJECT/$ICECAST_PROJECT.spec" "$W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec" "$W32_ICECAST_PROJECT/$W32_ICECAST_PROJECT.spec"; do
sed -i "s/_VERSION_ARCHIVE_/$ARCHIVE_VERSION/; s/^Version:\(\s*\)[^\s]*$/Version:\1$CI_VERSION/; s#^%changelog.*\$#\0\n* `date --date=$DATE +"%a %b %d %Y"` $AUTHOR - $CI_VERSION-1\n- $TEXT\n\n#" "$i"; sed -i "s/_VERSION_ARCHIVE_/$ARCHIVE_VERSION/; s/^Version:\(\s*\)[^\s]*$/Version:\1$CI_VERSION/; s#^%changelog.*\$#\0\n* $(LC_TIME=C date --date="$DATE" +"%a %b %d %Y") $AUTHOR - $CI_VERSION-1\n- $TEXT\n\n#" "$i";
done done
sed -i "s/\(icecast_win32_\).*\(.exe\)/\1$WIN32_VERSION\2/" $W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec
popd popd
# we only update the changelog for releases - until i figure out if we want to run the magic script pre CI # we only update the changelog for releases - until i figure out if we want to run the magic script pre CI
if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then
sed -i "1s#^#`date --date=$DATE +"%Y-%m-%d %H:%M:%S"` $AUTHOR\n\n * $TEXT\n\n#" ChangeLog sed -i "1s#^#$(LC_TIME=C date --date="$DATE" +"%Y-%m-%d %H:%M:%S") $AUTHOR\n\n * $TEXT\n\n#" ChangeLog
fi fi
# actual version change for the build
sed -i "1s#\[[.0-9]*\]#[$SHORT_VERSION]#" configure.ac sed -i "1s#\[[.0-9]\+\]#[$SHORT_VERSION]#" configure.ac
sed -i "s/Icecast .* Documentation/Icecast $STRANGE_VERSION Documentation/; s/icecast-.*-documentation/icecast-$HTML_VERSION-documentation/" doc/index.html sed -i "s/Icecast .* Documentation/Icecast $STRANGE_VERSION Documentation/; s/icecast-.*-documentation/icecast-$HTML_VERSION-documentation/" doc/index.html
sed -i "s/\(\"DisplayVersion\" \"\).*\(\"\)$/\1$STRANGE_VERSION\2/" win32/icecast.nsis sed -i "s/\(\"DisplayVersion\" \"\).*\(\"\)$/\1$STRANGE_VERSION\2/" win32/icecast.nsis
sed -i "s/\(OutFile \"icecast_win32_\).*\(.exe\"\)$/\1$WIN32_VERSION\2/" win32/icecast.nsis # Update version in all config.sh script used for releases and nightly builds
sed -i "s/^\(export ICECAST_VERSION=\).*$/\1$SHORT_VERSION/" ci/osc/*-config.sh
sed -i "s/^\(export ICECAST_VERSION=\).*$/\1$SHORT_VERSION/; s/\(export ICECAST_BETA_VERSION=\).*$/\1$BETA_VERSION/" ci/osc/*-config.sh
# we only do this for release builds # we only do this for release builds
if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then

View File

@ -7,6 +7,6 @@ Maintainer: Thomas B. Ruecker <thomas@ruecker.fi>
Uploaders: Thomas B. Ruecker <thomas@ruecker.fi> Uploaders: Thomas B. Ruecker <thomas@ruecker.fi>
Homepage: http://www.icecast.org/ Homepage: http://www.icecast.org/
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Build-Depends: cdbs, autotools-dev, devscripts, libtool, automake1.11, autoconf, debhelper, dh-buildinfo, libogg-dev (>> 1.0.0), libvorbis-dev (>> 1.0.0), libxslt1-dev | libxslt-dev, libxml2-dev, libcurl4-gnutls-dev, libtheora-dev (>= 0.0.0.alpha7), libspeex-dev, libssl-dev, po-debconf, pkg-config, dh-autoreconf, automake Build-Depends: cdbs, autotools-dev, devscripts, libtool, automake1.11, autoconf, debhelper, dh-buildinfo, libogg-dev (>> 1.0.0), libvorbis-dev (>> 1.0.0), libxslt1-dev | libxslt-dev, libxml2-dev, libcurl4-gnutls-dev, libtheora-dev (>= 0.0.0.alpha7), libspeex-dev, libssl-dev, po-debconf, pkg-config, dh-autoreconf, automake, librhash-dev, libigloo-dev
Package-List: Package-List:
icecast2 deb sound optional arch=any icecast2 deb sound optional arch=any

20
ci/osc/common-config.sh Normal file
View File

@ -0,0 +1,20 @@
# Mark the git dir safe, as it may have wider permissions
git config --global --add safe.directory "$PWD"
GIT_BRANCH=${CI_COMMIT_BRANCH}
GIT_COMMIT=`git rev-parse HEAD`
# OBS settings, override using OBS_BASE_PROJECT
# By default: use multimedia:xiph (or OBS_BASE_PROJECT) for releases
# multimedia:xiph:nightly-master for master branch nightlies
# multimedia:xiph:nightly-devel for devel branch nightlies
OBS_BASE_PROJECT=${OBS_BASE_PROJECT:-multimedia:xiph}
OBS_BASE_NIGHTLY_MASTER="${OBS_BASE_PROJECT}:nightly-master"
OBS_BASE_NIGHTLY_DEVEL="${OBS_BASE_PROJECT}:nightly-devel"
OBS_BASE_BETA="${OBS_BASE_PROJECT}:beta"
OBS_BASE_RELEASE="${OBS_BASE_PROJECT}"
export ICECAST_PROJECT=${ICECAST_PROJECT:-icecast}
export W32_ICECAST_PROJECT=${W32_ICECAST_PROJECT:-mingw32-icecast}
export W32_ICECAST_INSTALLER_PROJECT=${W32_ICECAST_INSTALLER_PROJECT:-mingw32-icecast-installer}
export ICECAST_VERSION=2.4.99.3

View File

@ -1,85 +1,111 @@
#!/bin/bash -xe #!/bin/bash -xe
# Script used by Gitlab's CI to push built packgages to OBS for packaging.
# It's configured using common-config.sh and nightly-config.sh for master/devel
# and release-config.sh through variables
# We'll need OBS_BASES to be set as a list of strings indicating OBS projects
# we want to upload our packages to.
SCRIPT_DIR=`dirname $0` # Set SCRIPT_DIR to current directory of the script
SCRIPT_DIR=`realpath $SCRIPT_DIR` SCRIPT_DIR=$(realpath "$(dirname "$0")")
# Set OSC_RC_FILE to define which file has osc config (and credentials)
# This can be overriden through CI or when manually called
OSC_RC_FILE=${OSC_RC_FILE:-$HOME/.config/osc/oscrc}
# Gitlab CI calls the script without parameters for master/devel
# and adds "release" as a param for releases, so we use that to load variables
CONFIG=${1:-nightly} CONFIG=${1:-nightly}
. $SCRIPT_DIR/$CONFIG-config.sh . "$SCRIPT_DIR/common-config.sh"
. "$SCRIPT_DIR/$CONFIG-config.sh"
: "${OSC_RC:?Variable OSC_RC not set or empty}" # This is set by Gitlab, but should be set if run manually
: "${CI_PIPELINE_URL:?Variable CI_PIPELINE_URL not set or empty}" : "${CI_PIPELINE_URL:?Variable CI_PIPELINE_URL not set or empty}"
# Print current dir for debug
pwd pwd
# Print content
ls -la ls -la
rm -rf osc_tmp # We use $HOME/.local/bin/osc --config=$ORC_RC_FILE as our osc command
mkdir -p osc_tmp # This can be overriden, and would be changed if osc was properly installed
cd osc_tmp export OSC_CMD="${OSC_CMD:-$HOME/.local/bin/osc} --config=$OSC_RC_FILE"
echo "Using ${OSC_CMD} for \$OSC_CMD, as $(id)"
export HOME=`pwd` # Remember where we are
export SOURCE=`pwd`/.. export SOURCE=$PWD
export OSC_CMD="osc-wrapper.py --config=$OSC_RC"
# checkout into a dkirectory named like the project - avoiding having it in a subdir called OBS_BASE # For each project on OBS
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do for OBS_BASE in $OBS_BASES; do
$OSC_CMD checkout -o "$i" $OBS_BASE "$i" # Create a temp dir for our upload, clean it up
rm -vrf "$i"/* export OSC_TMP=osc_tmp-$OBS_BASE
done rm -rf "$OSC_TMP"
mkdir -p "$OSC_TMP"
cd "$OSC_TMP"
# init dir for osc for the repository
$OSC_CMD init "$OBS_BASE"
# no comment needed # checkout into a dkirectory named like the project - avoiding having it in a subdir called OBS_BASE
for i in "$ICECAST_PROJECT" "$W32_ICECAST_PROJECT"; do for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
cp $SOURCE/icecast-$ICECAST_VERSION.tar.gz "$i"/icecast2_$ICECAST_CI_VERSION.orig.tar.gz $OSC_CMD checkout -o "$i" "$OBS_BASE" "$i" || $OSC_CMD mkpac "$i"
done rm -vrf "${i:?}"/*
# we copy the spec for these projects - for the icecast project the spec is globeed
for i in "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
cp -a $SCRIPT_DIR/$i/$i.spec $i/
done
# this is more complex because we have more files.
cp -a $SCRIPT_DIR/$ICECAST_PROJECT/icecast* $ICECAST_PROJECT/
cp -a $SCRIPT_DIR/$ICECAST_PROJECT/debian $ICECAST_PROJECT/
if [ "$DISABLE_CHANGELOG" == "0" ]; then
pushd $SOURCE
$SCRIPT_DIR/../create-changelog-and-set-versions.sh "$ICECAST_BETA_VERSION" "$ICECAST_VERSION" "2.5 beta$ICECAST_BETA_VERSION" "25-beta-$ICECAST_BETA_VERSION" "2.5-beta$ICECAST_BETA_VERSION" "$ICECAST_VERSION" "$ICECAST_CI_VERSION" "$RELEASE_DATETIME" "$RELEASE_AUTHOR" "CI Build - $CI_PIPELINE_URL" "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"
popd
else
for i in "$ICECAST_PROJECT/$ICECAST_PROJECT.spec" "$W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec" "$W32_ICECAST_PROJECT/$W32_ICECAST_PROJECT.spec"; do
sed -i "s/_VERSION_ARCHIVE_/$ICECAST_VERSION/;" "$i";
done done
fi
tar -C $ICECAST_PROJECT -cvzf $ICECAST_PROJECT/icecast2_$ICECAST_CI_VERSION-1.debian.tar.gz debian/ # Place the built archive in the source for OBS
for i in "$ICECAST_PROJECT" "$W32_ICECAST_PROJECT"; do
cp "$SOURCE/icecast-$ICECAST_VERSION.tar.gz" "$i/icecast2_$ICECAST_CI_VERSION.orig.tar.gz"
done
# remove debian/ so it does not end up in the archive # we copy the spec for these projects - for the icecast project the spec is globeed
rm -rf $ICECAST_PROJECT/debian for i in "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
cp -a "$SCRIPT_DIR/$i/$i.spec" "$i/"
done
# we fix the dsc to have the correct hashsums so dpkg-buildpackage and co do not complain # this is more complex because we have more files.
cp -a "$SCRIPT_DIR/$ICECAST_PROJECT"/icecast* "$ICECAST_PROJECT/"
cp -a "$SCRIPT_DIR/$ICECAST_PROJECT"/debian "$ICECAST_PROJECT/"
pushd $ICECAST_PROJECT # If we need to add automated changelog, call the versionning script
# Otherwise, assume it's a release, and we clean up the .spec files to be updated later by the script
if [ "$DISABLE_CHANGELOG" == "0" ]; then
pushd "$SOURCE"
"$SCRIPT_DIR/../create-changelog-and-set-versions.sh" "$ICECAST_VERSION" "$ICECAST_VERSION" "$ICECAST_CI_VERSION" "$RELEASE_DATETIME" "$RELEASE_AUTHOR" "CI Build - $CI_PIPELINE_URL" "$ICECAST_PROJECT"
popd
else
for i in "$ICECAST_PROJECT/$ICECAST_PROJECT.spec" "$W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec" "$W32_ICECAST_PROJECT/$W32_ICECAST_PROJECT.spec"; do
sed -i "s/_VERSION_ARCHIVE_/$ICECAST_VERSION/;" "$i";
done
fi
$SCRIPT_DIR/../fix-dsc.sh tar -C "$ICECAST_PROJECT" -cvzf "$ICECAST_PROJECT/icecast2_$ICECAST_CI_VERSION-1.debian.tar.gz" debian/
popd # remove debian/ so it does not end up in the archive
rm -rf "$ICECAST_PROJECT/debian"
# we use addremove to detect changes and commit them to the server # we fix the dsc to have the correct hashsums so dpkg-buildpackage and co do not complain
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
pushd $i
$OSC_CMD addremove pushd "$ICECAST_PROJECT"
$OSC_CMD diff
$OSC_CMD commit -m "Commit via $CI_PIPELINE_URL" "$SCRIPT_DIR/../fix-dsc.sh"
popd popd
# we use addremove to detect changes and commit them to the server
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
pushd "$i"
$OSC_CMD addremove
$OSC_CMD diff
$OSC_CMD commit -m "Commit via $CI_PIPELINE_URL - Tag: ${CI_COMMIT_TAG:-N/A} - Branch: ${GIT_BRANCH:-N/A} - Commit: $GIT_COMMIT - Release Author: ${RELEASE_AUTHOR} - Commit Author: ${CI_COMMIT_AUTHOR}"
popd
done
done done
# we cleanup because the OSC_RC should not remain on disk # we cleanup because the OSC_RC should not remain on disk
if [ "$NOCLEANUP" != "1" ]; then if [ "$NOCLEANUP" != "1" ]; then
shred -vzf $OSC_RC .osc_cookiejar for file in "$OSC_RC" "$OSC_RC_FILE" .osc_cookiejar; do
cd .. [ -n "$file" ] && [ -f "$file" ] && shred -vzf "$file"
echo > $OSC_RC echo > "$file"
rm -rf "osc_tmp" done
rm -rf osc_tmp*
fi fi

View File

@ -41,7 +41,7 @@ BuildRequires: automake, pkgconfig
BuildRequires: libvorbis-devel >= 1.0, libogg-devel >= 1.0, curl-devel >= 7.10.0 BuildRequires: libvorbis-devel >= 1.0, libogg-devel >= 1.0, curl-devel >= 7.10.0
BuildRequires: libxml2-devel, libxslt-devel, speex-devel BuildRequires: libxml2-devel, libxslt-devel, speex-devel
# To be enabled as soon as Fedora's libtheora supports ogg_stream_init # To be enabled as soon as Fedora's libtheora supports ogg_stream_init
BuildRequires: libtheora-devel >= 1.0, openssl-devel BuildRequires: libtheora-devel >= 1.0, openssl-devel >= 1.1, rhash-devel, libigloo-devel >= 0.9.2
# From suse packaging # From suse packaging
BuildRequires: libtool, BuildRequires: libtool,

View File

@ -56,19 +56,23 @@ cp /etc/mime.types installer/
find installer/ find installer/
%build %build
cd installer cd installer
cp icecast.nsis icecast.nsis.orig
sed -i "s/\(\"DisplayVersion\" \"\).*\(\"\)$/\1%{version}\2/" icecast.nsis
sed -i 's/\(OutFile "icecast_win32_\).*\(.exe"\)$/\1%{version}\2/' icecast.nsis
diff -u icecast.nsis.orig icecast.nsis || true
export -n MALLOC_CHECK_ export -n MALLOC_CHECK_
export -n MALLOC_PERTURB_ export -n MALLOC_PERTURB_
makensis icecast.nsis -V4 makensis icecast.nsis -V4
%install %install
mkdir -p "%{buildroot}/%{_mingw32_bindir}" mkdir -p "%{buildroot}/%{_mingw32_bindir}"
cp %_builddir/installer/icecast_win32_2.5-beta3.exe "%{buildroot}/%{_mingw32_bindir}" cp %_builddir/installer/icecast_win32_%{version}.exe "%{buildroot}/%{_mingw32_bindir}"
%clean %clean
%files %files
%defattr(-,root,root) %defattr(-,root,root)
%{_mingw32_bindir}/icecast_win32_2.5-beta3.exe %{_mingw32_bindir}/icecast_win32_%{version}.exe
%changelog %changelog
* Sun Mar 13 2022 Philipp Schafft <lion@lion.leolix.org> - 2.4.99.3-1 * Sun Mar 13 2022 Philipp Schafft <lion@lion.leolix.org> - 2.4.99.3-1

View File

@ -26,7 +26,9 @@ BuildRequires: mingw32-libxml2-devel
BuildRequires: mingw32-libxslt-devel BuildRequires: mingw32-libxslt-devel
BuildRequires: mingw32-libspeex-devel BuildRequires: mingw32-libspeex-devel
BuildRequires: mingw32-libtheora-devel >= 1.0 BuildRequires: mingw32-libtheora-devel >= 1.0
BuildRequires: mingw32-libopenssl-devel BuildRequires: mingw32-libopenssl-1_1-devel
BuildRequires: mingw32-libigloo-devel >= 0.9.0
BuildRequires: mingw32-librhash-devel >= 0.9.0
%_mingw32_package_header_debug %_mingw32_package_header_debug
BuildArch: noarch BuildArch: noarch
@ -57,12 +59,13 @@ MINGW32_CFLAGS="%{_mingw32_cflags}" \
PATH="%{_mingw32_bindir}:$PATH" \ PATH="%{_mingw32_bindir}:$PATH" \
%{_mingw32_configure} \ %{_mingw32_configure} \
--with-curl=%{_mingw32_prefix} \ --with-curl=%{_mingw32_prefix} \
--disable-static --enable-shared #\ --disable-static --enable-shared
%{__make} %{?_smp_mflags}
%{_mingw32_make} %{?_smp_mflags}
%install %install
make DESTDIR=%{buildroot} install %{?_smp_mflags} %{_mingw32_makeinstall} %{?_smp_mflags}
find %{buildroot}
rm %{buildroot}%{_mingw32_sysconfdir}/icecast.xml rm %{buildroot}%{_mingw32_sysconfdir}/icecast.xml
cp -rfvp win32 %{buildroot}%{_mingw32_datadir}/icecast/win32 cp -rfvp win32 %{buildroot}%{_mingw32_datadir}/icecast/win32

View File

@ -1,22 +1,30 @@
GIT_BRANCH=${CI_COMMIT_BRANCH:?Please define CI_COMMIT_BRANCH} if [ "z$OBS_BASES" = "z" ]; then
if [ "z$OBS_BASE" = "z" ]; then
if [ "$GIT_BRANCH" = "master" ]; then if [ "$GIT_BRANCH" = "master" ]; then
export OBS_BASE=multimedia:xiph:nightly-master export OBS_BASES=$OBS_BASE_NIGHTLY_MASTER
elif [ "$GIT_BRANCH" = "devel" ]; then elif [ "$GIT_BRANCH" = "devel" ]; then
export OBS_BASE=multimedia:xiph:nightly-devel export OBS_BASES=$OBS_BASE_NIGHTLY_DEVEL
else else
echo "branch '$GIT_BRANCH' is not master or devel, please export OBS_BASE accordingly"; echo "branch '$GIT_BRANCH' is not master or devel, please export OBS_BASE accordingly";
exit 1 exit 1
fi fi
fi fi
export ICECAST_PROJECT=icecast
export W32_ICECAST_PROJECT=mingw32-icecast # How long should the hash be, overridable
export W32_ICECAST_INSTALLER_PROJECT=mingw32-icecast-installer GIT_HASH_LENGTH=${GIT_HASH_LENGTH:-4}
export ICECAST_BETA_VERSION=3 # Get the current commit ID
export ICECAST_VERSION=2.4.99.3 GIT_HASH=$(git rev-parse --short="$GIT_HASH_LENGTH" HEAD)
export ICECAST_CI_VERSION=$ICECAST_VERSION+`date +%Y%m%d%H%M%S`+`git rev-parse HEAD` # How long should the date be, we want it to be increasing
# Consider "nightly", but this can be overriden
SHORT_DATE_FORMAT=${SHORT_DATE_FORMAT:-%Y%m%d%H}
# Compute date for CI VERSION
SHORT_DATE=$(date --utc +"$SHORT_DATE_FORMAT")
ICECAST_PLUS_VERSION=${ICECAST_VERSION/-/+}
# CI_VERSION will be something like 2.5.0+202411171222+1234
export ICECAST_CI_VERSION=${ICECAST_PLUS_VERSION}+${SHORT_DATE}+${GIT_HASH}
export DISABLE_CHANGELOG=0 export DISABLE_CHANGELOG=0
export RELEASE_AUTHOR=${CI_COMMIT_AUTHOR:?Please set CI_COMMIT_AUTHOR} export RELEASE_AUTHOR=${CI_COMMIT_AUTHOR:?Please set CI_COMMIT_AUTHOR}
export RELEASE_DATETIME=now export RELEASE_DATETIME=now

View File

@ -1,7 +1,25 @@
#!/bin/sh -xe #!/bin/sh -xe
# Script used to make sure we are ready to use osc to upload to OBS
# Installing osc could (should) be done in the image
# Second part handle a OSC_RC set to a file variable from gitlab,
# in the proper path, with the proper permissions
# actual config path can be overridden using $OSC_RC_FILE
# Debug info for the pipeline
cat /etc/os* cat /etc/os*
apk update
apk add python3 py3-pip python3-dev openssl-dev g++ make swig coreutils bash ca-certificates git # We are alone using this container. Let's break stuff (someday use a venv).
pip install osc pip install osc --break-system-packages
which osc-wrapper.py
osc-wrapper.py --version OSC_RC_FILE=${OSC_RC_FILE:-$HOME/.config/osc/oscrc}
echo "Using $OSC_RC_FILE as osc config (override with \$OSC_RC_FILE)"
# if $OSC_RC is set, and the file exists, copy it as osc's config
if [ -n "$OSC_RC" ] && [ -f "$OSC_RC" ]; then
echo "Found config at $OSC_RC, copying to $OSC_RC_FILE"
install -m 600 "$OSC_RC" -D "$OSC_RC_FILE"
else
echo "\$OSC_RC not set or file does not exist"
exit 1
fi

View File

@ -1,18 +1,18 @@
if [ "z$OBS_BASE" = "z" ]; then if [ "z$OBS_BASES" = "z" ]; then
if [ "z$CI_COMMIT_TAG" != "z" ]; then if [ "z$CI_COMMIT_TAG" != "z" ]; then
export OBS_BASE=multimedia:xiph:beta if echo $CI_COMMIT_TAG | grep -q '^v[0-9]\+\.[0-9]\+\.[0-9]\+$'; then
export OBS_BASES="$OBS_BASE_RELEASE $OBS_BASE_BETA"
else
export OBS_BASES="$OBS_BASE_BETA"
fi
else else
echo "tag variable CI_COMMIT_TAG not defined, please export OBS_BASE accordingly"; echo "tag variable CI_COMMIT_TAG not defined, please export OBS_BASES accordingly";
exit 1 exit 1
fi fi
fi fi
export ICECAST_PROJECT=icecast
export W32_ICECAST_PROJECT=mingw32-icecast
export W32_ICECAST_INSTALLER_PROJECT=mingw32-icecast-installer
export ICECAST_BETA_VERSION=3
export ICECAST_VERSION=2.4.99.3 export ICECAST_VERSION=2.4.99.3
export ICECAST_CI_VERSION=$ICECAST_VERSION export ICECAST_CI_VERSION=${ICECAST_VERSION/-/+}
export DISABLE_CHANGELOG=1 export DISABLE_CHANGELOG=1
export RELEASE_AUTHOR="Philipp Schafft <lion@lion.leolix.org>" export RELEASE_AUTHOR="Philipp Schafft <lion@lion.leolix.org>"
export RELEASE_DATETIME=2022-03-13T18:25:33+00:00 export RELEASE_DATETIME=2022-03-13T18:25:33+00:00

View File

@ -243,6 +243,14 @@ PKG_HAVE_WITH_MODULES([OPENSSL], [openssl >= 1.1.0], [
LIBS="${LIBS} ${OPENSSL_LIBS}" LIBS="${LIBS} ${OPENSSL_LIBS}"
]) ])
dnl
dnl libmaxminddb
dnl
PKG_HAVE_WITH_MODULES([MAXMINDDB], [libmaxminddb >= 1.3.2], [
CFLAGS="${CFLAGS} ${MAXMINDDB_CFLAGS}"
LIBS="${LIBS} ${MAXMINDDB_LIBS}"
])
dnl dnl
dnl librhash - first try pkgconfig and then basic search dnl librhash - first try pkgconfig and then basic search
dnl since the function is defined in rhash.h we need to check for that first, dnl since the function is defined in rhash.h we need to check for that first,

View File

@ -34,12 +34,14 @@ noinst_HEADERS = \
prng.h \ prng.h \
matchfile.h \ matchfile.h \
tls.h \ tls.h \
geoip.h \
refobject.h \ refobject.h \
module.h \ module.h \
reportxml.h \ reportxml.h \
reportxml_helper.h \ reportxml_helper.h \
json.h \ json.h \
xml2json.h \ xml2json.h \
valuefile.h \
string_renderer.h \ string_renderer.h \
listensocket.h \ listensocket.h \
fastevent.h \ fastevent.h \
@ -88,12 +90,14 @@ icecast_SOURCES = \
prng.c \ prng.c \
matchfile.c \ matchfile.c \
tls.c \ tls.c \
geoip.c \
refobject.c \ refobject.c \
module.c \ module.c \
reportxml.c \ reportxml.c \
reportxml_helper.c \ reportxml_helper.c \
json.c \ json.c \
xml2json.c \ xml2json.c \
valuefile.c \
string_renderer.c \ string_renderer.c \
listensocket.c \ listensocket.c \
fastevent.c \ fastevent.c \

View File

@ -150,6 +150,12 @@
#define DEFAULT_HTML_REQUEST "" #define DEFAULT_HTML_REQUEST ""
#define BUILDM3U_RAW_REQUEST "buildm3u" #define BUILDM3U_RAW_REQUEST "buildm3u"
typedef struct {
size_t listeners;
size_t tls;
size_t ipv6;
} country_t;
typedef struct { typedef struct {
const char *prefix; const char *prefix;
size_t length; size_t length;
@ -577,6 +583,8 @@ xmlDocPtr admin_build_sourcelist(const char *mount, client_t *client, admin_form
snprintf(buf, sizeof(buf), "%"PRIu64, source->dumpfile_written); snprintf(buf, sizeof(buf), "%"PRIu64, source->dumpfile_written);
xmlNewTextChild(srcnode, NULL, XMLSTR("dumpfile_written"), XMLSTR(buf)); xmlNewTextChild(srcnode, NULL, XMLSTR("dumpfile_written"), XMLSTR(buf));
admin_add_geoip_to_mount(source, srcnode, client->mode);
} }
node = avl_get_next(node); node = avl_get_next(node);
} }
@ -922,6 +930,30 @@ static inline xmlNodePtr __add_listener(client_t *client,
xmlNewTextChild(node, NULL, XMLSTR("protocol"), XMLSTR(client_protocol_to_string(client->protocol))); xmlNewTextChild(node, NULL, XMLSTR("protocol"), XMLSTR(client_protocol_to_string(client->protocol)));
if (client->con) {
connection_t *con = client->con;
if (*con->geoip.iso_3166_1_alpha_2 || con->geoip.have_latitude || con->geoip.have_longitude) {
xmlNodePtr geoip = xmlNewChild(node, NULL, XMLSTR("geoip"), NULL);
if (*con->geoip.iso_3166_1_alpha_2) {
xmlNodePtr country = xmlNewChild(geoip, NULL, XMLSTR("country"), NULL);
xmlSetProp(country, XMLSTR("iso-alpha-2"), XMLSTR(con->geoip.iso_3166_1_alpha_2));
}
if (con->geoip.have_latitude || con->geoip.have_longitude) {
xmlNodePtr location = xmlNewChild(geoip, NULL, XMLSTR("location"), NULL);
if (con->geoip.have_latitude) {
snprintf(buf, sizeof(buf), "%f", con->geoip.latitude);
xmlSetProp(location, XMLSTR("latitude"), XMLSTR(buf));
}
if (con->geoip.have_longitude) {
snprintf(buf, sizeof(buf), "%f", con->geoip.longitude);
xmlSetProp(location, XMLSTR("longitude"), XMLSTR(buf));
}
}
}
}
do { do {
xmlNodePtr history = xmlNewChild(node, NULL, XMLSTR("history"), NULL); xmlNodePtr history = xmlNewChild(node, NULL, XMLSTR("history"), NULL);
size_t i; size_t i;
@ -950,6 +982,80 @@ void admin_add_listeners_to_mount(source_t *source,
avl_tree_unlock(source->client_tree); avl_tree_unlock(source->client_tree);
} }
static void admin_add_geoip_to_mount__country(source_t *source,
xmlNodePtr parent,
operation_mode mode,
const char *code,
country_t *country)
{
if (country->listeners) {
xmlNodePtr node = xmlNewChild(parent, NULL, XMLSTR("country"), NULL);
char buf[22];
if (code)
xmlSetProp(node, XMLSTR("iso-alpha-2"), XMLSTR(code));
snprintf(buf, sizeof(buf), "%llu", (long long unsigned)country->listeners);
xmlNewTextChild(node, NULL, XMLSTR("listeners"), XMLSTR(buf));
snprintf(buf, sizeof(buf), "%llu", (long long unsigned)country->tls);
xmlNewTextChild(node, NULL, XMLSTR("tls"), XMLSTR(buf));
snprintf(buf, sizeof(buf), "%llu", (long long unsigned)country->ipv6);
xmlNewTextChild(node, NULL, XMLSTR("ipv6"), XMLSTR(buf));
}
}
void admin_add_geoip_to_mount(source_t *source,
xmlNodePtr parent,
operation_mode mode)
{
avl_node *client_node;
xmlNodePtr geoip = xmlNewChild(parent, NULL, XMLSTR("geoip"), NULL);
country_t countries[26][26];
country_t default_country;
memset(countries, 0, sizeof(countries));
memset(&default_country, 0, sizeof(default_country));
avl_tree_rlock(source->client_tree);
client_node = avl_get_first(source->client_tree);
while(client_node) {
client_t *client = client_node->key;
connection_t *con = client->con;
country_t *country = &default_country;
if (con && *con->geoip.iso_3166_1_alpha_2) {
const char *iso = client->con->geoip.iso_3166_1_alpha_2;
if ((iso[0] >= 'a' && iso[0] <= 'z') && (iso[1] >= 'a' && iso[1] <= 'z')) {
country = &(countries[iso[0] - 'a'][iso[1] - 'a']);
}
}
country->listeners++;
if (con) {
if (con->tls)
country->tls++;
if (con->ip && strchr(con->ip, ':'))
country->ipv6++;
}
client_node = avl_get_next(client_node);
}
avl_tree_unlock(source->client_tree);
for (size_t idx_a = 0; idx_a < 26; idx_a++) {
for (size_t idx_b = 0; idx_b < 26; idx_b++) {
const char code[3] = {idx_a + 'a', idx_b + 'a', 0};
admin_add_geoip_to_mount__country(source, geoip, mode, code, &(countries[idx_a][idx_b]));
}
}
admin_add_geoip_to_mount__country(source, geoip, mode, NULL, &default_country);
}
static void command_show_listeners(client_t *client, static void command_show_listeners(client_t *client,
source_t *source, source_t *source,
admin_format_t response) admin_format_t response)
@ -970,6 +1076,7 @@ static void command_show_listeners(client_t *client,
xmlNewTextChild(srcnode, NULL, XMLSTR(client->mode == OMODE_LEGACY ? "Listeners" : "listeners"), XMLSTR(buf)); xmlNewTextChild(srcnode, NULL, XMLSTR(client->mode == OMODE_LEGACY ? "Listeners" : "listeners"), XMLSTR(buf));
admin_add_listeners_to_mount(source, srcnode, client->mode); admin_add_listeners_to_mount(source, srcnode, client->mode);
admin_add_geoip_to_mount(source, srcnode, client->mode);
admin_send_response(doc, client, response, admin_send_response(doc, client, response,
LISTCLIENTS_HTML_REQUEST); LISTCLIENTS_HTML_REQUEST);

View File

@ -61,6 +61,11 @@ void admin_add_listeners_to_mount(source_t *source,
xmlNodePtr parent, xmlNodePtr parent,
operation_mode mode); operation_mode mode);
void admin_add_geoip_to_mount(source_t *source,
xmlNodePtr parent,
operation_mode mode);
xmlNodePtr admin_add_role_to_authentication(auth_t *auth, xmlNodePtr parent); xmlNodePtr admin_add_role_to_authentication(auth_t *auth, xmlNodePtr parent);
admin_command_id_t admin_get_command(const char *command); admin_command_id_t admin_get_command(const char *command);

View File

@ -55,6 +55,7 @@
#include "slave.h" #include "slave.h"
#include "xslt.h" #include "xslt.h"
#include "prng.h" #include "prng.h"
#include "geoip.h"
#define CATMODULE "CONFIG" #define CATMODULE "CONFIG"
#define RANGE_PORT 1, 65535 #define RANGE_PORT 1, 65535
@ -275,6 +276,23 @@ static fallback_override_t config_str_to_fallback_override_t(ice_config_t *confi
} }
} }
static interpolation_t config_str_to_interpolation_t(ice_config_t *configuration, xmlNodePtr node, const char *str)
{
if (!str || !*str || strcmp(str, "default") == 0) {
return INTERPOLATION_DEFAULT;
} else if (strcasecmp(str, "none") == 0) {
return INTERPOLATION_NONE;
} else if (strcasecmp(str, "strftime") == 0) {
return INTERPOLATION_STRFTIME;
} else if (strcasecmp(str, "uuid") == 0) {
return INTERPOLATION_UUID;
} else {
__found_bad_tag(configuration, node, BTR_INVALID, str);
ICECAST_LOG_ERROR("Unknown interpolation type \"%s\", falling back to DEFAULT.", str);
return INTERPOLATION_DEFAULT;
}
}
char * config_href_to_id(ice_config_t *configuration, xmlNodePtr node, const char *href) char * config_href_to_id(ice_config_t *configuration, xmlNodePtr node, const char *href)
{ {
if (!href || !*href) if (!href || !*href)
@ -914,6 +932,7 @@ void config_clear(ice_config_t *c)
if (c->log_dir) xmlFree(c->log_dir); if (c->log_dir) xmlFree(c->log_dir);
if (c->webroot_dir) xmlFree(c->webroot_dir); if (c->webroot_dir) xmlFree(c->webroot_dir);
if (c->adminroot_dir) xmlFree(c->adminroot_dir); if (c->adminroot_dir) xmlFree(c->adminroot_dir);
if (c->geoipdbfile) xmlFree(c->geoipdbfile);
if (c->null_device) xmlFree(c->null_device); if (c->null_device) xmlFree(c->null_device);
if (c->pidfile) xmlFree(c->pidfile); if (c->pidfile) xmlFree(c->pidfile);
if (c->banfile) xmlFree(c->banfile); if (c->banfile) xmlFree(c->banfile);
@ -1004,6 +1023,8 @@ void config_reread_config(void)
restart_logging(config); restart_logging(config);
prng_configure(config); prng_configure(config);
main_config_reload(config); main_config_reload(config);
igloo_ro_unref(&global.geoip_db);
global.geoip_db = geoip_db_new(config->geoipdbfile);
connection_reread_config(config); connection_reread_config(config);
yp_recheck_config(config); yp_recheck_config(config);
fserve_recheck_mime_types(config); fserve_recheck_mime_types(config);
@ -1744,8 +1765,13 @@ static void _parse_mount(xmlDocPtr doc,
password = (char *)xmlNodeListGetString(doc, password = (char *)xmlNodeListGetString(doc,
node->xmlChildrenNode, 1); node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("dump-file")) == 0) { } else if (xmlStrcmp(node->name, XMLSTR("dump-file")) == 0) {
xmlChar * interpolation = xmlGetProp(node, XMLSTR("interpolation"));
if (interpolation) {
mount->dumpfile_interpolation = config_str_to_interpolation_t(configuration, node, (const char *)interpolation);
xmlFree(interpolation);
}
mount->dumpfile = (char *)xmlNodeListGetString(doc, mount->dumpfile = (char *)xmlNodeListGetString(doc,
node->xmlChildrenNode, 1); node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("dump-file-size-limit")) == 0) { } else if (xmlStrcmp(node->name, XMLSTR("dump-file-size-limit")) == 0) {
unsigned int val = mount->dumpfile_size_limit; unsigned int val = mount->dumpfile_size_limit;
__read_unsigned_int(configuration, doc, node, &val, 0, UINT_MAX); __read_unsigned_int(configuration, doc, node, &val, 0, UINT_MAX);
@ -2719,6 +2745,14 @@ static void _parse_paths(xmlDocPtr doc,
} }
} }
xmlFree(temp); xmlFree(temp);
} else if (xmlStrcmp(node->name, XMLSTR("geoipdb")) == 0) {
if (!(temp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1))) {
ICECAST_LOG_WARN("<geoipdb> setting must not be empty.");
continue;
}
if (configuration->geoipdbfile)
xmlFree(configuration->geoipdbfile);
configuration->geoipdbfile = (char *)temp;
} else if (xmlStrcmp(node->name, XMLSTR("resource")) == 0 || xmlStrcmp(node->name, XMLSTR("alias")) == 0) { } else if (xmlStrcmp(node->name, XMLSTR("resource")) == 0 || xmlStrcmp(node->name, XMLSTR("alias")) == 0) {
_parse_resource(doc, node, configuration); _parse_resource(doc, node, configuration);
} else { } else {
@ -3008,6 +3042,8 @@ static void merge_mounts(mount_proxy * dst, mount_proxy * src)
if (!dst->dumpfile) if (!dst->dumpfile)
dst->dumpfile = (char*)xmlStrdup((xmlChar*)src->dumpfile); dst->dumpfile = (char*)xmlStrdup((xmlChar*)src->dumpfile);
if (dst->dumpfile_interpolation == INTERPOLATION_DEFAULT)
dst->dumpfile_interpolation = src->dumpfile_interpolation;
if (!dst->dumpfile_size_limit) if (!dst->dumpfile_size_limit)
dst->dumpfile_size_limit = src->dumpfile_size_limit; dst->dumpfile_size_limit = src->dumpfile_size_limit;
if (!dst->dumpfile_time_limit) if (!dst->dumpfile_time_limit)

View File

@ -79,6 +79,13 @@ typedef enum {
FALLBACK_OVERRIDE_OWN FALLBACK_OVERRIDE_OWN
} fallback_override_t; } fallback_override_t;
typedef enum {
INTERPOLATION_DEFAULT = 0,
INTERPOLATION_NONE,
INTERPOLATION_STRFTIME,
INTERPOLATION_UUID
} interpolation_t;
typedef struct _mount_proxy { typedef struct _mount_proxy {
/* The mountpoint this proxy is used for */ /* The mountpoint this proxy is used for */
char *mountname; char *mountname;
@ -88,6 +95,7 @@ typedef struct _mount_proxy {
* NULL to not dump. * NULL to not dump.
*/ */
char *dumpfile; char *dumpfile;
interpolation_t dumpfile_interpolation;
uint64_t dumpfile_size_limit; uint64_t dumpfile_size_limit;
unsigned int dumpfile_time_limit; unsigned int dumpfile_time_limit;
/* Send contents of file to client before the stream */ /* Send contents of file to client before the stream */
@ -288,6 +296,7 @@ struct ice_config_tag {
char *allowfile; char *allowfile;
char *webroot_dir; char *webroot_dir;
char *adminroot_dir; char *adminroot_dir;
char *geoipdbfile;
prng_seed_config_t *prng_seed; prng_seed_config_t *prng_seed;
resource_t *resources; resource_t *resources;
reportxml_database_t *reportxml_db; reportxml_database_t *reportxml_db;

View File

@ -57,6 +57,7 @@
#include "acl.h" #include "acl.h"
#include "listensocket.h" #include "listensocket.h"
#include "fastevent.h" #include "fastevent.h"
#include "geoip.h"
/* for ADMIN_COMMAND_ERROR, and ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION */ /* for ADMIN_COMMAND_ERROR, and ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION */
#include "admin.h" #include "admin.h"
@ -189,6 +190,8 @@ int client_create(client_t **c_ptr, connection_t *con, http_parser_t *parser)
fastevent_emit(FASTEVENT_TYPE_CLIENT_CREATE, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client); fastevent_emit(FASTEVENT_TYPE_CLIENT_CREATE, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
geoip_lookup_client(global.geoip_db, client);
return ret; return ret;
} }

View File

@ -16,6 +16,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#include <stdbool.h>
#include "tls.h" #include "tls.h"
@ -64,6 +65,14 @@ struct connection_tag {
/* IP Address of the client as seen by the server */ /* IP Address of the client as seen by the server */
char *ip; char *ip;
struct {
double latitude;
double longitude;
bool have_latitude;
bool have_longitude;
char iso_3166_1_alpha_2[3]; /* 2 bytes plus \0 */
} geoip;
}; };
void connection_initialize(void); void connection_initialize(void);

View File

@ -16,132 +16,134 @@
#include "logging.h" #include "logging.h"
#define CATMODULE "errors" #define CATMODULE "errors"
#define BASE_DEF(x) .id = (x), .symbol = # x
// cut -d' ' -f2 x | while read x; do printf " {.id = %-60s .http_status = xxx,\n .message = \"\"},\n" "$x",; done // cut -d' ' -f2 x | while read x; do printf " {.id = %-60s .http_status = xxx,\n .message = \"\"},\n" "$x",; done
static const icecast_error_t __errors[] = { static const icecast_error_t __errors[] = {
{.id = ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING), .http_status = 400,
.uuid = "52735a81-16fe-4d7e-9984-5aed8a941055", .uuid = "52735a81-16fe-4d7e-9984-5aed8a941055",
.message = "Destination not running"}, .message = "Destination not running"},
{.id = ICECAST_ERROR_ADMIN_METADAT_BADCALL, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_METADAT_BADCALL), .http_status = 400,
.uuid = "85d33e67-5c4e-4511-b4fa-3ca69ccd03de", .uuid = "85d33e67-5c4e-4511-b4fa-3ca69ccd03de",
.message = "illegal metadata call"}, .message = "illegal metadata call"},
{.id = ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION, .http_status = 501, {BASE_DEF(ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION), .http_status = 501,
.uuid = "14f4d814-98d9-468c-8a0b-ba5e74c9d771", .uuid = "14f4d814-98d9-468c-8a0b-ba5e74c9d771",
.message = "No such action"}, .message = "No such action"},
{.id = ICECAST_ERROR_ADMIN_MISSING_PARAMETER, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_MISSING_PARAMETER), .http_status = 400,
.uuid = "cb11dc71-6149-454c-8d4e-47a3af26b03a", .uuid = "cb11dc71-6149-454c-8d4e-47a3af26b03a",
.message = "Missing parameter"}, .message = "Missing parameter"},
{.id = ICECAST_ERROR_ADMIN_missing_parameter, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_missing_parameter), .http_status = 400,
.uuid = "8be9ef0a-2b32-450c-aec9-a414ca0c074c", .uuid = "8be9ef0a-2b32-450c-aec9-a414ca0c074c",
.message = "missing parameter"}, .message = "missing parameter"},
{.id = ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES, .http_status = 422, {BASE_DEF(ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES), .http_status = 422,
.uuid = "3bed51bb-a10f-4af3-9965-4e67181de7d6", .uuid = "3bed51bb-a10f-4af3-9965-4e67181de7d6",
.message = "mountpoint will not accept URL updates"}, .message = "mountpoint will not accept URL updates"},
{.id = ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION, .http_status = 404, {BASE_DEF(ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION), .http_status = 404,
.uuid = "c5f1ee06-46a0-4697-9f01-6e9fc333d555", .uuid = "c5f1ee06-46a0-4697-9f01-6e9fc333d555",
.message = "No such destination"}, .message = "No such destination"},
{.id = ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS, .http_status = 501, {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS), .http_status = 501,
.uuid = "7e1a8426-2ae1-4a6b-bfd9-59d8f8153021", .uuid = "7e1a8426-2ae1-4a6b-bfd9-59d8f8153021",
.message = "Adding users to role not supported by role"}, .message = "Adding users to role not supported by role"},
{.id = ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS, .http_status = 501, {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS), .http_status = 501,
.uuid = "367fbad1-389e-4292-bba8-c97984e616cc", .uuid = "367fbad1-389e-4292-bba8-c97984e616cc",
.message = "Deleting users from role not supported by role"}, .message = "Deleting users from role not supported by role"},
{.id = ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND, .http_status = 404, {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND), .http_status = 404,
.uuid = "59fe9c81-8c34-49ff-800f-7ec42ea498be", .uuid = "59fe9c81-8c34-49ff-800f-7ec42ea498be",
.message = "Role not found"}, .message = "Role not found"},
{.id = ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST, .http_status = 404, {BASE_DEF(ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST), .http_status = 404,
.uuid = "2f51a026-02e4-4fe4-bf9d-cc16557b3b65", .uuid = "2f51a026-02e4-4fe4-bf9d-cc16557b3b65",
.message = "Source does not exist"}, .message = "Source does not exist"},
{.id = ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE), .http_status = 400,
.uuid = "00b9d977-f41d-455f-820f-6d457dffb246", .uuid = "00b9d977-f41d-455f-820f-6d457dffb246",
.message = "Source is not available"}, .message = "Source is not available"},
{.id = ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL), .http_status = 400,
.uuid = "4be9a010-7a3f-44e4-b74d-3c6d9c4f7236", .uuid = "4be9a010-7a3f-44e4-b74d-3c6d9c4f7236",
.message = "supplied mountpoints are identical"}, .message = "supplied mountpoints are identical"},
{.id = ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND, .http_status = 400, {BASE_DEF(ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND), .http_status = 400,
.uuid = "811bddac-5be5-4580-9cde-7b849e66dfe5", .uuid = "811bddac-5be5-4580-9cde-7b849e66dfe5",
.message = "unrecognised command"}, .message = "unrecognised command"},
{.id = ICECAST_ERROR_AUTH_BUSY, .http_status = 503, {BASE_DEF(ICECAST_ERROR_AUTH_BUSY), .http_status = 503,
.uuid = "26708754-8f98-4191-81d1-7fb7246200d6", .uuid = "26708754-8f98-4191-81d1-7fb7246200d6",
.message = "busy, please try again later"}, .message = "busy, please try again later"},
{.id = ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS, .http_status = 415, {BASE_DEF(ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS), .http_status = 415,
.uuid = "f684ad3c-513b-4d87-9a66-424788bc6adb", .uuid = "f684ad3c-513b-4d87-9a66-424788bc6adb",
.message = "Content-type not supported"}, .message = "Content-type not supported"},
{.id = ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR, .http_status = 500, {BASE_DEF(ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR), .http_status = 500,
.uuid = "47a4b11b-5d2a-46e2-8948-942e7b0af3e6", .uuid = "47a4b11b-5d2a-46e2-8948-942e7b0af3e6",
.message = "internal format allocation problem"}, .message = "internal format allocation problem"},
{.id = ICECAST_ERROR_CON_MISSING_PASS_PARAMETER, .http_status = 400 /* XXX */, {BASE_DEF(ICECAST_ERROR_CON_MISSING_PASS_PARAMETER), .http_status = 400 /* XXX */,
.uuid = "b59c3a05-e2b1-4a14-8798-bbe1ae46603b", .uuid = "b59c3a05-e2b1-4a14-8798-bbe1ae46603b",
.message = "missing pass parameter"}, .message = "missing pass parameter"},
{.id = ICECAST_ERROR_CON_MOUNT_IN_USE, .http_status = 409, {BASE_DEF(ICECAST_ERROR_CON_MOUNT_IN_USE), .http_status = 409,
.uuid = "c5724467-5f85-48c7-b45a-915c3150c292", .uuid = "c5724467-5f85-48c7-b45a-915c3150c292",
.message = "Mountpoint in use"}, .message = "Mountpoint in use"},
{.id = ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH, .http_status = 400, {BASE_DEF(ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH), .http_status = 400,
.uuid = "1ae45ead-40fc-4de2-b56f-e54d3247f2ee", .uuid = "1ae45ead-40fc-4de2-b56f-e54d3247f2ee",
.message = "source mountpoint not starting with /"}, .message = "source mountpoint not starting with /"},
{.id = ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN, .http_status = 400, {BASE_DEF(ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN), .http_status = 400,
.uuid = "2cd86778-ac30-49e7-a108-26d627a7923b", .uuid = "2cd86778-ac30-49e7-a108-26d627a7923b",
.message = "No Content-type given"}, .message = "No Content-type given"},
{.id = ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT, .http_status = 429, {BASE_DEF(ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT), .http_status = 429,
.uuid = "9c72c1ec-f638-4d33-a077-6acbbff25317", .uuid = "9c72c1ec-f638-4d33-a077-6acbbff25317",
.message = "Reached limit of concurrent connections on those credentials"}, .message = "Reached limit of concurrent connections on those credentials"},
{.id = ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT, .http_status = 503, {BASE_DEF(ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT), .http_status = 503,
.uuid = "c770182d-c854-422a-a8e5-7142689234a3", .uuid = "c770182d-c854-422a-a8e5-7142689234a3",
.message = "too many sources connected"}, .message = "too many sources connected"},
{.id = ICECAST_ERROR_CON_UNIMPLEMENTED, .http_status = 501, {BASE_DEF(ICECAST_ERROR_CON_UNIMPLEMENTED), .http_status = 501,
.uuid = "58ce6cb4-72b4-49da-8ad2-feaf775bc61e", .uuid = "58ce6cb4-72b4-49da-8ad2-feaf775bc61e",
.message = "Unimplemented"}, .message = "Unimplemented"},
{.id = ICECAST_ERROR_CON_UNKNOWN_REQUEST, .http_status = 405, {BASE_DEF(ICECAST_ERROR_CON_UNKNOWN_REQUEST), .http_status = 405,
.uuid = "78f590cc-8812-40d5-a4ef-17344ab75b35", .uuid = "78f590cc-8812-40d5-a4ef-17344ab75b35",
.message = "unknown request"}, .message = "unknown request"},
{.id = ICECAST_ERROR_CON_UPGRADE_ERROR, .http_status = 400 /* XXX */, {BASE_DEF(ICECAST_ERROR_CON_UPGRADE_ERROR), .http_status = 400 /* XXX */,
.uuid = "ec16f654-f262-415f-ab91-95703ae33704", .uuid = "ec16f654-f262-415f-ab91-95703ae33704",
.message = "Can not upgrade protocol"}, .message = "Can not upgrade protocol"},
{.id = ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS, .http_status = 400 /* XXX */, {BASE_DEF(ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS), .http_status = 400 /* XXX */,
.uuid = "652548c6-2a7d-4c73-a1c5-e53759032bd1", .uuid = "652548c6-2a7d-4c73-a1c5-e53759032bd1",
.message = "Mountpoint is not available for direct access"}, .message = "Mountpoint is not available for direct access"},
{.id = ICECAST_ERROR_FSERV_FILE_NOT_FOUND, .http_status = 404, {BASE_DEF(ICECAST_ERROR_FSERV_FILE_NOT_FOUND), .http_status = 404,
.uuid = "18c32b43-0d8e-469d-b434-10133cdd06ad", .uuid = "18c32b43-0d8e-469d-b434-10133cdd06ad",
.message = "The file you requested could not be found"}, .message = "The file you requested could not be found"},
{.id = ICECAST_ERROR_FSERV_FILE_NOT_READABLE, .http_status = 404 /* XXX */, {BASE_DEF(ICECAST_ERROR_FSERV_FILE_NOT_READABLE), .http_status = 404 /* XXX */,
.uuid = "c883d55d-fb41-4f4c-8800-563f5542f51d", .uuid = "c883d55d-fb41-4f4c-8800-563f5542f51d",
.message = "File not readable"}, .message = "File not readable"},
{.id = ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE, .http_status = 416, {BASE_DEF(ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE), .http_status = 416,
.uuid = "5874cc51-770b-42b5-82d2-737b2b406b30", .uuid = "5874cc51-770b-42b5-82d2-737b2b406b30",
.message = "Request Range Not Satisfiable"}, .message = "Request Range Not Satisfiable"},
{.id = ICECAST_ERROR_GEN_BUFFER_REALLOC, .http_status = 500, {BASE_DEF(ICECAST_ERROR_GEN_BUFFER_REALLOC), .http_status = 500,
.uuid = "cda8203e-f237-4090-8d43-544efdd6295c", .uuid = "cda8203e-f237-4090-8d43-544efdd6295c",
.message = "Buffer reallocation failed."}, .message = "Buffer reallocation failed."},
{.id = ICECAST_ERROR_GEN_CLIENT_LIMIT, .http_status = 503, {BASE_DEF(ICECAST_ERROR_GEN_CLIENT_LIMIT), .http_status = 503,
.uuid = "87fd3e61-6702-4473-b506-f616d27a142f", .uuid = "87fd3e61-6702-4473-b506-f616d27a142f",
.message = "Icecast connection limit reached"}, .message = "Icecast connection limit reached"},
{.id = ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE, .http_status = 401, {BASE_DEF(ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE), .http_status = 401,
.uuid = "25387198-0643-4577-9139-7c4f24f59d4a", .uuid = "25387198-0643-4577-9139-7c4f24f59d4a",
.message = "You need to authenticate"}, .message = "You need to authenticate"},
{.id = ICECAST_ERROR_GEN_HEADER_GEN_FAILED, .http_status = 500, {BASE_DEF(ICECAST_ERROR_GEN_HEADER_GEN_FAILED), .http_status = 500,
.uuid = "a8b3c3fe-cb87-45fe-9a9d-ee4c2075d43a", .uuid = "a8b3c3fe-cb87-45fe-9a9d-ee4c2075d43a",
.message = "Header generation failed."}, .message = "Header generation failed."},
{.id = ICECAST_ERROR_GEN_MEMORY_EXHAUSTED, .http_status = 503, {BASE_DEF(ICECAST_ERROR_GEN_MEMORY_EXHAUSTED), .http_status = 503,
.uuid = "18411e73-713e-4910-b7e4-52a2e324b4e0", .uuid = "18411e73-713e-4910-b7e4-52a2e324b4e0",
.message = "memory exhausted"}, .message = "memory exhausted"},
{.id = ICECAST_ERROR_GEN_SAFE_METHOD_ON_UNSAFE_CALL, .http_status = 405, {BASE_DEF(ICECAST_ERROR_GEN_SAFE_METHOD_ON_UNSAFE_CALL), .http_status = 405,
.uuid = "6f4c95e3-b446-4814-b4b8-0cb585dbe4bd", .uuid = "6f4c95e3-b446-4814-b4b8-0cb585dbe4bd",
.message = "Safe HTTP method used on unsafe call"}, .message = "Safe HTTP method used on unsafe call"},
{.id = ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE, .http_status = 404 /* XXX */, {BASE_DEF(ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE), .http_status = 404 /* XXX */,
.uuid = "88d06875-fcf2-4417-84af-05866c97745c", .uuid = "88d06875-fcf2-4417-84af-05866c97745c",
.message = "Mount unavailable"}, .message = "Mount unavailable"},
{.id = ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR, .http_status = 500 /* XXX */, {BASE_DEF(ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR), .http_status = 500 /* XXX */,
.uuid = "9e50d94d-f03d-4515-8216-577bf8e9f70d", .uuid = "9e50d94d-f03d-4515-8216-577bf8e9f70d",
.message = "Stream preparation error"}, .message = "Stream preparation error"},
{.id = ICECAST_ERROR_SOURCE_MAX_LISTENERS, .http_status = 503, {BASE_DEF(ICECAST_ERROR_SOURCE_MAX_LISTENERS), .http_status = 503,
.uuid = "df147168-baaa-4959-82a4-746a1232927d", .uuid = "df147168-baaa-4959-82a4-746a1232927d",
.message = "Maximum listeners reached for this source"}, .message = "Maximum listeners reached for this source"},
{.id = ICECAST_ERROR_XSLT_PARSE, .http_status = 404 /* XXX */, {BASE_DEF(ICECAST_ERROR_XSLT_PARSE), .http_status = 404 /* XXX */,
.uuid = "f86b5b28-c1f8-49f6-a4cd-a18e2a6a44fd", .uuid = "f86b5b28-c1f8-49f6-a4cd-a18e2a6a44fd",
.message = "Could not parse XSLT file"}, .message = "Could not parse XSLT file"},
{.id = ICECAST_ERROR_XSLT_problem, .http_status = 500, {BASE_DEF(ICECAST_ERROR_XSLT_problem), .http_status = 500,
.uuid = "d3c6e4b3-7d6e-4191-a81b-970273067ae3", .uuid = "d3c6e4b3-7d6e-4191-a81b-970273067ae3",
.message = "XSLT problem"}, .message = "XSLT problem"},
{.id = ICECAST_ERROR_RECURSIVE_ERROR, .http_status = 500, {BASE_DEF(ICECAST_ERROR_RECURSIVE_ERROR), .http_status = 500,
.uuid = "13489d5c-eae6-4bf3-889e-ec1fa9a9b9ac", .uuid = "13489d5c-eae6-4bf3-889e-ec1fa9a9b9ac",
.message = "Recursive error"} .message = "Recursive error"}
}; };
@ -170,3 +172,10 @@ const icecast_error_t * error_get_by_uuid(const char *uuid)
return NULL; return NULL;
} }
const icecast_error_t * error_get_by_index(size_t idx) {
if (idx < (sizeof(__errors)/sizeof(*__errors))) {
return &(__errors[idx]);
}
return NULL;
}

View File

@ -59,11 +59,13 @@ struct icecast_error_tag {
const int http_status; const int http_status;
const char *uuid; const char *uuid;
const char *message; const char *message;
const char *symbol;
}; };
typedef struct icecast_error_tag icecast_error_t; typedef struct icecast_error_tag icecast_error_t;
const icecast_error_t * error_get_by_id(icecast_error_id_t id); const icecast_error_t * error_get_by_id(icecast_error_id_t id);
const icecast_error_t * error_get_by_uuid(const char *uuid); const icecast_error_t * error_get_by_uuid(const char *uuid);
const icecast_error_t * error_get_by_index(size_t idx);
#endif /* __ERRORS_H__ */ #endif /* __ERRORS_H__ */

View File

@ -26,6 +26,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "refbuf.h" #include "refbuf.h"
#include "source.h" #include "source.h"
#include "client.h" #include "client.h"
@ -211,7 +215,9 @@ int format_ebml_get_plugin(source_t *source)
plugin->set_tag = NULL; plugin->set_tag = NULL;
plugin->apply_settings = NULL; plugin->apply_settings = NULL;
plugin->contenttype = httpp_getvar(source->parser, "content-type"); if (igloo_sp_replace(httpp_getvar(source->parser, "content-type"), &(plugin->contenttype), igloo_instance) != igloo_ERROR_NONE) {
ICECAST_LOG_ERROR("Cannot set content type for EBML source %#H. BAD.", source->mount);
}
plugin->_state = ebml_source_state; plugin->_state = ebml_source_state;
vorbis_comment_init(&plugin->vc); vorbis_comment_init(&plugin->vc);

View File

@ -30,6 +30,9 @@
# include <strings.h> # include <strings.h>
#endif #endif
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h" #include "global.h"
#include "refbuf.h" #include "refbuf.h"
#include "event.h" #include "event.h"
@ -78,6 +81,7 @@ int format_mp3_get_plugin(source_t *source)
format_plugin_t *plugin; format_plugin_t *plugin;
mp3_state *state = calloc(1, sizeof(mp3_state)); mp3_state *state = calloc(1, sizeof(mp3_state));
refbuf_t *meta; refbuf_t *meta;
const char *contenttype;
plugin = (format_plugin_t *) calloc(1, sizeof(format_plugin_t)); plugin = (format_plugin_t *) calloc(1, sizeof(format_plugin_t));
@ -90,10 +94,14 @@ int format_mp3_get_plugin(source_t *source)
plugin->set_tag = mp3_set_tag; plugin->set_tag = mp3_set_tag;
plugin->apply_settings = format_mp3_apply_settings; plugin->apply_settings = format_mp3_apply_settings;
plugin->contenttype = httpp_getvar(source->parser, "content-type"); contenttype = httpp_getvar(source->parser, "content-type");
if (plugin->contenttype == NULL) { if (contenttype == NULL) {
/* We default to MP3 audio for old clients without content types */ /* We default to MP3 audio for old clients without content types */
plugin->contenttype = "audio/mpeg"; contenttype = "audio/mpeg";
}
if (igloo_sp_replace(contenttype, &(plugin->contenttype), igloo_instance) != igloo_ERROR_NONE) {
ICECAST_LOG_ERROR("Cannot set content type for legacy source %#H. BAD.", source->mount);
} }
plugin->_state = state; plugin->_state = state;

View File

@ -27,6 +27,10 @@
#include <ogg/ogg.h> #include <ogg/ogg.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "refbuf.h" #include "refbuf.h"
#include "source.h" #include "source.h"
#include "client.h" #include "client.h"
@ -178,7 +182,10 @@ int format_ogg_get_plugin(source_t *source)
plugin->set_tag = NULL; plugin->set_tag = NULL;
if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0) if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
httpp_setvar (source->parser, "content-type", "application/ogg"); httpp_setvar (source->parser, "content-type", "application/ogg");
plugin->contenttype = httpp_getvar (source->parser, "content-type");
if (igloo_sp_replace(httpp_getvar(source->parser, "content-type"), &(plugin->contenttype), igloo_instance) != igloo_ERROR_NONE) {
ICECAST_LOG_ERROR("Cannot set content type for Ogg source %#H. BAD.", source->mount);
}
ogg_sync_init (&state->oy); ogg_sync_init (&state->oy);
vorbis_comment_init(&plugin->vc); vorbis_comment_init(&plugin->vc);

View File

@ -15,6 +15,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "source.h" #include "source.h"
#include "format.h" #include "format.h"
#include "format_text.h" #include "format_text.h"
@ -105,7 +109,9 @@ int format_text_get_plugin(source_t *source)
plugin->set_tag = NULL; plugin->set_tag = NULL;
plugin->apply_settings = NULL; plugin->apply_settings = NULL;
plugin->contenttype = httpp_getvar(source->parser, "content-type"); if (igloo_sp_replace(httpp_getvar(source->parser, "content-type"), &(plugin->contenttype), igloo_instance) != igloo_ERROR_NONE) {
ICECAST_LOG_ERROR("Cannot set content type for text source %#H. BAD.", source->mount);
}
skip = httpp_getvar(source->parser, "x-icecast-text-skip-char"); skip = httpp_getvar(source->parser, "x-icecast-text-skip-char");
if (skip) { if (skip) {

133
src/geoip.c Normal file
View File

@ -0,0 +1,133 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2023 , Philipp Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "icecasttypes.h"
#ifdef HAVE_MAXMINDDB
#include <maxminddb.h>
#include <string.h>
#include <errno.h>
#endif
#include <igloo/error.h>
#include <igloo/ro.h>
#include "geoip.h"
#include "global.h"
#include "client.h"
#include "connection.h"
#include "util_string.h"
#include "logging.h"
#define CATMODULE "geoip"
struct geoip_db_tag {
igloo_ro_full_t __parent;
#ifdef HAVE_MAXMINDDB
MMDB_s mmdb;
#endif
};
static void geoip_db_free(igloo_ro_t self)
{
#ifdef HAVE_MAXMINDDB
geoip_db_t *db = igloo_ro_to_type(self, geoip_db_t);
MMDB_close(&(db->mmdb));
#endif
}
igloo_RO_PUBLIC_TYPE(geoip_db_t, igloo_ro_full_t,
igloo_RO_TYPEDECL_FREE(geoip_db_free)
);
#ifdef HAVE_MAXMINDDB
geoip_db_t * geoip_db_new(const char *filename)
{
geoip_db_t *ret;
MMDB_s mmdb;
int status;
if (!filename) {
ICECAST_LOG_INFO("No geoip database given");
return NULL;
}
status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);
if (status != MMDB_SUCCESS) {
if (status == MMDB_IO_ERROR) {
ICECAST_LOG_ERROR("Cannot open geoip database: %s: %s", MMDB_strerror(status), strerror(errno));
} else {
ICECAST_LOG_ERROR("Cannot open geoip database: %s", MMDB_strerror(status));
}
return NULL;
}
if (igloo_ro_new_raw(&ret, geoip_db_t, igloo_instance) != igloo_ERROR_NONE)
return NULL;
ret->mmdb = mmdb;
ICECAST_LOG_INFO("Loaded geoip database: %s", filename);
return ret;
}
void geoip_lookup_client(geoip_db_t *self, client_t * client)
{
int gai_error, mmdb_error;
MMDB_lookup_result_s result;
connection_t *con;
if (!self || !client)
return;
if (!client->con && !client->con->ip)
return;
con = client->con;
result = MMDB_lookup_string(&(self->mmdb), client->con->ip, &gai_error, &mmdb_error);
if (gai_error || mmdb_error != MMDB_SUCCESS)
return;
if (result.found_entry) {
MMDB_entry_data_s entry_data;
int status;
status = MMDB_get_value(&result.entry, &entry_data, "country", "iso_code", (const char*)NULL);
if (status == MMDB_SUCCESS && entry_data.has_data) {
if (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {
if (entry_data.data_size < sizeof(con->geoip.iso_3166_1_alpha_2)) {
memcpy(con->geoip.iso_3166_1_alpha_2, entry_data.utf8_string, entry_data.data_size);
con->geoip.iso_3166_1_alpha_2[entry_data.data_size] = 0;
util_strtolower(con->geoip.iso_3166_1_alpha_2);
ICECAST_LOG_DINFO("FOUND: <%zu> <%H>", (size_t)entry_data.data_size, con->geoip.iso_3166_1_alpha_2);
}
}
}
status = MMDB_get_value(&result.entry, &entry_data, "location", "latitude", (const char*)NULL);
if (status == MMDB_SUCCESS && entry_data.has_data && entry_data.type == MMDB_DATA_TYPE_DOUBLE) {
con->geoip.latitude = entry_data.double_value;
con->geoip.have_latitude = true;
}
status = MMDB_get_value(&result.entry, &entry_data, "location", "longitude", (const char*)NULL);
if (status == MMDB_SUCCESS && entry_data.has_data && entry_data.type == MMDB_DATA_TYPE_DOUBLE) {
con->geoip.longitude = entry_data.double_value;
con->geoip.have_longitude = true;
}
}
}
#endif

25
src/geoip.h Normal file
View File

@ -0,0 +1,25 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2023-2023, Philipp Schafft <lion@lion.leolix.org>,
*/
#ifndef __GEOIP_H__
#define __GEOIP_H__
#include "icecasttypes.h"
#include "client.h"
igloo_RO_FORWARD_TYPE(geoip_db_t);
#ifdef HAVE_MAXMINDDB
geoip_db_t * geoip_db_new(const char *filename);
void geoip_lookup_client(geoip_db_t *self, client_t * client);
#else
#define geoip_db_new(filename) NULL
#define geoip_lookup_client(self,client)
#endif
#endif /* __GEOIP_H__ */

View File

@ -51,6 +51,7 @@ void global_shutdown(void)
{ {
thread_mutex_destroy(&_global_mutex); thread_mutex_destroy(&_global_mutex);
igloo_ro_unref(&global.modulecontainer); igloo_ro_unref(&global.modulecontainer);
igloo_ro_unref(&global.geoip_db);
avl_tree_free(global.source_tree, NULL); avl_tree_free(global.source_tree, NULL);
igloo_sp_unref(&_instance_uuid, igloo_instance); igloo_sp_unref(&_instance_uuid, igloo_instance);
} }

View File

@ -53,6 +53,7 @@ typedef struct ice_global_tag
relay_t *master_relays; relay_t *master_relays;
module_container_t *modulecontainer; module_container_t *modulecontainer;
geoip_db_t *geoip_db;
/* state */ /* state */

View File

@ -134,6 +134,10 @@ typedef struct mount_identifier_tag mount_identifier_t;
typedef struct string_renderer_tag string_renderer_t; typedef struct string_renderer_tag string_renderer_t;
/* ---[ geoip.[ch] ]--- */
typedef struct geoip_db_tag geoip_db_t;
/* ---[ event.[ch] ]--- */ /* ---[ event.[ch] ]--- */
typedef struct event_tag event_t; typedef struct event_tag event_t;
@ -160,6 +164,7 @@ typedef void * refobject_t;
/* --- [ For libigloo ]--- */ /* --- [ For libigloo ]--- */
#define igloo_RO_APPTYPES \ #define igloo_RO_APPTYPES \
igloo_RO_TYPE(string_renderer_t) \ igloo_RO_TYPE(string_renderer_t) \
igloo_RO_TYPE(geoip_db_t) \
igloo_RO_TYPE(event_t) \ igloo_RO_TYPE(event_t) \
igloo_RO_TYPE(event_registration_t) \ igloo_RO_TYPE(event_registration_t) \
igloo_RO_TYPE(module_t) \ igloo_RO_TYPE(module_t) \

View File

@ -45,6 +45,7 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#endif #endif
#include "icecasttypes.h"
#include <igloo/error.h> #include <igloo/error.h>
#include "common/thread/thread.h" #include "common/thread/thread.h"
@ -91,7 +92,10 @@
#include "listensocket.h" #include "listensocket.h"
#include "fastevent.h" #include "fastevent.h"
#include "prng.h" #include "prng.h"
#include "geoip.h"
#include "navigation.h" #include "navigation.h"
#include "valuefile.h"
#include "string_renderer.h"
#include <libxml/xmlmemory.h> #include <libxml/xmlmemory.h>
@ -260,6 +264,13 @@ static void show_version(bool full)
} }
} }
static void export_database(void)
{
string_renderer_t * db = valuefile_export_database();
fputs(string_renderer_to_string_zero_copy(db), stdout);
igloo_ro_unref(&db);
}
static bool _parse_config_opts(int argc, char **argv, char *filename, size_t size) static bool _parse_config_opts(int argc, char **argv, char *filename, size_t size)
{ {
int i; int i;
@ -301,6 +312,9 @@ static bool _parse_config_opts(int argc, char **argv, char *filename, size_t siz
} else if (strcmp(opt, "-V") == 0) { } else if (strcmp(opt, "-V") == 0) {
show_version(true); show_version(true);
exit(0); exit(0);
} else if (strcmp(opt, "--export-database") == 0) {
export_database();
exit(0);
} else if (strcmp(opt, "-c") == 0) { } else if (strcmp(opt, "-c") == 0) {
if ((i + 1) < argc) { if ((i + 1) < argc) {
strncpy(filename, argv[++i], size-1); strncpy(filename, argv[++i], size-1);
@ -755,6 +769,10 @@ int main(int argc, char **argv)
ICECAST_LOG_INFO("Server's PID is %lli", (long long int)getpid()); ICECAST_LOG_INFO("Server's PID is %lli", (long long int)getpid());
__log_system_name(); __log_system_name();
config = config_get_config();
global.geoip_db = geoip_db_new(config->geoipdbfile);
config_release_config();
/* REM 3D Graphics */ /* REM 3D Graphics */
/* let her rip */ /* let her rip */

View File

@ -237,8 +237,11 @@ void source_clear_source (source_t *source)
source->parser = NULL; source->parser = NULL;
source->con = NULL; source->con = NULL;
if (source->format) if (source->format) {
source->format->contenttype = NULL; if (igloo_sp_unref(&(source->format->contenttype), igloo_instance) != igloo_ERROR_NONE) {
ICECAST_LOG_ERROR("Cannot unref content type for source %#H", source->mount);
}
}
/* log bytes read in access log */ /* log bytes read in access log */
if (source->client && source->format) if (source->client && source->format)
@ -634,12 +637,9 @@ static void send_to_listener (source_t *source, client_t *client, int deletion_e
static bool source_open_dumpfile(source_t *source) static bool source_open_dumpfile(source_t *source)
{ {
const char *filename = source->dumpfilename; const char *filename = source->dumpfilename;
interpolation_t interpolation = source->dumpfile_interpolation;
time_t curtime = time(NULL); time_t curtime = time(NULL);
#ifndef _WIN32
/* some of the below functions seems not to be standard winapi functions */
char buffer[PATH_MAX]; char buffer[PATH_MAX];
struct tm *loctime;
#endif
if (!filename) { if (!filename) {
ICECAST_LOG_WARN("Can not open dump file for source %#H. No filename defined.", source->mount); ICECAST_LOG_WARN("Can not open dump file for source %#H. No filename defined.", source->mount);
@ -661,13 +661,46 @@ static bool source_open_dumpfile(source_t *source)
ICECAST_LOG_DDEBUG("source=%p{.mount=%#H, .burst_point=%p, .stream_data=%p, .stream_data_tail=%p, ...}", source, source->mount, source->burst_point, source->stream_data, source->stream_data_tail); ICECAST_LOG_DDEBUG("source=%p{.mount=%#H, .burst_point=%p, .stream_data=%p, .stream_data_tail=%p, ...}", source, source->mount, source->burst_point, source->stream_data, source->stream_data_tail);
if (interpolation == INTERPOLATION_DEFAULT) {
#ifndef _WIN32 #ifndef _WIN32
/* Convert it to local time representation. */ interpolation = INTERPOLATION_STRFTIME;
loctime = localtime(&curtime); #else
interpolation = INTERPOLATION_NONE;
strftime(buffer, sizeof(buffer), filename, loctime);
filename = buffer;
#endif #endif
}
switch (interpolation) {
case INTERPOLATION_NONE:
/* no-op */
break;
case INTERPOLATION_STRFTIME:
#ifndef _WIN32
{
/* some of the below functions seems not to be standard winapi functions */
struct tm *loctime;
/* Convert it to local time representation. */
loctime = localtime(&curtime);
strftime(buffer, sizeof(buffer), filename, loctime);
filename = buffer;
}
#else
ICECAST_LOG_ERROR("Can not interpolation dump file filename for source %#H. strftime() interpolation is not supported by your operating system.", source->mount);
#endif
break;
case INTERPOLATION_UUID:
if (!util_interpolation_uuid(buffer, sizeof(buffer), filename)) {
ICECAST_LOG_WARN("Can not open dump file for source %#H. Filename does not interpolate.", source->mount);
event_emit_va("dumpfile-error", EVENT_EXTRA_SOURCE, source, EVENT_EXTRA_LIST_END);
return false;
}
filename = buffer;
break;
default:
ICECAST_LOG_ERROR("Bug. Bad interpolation %i. Source %p on %#H.", (int)interpolation, source, source->mount);
break;
}
source->dumpfile = fopen(filename, "ab"); source->dumpfile = fopen(filename, "ab");
@ -1258,11 +1291,13 @@ static void source_apply_mount (ice_config_t *config, source_t *source, mount_pr
} }
if (mountinfo) { if (mountinfo) {
source->dumpfile_size_limit = mountinfo->dumpfile_size_limit; source->dumpfile_size_limit = mountinfo->dumpfile_size_limit;
source->dumpfile_time_limit = mountinfo->dumpfile_time_limit; source->dumpfile_time_limit = mountinfo->dumpfile_time_limit;
source->dumpfile_interpolation = mountinfo->dumpfile_interpolation;
} else { } else {
source->dumpfile_size_limit = 0; source->dumpfile_size_limit = 0;
source->dumpfile_time_limit = 0; source->dumpfile_time_limit = 0;
source->dumpfile_interpolation = INTERPOLATION_DEFAULT;
} }

View File

@ -73,6 +73,7 @@ struct source_tag {
/* Dumpfile related data */ /* Dumpfile related data */
/* Config */ /* Config */
char *dumpfilename; /* Name of a file to dump incoming stream to */ char *dumpfilename; /* Name of a file to dump incoming stream to */
interpolation_t dumpfile_interpolation;
uint64_t dumpfile_size_limit; uint64_t dumpfile_size_limit;
unsigned int dumpfile_time_limit; unsigned int dumpfile_time_limit;
/* Runtime */ /* Runtime */

View File

@ -934,8 +934,10 @@ static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, unsigned int flags, const
if (source_real->running) if (source_real->running)
xmlNewTextChild(xmlnode, NULL, XMLSTR("content-type"), XMLSTR(source_real->format->contenttype)); xmlNewTextChild(xmlnode, NULL, XMLSTR("content-type"), XMLSTR(source_real->format->contenttype));
if (flags & STATS_XML_FLAG_SHOW_LISTENERS) if (flags & STATS_XML_FLAG_SHOW_LISTENERS) {
admin_add_listeners_to_mount(source_real, xmlnode, client->mode); admin_add_listeners_to_mount(source_real, xmlnode, client->mode);
admin_add_geoip_to_mount(source_real, xmlnode, client->mode);
}
} }
avl_tree_unlock(global.source_tree); avl_tree_unlock(global.source_tree);

View File

@ -39,6 +39,9 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include <igloo/uuid.h>
#include <igloo/error.h> /* for igloo_ERROR_NONE */
#include "common/net/sock.h" #include "common/net/sock.h"
#include "common/thread/thread.h" #include "common/thread/thread.h"
@ -53,6 +56,7 @@
#include "auth.h" #include "auth.h"
#include "acl.h" #include "acl.h"
#include "listensocket.h" #include "listensocket.h"
#include "global.h" /* for igloo_instance */
#define CATMODULE "util" #define CATMODULE "util"
@ -1271,3 +1275,82 @@ int get_line(FILE *file, char *buf, size_t siz)
} }
return 0; return 0;
} }
bool util_interpolation_uuid(char * buffer, size_t bufferlen, const char *in)
{
char *random_uuid = NULL;
if (!buffer || bufferlen < 1 || !in)
return false;
for (; *in; in++) {
if (*in == '%') {
in++;
switch (*in) {
case '%':
if (!bufferlen) {
free(random_uuid);
return false;
}
*buffer++ = *in;
bufferlen--;
break;
case 'r':
case 'g':
if (true) {
const char *str;
size_t len;
switch (*in) {
case 'r':
if (!random_uuid) {
if (igloo_uuid_new_random_cstr(&random_uuid, igloo_instance) != igloo_ERROR_NONE) {
return false;
}
}
str = random_uuid;
break;
case 'g':
str = global_instance_uuid();
break;
default:
free(random_uuid);
return false;
}
len = strlen(str);
if (bufferlen <= len) {
free(random_uuid);
return false;
}
memcpy(buffer, str, len);
buffer += len;
bufferlen -= len;
}
break;
default:
free(random_uuid);
return false;
}
} else {
if (!bufferlen) {
free(random_uuid);
return false;
}
*buffer++ = *in;
bufferlen--;
}
}
free(random_uuid);
if (bufferlen) {
*buffer = 0;
return true;
}
return false;
}

View File

@ -124,4 +124,8 @@ struct tm *localtime_r(const time_t *timep, struct tm *result);
char *util_conv_string (const char *string, const char *in_charset, const char *out_charset); char *util_conv_string (const char *string, const char *in_charset, const char *out_charset);
int get_line(FILE *file, char *buf, size_t siz); int get_line(FILE *file, char *buf, size_t siz);
/* returns true on successi, when returning false buffer[] is in undefined state. */
bool util_interpolation_uuid(char * buffer, size_t bufferlen, const char *in);
#endif /* __UTIL_H__ */ #endif /* __UTIL_H__ */

210
src/valuefile.c Normal file
View File

@ -0,0 +1,210 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2024 , Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <igloo/ro.h>
#include <igloo/error.h>
#include "valuefile.h"
#include "string_renderer.h"
#include "global.h"
#include "errors.h"
#include "logging.h"
#define CATMODULE "valuefile"
// universal:
#define WK_ASI "ddd60c5c-2934-404f-8f2d-fcb4da88b633"
#define WK_TAGNAME "bfae7574-3dae-425d-89b1-9c087c140c23"
#define WK_ENGLISH "c50134ca-0a32-5c5c-833c-2686043c0b3f"
#define WK_GB_TEXT "0678cfe2-1386-43db-86a9-6c491b2df64c"
#define WK_RELATES_TO "079ab791-784e-4bb9-ae2d-07e01710a60c"
#define WK_SEE_ALSO "a75f9010-9db3-4d78-bd78-0dd528d6b55d"
#define WK_DEFAULT_TYPE "87c4892f-ae39-476e-8ed0-d9ed321dafe9"
#define WK_UNSIGNED_INT "dea3782c-6bcb-4ce9-8a39-f8dab399d75d"
// Icecast:
#define WK_EQ_HTTP "06cfbaf9-01fe-4d82-8075-0389231c46b3"
#define WK_STATE_info "c2bb1328-87d6-49ad-b7df-0b381a2e606d"
#define WK_STATE_error "e0faf79c-bddf-42d7-a237-975aca861a0b"
#define WK_STATE_warning "107c0e91-1fd8-4bbc-a54c-899cbb3d9bc6"
static const char *states[] = {WK_STATE_info, WK_STATE_error, WK_STATE_warning};
static const struct {
const char *uuid;
const char *symbol;
const char *tagname;
const char *text;
const char *state;
const unsigned int http_status;
} extra[] = {
{.uuid = "be7fac90-54fb-4673-9e0d-d15d6a4963a2", .symbol = "AUTH_ALTER_REDIRECT_SEE_OTHER", .http_status = 303},
{.uuid = "4b08a03a-ecce-4981-badf-26b0bb6c9d9c", .symbol = "AUTH_ALTER_REDIRECT_TEMPORARY", .http_status = 307},
{.uuid = "36bf6815-95cb-4cc8-a7b0-6b4b0c82ac5d", .symbol = "AUTH_ALTER_REDIRECT_PERMANENT", .http_status = 308},
{.uuid = WK_EQ_HTTP, .tagname = "equivalent-http-status"},
{.uuid = WK_STATE_info, .tagname = "info"},
{.uuid = WK_STATE_error, .tagname = "error"},
{.uuid = WK_STATE_warning, .tagname = "warning"},
// grep __reportxml_add_maintenance.\*\" admin.c | sed 's/^.*__reportxml_add_maintenance([^,]*,[^,]*, *"\([^"]*\)", *"\([^"]*\)", *"\([^"]*\)", NULL);.*$/{.uuid = "\1", .state = WK_STATE_\2, .text = "\3"},/'
{.uuid = "a93a842a-9664-43a9-b707-7f358066fe2b", .state = WK_STATE_error, .text = "Global client, and source limit is bigger than suitable for current open file limit."},
{.uuid = "fdbacc56-1150-4c79-b53a-43cc79046f40", .state = WK_STATE_info, .text = "Core files are disabled."},
{.uuid = "688bdebc-c190-4b5d-8764-3a050c48387a", .state = WK_STATE_info, .text = "Core files are enabled."},
{.uuid = "c704804e-d3b9-4544-898b-d477078135de", .state = WK_STATE_warning, .text = "Developer logging is active. This mode is not for production."},
{.uuid = "f90219e1-bd07-4b54-b1ee-0ba6a0289a15", .state = WK_STATE_error, .text = "IPv6 not enabled."},
{.uuid = "709ab43b-251d-49a5-a4fe-c749eaabf17c", .state = WK_STATE_info, .text = "IPv4-mapped IPv6 is available on this system."},
{.uuid = "c4f25c51-2720-4b38-a806-19ef024b5289", .state = WK_STATE_warning, .text = "Hostname is not set to anything useful in <hostname>."},
{.uuid = "8defae31-a52e-4bba-b904-76db5362860f", .state = WK_STATE_warning, .text = "No useful location is given in <location>."},
{.uuid = "cf86d88e-dc20-4359-b446-110e7065d17a", .state = WK_STATE_warning, .text = "No admin contact given in <admin>. YP directory support is disabled."},
{.uuid = "e2ba5a8b-4e4f-41ca-b455-68ae5fb6cae0", .state = WK_STATE_error, .text = "No PRNG seed configured. PRNG is insecure."},
{.uuid = "6620ef7b-46ef-4781-9a5e-8ee7f0f9d44e", .state = WK_STATE_error, .text = "Unknown tags are used in the config file. See the error.log for details."},
{.uuid = "b6224fc4-53a1-433f-a6cd-d5b85c60f1c9", .state = WK_STATE_error, .text = "Obsolete tags are used in the config file. See the error.log for details and update your configuration accordingly."},
{.uuid = "0f6f757d-52d8-4b9a-8e57-9bcd528fffba", .state = WK_STATE_error, .text = "Invalid tags are used in the config file. See the error.log for details and update your configuration accordingly."},
{.uuid = "8fc33086-274d-4ccb-b32f-599b3fa0f41a", .state = WK_STATE_error, .text = "The configuration did not validate. See the error.log for details and update your configuration accordingly."},
{.uuid = "6830cbf7-cd68-4c0c-ab5a-81499c70fd34", .state = WK_STATE_info, .text = "chroot configured and active."},
{.uuid = "2d584a76-e67c-4268-b7e8-139b0b9b1131", .state = WK_STATE_error, .text = "chroot configured but failed."},
{.uuid = "1a3fea5c-3352-4cb5-85cc-51ab9bd6ea83", .state = WK_STATE_error, .text = "chroot configured but not supported by operating system."},
{.uuid = "bab05e81-fd03-4773-9fc5-c4609883a5e3", .state = WK_STATE_info, .text = "Change of UID/GID configured and active."},
{.uuid = "4f856dd4-7aac-44b4-95b5-b6798f547603", .state = WK_STATE_error, .text = "Change of UID/GID configured but failed."},
{.uuid = "afcaa756-b91c-4496-a9e2-44400a18789c", .state = WK_STATE_error, .text = "Change of UID/GID configured but not supported by operating system."},
{.uuid = "f68dd8a3-22b1-4118-aba6-b039f2c5b51e", .state = WK_STATE_info, .text = "Currently no sources are connected to this server."},
{.uuid = "a3a51986-3bba-42b9-ad5c-d9ecc9967320", .state = WK_STATE_warning, .text = "Legacy sources are connected. See mount list for details."},
{.uuid = "08676614-50b4-4ea7-ba99-7c2ffcecf705", .state = WK_STATE_warning, .text = "More than 90% of the server's configured maximum clients are connected"},
{.uuid = "417ae59c-de19-4ed1-ade1-429c689f1152", .state = WK_STATE_info, .text = "More than 75% of the server's configured maximum clients are connected"},
{.uuid = "dc91ce96-f473-41d1-bfff-379666306911", .state = WK_STATE_info, .text = "Environment is noisy."},
{.uuid = "40d134e3-fbbe-46b1-a409-9b2ca8954528", .state = WK_STATE_warning, .text = "No secure password hash support detected."},
{NULL}
};
static igloo_error_t valuefile_value(string_renderer_t *renderer, const char *value, bool first)
{
if (!first)
string_renderer_add_string(renderer, " ");
if (!value) {
return string_renderer_add_string(renderer, "!null");
} else if (!*value) {
return string_renderer_add_string(renderer, "!empty");
}
return string_renderer_add_string_with_options(renderer, value, false, STRING_RENDERER_ENCODING_URI);
}
static igloo_error_t valuefile_line_metadata(string_renderer_t *renderer, const char *tag, const char *relation, const char *context, const char *type, const char *encoding, const char *value)
{
string_renderer_add_string(renderer, "tag-metadata ");
string_renderer_add_string(renderer, tag);
string_renderer_add_string(renderer, " ");
string_renderer_add_string(renderer, relation);
valuefile_value(renderer, context, false);
valuefile_value(renderer, type, false);
valuefile_value(renderer, encoding, false);
valuefile_value(renderer, value, false);
string_renderer_add_string(renderer, "\n");
return igloo_ERROR_NONE;
}
static igloo_error_t valuefile_line_relation(string_renderer_t *renderer, const char *tag, const char *relation, const char *related, const char *context, const char *filter)
{
string_renderer_add_string(renderer, "tag-relation ");
string_renderer_add_string(renderer, tag);
string_renderer_add_string(renderer, " ");
string_renderer_add_string(renderer, relation);
string_renderer_add_string(renderer, " ");
string_renderer_add_string(renderer, related);
valuefile_value(renderer, context, false);
valuefile_value(renderer, filter, false);
string_renderer_add_string(renderer, "\n");
return igloo_ERROR_NONE;
}
static igloo_error_t valuefile_line_tag_ise(string_renderer_t *renderer, const char *ise, const char *comment)
{
string_renderer_add_string(renderer, "tag-ise ");
string_renderer_add_string(renderer, ise);
if (comment) {
string_renderer_add_string(renderer, " # ");
string_renderer_add_string(renderer, comment);
}
string_renderer_add_string(renderer, "\n");
return igloo_ERROR_NONE;
}
static igloo_error_t valuefile_http_status(string_renderer_t *renderer, const char *tag, unsigned int http_status)
{
if (http_status < 100 || http_status > 599)
return igloo_ERROR_NONE;
string_renderer_add_string(renderer, "tag-metadata ");
string_renderer_add_string(renderer, tag);
string_renderer_add_string(renderer, " ");
string_renderer_add_string(renderer, WK_EQ_HTTP);
string_renderer_add_string(renderer, " !null !null !null ");
string_renderer_add_int(renderer, http_status);
string_renderer_add_string(renderer, "\n");
return igloo_ERROR_NONE;
}
string_renderer_t * valuefile_export_database(void)
{
string_renderer_t *renderer;
if (igloo_ro_new(&renderer, string_renderer_t, igloo_instance) != igloo_ERROR_NONE)
return NULL;
string_renderer_add_string(renderer, "!!ValueFile 54bf8af4-b1d7-44da-af48-5278d11e8f32 e5da6a39-46d5-48a9-b174-5c26008e208e\n");
string_renderer_add_string(renderer, "!!Feature f06c2226-b33e-48f2-9085-cd906a3dcee0 # tagpool-source-format-modern-limited\n");
string_renderer_add_string(renderer, "\n");
// errors:
string_renderer_add_string(renderer, "# Errors:\n");
for (size_t i = 0; ; i++) {
const icecast_error_t * error = error_get_by_index(i);
if (!error)
break;
valuefile_line_tag_ise(renderer, error->uuid, error->message);
valuefile_line_metadata(renderer, error->uuid, WK_ASI, NULL, WK_TAGNAME, NULL, error->symbol);
valuefile_line_metadata(renderer, error->uuid, WK_GB_TEXT, WK_ENGLISH, NULL, NULL, error->message);
valuefile_http_status(renderer, error->uuid, error->http_status);
}
string_renderer_add_string(renderer, "\n");
string_renderer_add_string(renderer, "# Extra:\n");
for (size_t i = 0; extra[i].uuid; i++) {
valuefile_line_tag_ise(renderer, extra[i].uuid, extra[i].symbol);
if (extra[i].symbol)
valuefile_line_metadata(renderer, extra[i].uuid, WK_ASI, NULL, WK_TAGNAME, NULL, extra[i].symbol);
if (extra[i].tagname)
valuefile_line_metadata(renderer, extra[i].uuid, WK_ASI, NULL, WK_TAGNAME, NULL, extra[i].tagname);
if (extra[i].text)
valuefile_line_metadata(renderer, extra[i].uuid, WK_GB_TEXT, WK_ENGLISH, NULL, NULL, extra[i].text);
if (extra[i].state)
valuefile_line_relation(renderer, extra[i].uuid, WK_RELATES_TO, extra[i].state, NULL, NULL);
valuefile_http_status(renderer, extra[i].uuid, extra[i].http_status);
}
string_renderer_add_string(renderer, "\n");
string_renderer_add_string(renderer, "# Special:\n");
valuefile_line_relation(renderer, WK_EQ_HTTP, WK_DEFAULT_TYPE, WK_UNSIGNED_INT, NULL, NULL);
for (size_t i = 0; i < (sizeof(states)/sizeof(*states)); i++) {
for (size_t j = 0; j < (sizeof(states)/sizeof(*states)); j++) {
if (i != j)
valuefile_line_relation(renderer, states[i], WK_SEE_ALSO, states[j], NULL, NULL);
}
}
string_renderer_add_string(renderer, "\n");
return renderer;
}

18
src/valuefile.h Normal file
View File

@ -0,0 +1,18 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2024 , Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __VALUEFILE_H__
#define __VALUEFILE_H__
#include <stdbool.h>
#include "icecasttypes.h"
string_renderer_t * valuefile_export_database(void);
#endif

View File

@ -467,6 +467,90 @@ static void render_node_legacystats(json_renderer_t *renderer, xmlDocPtr doc, xm
json_renderer_write_key(renderer, (const char *)cur->name, JSON_RENDERER_FLAGS_NONE); json_renderer_write_key(renderer, (const char *)cur->name, JSON_RENDERER_FLAGS_NONE);
render_node(renderer, doc, cur, node, cache); render_node(renderer, doc, cur, node, cache);
nodelist_unset(&nodelist, i); nodelist_unset(&nodelist, i);
} else if (strcmp((const char *)cur->name, "geoip") == 0) {
xmlNodePtr geoip = NULL;
for (size_t j = i; j < len; j++) {
xmlNodePtr subcur = nodelist_get(&nodelist, j);
if (subcur == NULL)
continue;
if (subcur->type == XML_ELEMENT_NODE && subcur->name && strcmp((const char *)cur->name, (const char *)subcur->name) == 0) {
nodelist_unset(&nodelist, j);
geoip = subcur;
}
}
if (geoip) {
json_renderer_write_key(renderer, (const char *)cur->name, JSON_RENDERER_FLAGS_NONE);
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_OBJECT);
{
xmlNodePtr child = geoip->xmlChildrenNode;
json_renderer_write_key(renderer, "country", JSON_RENDERER_FLAGS_NONE);
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_ARRAY);
while (child) {
if (child->type == XML_ELEMENT_NODE && child->name && strcmp((const char *)child->name, "country") == 0) {
xmlChar *keyval = xmlGetProp(child, XMLSTR("iso-alpha-2"));
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_OBJECT);
if (keyval) {
json_renderer_write_key(renderer, "iso-alpha-2", JSON_RENDERER_FLAGS_NONE);
json_renderer_write_string(renderer, (const char *)keyval, JSON_RENDERER_FLAGS_NONE);
xmlFree(keyval);
}
for (xmlNodePtr subchild = child->xmlChildrenNode; subchild; subchild = subchild->next) {
if (subchild->type == XML_ELEMENT_NODE && subchild->name) {
xmlChar *value = xmlNodeListGetString(doc, subchild->xmlChildrenNode, 1);
if (value) {
json_renderer_write_key(renderer, (const char*)subchild->name, JSON_RENDERER_FLAGS_NONE);
json_renderer_write_int(renderer, strtoll((const char*)value, NULL, 10));
xmlFree(value);
}
}
}
json_renderer_end(renderer);
}
child = child->next;
}
json_renderer_end(renderer);
}
{
xmlNodePtr child = geoip->xmlChildrenNode;
json_renderer_write_key(renderer, "location", JSON_RENDERER_FLAGS_NONE);
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_ARRAY);
while (child) {
ICECAST_LOG_INFO("child->name=<%s>", child->name);
if (child->type == XML_ELEMENT_NODE && child->name && strcmp((const char *)child->name, "location") == 0) {
static const char * keys[] = {"latitude", "longitude", NULL};
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_OBJECT);
for (const char **p = keys; *p; p++) {
xmlChar *keyval = xmlGetProp(child, XMLSTR(*p));
if (keyval) {
json_renderer_write_key(renderer, *p, JSON_RENDERER_FLAGS_NONE);
json_renderer_write_string(renderer, (const char *)keyval, JSON_RENDERER_FLAGS_NONE);
xmlFree(keyval);
}
}
json_renderer_end(renderer);
}
child = child->next;
}
json_renderer_end(renderer);
}
json_renderer_end(renderer);
}
} }
} }
//render_node_generic(renderer, doc, node, parent, cache); //render_node_generic(renderer, doc, node, parent, cache);

View File

@ -9,4 +9,4 @@ echo Please open http://localhost:8000/ in your web browser to see the web inter
echo. echo.
echo. echo.
echo Leave this window open to keep Icecast running and, if necessary, minimize it. echo Leave this window open to keep Icecast running and, if necessary, minimize it.
.\bin\icecast.exe -c .\etc\icecast.xml .\bin\icecast.exe -c icecast.xml

View File

@ -17,7 +17,7 @@
;Name and file ;Name and file
Name "Icecast" Name "Icecast"
OutFile "icecast_win32_2.5-beta3.exe" OutFile "icecast_win32_TOBEREPLACED.exe"
;Default installation folder ;Default installation folder
InstallDir "$PROGRAMFILES32\Icecast" InstallDir "$PROGRAMFILES32\Icecast"