freedombone-utils-selector 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. #!/bin/bash
  2. #
  3. # .---. . .
  4. # | | |
  5. # |--- .--. .-. .-. .-.| .-. .--.--. |.-. .-. .--. .-.
  6. # | | (.-' (.-' ( | ( )| | | | )( )| | (.-'
  7. # ' ' --' --' -' - -' ' ' -' -' -' ' - --'
  8. #
  9. # Freedom in the Cloud
  10. #
  11. # Functions for selecting which apps to install or remove
  12. #
  13. # License
  14. # =======
  15. #
  16. # Copyright (C) 2015-2016 Bob Mottram <bob@freedombone.net>
  17. #
  18. # This program is free software: you can redistribute it and/or modify
  19. # it under the terms of the GNU Affero General Public License as published by
  20. # the Free Software Foundation, either version 3 of the License, or
  21. # (at your option) any later version.
  22. #
  23. # This program is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. # GNU Affero General Public License for more details.
  27. #
  28. # You should have received a copy of the GNU Affero General Public License
  29. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  30. # Array containing names of available apps
  31. APPS_AVAILABLE=()
  32. # Array containing 1 or 0 indicating installed apps
  33. APPS_INSTALLED=()
  34. # Apps selected with checklist
  35. APPS_CHOSEN=()
  36. # A list of the names of installed apps
  37. APPS_INSTALLED_NAMES=()
  38. # file containing a list of removed apps
  39. REMOVED_APPS_FILE=/root/removed
  40. INSTALLED_APPS_LIST=/usr/share/${PROJECT_NAME}/installed.txt
  41. # keep a list of which users have been added to which apps
  42. # so that when a new app is added existing users can be added
  43. APP_USERS_FILE=$HOME/app_users.txt
  44. if [ ! $COMPLETION_FILE ]; then
  45. COMPLETION_FILE=$HOME/${PROJECT_NAME}-completed.txt
  46. fi
  47. # Loads variables defined at the beginning of an app script
  48. function app_load_variables {
  49. app_name=$1
  50. config_var_name=${app_name}_variables
  51. if [ ! ${!config_var_name} ]; then
  52. echo $"${app_name}_variables was not found"
  53. return
  54. fi
  55. configvarname=$config_var_name[@]
  56. configvarname=( ${!configvarname} )
  57. for v in "${configvarname[@]}"
  58. do
  59. read_config_param $v
  60. done
  61. }
  62. # Saves variables for a given app script
  63. function app_save_variables {
  64. app_name=$1
  65. config_var_name=${app_name}_variables
  66. if [ ! ${!config_var_name} ]; then
  67. return
  68. fi
  69. configvarname=$config_var_name[@]
  70. configvarname=( ${!configvarname} )
  71. for v in "${configvarname[@]}"
  72. do
  73. write_config_param $v "${!v}"
  74. done
  75. }
  76. # gets the variants list from an app script
  77. function app_variants {
  78. filename=$1
  79. variants_line=$(cat ${filename} | grep 'VARIANTS=')
  80. if [[ "$variants_line" == *"'"* ]]; then
  81. variants_list=$(echo "$variants_line" | awk -F '=' '{print $2}' | awk -F "'" '{print $2}')
  82. else
  83. variants_list=$(echo "$variants_line" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
  84. fi
  85. echo "$variants_list"
  86. }
  87. # whether a given item is in an array
  88. function item_in_array {
  89. local e
  90. for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  91. return 1
  92. }
  93. # returns a list of available system variants
  94. # based upon the variants string in each app script
  95. function available_system_variants {
  96. function_check item_in_array
  97. FILES=/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*
  98. new_available_variants_list=()
  99. for filename in $FILES
  100. do
  101. system_variants_list=$(app_variants $filename)
  102. variants_array=($system_variants_list)
  103. for variant_str in "${variants_array[@]}"
  104. do
  105. item_in_array "${variant_str}" "${new_available_variants_list[@]}"
  106. if [[ $? != 0 ]]; then
  107. new_available_variants_list+=("$variant_str")
  108. fi
  109. done
  110. done
  111. available_variants_list=($(sort <<<"${new_available_variants_list[*]}"))
  112. }
  113. function is_valid_variant {
  114. sys_type="$1"
  115. available_variants_list=()
  116. function_check available_system_variants
  117. available_system_variants
  118. for variant_str in "${available_variants_list[@]}"
  119. do
  120. if [[ "$sys_type" == "$variant_str" ]]; then
  121. return "1"
  122. fi
  123. done
  124. return "0"
  125. }
  126. function show_available_variants {
  127. available_variants_list=()
  128. function_check available_system_variants
  129. available_system_variants
  130. for variant_str in "${available_variants_list[@]}"
  131. do
  132. echo " $variant_str"
  133. done
  134. }
  135. # mark a given app as having been removed so that it doesn't get reinstalled on updates
  136. function remove_app {
  137. app_name=$1
  138. if [ ! -f $REMOVED_APPS_FILE ]; then
  139. touch $REMOVED_APPS_FILE
  140. fi
  141. if ! grep -Fxq "_${app_name}_" $REMOVED_APPS_FILE; then
  142. echo "_${app_name}_" >> $REMOVED_APPS_FILE
  143. fi
  144. if grep -Fxq "install_${app_name}" $COMPLETION_FILE; then
  145. sed -i "/install_${app_name}/d" $COMPLETION_FILE
  146. fi
  147. if grep -Fxq "install_${app_name}" $INSTALLED_APPS_LIST; then
  148. sed -i "/install_${app_name}/d" $INSTALLED_APPS_LIST
  149. fi
  150. }
  151. # returns 1 if an app has been marked as removed
  152. function app_is_removed {
  153. app_name="$1"
  154. if [ ! -f $REMOVED_APPS_FILE ]; then
  155. echo "0"
  156. return
  157. fi
  158. if ! grep -Fxq "_${app_name}_" $REMOVED_APPS_FILE; then
  159. echo "0"
  160. else
  161. echo "1"
  162. fi
  163. }
  164. # Allows an app to be reinstalled even if it was previously marked as being removed
  165. function reinstall_app {
  166. app_name=$1
  167. if [ ! -f $REMOVED_APPS_FILE ]; then
  168. return
  169. fi
  170. if [[ $(app_is_removed $app_name) == "1" ]]; then
  171. sed -i "/_${app_name}_/d" $REMOVED_APPS_FILE
  172. fi
  173. }
  174. # returns 1 if an app is installed
  175. function app_is_installed {
  176. app_name="$1"
  177. # Why does this secondary file exist, apart from COMPLETION_FILE ?
  178. # It's so that it is visible to unprivileged users from the user control panel
  179. if [ -f $INSTALLED_APPS_LIST ]; then
  180. if ! grep -Fxq "install_${app_name}" $INSTALLED_APPS_LIST; then
  181. echo "0"
  182. else
  183. echo "1"
  184. fi
  185. return
  186. fi
  187. # check the completion file to see if it was installed
  188. if [ ! -f $COMPLETION_FILE ]; then
  189. echo "0"
  190. return
  191. fi
  192. if ! grep -Fxq "install_${app_name}" $COMPLETION_FILE; then
  193. echo "0"
  194. else
  195. echo "1"
  196. fi
  197. }
  198. # called at the end of the install section of an app script
  199. function install_completed {
  200. if [ ! ${1} ]; then
  201. exit 673935
  202. fi
  203. if ! grep -Fxq "install_${1}" $COMPLETION_FILE; then
  204. echo "install_${1}" >> $COMPLETION_FILE
  205. fi
  206. }
  207. # populates an array of "0" or "1" for whether apps are installed
  208. function get_apps_installed {
  209. for a in "${APPS_AVAILABLE[@]}"
  210. do
  211. APPS_INSTALLED+=("$(app_is_installed $a)")
  212. done
  213. }
  214. # populates an array of installed app names
  215. function get_apps_installed_names {
  216. APPS_INSTALLED_NAMES=()
  217. for a in "${APPS_AVAILABLE[@]}"
  218. do
  219. if [[ $(app_is_installed $a) == "1" ]]; then
  220. APPS_INSTALLED_NAMES+=("$a")
  221. fi
  222. done
  223. }
  224. # detects what apps are available
  225. function detect_apps {
  226. FILES=/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*
  227. function_check item_in_array
  228. APPS_AVAILABLE=()
  229. APPS_CHOSEN=()
  230. # for all the app scripts
  231. for filename in $FILES
  232. do
  233. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  234. item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"
  235. if [[ $? != 0 ]]; then
  236. APPS_AVAILABLE+=("${app_name}")
  237. APPS_CHOSEN+=("0")
  238. fi
  239. done
  240. function_check get_apps_installed
  241. get_apps_installed
  242. get_apps_installed_names
  243. }
  244. # detects what apps are available and can be installed
  245. # If the variants list within an app script is an empty string then
  246. # it is considered to be too experimental to be installable
  247. function detect_installable_apps {
  248. FILES=/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*
  249. APPS_AVAILABLE=()
  250. APPS_CHOSEN=()
  251. APPS_INSTALLED=()
  252. APPS_INSTALLED_NAMES=()
  253. function_check app_variants
  254. function_check app_is_installed
  255. function_check item_in_array
  256. # for all the app scripts
  257. for filename in $FILES
  258. do
  259. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  260. item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"
  261. if [[ $? != 0 ]]; then
  262. variants_list=$(app_variants $filename)
  263. # check for empty string
  264. if [ ${#variants_list} -gt 0 ]; then
  265. APPS_AVAILABLE+=("${app_name}")
  266. APPS_CHOSEN+=("0")
  267. APPS_INSTALLED+=("$(app_is_installed $app_name)")
  268. if [[ $(app_is_installed $app_name) == "1" ]]; then
  269. APPS_INSTALLED_NAMES+=("$app_name")
  270. fi
  271. fi
  272. fi
  273. done
  274. }
  275. # creates the APPS_AVAILABLE and APPS_CHOSEN arrays based on
  276. # the given variant name
  277. function choose_apps_for_variant {
  278. variant_name="$1"
  279. function_check item_in_array
  280. function_check app_variants
  281. function_check app_is_removed
  282. if [ ${#variant_name} -eq 0 ]; then
  283. echo $"No variant name for choosing apps"
  284. exit 237567
  285. fi
  286. FILES=/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*
  287. APPS_CHOSEN=()
  288. # for all the app scripts
  289. for filename in $FILES
  290. do
  291. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  292. item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"
  293. if [[ $? == 0 ]]; then
  294. if grep -q "VARIANTS=" ${filename}; then
  295. variants_list=$(app_variants $filename)
  296. if [[ "${variants_list}" == 'all'* || \
  297. "${variants_list}" == "$variant_name" || \
  298. "${variants_list}" == "$variant_name "* || \
  299. "${variants_list}" == *" $variant_name "* || \
  300. "${variants_list}" == *" $variant_name" ]]; then
  301. if [[ $(app_is_removed ${a}) == "0" ]]; then
  302. echo $"${app_name} chosen"
  303. APPS_CHOSEN+=("1")
  304. else
  305. APPS_CHOSEN+=("0")
  306. fi
  307. else
  308. APPS_CHOSEN+=("0")
  309. fi
  310. else
  311. APPS_CHOSEN+=("0")
  312. fi
  313. fi
  314. done
  315. function_check get_apps_installed
  316. get_apps_installed
  317. }
  318. # show a list of apps which have been chosen
  319. function list_chosen_apps {
  320. app_index=0
  321. for a in "${APPS_AVAILABLE[@]}"
  322. do
  323. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  324. echo $"${a}"
  325. fi
  326. app_index=$[app_index+1]
  327. done
  328. }
  329. function remove_apps {
  330. app_index=0
  331. for a in "${APPS_AVAILABLE[@]}"
  332. do
  333. if [[ ${APPS_INSTALLED[$app_index]} == "1" ]]; then
  334. if [[ ${APPS_CHOSEN[$app_index]} == "0" ]]; then
  335. echo $"Removing users for application: ${a}"
  336. function_check remove_users_for_app
  337. remove_users_for_app ${a}
  338. echo $"Removing application: ${a}"
  339. function_check app_load_variables
  340. app_load_variables ${a}
  341. function_check remove_app
  342. remove_app ${a}
  343. function_check remove_${a}
  344. remove_${a}
  345. echo $"${a} was removed"
  346. fi
  347. fi
  348. app_index=$[app_index+1]
  349. done
  350. update_installed_apps_list
  351. }
  352. function install_apps_interactive {
  353. echo $"Interactive installer"
  354. app_index=0
  355. for a in "${APPS_AVAILABLE[@]}"
  356. do
  357. if [[ ${APPS_INSTALLED[$app_index]} == "0" ]]; then
  358. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  359. # interactively obtain settings for this app
  360. if [[ $(function_exists install_interactive_${a}) == "1" ]]; then
  361. install_interactive_${a}
  362. fi
  363. fi
  364. fi
  365. app_index=$[app_index+1]
  366. done
  367. echo $"Interactive settings complete"
  368. }
  369. function user_added_to_app {
  370. user_name="$1"
  371. app_name="$2"
  372. if [[ $(is_valid_user "$user_name") == "1" ]]; then
  373. if [[ $(function_exists add_user_${app_name}) == "1" ]]; then
  374. if grep -Fxq "${app_name}_${user_name}" $APP_USERS_FILE; then
  375. echo "1"
  376. return
  377. fi
  378. fi
  379. fi
  380. echo "0"
  381. }
  382. function add_users_after_install {
  383. app_name="$1"
  384. read_config_param MY_USERNAME
  385. # ensure a minimum password length
  386. if [ ! $MINIMUM_PASSWORD_LENGTH ]; then
  387. MINIMUM_PASSWORD_LENGTH=20
  388. fi
  389. if [ ${#MINIMUM_PASSWORD_LENGTH} -lt 20 ]; then
  390. MINIMUM_PASSWORD_LENGTH=20
  391. fi
  392. ADMIN_USERNAME=$(get_completion_param "Admin user")
  393. if [ ! $ADMIN_USERNAME ]; then
  394. ADMIN_USERNAME=$MY_USERNAME
  395. fi
  396. for d in /home/*/ ; do
  397. USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
  398. if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
  399. if [[ "$USERNAME" != "$ADMIN_USERNAME" ]]; then
  400. if [[ $(user_added_to_app "${USERNAME}" "${app_name}") == "0" ]]; then
  401. valstr=$"Login for user ${USERNAME}="
  402. if grep -q "${valstr}" /home/${ADMIN_USERNAME}/README; then
  403. app_password=$(cat /home/${ADMIN_USERNAME}/README | grep "${valstr}" | head -n 1 | awk -F '=' '{print $2}')
  404. else
  405. app_password="$(create_password ${MINIMUM_PASSWORD_LENGTH})"
  406. echo "${valstr}${app_password}" >> /home/${ADMIN_USERNAME}/README
  407. fi
  408. add_user_${app_name} "${USERNAME}" "${app_password}"
  409. echo "${app_name}_${USERNAME}" >> $APP_USERS_FILE
  410. chown ${ADMIN_USERNAME}:${ADMIN_USERNAME} /home/${ADMIN_USERNAME}/README
  411. fi
  412. fi
  413. fi
  414. done
  415. }
  416. function remove_users_for_app {
  417. app_name="$1"
  418. read_config_param MY_USERNAME
  419. for d in /home/*/ ; do
  420. USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
  421. if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
  422. if [[ "$USERNAME" != "$MY_USERNAME" ]]; then
  423. if [[ $(user_added_to_app "${USERNAME}" "${app_name}") == "1" ]]; then
  424. if [[ $(function_exists remove_user_${app_name}) == "1" ]]; then
  425. remove_user_${app_name} "${USERNAME}"
  426. fi
  427. sed -i "/${app_name}_${USERNAME}/d" $APP_USERS_FILE
  428. fi
  429. fi
  430. fi
  431. done
  432. }
  433. function install_apps {
  434. is_interactive=$1
  435. APP_INSTALLED_SUCCESS=1
  436. # interactive install configuration for each app
  437. if [ ${is_interactive} ]; then
  438. install_apps_interactive
  439. fi
  440. # now install the apps
  441. app_index=0
  442. for a in "${APPS_AVAILABLE[@]}"
  443. do
  444. if [[ ${APPS_INSTALLED[$app_index]} == "0" ]]; then
  445. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  446. if [ ${is_interactive} ]; then
  447. # clears any removal indicator
  448. function_check reinstall_app
  449. reinstall_app ${a}
  450. function_check app_load_variables
  451. app_load_variables ${a}
  452. if [[ $(app_is_installed ${a}) == "1" ]]; then
  453. echo $"Upgrading application from interactive: ${a}"
  454. upgrade_${a}
  455. echo $"${a} was upgraded from interactive"
  456. else
  457. echo $"Installing application from interactive: ${a}"
  458. APP_INSTALLED=
  459. install_${a}
  460. if [ $APP_INSTALLED ]; then
  461. function_check app_save_variables
  462. app_save_variables ${a}
  463. function_check add_users_after_install
  464. add_users_after_install ${a}
  465. ${PROJECT_NAME}-mirrors --app ${a}
  466. function_check install_completed
  467. install_completed ${a}
  468. echo $"${a} was installed from interactive"
  469. else
  470. echo "Failed to install: ${a}" >> /var/log/${PROJECT_NAME}.log
  471. APP_INSTALLED_SUCCESS=
  472. echo $"${a} was not installed from interactive"
  473. fi
  474. fi
  475. else
  476. # check if the app was removed
  477. if [[ $(app_is_removed ${a}) == "0" ]]; then
  478. function_check app_load_variables
  479. app_load_variables ${a}
  480. if [[ $(app_is_installed ${a}) == "1" ]]; then
  481. echo $"Upgrading application: ${a}"
  482. upgrade_${a}
  483. echo $"${a} was upgraded"
  484. else
  485. echo $"Installing application: ${a}"
  486. APP_INSTALLED=
  487. install_${a}
  488. if [ $APP_INSTALLED ]; then
  489. function_check app_save_variables
  490. app_save_variables ${a}
  491. function_check add_users_after_install
  492. add_users_after_install ${a}
  493. ${PROJECT_NAME}-mirrors --app ${a}
  494. function_check install_completed
  495. install_completed ${a}
  496. echo $"${a} was installed"
  497. else
  498. echo "Failed to install: ${a}" >> /var/log/${PROJECT_NAME}.log
  499. APP_INSTALLED_SUCCESS=
  500. echo $"${a} was not installed"
  501. fi
  502. fi
  503. else
  504. echo $"${a} has been removed and so will not be reinstalled"
  505. fi
  506. fi
  507. fi
  508. fi
  509. app_index=$[app_index+1]
  510. done
  511. function_check update_installed_apps_list
  512. update_installed_apps_list
  513. }
  514. # NOTE: deliberately no exit 0