freedombone-controlpanel 64KB

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