freedombone-controlpanel 66KB

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