freedombone-controlpanel 64KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993
  1. #!/bin/bash
  2. #
  3. # .---. . .
  4. # | | |
  5. # |--- .--. .-. .-. .-.| .-. .--.--. |.-. .-. .--. .-.
  6. # | | (.-' (.-' ( | ( )| | | | )( )| | (.-'
  7. # ' ' --' --' -' - -' ' ' -' -' -' ' - --'
  8. #
  9. # Freedom in the Cloud
  10. #
  11. # Administrator control panel for the Freedombone system
  12. #
  13. # License
  14. # =======
  15. #
  16. # Copyright (C) 2015-2018 Bob Mottram <bob@freedombone.net>
  17. #
  18. # This program is free software: you can redistribute it and/or modify
  19. # it under the terms of the GNU Affero General Public License as published by
  20. # the Free Software Foundation, either version 3 of the License, or
  21. # (at your option) any later version.
  22. #
  23. # This program is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. # GNU Affero General Public License for more details.
  27. #
  28. # You should have received a copy of the GNU Affero General Public License
  29. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  30. PROJECT_NAME='freedombone'
  31. export TEXTDOMAIN=${PROJECT_NAME}-controlpanel
  32. export TEXTDOMAINDIR="/usr/share/locale"
  33. if [[ $USER != 'root' ]]; then
  34. # show the user version of the control panel
  35. #${PROJECT_NAME}-controlpanel-user
  36. controluser
  37. exit 0
  38. fi
  39. function please_wait {
  40. local str width height length
  41. width=$(tput cols)
  42. height=$(tput lines)
  43. str=$"Please wait"
  44. length=${#str}
  45. clear
  46. tput cup $((height / 2)) $(((width / 2) - (length / 2)))
  47. echo "$str"
  48. tput cup $((height * 3 / 5)) $(((width / 2)))
  49. echo -n ''
  50. }
  51. please_wait
  52. # Start including files
  53. source /usr/local/bin/${PROJECT_NAME}-vars
  54. UTILS_FILES="/usr/share/${PROJECT_NAME}/utils/${PROJECT_NAME}-utils-*"
  55. for f in $UTILS_FILES
  56. do
  57. source "$f"
  58. done
  59. APP_FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  60. for f in $APP_FILES
  61. do
  62. source "$f"
  63. done
  64. # End including files
  65. COMPLETION_FILE="$HOME/${PROJECT_NAME}-completed.txt"
  66. SELECTED_USERNAME=
  67. ADMIN_USER=
  68. UPGRADE_SCRIPT_NAME="${PROJECT_NAME}-upgrade"
  69. UPDATE_DATE_SCRIPT=/usr/bin/updatedate
  70. # Minimum number of characters in a password
  71. MINIMUM_PASSWORD_LENGTH=$(grep 'MINIMUM_PASSWORD_LENGTH=' "/usr/share/${PROJECT_NAME}/utils/${PROJECT_NAME}-utils-passwords" | head -n 1 | awk -F '=' '{print $2}')
  72. # Mumble
  73. MUMBLE_PORT=64738
  74. MUMBLE_ONION_PORT=8095
  75. SSH_PORT=2222
  76. # outgoing SMTP proxy
  77. SMTP_PROXY_ENABLE=$'no'
  78. SMTP_PROXY_PROTOCOL='smtps'
  79. SMTP_PROXY_SERVER='mail.myispdomain'
  80. SMTP_PROXY_PORT=465
  81. SMTP_PROXY_USERNAME=''
  82. SMTP_PROXY_PASSWORD=''
  83. WIFI_INTERFACE=wlan0
  84. WIFI_SSID=
  85. WIFI_TYPE='wpa2-psk'
  86. WIFI_PASSPHRASE=
  87. WIFI_HOTSPOT='no'
  88. WIFI_NETWORKS_FILE="$HOME/${PROJECT_NAME}-wifi.cfg"
  89. USB_DRIVE=sdb
  90. # get default USB from config file
  91. CONFIGURATION_FILE="$HOME/${PROJECT_NAME}.cfg"
  92. read_config_param WIFI_HOTSPOT
  93. read_config_param WIFI_INTERFACE
  94. read_config_param WIFI_TYPE
  95. read_config_param WIFI_SSID
  96. read_config_param WIFI_PASSPHRASE
  97. read_config_param SSH_PORT
  98. read_config_param SMTP_PROXY_ENABLE
  99. read_config_param SMTP_PROXY_PROTOCOL
  100. read_config_param SMTP_PROXY_SERVER
  101. read_config_param SMTP_PROXY_PORT
  102. read_config_param SMTP_PROXY_USERNAME
  103. read_config_param SMTP_PROXY_PASSWORD
  104. read_config_param USB_DRIVE
  105. read_config_param MY_USERNAME
  106. read_config_param ONION_ONLY
  107. if [[ $USB_DRIVE == *"dev"* ]]; then
  108. USB_DRIVE=$(echo ${USB_DRIVE} | awk -F '/' '{print $3}' | sed 's|1||g' | sed 's|2||g')
  109. fi
  110. function any_key {
  111. echo ''
  112. # shellcheck disable=SC2034
  113. read -n1 -rsp $"Press any key to continue..." key
  114. }
  115. function reset_password_tries {
  116. passwords_select_user
  117. if [ ! "$SELECTED_USERNAME" ]; then
  118. return
  119. fi
  120. pam_tally --user "$SELECTED_USERNAME" --reset
  121. dialog --title $"Reset password tries" \
  122. --msgbox $"Password tries have been reset for $SELECTED_USERNAME" 6 60
  123. }
  124. function check_for_updates {
  125. if [ ! -f "/etc/cron.weekly/$UPGRADE_SCRIPT_NAME" ]; then
  126. dialog --title $"Check for updates" \
  127. --msgbox $"Upgrade script was not found" 6 40
  128. return
  129. fi
  130. clear
  131. /etc/cron.weekly/$UPGRADE_SCRIPT_NAME
  132. any_key
  133. }
  134. function add_user {
  135. data=$(mktemp 2>/dev/null)
  136. dialog --backtitle $"Freedombone Control Panel" \
  137. --title $"Add new user" \
  138. --form "\\n" 8 60 3 \
  139. $"Username:" 1 1 "" 1 28 16 15 \
  140. $"ssh public key (optional):" 2 1 "" 2 28 40 10000 \
  141. 2> "$data"
  142. sel=$?
  143. case $sel in
  144. 1) rm -f "$data"
  145. return;;
  146. 255) rm -f "$data"
  147. return;;
  148. esac
  149. new_user_username=$(sed -n 1p < "$data")
  150. new_user_ssh_public_key=$(sed -n 2p < "$data")
  151. rm -f "$data"
  152. if [ ${#new_user_username} -lt 2 ]; then
  153. dialog --title $"New username" \
  154. --msgbox $"No username was given" 6 40
  155. return
  156. fi
  157. if [[ "$new_user_username" == *" "* ]]; then
  158. dialog --title $"Invalid username" \
  159. --msgbox $"The username should not contain any spaces" 6 40
  160. return
  161. fi
  162. if [ ${#new_user_ssh_public_key} -lt 20 ]; then
  163. clear
  164. "${PROJECT_NAME}-adduser" "$new_user_username"
  165. any_key
  166. else
  167. if [[ "$new_user_ssh_public_key" == "ssh-"* ]]; then
  168. clear
  169. "${PROJECT_NAME}-adduser" "$new_user_username" "$new_user_ssh_public_key"
  170. any_key
  171. else
  172. dialog --title $"ssh public key" \
  173. --msgbox $"This does not look like an ssh public key" 6 40
  174. fi
  175. fi
  176. }
  177. function pad_string {
  178. echo -n -e "$1" | sed -e :a -e 's/^.\{1,25\}$/& /;ta'
  179. }
  180. function show_tor_bridges {
  181. if ! grep -q "#BridgeRelay" /etc/tor/torrc; then
  182. if grep -q "BridgeRelay 1" /etc/tor/torrc; then
  183. read_config_param 'TOR_BRIDGE_PORT'
  184. read_config_param 'TOR_BRIDGE_NICKNAME'
  185. if [ ${#TOR_BRIDGE_NICKNAME} -gt 0 ]; then
  186. W+=($"Your Tor Bridge" "$(get_ipv4_address):${TOR_BRIDGE_PORT} ${TOR_BRIDGE_NICKNAME}")
  187. fi
  188. fi
  189. fi
  190. bridges_list=$(grep "Bridge " /etc/tor/torrc | grep -v '##')
  191. if [ ${#bridges_list} -gt 0 ]; then
  192. for i in "${bridges_list[@]}"
  193. do
  194. bridgestr=$(i//Bridge /)
  195. W+=($"Tor Bridge" "$bridgestr")
  196. done
  197. fi
  198. }
  199. function show_domains {
  200. read_config_param "DEFAULT_DOMAIN_NAME"
  201. W=()
  202. W+=("IPv4" "$(get_ipv4_address) / $(get_external_ipv4_address)")
  203. ipv6_address="$(get_ipv6_address)"
  204. if [ ${#ipv6_address} -gt 0 ]; then
  205. W+=("IPv6" "${ipv6_address}")
  206. fi
  207. if grep -q "ssh onion domain" "$COMPLETION_FILE"; then
  208. domain_onion=$(grep 'ssh onion domain' "${COMPLETION_FILE}" | awk -F ':' '{print $2}')
  209. W+=("ssh" "${DEFAULT_DOMAIN_NAME} / ${domain_onion}")
  210. fi
  211. if grep -q "email onion domain" "$COMPLETION_FILE"; then
  212. domain_onion=$(grep 'email onion domain' "${COMPLETION_FILE}" | awk -F ':' '{print $2}')
  213. W+=("Email" "${DEFAULT_DOMAIN_NAME} / ${domain_onion}")
  214. fi
  215. if grep -q "sks onion domain" "$COMPLETION_FILE"; then
  216. read_config_param "KEYSERVER_DOMAIN_NAME"
  217. domain_onion=$(grep 'sks onion domain' "${COMPLETION_FILE}" | awk -F ':' '{print $2}')
  218. W+=("SKS" "${KEYSERVER_DOMAIN_NAME} / ${domain_onion}")
  219. fi
  220. INTRODUCER_FILENAME=/home/tahoelafs/data/private/introducer.furl
  221. if [ -f $INTRODUCER_FILENAME ]; then
  222. W+=("Tahoe-LAFS" "$(cat $INTRODUCER_FILENAME)")
  223. fi
  224. show_tor_bridges
  225. # shellcheck disable=SC2068
  226. for app_name in ${APPS_INSTALLED_NAMES[@]}
  227. do
  228. if ! grep -q "SHOW_ON_ABOUT=1" "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}"; then
  229. continue
  230. fi
  231. # handle the foibles of capitalisation
  232. if ! grep -q "${app_name} domain" "$COMPLETION_FILE"; then
  233. app_name_upper=$(echo "${app_name}" | awk '{print toupper($0)}')
  234. if grep -q "${app_name_upper} domain" "$COMPLETION_FILE"; then
  235. app_name=${app_name_upper}
  236. else
  237. app_name_first_upper="$(tr '[:lower:]' '[:upper:]' <<< "${app_name:0:1}")${app_name:1}"
  238. if grep -q "${app_name_first_upper} domain" "$COMPLETION_FILE"; then
  239. app_name=${app_name_first_upper}
  240. fi
  241. fi
  242. fi
  243. if [ ${#app_name} -gt 0 ]; then
  244. icann_address=$(get_app_icann_address "$app_name")
  245. if grep -q "SHOW_ICANN_ADDRESS_ON_ABOUT=0" "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}"; then
  246. icann_address='-'
  247. fi
  248. if [[ "$ONION_ONLY" != 'no' ]]; then
  249. if [[ "${icann_address}" != "${LOCAL_NAME}.local" ]]; then
  250. icann_address='-'
  251. fi
  252. fi
  253. onion_address=$(get_app_onion_address "$app_name")
  254. if [ ${#onion_address} -eq 0 ]; then
  255. onion_address="-"
  256. fi
  257. if [[ "${icann_address}" != '-' ]]; then
  258. if [[ "${onion_address}" != '-' ]]; then
  259. W+=("${app_name}" "${icann_address} / ${onion_address}")
  260. else
  261. W+=("${app_name}" "${icann_address}")
  262. fi
  263. else
  264. W+=("${app_name}" "${onion_address}")
  265. fi
  266. if grep -q "mobile${app_name} onion domain" "$COMPLETION_FILE"; then
  267. onion_address=$(get_app_onion_address "${app_name}" "mobile")
  268. if [[ "${icann_address}" != '-' ]]; then
  269. W+=("${app_name} (mobile)" "${icann_address} / ${onion_address}")
  270. else
  271. W+=("${app_name} (mobile)" "${onion_address}")
  272. fi
  273. fi
  274. fi
  275. done
  276. if grep -q "rss reader domain" "$COMPLETION_FILE"; then
  277. if [ -d /var/lib/tor/hidden_service_ttrss ]; then
  278. domain_onion=$(cat /var/lib/tor/hidden_service_ttrss/hostname)
  279. W+=("RSS Reader" "${domain_onion}")
  280. fi
  281. if [ -d /var/lib/tor/hidden_service_mobilerss ]; then
  282. domain_onion=$(cat /var/lib/tor/hidden_service_mobilerss/hostname)
  283. W+=("RSS mobile" "${domain_onion}")
  284. fi
  285. fi
  286. width=$(tput cols)
  287. height=$(tput lines)
  288. # shellcheck disable=SC2068
  289. dialog --backtitle $"Freedombone Control Panel" --title $"Domains" --menu $"Use Shift+cursors to select and copy onion addresses" $((height-4)) $((width-4)) $((height-4)) "${W[@]}" 3>&2 2>&1 1>&3
  290. }
  291. function show_users {
  292. echo 'Users'
  293. echo '====='
  294. echo ''
  295. echo -n -e "$(pad_string 'Name')"
  296. echo -n -e "$(pad_string 'Data')"
  297. echo ''
  298. echo '----------------------------------'
  299. for d in /home/*/ ; do
  300. USRNAME=$(echo "$d" | awk -F '/' '{print $3}')
  301. if [[ $(is_valid_user "$USRNAME") == "1" ]]; then
  302. echo -n -e "$(pad_string "${USRNAME}")"
  303. # size of the home directory
  304. du -s -h "/home/${USRNAME}" | awk -F ' ' '{print $1}'
  305. fi
  306. done
  307. echo ''
  308. }
  309. function show_tahoelafs {
  310. if [ ! -f /home/tahoelafs/storage/private/storage.furl ]; then
  311. return
  312. fi
  313. echo 'Tahoe-LAFS Storage Node'
  314. echo '======================='
  315. echo ''
  316. echo "Hostname: $(get_tahoelafs_storage_hostname)"
  317. echo "Public key: $(get_tahoelafs_public_key)"
  318. echo "Nickname: $(get_tahoelafs_nick)"
  319. echo "FURL: $(get_tahoelafs_furl)"
  320. echo ''
  321. }
  322. function show_ip_addresses {
  323. echo $'IP/DNS addresses'
  324. echo '================'
  325. echo ''
  326. echo -n "IPv4: $(get_ipv4_address)/$(get_external_ipv4_address)"
  327. ipv6_address="$(get_ipv6_address)"
  328. if [ ${#ipv6_address} -gt 0 ]; then
  329. echo " IPv6: ${ipv6_address}"
  330. fi
  331. echo ''
  332. echo ''
  333. }
  334. function show_ssh_public_key {
  335. echo $'SSH Public Keys'
  336. echo '==============='
  337. echo ''
  338. get_ssh_server_key
  339. echo ''
  340. echo ''
  341. }
  342. function show_about {
  343. detect_apps
  344. get_apps_installed_names
  345. #clear
  346. #echo "==== ${PROJECT_NAME} version ${VERSION} ($DEBIAN_VERSION) ===="
  347. #echo ''
  348. #show_ip_addresses
  349. #show_ssh_public_key
  350. show_domains
  351. #show_users
  352. #any_key
  353. }
  354. function select_user {
  355. SELECTED_USERNAME=
  356. # shellcheck disable=SC2207
  357. users_array=($(ls /home))
  358. delete=(git)
  359. # shellcheck disable=SC2068
  360. for del in ${delete[@]}
  361. do
  362. # shellcheck disable=SC2206
  363. users_array=(${users_array[@]/$del})
  364. done
  365. i=0
  366. W=()
  367. name=()
  368. # shellcheck disable=SC2068
  369. for u in ${users_array[@]}
  370. do
  371. if [[ $(is_valid_user "$u") == "1" ]]; then
  372. i=$((i+1))
  373. W+=("$i" "$u")
  374. name+=("$u")
  375. fi
  376. done
  377. if [ $i -eq 1 ]; then
  378. SELECTED_USERNAME="${name[0]}"
  379. else
  380. # shellcheck disable=SC2068
  381. user_index=$(dialog --backtitle $"Freedombone Control Panel" --title $"Select User" --menu $"Select one of the following:" 24 40 17 ${W[@]} 3>&2 2>&1 1>&3)
  382. # shellcheck disable=SC2181
  383. if [ $? -eq 0 ]; then
  384. SELECTED_USERNAME="${name[$((user_index-1))]}"
  385. fi
  386. fi
  387. }
  388. function delete_user {
  389. select_user
  390. if [ ! "$SELECTED_USERNAME" ]; then
  391. return
  392. fi
  393. if grep -Fxq "Admin user:$SELECTED_USERNAME" "$COMPLETION_FILE"; then
  394. dialog --title $"Administrator user" \
  395. --msgbox $"You can't delete the administrator user" 6 40
  396. return
  397. fi
  398. clear
  399. "${PROJECT_NAME}-rmuser" "$SELECTED_USERNAME"
  400. any_key
  401. }
  402. function configure_remote_backups {
  403. if ! grep -Fxq "Admin user:$ADMIN_USER" "$COMPLETION_FILE"; then
  404. dialog --title $"Administrator user" \
  405. --msgbox $"No Administrator user found. Check $COMPLETION_FILE" 6 40
  406. return
  407. fi
  408. if [ ${#ADMIN_USER} -lt 2 ]; then
  409. dialog --title $"Administrator user" \
  410. --msgbox $"Username not found" 6 40
  411. return
  412. fi
  413. if [ ! -d "/home/$ADMIN_USER" ]; then
  414. dialog --title $"Administrator user" \
  415. --msgbox $"Home directory not found" 6 40
  416. return
  417. fi
  418. if ! "${PROJECT_NAME}-remote" -u "$ADMIN_USER"; then
  419. any_key
  420. fi
  421. }
  422. function change_password {
  423. select_user
  424. if [ ! "$SELECTED_USERNAME" ]; then
  425. return
  426. fi
  427. dialog --title $"Change password" \
  428. --passwordbox $"New password for user $SELECTED_USERNAME" 8 40 2> "$data"
  429. newpassword=$(<"$data")
  430. rm -f "$data"
  431. if [ "${#newpassword}" -lt "${MINIMUM_PASSWORD_LENGTH}" ]; then
  432. dialog --title $"Change password" \
  433. --msgbox $"The password should be ${MINIMUM_PASSWORD_LENGTH} or more characters" 6 40
  434. return
  435. fi
  436. echo "$SELECTED_USERNAME:$newpassword"|chpasswd
  437. dialog --title $"Change password" \
  438. --msgbox $"Password for $SELECTED_USERNAME was changed" 6 40
  439. }
  440. function change_ssh_public_key {
  441. select_user
  442. if [ ! "$SELECTED_USERNAME" ]; then
  443. return
  444. fi
  445. if grep -Fxq "Admin user:$SELECTED_USERNAME" "$COMPLETION_FILE"; then
  446. dialog --title $"Change ssh public key" \
  447. --backtitle $"Freedombone Control Panel" \
  448. --defaultno \
  449. --yesno $"\\nThis is the administrator user.\\n\\nAre you sure you want to change the ssh public key for the administrator?" 10 60
  450. sel=$?
  451. case $sel in
  452. 1) return;;
  453. 255) return;;
  454. esac
  455. fi
  456. data=$(mktemp 2>/dev/null)
  457. dialog --title $"Change ssh public key for $SELECTED_USERNAME" \
  458. --backtitle $"Freedombone Control Panel" \
  459. --inputbox $"Paste the ssh public key below" 8 60 2>"$data"
  460. sel=$?
  461. case $sel in
  462. 0)
  463. SSH_PUBLIC_KEY=$(<"$data")
  464. if [ "$SSH_PUBLIC_KEY" ]; then
  465. if [ ${#SSH_PUBLIC_KEY} -gt 5 ]; then
  466. if [ -f "$SSH_PUBLIC_KEY" ]; then
  467. if [ ! -d "/home/$SELECTED_USERNAME/.ssh" ]; then
  468. mkdir "/home/$SELECTED_USERNAME/.ssh"
  469. fi
  470. cp "$SSH_PUBLIC_KEY" \
  471. "/home/$SELECTED_USERNAME/.ssh/authorized_keys"
  472. chown -R "$SELECTED_USERNAME":"$SELECTED_USERNAME" \
  473. "/home/$SELECTED_USERNAME/.ssh"
  474. dialog --title $"Change ssh public key" \
  475. --msgbox $"ssh public key was installed" 6 40
  476. else
  477. if [[ "$SSH_PUBLIC_KEY" == "ssh-"* ]]; then
  478. if [ ! -d "/home/$SELECTED_USERNAME/.ssh" ]; then
  479. mkdir "/home/$SELECTED_USERNAME/.ssh"
  480. fi
  481. echo "$SSH_PUBLIC_KEY" > \
  482. "/home/$SELECTED_USERNAME/.ssh/authorized_keys"
  483. chown -R "$SELECTED_USERNAME":"$SELECTED_USERNAME" \
  484. "/home/$SELECTED_USERNAME/.ssh"
  485. dialog --title $"Change ssh public key" \
  486. --msgbox $"ssh public key was installed" 6 40
  487. fi
  488. fi
  489. fi
  490. fi
  491. ;;
  492. esac
  493. rm -f "$data"
  494. }
  495. function remove_user_from_mailing_list {
  496. select_user
  497. if [ ! "$SELECTED_USERNAME" ]; then
  498. return
  499. fi
  500. USER_MAILING_LISTS=$(grep '\[' "/home/$SELECTED_USERNAME/.procmailrc" | grep '\]' | awk -F '\[' '{print $2}' | awk -F '\\' '{print $1}')
  501. i=0
  502. W=()
  503. list_name=()
  504. while read -r listname; do
  505. i=$((i+1))
  506. W+=("$i" "$listname")
  507. list_name+=("$listname")
  508. echo "$listname"
  509. done <<< "$USER_MAILING_LISTS"
  510. i=$((i+1))
  511. W+=("$i" $"Exit back to user mainenance")
  512. # shellcheck disable=SC2068
  513. list_selected=$(dialog --default-item "$i" --backtitle $"Freedombone Control Panel" --title $"Remove a mailing list for $SELECTED_USERNAME" --menu $"Select one of the following:" 24 50 17 ${W[@]} 3>&2 2>&1 1>&3)
  514. # shellcheck disable=SC2181
  515. if [ $? -eq 0 ]; then # Exit with OK
  516. if [ "${list_selected}" -ne "${i}" ]; then
  517. remove_list_name="${list_name[$((list_selected-1))]}"
  518. # find the line number where the list is defined
  519. line_number=0
  520. i=0
  521. while read -r line
  522. do
  523. if [[ "$line" == *"\\[${remove_list_name}\\]"* ]]; then
  524. line_number=${i}
  525. fi
  526. i=$((i+1))
  527. done < "/home/$SELECTED_USERNAME/.procmailrc"
  528. if [ ${line_number} -eq 0 ]; then
  529. # no match was found
  530. return
  531. fi
  532. # recreate the file
  533. if [ -f "/home/${SELECTED_USERNAME}/.procmailrc_new" ]; then
  534. rm "/home/${SELECTED_USERNAME}/.procmailrc_new"
  535. fi
  536. i=0
  537. clip=0
  538. while read -r line
  539. do
  540. i=$((i+1))
  541. if [ ${i} -gt $((line_number-1)) ]; then
  542. if [ ${clip} -eq 0 ]; then
  543. clip=1
  544. fi
  545. if [ ${clip} -eq 1 ]; then
  546. if [ ${i} -lt $((line_number+2)) ]; then
  547. continue
  548. else
  549. if [ ${#line} -lt 1 ]; then
  550. clip=2
  551. continue
  552. fi
  553. if [[ "$line" == ":"* || "$line" == "#"* ]]; then
  554. clip=2
  555. else
  556. continue
  557. fi
  558. fi
  559. fi
  560. fi
  561. echo "$line" >> "/home/${SELECTED_USERNAME}/.procmailrc_new"
  562. if [[ "$line" == *"\\[${remove_list_name}\\]"* ]]; then
  563. line_number=${i}
  564. fi
  565. done < "/home/$SELECTED_USERNAME/.procmailrc"
  566. cp "/home/${SELECTED_USERNAME}/.procmailrc_new" "/home/${SELECTED_USERNAME}/.procmailrc"
  567. rm "/home/${SELECTED_USERNAME}/.procmailrc_new"
  568. chown "${SELECTED_USERNAME}":"${SELECTED_USERNAME}" "/home/${SELECTED_USERNAME}/.procmailrc"
  569. dialog --title $"Remove user from mailing list" \
  570. --msgbox $"${SELECTED_USERNAME} has been removed from ${remove_list_name}" 6 50
  571. fi
  572. fi
  573. }
  574. function add_to_mailing_list {
  575. select_user
  576. if [ ! "$SELECTED_USERNAME" ]; then
  577. return
  578. fi
  579. data=$(mktemp 2>/dev/null)
  580. dialog --backtitle $"Freedombone Control Panel" \
  581. --title $"Subscribe $SELECTED_USERNAME to a mailing list" \
  582. --form $"You can either enter a subject or an email address\\n" 11 68 4 \
  583. $"List folder name:" 1 1 "" 1 35 26 25 \
  584. $"Name between [] on subject line:" 2 1 "" 2 35 26 25 \
  585. $"List email address:" 3 1 "" 3 35 26 25 \
  586. $"Public:" 4 1 $"yes" 4 35 4 25 \
  587. 2> "$data"
  588. sel=$?
  589. case $sel in
  590. 1) rm -f "$data"
  591. return;;
  592. 255) rm -f "$data"
  593. return;;
  594. esac
  595. LIST_NAME=$(sed -n 1p < "$data")
  596. LIST_SUBJECT=$(sed -n 2p < "$data")
  597. LIST_EMAIL=$(sed -n 3p < "$data")
  598. LIST_PUBLIC=$(sed -n 4p < "$data")
  599. if [ ${#LIST_PUBLIC} -lt 1 ]; then
  600. LIST_PUBLIC='no'
  601. fi
  602. if [[ $LIST_PUBLIC == $'y' || $LIST_PUBLIC == $'Y' || $LIST_PUBLIC == $'true' || $LIST_PUBLIC == $'True' || $LIST_PUBLIC == $'yes' || $LIST_PUBLIC == $'Yes' || $LIST_PUBLIC == $'YES' ]]; then
  603. LIST_PUBLIC='yes'
  604. else
  605. LIST_PUBLIC='no'
  606. fi
  607. if [ ${#LIST_NAME} -lt 2 ]; then
  608. dialog --title $"Add mailing list" \
  609. --msgbox $"No mailing list name was given" 6 40
  610. rm -f "$data"
  611. return
  612. fi
  613. if [ ${#LIST_SUBJECT} -lt 2 ]; then
  614. if [ ${#LIST_EMAIL} -lt 2 ]; then
  615. dialog --title $"Add mailing list" \
  616. --msgbox $"No mailing list subject or address was given" 6 40
  617. rm -f "$data"
  618. return
  619. fi
  620. fi
  621. if [ ${#LIST_SUBJECT} -gt 1 ]; then
  622. "${PROJECT_NAME}-addlist" -u "$SELECTED_USERNAME" -l "$LIST_NAME" \
  623. -s "$LIST_SUBJECT" --public "$LIST_PUBLIC"
  624. else
  625. if [[ "$LIST_EMAIL" != *"@"* || "$LIST_EMAIL" != *"."* ]]; then
  626. dialog --title $"Add mailing list" \
  627. --msgbox $"Unrecognised email address" 6 40
  628. rm -f "$data"
  629. return
  630. else
  631. "${PROJECT_NAME}-addlist" -u "$SELECTED_USERNAME" -l "$LIST_NAME" \
  632. -e "$LIST_EMAIL" --public "$LIST_PUBLIC"
  633. fi
  634. fi
  635. dialog --title $"Add mailing list" \
  636. --msgbox $"$LIST_NAME list was added" 6 40
  637. rm -f "$data"
  638. }
  639. function email_rule {
  640. select_user
  641. if [ ! "$SELECTED_USERNAME" ]; then
  642. return
  643. fi
  644. data=$(mktemp 2>/dev/null)
  645. dialog --backtitle $"Freedombone Control Panel" \
  646. --title $"Email rule for user $SELECTED_USERNAME" \
  647. --form "\\n" 9 65 4 \
  648. $"When email arrives from address:" 1 1 "" 1 35 24 28 \
  649. $"Move to folder:" 2 1 "" 2 35 24 28 \
  650. $"Public:" 3 1 $"no" 3 35 4 25 \
  651. 2> "$data"
  652. sel=$?
  653. case $sel in
  654. 1) rm -f "$data"
  655. return;;
  656. 255) rm -f "$data"
  657. return;;
  658. esac
  659. RULE_EMAIL=$(sed -n 1p < "$data")
  660. RULE_FOLDER=$(sed -n 2p < "$data")
  661. RULE_PUBLIC=$(sed -n 3p < "$data")
  662. if [ ${#RULE_PUBLIC} -lt 1 ]; then
  663. RULE_PUBLIC='no'
  664. fi
  665. if [[ $RULE_PUBLIC == $'y' || $RULE_PUBLIC == $'Y' || $RULE_PUBLIC == $'true' || $RULE_PUBLIC == $'True' || $RULE_PUBLIC == $'yes' || $RULE_PUBLIC == $'Yes' || $RULE_PUBLIC == $'YES' ]]; then
  666. RULE_PUBLIC='yes'
  667. else
  668. RULE_PUBLIC='no'
  669. fi
  670. if [ ${#RULE_EMAIL} -lt 2 ]; then
  671. dialog --title $"Add email rule" \
  672. --msgbox $"No email address was given" 6 40
  673. rm -f "$data"
  674. return
  675. fi
  676. if [ ${#RULE_FOLDER} -lt 2 ]; then
  677. dialog --title $"Add email rule" \
  678. --msgbox $"No folder name was given" 6 40
  679. rm -f "$data"
  680. return
  681. fi
  682. if [[ "$RULE_EMAIL" != *"@"* || "$RULE_EMAIL" != *"."* ]]; then
  683. dialog --title $"Add email rule" \
  684. --msgbox $"Unrecognised email address" 6 40
  685. rm -f "$data"
  686. return
  687. fi
  688. "${PROJECT_NAME}-addemail" -u "$SELECTED_USERNAME" -e "$RULE_EMAIL" \
  689. -g "$RULE_FOLDER" --public $RULE_PUBLIC
  690. dialog --title $"Add email rule" \
  691. --msgbox $"Email rule for $RULE_EMAIL was added" 6 40
  692. rm -f "$data"
  693. }
  694. function block_unblock_email {
  695. select_user
  696. if [ ! "$SELECTED_USERNAME" ]; then
  697. return
  698. fi
  699. blockstr=$"Block/Unblock email going to"
  700. data=$(mktemp 2>/dev/null)
  701. dialog --backtitle $"Freedombone Control Panel" \
  702. --title "$blockstr $SELECTED_USERNAME" \
  703. --form "\\n" 8 65 3 \
  704. $"When email arrives from address:" 1 1 "" 1 35 24 100 \
  705. $"Block it:" 2 1 "yes" 2 35 4 4 \
  706. 2> "$data"
  707. sel=$?
  708. case $sel in
  709. 1) rm -f "$data"
  710. return;;
  711. 255) rm -f "$data"
  712. return;;
  713. esac
  714. BLOCK_EMAIL=$(sed -n 1p < "$data")
  715. BLOCK=$(sed -n 2p < "$data")
  716. rm -f "$data"
  717. if [ ${#BLOCK_EMAIL} -lt 2 ]; then
  718. dialog --title $"Block/Unblock an email" \
  719. --msgbox $"No email address was given" 6 40
  720. return
  721. fi
  722. if [[ "$BLOCK_EMAIL" != *"@"* || "$BLOCK_EMAIL" != *"."* ]]; then
  723. dialog --title $"Block/Unblock an email" \
  724. --msgbox $"Unrecognised email address" 6 40
  725. return
  726. fi
  727. if [[ $BLOCK == "y"* || $BLOCK == "Y"* ]]; then
  728. "${PROJECT_NAME}-ignore" -u "$SELECTED_USERNAME" -e "$BLOCK_EMAIL"
  729. dialog --title $"Block an email" \
  730. --msgbox "Email from $BLOCK_EMAIL to $SELECTED_USERNAME blocked" 6 75
  731. else
  732. "${PROJECT_NAME}-unignore" -u "$SELECTED_USERNAME" -e "$BLOCK_EMAIL"
  733. dialog --title $"Unblock an email" \
  734. --msgbox "Email from $BLOCK_EMAIL to $SELECTED_USERNAME unblocked" 6 75
  735. fi
  736. }
  737. function block_unblock_subject {
  738. select_user
  739. if [ ! "$SELECTED_USERNAME" ]; then
  740. return
  741. fi
  742. blockstr=$"Block/Unblock email going to"
  743. data=$(mktemp 2>/dev/null)
  744. dialog --backtitle $"Freedombone Control Panel" \
  745. --title "$blockstr $SELECTED_USERNAME" \
  746. --form "\\n" 8 70 3 \
  747. $"When email arrives with subject text:" 1 1 "" 1 40 24 28 \
  748. $"Block it:" 2 1 "yes" 2 40 4 4 \
  749. 2> "$data"
  750. sel=$?
  751. case $sel in
  752. 1) rm -f "$data"
  753. return;;
  754. 255) rm -f "$data"
  755. return;;
  756. esac
  757. BLOCK_SUBJECT=$(sed -n 1p < "$data")
  758. BLOCK=$(sed -n 2p < "$data")
  759. rm -f "$data"
  760. if [ ${#BLOCK_SUBJECT} -lt 2 ]; then
  761. dialog --title $"Block/Unblock an email" \
  762. --msgbox $"No subject was given" 6 40
  763. return
  764. fi
  765. if [[ $BLOCK == "y"* || $BLOCK == "Y"* ]]; then
  766. "${PROJECT_NAME}-ignore" -u "$SELECTED_USERNAME" -t "$BLOCK_SUBJECT"
  767. dialog --title $"Block an email" \
  768. --msgbox "Email with subject $BLOCK_SUBJECT to $SELECTED_USERNAME blocked" 6 40
  769. else
  770. "${PROJECT_NAME}-unignore" -u "$SELECTED_USERNAME" -t "$BLOCK_SUBJECT"
  771. dialog --title $"Unblock an email" \
  772. --msgbox "Email with subject $BLOCK_SUBJECT to $SELECTED_USERNAME unblocked" 6 40
  773. fi
  774. }
  775. function create_keydrive_master {
  776. select_user
  777. if [ ! "$SELECTED_USERNAME" ]; then
  778. return
  779. fi
  780. dialog --title $"USB Master Keydrive" \
  781. --msgbox $"Plug in a LUKS encrypted USB drive" 6 40
  782. clear
  783. detect_usb_drive
  784. "${PROJECT_NAME}-keydrive" -u "$SELECTED_USERNAME" --master 'yes' -d "$USB_DRIVE"
  785. any_key
  786. }
  787. function create_keydrive_fragment {
  788. select_user
  789. if [ ! "$SELECTED_USERNAME" ]; then
  790. return
  791. fi
  792. dialog --title $"USB Fragment Keydrive" \
  793. --msgbox $"Plug in a LUKS encrypted USB drive" 6 40
  794. clear
  795. detect_usb_drive
  796. "${PROJECT_NAME}-keydrive" -u "$SELECTED_USERNAME" -d "$USB_DRIVE"
  797. any_key
  798. }
  799. function backup_data {
  800. dialog --title $"Backup data to USB" \
  801. --msgbox $"Plug in a LUKS encrypted USB drive" 6 40
  802. clear
  803. detect_usb_drive
  804. echo ''
  805. echo $"Detected USB drive $USB_DRIVE"
  806. echo ''
  807. echo $'Enter the passphrase for your LUKS encrypted backup drive:'
  808. "${PROJECT_NAME}-backup-local"
  809. any_key
  810. }
  811. function restore_data_from_storage {
  812. restore_type="$1"
  813. AllStr=$"all"
  814. ExitStr=$"Exit"
  815. RestoreStr=$"Restore apps"
  816. if [[ $restore_type != "local" ]]; then
  817. restore_command="${PROJECT_NAME}-restore-remote $remote_domain_name configuration;;"
  818. else
  819. remote_domain_name="$1"
  820. detect_usb_drive
  821. restore_command="${PROJECT_NAME}-restore-local $USB_DRIVE"
  822. RestoreStr=$"Restore apps from USB drive $USB_DRIVE"
  823. fi
  824. utils_installed=(configfiles
  825. blocklist
  826. mariadb
  827. postgresql
  828. letsencrypt
  829. passwords
  830. mutt
  831. gpg
  832. procmail
  833. spamassassin
  834. readme
  835. ssh
  836. userconfig
  837. userlocal
  838. userfin
  839. certs
  840. personal
  841. email)
  842. detect_apps
  843. while true
  844. do
  845. app_list=()
  846. n=1
  847. applist="$n $AllStr off"
  848. n=$((n+1))
  849. app_list+=("$AllStr")
  850. util_index=0
  851. # shellcheck disable=SC2068
  852. for a in ${utils_installed[@]}
  853. do
  854. applist="$applist $n $a off"
  855. app_name=${utils_installed[util_index]}
  856. n=$((n+1))
  857. util_index=$((util_index+1))
  858. app_list+=("$app_name")
  859. done
  860. app_index=0
  861. # shellcheck disable=SC2068
  862. for a in ${APPS_INSTALLED_NAMES[@]}
  863. do
  864. applist="$applist $n $a off"
  865. n=$((n+1))
  866. app_name=${APPS_INSTALLED_NAMES[app_index]}
  867. app_index=$((app_index+1))
  868. app_list+=("$app_name")
  869. done
  870. applist="$applist $n $ExitStr on"
  871. n=$((n+1))
  872. app_list+=("$ExitStr")
  873. # shellcheck disable=SC2086
  874. choice=$(dialog --stdout --backtitle $"Freedombone" \
  875. --title "$RestoreStr" \
  876. --radiolist $'Choose:' \
  877. 30 50 20 $applist)
  878. # shellcheck disable=SC2181
  879. if [ $? -ne 0 ]; then
  880. break
  881. fi
  882. app_index=$((choice-1))
  883. app_name=${app_list[app_index]}
  884. # exit
  885. if [[ "$app_name" == "$ExitStr" ]]; then
  886. break
  887. fi
  888. clear
  889. # Restore all
  890. if [[ "$app_name" == "$AllStr" ]]; then
  891. $restore_command
  892. retcode="$?"
  893. if [[ "$retcode" != "0" ]]; then
  894. any_key
  895. if [[ "$1" == "local" ]]; then
  896. dialog --title $"Restore all apps from USB" \
  897. --msgbox $"Restore failed with code $retcode" 6 60
  898. else
  899. dialog --title $"Restore all apps from $1" \
  900. --msgbox $"Restore failed with code $retcode" 6 60
  901. fi
  902. break
  903. fi
  904. if [[ "$1" == "local" ]]; then
  905. dialog --title $"Restore all apps from USB" \
  906. --msgbox $"Restore complete" 6 40
  907. else
  908. dialog --title $"Restore all apps from $1" \
  909. --msgbox $"Restore complete" 6 40
  910. fi
  911. break
  912. fi
  913. # Restore an app
  914. $restore_command "${app_name}"
  915. retcode="$?"
  916. if [[ "$retcode" != "0" ]]; then
  917. any_key
  918. dialog --title $"Restore apps from USB" \
  919. --msgbox $"Restore of ${app_name} failed with code $retcode" 6 60
  920. return
  921. fi
  922. # finished
  923. if [[ "$1" == "local" ]]; then
  924. dialog --title $"Restore apps from USB" \
  925. --msgbox $"Restore complete" 6 40
  926. else
  927. dialog --title $"Restore apps from $1" \
  928. --msgbox $"Restore complete" 6 40
  929. fi
  930. done
  931. }
  932. function restore_data {
  933. dialog --title $"Restore data from USB" \
  934. --msgbox $"Plug in your backup USB drive" 6 40
  935. clear
  936. echo ' '
  937. echo $'Enter the passphrase for your LUKS encrypted backup drive:'
  938. restore_data_from_storage local
  939. }
  940. function restore_data_remote {
  941. if [ ! $ADMIN_USER ]; then
  942. dialog --title $"Restore data from remote server" \
  943. --msgbox $"Unknown admin user" 6 40
  944. return
  945. fi
  946. data=$(mktemp 2>/dev/null)
  947. dialog --title $"Restore from remote server" \
  948. --backtitle $"Freedombone Control Panel" \
  949. --inputbox $"Enter the domain name of the server from which you wish to restore" 8 60 2>"$data"
  950. sel=$?
  951. case $sel in
  952. 0)
  953. friend_server_domain_name=$(<"$data")
  954. if [ ${#friend_server_domain_name} -lt 2 ]; then
  955. rm -f "$data"
  956. return
  957. fi
  958. if [[ $friend_server_domain_name != *"."* ]]; then
  959. dialog --title $"Remote server domain name" \
  960. --msgbox $"Invalid domain name" 6 40
  961. rm -f "$data"
  962. return
  963. fi
  964. restore_data_from_storage "$friend_server_domain_name"
  965. ;;
  966. esac
  967. rm -f "$data"
  968. }
  969. function logging_on_off {
  970. logging="no"
  971. dialog --title $"Logging" \
  972. --backtitle $"Freedombone Control Panel" \
  973. --defaultno \
  974. --yesno $"\\nDo you want to turn logging on?" 7 60
  975. sel=$?
  976. case $sel in
  977. 0) logging="yes";;
  978. 255) return;;
  979. esac
  980. clear
  981. echo ''
  982. echo $'This may take a few seconds. Please wait...'
  983. if [[ $logging == "no" ]]; then
  984. ${PROJECT_NAME}-logging off
  985. else
  986. ${PROJECT_NAME}-logging on
  987. fi
  988. }
  989. function restore_gpg_key {
  990. select_user
  991. if [ ! "$SELECTED_USERNAME" ]; then
  992. return
  993. fi
  994. restorestr=$"Restore GPG key for user"
  995. dialog --title "$restorestr $SELECTED_USERNAME" \
  996. --msgbox $"Plug in your USB keydrive" 6 40
  997. clear
  998. "${PROJECT_NAME}-recoverkey" -u "$SELECTED_USERNAME"
  999. any_key
  1000. }
  1001. function security_settings {
  1002. "${PROJECT_NAME}-sec"
  1003. }
  1004. function format_drive {
  1005. detect_usb_drive
  1006. dialog --title $"Format USB drive $USB_DRIVE" \
  1007. --backtitle $"Freedombone Control Panel" \
  1008. --defaultno \
  1009. --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
  1010. sel=$?
  1011. case $sel in
  1012. 1) return;;
  1013. 255) return;;
  1014. esac
  1015. clear
  1016. echo ''
  1017. echo $"Formatting drive $USB_DRIVE. ALL CONTENTS WILL BE LOST."
  1018. echo ''
  1019. "${PROJECT_NAME}-format" "$USB_DRIVE"
  1020. any_key
  1021. }
  1022. function remove_backups {
  1023. detect_usb_drive
  1024. # shellcheck disable=SC2154
  1025. dialog --title $"Remove backups from a USB drive $USB_DRIVE" \
  1026. --backtitle $"Freedombone Control Panel" \
  1027. --defaultno \
  1028. --yesno $"\\nPlease confirm that you wish to remove backups from this drive\\n\\n ${drive}\\n\\nYou will not be able to recover them afterwards." 12 60
  1029. sel=$?
  1030. case $sel in
  1031. 1) return;;
  1032. 255) return;;
  1033. esac
  1034. clear
  1035. "${PROJECT_NAME}-backup-local" "$USB_DRIVE" remove
  1036. any_key
  1037. }
  1038. function shut_down_system {
  1039. dialog --title $"Power off the system" \
  1040. --backtitle $"Freedombone Control Panel" \
  1041. --defaultno \
  1042. --yesno $"\\nPlease confirm that you wish to power off the system.\\n\\nWARNING: to power on again you will need to have physical access to the hardware." 10 60
  1043. sel=$?
  1044. case $sel in
  1045. 1) return;;
  1046. 255) return;;
  1047. esac
  1048. systemctl poweroff
  1049. }
  1050. function restart_system {
  1051. dialog --title $"Restart the system" \
  1052. --backtitle $"Freedombone Control Panel" \
  1053. --defaultno \
  1054. --yesno $"\\nPlease confirm that you wish to restart the system.\\n\\nWARNING: If you are using full disk encryption then you will need physical access to the hardware to type in the password" 10 60
  1055. sel=$?
  1056. case $sel in
  1057. 1) return;;
  1058. 255) return;;
  1059. esac
  1060. systemctl reboot -i
  1061. }
  1062. function change_system_name {
  1063. data=$(mktemp 2>/dev/null)
  1064. dialog --title $"Change the name of this system" \
  1065. --backtitle $"Freedombone Control Panel" \
  1066. --inputbox $'Enter a new name for this system on your local network\\n\\nIt will appear as newname.local' 10 60 2>"$data"
  1067. sel=$?
  1068. case $sel in
  1069. 0) NEW_SYSTEM_NAME=$(<"$data")
  1070. if [ "$NEW_SYSTEM_NAME" ]; then
  1071. if [ ${#NEW_SYSTEM_NAME} -gt 1 ]; then
  1072. sed -i "s|host-name=.*|host-name=$NEW_SYSTEM_NAME|g" /etc/avahi/avahi-daemon.conf
  1073. systemctl restart avahi-daemon
  1074. if grep -q "host-name=$NEW_SYSTEM_NAME" /etc/avahi/avahi-daemon.conf; then
  1075. dialog --title $"New local network name" \
  1076. --msgbox $"The name of this system on your local network was changed successfully" 6 70
  1077. fi
  1078. fi
  1079. fi
  1080. ;;
  1081. esac
  1082. rm -f "$data"
  1083. }
  1084. function set_dynamic_IP {
  1085. revert_to_dynamic=
  1086. dialog --title $"Return to using a dynamic IP address" \
  1087. --backtitle $"Freedombone Control Panel" \
  1088. --yesno $"\\nDo you wish to go back to using a dynamic IP address?" 8 60
  1089. sel=$?
  1090. case $sel in
  1091. 0) revert_to_dynamic=1
  1092. ;;
  1093. 1) return;;
  1094. esac
  1095. if [ $revert_to_dynamic ]; then
  1096. wifi_original_network_settings
  1097. clear
  1098. echo ''
  1099. echo $'Changing to a dynamic IP address.'
  1100. echo ''
  1101. echo $"System is rebooting. You may need to close this terminal and log in from a new one."
  1102. systemctl reboot -i
  1103. fi
  1104. }
  1105. function set_static_IP {
  1106. IPv4_address=$(get_ipv4_address)
  1107. IPv4_address_base=$(echo "$IPv4_address" | awk -F '.' '{print $1"."$2"."$3}')
  1108. STATIC_IP="${IPv4_address_base}.60"
  1109. STATIC_GATEWAY="${IPv4_address_base}.1"
  1110. NEW_STATIC_IP=
  1111. NEW_STATIC_GATEWAY=
  1112. if [ -f /etc/network/interfaces.d/static ]; then
  1113. STATIC_IP=$(grep "address " /etc/network/interfaces.d/static | head -n 1 | awk -F ' ' '{print $2}')
  1114. STATIC_GATEWAY=$(grep "gateway " /etc/network/interfaces.d/static | head -n 1 | awk -F ' ' '{print $2}')
  1115. fi
  1116. # get the IP for the box
  1117. data=$(mktemp 2>/dev/null)
  1118. dialog --title $"Set a static local IP address" \
  1119. --backtitle $"Freedombone Control Panel" \
  1120. --inputbox $"In order to forward incoming internet traffic to this system most internet routers need to know a static local IP address to send the data to.\\n\\n
  1121. Enter a static local IP address for this system.\\n\\nIt will typically be ${IPv4_address_base}.x\\n\\nIf you leave this field blank then the system will revert to using a dynamic IP address." 18 60 "$STATIC_IP" 2>"$data"
  1122. sel=$?
  1123. case $sel in
  1124. 0) NEW_STATIC_IP=$(<"$data")
  1125. if [[ "$NEW_STATIC_IP" != *"."* ]]; then
  1126. set_dynamic_IP
  1127. rm -f "$data"
  1128. return
  1129. fi
  1130. ;;
  1131. 1) rm -f "$data"
  1132. return;;
  1133. esac
  1134. rm -f "$data"
  1135. # get the gateway
  1136. data=$(mktemp 2>/dev/null)
  1137. dialog --title $"Set the IP address of your internet router/modem" \
  1138. --backtitle $"Freedombone Control Panel" \
  1139. --inputbox $"Set the local IP address for your internet router or ADSL modem.\\n\\nIt will typically be ${IPv4_address_base}.1, ${IPv4_address_base}.254, or similar" 12 60 "$STATIC_GATEWAY" 2>"$data"
  1140. sel=$?
  1141. case $sel in
  1142. 0) NEW_STATIC_GATEWAY=$(<"$data")
  1143. if [[ "$NEW_STATIC_GATEWAY" != *"."* ]]; then
  1144. rm -f "$data"
  1145. return
  1146. fi
  1147. ;;
  1148. 1) rm -f "$data"
  1149. return;;
  1150. esac
  1151. if [[ "$NEW_STATIC_GATEWAY" == *"."* && "$NEW_STATIC_IP" == *"."* ]]; then
  1152. ip_addresses_have_changed=1
  1153. if [ -f /etc/network/interfaces.d/static ]; then
  1154. ip_addresses_have_changed=
  1155. if ! grep -q "address ${NEW_STATIC_IP}" /etc/network/interfaces.d/static; then
  1156. ip_addresses_have_changed=1
  1157. fi
  1158. if ! grep -q "gateway ${NEW_STATIC_GATEWAY}" /etc/network/interfaces.d/static; then
  1159. ip_addresses_have_changed=1
  1160. fi
  1161. fi
  1162. if [ $ip_addresses_have_changed ]; then
  1163. write_config_param "NETWORK_IS_STATIC" "1"
  1164. write_config_param "LOCAL_NETWORK_STATIC_IP_ADDRESS" "$NEW_STATIC_IP"
  1165. write_config_param "ROUTER_IP_ADDRESS" "$NEW_STATIC_GATEWAY"
  1166. email_change_relay "$NEW_STATIC_IP"
  1167. static_wifi_address=
  1168. if [[ $(config_param_exists "WIFI_INTERFACE") == "1" ]]; then
  1169. dialog --title $"Static local IP address" \
  1170. --backtitle $"Freedombone Control Panel" \
  1171. --yesno $"\\nSet a static address for the wifi adapter?\\n\\nIf you select 'no' then wired ethernet will be used." 10 60
  1172. sel=$?
  1173. case $sel in
  1174. 0) static_wifi_address=1
  1175. write_config_param "NETWORK_IS_STATIC" "1"
  1176. ;;
  1177. esac
  1178. fi
  1179. echo '# This file describes the network interfaces available on your system' > /etc/network/interfaces
  1180. echo '# and how to activate them. For more information, see interfaces(5).' >> /etc/network/interfaces
  1181. echo 'source /etc/network/interfaces.d/*' >> /etc/network/interfaces
  1182. if [ ! $static_wifi_address ]; then
  1183. # wired network
  1184. remove_wifi_startup_script
  1185. { echo 'auto eth0';
  1186. echo 'iface eth0 inet static';
  1187. echo " address ${NEW_STATIC_IP}";
  1188. echo ' netmask 255.255.255.0';
  1189. echo " gateway ${NEW_STATIC_GATEWAY}"; } >> /etc/network/interfaces.d/static
  1190. else
  1191. # wifi network
  1192. wifi_settings
  1193. fi
  1194. clear
  1195. echo ''
  1196. echo $'Restarting the network daemon.'
  1197. echo ''
  1198. echo $'If you logged in using the previous IP address then you may need to close this terminal and log in again on the new one.'
  1199. function_check pihole_change_ipv4
  1200. pihole_change_ipv4 "${NEW_STATIC_IP}"
  1201. dialog --title $"Static local IP address" \
  1202. --backtitle $"Freedombone Control Panel" \
  1203. --yesno $"\\nFor the change to take effect your system will now need to reboot. Do this now?" 8 60
  1204. sel=$?
  1205. case $sel in
  1206. 0) systemctl reboot -i;;
  1207. esac
  1208. fi
  1209. fi
  1210. rm -f "$data"
  1211. }
  1212. function wifi_settings {
  1213. if [ -f /etc/hostapd/hostapd.conf ]; then
  1214. return
  1215. fi
  1216. TEMP_WIFI_NETWORKS_FILE=~/.temp-${PROJECT_NAME}-wifi.cfg
  1217. ${PROJECT_NAME}-wifi --networksinteractive $TEMP_WIFI_NETWORKS_FILE
  1218. if [ -f $TEMP_WIFI_NETWORKS_FILE ]; then
  1219. cp "$TEMP_WIFI_NETWORKS_FILE" "$WIFI_NETWORKS_FILE"
  1220. rm $TEMP_WIFI_NETWORKS_FILE
  1221. "${PROJECT_NAME}-wifi" --networks "$WIFI_NETWORKS_FILE"
  1222. create_wifi_startup_script
  1223. if [[ $(wifi_is_running) == "1" ]]; then
  1224. dialog --title $"Wifi Settings" \
  1225. --msgbox $"Wifi settings were changed." 6 60
  1226. else
  1227. dialog --title $"Wifi Settings" \
  1228. --msgbox $"Wifi settings were changed. You will need to restart the system with ethernet cable removed for the changes to take effect." 7 60
  1229. fi
  1230. else
  1231. remove_wifi_startup_script
  1232. fi
  1233. }
  1234. function wifi_edit_networks {
  1235. if [ -f /etc/hostapd/hostapd.conf ]; then
  1236. return
  1237. fi
  1238. if [ ! -f "$WIFI_NETWORKS_FILE" ]; then
  1239. { echo $'# Add wifi networks as follows:';
  1240. echo '#';
  1241. echo $'# MySSID';
  1242. echo $'# wpa2-psk';
  1243. echo $'# myWifiPassphrase';
  1244. echo '#';
  1245. echo $'# AnotherSSID';
  1246. echo $'# none';
  1247. echo '#'; } > "$WIFI_NETWORKS_FILE"
  1248. fi
  1249. editor "$WIFI_NETWORKS_FILE"
  1250. "${PROJECT_NAME}-wifi" --networks "$WIFI_NETWORKS_FILE"
  1251. }
  1252. function hotspot_settings {
  1253. data=$(mktemp 2>/dev/null)
  1254. dialog --backtitle $"Freedombone Control Panel" \
  1255. --title $"Hotspot Settings" \
  1256. --form $"" 10 60 4 \
  1257. $"Enabled (yes/no):" 1 1 "$WIFI_HOTSPOT" 1 24 5 5 \
  1258. $"SSID:" 2 1 "$WIFI_SSID" 2 24 256 256 \
  1259. $"Type (wpa2-psk/none):" 3 1 "$WIFI_TYPE" 3 24 10 10 \
  1260. $"Passphrase:" 4 1 "$WIFI_PASSPHRASE" 4 24 256 256 \
  1261. 2> "$data"
  1262. sel=$?
  1263. case $sel in
  1264. 1) rm -f "$data"
  1265. return;;
  1266. 255) rm -f "$data"
  1267. return;;
  1268. esac
  1269. TEMP_WIFI_HOTSPOT=$(sed -n 1p < "$data")
  1270. TEMP_WIFI_SSID=$(sed -n 2p < "$data")
  1271. TEMP_WIFI_TYPE=$(sed -n 3p < "$data")
  1272. TEMP_WIFI_PASSPHRASE=$(sed -n 4p < "$data")
  1273. rm -f "$data"
  1274. if [ ${#TEMP_WIFI_SSID} -lt 2 ]; then
  1275. return
  1276. fi
  1277. if [ ${#TEMP_WIFI_TYPE} -lt 2 ]; then
  1278. return
  1279. fi
  1280. WIFI_EXTRA=''
  1281. if [[ $TEMP_WIFI_HOTSPOT == $'yes' || $TEMP_WIFI_HOTSPOT == $'y' || $TEMP_WIFI_HOTSPOT == $'on' ]]; then
  1282. TEMP_WIFI_HOTSPOT='yes'
  1283. else
  1284. TEMP_WIFI_HOTSPOT='no'
  1285. if [ -f "$WIFI_NETWORKS_FILE" ]; then
  1286. WIFI_EXTRA="--networks $WIFI_NETWORKS_FILE"
  1287. fi
  1288. fi
  1289. if [[ $TEMP_WIFI_TYPE != $'none' ]]; then
  1290. if [ ! "$TEMP_WIFI_PASSPHRASE" ]; then
  1291. dialog --title $"Wifi Settings" \
  1292. --msgbox $"No wifi hotspot passphrase was given" 6 40
  1293. return
  1294. fi
  1295. if [ ${#TEMP_WIFI_PASSPHRASE} -lt 2 ]; then
  1296. dialog --title $"Wifi Settings" \
  1297. --msgbox $"Wifi hotspot passphrase was too short" 6 40
  1298. return
  1299. fi
  1300. WIFI_HOTSPOT=$TEMP_WIFI_HOTSPOT
  1301. WIFI_SSID=$TEMP_WIFI_SSID
  1302. WIFI_TYPE=$TEMP_WIFI_TYPE
  1303. WIFI_PASSPHRASE=$TEMP_WIFI_PASSPHRASE
  1304. if ! "${PROJECT_NAME}-wifi" -i "$WIFI_INTERFACE" -s "$WIFI_SSID" -t "$WIFI_TYPE" -p "$WIFI_PASSPHRASE" --hotspot "$WIFI_HOTSPOT" "$WIFI_EXTRA"; then
  1305. echo $"Can't enable wifi hotspot"
  1306. any_key
  1307. fi
  1308. else
  1309. WIFI_HOTSPOT=$TEMP_WIFI_HOTSPOT
  1310. WIFI_SSID=$TEMP_WIFI_SSID
  1311. WIFI_TYPE=$TEMP_WIFI_TYPE
  1312. WIFI_PASSPHRASE=$TEMP_WIFI_PASSPHRASE
  1313. "${PROJECT_NAME}-wifi" -i "$WIFI_INTERFACE" -s "$WIFI_SSID" -t "$WIFI_TYPE" --hotspot "$WIFI_HOTSPOT" "$WIFI_EXTRA"
  1314. fi
  1315. # store any changes
  1316. write_config_param "WIFI_HOTSPOT" "$WIFI_HOTSPOT"
  1317. write_config_param "WIFI_SSID" "$WIFI_SSID"
  1318. write_config_param "WIFI_TYPE" "$WIFI_TYPE"
  1319. write_config_param "WIFI_PASSPHRASE" "$WIFI_PASSPHRASE"
  1320. dialog --title $"Wifi Settings" \
  1321. --msgbox $"Hotspot settings were changed" 6 40
  1322. }
  1323. function reinstall_mariadb {
  1324. dialog --title $"Reinstall MariaDB" \
  1325. --backtitle $"Freedombone Control Panel" \
  1326. --defaultno \
  1327. --yesno $"\\nThis should be a LAST RESORT, if the mysql daemon won't start. You will lose ALL databases and will then need to restore them from backup.\\n\\nAre you sure that you wish to continue?" 12 60
  1328. sel=$?
  1329. case $sel in
  1330. 1) return;;
  1331. 255) return;;
  1332. esac
  1333. clear
  1334. database_reinstall
  1335. dialog --title $"Reinstall MariaDB" \
  1336. --msgbox $"MariaDB has been reinstalled" 6 40
  1337. }
  1338. function email_extra_domains {
  1339. email_hostnames=$(grep "dc_other_hostnames" /etc/exim4/update-exim4.conf.conf | awk -F "'" '{print $2}')
  1340. data=$(mktemp 2>/dev/null)
  1341. dialog --title $"Email Domains" \
  1342. --backtitle $"Freedombone Control Panel" \
  1343. --inputbox $"Enter the list of email domains to use, separated by semicolons" 8 60 "$email_hostnames" 2>"$data"
  1344. sel=$?
  1345. case $sel in
  1346. 0)
  1347. emailhostnames=$(<"$data")
  1348. if [ ${#emailhostnames} -gt 2 ]; then
  1349. if [[ "$email_hostnames" != "$emailhostnames" ]]; then
  1350. if [[ "$emailhostnames" == *"."* ]]; then
  1351. if [[ "$emailhostnames" != *" "* ]]; then
  1352. sed -i "s|dc_other_hostnames=.*|dc_other_hostnames='$emailhostnames'|g" /etc/exim4/update-exim4.conf.conf
  1353. update-exim4.conf
  1354. dpkg-reconfigure --frontend noninteractive exim4-config
  1355. systemctl restart saslauthd
  1356. dialog --title $"Email Domains" \
  1357. --backtitle $"Freedombone Control Panel" \
  1358. --msgbox $"Email domains were changed" 6 50
  1359. else
  1360. dialog --title $"Email Domains not set" \
  1361. --backtitle $"Freedombone Control Panel" \
  1362. --msgbox $"There should be no spaces in the list" 6 50
  1363. fi
  1364. fi
  1365. fi
  1366. fi
  1367. ;;
  1368. esac
  1369. rm -f "$data"
  1370. }
  1371. function email_smtp_proxy {
  1372. MUTTRC_FILE=/home/$ADMIN_USER/.muttrc
  1373. if [ ! -f $MUTTRC_FILE ]; then
  1374. return
  1375. fi
  1376. data=$(mktemp 2>/dev/null)
  1377. dialog --backtitle $"Freedombone Control Panel" \
  1378. --title $"SMTP Proxy for $ADMIN_USER" \
  1379. --form $"You may need to proxy outgoing email via your ISP's mail server. If so enter the details below." 14 75 6 \
  1380. $"Enable proxy:" 1 1 "$SMTP_PROXY_ENABLE" 1 24 5 5 \
  1381. $"Protocol (smtp/smtps):" 2 1 "$SMTP_PROXY_PROTOCOL" 2 24 5 5 \
  1382. $"ISP mail server:" 3 1 "$SMTP_PROXY_SERVER" 3 24 40 10000 \
  1383. $"Port:" 4 1 "$SMTP_PROXY_PORT" 4 24 5 5 \
  1384. $"Username:" 5 1 "$SMTP_PROXY_USERNAME" 5 24 40 10000 \
  1385. $"Password:" 6 1 "$SMTP_PROXY_PASSWORD" 6 24 40 10000 \
  1386. 2> "$data"
  1387. sel=$?
  1388. case $sel in
  1389. 1) rm -f "$data"
  1390. return;;
  1391. 255) rm -f "$data"
  1392. return;;
  1393. esac
  1394. SMTP_PROXY_ENABLE=$(sed -n 1p < "$data")
  1395. SMTP_PROXY_PROTOCOL=$(sed -n 2p < "$data")
  1396. SMTP_PROXY_SERVER=$(sed -n 3p < "$data")
  1397. SMTP_PROXY_PORT=$(sed -n 4p < "$data")
  1398. SMTP_PROXY_USERNAME=$(sed -n 5p < "$data")
  1399. SMTP_PROXY_PASSWORD=$(sed -n 6p < "$data")
  1400. rm -f "$data"
  1401. # change muttrc
  1402. if [ "$SMTP_PROXY_ENABLE" != $'no' ]; then
  1403. if ! grep -q "set smtp_url" "$MUTTRC_FILE"; then
  1404. echo "set smtp_url=\"${SMTP_PROXY_PROTOCOL}://${SMTP_PROXY_USERNAME}:${SMTP_PROXY_PASSWORD}@${SMTP_PROXY_SERVER}:${SMTP_PROXY_PORT}/\"" >> "$MUTTRC_FILE"
  1405. else
  1406. sed -i "s|set smtp_url=.*|set smtp_url=\"${SMTP_PROXY_PROTOCOL}://${SMTP_PROXY_USERNAME}:${SMTP_PROXY_PASSWORD}@${SMTP_PROXY_SERVER}:${SMTP_PROXY_PORT}/\"|g" "$MUTTRC_FILE"
  1407. fi
  1408. sed -i 's|#set smtp_url|set smtp_url|g' "$MUTTRC_FILE"
  1409. else
  1410. if grep -q "set smtp_url" "$MUTTRC_FILE"; then
  1411. sed -i 's|set smtp_url|#set smtp_url|g' "$MUTTRC_FILE"
  1412. fi
  1413. fi
  1414. # save settings within the main configuration file
  1415. write_config_param "SMTP_PROXY_ENABLE" "$SMTP_PROXY_ENABLE"
  1416. write_config_param "SMTP_PROXY_PROTOCOL" "$SMTP_PROXY_PROTOCOL"
  1417. write_config_param "SMTP_PROXY_SERVER" "$SMTP_PROXY_SERVER"
  1418. write_config_param "SMTP_PROXY_PORT" "$SMTP_PROXY_PORT"
  1419. write_config_param "SMTP_PROXY_USERNAME" "$SMTP_PROXY_USERNAME"
  1420. write_config_param "SMTP_PROXY_PASSWORD" "$SMTP_PROXY_PASSWORD"
  1421. }
  1422. function menu_backup_restore {
  1423. while true
  1424. do
  1425. W=(1 $"Backup data to USB drive"
  1426. 2 $"Restore GPG key from USB keydrive"
  1427. 3 $"Restore data from USB drive"
  1428. 4 $"Reinstall mariadb"
  1429. 5 $"Configure remote backups"
  1430. 6 $"Restore from remote backup"
  1431. 7 $"Backup GPG key to USB (master keydrive)"
  1432. 8 $"Backup GPG key to USB (fragment keydrive)"
  1433. 9 $"Format a USB drive (LUKS encrypted)"
  1434. 10 $"Remove backups from a USB drive")
  1435. # shellcheck disable=SC2068
  1436. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Backup and Restore" --menu $"Choose an operation, or ESC for main menu:" 19 70 12 "${W[@]}" 3>&2 2>&1 1>&3)
  1437. if [ ! "$selection" ]; then
  1438. break
  1439. fi
  1440. case $selection in
  1441. 1) backup_data;;
  1442. 2) restore_gpg_key;;
  1443. 3) restore_data;;
  1444. 4) reinstall_mariadb;;
  1445. 5) configure_remote_backups;;
  1446. 6) restore_data_remote;;
  1447. 7) create_keydrive_master;;
  1448. 8) create_keydrive_fragment;;
  1449. 9) format_drive;;
  1450. 10) remove_backups;;
  1451. esac
  1452. done
  1453. }
  1454. function menu_email {
  1455. while true
  1456. do
  1457. W=(1 $"Add a user to a mailing list"
  1458. 2 $"Remove a user from a mailing list"
  1459. 3 $"Add an email rule"
  1460. 4 $"Block/Unblock an email address"
  1461. 5 $"Block/Unblock email with subject text"
  1462. 6 $"Outgoing Email Proxy"
  1463. 7 $"Extra email domains")
  1464. # shellcheck disable=SC2068
  1465. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Email Menu" --menu $"Choose an operation, or ESC for main menu:" 15 70 8 "${W[@]}" 3>&2 2>&1 1>&3)
  1466. if [ ! "$selection" ]; then
  1467. break
  1468. fi
  1469. case $selection in
  1470. 1) add_to_mailing_list;;
  1471. 2) remove_user_from_mailing_list;;
  1472. 3) email_rule;;
  1473. 4) block_unblock_email;;
  1474. 5) block_unblock_subject;;
  1475. 6) email_smtp_proxy;;
  1476. 7) email_extra_domains;;
  1477. esac
  1478. done
  1479. }
  1480. function domain_blocking_add {
  1481. data=$(mktemp 2>/dev/null)
  1482. dialog --title $"Block a domain or user" \
  1483. --backtitle $"Freedombone Control Panel" \
  1484. --inputbox $"Enter the domain name or GNU Social/postActiv/Pleroma nick@domain that you wish to block" 8 60 "" 2>"$data"
  1485. sel=$?
  1486. case $sel in
  1487. 0)
  1488. blocked_domain=$(<"$data")
  1489. if [ ${#blocked_domain} -gt 2 ]; then
  1490. if [[ "${blocked_domain}" == *'.'* ]]; then
  1491. firewall_block_domain "$blocked_domain"
  1492. if [[ "${blocked_domain}" != *'@'* ]]; then
  1493. dialog --title $"Block a domain" \
  1494. --msgbox $"The domain $blocked_domain has been blocked" 6 40
  1495. else
  1496. dialog --title $"Block a GNU Social/postActiv/Pleroma nickname" \
  1497. --msgbox $"$blocked_domain has been blocked" 6 40
  1498. fi
  1499. fi
  1500. fi
  1501. ;;
  1502. esac
  1503. rm -f "$data"
  1504. }
  1505. function ip_blocking_add {
  1506. data=$(mktemp 2>/dev/null)
  1507. dialog --title $"Block an IP address" \
  1508. --backtitle $"Freedombone Control Panel" \
  1509. --inputbox $"Enter the IP address that you wish to block" 8 60 "" 2>"$data"
  1510. sel=$?
  1511. case $sel in
  1512. 0)
  1513. blocked_ip=$(<"$data")
  1514. if [ ${#blocked_ip} -gt 2 ]; then
  1515. if [[ "${blocked_ip}" == *'.'* ]]; then
  1516. firewall_block_ip "$blocked_ip"
  1517. if [[ "${blocked_ip}" != *'@'* ]]; then
  1518. dialog --title $"Block an IP address" \
  1519. --msgbox $"The IP address $blocked_ip has been blocked" 6 40
  1520. fi
  1521. fi
  1522. fi
  1523. ;;
  1524. esac
  1525. rm -f "$data"
  1526. }
  1527. function domain_blocking_remove {
  1528. data=$(mktemp 2>/dev/null)
  1529. dialog --title $"Unblock a domain or user" \
  1530. --backtitle $"Freedombone Control Panel" \
  1531. --inputbox $"Enter the domain name or GNU Social/postActiv nick@domain that you wish to unblock" 8 60 "" 2>"$data"
  1532. sel=$?
  1533. case $sel in
  1534. 0)
  1535. unblocked_domain=$(<"$data")
  1536. if [ ${#unblocked_domain} -gt 2 ]; then
  1537. if [[ "${unblocked_domain}" == *'.'* ]]; then
  1538. firewall_unblock_domain "$unblocked_domain"
  1539. if [[ "${unblocked_domain}" != *'@'* ]]; then
  1540. dialog --title $"Unblock a domain" \
  1541. --msgbox $"The domain $unblocked_domain has been unblocked" 6 40
  1542. else
  1543. dialog --title $"Unblock a GNU Social/postActiv nickname" \
  1544. --msgbox $"$unblocked_domain has been unblocked" 6 40
  1545. fi
  1546. fi
  1547. fi
  1548. ;;
  1549. esac
  1550. rm -f "$data"
  1551. }
  1552. function ip_blocking_remove {
  1553. data=$(mktemp 2>/dev/null)
  1554. dialog --title $"Unblock an IP address" \
  1555. --backtitle $"Freedombone Control Panel" \
  1556. --inputbox $"Enter the IP address that you wish to unblock" 8 60 "" 2>"$data"
  1557. sel=$?
  1558. case $sel in
  1559. 0)
  1560. unblocked_ip=$(<"$data")
  1561. if [ ${#unblocked_ip} -gt 2 ]; then
  1562. if [[ "${unblocked_ip}" == *'.'* ]]; then
  1563. firewall_unblock_ip "$unblocked_ip"
  1564. if [[ "${unblocked_ip}" != *'@'* ]]; then
  1565. dialog --title $"Unblock an IP address" \
  1566. --msgbox $"The IP address $unblocked_ip has been unblocked" 6 40
  1567. fi
  1568. fi
  1569. fi
  1570. ;;
  1571. esac
  1572. rm -f "$data"
  1573. }
  1574. function domain_blocking_show {
  1575. if [ -f "$FIREWALL_DOMAINS" ]; then
  1576. clear
  1577. echo ''
  1578. echo $'The following domains or users have been blocked:'
  1579. echo ''
  1580. sort < "$FIREWALL_DOMAINS"
  1581. any_key
  1582. else
  1583. dialog --title $"Show blocked domains or users" \
  1584. --msgbox $"No domains or users are currently blocked" 6 40
  1585. fi
  1586. }
  1587. function domain_blocking {
  1588. while true
  1589. do
  1590. W=(1 $"Block a domain or user"
  1591. 2 $"Unblock a domain or user"
  1592. 3 $"Block an IP address"
  1593. 4 $"Unblock an IP address"
  1594. 5 $"Show blocked domains and users")
  1595. # shellcheck disable=SC2068
  1596. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Domain or User Blocking" --menu $"Choose an operation, or ESC for main menu:" 13 70 6 "${W[@]}" 3>&2 2>&1 1>&3)
  1597. if [ ! "$selection" ]; then
  1598. break
  1599. fi
  1600. case $selection in
  1601. 1) domain_blocking_add;;
  1602. 2) domain_blocking_remove;;
  1603. 3) ip_blocking_add;;
  1604. 4) ip_blocking_remove;;
  1605. 5) domain_blocking_show;;
  1606. esac
  1607. done
  1608. }
  1609. function menu_users {
  1610. while true
  1611. do
  1612. W=(1 $"Add a user"
  1613. 2 $"Delete a user"
  1614. 3 $"Change user password"
  1615. 4 $"Change user ssh public key"
  1616. 5 $"Reset password tries")
  1617. # shellcheck disable=SC2068
  1618. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Manage Users" --menu $"Choose an operation, or ESC for main menu:" 13 70 6 "${W[@]}" 3>&2 2>&1 1>&3)
  1619. if [ ! "$selection" ]; then
  1620. break
  1621. fi
  1622. case $selection in
  1623. 1) add_user;;
  1624. 2) delete_user;;
  1625. 3) change_password;;
  1626. 4) change_ssh_public_key;;
  1627. 5) reset_password_tries;;
  1628. esac
  1629. done
  1630. }
  1631. function wifi_enable {
  1632. disable_wifi='yes'
  1633. dialog --title $"Enable Wifi" \
  1634. --backtitle $"Freedombone Control Panel" \
  1635. --defaultno \
  1636. --yesno $"\\nDo you wish to enable wifi?" 10 50
  1637. sel=$?
  1638. case $sel in
  1639. 0) disable_wifi='no';;
  1640. 1) disable_wifi='yes';;
  1641. 255) return;;
  1642. esac
  1643. "${PROJECT_NAME}-wifi" --disable $disable_wifi
  1644. }
  1645. function add_clacks {
  1646. clacks=
  1647. data=$(mktemp 2>/dev/null)
  1648. dialog --title $"Add Clacks Overhead" \
  1649. --backtitle $"Freedombone Control Panel" \
  1650. --inputbox $"" 7 60 2>"$data"
  1651. sel=$?
  1652. case $sel in
  1653. 0)
  1654. clacks=$(<"$data")
  1655. if [ ${#clacks} -gt 1 ]; then
  1656. WEB_FILES="/etc/nginx/sites-available/*"
  1657. for f in $WEB_FILES
  1658. do
  1659. if grep -q "X-Clacks-Overhead" "$f"; then
  1660. sed -i "s|X-Clacks-Overhead .*|X-Clacks-Overhead \"GNU $clacks\";|g" "$f"
  1661. else
  1662. sed -i "/X-Content-Type-Options/a add_header X-Clacks-Overhead \"GNU $clacks\";" "$f"
  1663. fi
  1664. done
  1665. systemctl restart nginx
  1666. dialog --title $"Add Clacks Overhead" \
  1667. --msgbox $"\\nAdded for $clacks" 10 60
  1668. fi
  1669. ;;
  1670. esac
  1671. rm -f "$data"
  1672. }
  1673. function menu_wifi {
  1674. if [[ "$(wifi_exists)" == "0" ]]; then
  1675. dialog --title $"Wifi" \
  1676. --msgbox $"No wifi adaptors were detected" 6 40
  1677. return
  1678. fi
  1679. while true
  1680. do
  1681. status_str=$'Wifi OFF'
  1682. if [ -f /etc/hostapd/hostapd.conf ]; then
  1683. status_str=$'Hotspot ON'
  1684. else
  1685. if [ -f /etc/network/interfaces.d/wifi ]; then
  1686. status_str=$'Wifi ON'
  1687. fi
  1688. fi
  1689. W=(1 $"Enable or disable Wifi"
  1690. 2 $"Configure wifi networks"
  1691. 3 $"Manually edit wifi networks file"
  1692. 4 $"Hotspot settings")
  1693. # shellcheck disable=SC2068
  1694. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Wifi Menu" --menu $"${status_str}\\n\\nChoose an operation, or ESC for main menu:" 14 70 6 "${W[@]}" 3>&2 2>&1 1>&3)
  1695. if [ ! "$selection" ]; then
  1696. break
  1697. fi
  1698. case $selection in
  1699. 1) wifi_enable;;
  1700. 2) wifi_settings;;
  1701. 3) wifi_edit_networks;;
  1702. 4) hotspot_settings;;
  1703. esac
  1704. done
  1705. }
  1706. function menu_app_settings {
  1707. detect_installable_apps
  1708. W=()
  1709. appnames=()
  1710. n=1
  1711. app_index=0
  1712. # shellcheck disable=SC2068
  1713. for a in ${APPS_AVAILABLE[@]}
  1714. do
  1715. if [[ ${APPS_INSTALLED[$app_index]} != "0" ]]; then
  1716. if [[ $(function_exists "configure_interactive_${a}") == "1" ]]; then
  1717. W+=("$n" "$a")
  1718. n=$((n+1))
  1719. appnames+=("$a")
  1720. fi
  1721. fi
  1722. app_index=$((app_index+1))
  1723. done
  1724. if [ $n -le 1 ]; then
  1725. return
  1726. fi
  1727. # shellcheck disable=SC2086
  1728. choice=$(dialog --backtitle $"Freedombone" \
  1729. --title $"Change settings for an App" \
  1730. --menu $'Choose:' \
  1731. 26 40 30 "${W[@]}" 3>&2 2>&1 1>&3)
  1732. # shellcheck disable=SC2181
  1733. if [ "$choice" ]; then
  1734. app_index=$((choice-1))
  1735. chosen_app=${appnames[$app_index]}
  1736. "configure_interactive_${chosen_app}"
  1737. fi
  1738. }
  1739. function menu_top_level {
  1740. while true
  1741. do
  1742. W=(1 $"About this system"
  1743. 2 $"Backup and Restore"
  1744. 3 $"App Settings"
  1745. 4 $"Add/Remove Apps"
  1746. 5 $"Logging on/off"
  1747. 6 $"Manage Users"
  1748. 7 $"Email Menu"
  1749. 8 $"Domain or User Blocking"
  1750. 9 $"Security Settings"
  1751. 10 $"Change the name of this system"
  1752. 11 $"Set a static local IP address"
  1753. 12 $"Wifi menu"
  1754. 13 $"Add Clacks"
  1755. 14 $"Check for updates"
  1756. 15 $"Power off the system"
  1757. 16 $"Restart the system")
  1758. # shellcheck disable=SC2068
  1759. selection=$(dialog --backtitle $"Freedombone Administrator Control Panel" --title $"Administrator Control Panel" --menu $"Choose an operation, or ESC to exit:" 24 60 24 "${W[@]}" 3>&2 2>&1 1>&3)
  1760. if [ ! "$selection" ]; then
  1761. break
  1762. fi
  1763. please_wait
  1764. case $selection in
  1765. 1) show_about;;
  1766. 2) menu_backup_restore;;
  1767. 3) menu_app_settings;;
  1768. 4) if ! /usr/local/bin/addremove; then
  1769. any_key
  1770. fi
  1771. ;;
  1772. 5) logging_on_off;;
  1773. 6) menu_users;;
  1774. 7) menu_email;;
  1775. 8) domain_blocking;;
  1776. 9) security_settings;;
  1777. 10) change_system_name;;
  1778. 11) set_static_IP;;
  1779. 12) menu_wifi;;
  1780. 13) add_clacks;;
  1781. 14) check_for_updates;;
  1782. 15) shut_down_system;;
  1783. 16) restart_system;;
  1784. esac
  1785. done
  1786. }
  1787. if [ ! -f "$COMPLETION_FILE" ]; then
  1788. echo $'This command should only be run on an installed Freedombone system'
  1789. exit 1
  1790. fi
  1791. ADMIN_USER=$(get_completion_param "Admin user")
  1792. menu_top_level
  1793. clear
  1794. cat /etc/motd
  1795. exit 0