-
Notifications
You must be signed in to change notification settings - Fork 0
/
poppi.sh
executable file
·3156 lines (2785 loc) · 137 KB
/
poppi.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
#############################################################################################################
# Description: Pop!_OS post-installation methods tested on version 22.04 LTS #
# Github repository: https://github.com/simurqq/poppi #
# License: GPLv3 #
# Author: Victor Quebec #
# Date: Nov 5, 2024 #
# Requirements: Bash v4.2 and above, #
# coreutils, curl, jq, pip, python #
#############################################################################################################
# shellcheck disable=SC2010 source=/dev/null
set -o pipefail # details: https://t.ly/U7D1K
_START_TIME=$SECONDS
__init_logfile() {
# Description: Creates a log file for this script and manages its backups.
# Arguments: None.
# Output: File(s) 'poppi[_ddmmYYYY|YYYYmmdd]_HHmmss.log' in the script directory.
local fmt log_files oldlog total_logs
[ "$_LOGFILEON" -ne 1 ] && return 2
# Back up the logs, as specified by user
if [ "$_LFBKPNO" -ne 0 ]; then
shopt -s nullglob
log_files=("${_LOGFILE}"_*.log)
total_logs=${#log_files[@]}
if [[ $total_logs -ge "$_LFBKPNO" ]]; then
oldlog=$(find "$_BASEDIR" -type f -name "$(basename "${_LOGFILE}")_*" -printf "%T@ %p\n" | sort -n | head -1 | cut -d' ' -f2-) # locate the oldest log
[ -n "$oldlog" ] && rm "$oldlog" 2>&1 | log_trace "[LOG]" # FIFO rulez!
fi
# Format the output
if [ "$_LOGFILEFMT" == 'US' ]; then
fmt='%Y%m%d_%H%M%S'
else
fmt='%d%m%Y_%H%M%S'
fi
if [ -f "${_LOGFILE}"'.log' ]; then
mv "${_LOGFILE}"'.log' "${_LOGFILE}"'_'"$(date +$fmt)"'.log' 2>&1 | log_trace "[LOG]" # back up the existing file
fi
fi
# Create a log file
if [ ! -f "${_LOGFILE}"'.log' ]; then
if touch "${_LOGFILE}"'.log' 2>&1 | log_trace "[LOG]" && [ -w "${_LOGFILE}"'.log' ]; then
log_message "Created log file: ${_LOGFILE}.log" 1
log_message "Logging initialised" 1
else
log_and_exit "${FUNCNAME[0]}: Failed to create a logfile." 6
fi
fi
}
__init_vars() {
# Description: First function to load and set initial global variables.
# Arguments: None.
# Note: Avoid changing the order of initialisation due to variable dependencies!
# Global variables and constants with default values
_USERNAME=$(whoami) # user name; must be the first constant initialised
_APPSDIR="$HOME"/Portables # path to portable programs
_AUTODRIVES=() # USB drives to mount on each system boot
_AUTOSTART=() # packages to autostart on system reboot
_AVATARDSTDIR='/usr/share/pixmaps/faces' # source/destination directory for user avatar images
_AVATARIMG='popos.png' # avatar image file
_AVATARON=1 # enable/disable user avatar
_AVATARTXT=/var/lib/AccountsService/users/"$_USERNAME" # a text file with paths to user avatars
_BASEDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # script directory
_BASHRC="$HOME"/.bashrc # user settings updated with every new shell session
_BINPATH="$HOME"/.local/bin # directory for prerequisite binaries
_BOOKDIRS=() # directories to bookmark to GNOME Files/Nautilus
_CONFIG_FILE='' # user configuration file
_CONFIGS_DIR="$_BASEDIR"/data/configs # source directory for user configuration files
_CONFIGSP_DIR="$_BASEDIR"/data/configsp # source directory for user configuration files for portable programs
_CRONLINE=() # crontab commands
_DFTRMPRFL=$(gsettings get org.gnome.Terminal.ProfilesList default | sed "s/'//g") # default terminal profile
_DISPLAY="DISPLAY=${DISPLAY}" # by default = :1
_DOTFILES="$_BASEDIR"/data/dotfiles # source directory for dotfiles
_ENDMSG='false' # final message toggle
_EXEC_START=$(date +%s) # script launch time
_FFXADDONSURL='https://addons.mozilla.org/addon' # main server to download Firefox addons
_FFXAPPINI='/usr/lib/firefox/application.ini' # required Firefox settings
_FFXCHANNEL='' # Firefox channel info
_FFXCHANNELFILE='/usr/lib/firefox/defaults/pref/channel-prefs.js' # File with Firefox channel info
_FFXCONFIG=0 # configure Firefox?
_FFXCOOKIES=() # Firefox cookies to keep
_FFXDIR="$HOME"/.mozilla/firefox # Firefox profile directory
_FFXEXTSLST=() # list of installed Firefox extensions
_FFXEXTS=() # list of Firefox extensions
_FFXHOMEPAGE=0 # set/unset custom homepage for Firefox
_FFXPREFS='prefs.js' # Firefox preferences
_FFXPRF='' # Firefox default profile directory
_FFXUSEROVERRIDES='user-overrides.js' # Firefox user settings to override default ones (if _FFXPRIVACY enabled)
_FFXPRIVACY=0 # enable/disable Firefox privacy settings
_FILELOGGER_ACTIVE=false # log file status
_GCALC=() # custom functions for GNOME Calc
_GFAVOURITES=() # favourite programs to dock
_GNOMEXTS=() # GNOME extensions
_GNOMEXTSET=() # GNOME extension settings
_GSETTINGS=() # GNOME pre-configured GSettings
_GCSETTINGS=() # GNOME custom GSettings (schema, key, and value)
_GTKBKMRK="$HOME"/.config/gtk-3.0/bookmarks # directory bookmarks on Files/Nautilus
_GTKEXTS="$HOME"/.local/share/gnome-shell/extensions # default location for GNOME extensions
_INSTALLERS=() # .deb and other installer packages to install
_isLOCKED=0 # screenlock status
_JSON_DATA='' # contents of the user configuration file
_LFBKPNO=3 # number of log files to back up
_LODIR="$_BASEDIR"/data/libreoffice # directory for LibreOffice extensions
_LOEXTS=() # array for LibreOffice extensions
_LOEXTSURL='https://extensions.libreoffice.org/en/extensions/show' # main server to download LibreOffice extensions
_LOGFILE="$_BASEDIR"/poppi # log file
_LOGFILEFMT='Metric' # formatting of date and time: US or Metric
_LOGFILEON=1 # enable/disable log file
_MAXWIN=1 # maximise window
_MISC="$_BASEDIR"/data/misc # directory for miscellaneous files
_MSFONTS=0 # install Microsoft fonts
_OPTION='' # any of the valid POPPI options: -[abcdfghprvx]
_OS_RELEASE="/etc/os-release" # file with OS info
_OS="Pop!_OS" # operating system
_OVERAMPLIFY=0 # overamplify the system volume
_PORTABLES=() # an array of AppImages and other portable packages to install
_POWERMODE='off' # auto-suspend on/off
_SCHEMA="$_MISC"/schema.json # JSON schema for validation
_SCREENLOCK=0 # screenlock period of inactivity
_SCRIPT=$(basename "$0") # this script
_SETGEARY=0 # set email client Geary
_SETMONDAY=0 # set start of the week to Monday
_STRLEN=$(($(tput cols) - 5)) # width of field to print the log message; slightly less than terminal width to avoid string duplication
_TESTSERVER='duckduckgo.com' # test server's URL
_TIMER=1 # enable/disable timer
_USERAPPS="$HOME"/.local/share/applications # .desktop launchers for portable programs
_USERICONS="$HOME"/.local/share/icons # icons for portable programs
_USERID=$(id -u "$_USERNAME") # user login id
_USERPROFILE="$HOME"/.profile # user profile directory (requires system reboot)
_USERSESSION="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$_USERID/bus" # user session, useful for crontab operations
_VERSION="0.9.5" # script version
_VSIX=() # extensions for VS Codium
_WPEXTDIR='' # external directory to source wallpapers
_WPON=0 # enable custom wallpapers
_WPSRCDIR="$HOME"/Pictures/Wallpapers # local directory to source wallpapers
_XID="" # Firefox extension ID
# List of common compressed archive MIME types
_COMPRESSED_TYPES=(
"application/gzip"
"application/x-bzip2"
"application/x-xz"
"application/x-tar"
"application/zip"
"application/x-7z-compressed"
"application/x-rar-compressed"
"application/x-lzip"
"application/x-lzma"
"application/x-lzop"
"application/zstd"
)
# Display colours in ANSI format '\e[38;2;R;G;Bm'
_CHEAD=$'\e[38;2;72;185;199m'
_CINFO=$'\e[38;2;148;148;148m'
_COKAY=$'\e[38;2;78;154;10m'
_CSTOP=$'\e[38;2;255;50;50m'
_CWARN=$'\e[38;2;240;230;115m'
_CNONE=$'\e[0m'
# set readonly status 'after' variable initialisation
readonly _AVATARDSTDIR _BASEDIR _BASHRC _COMPRESSED_TYPES \
_CONFIGS_DIR _CONFIGSP_DIR _DFTRMPRFL _DISPLAY _DOTFILES \
_FFXADDONSURL _FFXAPPINI _FFXCHANNELFILE _FFXDIR _FFXPREFS \
_FFXUSEROVERRIDES _GTKBKMRK _GTKEXTS _LODIR _LOEXTSURL \
_LOGFILE _MISC _OS_RELEASE _OS _SCRIPT _STRLEN _USERAPPS \
_USERICONS _USERID _USERNAME _USERPROFILE _USERSESSION
}
__load_configs() {
# Description: Loads user configuration settings from a JSON file.
# Arguments: None.
_JSON_DATA=$(jq '.' "$_CONFIG_FILE") # Load the contents of the user configuration file to memory
# Define an associative array to map JSON keys to Bash variables
declare -A json_keys=(
["FIREFOX.\"ffx.cookies_to_keep\""]="_FFXCOOKIES"
["FIREFOX.\"ffx.extensions\""]="_FFXEXTS"
["MISCOPS.\"msc.automount_drives\""]="_AUTODRIVES"
["MISCOPS.\"msc.bookmarked_dirs\""]="_BOOKDIRS"
["MISCOPS.\"msc.crontab_cmds\""]="_CRONLINE"
["MISCOPS.\"msc.gnome_favourites\""]="_GFAVOURITES"
["MISCOPS.\"msc.gnome_calc_functions\""]="_GCALC"
["MISCOPS.\"msc.gnome_extensions\""]="_GNOMEXTS"
["MISCOPS.\"msc.gnome_extension_settings\""]="_GNOMEXTSET"
["MISCOPS.\"msc.gnome_settings\""]="_GSETTINGS"
["MISCOPS.\"msc.gnome_custom_settings\""]="_GCSETTINGS"
["PACKAGES.\"pkg.autostart\""]="_AUTOSTART"
["PACKAGES.\"pkg.portables\""]="_PORTABLES"
["PACKAGES.\"pkg.portables\".codium.extensions"]="_VSIX"
["PACKAGES.\"pkg.installers\""]="_INSTALLERS"
["PACKAGES.\"pkg.installers\".LibreOffice.extensions"]="_LOEXTS"
)
# Loop through each key in the associative array
for key in "${!json_keys[@]}"; do
var_name="${json_keys[$key]}"
if jq -e ".${key} | length > 0" <<<"$_JSON_DATA" >/dev/null; then
if [[ "$key" == "PACKAGES.\"pkg.portables\"" ]]; then
# Extract keys of portables where 'required' is 1
mapfile -t "$var_name" < <(echo "$_JSON_DATA" | jq -r '.PACKAGES."pkg.portables" | to_entries | map(select(.value.required == 1) | .key) | .[]')
elif [[ "$key" == "PACKAGES.\"pkg.installers\"" ]]; then
# Extract keys of installers where 'required' is 1
mapfile -t "$var_name" < <(echo "$_JSON_DATA" | jq -r '.PACKAGES."pkg.installers" | to_entries | map(select(.value.required == 1) | .key) | .[]')
# Extract subkeys and values for Gnome Settings
elif [[ "$key" == "MISCOPS.\"msc.gnome_settings\"" ]]; then
mapfile -t "$var_name" < <(echo "$_JSON_DATA" | jq -r '.MISCOPS."msc.gnome_settings" | to_entries | map(select(.value != null and .value != "")) | map(.key + ":" + (.value | tostring)) | .[]')
else
mapfile -t "$var_name" < <(echo "$_JSON_DATA" | jq -r ".${key}[]")
fi
fi
done
# Extract and assign values to global colour variables
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_CHEAD" '.GENERAL."gen.colour_head" // $default | select(. != "") // $default') && _CHEAD=$(set_colour "$usrVal" "$_CHEAD")
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_CINFO" '.GENERAL."gen.colour_info" // $default | select(. != "") // $default') && _CINFO=$(set_colour "$usrVal" "$_CINFO")
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_COKAY" '.GENERAL."gen.colour_okay" // $default | select(. != "") // $default') && _COKAY=$(set_colour "$usrVal" "$_COKAY")
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_CSTOP" '.GENERAL."gen.colour_stop" // $default | select(. != "") // $default') && _CSTOP=$(set_colour "$usrVal" "$_CSTOP")
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_CWARN" '.GENERAL."gen.colour_warn" // $default | select(. != "") // $default') && _CWARN=$(set_colour "$usrVal" "$_CWARN")
# Extract and assign other values to global variables
_LFBKPNO=$(echo "$_JSON_DATA" | jq -r --arg default "$_LFBKPNO" '.GENERAL."gen.logfile_backup_no" // $default | select(. != "") // $default')
_LOGFILEFMT=$(echo "$_JSON_DATA" | jq -r --arg default "$_LOGFILEFMT" '.GENERAL."gen.logfile_format" // $default | select(. != "") // $default')
_LOGFILEON=$(echo "$_JSON_DATA" | jq -r --arg default "$_LOGFILEON" '.GENERAL."gen.logfile_on" // $default | select(. != "") // $default')
_MAXWIN=$(echo "$_JSON_DATA" | jq -r --arg default "$_MAXWIN" '.GENERAL."gen.maximise_window" // $default | select(. != "") // $default')
_TIMER=$(echo "$_JSON_DATA" | jq -r --arg default "$_TIMER" '.GENERAL."gen.set_timer" // $default | select(. != "") // $default')
_TESTSERVER=$(echo "$_JSON_DATA" | jq -r --arg default "$_TESTSERVER" '.GENERAL."gen.test_server" // $default | select(. != "") // $default')
_AVATARON=$(echo "$_JSON_DATA" | jq -r --arg default "$_AVATARON" '.MISCOPS."msc.avatar_enable" // $default | select(. != "") // $default')
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_AVATARIMG" '.MISCOPS."msc.avatar_image" // $default | select(. != "") // $default')
[ -f "$_MISC/$usrVal" ] && _AVATARIMG="$usrVal"
_MSFONTS=$(echo "$_JSON_DATA" | jq -r --arg default "$_MSFONTS" '.MISCOPS."msc.ms_fonts" // $default | select(. != "") // $default')
_SETGEARY=$(echo "$_JSON_DATA" | jq -r --arg default "$_SETGEARY" '.MISCOPS."msc.set_geary" // $default | select(. != "") // $default')
_SETMONDAY=$(echo "$_JSON_DATA" | jq -r --arg default "$_SETMONDAY" '.MISCOPS."msc.week_starts_on_monday" // $default | select(. != "") // $default')
_OVERAMPLIFY=$(echo "$_JSON_DATA" | jq -r --arg default "$_OVERAMPLIFY" '.MISCOPS."msc.volume_overamplify" // $default | select(. != "") // $default')
_WPON=$(echo "$_JSON_DATA" | jq -r --arg default "$_WPON" '.MISCOPS."msc.wallpaper_on" // $default | select(. != "") // $default')
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_WPSRCDIR" '.MISCOPS."msc.wallpaper_src_dir" // $default | select(. != "") // $default')
[[ -d "$usrVal" ]] && _WPSRCDIR="$usrVal"
_WPEXTDIR=$(echo "$_JSON_DATA" | jq -r --arg default "$_WPEXTDIR" '.MISCOPS."msc.wallpaper_ext_dir" // $default | select(. != "") // $default')
_FFXCONFIG=$(echo "$_JSON_DATA" | jq -r --arg default "$_FFXCONFIG" '.FIREFOX."ffx.configure" // $default | select(. != "") // $default')
_FFXHOMEPAGE=$(echo "$_JSON_DATA" | jq -r --arg default "$_FFXHOMEPAGE" '.FIREFOX."ffx.set_homepage" // $default | select(. != "") // $default')
_FFXPRIVACY=$(echo "$_JSON_DATA" | jq -r --arg default "$_FFXPRIVACY" '.FIREFOX."ffx.set_privacy" // $default | select(. != "") // $default')
usrVal=$(echo "$_JSON_DATA" | jq -r --arg default "$_APPSDIR" '.PACKAGES."pkg.portables_dir" // $default | select(. != "") // $default')
[[ -d "$usrVal" ]] && _APPSDIR="$usrVal"
}
__logger_core() {
# Description: Main function for logging all processes executing in the shell both in the log file and on the terminal.
# Arguments: Two (2) - the level of log and the message to log.
if [[ $# -ne 2 ]]; then
return 3
else
declare -r lvl="${1}"
declare -r lvl_msg="${2}"
declare -r lvl_ts="$(LC_TIME=en_GB.UTF-8 date)"
declare lvl_console="1"
declare lvl_prefix=" "
declare lvl_nc="${_CNONE}"
case ${lvl} in
info)
declare -r lvl_str="INFO"
declare -r lvl_sym="•"
declare -r lvl_color="${_CINFO}"
_FILELOGGER_ACTIVE='true'
;;
success)
declare -r lvl_str="SUCCESS"
declare -r lvl_sym="✓"
declare -r lvl_color="${_COKAY}"
_FILELOGGER_ACTIVE='true'
;;
trace)
declare -r lvl_str="TRACE"
declare -r lvl_sym="~"
declare -r lvl_color="${_CINFO}"
_FILELOGGER_ACTIVE='true'
;;
warn | warning)
declare -r lvl_str="WARNING"
declare -r lvl_sym="!"
declare -r lvl_color="${_CWARN}"
_FILELOGGER_ACTIVE='true'
;;
error)
declare -r lvl_str="ERROR"
declare -r lvl_sym="✗"
declare -r lvl_color="${_CSTOP}"
_FILELOGGER_ACTIVE='true'
;;
progress)
declare -r lvl_str="PROGRESS"
declare -r lvl_sym="»"
declare -r lvl_color="${_CINFO}"
_FILELOGGER_ACTIVE='false'
lvl_console="0"
;;
prompt)
declare -r lvl_str="PROMPT"
declare -r lvl_sym="⸮"
declare -r lvl_color="${_CWARN}"
_FILELOGGER_ACTIVE='false'
lvl_console="2"
;;
stage)
declare -r lvl_str="STAGE"
declare -r lvl_sym="—"
declare -r lvl_color="${_CHEAD}"
_FILELOGGER_ACTIVE='true'
;;
esac
fi
if [[ $lvl_console -eq 1 ]]; then
printf "\r%s%s%s %s %s\n" "${lvl_color}" "${lvl_prefix}" "${lvl_sym}" "${lvl_msg}" "${lvl_nc}"
elif [[ $lvl_console -eq 2 ]]; then
printf "%s%s%s %s %s" "${lvl_color}" "${lvl_prefix}" "${lvl_sym}" "${lvl_msg}" "${lvl_nc}"
else
printf "\r%s%s%s %s %s\r" "${lvl_color}" "${lvl_prefix}" "${lvl_sym}" "${lvl_msg}" "${lvl_nc}" # progress reports on the same line ('\r')
fi
if [[ $_FILELOGGER_ACTIVE == 'true' && -f "$_LOGFILE"'.log' ]]; then
printf "%s %-25s %-10s %s\n" "${lvl_ts}" "${FUNCNAME[2]}" "[${lvl_str}]" "$lvl_msg" >>"$_LOGFILE"'.log'
fi
}
__make_configs() {
# Description: Writes default configuration parameters for this script to a compacted JSON file.
# Arguments: None.
# Rationale: As the core element of POPPI, user configuration file is a convenient playground for experimenting with different program settings.
# Change to base directory or return error code if it fails
cd "$_BASEDIR" || {
log_and_exit "${FUNCNAME[0]}: Failed to access '$_BASEDIR'." 4
}
# Make a new configuration file
if ! cat <<EOF >"$_BASEDIR"/configure.pop 2>&1 | log_trace "[MKC]"; then
{"GENERAL":{"gen.colour_head":"#48b9c7","gen.colour_info":"#949494","gen.colour_okay":"#4e9a0a","gen.colour_stop":"#ff3232","gen.colour_warn":"#f0e673","gen.logfile_backup_no":1,"gen.logfile_format":"Metric","gen.logfile_on":1,"gen.maximise_window":1,"gen.set_timer":1,"gen.test_server":"duckduckgo.com"},"FIREFOX":{"ffx.configure":0,"ffx.cookies_to_keep":[],"ffx.extensions":[],"ffx.set_homepage":0,"ffx.set_privacy":0},"PACKAGES":{"pkg.autostart":[],"pkg.installers":{"Calibre":{"required":0},"DConf-Editor":{"required":0},"FFMPEG_s":{"required":0},"FSearch":{"required":0},"LibreOffice":{"required":0,"extensions":[]},"lmsensors":{"required":0},"pdf.tocgen":{"required":0},"TeamViewer":{"required":0},"Virt-Manager":{"required":0}},"pkg.portables":{"audacity":{"required":0},"bleachbit":{"required":0},"cpux":{"required":0},"curl":{"required":0},"deadbeef":{"required":0},"hwprobe":{"required":0},"imagemagick":{"required":0},"inkscape":{"required":0},"jq":{"required":0},"keepassxc":{"required":0},"krita":{"required":0},"musescore":{"required":0},"neofetch":{"required":0},"qbittorrent":{"required":0},"smplayer":{"required":0},"sqlitebrowser":{"required":0},"styli.sh":{"required":0},"codium":{"required":0,"extensions":[]},"xnview":{"required":0},"xournalpp":{"required":0},"ytdlp":{"required":0}},"pkg.portables_dir":""},"MISCOPS":{"msc.automount_drives":[],"msc.avatar_enable":1,"msc.avatar_image":"popos.png","msc.bookmarked_dirs":[],"msc.crontab_cmds":[],"msc.gnome_calc_functions":[],"msc.gnome_custom_settings":[],"msc.gnome_extensions":[],"msc.gnome_extension_settings":[],"msc.gnome_favourites":[],"msc.gnome_settings":{"button_layout":"","button_position":"","capslock_as_extra_escape":0,"centre_windows_on_open":0,"check_alive_timeout":5000,"compose_key":"","font_scaling_factor":"1.0","font_terminal":"Fira Mono 12","font_ui":"Fira Sans Semi-Light 10","keyboard_languages":[],"launch_browser":"","launch_files":"","launch_settings":"","launch_terminal":"","set_wallpaper":"","show_seconds":0,"show_weekdays":0,"switch_workspace_down":"","switch_workspace_up":"","windows_close":"","windows_maximise":"","windows_minimise":""},"msc.ms_fonts":0,"msc.set_geary":0,"msc.volume_overamplify":0,"msc.wallpaper_on":0,"msc.wallpaper_src_dir":"","msc.wallpaper_ext_dir":"","msc.week_starts_on_monday":0}}
EOF
log_message "Creating user configuration file failed. Skipping ..." 3
fi
# Return to the previous directory
cd - >/dev/null || log_message "Could not return to previous directory." 4
}
__make_dirs() {
# Description: Creates the default script directories, if abscent.
# Arguments: None.
local dir Dirs
declare -a Dirs=('configs' 'configsp' 'dotfiles' 'firefox' 'icons' 'launchers' 'misc')
for dir in "${Dirs[@]}"; do
if [ ! -d "$_BASEDIR/data/$dir" ]; then
if ! mkdir -p "$_BASEDIR/data/$dir"; then
log_and_exit "${FUNCNAME[0]}: Failed to create directory '$_BASEDIR/data/$dir'." 5
else
log_message "Created directory '$_BASEDIR/data/$dir'" 1
fi
fi
done
}
all() {
# Description: Executes all operations in sequence.
# Arguments: None.
# Note: The order of functions may affect the performance of the script!
set_portables
set_installers
set_firefox
miscops
set_configs
set_gsettings
set_gnome_extensions
misc_set_avatar
set_favourites
}
bytes_to_human() {
# Description: Converts bytes to human-readable format. Used in `fetch_file()`.
# Arguments: One (1) - size of the downloaded file in bytes.
local bytes=$1
local size=('B' 'KB' 'MB' 'GB' 'TB')
local factor=1024
local count=0
while [[ $bytes -gt $factor ]]; do
bytes=$((bytes / factor))
count=$((count + 1))
done
echo "${bytes}${size[$count]}"
}
check_internet() {
if misc_connect_wifi; then
log_message "Checking connectivity ..." 5
if ping -c 4 -i 0.2 "$_TESTSERVER" >/dev/null 2>&1 | log_trace "[WEB]"; then
log_message "Connected to the Internet" 1
else
log_and_exit "${FUNCNAME[0]}: You are not connected to the Internet,
please check your network connection and try again." 8
fi
fi
}
check_user() {
if [[ $EUID -eq 0 ]] || [[ $EUID -ne $_USERID ]]; then
log_and_exit "${FUNCNAME[0]}: This script must be run by '$_USERNAME'." 9
else
log_message "Script run by '$_USERNAME'" 1
fi
}
display_usage() {
cat <<EOF
${_CINFO}
A set of post-installation methods developed for and tested on Pop!_OS 22.04 LTS.
${_CNONE}USAGE:${_CINFO}
./${_SCRIPT} -[abcdfghipvx] [CONFIGURATION_FILE]
${_CNONE}OPTIONS:${_CINFO}
-a, --all Download, install and set everything
-b, --bookmark Bookmark select directories to GNOME Files/Nautilus
-c, --connect Check and configure Wi-Fi connection
-d, --dock Set your favourite programs on the dock
-f, --set-firefox Configure options for Firefox
-g, --set-gsettings Set GNOME GSettings
-h, --help Display this help message
-i, --set-installers Install/update non-portable programs
-p, --set-portables Install/update portable programs
-v, --version Display version info
-x, --gnome-extensions Get and enable GNOME extensions
${_CNONE}DOCUMENTATION & BUGS:${_CINFO}
Report bugs to: https://github.com/simurqq/poppi/issues
Documentation: https://github.com/simurqq/poppi/README.MD
License: GPLv3
${_CNONE}
EOF
}
display_version() {
cat <<EOF
${_CINFO}Pop!_OS Post-Installation (POPPI) version $_VERSION
Copyright (C) 2024 Victor Quebec
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Victor Quebec for the benefit of the Pop!_OS community.${_CNONE}
EOF
}
fetch_file() {
# Description: The main function powered by cURL to download files.
# Arguments: Three (3)
# -- URL of the file to download [required]
# -- Name of the file to download [optional]
# -- Location of the file to download [optional]
local content_length curl_output fetch filename filesize http_code loc percentage prevsize url
# Check the number of arguments
if [ $# -eq 0 ] || [ $# -gt 3 ]; then
log_and_exit "${FUNCNAME[0]}: Wrong number of arguments: $#" 3
fi
# Check if ${1} is a valid URL
if ! curl -sfIL "${1}" >/dev/null; then
log_and_exit "${FUNCNAME[0]}: Not a valid URL: ${1}" 10
fi
# Assign arguments to variables
url=${1}
filename=${2:-$(basename "${1}")}
loc=${3:-$_BASEDIR}
# Options to download the file with custom or remote server name
if [ $# -eq 1 ]; then
fetch="curl -sfLC - --output-dir $loc '$url' -O"
else
fetch="curl -sfLC - --output-dir $loc '$url' -o $filename"
fi
curl_output=$(curl -sIL "$url" 2>&1)
content_length=$(grep -i "content-length: [^0]" <<<"$curl_output" | tail -1 | awk '{print $2}' | tr -d '\r')
content_length=${content_length:-0}
http_code=$(grep -i "http.*200" <<<"$curl_output" | cut -d' ' -f2)
# Update the download instead of re-downloading the file
if [ -f "$loc/$filename" ]; then
filesize=$(stat -c '%s' "$loc/$filename")
if [ "$filesize" -lt "$content_length" ]; then
log_message "Updating download for '$filename'..." 4 "$_STRLEN"
else
return 11
fi
fi
# Download the file, if it is on the remote server
if [ "$http_code" == 200 ]; then
filesize=1
prevsize=0
eval "$fetch" | while [ "$filesize" != "$content_length" ]; do
if [ -f "$loc/$filename" ]; then
filesize=$(stat -c '%s' "$loc/$filename")
if [ "$content_length" -gt 0 ]; then
percentage=$(printf "%d" "$((100 * filesize / content_length))")
log_message "Downloading '$filename' ... $(bytes_to_human "$filesize") ($percentage%)" 4 "$_STRLEN"
else
log_message "Downloading '$filename' ... $(bytes_to_human "$filesize")" 4 "$_STRLEN"
if [ "$prevsize" -eq "$filesize" ]; then
((k++))
if [ "$k" -eq 3 ]; then break; fi
fi
prevsize="$filesize"
fi
fi
sleep 1
done
else
log_message "No file on server? Skipping download for '$filename'..."
fi
}
ffx_extensions() {
# Description: Downloads and installs Firefox extensions.
# Arguments: None.
local counter ext_filename ext_title ext_total ext_URL extension tmpdir
if [ "${#_FFXEXTS[@]}" -eq 0 ]; then
log_message "No Firefox extensions to install. Skipping ..." 3
return 1
fi
ext_total="${#_FFXEXTS[@]}" # Total number of extensions to install
# Process the extensions
if mkdir -p "$_FFXDIR/$_FFXPRF"/extensions; then
tmpdir=$(mktemp -d)
# Read the list of installed extensions, if any.
if ! touch "$_FFXDIR/$_FFXPRF"/extensions/xpi_list.txt; then
log_message "Failed to create a list of extensions. Skipping ..." 3
else
IFS=$'\n' read -d '' -r -a _FFXEXTSLST <"$_FFXDIR/$_FFXPRF/extensions/xpi_list.txt"
fi
# Download extensions
for extension in "${_FFXEXTS[@]}"; do
((counter++))
# Check if extension is installed
if isInstalled "$extension"; then
log_message "Extension '$extension' is already installed. Skipping ..."
continue
fi
# Fetch extension's title from Mozilla Addons
ext_title=$(curl -sfL "$_FFXADDONSURL/$extension" | grep -oP '<h1 class=\"AddonTitle\"(?:\s[^>]*)?>\K.*?(?=<)')
if [ -z "$ext_title" ]; then
ext_title="$extension" # Get extension's title from the array, if fetch fails
fi
log_message "Downloading Firefox extension '$ext_title' ($counter/$ext_total) ..." 4 "$_STRLEN"
# Fetch extension's URL from Mozilla Addons
ext_URL=$(curl -sfL "$_FFXADDONSURL/$extension" | grep -Eo "(http|https)://[a-zA-Z0-9./?=_%:-]*.xpi")
if [ -z "$ext_URL" ]; then
log_message "Failed to fetch Firefox extension URL. Skipping ..." 3
continue
fi
ext_filename=$(basename "$ext_URL")
fetch_file "$ext_URL" "$ext_filename" "$tmpdir"
# Rename extensions by ID
log_message "Trying to determine ID for Firefox extension '$ext_title' ..."
_XID=$(ffx_xID "$tmpdir/$ext_filename")
#echo "$_XID"
if [ -z "$_XID" ]; then
cp "$tmpdir/$ext_filename" "$_BASEDIR"/data/firefox
log_message "Failed to determine ID for extension '$xpi'.
Please try to add the extension manually from '$_BASEDIR/data/firefox'..." 3
fi
# Move extension to user profile
if mv "$tmpdir/$ext_filename" "$_FFXDIR/$_FFXPRF/extensions/$_XID".xpi >/dev/null 2>&1; then
log_message "Extension '$_XID.xpi' moved to profile $_FFXPRF" 1
fi
printf "%s:%s\n" "$extension" "$_FFXDIR/$_FFXPRF/extensions/$_XID.xpi" >>"$_FFXDIR/$_FFXPRF"/extensions/xpi_list.txt
done
else
log_message "Failed to create extensions directory for '$_FFXPRF'"
fi
}
ffx_permissions() {
# Description: Uses a Python script to interact with the SQLite database
# Arguments: None.
local dbfile url
if ! set_dependency python3; then
log_message "Failed to locate Python on this system. Skipping ..." 3
return 15
fi
if [ ${#_FFXCOOKIES[@]} -ne 0 ]; then
dbfile="permissions.sqlite"
python3 <<EOF
import sqlite3
conn = sqlite3.connect('$dbfile') # connect to the SQLite database
c = conn.cursor() # create a cursor object
# Required by Firefox
c.execute("PRAGMA user_version = 12;")
c.execute("PRAGMA page_size = 32768;")
c.execute("VACUUM;")
# Create table moz_hosts
c.execute('''CREATE TABLE IF NOT EXISTS moz_hosts
(id INTEGER PRIMARY KEY,
host TEXT,
type TEXT,
permission INTEGER,
expireType INTEGER,
expireTime INTEGER,
modificationTime INTEGER,
isInBrowserElement INTEGER)''')
# Create table moz_perms
c.execute('''CREATE TABLE IF NOT EXISTS moz_perms
(id INTEGER PRIMARY KEY,
origin TEXT UNIQUE,
type TEXT,
permission INTEGER,
expireType INTEGER,
expireTime INTEGER,
modificationTime INTEGER)''')
# Insert a row of data
$(for url in "${_FFXCOOKIES[@]}"; do
[ -n "$url" ] && echo "if not c.execute(\"SELECT 1 FROM moz_perms WHERE origin = ?\", (\"$url\",)).fetchone():
c.execute(\"INSERT INTO moz_perms (origin, type, permission, expireType, expireTime, modificationTime) VALUES (?, 'cookie', 1, 0, 0, $_EXEC_START)\", (\"$url\",))"
done)
conn.commit() # save (commit) the changes
conn.close() # close the connection
EOF
fi
if [ -f ./permissions.sqlite ]; then
if [ -d "$_FFXDIR/$_FFXPRF" ]; then
mv ./permissions.sqlite "$_FFXDIR/$_FFXPRF" # move the newly created file to Firefox profile.
else
log_message "Failed to set cookies: directory '$_FFXDIR/$_FFXPRF' unavailable" 3
return 12
fi
else
log_message "Failed to set cookies: file 'permissions.sqlite' unavailable" 3
fi
}
ffx_profile() {
# Description: Identifies Firefox default profile.
# Arguments: None.
local profile
profile="$_FFXDIR"/profiles.ini
log_message "[+] Identifying Firefox profile ..." 5
if [ -f "$profile" ]; then # method #1
_FFXPRF=$(grep -oE "^(Default|Path)=.*\-$_FFXCHANNEL$" "$profile" | cut -d= -f2 | head -1)
[ -n "$_FFXPRF" ] && log_message "Firefox default profile: $_FFXPRF" 1
elif [ -d "$_FFXDIR" ]; then
# Identify profile directory, method #2.
# useful when script re-launched after first run in the background,
# when Firefox doesn't append 'Default=1' to the [Profile0] section of 'profiles.ini' (as in method #1).
_FFXPRF=$(basename "$(find "$_FFXDIR" -maxdepth 1 -type d -name "*default*")")
[ -n "$_FFXPRF" ] && log_message "Firefox default profile for user '$_USERNAME': $_FFXPRF" 1
elif command -v firefox >/dev/null; then # both attempts failed, create a new profile
firefox -CreateProfile "default-$_FFXCHANNEL" 2>&1 | log_trace "[FFX]" && sleep 3
_FFXPRF=$(basename "$(find "$_FFXDIR" -maxdepth 1 -type d -name "*default*")")
[ -n "$_FFXPRF" ] && log_message "Firefox default profile for user '$_USERNAME' created: $_FFXPRF" 1
else
log_message "Failed to identify default Firefox profile. Skipping ..." 3
return 5
fi
# Run Firefox in the background to populate the profile directory with necessary files.
firefox --headless -P "default-$_FFXCHANNEL" 2>&1 | log_trace "[FFX]" &
sleep 3
killproc firefox Firefox
}
ffx_xID() {
# Description: Determines Firefox extension ID.
# Arguments: One (1) - extension file (*.xpi).
# alternative option to retrieve ID:
# xid=$(unzip -p "$xpi" META-INF/mozilla.rsa | openssl pkcs7 -print -inform der | grep "subject.*CN\=" | head -1) && xid=${xid##*\=}
local xid xpi
if [ $# -ne 1 ]; then
log_message "${FUNCTION[0]}: Wrong number of arguments: $#" 3
return 3
fi
xpi="${1}"
# Fetch the ID from extension's manifest
keys=('applications' 'browser_specific_settings')
for key in "${keys[@]}"; do
xid=$(unzip -p "$xpi" manifest.json | jq -r '.'"$key"'.gecko.id' 2>&1)
if [ "$xid" != 'null' ]; then
echo "$xid"
return 0
fi
done
# Still trying ...
if unzip -l "$xpi" | grep -q "mozilla.rsa"; then
xid=$(unzip -p "$xpi" META-INF/mozilla.rsa | openssl asn1parse -inform DER | grep -A 1 commonName | grep -o '{.*}')
if [ "$xid" != 'null' ]; then
xid=$(trimex "$xid")
echo "$xid"
return 0
fi
fi
# Final attempt
if unzip -l "$xpi" | grep -q "cose.sig"; then
xid=$(unzip -p "$xpi" META-INF/cose.sig | strings | grep '0Y0')
if [ "$xid" != 'null' ]; then
xid=$(trimex "$xid")
echo "$xid"
return 0
fi
fi
return 13
}
finale() {
# Description: Prints out a message upon the completition of all the operations
# Arguments: None.
local time
if [[ "$_TIMER" = 1 ]]; then
time=$(timer)
log_message "POPPI took $time to complete all the assignments. See you next time!" 1 "$_STRLEN"
else
log_message "All assignments complete. See you next time!" 1 "$_STRLEN"
fi
}
headline() {
# Description: Calculates the terminal workspace and prints out the headline with script's version number
# Arguments: None.
local char columns dash_size min_cols msg1 msg2 msg_size
columns=$(tput cols)
min_cols=22
msg1="POPPI v${_VERSION}"
msg2="::::: $msg1 :::::"
msg_size=$((${#msg1} + 3)) # incl. spaces on both sides of text and versions 10+, i.e., extra 3 chars
dif=$((columns - msg_size))
dash_size=$((dif / 2))
[ $(("$dif" % 2)) -gt 0 ] && dash_size=$((dash_size + 1)) # normalise dash size when 'dif' is an odd number
char=":"
if [[ columns -le ${min_cols} ]]; then
printf '%s\n\n' "${_CINFO}${msg2}${_CNONE}"
else
printf "${_CINFO}%0${dash_size}s" | tr " " ${char}
printf '%s' " ${msg1} "
printf "%0${dash_size}s" | tr " " ${char}
printf '\n\n%s' "${_CNONE}"
fi
}
isExternalDir() {
# Description: Checks if the directory is on external drive.
# Argument: One (1) - path to the directory represented as string.
# Rationale: If the directory to be bookmarked is on the external drive
# (see: _AUTODRIVES and "msc.automount_drives" in the user configuration file),
# which has not been mounted, [ -d "$path" ] will simply fail.
# Therefore, it is necessary to do this check before the directory is passed to `process_path()`.
# It's also imperative that bookmarking of directories takes place after (!) mounting external drives.
local dir drive fstab
[ $# != 1 ] && return 3
dir="${1}"
fstab=$(</etc/fstab)
for drive in "${_AUTODRIVES[@]}"; do
if grep -iq "$drive" <<<"$dir" >/dev/null 2>&1 &&
grep -iq "$drive" <<<"$fstab" >/dev/null 2>&1; then
return 0
fi
done
return 13
}
isInstalled() {
# Description: Checks if Firefox exetnsion is installed.
# Arguments: One (1) - exetnsion (XPI) file's name.
local arg xTitle xFile
arg="${1}"
for ext in "${_FFXEXTSLST[@]}"; do
xTitle="${ext%%\:*}"
xFile="${ext##*\:}"
if [ "$xTitle" == "$arg" ] && [ -f "$xFile" ]; then
return 0
fi
done
return 13
}
killproc() {
# Description: Kills running processes when necessary for certain operations; used in `set_firefox()` and `set_installers()`.
# Arguments: Two (2)
# -- executable to kill
# -- executable title
local prc title
[ $# -eq 0 ] || [ $# -gt 2 ] && return 3
prc="${1}"
title="${2:-1}"
while
prc=$(pidof "$prc")
[ -n "$prc" ]
do
if kill -9 "$prc" >/dev/null; then
sleep 3
log_message "Process $prc suspended ($title)" 1
return 0
fi
done
return 13
}
libreoffice_extensions() {
# Description: Downloads and installs LibreOffice extensions using LO's native facilities.
# Arguments: None.
local counter dir extension ext_total gh_desc gh_repo loextension logstr oxtURL sfrurl tmpdir
if [ "${#_LOEXTS[@]}" -eq 0 ]; then
return 1
fi
if ! mkdir -p "$_LODIR"; then
log_message "Failed to create directory '$_LODIR'. Skipping ..." 3
return 5
fi
log_message "[+] Setting up LibreOffice extensions ..." 5
# Check and install extensions from the local LibreOffice directory, if available
if find "$_LODIR" -maxdepth 1 -type f -name '*.oxt' >/dev/null 2>&1; then
for loextension in "$_LODIR"/*.oxt; do
extension=$(basename "$loextension")
# Check if extension is already installed
if unopkg list | grep -q "$extension"; then
log_message "Extension '$extension' already installed and activated. Skipping ..."
continue
fi
log_message "[+] Installing '$loextension' ..." 5
unopkg add -s -f "$loextension" 2>&1 | log_trace "[LOX]"
# Unlock LO extension installer
if ! killproc unopkg; then
lockfile="$HOME"/.config/libreoffice/4/.lock
[ -f "$lockfile" ] && rm "$lockfile" && log_message "Lock file (unopkg) removed"
fi
done
fi
for loextension in "${_LOEXTS[@]}"; do
log_message "[+] Installing '$loextension' ..." 5
((counter++))
ext_total="${#_LOEXTS[@]}"
case "$loextension" in
theme-sifr)
# Declare local variables
local dir gh_desc gh_repo sfrurl tmpdir
if which libreoffice >/dev/null 2>&1; then
gh_repo="libreoffice-style-sifr"
gh_desc="Icon Theme Sifr"
log_message "Getting the latest version of ${gh_desc}..."
sfrurl="https://github.com/rizmut/$gh_repo/archive/master.tar.gz"
# Check if archive file available locally
if [ ! -f "$_LODIR/$gh_repo.tar.gz" ]; then
fetch_file $sfrurl "$gh_repo.tar.gz" "$_LODIR" && log_message "$gh_desc downloaded" 1
fi
# Check the file's integrity
if tar -tf "$_LODIR/$gh_repo.tar.gz" &>/dev/null; then
log_message "Unpacking archive ..."
tar -xzf "$_LODIR/$gh_repo.tar.gz" -C "$_LODIR" 2>&1 | log_trace "[MSC]"
log_message "Deleting old $gh_desc ..."
sudo rm -f "/usr/share/libreoffice/share/config/images_sifr.zip" 2>&1 | log_trace "[MSC]"
sudo rm -f "/usr/share/libreoffice/share/config/images_sifr_dark.zip" 2>&1 | log_trace "[MSC]"
sudo rm -f "/usr/share/libreoffice/share/config/images_sifr_dark_svg.zip" 2>&1 | log_trace "[MSC]"
sudo rm -f "/usr/share/libreoffice/share/config/images_sifr_svg.zip" 2>&1 | log_trace "[MSC]"
log_message "Installing $gh_desc ..."
sudo mkdir -p "/usr/share/libreoffice/share/config" 2>&1 | log_trace "[MSC]"
sudo cp -R "$_LODIR/$gh_repo-master/build/images_sifr.zip" \
"/usr/share/libreoffice/share/config" 2>&1 | log_trace "[MSC]"
sudo cp -R "$_LODIR/$gh_repo-master/build/images_sifr_dark.zip" \
"/usr/share/libreoffice/share/config" 2>&1 | log_trace "[MSC]"
sudo cp -R "$_LODIR/$gh_repo-master/build/images_sifr_dark_svg.zip" \
"/usr/share/libreoffice/share/config" 2>&1 | log_trace "[MSC]"
sudo cp -R "$_LODIR/$gh_repo-master/build/images_sifr_svg.zip" \
"/usr/share/libreoffice/share/config" 2>&1 | log_trace "[MSC]"
for dir in \
/usr/lib64/libreoffice/share/config \
/usr/lib/libreoffice/share/config \
/usr/local/lib/libreoffice/share/config \
/opt/libreoffice*/share/config; do
[ -d "$dir" ] || continue
sudo ln -sf "/usr/share/libreoffice/share/config/images_sifr.zip" "$dir" 2>&1 | log_trace "[MSC]"
sudo ln -sf "/usr/share/libreoffice/share/config/images_sifr_dark.zip" "$dir" 2>&1 | log_trace "[MSC]"
sudo ln -sf "/usr/share/libreoffice/share/config/images_sifr_svg.zip" "$dir" 2>&1 | log_trace "[MSC]"
sudo ln -sf "/usr/share/libreoffice/share/config/images_sifr_dark_svg.zip" "$dir" 2>&1 | log_trace "[MSC]"
done
log_message "Clearing cache ..."
rm -rf "$_LODIR/$gh_repo-master" 2>&1 | log_trace "[MSC]"
log_message "Sifr theme for LibreOffice set" 1
else
log_message "File '$gh_repo.tar.gz' not found or damaged.
Please download again" 3
fi
else
log_message "Failed to locate LibreOffice on this computer. Skipping ..." 3
fi
;;
*)
# Download and install other extensions
ext_title=$(curl -sfL "$_LOEXTSURL/$loextension" | awk -v RS='</h1>' '{gsub(/.*>/, ""); print $1}' | head -1)
logstr="Downloading LibreOffice extension '$ext_title' ($counter/$ext_total) ..."
log_message "$logstr" 4 "$_STRLEN"
oxtURL=$(curl -sfL "$_LOEXTSURL/$loextension" | grep -o "assets.*oxt" | head -1)
oxtURL=https://extensions.libreoffice.org/"$oxtURL"
extension=$(basename "$oxtURL")
# Check if extension is already installed
if unopkg list | grep -q "$extension"; then
log_message "Extension '$ext_title' already installed and activated. Skipping ..."
continue
fi
if [ -z "$oxtURL" ]; then
log_message "Extension '$ext_title' not found on remote server. Skipping ..." 3
continue
fi
if fetch_file "$oxtURL" "$extension" "$_LODIR"; then
log_message "Extension $ext_title downloaded" 1 "$_STRLEN"
else
log_message "Failed to download extension $ext_title. Skipping ..." 3 "$_STRLEN"
continue
fi
# Unlock LO extension installer
if ! killproc unopkg; then
lockfile="$HOME"/.config/libreoffice/4/.lock
[ -f "$lockfile" ] && rm "$lockfile" && log_message "Lock file (unopkg) removed"
fi
unopkg add -s -f "$_LODIR/$extension" 2>&1 | log_trace "[LOX]"
if [ "${PIPESTATUS[0]}" -eq 0 ]; then
log_message "Extension $ext_title installed" 1 "$_STRLEN"
else
log_message "Failed to install extension $ext_title" 3 "$_STRLEN"
continue
fi
;;
esac
done
}