#!/bin/bash # _____ _ _ # | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___ # | __| _| -_| -_| . | . | | . | . | | -_| # |__| |_| |___|___|___|___|_|_|_|___|___|_|_|___| # # Freedom in the Cloud # # Icecast application # # Notes: An attempt was made to get ices2 running with systemd, but that # was very unsuccessful. Instead there's a hacky cron entry which # starts icecast2 and ices2 if necessary # # License # ======= # # Copyright (C) 2017-2018 Bob Mottram # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . VARIANTS='full' IN_DEFAULT_INSTALL=0 SHOW_ON_ABOUT=1 SHOW_ICANN_ADDRESS_ON_ABOUT=0 ICECAST_DOMAIN_NAME= ICECAST_CODE= ICECAST_PORT=8005 ICECAST_ONION_PORT=8146 ICECAST_DIR=/icestream ICECAST_PLAYLIST_FILE=/etc/ices2/playlist.txt ICECAST_LOGIN_TEXT=$"Icecast login" ICECAST_SHORT_DESCRIPTION=$'Icecast' ICECAST_DESCRIPTION=$'Icecast' ICECAST_MOBILE_APP_URL= icecast_variables=(MY_USERNAME MY_EMAIL_ADDRESS ONION_ONLY ICECAST_DOMAIN_NAME ICECAST_CODE DEFAULT_LANGUAGE) function icecast_rescan { if [ -d $ICECAST_DIR ]; then if [ -f $ICECAST_PLAYLIST_FILE ]; then rm $ICECAST_PLAYLIST_FILE fi icecast_add_file_to_playlist $ICECAST_DIR fi } function icecast_update_daemon { systemctl stop icecast2 if [ -f /etc/init.d/icecast2 ]; then rm /etc/init.d/icecast2 fi { echo '#!/bin/sh'; echo "kill \$(pidof ices2)"; echo 'systemctl stop icecast2'; } > /usr/bin/stop_icecast chmod +x /usr/bin/stop_icecast # Note that the sleep here actually is important { echo '#!/bin/bash'; echo "isrunning=\$(ps aux | grep ices2)"; echo "if [[ \"\$isrunning\" != *\"ices-playlist\"* ]]; then"; echo ' systemctl start icecast2'; echo ' sleep 3'; echo ' cd /etc/ices2'; echo ' ices2 ices-playlist.xml'; echo 'fi'; } > /usr/bin/start_icecast chmod +x /usr/bin/start_icecast { echo '[Unit]'; echo 'Description=Icecast'; echo 'After=network.target'; echo 'After=tor.service'; echo ''; echo '[Service]'; echo 'User=icecast2'; echo 'Group=icecast'; echo 'ExecStart=/usr/bin/icecast2 -c /etc/icecast2/icecast.xml'; echo 'Restart=on-failure'; echo 'RestartSec=10'; echo ''; echo '[Install]'; echo 'WantedBy=multi-user.target'; } > /etc/systemd/system/icecast2.service chown -R icecast2:icecast /etc/ices2 chown -R icecast2:icecast /etc/icecast2 systemctl daemon-reload systemctl enable icecast2 if ! grep -q "start_icecast" /etc/crontab; then cron_add_mins 1 '/usr/bin/start_icecast 2> /dev/null' fi } function change_password_icecast { curr_username="$1" new_user_password="$2" stop_icecast sed -i -e "s|[^<]*|$new_user_password|" \ -e "s|[^<]*|$new_user_password|" \ -e "s|[^<]*|$new_user_password|" \ /etc/icecast2/icecast.xml sed -i "s|.*|${new_user_password}|g" /etc/ices2/ices-playlist.xml "${PROJECT_NAME}-pass" -u "$curr_username" -a icecast -p "$new_user_password" start_icecast } function logging_on_icecast { echo -n '' } function logging_off_icecast { echo -n '' } function reconfigure_icecast { echo -n '' } function icecast_convert_files { clear cd "${1}" || exit 2346824648 echo $'Converting any mp3 files to ogg format' find . -type f -name '*.mp3' -exec bash -c 'ffmpeg -i "$0" -c:a libvorbis -q:a 4 "${0/%mp3/ogg}"' '{}' \; find . -name "*.mp3" -print0 | xargs -0 rm -f echo $'Converting any mp4 files to ogv format' find . -type f -name '*.mp4' -exec bash -c 'ffmpeg -i "$0" -c:a libvorbis -q:a 4 "${0/%mp3/ogv}"' '{}' \; find . -name "*.mp4" -print0 | xargs -0 rm -f chown -R icecast2:icecast $ICECAST_DIR } function icecast_add_file_to_playlist { files_dir="${1}" if [ ! -d "$files_dir" ]; then return fi echo $'Adding ogg files to playlist' find "$files_dir" -type f -name '*.ogg' -print0 | while read -r -d $'\0' file; do if ! grep -q "$file" $ICECAST_PLAYLIST_FILE; then echo "$file" >> $ICECAST_PLAYLIST_FILE fi done echo $'Adding ogv files to playlist' find "$files_dir" -type f -name '*.ogv' -print0 | while read -r -d $'\0' file; do if ! grep -q "$file" $ICECAST_PLAYLIST_FILE; then echo "$file" >> $ICECAST_PLAYLIST_FILE fi done chown icecast2:icecast $ICECAST_PLAYLIST_FILE stop_icecast start_icecast } function icecast_import_from_directory { data=$(mktemp 2>/dev/null) dialog --title "Choose a directory containing stream files" --dselect "/home/$MY_USERNAME/" 30 60 2> "$data" selected_dir=$(cat "$data") rm -f "$data" if [[ "$selected_dir" == "$ICECAST_DIR" ]]; then return fi if [ ! -d "$selected_dir" ]; then return fi if [[ "$selected_dir" == "/home/$MY_USERNAME/" ]]; then return fi if [[ "$selected_dir" == "/home/$MY_USERNAME/."* ]]; then return fi if [[ "$selected_dir" == *"/Maildir" || "$selected_dir" == *"/Sync" ]]; then return fi dialog --title $"Import stream files directory into Icecast" \ --backtitle $"Freedombone Control Panel" \ --defaultno \ --yesno $"\\nImport the directory:\\n\\n $selected_dir" 12 75 sel=$? case $sel in 1) return;; 255) return;; esac if [ ! -d $ICECAST_DIR ]; then mkdir -p $ICECAST_DIR fi dest_dir=$(basename "$selected_dir") # shellcheck disable=SC2086 mv $selected_dir $ICECAST_DIR icecast_convert_files "$ICECAST_DIR/$dest_dir" icecast_add_file_to_playlist "$ICECAST_DIR/$dest_dir" dialog --title $"Import stream files directory into Icecast" \ --msgbox $"Import success" 6 40 } function icecast_import_from_usb { clear detect_usb_drive if [ ! -b "$USB_DRIVE" ]; then dialog --title $"Import stream files from USB drive" --msgbox $'No USB drive found' 6 50 return fi backup_mount_drive "${USB_DRIVE}" if [ ! -d "$USB_MOUNT$ICECAST_DIR" ]; then dialog --title $"Import stream files from USB drive" --msgbox $'No stream files directory found on USB drive' 6 50 backup_unmount_drive "${USB_DRIVE}" fi cp -ru "$USB_MOUNT$ICECAST_DIR/"* $ICECAST_DIR backup_unmount_drive "${USB_DRIVE}" icecast_convert_files $ICECAST_DIR dialog --title $"Import stream files from USB drive" --msgbox $'Import complete. You may now remove the USB drive' 6 50 } function icecast_export_to_usb { clear detect_usb_drive if [ ! -b "$USB_DRIVE" ]; then dialog --title $"Export stream files to USB drive" --msgbox $'No USB drive found' 6 50 return fi backup_mount_drive "${USB_DRIVE}" if [ ! -d "$USB_MOUNT$ICECAST_DIR" ]; then mkdir -p "$USB_MOUNT$ICECAST_DIR" fi cp -ru "$ICECAST_DIR/"* "$USB_MOUNT$ICECAST_DIR" backup_unmount_drive "${USB_DRIVE}" dialog --title $"Export stream files to USB drive" --msgbox $'Export complete. You may now remove the USB drive' 6 50 } function icecast_format_drive { detect_usb_drive data=$(mktemp 2>/dev/null) dialog --title $"Format USB drive $USB_DRIVE for stream file storage" \ --backtitle $"Freedombone Control Panel" \ --defaultno \ --yesno $"\\nPlease confirm that you wish to format drive\\n\\n ${USB_DRIVE}\\n\\nAll current data on the drive will be lost, and you will be prompted to give a password used to encrypt the drive.\\n\\nDANGER: If you screw up here and format the wrong drive it's your own fault!" 16 60 sel=$? case $sel in 1) rm -f "$data" return;; 255) rm -f "$data" return;; esac rm -f "$data" clear echo '' echo $"Formatting drive $USB_DRIVE. ALL CONTENTS WILL BE LOST." echo '' "${PROJECT_NAME}-format" "$USB_DRIVE" dialog --title $"Format USB drive $USB_DRIVE for stream file storage" --msgbox $'Format complete. You may now export stream files or remove the USB drive' 6 50 } function icecast_edit_playlist { editor $ICECAST_PLAYLIST_FILE stop_icecast start_icecast } function icecast_change_login { read_config_param "$MY_USERNAME" ICECAST_USER_PASSWORD=$("${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a icecastuser) data=$(mktemp 2>/dev/null) dialog --title $"Change Icecast stream visitor login" \ --backtitle $"Freedombone Control Panel" \ --inputbox $"Enter the new login password for stream visitors" 8 60 "$ICECAST_USER_PASSWORD" 2>"$data" sel=$? case $sel in 0) ICECAST_USER_PASSWORD=$(<"$data") if [[ "$ICECAST_USER_PASSWORD" != *' '* ]]; then if [ ${#ICECAST_USER_PASSWORD} -gt 8 ]; then "${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a icecastuser -p "$ICECAST_USER_PASSWORD" dialog --title $"Change Icecast stream visitor login" \ --msgbox $"Password changed to $ICECAST_USER_PASSWORD" 6 75 fi fi ;; esac rm -f "$data" } function icecast_enable_login { dialog --title $"Enable Icecast login" \ --backtitle $"Freedombone Control Panel" \ --defaultno \ --yesno $"\\nDo you want to add a login so that random web users can't access your stream?" 10 60 sel=$? case $sel in 0) if grep -q '#auth_basic' /etc/nginx/sites-available/icecast; then sed -i 's|#auth_basic|auth_basic|g' /etc/nginx/sites-available/icecast systemctl restart nginx fi read_config_param "$MY_USERNAME" ICECAST_USER_PASSWORD=$("${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a icecastuser) dialog --title $"Enable Icecast login" \ --msgbox $"Icecast logins are now enabled with the password $ICECAST_USER_PASSWORD" 6 65 ICECAST_USER_PASSWORD= ;; 1) if ! grep -q '#auth_basic' /etc/nginx/sites-available/icecast; then sed -i 's|auth_basic|#auth_basic|g' /etc/nginx/sites-available/icecast systemctl restart nginx fi dialog --title $"Disable Icecast login" \ --msgbox $"Icecast logins are now disabled. Anyone can access your stream." 6 65 ;; esac } function icecast_set_stream_name { data=$(mktemp 2>/dev/null) dialog --backtitle $"Freedombone Control Panel" \ --title $"Change Icecast stream details" \ --form "\\n" 8 60 4 \ $"Stream name:" 1 1 "Example stream name" 1 18 40 1000 \ $"Description:" 2 1 "A short description of your stream" 2 18 40 1000 \ $"Genre:" 3 1 "Example genre" 3 18 40 1000 \ 2> "$data" sel=$? case $sel in 1) rm -f "$data" return;; 255) rm -f "$data" return;; esac stream_name=$(sed -n 1p < "$data") stream_description=$(sed -n 2p < "$data") stream_genre=$(sed -n 3p < "$data") if [ ${#stream_name} -gt 2 ]; then sed -i "s|.*|${stream_name}|g" /etc/ices2/ices-playlist.xml fi if [ ${#stream_description} -gt 2 ]; then sed -i "s|.*|${stream_description}|g" /etc/ices2/ices-playlist.xml fi if [ ${#stream_genre} -gt 2 ]; then sed -i "s|.*|${stream_genre}|g" /etc/ices2/ices-playlist.xml fi rm -f "$data" stop_icecast start_icecast } function icecast_set_maximum_streams { data=$(mktemp 2>/dev/null) dialog --title $"Set the maximum clients" \ --backtitle $"Freedombone Control Panel" \ --inputbox $"Maximum number of clients" 8 40 "10" 2>"$data" sel=$? case $sel in 0) max_clients=$(<"$data") if [ ${#max_clients} -gt 0 ]; then if [[ "$max_clients" != *' '* ]]; then # shellcheck disable=SC2076 if [[ "$max_clients" =~ '^[0-9]+$' ]] ; then sed -i "s|.*|${max_clients}|g" /etc/icecast2/icecast.xml stop_icecast start_icecast dialog --title $"Set the maximum clients" \ --msgbox $"\\nMaximum Icecast clients was set to ${max_clients}" 8 50 fi fi fi ;; esac rm -f "$data" } function configure_interactive_icecast { W=(1 $"Import stream files from directory" 2 $"Import stream files from USB drive" 3 $"Format a USB drive for stream file storage" 4 $"Export stream files to USB drive" 5 $"Manually edit playlist" 6 $"Enable login for stream visitors" 7 $"Change password for stream visitors" 8 $"Re-scan playlist" 9 $"Restart stream" 10 $"Set Stream Name/Description/Genre" 11 $"Set maximum number of clients/streams") while true do # shellcheck disable=SC2068 selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Icecast" --menu $"Choose an operation, or ESC to exit:" 20 60 11 "${W[@]}" 3>&2 2>&1 1>&3) if [ ! "$selection" ]; then break fi case $selection in 1) icecast_import_from_directory;; 2) icecast_import_from_usb;; 3) icecast_format_drive;; 4) icecast_export_to_usb;; 5) icecast_edit_playlist;; 6) icecast_enable_login;; 7) icecast_change_login;; 8) clear echo $'Rescanning Icecast playlist' icecast_rescan;; 9) clear echo $'Restarting Icecast stream' stop_icecast start_icecast;; 10) icecast_set_stream_name;; 11) icecast_set_maximum_streams;; esac done } function upgrade_icecast { icecast_update_daemon } function backup_local_icecast { if [ ! -d $ICECAST_DIR ]; then return fi stop_icecast cp /etc/nginx/.icepasswd $ICECAST_DIR cp /etc/ices2/ices-playlist.xml $ICECAST_DIR function_check backup_directory_to_usb backup_directory_to_usb $ICECAST_DIR icecast rm $ICECAST_DIR/.icepasswd start_icecast } function restore_local_icecast { if [ ! -d $ICECAST_DIR ]; then return fi stop_icecast temp_restore_dir=/root/tempicecast function_check restore_directory_from_usb restore_directory_from_usb $temp_restore_dir icecast if [ -d $temp_restore_dir$ICECAST_DIR ]; then cp -r $temp_restore_dir$ICECAST_DIR $ICECAST_DIR/ else cp -r $temp_restore_dir/* $ICECAST_DIR/* fi cp $ICECAST_DIR/.icepasswd /etc/nginx/.icepasswd rm $ICECAST_DIR/.icepasswd cp $ICECAST_DIR/ices-playlist.xml /etc/ices2 rm $ICECAST_DIR/ices-playlist.xml chown -R icecast2:icecast $ICECAST_DIR chown -R icecast2:icecast /etc/ices2 start_icecast rm -rf $temp_restore_dir } function backup_remote_icecast { if [ ! -d $ICECAST_DIR ]; then return fi stop_icecast cp /etc/nginx/.icepasswd $ICECAST_DIR cp /etc/ices2/ices-playlist.xml $ICECAST_DIR function_check backup_directory_to_friend backup_directory_to_friend $ICECAST_DIR icecast rm $ICECAST_DIR/.icepasswd start_icecast } function restore_remote_icecast { if [ ! -d $ICECAST_DIR ]; then return fi stop_icecast temp_restore_dir=/root/tempicecast function_check restore_directory_from_friend restore_directory_from_friend $temp_restore_dir icecast if [ -d $temp_restore_dir$ICECAST_DIR ]; then cp -r $temp_restore_dir$ICECAST_DIR $ICECAST_DIR/ else cp -r $temp_restore_dir/* $ICECAST_DIR/* fi cp $ICECAST_DIR/.icepasswd /etc/nginx/.icepasswd rm $ICECAST_DIR/.icepasswd cp $ICECAST_DIR/ices-playlist.xml /etc/ices2 rm $ICECAST_DIR/ices-playlist.xml chown -R icecast2:icecast $ICECAST_DIR start_icecast rm -rf $temp_restore_dir } function remove_icecast { nginx_dissite icecast sed -i '/start_icecast/d' /etc/crontab stop_icecast systemctl disable icecast2 rm /etc/systemd/system/icecast2.service rm /usr/bin/start_icecast rm /usr/bin/stop_icecast if [ -f /etc/nginx/sites-available/icecast ]; then rm /etc/nginx/sites-available/icecast fi if [ -d /var/www/icecast ]; then rm -rf /var/www/icecast fi apt-get -yq remove --purge icecast2 if [ -d /etc/icecast2 ]; then rm -rf /etc/icecast2 fi if [ -d /etc/ices2 ]; then rm -rf /etc/ices2 fi function_check remove_onion_service remove_onion_service icecast ${ICECAST_ONION_PORT} sed -i '/icecast/d' "$COMPLETION_FILE" } function install_icecast { apt-get -yq install software-properties-common debconf-utils apt-get -yq update debconf-set-selections <<< "icecast2 icecast2/icecast-setup boolean false" apt-get -yq install icecast2 apt-get -yq install ices2 ffmpeg apache2-utils if [ ! -f /etc/icecast2/icecast.xml ]; then echo $'Icecast not installed' exit 7923528 fi if [ ! "${ICECAST_PASSWORD}" ]; then if [ -f "${IMAGE_PASSWORD_FILE}" ]; then ICECAST_PASSWORD="$(printf "%s" "$(cat "$IMAGE_PASSWORD_FILE")")" else ICECAST_PASSWORD="$(create_password "${MINIMUM_PASSWORD_LENGTH}")" fi fi ICECAST_ONION_HOSTNAME=$(add_onion_service icecast 80 ${ICECAST_ONION_PORT}) sed -i -e "s|[^<]*|$ICECAST_PASSWORD|" \ -e "s|[^<]*|$ICECAST_PASSWORD|" \ -e "s|[^<]*|$ICECAST_PASSWORD|" \ -e "s|[^<]*|$ICECAST_ONION_HOSTNAME|" \ /etc/icecast2/icecast.xml sed -i "s|.*|10|g" /etc/icecast2/icecast.xml sed -i "s|.*|$ICECAST_PORT|g" /etc/icecast2/icecast.xml sed -i "s|.*|$MY_USERNAME|g" /etc/icecast2/icecast.xml sed -i "s|.*|$MY_EMAIL_ADDRESS|g" /etc/icecast2/icecast.xml sed -i "s|.*|The Interwebs|g" /etc/icecast2/icecast.xml #sed -i 's|'; echo ' 1'; echo ' '; echo ' /var/log/ices'; echo ' ices.log'; echo ' '; echo ' 1'; echo ' '; echo ' 0'; echo ''; echo ' '; echo ' '; echo ''; echo ' '; echo ' '; echo ' '; echo ' Example stream name'; echo ' Example genre'; echo ' A short description of your stream'; echo ' '; echo ''; echo ' '; echo ''; echo ' '; echo ' playlist'; echo ' basic'; echo " $ICECAST_PLAYLIST_FILE"; echo ' '; echo ' 0'; echo ' '; echo ' 0'; echo ' '; echo ' 0'; echo ' '; echo ''; echo ' '; echo ' '; echo ' '; echo ' localhost'; echo " $ICECAST_PORT"; echo " $ICECAST_PASSWORD"; echo ' /example1.ogg'; echo ' '; echo ' 2'; echo ' 5 '; echo ''; echo ' '; echo ' 80'; echo ''; echo ' '; echo ' '; echo ' 64000'; echo ' 22050'; echo ' 1'; echo ' '; echo ' '; echo ''; echo ' '; echo ''; } > /etc/ices2/ices-playlist.xml sed -i 's|ENABLE=.*|ENABLE=true|g' /etc/default/icecast2 if [ ! -d $ICECAST_DIR ]; then mkdir $ICECAST_DIR fi chown -R icecast2:icecast $ICECAST_DIR # create a password for users ICECAST_USER_PASSWORD="$(create_password "${MINIMUM_PASSWORD_LENGTH}")" if grep -q "$MY_USERNAME:" /etc/nginx/.icepasswd; then sed -i "/$MY_USERNAME:/d" /etc/nginx/.icepasswd fi echo "$ICECAST_USER_PASSWORD" | htpasswd -i -s -c /etc/nginx/.icepasswd "$MY_USERNAME" if [ ! -f /etc/nginx/.icepasswd ]; then echo $'/etc/nginx/.icepasswd not found' exit 73528235 fi "${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a icecast -p "$ICECAST_PASSWORD" "${PROJECT_NAME}-pass" -u "$MY_USERNAME" -a icecastuser -p "$ICECAST_USER_PASSWORD" groupadd icecast useradd -c "Icecast system account" -d /etc/icecast2 -m -r -g icecast icecast2 icecast_update_daemon nginx_ensite icecast systemctl restart nginx icecast_rescan start_icecast APP_INSTALLED=1 } function install_interactive_icecast { install_icecast } # NOTE: deliberately no exit 0