-
Notifications
You must be signed in to change notification settings - Fork 3
/
wicens.sh
4309 lines (3742 loc) · 161 KB
/
wicens.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
#!/bin/sh
################################################################################
# _ #
# _ __ (_)_____ ___ ____ _____ #
# | | /| / // // ___// _ \ / __ \ / ___/ #
# | |/ |/ // // /__ / __// / / /(__ ) #
# |__/|__//_/ \___/ \___//_/ /_//____/ #
# #
# WAN IP Change Email Notification Script #
# #
################################################################################
# Thanks to all who contribute(d) at SNBforums, pieces of your code are here ;)
# written by maverickcdn
# github.com/maverickcdn/wicens
# SNBforums thread https://www.snbforums.com/threads/wicens-wan-ip-change-email-notification-script.69294/
# found in amtm thanks to @thelonelycoder
# shellcheck disable=SC2039,SC2183,SC2104,SC1090,SC2154,SC2034
# hex expr menuprintf continuefunc constantsource unassvar unusedvar
[ "$1" = 'debug' ] && shift && set -x
export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH"
start_time="$(awk '{print $1}' < /proc/uptime)" # for calc menu load time in ms
# START ###############################################################################################################
script_version='4.02'
script_ver_date='June 14 2024'
current_core_config='4.0' # version of core config (F_default_update_create)
current_user_config='4.0' # version of user config (F_default_user_create)
script_name="$(basename "$0")"
script_name_full="/jffs/scripts/$script_name"
script_dir='/jffs/addons/wicens'
script_git_src='https://raw.githubusercontent.com/maverickcdn/wicens/master/'
run_option="$1"
[ -z "$1" ] && run_option='tty' # used to show tty vs cron/test/wancall/fwupdate/send run
config_src="${script_dir}/wicens_user_config.wic" # user settings
update_src="${script_dir}/wicens_update_conf.wic" # core config file
history_src="${script_dir}/wicens_wan_history.wic" # historical wan ip change file
script_backup_file="${script_dir}/wicens_user_config.backup" # user settings backup
history_src_backup="${script_dir}/wicens_history_src.backup" # historical wan ip change file backup
reboot_email='/tmp/wicens_rebootmail.txt' # reboot notification mail text
fw_email='/tmp/wicens_fwmail.txt' # firmware update notification mail text
update_email='/tmp/wicens_updatemail.txt' # script update notification mail text
wanip_email='/tmp/wicens_wanipemail.txt' # wanip change notification mail text
mail_log="${script_dir}/wicens_email.log" # log file for sendmail/curl
script_lock="/tmp/wicens_lock.$run_option" # script temp lock file by argument
internet_lock="/tmp/wicens_internetlock.$run_option" # internet check lock, prevents killing processes waiting for internet
wicens_send_retry='/tmp/wicens_send.retry' # retry count file for send option
wicens_send_copy='/tmp/wicens_user_email.txt' # backup of email for send option in retries
wicens_update_retry='/tmp/wicens_update.retry' # retry count file for script update notification
wicens_fw_retry='/tmp/wicens_fw.retry' # retry count file for fw update notification
wicens_wanip_retry='/tmp/wicens_wanip.retry' # retry count file for wan ip change notification
wicens_reboot_retry='/tmp/wicens_reboot.retry' # retry count file for reboot notification
cred_loc="${script_dir}/.wicens_cred.enc"
cred_loc_bak="${cred_loc}bak"
amtm_email_conf='/jffs/addons/amtm/mail/email.conf'
amtm_cred_loc='/jffs/addons/amtm/mail/emailpw.enc'
amtm_d='L3Vzci9zYmluL29wZW5zc2wgMj4vZGV2L251bGwgYWVzLTI1Ni1jYmMgLXBia2RmMiAtZCAtaW4gL2pmZnMvYWRkb25zL2FtdG0vbWFpbC9lbWFpbHB3LmVuYyAtcGFzcyBwYXNzOmRpdGJhYm90LGlzb2kK'
user_d='L3Vzci9zYmluL29wZW5zc2wgZW5jIC1tZCBzaGE1MTIgLXBia2RmMiAtYWVzLTI1Ni1jYmMgLWQgLWEgLXBhc3MgcGFzczoiJChGX252cmFtIGJvYXJkbnVtIHwgL2Jpbi9zZWQgcy86Ly9nKSIK'
user_e='L3Vzci9zYmluL29wZW5zc2wgZW5jIC1tZCBzaGE1MTIgLXBia2RmMiAtYWVzLTI1Ni1jYmMgLWEgLXNhbHQgLXBhc3MgcGFzczoiJChGX252cmFtIGJvYXJkbnVtIHwgL2Jpbi9zZWQgcy86Ly9nKSIgfCB0ciAtZCAiXG4iCg=='
ip_regex='([0-9]{1,3}[\.]){3}[0-9]{1,3}'
current_wan_ip=''
building_settings=0
test_mode=0
sample_email=0
from_menu=0
# in script and user configs 0=disabled 1=enabled
# SCRIPT MISC #########################################################################################################
F_git_get() {
case "$1" in
'file') curl -fsL --retry 2 --retry-delay 3 --connect-timeout 3 ${script_git_src}${script_name} ;;
'download') if curl -fsL --retry 2 --retry-delay 3 --connect-timeout 3 ${script_git_src}${script_name} -o /jffs/scripts/wicens.sh ; then F_chmod "$script_name_full" ; else return 1 ; fi ;;
'changelog') curl -fsL --retry 2 --retry-delay 3 --connect-timeout 3 ${script_git_src}CHANGELOG.md ;;
'hotfix') F_git_get changelog | sed -n "/^## $git_version/,/^## /p" | head -n -1 | sed 's/## //g' ;;
'update') F_git_get changelog | sed -n "/^## $git_version/,/^## $script_version/p" | head -n -1 | sed 's/## //g' ;;
esac
return 0
} # git_get
F_ctrlc() { F_terminal_check_fail "Script interrupted..." ; F_clean_exit ;} # CTRL+C catch with trap
trap F_ctrlc INT HUP # trap ctrl+c exit clean
F_replace_var() { sed -i "1,/${1}=.*/{s/${1}=.*/${1}=\'${2}\'/;}" "$3" ;} # 1=var to change 2=new var string 3=file
F_chmod() { [ ! -x "$1" ] && chmod a+rx "$1" ;} # file permissions
F_crlf() { if grep -q $'\x0D' "$1" 2> /dev/null ; then dos2unix "$1" && F_terminal_check_ok "$(F_printfstr "$1" | awk -F/ '{print $(NF)}') contained CRLF, executed dos2unix" ; fi ;} # crlf
F_nvram() { nvram get "$1" ;}
F_printf() { printf '%b\n' "$1" ;} # printf recognize escape strings
F_printfstr() { printf '%s\n' "$1" ;} # printf raw string
F_date() {
case "$1" in
'r') date -R ;;
's') date +'%s' ;;
'f') date +'%b %d %Y %T' ;;
esac
}
# TERMINAL/LOGGING ####################################################################################################
F_terminal_show() { F_printf "$tTERMHASH $1" ;} # [~~~~]
F_terminal_padding() { F_printfstr '' ;} # blank line
F_terminal_separator() { F_printfstr '--------------------------------------------------------------------------------' ;} # 80 column
F_email_seperator() { F_printfstr '----------------------------------------------------------' ;} # 58 column
F_terminal_erase() { printf '%b' "$tBACK$tERASE" ;} # erase previous line
F_terminal_entry() { printf '%b' "$tTERMHASH $1" ;} # [~~~~] no new line
F_terminal_check() { printf '%b' "$tCHECK $1" ;} # [WAIT] no new line
F_terminal_check_ok() { F_printf "\r${tERASE}${tCHECKOK} $1" ;} # [ OK ]
F_terminal_check_fail() { F_printf "\r${tERASE}${tCHECKFAIL} $1" ;} # [FAIL]
F_term_waitdel() { printf '%b' "${tERASE}${tCHECK} $1 \r" ;} # used in countdowns (line re-write)
F_status_grn() { F_terminal_show "$(printf "%s%s|\n" "$1" "$(printf '%*s' "$((35 - ${#1}))" | tr ' ' '-')") ${tGRN}${2}${tCLR}" ;} # status enabled custom text
F_status_enabled(){ F_terminal_show "$(printf "%s%s|\n" "$1" "$(printf '%*s' "$((35 - ${#1}))" | tr ' ' '-')") ${tGRN}Enabled${tCLR}" ;} # status Enabled
F_status_pass(){ F_terminal_show "$(printf "%s%s|\n" "$1" "$(printf '%*s' "$((35 - ${#1}))" | tr ' ' '-')") ${tGRN}Passed${tCLR}" ;} # status pass
F_status_fail(){ F_terminal_show "$(printf "%s%s|\n" "$1" "$(printf '%*s' "$((35 - ${#1}))" | tr ' ' '-')") ${tRED}Failed${tCLR}" ;} # status fail
F_status_disabled(){ F_terminal_show "$(printf "%s%s|\n" "$1" "$(printf '%*s' "$((35 - ${#1}))" | tr ' ' '-')") ${tRED}Disabled${tCLR}" ;} # status Disabled
F_menu_enabled() { F_terminal_show "$1 ${tGRN}Enabled${tCLR}" ;} # menu enabled
F_menu_disabled() { F_terminal_show "$1 ${tRED}Disabled${tCLR}" ;} # menu disabled
F_edit() { F_terminal_show "$1 ${tGRN}${2}${tCLR}" ;} # edit menu
F_terminal_warning() { printf '%b%48s\n%48s\n%48s%b\n\n' "$tRED" "#################" "# WARNING #" "#################" "$tCLR" ;} # terminal warning
F_fail_entry() { F_terminal_check_fail "Invalid entry, any key to retry" && read -rsn1 && F_terminal_erase && continue ;} # terminal input invalid entry
F_log() { F_printfstr "${run_option} : $1" | logger -t "wicens[$$]" ;} # logging
F_log_show() { F_log "$1" ; F_terminal_show "$1" ;} # log and print formatted
F_log_terminal_ok() { F_terminal_check_ok "$1" ; F_log "$1" ;} # log [ OK ]
F_log_terminal_fail() { F_terminal_check_fail "$1" ; F_log "$1" ;} # log [FAIL]
#requires being passed a line # for head to terminate on
F_terminal_entry_header() {
cut_line=$((${1} + 2)) # add to passed line to account for top 2 lines of status page
F_terminal_header
F_status | head -n "$cut_line" | tail -n "$((cut_line - 2))" # remove top two lines of status page
F_terminal_separator
F_terminal_padding
} # terminal_entry_header
F_terminal_color() {
case "$opt_color" in
0)
tGRN=''
tRED=''
tPUR=''
tYEL=''
tCLR=''
;;
1)
tGRN="\033[1;32m"
tRED="\033[1;31m"
tPUR="\033[1;95m"
tYEL="\033[1;93m"
tCLR="\033[0m"
;;
esac
tERASE="\033[2K"
tBACK="\033[1A"
tCHECK="[${tYEL}WAIT${tCLR}]"
tCHECKOK="[${tGRN} OK ${tCLR}]"
tCHECKFAIL="[${tRED}FAIL${tCLR}]"
tTERMHASH="[${tPUR}~~~~${tCLR}]"
} # terminal_color
F_confirm() {
F_terminal_padding
while true ; do
case "$1" in
'correct') F_terminal_check "Is ${tGRN}${2}${tCLR} correct? | Y||y or N||n " ;;
*) F_terminal_check "$1 | Y||y or N||n " ;;
esac
read -rsn1 ynentry
case "$ynentry" in
Y|y) return 0 ;;
N|n) return 1 ;;
E|e) F_terminal_check_fail "Cancelled, exit selected" ; F_menu_exit ;;
*) F_fail_entry ;;
esac
break
done
} # confirm
F_wait() {
F_terminal_padding
wait_time="$1"
while [ "$wait_time" -ne '0' ] ; do
F_term_waitdel "Loading menu in $wait_time secs... any key to skip "
wait_time=$((wait_time - 1))
waiting=zzz
read -rsn1 -t1 waiting
[ ${#waiting} -le 1 ] && break
done
} # menu_wait
# MISC ################################################################################################################
F_random_num() {
if [ -z "$1" ] ; then random_max='30' ; else random_max="$1" ; fi
# pull uptime (w/ millisecond) as seed # for rng
awk -v min=1 -v max="$random_max" -v seed="$(awk '{print $1}' < /proc/uptime | tr -d '.')" 'BEGIN{srand(seed); print int(min+rand()*(max-min+1))}'
} # random_num
F_private_ip() {
# RFC 1918 defines private IP address ranges
# 10.0.0.0/8
# 172.16.0.0/12
# 192.168.0.0/16
# Also including 169.254.0.0/16 (used for Automatic Private IP Addressing)
grep -Eq '^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^169\.254\.'
} # private_ip as per ChatGPT
F_cgnat_ip(){
# test if the input is a RFC 6598 address for CGNAT
grep -Eq '^100\.(6[4-9]|[7-9][0-9]|1[0-1][0-9]|12[0-7])\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
} # cgnat as per ChatGPT
F_uptime() {
router_uptime="$(awk '{print $1}' < /proc/uptime | cut -d'.' -f1)"
uptime_pretty="$(printf '%3dd %2dh %2dm %2dsec\n' $((router_uptime/86400)) $((router_uptime%86400/3600)) $((router_uptime%3600/60)) $((router_uptime%60)))"
} # uptime
# SCRIPT CONTROL ######################################################################################################
F_clean_exit() {
# save last seen uptime
F_uptime
[ "$router_uptime" -gt 1200 ] && [ -f "$update_src" ] && F_replace_var router_reboot_uptime "$router_uptime" "$update_src" # wait 20mins before saving uptime, if reboot notify enabled need to capture last known uptime
# pre lock removal restarts
case "$1" in
'reload') exec sh "$script_name_full" reload ;;
esac
# remove all potential locks
for lock_to_remove in "$script_lock" "$ntp_lock" "$internet_lock" ; do
[ -f "$lock_to_remove" ] && rm -f "$lock_to_remove" && F_terminal_check_ok "Removed $lock_to_remove lock file"
done
if [ ! -f "$script_lock" ] ; then
F_printf "[${tGRN}EXIT${tCLR}] ${tYEL}Goodbye :) $tCLR"
F_terminal_padding
else
if [ "$$" != "$(sed -n '2p' "$script_lock")" ] ; then
F_log_terminal_ok "Lock file still present but not from this process..."
else
F_log_terminal_fail "Critical error - Failed to remove lock file"
exit 1
fi
fi
case "$1" in
'reset') exec sh "$script_name_full" ;; # restart the script completely
'fail') exit 1 ;; # for forwarder calls from other scripts
esac
exit 0
} # clean_exit
F_menu_exit() {
F_terminal_padding
F_terminal_check "Any key to return to the Main Menu - E||e to Exit"
read -rsn1 exitwait
case "$exitwait" in
E|e) F_terminal_check_ok "Exiting." ; F_clean_exit ;;
*) F_clean_exit reload ;;
esac
} # menu_exit
# FIRMWARE CHECK ######################################################################################################
F_firmware_check() {
F_fw_valid_version() {
if [ -z "$build_full" ] ; then
F_terminal_header
F_log_terminal_fail "Could not determine your firmware version from nvram"
rm -r "$script_dir" 2> /dev/null
F_clean_exit
fi
[ "$(F_nvram firmver)" = '3.0.0.6' ] && return 0
case "$build_no" in
'386'|'388') return 0 ;;
'384') [ "$build_sub" -ge 15 ] && return 0 ;;
'374') [ "$john_sub" -ge 48 ] && return 0 ;;
*)
F_terminal_header
F_log_terminal_fail "Sorry this version of firmware is not compatible, please update to 384.15 or newer, or 374 LTS release 48 or newer to utilize this script"
F_terminal_padding
rm -r "$script_dir" 2> /dev/null
F_clean_exit
;;
esac
} # fw_valid_version
F_fw_write() {
pulled_device_name="$(F_nvram lan_hostname)"
pulled_lan_name="$(F_nvram lan_domain)"
device_model="$(F_nvram odmpid)"
lan_addr="$(F_nvram lan_ipaddr)"
[ -z "$device_model" ] && device_model="$(F_nvram productid)"
case "$build_no" in
'374') F_replace_var fw_build_sub "$john_sub" "$update_src" ;;
*) F_replace_var fw_build_sub "$build_sub" "$update_src" ;;
esac
F_replace_var fw_build_full "${build_no}.${build_sub}_${build_extend}" "$update_src"
F_replace_var fw_pulled_device_name "$pulled_device_name" "$update_src"
F_replace_var fw_pulled_lan_name "$pulled_lan_name" "$update_src"
F_replace_var fw_device_model "$device_model" "$update_src"
F_replace_var fw_build_no "$build_no" "$update_src"
F_replace_var fw_build_extend "$build_extend" "$update_src"
F_replace_var fw_lan_addr "$lan_addr" "$update_src"
[ "$config_updated" != "1" ] && source "$update_src"
case "$1" in
'fwupdate')
from_menu=1
F_terminal_header
F_log_show "Found new firmware version installed on router"
F_log_terminal_ok "core config v${update_settings_version} updated for new fw version ${build_full}_${build_extend}"
F_replace_var fw_notify_state 0 "$config_src" # reset Email notification after upgrading
F_wait 10
F_clean_exit reload
;;
*)
case "$config_updated" in
1)
# for integrity_check updates
source "$update_src"
F_log_terminal_ok "core config v${update_settings_version} updated with new router firmware information"
;;
*)
from_menu=1
F_terminal_header
F_printf "[ ${tGRN}HI${tCLR} ] ${tYEL}===== Welcome to wicens the WAN IP change Email notification script =====${tCLR}"
F_terminal_padding
F_terminal_check_ok "Created $script_dir directory"
F_terminal_check_ok "Created default user config v${current_user_config} for script v$script_version in $script_dir"
F_terminal_check_ok "Created default core config v$current_core_config for script v$script_version in $script_dir"
F_log_terminal_ok "Updated core config v${update_settings_version} with router firmware information"
F_terminal_padding
F_terminal_check "Any key to continue to the menu"
read -rsn1
F_clean_exit reload
;;
esac
;;
esac
} # fw_write
# only if we havent checked fw already in the last x mins
if [ "$fw_nvram_check_diff" -gt "$max_fw_nvram_check" ] ; then
# start of fw check
# set fw vars check if written, check for update ##################################################################
F_replace_var fw_nvram_check_epoch "$(F_date s)" "$update_src"
build_full="$(F_nvram buildno)"
build_no="$(F_printf "$build_full" | cut -f1 -d '.')"
build_sub="$(F_printf "$build_full" | cut -f2 -d '.')"
build_extend="$(F_nvram extendno)"
# initial firmware compatibility check, first run will be unpopulated
if [ -z "$fw_build_no" ] || [ -z "$fw_build_sub" ] || [ -z "$fw_build_extend" ] || [ -z "$fw_build_full" ] ; then
if F_fw_valid_version ; then
# new install set install date
[ -z "$install_date" ] && F_replace_var install_date "$(F_date f)" "$config_src"
F_fw_write
fi
fi
case "$fw_build_no" in
'374')
john_sub=${build_extend:0:2}
[ "$johnsub" != "$fw_build_sub" ] && F_fw_write fwupdate
;;
# check if user has upgraded firmware and update config, saving to config avoids numerous nvram calls every run not from terminal
*)
[ "$build_no" != "$fw_build_no" ] || [ "$build_sub" != "$fw_build_sub" ] || [ "$build_extend" != "$fw_build_extend" ] && F_fw_write fwupdate
;;
esac
fi
return 0
} # firmware_check
# ALIAS ###############################################################################################################
F_alias() {
case "$1" in
'remove')
if [ -f /jffs/configs/profile.add ] ; then
if grep -Fq "alias wicens=" /jffs/configs/profile.add ; then
sed -i "/alias wicens=/d" /jffs/configs/profile.add
[ ! -s /jffs/configs/profile.add ] && rm -f /jffs/configs/profile.add
F_log_terminal_ok "Removed alias from /jffs/configs/profile.add"
else
F_log_terminal_ok "No alias found in /jffs/configs/profile.add to remove"
fi
else
F_log_terminal_ok "No alias found in /jffs/configs/profile.add to remove"
fi
return 0
;;
esac
if [ ! -f /jffs/configs/profile.add ] ; then
F_printfstr "alias wicens=\"/bin/sh ${script_name_full}\" # added by wicens $(F_date r)" > /jffs/configs/profile.add
F_log "Created /jffs/configs/profile.add and added entry for wicens"
elif ! grep -Fq "alias wicens=" /jffs/configs/profile.add ; then
F_printfstr "alias wicens=\"/bin/sh ${script_name_full}\" # added by wicens $(F_date r)" >> /jffs/configs/profile.add
F_log "Added alias in /jffs/configs/profile.add for wicens"
fi
return 0
} # alias only checked on tty runs
# USER SETTINGS #######################################################################################################
#######################################################################################################################
F_default_user_create() {
{
F_printfstr "#!/bin/sh"
F_printfstr "# wicens user config file"
F_printfstr "build_settings_version='$current_user_config'"
F_printfstr "###########################################################"
F_printfstr "saved_wan_ip="
F_printfstr "saved_wan_date='never'"
F_printfstr "saved_wan_epoch="
F_printfstr "###########################################################"
F_printfstr "# User config settings ####################################"
F_printfstr "user_login_addr="
F_printfstr "user_smtp_server="
F_printfstr "user_from_addr="
F_printfstr "user_send_to_addr="
F_printfstr "user_send_to_cc="
F_printfstr "user_email_from='wicens script'"
F_printfstr "user_message_type="
F_printfstr "user_custom_subject="
F_printfstr "user_custom_text="
F_printfstr "user_custom_script="
F_printfstr "user_custom_script_time="
F_printfstr "###########################################################"
F_printfstr "user_update_notification=0"
F_printfstr "user_fw_update_notification=0"
F_printfstr "user_reboot_notification=0"
F_printfstr "user_wanip_notification=0"
F_printfstr "###########################################################"
F_printfstr "last_cron_run='never'"
F_printfstr "cron_run_count=0"
F_printfstr "last_wancall_run='never'"
F_printfstr "wancall_run_count=0"
F_printfstr "last_wancall_log_count=0"
F_printfstr "last_ip_change='never'"
F_printfstr "ip_change_count=0"
F_printfstr "install_date="
F_printfstr "update_date='never'"
F_printfstr "created_date='never'"
F_printfstr "opt_color=1"
F_printfstr "log_cron_msg=1"
F_printfstr "amtm_import=0"
F_printfstr "protocol='smtps'"
F_printfstr "ssl_flag="
F_printfstr "###########################################################"
F_printfstr "# Created : $(F_date r)"
} > "$config_src"
F_log "Created default user config v${current_user_config} for v$script_version in $script_dir"
} # create user config
F_default_update_create() {
{
F_printfstr "#!/bin/sh"
F_printfstr "# wicens core config file"
F_printfstr "update_settings_version='$current_core_config'"
F_printfstr "###########################################################"
F_printfstr "fw_build_no="
F_printfstr "fw_build_sub="
F_printfstr "fw_build_extend="
F_printfstr "fw_pulled_device_name="
F_printfstr "fw_pulled_lan_name="
F_printfstr "fw_device_model="
F_printfstr "fw_build_full="
F_printfstr "fw_lan_addr="
F_printfstr "###########################################################"
F_printfstr "update_avail='none'"
F_printfstr "update_cron_epoch=0"
F_printfstr "update_notify_state=0"
F_printfstr "fw_notify_state=0"
F_printfstr "reboot_notify_state=0"
F_printfstr "router_reboot_uptime="
F_printfstr "fw_nvram_check_epoch=0"
F_printfstr "# USER CAN EDIT BELOW SETTINGS ###########################"
F_printfstr "update_period=172800 # period between script update checks default:48hrs"
F_printfstr "wan_history_count=5 # number of historcal IPs in Email message"
F_printfstr "retry_wait_period=14400 # period between failed email retries default:4 hrs"
F_printfstr "max_email_retry=3 # max cron run retries before waiting for retry_period default:3"
F_printfstr "cron_check_freq=11 # minutes between cron checks default:11"
F_printfstr "wan_event_wait=40 # sleep before compare after wan-event call default:40"
F_printfstr "reboot_notify_wait=20 # sleep before reboot notify services-start call default:20"
F_printfstr "max_fw_nvram_check=600 # fw checks to nvram only every 10 minutes with tty default:600"
F_printfstr "dual_wan_check=1 # getrealip abort if dual wan enabled default:1"
F_printfstr "###########################################################"
F_printfstr "# add or change list of test sites in below function for internet test"
F_printfstr "F_test_sites() {"
F_printfstr " F_printfstr \"google.com\""
F_printfstr " F_printfstr \"bing.com\""
F_printfstr " F_printfstr \"yahoo.com\""
F_printfstr " F_printfstr \"github.com\""
F_printfstr " F_printfstr \"asus.com\""
F_printfstr " F_printfstr \"sourceforge.net\""
F_printfstr " F_printfstr \"snbforums.com\""
F_printfstr " F_printfstr \"wikipedia.org\""
F_printfstr "}"
F_printfstr "###########################################################"
F_printfstr "# Created : $(F_date r)"
} > "$update_src"
F_log "Created default core config v$current_core_config for v$script_version in $script_dir"
} # create current_core
F_user_settings() {
# first run create dir and default configs
[ ! -d "$script_dir" ] && mkdir "$script_dir" && F_log "Created $script_dir directory"
[ ! -f "$update_src" ] && F_default_update_create && F_chmod "$update_src"
[ ! -f "$config_src" ] && F_default_user_create && F_chmod "$config_src"
source "$config_src" # source user config file
source "$update_src" # source script config file
[ -n "$user_custom_subject" ] && user_custom_subject_decoded="$(F_printf "$user_custom_subject" | /usr/sbin/openssl base64 -d)"
[ -n "$user_custom_text" ] && user_custom_text_decoded="$(F_printf "$user_custom_text" | /usr/sbin/openssl base64 -d)"
[ -n "$user_custom_script" ] && user_custom_script_decoded="$(F_printf "$user_custom_script" | /usr/sbin/openssl base64 -d)"
case "$user_custom_script_time" in
i) user_script_call_time='immediate' ;;
w) user_script_call_time='wait' ;;
esac
case "$run_option" in
'tty')
fw_nvram_check_diff=$(($(F_date s) - fw_nvram_check_epoch))
F_terminal_color # load user terminal settings
F_integrity_check # check config file status
;;
esac
F_settings_test # sets vars for valid config, enabled options etc
original_wan_ip="$(grep -F 'saved_wan_ip' 2> /dev/null < "$config_src" | grep -Eo "$ip_regex")"
original_wan_date="$(grep -F 'saved_wan_date' 2> /dev/null < "$config_src" | cut -d'=' -f2 | tr -d "'")"
original_wan_epoch="$(grep -F 'saved_wan_epoch' 2> /dev/null < "$config_src" | cut -d'=' -f2 | tr -d "'")"
# update_cron_epoch comes from core config (default=0) update_diff used in status and (time remaining to sched check) if enabled script updates
if [ "$update_cron_epoch" -gt 0 ] ; then update_diff=$((run_epoch - update_cron_epoch)) ; else update_diff="$update_period" ; fi
} # user settings
# MENU OPTIONS ########################################################################################################
#######################################################################################################################
F_opt_about() {
clear
{ # start of | more
F_printfstr " WICENS - WAN IP Change Email Notification Script " ; F_printfstr ''
F_printfstr "This script when configured will send a notification to your Email(s) "
F_printfstr "notifying you when your WAN IP has changed. " ; F_printfstr ''
F_printfstr "Optional Firmware Update and Router Reboot notifications also available " ; F_printfstr ''
F_printfstr "Supports GMail, Hotmail, Outlook, ISP based Email " ; F_printfstr ''
F_printfstr "Supports amtm Email configuration import " ; F_printfstr ''
F_printfstr "Script will function in Double NAT scenarios but does not support Dual WAN "
F_printfstr "Dual WAN check can be disabled by editing setting in config manually " ; F_printfstr ''
F_printfstr "SMTP Email send formats available: "
F_printfstr "sendmail - StartTLS v1.1 higher (eg. GMail port 587) "
F_printfstr "sendmail - StartTLS v1 only "
F_printfstr "curl - SSL (eg GMail port 465) # amtm default "
F_printfstr "sendmail - SMTP plain auth (no encryption) "
F_printfstr "sendmail - ISP based (no password reqd, generally port 25) " ; F_printfstr ''
F_printfstr "IMPORTANT - If using GMail/Outlook you must use 2 factor authentication and "
F_printfstr "setup an assigned App password for this script to use. " ; F_printfstr ''
F_printfstr "IMPORTANT - Your Email address(es) are stored as plain text within this "
F_printfstr "script. Your Email password is encrypted and saved to router storage. "
F_printfstr "If you dont practice good security habits around your router ssh access, "
F_printfstr "this script might not be for you. " ; F_printfstr ''
F_printfstr "Script compares IP in nvram for wan0 to saved IP with wancall connected "
F_printfstr "events and cron, cron is also a watchdog and monitors for failed Email "
F_printfstr "attempts. Should the nvram IP be invalid/private IP script will use firmware "
F_printfstr "built in getrealip.sh to retrieve your WAN IP using Google STUN server. "
F_printfstr "If Dual Wan is enabled, script will abort before running getrealip.sh " ; F_printfstr ''
F_printfstr "All cron/wan-event/services-start/update-notification entries needed for this"
F_printfstr "script are automatically created and removed with enable and disable options." ; F_printfstr ''
F_printfstr "NTP sync must occur to update router date/time for proper script function " ; F_printfstr ''
F_printfstr "### Technical ### " ; F_printfstr ''
F_printfstr "Supports being used as an Email forwarder for other scripts, in your "
F_printfstr "script call /jffs/scripts/wicens.sh send {your email.txt path here} "
F_printfstr "ie. /jffs/scripts/wicens.sh send /tmp/email.txt "
F_printfstr "Use option fe (unlisted) in the menu to view a sample Email .txt file " ; F_printfstr ''
F_printfstr "When using wicens as an Email forwarder you can pass a second argument after "
F_printfstr "the Email text path as an alternate send to address different from what is "
F_printfstr "saved in the current config ie. wicens send /path \"[email protected]\" " ; F_printfstr ''
F_printfstr "Should Email sending fail the script will retry 4 more times with cron "
F_printfstr "1/${cron_check_freq}mins) in $update_period second intervals. " ; F_printfstr ''
F_printfstr "Script generates a lock file /tmp/wicens_lock.$run_option to prevent "
F_printfstr "duplicate runs as well as /tmp/wicens_internet_lock.$run_option "
F_printfstr "when sending Email notifications. Script will automatically remove stale "
F_printfstr "lock files if original starting process no longer exists or lock file are "
F_printfstr "over age limit. " ; F_printfstr ''
F_printfstr "Sendmail/Curl output for Emails is saved to /tmp/wicens_email.log for "
F_printfstr "debugging if needed. This file can be viewed by running this script and "
F_printfstr "select option L||l " ; F_printfstr ''
F_printfstr "Sendmail doesnt always return an error code on a misconfiguration so false "
F_printfstr "send success can occur. If script says Email has sent but no Email received "
F_printfstr "use option L||l from the Main Menu to read sendmail output for errors " ; F_printfstr ''
F_printfstr "The script does not update its saved WAN IP until the script has completed "
F_printfstr "sending the notification so in the event of message failure it should run "
F_printfstr "again with next cron run and attempt to send again. " ; F_printfstr ''
F_printfstr "Using option 5 you can call your own script either immediately upon WAN IP "
F_printfstr "change detection, or wait until the Email message has been successfully sent."
F_printfstr "Script will be put in background as to not block this script. " ; F_printfstr ''
F_printfstr "Output from a custom script set to run on WAN IP change is saved to "
F_printfstr "${script_dir}/user_script.log " ; F_printfstr ''
F_printfstr "Hidden menu options - 1f forces build_settings menu - fl remove mail log file"
F_printfstr "vv - list out all settings from config files - fr remove any found update "
F_printfstr "fe - show example Email text file for using wicens as Email forwarder " ; F_printfstr ''
F_printfstr "If you wish to see sample Reboot/FW update Emails you can force send them "
F_printfstr "by running wicens w/ reboot or fwupdate as an argument " ; F_printfstr ''
F_printfstr "Every Sunday the script will log the number of times it ran with wan-event. " ; F_printfstr ''
F_printfstr "Thank you for using this script. " ; F_printfstr ''
F_printfstr "SNBforums thread https://www.snbforums.com/threads/wicens-wan-ip-change-email-notification-script.69294/" ; F_printfstr ''
F_printfstr "GitHub source https://github.com/maverickcdn/wicens "
} | more
F_menu_exit
} # about
F_opt_backup_restore() {
F_backup() {
if [ -f "$script_backup_file" ] ; then
while true ; do
F_terminal_warning
F_terminal_check "Backup file exists, Y||y to overwrite - Any key to return to Main Menu"
read -rsn1 configremove
case "$configremove" in
y|Y) rm -f "$script_backup_file" ; F_terminal_erase ;;
*) F_clean_exit reload ;;
esac
break
done
fi
F_terminal_check "Starting backup"
if cp "$config_src" "$script_backup_file" ; then
F_terminal_check_ok "Backup successful, saved to $script_backup_file"
F_printfstr "# Backup : by v${script_version} created $(F_date r)" >> "$script_backup_file"
if [ -f "$history_src" ] ; then
cp "$history_src" "$history_src_backup"
fi
if [ "$user_message_type" != 'smtp_isp_nopswd' ] ; then
if [ -f "$cred_loc" ] ; then
if cp "$cred_loc" "$cred_loc_bak" ; then
F_terminal_check_ok "Password backup successful"
else
F_terminal_check_fail"Error backing up password"
fi
else
case "$amtm_import" in
0) F_terminal_check_fail "Couldn't find password to backup" ;;
1) F_terminal_check_ok "amtm import enabled, skipping password backup" ;;
esac
fi
else
# cleanup if smtp_isp_nopswd
if [ -f "$cred_loc_bak" ] ; then
rm -f "$cred_loc_bak"
fi
fi
else
F_terminal_check_fail "Critical error, backup failed, could not output to $script_backup_file"
fi
[ "$1" = 'resetbackup' ] && F_terminal_check "Any key to continue..." && read -rsn1
} # backup
F_restore() {
source "$script_backup_file"
[ "$(F_printfstr "$build_settings_version" | cut -d'.' -f1)" -le 3 ] && [ "$amtm_import" = 0 ] && amtm_import=1 # v3-v4
restore=1
F_settings_test
F_status | sed -n '1,/Script install/p'
F_terminal_show "File history:"
sed -n "/# Created/,/&/p" "$script_backup_file"
if ! F_confirm "Do you wish to restore this config?" ; then
F_terminal_check_ok "No received, exiting..."
F_menu_exit
fi
F_terminal_check_ok "Ok received"
F_terminal_check "Restoring backup"
if cp -f "$script_backup_file" "$config_src" ; then
F_printfstr "# Restored: by v${script_version} from backup on $(F_date r)" >> "$config_src"
if [ -f "$history_src_backup" ] ; then
cp "$history_src_backup" "$history_src"
fi
F_user_settings # reload
F_replace_var created_date "$(F_date f)" "$config_src"
if [ "$user_fw_update_notification" = 1 ] ; then
! F_notify_firmware check status && F_notify_firmware create && F_settings_test
fi
if [ "$user_reboot_notification" = 1 ] ; then
! F_notify_reboot check status && F_notify_reboot create && F_settings_test
fi
if [ "$user_update_notification" = 1 ] ; then
! F_notify_update check && F_notify_update create && F_settings_test
fi
if [ "$user_wanip_notification" = 1 ] ; then
if [ "$status_cru" = 0 ] || [ "$status_srvstrt" = 0 ] || [ "$status_wanevent" = 0 ] ; then F_notify_wanip create && F_settings_test ; fi
fi
if [ "$user_message_type" != 'smtp_isp_nopswd' ] ; then
if [ -f "$cred_loc_bak" ] ; then
if cp -f "$cred_loc_bak" "$cred_loc" ; then
F_terminal_check_ok "Successfully restored backed up password"
else
F_terminal_check_fail "Error restoring backed up password"
fi
else
F_terminal_check_fail "Error, no backed up password found"
fi
fi
else
F_terminal_check_fail "Critical error copying backup to script"
fi
F_terminal_check_ok "Done restoring backup settings to script"
} # restore
# from F_reset, if valid config backup before reset
[ "$1" = 'resetbackup' ] && F_backup resetbackup && return 0
F_terminal_header
F_terminal_show "${tYEL}===== Backup/Restore Settings Menu =====${tCLR} E||e to exit"
F_terminal_padding
# no valid settings/backup
if [ "$status_email_cfg" = 0 ] && [ ! -f "$script_backup_file" ] ; then
F_terminal_warning
F_terminal_check_fail "Error invalid current settings and no backup found to restore"
F_terminal_padding
F_terminal_show "Use Menu option 1 to edit settings"
F_menu_exit
fi
while true ; do
if [ -f "$script_backup_file" ] ; then
F_terminal_check_ok "Backup found! R||r to restore settings D||d to delete backup"
else
F_terminal_check_fail "No backup found to restore"
fi
if [ "$status_email_cfg" = 1 ] ; then
F_terminal_check_ok "Valid config found! B||b to backup current config"
else
F_terminal_check_fail "No valid config to backup, Main Menu option 1 to add a config"
fi
F_terminal_padding
F_terminal_check "Selection : "
read -r bandrwait
case "$bandrwait" in
D|d)
F_terminal_erase
F_terminal_check_ok "Delete backup selected"
F_terminal_padding
F_terminal_warning
if ! F_confirm "This will delete your backup, are you sure?" ; then
F_terminal_check_ok "No received, exiting..."
F_menu_exit
fi
if [ -f "$script_backup_file" ] ; then
rm -f "$script_backup_file"
F_terminal_check_ok "Saved backup removed"
[ -f "$cred_loc_bak" ] && rm -f "$cred_loc_bak"
F_menu_exit
else
F_terminal_check_fail "Error, no saved backup to delete"
F_terminal_padding
F_terminal_check "Any key to return to the Main Menu"
read -rsn1
F_clean_exit reload
fi
;;
B|b)
if [ "$status_email_cfg" = 0 ] ; then
F_terminal_check_fail "Error, no valid config found to backup"
F_terminal_padding
F_terminal_check "Any key to return to the Main Menu"
read -rsn1
F_clean_exit reload
else
F_terminal_erase
F_terminal_check_ok "B selected for backup"
F_backup
F_menu_exit
fi
;;
R|r)
if [ -f "$script_backup_file" ] ; then
F_terminal_erase
F_terminal_check_ok "R selected for restore"
F_restore
F_menu_exit
else
F_terminal_erase
F_terminal_check_fail "Invalid entry, no valid backup exists, any key to continue"
read -rsn1
F_opt_backup_restore
fi
;;
E|e) F_clean_exit reload ;;
*)
F_terminal_check_fail "Invalid entry, B/R/D - any key to retry, E return to Main Menu"
read -rsn1
case "$brinvalid" in
E|e) F_clean_exit reload ;;
*) F_opt_backup_restore ;;
esac
;;
esac
break
done
} # backup_restore
F_opt_color() {
case "$opt_color" in
1)
F_terminal_erase
F_terminal_padding
F_terminal_check "Setting script to no color mode"
F_replace_var opt_color '0' "$config_src"
F_terminal_check_ok "Set to no color mode, return to the Main Menu to view changes"
;;
0)
F_terminal_erase
F_terminal_padding
F_terminal_check "Setting script to color mode"
F_replace_var opt_color '1' "$config_src"
F_terminal_check_ok "Set to color mode, return to the Main Menu to view changes"
;;
esac
F_menu_exit
} # color
F_opt_count() {
F_terminal_header
F_terminal_show "${tYEL}===== Counts Reset Menu =====${tCLR} E||e to Exit"
F_terminal_padding
F_status | sed -n '/Cron run/,/Script configured/p'
F_terminal_separator
F_terminal_warning
F_terminal_show "This will reset cron/wan-event check counts and configured date"
F_terminal_show "as well as the option to reset WAN IP change records"
if F_confirm "Are you sure you wish to reset counts/dates?" ; then
F_terminal_check_ok "Ok received, resetting counts and configured date..."
F_replace_var cron_run_count 0 "$config_src"
F_replace_var last_cron_run 'never' "$config_src"
F_replace_var wancall_run_count 0 "$config_src"
F_replace_var last_wancall_run 'never' "$config_src"
F_replace_var last_wancall_log_count 0 "$config_src"
F_replace_var created_date "$(F_date f)" "$config_src"
F_log_terminal_ok "Reset cron count, wan-event count and configured date"
if [ "$last_ip_change" != 'never' ] ; then
if F_confirm "Reset last recorded WAN IP change date and total change count? " ; then
F_replace_var last_ip_change 'never' "$config_src"
F_replace_var ip_change_count 0 "$config_src"
F_log_terminal_ok "Reset last recorded WAN IP change date"
else
F_terminal_check_ok "Keeping WAN IP change records"
fi
else
F_terminal_check_ok "No IP change records to remove"
fi
if [ -f "$history_src" ] ; then
if F_confirm "Remove historical WAN IP change records?" ; then
rm -f "$history_src"
F_log_terminal_ok "Removed historical WAN IP change records"
else
F_terminal_check_ok "Keeping historical WAN IP change records"
fi
else
F_terminal_check_ok "No historical WAN IP change file found"
fi
else
F_terminal_check_ok "No received, exiting..."
fi
F_menu_exit
} # count
F_opt_forward() {
[ -n "$fwd_send_addr" ] && user_send_to_addr="$fwd_send_addr"
if [ ! -f "$wicens_send_retry" ] ; then
{
F_printfstr "#!/bin/sh"
F_printfstr "fwd_send_msg='${wicens_send_copy}'"
F_printfstr "fwd_send_addr='${user_send_to_addr}'"
F_printfstr "wicens_send_retry_time='${run_epoch}'"
F_printfstr "# Attempting to send $fwd_send_msg to $user_send_to_addr $(F_date r)"
} > "$wicens_send_retry"
F_chmod "$wicens_send_retry"
else
F_printfstr "# Attempting to send $fwd_send_msg to $user_send_to_addr $(F_date r)" >> "$wicens_send_retry"
fi
mail_file='/tmp/wicens_send.txt'
cp "$fwd_send_msg" "$wicens_send_copy" 2> /dev/null # copy incase send fails and user has email removed in their script
mail_file="$fwd_send_msg"
[ -f "$mail_log" ] && rm -f "$mail_log"
F_internet_check send
if ! F_send_email ; then
F_log "Error, failed to send $fwd_send_msg Email to $user_send_to_addr"
user_pswd=''
rm -f "$mail_file"
return 1
fi
user_pswd=''
rm -f "$mail_file"
rm -f "$wicens_send_retry"
rm -f "$wicens_send_copy"
F_log_terminal_ok "Success, finished sending $fwd_send_msg Email to $user_send_to_addr"
return 0
} # forward
F_opt_mail_log() {
if [ -f "$mail_log" ] ; then
more < "$mail_log"
F_terminal_padding
F_terminal_check_ok "End of contents."
F_menu_exit
else
F_terminal_check_fail "No log file found"
F_menu_exit
fi
} # mail_log
F_opt_reset() {
F_terminal_header
F_terminal_show "${tYEL}===== Script Reset Menu =====${tCLR} E||e to Exit"
F_terminal_padding
F_terminal_warning
if [ "$status_email_cfg" = 1 ] && [ "$backup_skip" != "1" ] ; then
F_terminal_check_ok "Found valid config"
F_terminal_padding
F_terminal_show "You're about to reset, would you like to make a backup?"