Line 1... |
Line 1... |
1 |
#!/bin/bash
|
1 |
#!/bin/bash
|
2 |
# $Id: alcasar.sh 2420 2017-10-01 18:56:46Z richard $
|
2 |
# $Id: alcasar.sh 2421 2017-10-08 21:57:57Z richard $
|
3 |
|
3 |
|
4 |
# alcasar.sh
|
4 |
# alcasar.sh
|
5 |
|
5 |
|
6 |
# ALCASAR Install script - CopyLeft ALCASAR Team [Rexy + 3abtux + Steweb + Crox + ...]
|
6 |
# ALCASAR Install script - CopyLeft ALCASAR Team [Rexy + 3abtux + Steweb + Crox + ...]
|
7 |
# Ce programme est un logiciel libre ; This software is free and open source
|
7 |
# Ce programme est un logiciel libre ; This software is free and open source
|
Line 31... |
Line 31... |
31 |
# network : Network parameters
|
31 |
# network : Network parameters
|
32 |
# ACC : ALCASAR Control Center installation
|
32 |
# ACC : ALCASAR Control Center installation
|
33 |
# CA : Certification Authority initialization
|
33 |
# CA : Certification Authority initialization
|
34 |
# time_server : NTPd configuration
|
34 |
# time_server : NTPd configuration
|
35 |
# init_db : Initilization of radius database managed with MariaDB
|
35 |
# init_db : Initilization of radius database managed with MariaDB
|
36 |
# radius : FreeRadius initialisation
|
36 |
# freeradius : FreeRadius initialisation
|
37 |
# chilli : coovachilli initialisation (+authentication page)
|
37 |
# chilli : coovachilli initialisation (+authentication page)
|
38 |
# dansguardian : DansGuardian filtering HTTP proxy configuration
|
38 |
# dansguardian : DansGuardian filtering HTTP proxy configuration
|
39 |
# antivirus : HAVP + libclamav configuration
|
39 |
# antivirus : HAVP + libclamav configuration
|
40 |
# tinyproxy : little proxy for user filtered with "WL + antivirus" and "antivirus"
|
40 |
# tinyproxy : little proxy for user filtered with "WL + antivirus" and "antivirus"
|
41 |
# ulogd : log system in userland (match NFLOG target of iptables)
|
41 |
# ulogd : log system in userland (match NFLOG target of iptables)
|
Line 393... |
Line 393... |
393 |
grep -v '[eE]nter password:' | \
|
393 |
grep -v '[eE]nter password:' | \
|
394 |
sed -e "s/PBKDF2 hash of your password is //"`
|
394 |
sed -e "s/PBKDF2 hash of your password is //"`
|
395 |
echo "GRUB2_PASSWORD=$pbkdf2" > /boot/grub2/user.cfg
|
395 |
echo "GRUB2_PASSWORD=$pbkdf2" > /boot/grub2/user.cfg
|
396 |
chmod 0600 /boot/grub2/user.cfg
|
396 |
chmod 0600 /boot/grub2/user.cfg
|
397 |
echo "# Login name and password to protect GRUB2 boot menu (!!!qwerty keyboard) : " > $PASSWD_FILE
|
397 |
echo "# Login name and password to protect GRUB2 boot menu (!!!qwerty keyboard) : " > $PASSWD_FILE
|
- |
|
398 |
echo "GRUB2_user=root" >> $PASSWD_FILE
|
398 |
echo "GRUB2_user=root GRUB2_password=$grub2pwd" >> $PASSWD_FILE
|
399 |
echo "GRUB2_password=$grub2pwd" >> $PASSWD_FILE
|
399 |
mysqlpwd=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
400 |
mysqlpwd=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
400 |
echo "# Login name and Password of MariaDB administrator:" >> $PASSWD_FILE
|
401 |
echo "# Login name and Password of MariaDB administrator:" >> $PASSWD_FILE
|
401 |
echo "db_root=$mysqlpwd" >> $PASSWD_FILE
|
402 |
echo "db_root=$mysqlpwd" >> $PASSWD_FILE
|
402 |
radiuspwd=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
403 |
radiuspwd=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
403 |
echo "# Login name and password of MariaDB user:" >> $PASSWD_FILE
|
404 |
echo "# Login name and password of MariaDB user:" >> $PASSWD_FILE
|
- |
|
405 |
echo "db_user=$DB_USER" >> $PASSWD_FILE
|
404 |
echo "db_user=$DB_USER db_password=$radiuspwd" >> $PASSWD_FILE
|
406 |
echo "db_password=$radiuspwd" >> $PASSWD_FILE
|
405 |
secretuam=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
407 |
secretuam=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
406 |
echo "# Shared secret between the script 'intercept.php' and coova-chilli:" >> $PASSWD_FILE
|
408 |
echo "# Shared secret between the script 'intercept.php' and coova-chilli:" >> $PASSWD_FILE
|
407 |
echo "secret_uam=$secretuam" >> $PASSWD_FILE
|
409 |
echo "secret_uam=$secretuam" >> $PASSWD_FILE
|
408 |
secretradius=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
410 |
secretradius=`cat /dev/urandom | tr -dc [:alnum:] | head -c16`
|
409 |
echo "# Shared secret between coova-chilli and FreeRadius:" >> $PASSWD_FILE
|
411 |
echo "# Shared secret between coova-chilli and FreeRadius:" >> $PASSWD_FILE
|
Line 1058... |
Line 1060... |
1058 |
/usr/bin/systemctl unset-environment MYSQLD_OPTS
|
1060 |
/usr/bin/systemctl unset-environment MYSQLD_OPTS
|
1059 |
/usr/bin/systemctl daemon-reload
|
1061 |
/usr/bin/systemctl daemon-reload
|
1060 |
} # End of init_db ()
|
1062 |
} # End of init_db ()
|
1061 |
|
1063 |
|
1062 |
##########################################################################
|
1064 |
##########################################################################
|
1063 |
## Fonction "radius" ##
|
1065 |
## Fonction "freeradius" ##
|
1064 |
## - Paramètrage des fichiers de configuration FreeRadius ##
|
1066 |
## - Paramètrage des fichiers de configuration FreeRadius ##
|
1065 |
## - Affectation du secret partagé entre coova-chilli et freeradius ##
|
1067 |
## - Affectation du secret partagé entre coova-chilli et freeradius ##
|
1066 |
## - Modification de fichier de conf pour l'accès à Mysql ##
|
1068 |
## - Modification de fichier de conf pour l'accès à Mysql ##
|
1067 |
##########################################################################
|
1069 |
##########################################################################
|
1068 |
radius ()
|
1070 |
freeradius ()
|
1069 |
{
|
1071 |
{
|
1070 |
cp -f $DIR_CONF/empty-radiusd-db.sql /etc/raddb/
|
1072 |
cp -f $DIR_CONF/empty-radiusd-db.sql /etc/raddb/
|
1071 |
chown -R radius:radius /etc/raddb
|
1073 |
chown -R radius:radius /etc/raddb
|
1072 |
[ -e /etc/raddb/radiusd.conf.default ] || cp /etc/raddb/radiusd.conf /etc/raddb/radiusd.conf.default
|
1074 |
[ -e /etc/raddb/radiusd.conf.default ] || cp /etc/raddb/radiusd.conf /etc/raddb/radiusd.conf.default
|
1073 |
# Set radius global parameters (radius.conf)
|
1075 |
# Set radius global parameters (radius.conf)
|
Line 1120... |
Line 1122... |
1120 |
|
1122 |
|
1121 |
# queries.conf modifications : case sensitive for username, check simultaneous use, patch on 'postauth' table, etc.
|
1123 |
# queries.conf modifications : case sensitive for username, check simultaneous use, patch on 'postauth' table, etc.
|
1122 |
[ -e /etc/raddb/mods-config/sql/main/mysql/queries.conf.default ] || cp /etc/raddb/mods-config/sql/main/mysql/queries.conf /etc/raddb/mods-config/sql/main/mysql/queries.conf.default
|
1124 |
[ -e /etc/raddb/mods-config/sql/main/mysql/queries.conf.default ] || cp /etc/raddb/mods-config/sql/main/mysql/queries.conf /etc/raddb/mods-config/sql/main/mysql/queries.conf.default
|
1123 |
cp -f $DIR_CONF/radius/queries.conf /etc/raddb/mods-config/sql/main/mysql/queries.conf
|
1125 |
cp -f $DIR_CONF/radius/queries.conf /etc/raddb/mods-config/sql/main/mysql/queries.conf
|
1124 |
chown -R radius:radius /etc/raddb/mods-config/sql/main/mysql/queries.conf
|
1126 |
chown -R radius:radius /etc/raddb/mods-config/sql/main/mysql/queries.conf
|
- |
|
1127 |
# sqlcounter modifications
|
- |
|
1128 |
[ -e /etc/raddb/mods-config/sql/counter/mysql/dailycounter.conf.default ] || cp /etc/raddb/mods-config/sql/counter/mysql/dailycounter.conf /etc/raddb/mods-config/sql/counter/mysql/dailycounter.conf.default
|
- |
|
1129 |
cat << EOF > /etc/raddb/mods-config/sql/counter/mysql/dailycounter.conf
|
- |
|
1130 |
query = "SELECT IFNULL((SELECT SUM(acctsessiontime - \
|
- |
|
1131 |
GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)),0)) \
|
- |
|
1132 |
FROM radacct WHERE username = '%{${key}}' AND \
|
1125 |
# sqlcounter.conf modifications (change the Max-All-Session-Time counter)
|
1133 |
UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'),0)"
|
- |
|
1134 |
EOF
|
1126 |
[ -e /etc/raddb/sql/mysql/counter.conf.default ] || cp /etc/raddb/sql/mysql/counter.conf /etc/raddb/sql/mysql/counter.conf.default
|
1135 |
[ -e /etc/raddb/mods-config/sql/counter/mysql/monthlycounter.conf.default ] || cp /etc/raddb/mods-config/sql/counter/mysql/monthlycounter.conf /etc/raddb/mods-config/sql/counter/mysql/monthlycounter.conf.default
|
1127 |
cp -f $DIR_CONF/radius/counter.conf /etc/raddb/sql/mysql/counter.conf
|
1136 |
cat << EOF > /etc/raddb/mods-config/sql/counter/mysql/monthlycounter.conf
|
- |
|
1137 |
query = "SELECT IFNULL((SELECT SUM(acctsessiontime - \
|
- |
|
1138 |
GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
|
- |
|
1139 |
FROM radacct WHERE username='%{${key}}' AND \
|
- |
|
1140 |
UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'),0)"
|
- |
|
1141 |
EOF
|
- |
|
1142 |
[ -e /etc/raddb/mods-config/sql/counter/mysql/noresetcounter.conf.default ] || cp /etc/raddb/mods-config/sql/counter/mysql/noresetcounter.conf /etc/raddb/mods-config/sql/counter/mysql/noresetcounter.conf.default
|
- |
|
1143 |
cat << EOF > /etc/raddb/mods-config/sql/counter/mysql/noresetcounter.conf
|
- |
|
1144 |
# This is the query modified for ALCASAR needs (thanks to Daniel Laliberte --> authorized period after the first connection)
|
- |
|
1145 |
query = "SELECT IFNULL((SELECT TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime)) \
|
- |
|
1146 |
FROM radacct \
|
- |
|
1147 |
WHERE UserName='%{${key}}' \
|
- |
|
1148 |
ORDER BY acctstarttime \
|
- |
|
1149 |
LIMIT 1),0)"
|
- |
|
1150 |
EOF
|
1128 |
# make certain that mysql is up before radius start
|
1151 |
# make certain that mysql is up before freeradius start
|
1129 |
[ -e /lib/systemd/system/radiusd.service.default ] || cp /lib/systemd/system/radiusd.service /lib/systemd/system/radiusd.service.default
|
1152 |
[ -e /lib/systemd/system/radiusd.service.default ] || cp /lib/systemd/system/radiusd.service /lib/systemd/system/radiusd.service.default
|
1130 |
$SED "s?^After=.*?After=syslog.target network.target mysqld.service?g" /lib/systemd/system/radiusd.service
|
1153 |
$SED "s?^After=.*?After=syslog.target network.target mysqld.service?g" /lib/systemd/system/radiusd.service
|
1131 |
/usr/bin/systemctl daemon-reload
|
1154 |
/usr/bin/systemctl daemon-reload
|
1132 |
|
- |
|
1133 |
# Allow apache to change some conf files (ie : ldap on/off)
|
1155 |
# Allow apache to change some conf files (ie : ldap on/off)
|
1134 |
chgrp apache /etc/raddb /etc/raddb/sites-available /etc/raddb/mods-available
|
1156 |
chgrp apache /etc/raddb /etc/raddb/sites-available /etc/raddb/mods-available
|
1135 |
|
1157 |
|
1136 |
} # End radius ()
|
1158 |
} # End freeradius ()
|
1137 |
|
1159 |
|
1138 |
##################################################################################
|
1160 |
##################################################################################
|
1139 |
## Fonction "chilli" ##
|
1161 |
## Fonction "chilli" ##
|
1140 |
## - Création du fichier d'initialisation et de configuration de coova-chilli ##
|
1162 |
## - Création du fichier d'initialisation et de configuration de coova-chilli ##
|
1141 |
## - Paramètrage de la page d'authentification (intercept.php) ##
|
1163 |
## - Paramètrage de la page d'authentification (intercept.php) ##
|
Line 1620... |
Line 1642... |
1620 |
## Function "dnsmasq" ##
|
1642 |
## Function "dnsmasq" ##
|
1621 |
##################################################
|
1643 |
##################################################
|
1622 |
dnsmasq ()
|
1644 |
dnsmasq ()
|
1623 |
{
|
1645 |
{
|
1624 |
[ -d /var/log/dnsmasq ] || mkdir /var/log/dnsmasq
|
1646 |
[ -d /var/log/dnsmasq ] || mkdir /var/log/dnsmasq
|
1625 |
[ -e /etc/sysconfig/dnsmasq.default ] || cp /etc/sysconfig/dnsmasq /etc/sysconfig/dnsmasq.default
|
- |
|
1626 |
# $SED "s?^OPTION=.*?OPTION=-C /etc/dnsmasq.conf?g" /etc/sysconfig/dnsmasq # default conf file for the first dnsmasq instance
|
- |
|
1627 |
$SED "s?^.*OPTIONS=.*?#OPTIONS=\"--log-async=250 --log-queries --log-facility=/var/log/dnsmasq/queries.log\"?g" /etc/sysconfig/dnsmasq # General Options for dnslog or debugging
|
- |
|
1628 |
$SED "s?^local=.*?local=/$DOMAIN/?g" $DIR_DEST_ETC/alcasar-dns-name # default domain name for all dnsmasq daemons
|
- |
|
1629 |
[ -e /etc/dnsmasq.conf.default ] || cp /etc/dnsmasq.conf /etc/dnsmasq.conf.default
|
- |
|
1630 |
# 1st dnsmasq listen on udp 53 ("dnsmasq - forward"). It's used as dhcp server only if "alcasar-bypass" is on.
|
1647 |
# 1st dnsmasq listen on udp 53 ("dnsmasq - forward"). It's used as dhcp server only if "alcasar-bypass" is on.
|
1631 |
cat << EOF > /etc/dnsmasq.conf
|
1648 |
cat << EOF > /etc/dnsmasq.conf
|
1632 |
# Configuration file for "dnsmasq in forward mode"
|
1649 |
# Configuration file for "dnsmasq in forward mode"
|
1633 |
conf-file=$DIR_DEST_ETC/alcasar-dns-name # local DNS resolutions
|
1650 |
conf-file=$DIR_DEST_ETC/alcasar-dns-name # local DNS resolutions
|
1634 |
listen-address=$PRIVATE_IP
|
1651 |
listen-address=$PRIVATE_IP
|
Line 1905... |
Line 1922... |
1905 |
## - Creation du fichier de config: gammu_smsd_conf ##
|
1922 |
## - Creation du fichier de config: gammu_smsd_conf ##
|
1906 |
##################################################################
|
1923 |
##################################################################
|
1907 |
gammu_smsd()
|
1924 |
gammu_smsd()
|
1908 |
{
|
1925 |
{
|
1909 |
# Create 'gammu' databse
|
1926 |
# Create 'gammu' databse
|
1910 |
MYSQL="/usr/bin/mysql -uroot -p$mysqlpwd --exec"
|
1927 |
MYSQL="/usr/bin/mysql -uroot -p$mysqlpwd --execute"
|
1911 |
$MYSQL="CREATE DATABASE IF NOT EXISTS $DB_GAMMU;GRANT ALL ON $DB_GAMMU.* TO $DB_USER@localhost IDENTIFIED BY '$radiuspwd';FLUSH PRIVILEGES"
|
1928 |
$MYSQL="CREATE DATABASE IF NOT EXISTS $DB_GAMMU;GRANT ALL ON $DB_GAMMU.* TO $DB_USER@localhost IDENTIFIED BY '$radiuspwd';FLUSH PRIVILEGES"
|
1912 |
# Add a gammu database structure
|
1929 |
# Add a gammu database structure
|
1913 |
mysql -u$DB_USER -p$radiuspwd $DB_GAMMU < $DIR_CONF/empty-gammu-smsd-db.sql
|
1930 |
mysql -u$DB_USER -p$radiuspwd $DB_GAMMU < $DIR_CONF/empty-gammu-smsd-db.sql
|
1914 |
|
1931 |
|
1915 |
# Config file for the daemon
|
1932 |
# Config file for the daemon
|
Line 2172... |
Line 2189... |
2172 |
echo "net.ipv6.conf.default.autoconf = 0" >> /etc/sysctl.d/alcasar.conf
|
2189 |
echo "net.ipv6.conf.default.autoconf = 0" >> /etc/sysctl.d/alcasar.conf
|
2173 |
# switch to multi-users runlevel (instead of x11)
|
2190 |
# switch to multi-users runlevel (instead of x11)
|
2174 |
ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
|
2191 |
ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
|
2175 |
# GRUB modifications (only one time)
|
2192 |
# GRUB modifications (only one time)
|
2176 |
# Limit wait time to 3s - Create an alcasar entry instead of linux-nonfb - Change the default banner
|
2193 |
# Limit wait time to 3s - Create an alcasar entry instead of linux-nonfb - Change the default banner
|
2177 |
vm_vga=`lsmod | egrep -c "virtio|vmwgfx"` # test if in VM
|
2194 |
# vm_vga=`lsmod | egrep -c "virtio|vmwgfx"` # test if in VM
|
2178 |
grub_already_modified=`grep -c ALCASAR /boot/grub/menu.lst`
|
2195 |
# grub_already_modified=`grep -c ALCASAR /boot/grub/menu.lst`
|
2179 |
[ -e /etc/mageia-release.default ] || cp /etc/mageia-release /etc/mageia-release.default
|
2196 |
# [ -e /etc/mageia-release.default ] || cp /etc/mageia-release /etc/mageia-release.default
|
2180 |
if [ $grub_already_modified == 0 ]
|
2197 |
# if [ $grub_already_modified == 0 ]
|
2181 |
then
|
2198 |
# then
|
2182 |
$SED "s?^timeout.*?timeout 3?g" /boot/grub/menu.lst
|
2199 |
# $SED "s?^timeout.*?timeout 3?g" /boot/grub/menu.lst
|
2183 |
$SED "s?^title linux?title ALCASAR?g" /boot/grub/menu.lst
|
2200 |
# $SED "s?^title linux?title ALCASAR?g" /boot/grub/menu.lst
|
2184 |
$SED "/^kernel/s/splash quiet //" /boot/grub/menu.lst
|
2201 |
# $SED "/^kernel/s/splash quiet //" /boot/grub/menu.lst
|
2185 |
$SED "/^kernel/s/BOOT_IMAGE=linux /BOOT_IMAGE=linux-nonfb /" /boot/grub/menu.lst
|
2202 |
# $SED "/^kernel/s/BOOT_IMAGE=linux /BOOT_IMAGE=linux-nonfb /" /boot/grub/menu.lst
|
2186 |
$SED "/^gfxmenu/d" /boot/grub/menu.lst
|
2203 |
# $SED "/^gfxmenu/d" /boot/grub/menu.lst
|
2187 |
if [ $vm_vga == 0 ] # is not a VM
|
2204 |
# if [ $vm_vga == 0 ] # is not a VM
|
2188 |
then
|
2205 |
# then
|
2189 |
$SED "/BOOT_IMAGE=linux-nonfb/s/$/ vga=791/" /boot/grub/menu.lst # change display to 1024*768 (vga791) only if not on VM and only on ALCASAR entry
|
2206 |
# $SED "/BOOT_IMAGE=linux-nonfb/s/$/ vga=791/" /boot/grub/menu.lst # change display to 1024*768 (vga791) only if not on VM and only on ALCASAR entry
|
2190 |
fi
|
2207 |
# fi
|
2191 |
fi
|
2208 |
# fi
|
2192 |
if [ $vm_vga == 0 ] # is not a VM
|
2209 |
# if [ $vm_vga == 0 ] # is not a VM
|
2193 |
then
|
2210 |
# then
|
2194 |
cp -f $DIR_CONF/banner /etc/mageia-release
|
2211 |
cp -f $DIR_CONF/banner /etc/mageia-release
|
2195 |
echo " V$VERSION" >> /etc/mageia-release
|
2212 |
echo " V$VERSION" >> /etc/mageia-release
|
2196 |
else
|
2213 |
# else
|
2197 |
echo "ALCASAR V$VERSION" > /etc/mageia-release
|
2214 |
# echo "ALCASAR V$VERSION" > /etc/mageia-release
|
2198 |
fi
|
2215 |
# fi
|
2199 |
# Load and apply the previous conf file
|
2216 |
# Load and apply the previous conf file
|
2200 |
if [ "$mode" = "update" ]
|
2217 |
if [ "$mode" = "update" ]
|
2201 |
then
|
2218 |
then
|
2202 |
$DIR_DEST_BIN/alcasar-archive.sh --now # exports current logs in /var/Save/archive
|
2219 |
$DIR_DEST_BIN/alcasar-archive.sh --now # exports current logs in /var/Save/archive
|
2203 |
$DIR_DEST_BIN/alcasar-conf.sh --load
|
2220 |
$DIR_DEST_BIN/alcasar-conf.sh --load
|
Line 2339... |
Line 2356... |
2339 |
MAJ_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f1`
|
2356 |
MAJ_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f1`
|
2340 |
MIN_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f2|cut -c1`
|
2357 |
MIN_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f2|cut -c1`
|
2341 |
UPD_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f3`
|
2358 |
UPD_PREVIOUS_VERSION=`echo $PREVIOUS_VERSION|cut -d"." -f3`
|
2342 |
mode="update"
|
2359 |
mode="update"
|
2343 |
fi
|
2360 |
fi
|
2344 |
for func in init network ACC CA time_server init_db radius chilli dansguardian antivirus tinyproxy ulogd nfsen vnstat dnsmasq BL cron fail2ban gammu_smsd msec letsencrypt post_install
|
2361 |
for func in init network ACC CA time_server init_db freeradius chilli dansguardian antivirus tinyproxy ulogd nfsen vnstat dnsmasq BL cron fail2ban gammu_smsd msec letsencrypt post_install
|
2345 |
do
|
2362 |
do
|
2346 |
$func
|
2363 |
$func
|
2347 |
# echo "*** 'debug' : end of function $func ***"; read a
|
2364 |
# echo "*** 'debug' : end of function $func ***"; read a
|
2348 |
done
|
2365 |
done
|
2349 |
;;
|
2366 |
;;
|