freedombone-controlpanel 65KB

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