1
0
Fork 0

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
variables:
BASE_IMAGE_REGISTRY: registry.gitlab.xiph.org/xiph/icecast-docker-images
default:
image: alpine:3.15
image: "$BASE_IMAGE_REGISTRY/alpine-icecast-server:20240114141404"
tags:
- docker
- 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:
extends: .TemplateAlpine
variables:
GIT_SUBMODULE_STRATEGY: recursive
LIBIGLOO_PACKAGE_PREFIX: "alpine-libigloo-bins"
@ -33,6 +19,7 @@ make_dist:
script:
- set -xe # yes i know the scripts will be merged... but for sanities sake
- cat /etc/os*
- ./autogen.sh
- mkdir EXTRA-LIBS
- export PKG_CONFIG_PATH=`pwd`/EXTRA-LIBS/usr/local/lib/pkgconfig
@ -49,6 +36,7 @@ make_dist:
- ./configure || cat config.log
- make
- make dist
- tree
# Tests
#- su -c "./tests/admin-tests.sh" icecast
@ -57,29 +45,8 @@ make_dist:
untracked: true
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:
image: "$BASE_IMAGE_REGISTRY/alpine-osc:20240114141818"
only:
- master
- devel
@ -96,6 +63,7 @@ upload_dist:
- ./ci/osc/handle-osc-upload.sh
upload_dist_release:
image: "$BASE_IMAGE_REGISTRY/alpine-osc:20240114141818"
only:
- tags

View File

@ -1,6 +1,8 @@
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)
Icecast is a streaming media server which currently supports _WebM_ and

View File

@ -25,6 +25,7 @@
<th>Role</th>
<th>Sec. connected</th>
<th>User Agent</th>
<th>Location</th>
<th class="actions">Action</th>
</tr>
</thead>
@ -36,6 +37,10 @@
<td><xsl:value-of select="role" /></td>
<td><xsl:value-of select="connected" /></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">
<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>

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:
```
export ICECAST_BETA_VERSION=3
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.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 "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.
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.
# Nightlies

View File

@ -1,20 +1,56 @@
#!/bin/bash -xe
BETA_VERSION=${1:?Missing Beta Version, Use 2}; shift
SHORT_VERSION=${1:?Missing Short Version, Use 2.4.99.2}; shift
STRANGE_VERSION=${1:?Missing Strange Version, Use '2.5 beta2'}; shift
HTML_VERSION=${1:?Missing HTML Version, Use '25-beta-2'}; shift
WIN32_VERSION=${1:?Missing Win32 Version, Use '2.5-beta2'}; shift
ARCHIVE_VERSION=${1:?Missing Archive Version, Use '2.4.99.2' for ci or _VERSION_ARCHIVE_ for release}; shift
CI_VERSION=${1:?Missing CI Version - Use 2.4.99.2+bla}; shift
DATE=`date --date=${1:?ISO DATE or now} --iso-8601=seconds`; shift
# Script to handle version handling for releases manually or through the CI
# SHORT_VERSION (first parameter) will be used for most places
# It should be MAJOR.minor.patch to prepare an actual release
# or MAJOR.minor.patch.extra to prepare for the next minor release
# extra will then be the number of the beta release
# - in configure.ac, for the actual version used for building
# - to build the other version numbers such as :
# - 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
# Short description to be added to changelogs
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
if [ "$ARCHIVE_VERSION" = "_VERSION_ARCHIVE_" ]; then
@ -23,29 +59,28 @@ fi
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
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
sed -i "s/\(icecast_win32_\).*\(.exe\)/\1$WIN32_VERSION\2/" $W32_ICECAST_INSTALLER_PROJECT/$W32_ICECAST_INSTALLER_PROJECT.spec
popd
# 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
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
sed -i "1s#\[[.0-9]*\]#[$SHORT_VERSION]#" configure.ac
# actual version change for the build
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/\(\"DisplayVersion\" \"\).*\(\"\)$/\1$STRANGE_VERSION\2/" win32/icecast.nsis
sed -i "s/\(OutFile \"icecast_win32_\).*\(.exe\"\)$/\1$WIN32_VERSION\2/" win32/icecast.nsis
sed -i "s/^\(export ICECAST_VERSION=\).*$/\1$SHORT_VERSION/; s/\(export ICECAST_BETA_VERSION=\).*$/\1$BETA_VERSION/" ci/osc/*-config.sh
# 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
# we only do this for release builds
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>
Homepage: http://www.icecast.org/
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:
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
# 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`
SCRIPT_DIR=`realpath $SCRIPT_DIR`
# Set SCRIPT_DIR to current directory of the script
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}
. $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}"
# Print current dir for debug
pwd
# Print content
ls -la
rm -rf osc_tmp
mkdir -p osc_tmp
cd osc_tmp
# We use $HOME/.local/bin/osc --config=$ORC_RC_FILE as our osc command
# This can be overriden, and would be changed if osc was properly installed
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`
export SOURCE=`pwd`/..
export OSC_CMD="osc-wrapper.py --config=$OSC_RC"
# Remember where we are
export SOURCE=$PWD
# checkout into a dkirectory named like the project - avoiding having it in a subdir called OBS_BASE
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
$OSC_CMD checkout -o "$i" $OBS_BASE "$i"
rm -vrf "$i"/*
done
# For each project on OBS
for OBS_BASE in $OBS_BASES; do
# Create a temp dir for our upload, clean it up
export OSC_TMP=osc_tmp-$OBS_BASE
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
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
# 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";
# checkout into a dkirectory named like the project - avoiding having it in a subdir called OBS_BASE
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
$OSC_CMD checkout -o "$i" "$OBS_BASE" "$i" || $OSC_CMD mkpac "$i"
rm -vrf "${i:?}"/*
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
rm -rf $ICECAST_PROJECT/debian
# 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
# 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
for i in "$ICECAST_PROJECT" "$W32_ICECAST_INSTALLER_PROJECT" "$W32_ICECAST_PROJECT"; do
pushd $i
# we fix the dsc to have the correct hashsums so dpkg-buildpackage and co do not complain
$OSC_CMD addremove
$OSC_CMD diff
$OSC_CMD commit -m "Commit via $CI_PIPELINE_URL"
pushd "$ICECAST_PROJECT"
"$SCRIPT_DIR/../fix-dsc.sh"
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
# 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
shred -vzf $OSC_RC .osc_cookiejar
cd ..
echo > $OSC_RC
rm -rf "osc_tmp"
for file in "$OSC_RC" "$OSC_RC_FILE" .osc_cookiejar; do
[ -n "$file" ] && [ -f "$file" ] && shred -vzf "$file"
echo > "$file"
done
rm -rf osc_tmp*
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: libxml2-devel, libxslt-devel, speex-devel
# 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
BuildRequires: libtool,

View File

@ -56,19 +56,23 @@ cp /etc/mime.types installer/
find installer/
%build
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_PERTURB_
makensis icecast.nsis -V4
%install
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
%files
%defattr(-,root,root)
%{_mingw32_bindir}/icecast_win32_2.5-beta3.exe
%{_mingw32_bindir}/icecast_win32_%{version}.exe
%changelog
* 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-libspeex-devel
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
BuildArch: noarch
@ -57,12 +59,13 @@ MINGW32_CFLAGS="%{_mingw32_cflags}" \
PATH="%{_mingw32_bindir}:$PATH" \
%{_mingw32_configure} \
--with-curl=%{_mingw32_prefix} \
--disable-static --enable-shared #\
%{__make} %{?_smp_mflags}
--disable-static --enable-shared
%{_mingw32_make} %{?_smp_mflags}
%install
make DESTDIR=%{buildroot} install %{?_smp_mflags}
%{_mingw32_makeinstall} %{?_smp_mflags}
find %{buildroot}
rm %{buildroot}%{_mingw32_sysconfdir}/icecast.xml
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_BASE" = "z" ]; then
if [ "z$OBS_BASES" = "z" ]; 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
export OBS_BASE=multimedia:xiph:nightly-devel
export OBS_BASES=$OBS_BASE_NIGHTLY_DEVEL
else
echo "branch '$GIT_BRANCH' is not master or devel, please export OBS_BASE accordingly";
exit 1
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_CI_VERSION=$ICECAST_VERSION+`date +%Y%m%d%H%M%S`+`git rev-parse HEAD`
# How long should the hash be, overridable
GIT_HASH_LENGTH=${GIT_HASH_LENGTH:-4}
# Get the current commit ID
GIT_HASH=$(git rev-parse --short="$GIT_HASH_LENGTH" 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 RELEASE_AUTHOR=${CI_COMMIT_AUTHOR:?Please set CI_COMMIT_AUTHOR}
export RELEASE_DATETIME=now

View File

@ -1,7 +1,25 @@
#!/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*
apk update
apk add python3 py3-pip python3-dev openssl-dev g++ make swig coreutils bash ca-certificates git
pip install osc
which osc-wrapper.py
osc-wrapper.py --version
# We are alone using this container. Let's break stuff (someday use a venv).
pip install osc --break-system-packages
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
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
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
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_CI_VERSION=$ICECAST_VERSION
export ICECAST_CI_VERSION=${ICECAST_VERSION/-/+}
export DISABLE_CHANGELOG=1
export RELEASE_AUTHOR="Philipp Schafft <lion@lion.leolix.org>"
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}"
])
dnl
dnl libmaxminddb
dnl
PKG_HAVE_WITH_MODULES([MAXMINDDB], [libmaxminddb >= 1.3.2], [
CFLAGS="${CFLAGS} ${MAXMINDDB_CFLAGS}"
LIBS="${LIBS} ${MAXMINDDB_LIBS}"
])
dnl
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,

View File

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

View File

@ -150,6 +150,12 @@
#define DEFAULT_HTML_REQUEST ""
#define BUILDM3U_RAW_REQUEST "buildm3u"
typedef struct {
size_t listeners;
size_t tls;
size_t ipv6;
} country_t;
typedef struct {
const char *prefix;
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);
xmlNewTextChild(srcnode, NULL, XMLSTR("dumpfile_written"), XMLSTR(buf));
admin_add_geoip_to_mount(source, srcnode, client->mode);
}
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)));
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 {
xmlNodePtr history = xmlNewChild(node, NULL, XMLSTR("history"), NULL);
size_t i;
@ -950,6 +982,80 @@ void admin_add_listeners_to_mount(source_t *source,
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,
source_t *source,
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));
admin_add_listeners_to_mount(source, srcnode, client->mode);
admin_add_geoip_to_mount(source, srcnode, client->mode);
admin_send_response(doc, client, response,
LISTCLIENTS_HTML_REQUEST);

View File

@ -61,6 +61,11 @@ void admin_add_listeners_to_mount(source_t *source,
xmlNodePtr parent,
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);
admin_command_id_t admin_get_command(const char *command);

View File

@ -55,6 +55,7 @@
#include "slave.h"
#include "xslt.h"
#include "prng.h"
#include "geoip.h"
#define CATMODULE "CONFIG"
#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)
{
if (!href || !*href)
@ -914,6 +932,7 @@ void config_clear(ice_config_t *c)
if (c->log_dir) xmlFree(c->log_dir);
if (c->webroot_dir) xmlFree(c->webroot_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->pidfile) xmlFree(c->pidfile);
if (c->banfile) xmlFree(c->banfile);
@ -1004,6 +1023,8 @@ void config_reread_config(void)
restart_logging(config);
prng_configure(config);
main_config_reload(config);
igloo_ro_unref(&global.geoip_db);
global.geoip_db = geoip_db_new(config->geoipdbfile);
connection_reread_config(config);
yp_recheck_config(config);
fserve_recheck_mime_types(config);
@ -1744,8 +1765,13 @@ static void _parse_mount(xmlDocPtr doc,
password = (char *)xmlNodeListGetString(doc,
node->xmlChildrenNode, 1);
} 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,
node->xmlChildrenNode, 1);
node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("dump-file-size-limit")) == 0) {
unsigned int val = mount->dumpfile_size_limit;
__read_unsigned_int(configuration, doc, node, &val, 0, UINT_MAX);
@ -2719,6 +2745,14 @@ static void _parse_paths(xmlDocPtr doc,
}
}
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) {
_parse_resource(doc, node, configuration);
} else {
@ -3008,6 +3042,8 @@ static void merge_mounts(mount_proxy * dst, mount_proxy * src)
if (!dst->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)
dst->dumpfile_size_limit = src->dumpfile_size_limit;
if (!dst->dumpfile_time_limit)

View File

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

View File

@ -57,6 +57,7 @@
#include "acl.h"
#include "listensocket.h"
#include "fastevent.h"
#include "geoip.h"
/* for ADMIN_COMMAND_ERROR, and ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION */
#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);
geoip_lookup_client(global.geoip_db, client);
return ret;
}

View File

@ -16,6 +16,7 @@
#include <sys/types.h>
#include <time.h>
#include <stdbool.h>
#include "tls.h"
@ -64,6 +65,14 @@ struct connection_tag {
/* IP Address of the client as seen by the server */
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);

View File

@ -16,132 +16,134 @@
#include "logging.h"
#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
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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.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",
.message = "Recursive error"}
};
@ -170,3 +172,10 @@ const icecast_error_t * error_get_by_uuid(const char *uuid)
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 char *uuid;
const char *message;
const char *symbol;
};
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_uuid(const char *uuid);
const icecast_error_t * error_get_by_index(size_t idx);
#endif /* __ERRORS_H__ */

View File

@ -26,6 +26,10 @@
#include <stdlib.h>
#include <string.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "refbuf.h"
#include "source.h"
#include "client.h"
@ -211,7 +215,9 @@ int format_ebml_get_plugin(source_t *source)
plugin->set_tag = 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;
vorbis_comment_init(&plugin->vc);

View File

@ -30,6 +30,9 @@
# include <strings.h>
#endif
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "refbuf.h"
#include "event.h"
@ -78,6 +81,7 @@ int format_mp3_get_plugin(source_t *source)
format_plugin_t *plugin;
mp3_state *state = calloc(1, sizeof(mp3_state));
refbuf_t *meta;
const char *contenttype;
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->apply_settings = format_mp3_apply_settings;
plugin->contenttype = httpp_getvar(source->parser, "content-type");
if (plugin->contenttype == NULL) {
contenttype = httpp_getvar(source->parser, "content-type");
if (contenttype == NULL) {
/* 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;

View File

@ -27,6 +27,10 @@
#include <ogg/ogg.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "refbuf.h"
#include "source.h"
#include "client.h"
@ -178,7 +182,10 @@ int format_ogg_get_plugin(source_t *source)
plugin->set_tag = NULL;
if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
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);
vorbis_comment_init(&plugin->vc);

View File

@ -15,6 +15,10 @@
#include <stdlib.h>
#include <stdbool.h>
#include <igloo/sp.h>
#include <igloo/error.h>
#include "global.h"
#include "source.h"
#include "format.h"
#include "format_text.h"
@ -105,7 +109,9 @@ int format_text_get_plugin(source_t *source)
plugin->set_tag = 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");
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);
igloo_ro_unref(&global.modulecontainer);
igloo_ro_unref(&global.geoip_db);
avl_tree_free(global.source_tree, NULL);
igloo_sp_unref(&_instance_uuid, igloo_instance);
}

View File

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

View File

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

View File

@ -45,6 +45,7 @@
#include <sys/utsname.h>
#endif
#include "icecasttypes.h"
#include <igloo/error.h>
#include "common/thread/thread.h"
@ -91,7 +92,10 @@
#include "listensocket.h"
#include "fastevent.h"
#include "prng.h"
#include "geoip.h"
#include "navigation.h"
#include "valuefile.h"
#include "string_renderer.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)
{
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) {
show_version(true);
exit(0);
} else if (strcmp(opt, "--export-database") == 0) {
export_database();
exit(0);
} else if (strcmp(opt, "-c") == 0) {
if ((i + 1) < argc) {
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());
__log_system_name();
config = config_get_config();
global.geoip_db = geoip_db_new(config->geoipdbfile);
config_release_config();
/* REM 3D Graphics */
/* let her rip */

View File

@ -237,8 +237,11 @@ void source_clear_source (source_t *source)
source->parser = NULL;
source->con = NULL;
if (source->format)
source->format->contenttype = NULL;
if (source->format) {
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 */
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)
{
const char *filename = source->dumpfilename;
interpolation_t interpolation = source->dumpfile_interpolation;
time_t curtime = time(NULL);
#ifndef _WIN32
/* some of the below functions seems not to be standard winapi functions */
char buffer[PATH_MAX];
struct tm *loctime;
#endif
if (!filename) {
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);
if (interpolation == INTERPOLATION_DEFAULT) {
#ifndef _WIN32
/* Convert it to local time representation. */
loctime = localtime(&curtime);
strftime(buffer, sizeof(buffer), filename, loctime);
filename = buffer;
interpolation = INTERPOLATION_STRFTIME;
#else
interpolation = INTERPOLATION_NONE;
#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");
@ -1258,11 +1291,13 @@ static void source_apply_mount (ice_config_t *config, source_t *source, mount_pr
}
if (mountinfo) {
source->dumpfile_size_limit = mountinfo->dumpfile_size_limit;
source->dumpfile_time_limit = mountinfo->dumpfile_time_limit;
source->dumpfile_size_limit = mountinfo->dumpfile_size_limit;
source->dumpfile_time_limit = mountinfo->dumpfile_time_limit;
source->dumpfile_interpolation = mountinfo->dumpfile_interpolation;
} else {
source->dumpfile_size_limit = 0;
source->dumpfile_time_limit = 0;
source->dumpfile_size_limit = 0;
source->dumpfile_time_limit = 0;
source->dumpfile_interpolation = INTERPOLATION_DEFAULT;
}

View File

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

View File

@ -934,8 +934,10 @@ static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, unsigned int flags, const
if (source_real->running)
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_geoip_to_mount(source_real, xmlnode, client->mode);
}
}
avl_tree_unlock(global.source_tree);

View File

@ -39,6 +39,9 @@
#include <windows.h>
#endif
#include <igloo/uuid.h>
#include <igloo/error.h> /* for igloo_ERROR_NONE */
#include "common/net/sock.h"
#include "common/thread/thread.h"
@ -53,6 +56,7 @@
#include "auth.h"
#include "acl.h"
#include "listensocket.h"
#include "global.h" /* for igloo_instance */
#define CATMODULE "util"
@ -1271,3 +1275,82 @@ int get_line(FILE *file, char *buf, size_t siz)
}
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);
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__ */

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);
render_node(renderer, doc, cur, node, cache);
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);

View File

@ -9,4 +9,4 @@ echo Please open http://localhost:8000/ in your web browser to see the web inter
echo.
echo.
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 "Icecast"
OutFile "icecast_win32_2.5-beta3.exe"
OutFile "icecast_win32_TOBEREPLACED.exe"
;Default installation folder
InstallDir "$PROGRAMFILES32\Icecast"