diff --git a/.github/workflows/Create-NewReleases.yml b/.github/workflows/Create-NewReleases.yml index 0c3c4580..d99bdba2 100644 --- a/.github/workflows/Create-NewReleases.yml +++ b/.github/workflows/Create-NewReleases.yml @@ -30,7 +30,7 @@ jobs: id: nextver run: | if [[ -f version.txt ]]; then - NEXT_TAG=$(cat version.txt) + NEXT_TAG=$(head -n 1 version.txt) else CURRENT_TAG=$(git tag --sort=-v:refname | head -n 1) if [[ $CURRENT_TAG == '' ]]; then diff --git a/MerlinAU.asp b/MerlinAU.asp new file mode 100644 index 00000000..3e21edf1 --- /dev/null +++ b/MerlinAU.asp @@ -0,0 +1,2947 @@ + + + + + + + + + + + + +MerlinAU add-on for ASUSWRT-Merlin Firmware + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + +" /> +" /> + +" /> + +" /> +" /> +" /> +" /> +" /> +" /> + + + + + + + + +
  + + + + + + + +
+ + + +
+
 
+
MerlinAU
+
+
This is the MerlinAU add-on integrated into the router WebUI +[ + Wiki ] + +
+
 
+ + + + + + + + +
+ + + + + + + + + + + +
Firmware Status (click to expand/collapse)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
F/W Product/Model ID:
F/W Variant Detected:Unknown
F/W Version Installed: + <% nvram_get("firmver"); %>.<% nvram_get("buildno"); %>.<% nvram_get("extendno"); %> +
F/W Update Available:NONE FOUND
Estimated Update Time:TBD
Last Notification Date:TBD
Changelog Approval:Disabled
+ + + + + + + + + + + +
Settings Status (click to expand/collapse)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
F/W Update Check:Disabled
Changelog Check:Disabled
Beta-to-Release Updates:Disabled
Tailscale VPN Access:Disabled
Automatic Backups:Disabled
Auto-Updates for MerlinAU:Disabled
Email Notifications:Disabled
+ +
 
+ + + + + + +
Actions (click to expand/collapse)
+
+ ++ + + + + + + +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
 
+ + + + + + + + + + +
Configuration (click to expand/collapse)
+ + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+ +
+
+
+
+ +
+
+ + +
+ + + +
+ + + +
+ + +
+ Days: + + + + +
+ + + + + + + +
+
+
+ Hour: + +
+
+ Minutes: + +
+
+ + + +
+ + + +
+ +
+ +
+ +
+ + + + +
+ + + + +
+ + + + +
+ +
+ +
+
+ +
+
+
MerlinAU
+
+ + + diff --git a/MerlinAU.sh b/MerlinAU.sh index 34c260ad..697cc167 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -4,15 +4,15 @@ # # Original Creation Date: 2023-Oct-01 by @ExtremeFiretop. # Official Co-Author: @Martinski W. - Date: 2023-Nov-01 -# Last Modified: 2025-Mar-28 +# Last Modified: 2025-Mar-30 ################################################################### set -u ## Set version for each Production Release ## -readonly SCRIPT_VERSION=1.3.12 +readonly SCRIPT_VERSION=1.4.0 readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## -SCRIPT_BRANCH="master" +SCRIPT_BRANCH="dev" ##----------------------------------------## ## Modified by Martinski W. [2024-Jul-03] ## @@ -37,11 +37,13 @@ readonly CL_URL_3006="${FW_SFURL_BASE}/Documentation/Changelog-3006.txt/download readonly high_risk_terms="factory default reset|features are disabled|break backward compatibility|must be manually|strongly recommended" ##----------------------------------------## -## Modified by Martinski W. [2024-Dec-31] ## +## Modified by Martinski W. [2025-Mar-24] ## ##----------------------------------------## # For new script version updates from source repository # DLRepoVersion="" DLRepoVersionNum="" +DLRepoBuildNum=0 +ScriptBuildNum=0 ScriptVersionNum="" scriptUpdateNotify=0 @@ -57,7 +59,7 @@ routerModelCheckFailed=false offlineUpdateTrigger=false ##----------------------------------------## -## Modified by Martinski W. [2025-Feb-15] ## +## Modified by Martinski W. [2025-Feb-18] ## ##----------------------------------------## readonly NOct="\e[0m" readonly BOLDct="\e[1m" @@ -66,26 +68,46 @@ readonly REDct="\e[1;31m" readonly GRNct="\e[1;32m" readonly YLWct="\e[1;33m" readonly BLUEct="\e[1;34m" -readonly MAGENTAct="\e[1;35m" +readonly MGNTct="\e[1;35m" #Magenta# readonly CYANct="\e[1;36m" readonly WHITEct="\e[1;37m" readonly CRITct="\e[1;41m" -readonly InvREDct="\e[1;41m" -readonly InvGRNct="\e[1;42m" +readonly InvREDct="\e[41m" +readonly InvGRNct="\e[42m" +readonly InvMGNct="\e[45m" +readonly InvBREDct="\e[30;101m" +readonly InvBGRNct="\e[30;102m" readonly InvBYLWct="\e[30;103m" +readonly InvBMGNct="\e[30;105m" readonly ScriptFileName="${0##*/}" readonly ScriptFNameTag="${ScriptFileName%%.*}" readonly ScriptDirNameD="${ScriptFNameTag}.d" ##----------------------------------------## -## Modified by Martinski W. [2024-Dec-01] ## +## Modified by Martinski W. [2025-Jan-15] ## ##----------------------------------------## readonly ADDONS_PATH="/jffs/addons" readonly SCRIPTS_PATH="/jffs/scripts" readonly SETTINGS_DIR="${ADDONS_PATH}/$ScriptDirNameD" -readonly SETTINGSFILE="${SETTINGS_DIR}/custom_settings.txt" +readonly CONFIG_FILE="${SETTINGS_DIR}/custom_settings.txt" readonly SCRIPT_VERPATH="${SETTINGS_DIR}/version.txt" +readonly HELPER_JSFILE="${SETTINGS_DIR}/CheckHelper.js" +readonly PSWD_CHECK_JS="${SETTINGS_DIR}/PswdCheckStatus.js" +readonly CHANGELOG_PATH="${SETTINGS_DIR}/changelog.txt" +readonly SHARED_SETTINGS_FILE="${ADDONS_PATH}/custom_settings.txt" +readonly SHARED_WEB_DIR="$(readlink -f /www/user)" +readonly SCRIPT_WEB_DIR="${SHARED_WEB_DIR}/$SCRIPT_NAME" +readonly SCRIPT_WEB_ASP_FILE="${SCRIPT_NAME}.asp" +readonly SCRIPT_WEB_ASP_PATH="$SETTINGS_DIR/$SCRIPT_WEB_ASP_FILE" +readonly TEMP_MENU_TREE="/tmp/menuTree.js" +readonly ORIG_MENU_TREE="/www/require/modules/menuTree.js" +readonly WEBUI_LOCKFD=386 +readonly WEBUI_LOCKFILE="/tmp/addonwebui.lock" +readonly TEMPFILE="/tmp/MerlinAU_settings_$$.txt" +readonly webPageFileRegExp="user([1-9]|[1-2][0-9])[.]asp" +readonly webPageLineTabExp="\{url: \"$webPageFileRegExp\", tabName: " +readonly webPageLineRegExp="${webPageLineTabExp}\"$SCRIPT_NAME\"\}," # Give FIRST priority to built-in binaries over any other # export PATH="/bin:/usr/bin:/sbin:/usr/sbin:$PATH" @@ -120,14 +142,14 @@ then fi ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Apr-02] ## +## Modified by ExtremeFiretop [2024-Dec-21] ## ##------------------------------------------## #-------------------------------------------------------# # We'll use the built-in AMTM email configuration file # to send email notifications *IF* enabled by the user. #-------------------------------------------------------# readonly FW_UpdateEMailFormatTypeDefault=HTML -readonly FW_UpdateEMailNotificationDefault=false +readonly FW_UpdateEMailNotificationDefault=DISABLED readonly amtmMailDirPath="/jffs/addons/amtm/mail" readonly amtmMailConfFile="${amtmMailDirPath}/email.conf" readonly amtmMailPswdFile="${amtmMailDirPath}/emailpw.enc" @@ -143,13 +165,16 @@ else cronListCmd="crontab -l" fi ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Jan-22] ## ##----------------------------------------## inMenuMode=true isInteractive=false FlashStarted=false MerlinChangeLogURL="" GnutonChangeLogURL="" +keepConfigFile=false +bypassPostponedDays=false +runLoginCredentialsTest=false # Main LAN Network Info # readonly myLAN_HostName="$(nvram get lan_hostname)" @@ -184,9 +209,28 @@ case "$fwInstalledBranchVer" in "3006.102") MinSupportedFirmwareVers="$MinSupportedFW_3006_102_Ver" ;; esac -if [ "$(nvram get sw_mode)" -eq 1 ] -then inRouterSWmode=true -else inRouterSWmode=false +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-19] ## +##----------------------------------------## +aiMeshNodes_OK=false +mountWebGUI_OK=false +inMainRouterMode=false +inAccessPointMode=false + +readonly nvramSWmode="$(nvram get sw_mode)" +if [ "$nvramSWmode" = "1" ] +then + mountWebGUI_OK=true + aiMeshNodes_OK=true + inMainRouterMode=true +else + if [ "$nvramSWmode" = "3" ] && \ + [ "$(nvram get wlc_psta)" = "0" ] + then + mountWebGUI_OK=true + aiMeshNodes_OK=false + inAccessPointMode=true + fi fi readonly mainMenuReturnPromptStr="Press to return to the Main Menu..." @@ -199,6 +243,7 @@ readonly SEPstr="----------------------------------------------------------" ## Added by Martinski W. [2024-Nov-24] ## ##-------------------------------------## # menu setup variables # +readonly padStr=" " readonly theExitStr="${GRNct}e${NOct}=Exit to Main Menu" readonly theMUExitStr="${GRNct}e${NOct}=Exit" readonly theADExitStr="${GRNct}e${NOct}=Exit to Advanced Options Menu" @@ -214,7 +259,7 @@ routerLoginFailureMsg="Please try the following: 1. Confirm that you are *not* already logged into the router webGUI using a web browser. 2. Check that the \"Enable Access Restrictions\" option from the webGUI is *not* set up to restrict access to the router webGUI from the router's IP address [${GRNct}${mainLAN_IPaddr}${NOct}]. -3. Confirm your password via the \"Set Router Login Credentials\" option from the Main Menu." +3. Confirm your password via the \"Set Router Login Password\" option from the Main Menu." [ -t 0 ] && ! tty | grep -qwi "NOT" && isInteractive=true @@ -294,20 +339,31 @@ _WaitForEnterKey_() read -rs EnterKEY ; echo } -##----------------------------------## -## Added Martinski W. [2023-Nov-28] ## -##----------------------------------## +##-------------------------------------## +## Modified Martinski W. [2025-Feb-18] ## +##-------------------------------------## _WaitForYESorNO_() { - ! "$isInteractive" && return 0 - local promptStr + local defltCode=0 defltAnswer=NO promptStr - if [ $# -eq 0 ] || [ -z "$1" ] + if [ $# -eq 0 ] + then defltCode=0 ; defltAnswer=NO + elif [ "$1" = "NO" ] + then defltCode=1 ; defltAnswer=NO + elif [ "$1" = "YES" ] + then defltCode=0 ; defltAnswer=YES + fi + + ! "$isInteractive" && return "$defltCode" + + if [ $# -eq 0 ] || [ -z "$1" ] || \ + echo "$1" | grep -qE "^(YES|NO)$" then promptStr=" [yY|nN]? " else promptStr="$1 [yY|nN]? " fi printf "$promptStr" ; read -r YESorNO + [ -z "$YESorNO" ] && YESorNO="$defltAnswer" if echo "$YESorNO" | grep -qE "^([Yy](es)?|YES)$" then echo "OK" ; return 0 else echo "NO" ; return 1 @@ -315,76 +371,125 @@ _WaitForYESorNO_() } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-24] ## +## Modified by Martinski W. [2025-Jan-11] ## ##----------------------------------------## readonly LockFilePath="/tmp/var/${ScriptFNameTag}.LOCK" -LockFileMaxAgeSecs=600 #10-minutes# -LockWaitTimeoutSecs=120 +readonly LockTypeRegEx="(cliMenuLock|cliOptsLock|cliFileLock)" -_ReleaseLock_() { rm -f "$LockFilePath" ; } +_FindLockFileTypes_() +{ grep -woE "$LockTypeRegEx" "$LockFilePath" | tr '\n' ' ' | sed 's/[ ]*$//' ; } -if [ $# -eq 0 ] || [ -z "$1" ] || \ - [ "$1" = "resetLockFile" ] -then #Interactive Mode# - LockFileMaxAgeSecs=1200 - LockWaitTimeoutSecs=2 -elif [ "$1" = "addCronJob" ] -then #Special Case# - LockFileMaxAgeSecs=1200 - LockWaitTimeoutSecs=600 +_ReleaseLock_() +{ + local lockType + if [ $# -eq 0 ] || [ -z "$1" ] + then lockType="" + else lockType="$1" + fi + if [ -s "$LockFilePath" ] && \ + [ "$(wc -l < "$LockFilePath")" -gt 1 ] + then + if [ -z "$lockType" ] + then sed -i "/^$$|/d" "$LockFilePath" + else sed -i "/.*|${1}$/d" "$LockFilePath" + fi + [ -s "$LockFilePath" ] && return 0 + fi + rm -f "$LockFilePath" +} + +## Defaults ## +LockMaxTimeoutSecs=120 +LockFileMaxAgeSecs=600 #10-minutes# + +if [ $# -eq 0 ] || [ -z "$1" ] +then + #Interactive Mode# + LockMaxTimeoutSecs=3 + LockFileMaxAgeSecs=1200 +else + case "$1" in + run_now|resetLockFile) + LockMaxTimeoutSecs=3 + LockFileMaxAgeSecs=1200 + ;; + startup|addCronJob) + LockMaxTimeoutSecs=600 + LockFileMaxAgeSecs=900 + ;; + esac fi ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-17] ## +## Modified by Martinski W. [2025-Jan-15] ## ##----------------------------------------## _AcquireLock_() { + local retCode waitTimeoutSecs + local lockFileSecs ageOfLockSecs oldPID + local lockTypeReq lockTypeFound + + if [ $# -gt 0 ] && [ -n "$1" ] + then lockTypeReq="$1" + else lockTypeReq="cliAnyLock" + fi + + _CreateLockFile_() + { echo "$$|$lockTypeReq" > "$LockFilePath" ; } + if [ ! -f "$LockFilePath" ] - then - echo "$$" > "$LockFilePath" - return 0 + then _CreateLockFile_ ; return 0 fi - local lockFileSecs ageOfLockSecs oldPID retCode waitTimeoutSecs retCode=1 + lockTypeFound="" waitTimeoutSecs=0 while true do - if [ -f "$LockFilePath" ] + if [ -s "$LockFilePath" ] then - oldPID="$(cat "$LockFilePath")" + oldPID="$(head -n1 "$LockFilePath" | awk -F '|' '{print $1}')" + if [ -n "$oldPID" ] && ! pidof "$ScriptFileName" | grep -qow "$oldPID" + then sed -i "/^${oldPID}|/d" "$LockFilePath" + fi lockFileSecs="$(date +%s -r "$LockFilePath")" + lockTypeFound="$(_FindLockFileTypes_)" + if [ "$lockTypeReq" != "cliAnyLock" ] && \ + ! echo "$lockTypeFound" | grep -qw "$lockTypeReq" + then # Specific "Lock Type" NOT found # + echo "$$|$lockTypeReq" >> "$LockFilePath" + retCode=0 ; break + fi + [ -z "$lockTypeFound" ] && lockTypeFound="noTypeLock" else - echo "$$" > "$LockFilePath" - retCode=0 - break + _CreateLockFile_ + retCode=0 ; break fi ageOfLockSecs="$(($(date +%s) - lockFileSecs))" if [ "$ageOfLockSecs" -gt "$LockFileMaxAgeSecs" ] then - Say "Stale Lock Found (older than $LockFileMaxAgeSecs secs.) Resetting lock file..." - if [ -n "$oldPID" ] && kill -EXIT "$oldPID" 2>/dev/null && \ - pidof "$ScriptFileName" | grep -qow "$oldPID" + Say "Stale Lock Found (older than $LockFileMaxAgeSecs secs). Resetting lock file..." + if [ -n "$oldPID" ] && \ + pidof "$ScriptFileName" | grep -qow "$oldPID" && \ + kill -EXIT "$oldPID" 2>/dev/null then kill -TERM "$oldPID" ; wait "$oldPID" fi - echo "$$" > "$LockFilePath" - retCode=0 - break - elif [ "$waitTimeoutSecs" -le "$LockWaitTimeoutSecs" ] + _CreateLockFile_ + retCode=0 ; break + elif [ "$waitTimeoutSecs" -le "$LockMaxTimeoutSecs" ] then if [ "$((waitTimeoutSecs % 10))" -eq 0 ] then - Say "Lock Found [Age: $ageOfLockSecs secs.] Waiting for script [PID=$oldPID] to exit [Timer: $waitTimeoutSecs secs.]" + Say "Lock Found [$lockTypeFound: $ageOfLockSecs secs]. Waiting for script [PID=$oldPID] to exit [Timer: $waitTimeoutSecs secs]" fi - sleep 2 - waitTimeoutSecs="$((waitTimeoutSecs + 2))" + sleep 5 + waitTimeoutSecs="$((waitTimeoutSecs + 5))" else - Say "${REDct}**ERROR**${NOct}: The shell script ${ScriptFileName} [PID=$oldPID] is already running [Lock Age: $ageOfLockSecs secs.]" - retCode=1 - break + Say "${REDct}**ERROR**${NOct}: The shell script ${ScriptFileName} [PID=$oldPID] is already running [$lockTypeFound: $ageOfLockSecs secs]" + retCode=1 ; break fi done return "$retCode" @@ -403,7 +508,8 @@ _DoExit_() ##------------------------------------------## ## Modified by ExtremeFiretop [2024-May-21] ## ##------------------------------------------## -logo() { +_ShowLogo_() +{ echo -e "${YLWct}" echo -e " __ __ _ _ _ _ " echo -e " | \/ | | (_) /\ | | | |" @@ -415,12 +521,21 @@ logo() { } ##----------------------------------------## -## Modified by Martinski W. [2025-Feb-22] ## +## Modified by Martinski W. [2025-Mar-19] ## ##----------------------------------------## _ShowAbout_() { + local webUI_Page webUI_URL="[Not Available]" + if "$mountWebGUI_OK" + then + webUI_Page="$(_Check_WebGUI_Page_Exists_)" + if [ "$webUI_Page" != "NONE" ] + then webUI_URL="$(_GetRouterURL_)/$webUI_Page" + fi + fi + clear - logo + _ShowLogo_ cat </dev/null then - kill -TERM $Toggle_LEDs_PID - wait $Toggle_LEDs_PID + kill -TERM "$Toggle_LEDs_PID" + wait "$Toggle_LEDs_PID" # Set LEDs to their "initial state" # nvram set ${nvramLEDsVar}="$LEDsInitState" /sbin/service restart_leds >/dev/null 2>&1 @@ -723,13 +841,13 @@ _FWVersionStrToNum_() } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Mar-19] ## ##----------------------------------------## -if "$inRouterSWmode" +if "$inMainRouterMode" then readonly FW_Update_CRON_DefaultSchedule="0 0 * * *" else - ## Recommended 20 minutes AFTER for AiMesh Nodes ## + ## Set 20 minutes AFTER for APs and AiMesh Nodes ## readonly FW_Update_CRON_DefaultSchedule="20 0 * * *" fi @@ -782,24 +900,44 @@ readonly FW_SFURL_RELEASE="${FW_SFURL_BASE}/${PRODUCT_ID}/${FW_SFURL_RELEASE_SUF readonly isGNUtonFW="$(_GetFirmwareVariantFromRouter_)" ##----------------------------------------## -## Modified by Martinski W. [2024-Jul-03] ## +## Modified by Martinski W. [2025-Jan-05] ## +##----------------------------------------## +readonly FW_RouterProductID="${GRNct}${PRODUCT_ID}${NOct}" +# Some Model IDs have a lower case suffix of the same Product ID # +if [ "$PRODUCT_ID" = "$(echo "$MODEL_ID" | tr 'a-z' 'A-Z')" ] +then + readonly FW_RouterModelID="$PRODUCT_ID" + readonly FW_RouterModelIDstr="$FW_RouterProductID" +else + readonly FW_RouterModelID="${PRODUCT_ID}/$MODEL_ID" + readonly FW_RouterModelIDstr="${FW_RouterProductID}/${GRNct}${MODEL_ID}${NOct}" +fi + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-05] ## ##----------------------------------------## _ChangeToDev_() { + if ! _AcquireLock_ cliFileLock + then return 1 + fi SCRIPT_BRANCH="dev" SCRIPT_URL_REPO="${SCRIPT_URL_BASE}/$SCRIPT_BRANCH" - _SCRIPTUPDATE_ force + _SCRIPT_UPDATE_ force _DoExit_ 0 } ##----------------------------------------## -## Modified by Martinski W. [2024-Jul-03] ## +## Modified by Martinski W. [2025-Jan-05] ## ##----------------------------------------## _ChangeToStable_() { + if ! _AcquireLock_ cliFileLock + then return 1 + fi SCRIPT_BRANCH="master" SCRIPT_URL_REPO="${SCRIPT_URL_BASE}/$SCRIPT_BRANCH" - _SCRIPTUPDATE_ force + _SCRIPT_UPDATE_ force _DoExit_ 0 } @@ -867,20 +1005,152 @@ else readonly FW_Update_LOG_BASE_DefaultDIR="$ADDONS_PATH" fi +##-------------------------------------## +## Added by Martinski W. [2025-Jan-15] ## +##-------------------------------------## +_SetUp_FW_UpdateZIP_DirectoryPaths_() +{ + local theDirPath="" + if [ $# -eq 1 ] && [ -n "$1" ] && [ -d "$1" ] + then + theDirPath="$1" + else + theDirPath="$(Get_Custom_Setting FW_New_Update_ZIP_Directory_Path)" + fi + FW_ZIP_BASE_DIR="$theDirPath" + FW_ZIP_DIR="${FW_ZIP_BASE_DIR}/$FW_ZIP_SUBDIR" + FW_ZIP_FPATH="${FW_ZIP_DIR}/${FW_FileName}.zip" +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-15] ## +##-------------------------------------## +_SetUp_FW_UpdateLOG_DirectoryPaths_() +{ + local theDirPath="" + if [ $# -eq 1 ] && [ -n "$1" ] && [ -d "$1" ] + then + theDirPath="$1" + else + theDirPath="$(Get_Custom_Setting FW_New_Update_LOG_Directory_Path)" + fi + FW_LOG_BASE_DIR="$theDirPath" + FW_LOG_DIR="${FW_LOG_BASE_DIR}/$FW_LOG_SUBDIR" +} + +##-------------------------------------## +## Added by Martinski W. [2025-Mar-07] ## +##-------------------------------------## +_WriteVarDefToPswdCheckJSFile_() +{ + if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ] + then return 1; fi + + local varValue fixedVal + if [ $# -eq 3 ] && [ "$3" = "true" ] + then varValue="$2" + else varValue="'${2}'" + fi + + if [ ! -s "$PSWD_CHECK_JS" ] + then + echo "var $1 = ${varValue};" > "$PSWD_CHECK_JS" + elif ! grep -q "^var $1 =.*" "$PSWD_CHECK_JS" + then + echo "var $1 = ${varValue};" >> "$PSWD_CHECK_JS" + elif ! grep -q "^var $1 = ${varValue};" "$PSWD_CHECK_JS" + then + fixedVal="$(echo "$varValue" | sed 's/[\/&]/\\&/g')" + sed -i "s/^var $1 =.*/var $1 = ${fixedVal};/" "$PSWD_CHECK_JS" + fi +} + +##-------------------------------------## +## Added by Martinski W. [2025-Mar-07] ## +##-------------------------------------## +_GetLoginPswdCheckStatusJS_() +{ + if [ ! -s "$PSWD_CHECK_JS" ] ; then echo "0" ; return 0 ; fi + local checkCode + checkCode="$(grep "^var loginPswdCheckStatus =" "$PSWD_CHECK_JS" | awk -F '[ ;]' '{print $4}')" + if [ -z "$checkCode" ] + then echo "0" + else echo "$checkCode" + fi + return 0 +} + +##-------------------------------------## +## Added by Martinski W. [2025-Mar-07] ## +##-------------------------------------## +_UpdateLoginPswdCheckHelper_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi + local checkCode checkMsge prevChkCode + + case "$1" in + InitPWD) + checkCode=0 + checkMsge="Password is EMPTY." + ;; + NoACCESS) + checkCode=1 + checkMsge="Login access is RESTRICTED." + ;; + OldPSWD) + prevChkCode="$(_GetLoginPswdCheckStatusJS_)" + if [ -n "$prevChkCode" ] && [ "$prevChkCode" -gt 1 ] + then + return 0 + else + checkCode=2 + checkMsge="Password is unchanged." + fi + ;; + NewPSWD) + checkCode=3 + checkMsge="Password is NOT verified." + ;; + SUCCESS) + checkCode=4 + checkMsge="Password is verified." + ;; + FAILURE) + checkCode=5 + checkMsge="Password is INVALID." + ;; + UNKNOWN) + prevChkCode="$(_GetLoginPswdCheckStatusJS_)" + if [ -n "$prevChkCode" ] && [ "$prevChkCode" -gt 1 ] + then + return 0 + else + checkCode=6 + checkMsge="UNKNOWN" + fi + ;; + *) ##IGNORE## + return 1 ;; + esac + + _WriteVarDefToPswdCheckJSFile_ "loginPswdCheckStatus" "$checkCode" true + _WriteVarDefToPswdCheckJSFile_ "loginPswdCheckMsgStr" "$checkMsge" +} + ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## -_Init_Custom_Settings_Config_() +_InitCustomSettingsConfig_() { [ ! -d "$SETTINGS_DIR" ] && mkdir -m 755 -p "$SETTINGS_DIR" - if [ ! -f "$SETTINGSFILE" ] + if [ ! -f "$CONFIG_FILE" ] then { echo "FW_New_Update_Notification_Date TBD" echo "FW_New_Update_Notification_Vers TBD" echo "FW_New_Update_Postponement_Days=$FW_UpdateDefaultPostponementDays" - echo "FW_New_Update_EMail_Notification=$FW_UpdateEMailNotificationDefault" + echo "FW_New_Update_EMail_Notification $FW_UpdateEMailNotificationDefault" echo "FW_New_Update_EMail_FormatType=\"${FW_UpdateEMailFormatTypeDefault}\"" echo "FW_New_Update_Cron_Job_Schedule=\"${FW_Update_CRON_DefaultSchedule}\"" echo "FW_New_Update_ZIP_Directory_Path=\"${FW_Update_ZIP_DefaultSetupDIR}\"" @@ -888,108 +1158,115 @@ _Init_Custom_Settings_Config_() echo "FW_New_Update_LOG_Preferred_Path=\"${FW_Update_LOG_BASE_DefaultDIR}\"" echo "FW_New_Update_EMail_CC_Name=TBD" echo "FW_New_Update_EMail_CC_Address=TBD" + echo "credentials_base64 TBD" echo "CheckChangeLog ENABLED" + echo "FW_Update_Check TBD" echo "Allow_Updates_OverVPN DISABLED" - echo "FW_New_Update_Changelog_Approval=TBD" echo "FW_Allow_Beta_Production_Up ENABLED" - echo "FW_Auto_Backupmon ENABLED" echo "Allow_Script_Auto_Update DISABLED" echo "Script_Update_Cron_Job_SchedDays=\"${SW_Update_CRON_DefaultSchedDays}\"" - } > "$SETTINGSFILE" - chmod 0664 "$SETTINGSFILE" + } > "$CONFIG_FILE" + chmod 664 "$CONFIG_FILE" + _UpdateLoginPswdCheckHelper_ InitPWD return 1 fi local retCode=0 preferredPath - if ! grep -q "^FW_New_Update_Notification_Date " "$SETTINGSFILE" + # TEMPORARY Migration Function # + _Migrate_Settings_ + + if ! grep -q "^FW_New_Update_Notification_Date " "$CONFIG_FILE" then - sed -i "1 i FW_New_Update_Notification_Date TBD" "$SETTINGSFILE" + sed -i "1 i FW_New_Update_Notification_Date TBD" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_Notification_Vers " "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_Notification_Vers " "$CONFIG_FILE" then - sed -i "2 i FW_New_Update_Notification_Vers TBD" "$SETTINGSFILE" + sed -i "2 i FW_New_Update_Notification_Vers TBD" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_Postponement_Days=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_Postponement_Days=" "$CONFIG_FILE" then - sed -i "3 i FW_New_Update_Postponement_Days=$FW_UpdateDefaultPostponementDays" "$SETTINGSFILE" + sed -i "3 i FW_New_Update_Postponement_Days=$FW_UpdateDefaultPostponementDays" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_EMail_Notification=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_EMail_Notification " "$CONFIG_FILE" then - sed -i "4 i FW_New_Update_EMail_Notification=$FW_UpdateEMailNotificationDefault" "$SETTINGSFILE" + sed -i "4 i FW_New_Update_EMail_Notification $FW_UpdateEMailNotificationDefault" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_EMail_FormatType=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_EMail_FormatType=" "$CONFIG_FILE" then - sed -i "5 i FW_New_Update_EMail_FormatType=\"${FW_UpdateEMailFormatTypeDefault}\"" "$SETTINGSFILE" + sed -i "5 i FW_New_Update_EMail_FormatType=\"${FW_UpdateEMailFormatTypeDefault}\"" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_Cron_Job_Schedule=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_Cron_Job_Schedule=" "$CONFIG_FILE" then - sed -i "6 i FW_New_Update_Cron_Job_Schedule=\"${FW_Update_CRON_DefaultSchedule}\"" "$SETTINGSFILE" + sed -i "6 i FW_New_Update_Cron_Job_Schedule=\"${FW_Update_CRON_DefaultSchedule}\"" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_ZIP_Directory_Path=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_ZIP_Directory_Path=" "$CONFIG_FILE" then - sed -i "7 i FW_New_Update_ZIP_Directory_Path=\"${FW_Update_ZIP_DefaultSetupDIR}\"" "$SETTINGSFILE" + sed -i "7 i FW_New_Update_ZIP_Directory_Path=\"${FW_Update_ZIP_DefaultSetupDIR}\"" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_LOG_Directory_Path=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_LOG_Directory_Path=" "$CONFIG_FILE" then - sed -i "8 i FW_New_Update_LOG_Directory_Path=\"${FW_Update_LOG_BASE_DefaultDIR}\"" "$SETTINGSFILE" + sed -i "8 i FW_New_Update_LOG_Directory_Path=\"${FW_Update_LOG_BASE_DefaultDIR}\"" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_LOG_Preferred_Path=" "$SETTINGSFILE" + if ! grep -q "^FW_New_Update_LOG_Preferred_Path=" "$CONFIG_FILE" then preferredPath="$(Get_Custom_Setting FW_New_Update_LOG_Directory_Path)" - sed -i "9 i FW_New_Update_LOG_Preferred_Path=\"${preferredPath}\"" "$SETTINGSFILE" + sed -i "9 i FW_New_Update_LOG_Preferred_Path=\"${preferredPath}\"" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^CheckChangeLog " "$SETTINGSFILE" + if ! grep -q "^credentials_base64 " "$CONFIG_FILE" then - sed -i "10 i CheckChangeLog ENABLED" "$SETTINGSFILE" + sed -i "10 i credentials_base64 TBD" "$CONFIG_FILE" + _UpdateLoginPswdCheckHelper_ InitPWD retCode=1 + else + _UpdateLoginPswdCheckHelper_ UNKNOWN fi - if ! grep -q "^Allow_Updates_OverVPN " "$SETTINGSFILE" + if ! grep -q "^CheckChangeLog " "$CONFIG_FILE" then - sed -i "11 i Allow_Updates_OverVPN DISABLED" "$SETTINGSFILE" + sed -i "11 i CheckChangeLog ENABLED" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_Allow_Beta_Production_Up " "$SETTINGSFILE" + if ! grep -q "^FW_Update_Check " "$CONFIG_FILE" then - sed -i "12 i FW_Allow_Beta_Production_Up ENABLED" "$SETTINGSFILE" + sed -i "12 i FW_Update_Check TBD" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_Auto_Backupmon " "$SETTINGSFILE" + if ! grep -q "^Allow_Updates_OverVPN " "$CONFIG_FILE" then - sed -i "13 i FW_Auto_Backupmon ENABLED" "$SETTINGSFILE" + sed -i "13 i Allow_Updates_OverVPN DISABLED" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^Allow_Script_Auto_Update " "$SETTINGSFILE" + if ! grep -q "^FW_Allow_Beta_Production_Up " "$CONFIG_FILE" then - sed -i "14 i Allow_Script_Auto_Update DISABLED" "$SETTINGSFILE" + sed -i "14 i FW_Allow_Beta_Production_Up ENABLED" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^Script_Update_Cron_Job_SchedDays=" "$SETTINGSFILE" + if ! grep -q "^Allow_Script_Auto_Update " "$CONFIG_FILE" then - sed -i "15 i Script_Update_Cron_Job_SchedDays=\"${SW_Update_CRON_DefaultSchedDays}\"" "$SETTINGSFILE" + sed -i "15 i Allow_Script_Auto_Update DISABLED" "$CONFIG_FILE" retCode=1 fi - if ! grep -q "^FW_New_Update_Changelog_Approval=" "$SETTINGSFILE" + if ! grep -q "^Script_Update_Cron_Job_SchedDays=" "$CONFIG_FILE" then - sed -i "16 i FW_New_Update_Changelog_Approval=TBD" "$SETTINGSFILE" + sed -i "16 i Script_Update_Cron_Job_SchedDays=\"${SW_Update_CRON_DefaultSchedDays}\"" "$CONFIG_FILE" retCode=1 fi - dos2unix "$SETTINGSFILE" - chmod 0664 "$SETTINGSFILE" + dos2unix "$CONFIG_FILE" + chmod 664 "$CONFIG_FILE" return "$retCode" } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Jan-05] ## ##----------------------------------------## Get_Custom_Setting() { @@ -999,18 +1276,21 @@ Get_Custom_Setting() local setting_value="" setting_type="$1" default_value="TBD" [ $# -gt 1 ] && default_value="$2" - if [ -f "$SETTINGSFILE" ]; then + if [ -f "$CONFIG_FILE" ] + then case "$setting_type" in "ROGBuild" | "TUFBuild" | \ "credentials_base64" | \ "CheckChangeLog" | \ + "FW_Update_Check" | \ "Allow_Updates_OverVPN" | \ "FW_Allow_Beta_Production_Up" | \ "FW_Auto_Backupmon" | \ "Allow_Script_Auto_Update" | \ + "FW_New_Update_EMail_Notification" | \ "FW_New_Update_Notification_Date" | \ "FW_New_Update_Notification_Vers") - setting_value="$(grep "^${setting_type} " "$SETTINGSFILE" | awk -F ' ' '{print $2}')" + setting_value="$(grep "^${setting_type} " "$CONFIG_FILE" | awk -F ' ' '{print $2}')" ;; "FW_New_Update_Postponement_Days" | \ "FW_New_Update_Changelog_Approval" | \ @@ -1020,52 +1300,27 @@ Get_Custom_Setting() "FW_New_Update_ZIP_Directory_Path" | \ "FW_New_Update_LOG_Directory_Path" | \ "FW_New_Update_LOG_Preferred_Path" | \ - "FW_New_Update_EMail_Notification" | \ "FW_New_Update_EMail_FormatType" | \ "FW_New_Update_EMail_CC_Name" | \ "FW_New_Update_EMail_CC_Address") - grep -q "^${setting_type}=" "$SETTINGSFILE" && \ - setting_value="$(grep "^${setting_type}=" "$SETTINGSFILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" + grep -q "^${setting_type}=" "$CONFIG_FILE" && \ + setting_value="$(grep "^${setting_type}=" "$CONFIG_FILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" ;; *) setting_value="**ERROR**" ;; esac - [ -z "$setting_value" ] && echo "$default_value" || echo "$setting_value" + if [ -z "$setting_value" ] + then echo "$default_value" + else echo "$setting_value" + fi else echo "$default_value" fi } ##----------------------------------------## -## Modified by Martinski W. [2024-Apr-30] ## -##----------------------------------------## -_GetAllNodeSettings_() -{ - if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ] - then echo "**ERROR**" ; return 1; fi - - ## Node Setting KEY="Node_{MACaddress}_{keySuffix}" ## - local fullKeyName="Node_${1}_${2}" - local setting_value="TBD" matched_lines - - # Ensure the settings directory exists # - [ ! -d "$SETTINGS_DIR" ] && mkdir -m 755 -p "$SETTINGS_DIR" - - if [ -f "$SETTINGSFILE" ] - then - matched_lines="$(grep -E "^${fullKeyName}=.*" "$SETTINGSFILE")" - if [ -n "$matched_lines" ] - then - # Extract the value from the first matched line # - setting_value="$(echo "$matched_lines" | head -n 1 | awk -F '=' '{print $2}' | tr -d '"')" - fi - fi - echo "$setting_value" -} - -##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Jan-20] ## ##----------------------------------------## Update_Custom_Settings() { @@ -1074,31 +1329,34 @@ Update_Custom_Settings() local fixedVal oldVal="" local setting_type="$1" setting_value="$2" - # Check if the directory exists, and if not, create it + # Check if the directory exists, and if not, create it # [ ! -d "$SETTINGS_DIR" ] && mkdir -m 755 -p "$SETTINGS_DIR" case "$setting_type" in "ROGBuild" | "TUFBuild" | \ "credentials_base64" | \ "CheckChangeLog" | \ + "FW_Update_Check" | \ "Allow_Updates_OverVPN" | \ "FW_Allow_Beta_Production_Up" | \ "FW_Auto_Backupmon" | \ "Allow_Script_Auto_Update" | \ + "FW_New_Update_EMail_Notification" | \ "FW_New_Update_Notification_Date" | \ "FW_New_Update_Notification_Vers") - if [ -f "$SETTINGSFILE" ] + if [ -f "$CONFIG_FILE" ] then - if [ "$(grep -c "$setting_type" "$SETTINGSFILE")" -gt 0 ] + if [ "$(grep -c "^$setting_type" "$CONFIG_FILE")" -gt 0 ] then - if [ "$setting_value" != "$(grep "^$setting_type" "$SETTINGSFILE" | cut -f2 -d' ')" ]; then - sed -i "s/$setting_type.*/$setting_type $setting_value/" "$SETTINGSFILE" + if [ "$setting_value" != "$(grep "^$setting_type" "$CONFIG_FILE" | cut -f2 -d' ')" ] + then + sed -i "s/^$setting_type.*/$setting_type $setting_value/" "$CONFIG_FILE" fi else - echo "$setting_type $setting_value" >> "$SETTINGSFILE" + echo "$setting_type $setting_value" >> "$CONFIG_FILE" fi else - echo "$setting_type $setting_value" > "$SETTINGSFILE" + echo "$setting_type $setting_value" > "$CONFIG_FILE" fi ;; "FW_New_Update_Postponement_Days" | \ @@ -1109,25 +1367,24 @@ Update_Custom_Settings() "FW_New_Update_ZIP_Directory_Path" | \ "FW_New_Update_LOG_Directory_Path" | \ "FW_New_Update_LOG_Preferred_Path" | \ - "FW_New_Update_EMail_Notification" | \ "FW_New_Update_EMail_FormatType" | \ "FW_New_Update_EMail_CC_Name" | \ "FW_New_Update_EMail_CC_Address") - if [ -f "$SETTINGSFILE" ] + if [ -f "$CONFIG_FILE" ] then - if grep -q "^${setting_type}=" "$SETTINGSFILE" + if grep -q "^${setting_type}=" "$CONFIG_FILE" then - oldVal="$(grep "^${setting_type}=" "$SETTINGSFILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" + oldVal="$(grep "^${setting_type}=" "$CONFIG_FILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" if [ -z "$oldVal" ] || [ "$oldVal" != "$setting_value" ] then fixedVal="$(echo "$setting_value" | sed 's/[\/.,*-]/\\&/g')" - sed -i "s/${setting_type}=.*/${setting_type}=\"${fixedVal}\"/" "$SETTINGSFILE" + sed -i "s/${setting_type}=.*/${setting_type}=\"${fixedVal}\"/" "$CONFIG_FILE" fi else - echo "$setting_type=\"${setting_value}\"" >> "$SETTINGSFILE" + echo "$setting_type=\"${setting_value}\"" >> "$CONFIG_FILE" fi else - echo "$setting_type=\"${setting_value}\"" > "$SETTINGSFILE" + echo "$setting_type=\"${setting_value}\"" > "$CONFIG_FILE" fi if [ "$setting_type" = "FW_New_Update_Postponement_Days" ] then @@ -1137,10 +1394,6 @@ Update_Custom_Settings() then FW_UpdateExpectedRunDate="$setting_value" # - elif [ "$setting_type" = "FW_New_Update_EMail_Notification" ] - then - sendEMailNotificationsFlag="$setting_value" - # elif [ "$setting_type" = "FW_New_Update_EMail_FormatType" ] then sendEMailFormaType="$setting_value" @@ -1158,38 +1411,39 @@ Update_Custom_Settings() elif [ "$setting_type" = "FW_New_Update_Cron_Job_Schedule" ] then FW_UpdateCronJobSchedule="$setting_value" + _WebUI_AutoScriptUpdateCronSchedule_ + _WebUI_AutoFWUpdateCheckCronSchedule_ # elif [ "$setting_type" = "Script_Update_Cron_Job_SchedDays" ] then ScriptUpdateCronSchedDays="$setting_value" + _WebUI_AutoScriptUpdateCronSchedule_ # elif [ "$setting_type" = "FW_New_Update_ZIP_Directory_Path" ] then - FW_ZIP_BASE_DIR="$setting_value" - FW_ZIP_DIR="${setting_value}/$FW_ZIP_SUBDIR" - FW_ZIP_FPATH="${FW_ZIP_DIR}/${FW_FileName}.zip" + _SetUp_FW_UpdateZIP_DirectoryPaths_ "$setting_value" # elif [ "$setting_type" = "FW_New_Update_LOG_Directory_Path" ] - then # Addition for handling log directory path - FW_LOG_BASE_DIR="$setting_value" - FW_LOG_DIR="${setting_value}/$FW_LOG_SUBDIR" + then + _SetUp_FW_UpdateLOG_DirectoryPaths_ "$setting_value" fi ;; *) # Generic handling for arbitrary settings # - if grep -q "^${setting_type}=" "$SETTINGSFILE" + if grep -q "^${setting_type}=" "$CONFIG_FILE" then - oldVal="$(grep "^${setting_type}=" "$SETTINGSFILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" + oldVal="$(grep "^${setting_type}=" "$CONFIG_FILE" | awk -F '=' '{print $2}' | sed "s/['\"]//g")" if [ -z "$oldVal" ] || [ "$oldVal" != "$setting_value" ] then fixedVal="$(echo "$setting_value" | sed 's/[\/&]/\\&/g')" - sed -i "s/^${setting_type}=.*/${setting_type}=\"${fixedVal}\"/" "$SETTINGSFILE" + sed -i "s/^${setting_type}=.*/${setting_type}=\"${fixedVal}\"/" "$CONFIG_FILE" fi else - echo "${setting_type}=\"${setting_value}\"" >> "$SETTINGSFILE" + echo "${setting_type}=\"${setting_value}\"" >> "$CONFIG_FILE" fi ;; esac + return 0 } ##----------------------------------------## @@ -1197,122 +1451,347 @@ Update_Custom_Settings() ##----------------------------------------## Delete_Custom_Settings() { - if [ $# -lt 1 ] || [ -z "$1" ] || [ ! -f "$SETTINGSFILE" ] + if [ $# -lt 1 ] || [ -z "$1" ] || [ ! -f "$CONFIG_FILE" ] then return 1 ; fi local setting_type="$1" - sed -i "/^${setting_type}[ =]/d" "$SETTINGSFILE" + sed -i "/^${setting_type}[ =]/d" "$CONFIG_FILE" return $? } ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Jan-24] ## +## Modified by ExtremeFiretop [2024-Dec-21] ## ##------------------------------------------## -_Set_FW_UpdateLOG_DirectoryPath_() +_GetAllNodeSettings_() { - local newLogBaseDirPath="$FW_LOG_BASE_DIR" newLogFileDirPath="" - - while true - do - printf "\nEnter the directory path where the LOG subdirectory [${GRNct}${FW_LOG_SUBDIR}${NOct}] will be stored.\n" - printf "[${theADExitStr}] [CURRENT: ${GRNct}${FW_LOG_BASE_DIR}${NOct}]: " - read -r userInput + if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ] + then echo "**ERROR**" ; return 1; fi - if [ -z "$userInput" ] || echo "$userInput" | grep -qE "^(e|exit|Exit)$" - then break ; fi + ## Node Setting KEY="Node_{MACaddress}_{keySuffix}" ## + local fullKeyName="Node_${1}_${2}" + local setting_value="TBD" matched_lines - if echo "$userInput" | grep -q '/$' - then userInput="${userInput%/*}" ; fi + # Ensure the settings directory exists # + [ ! -d "$SETTINGS_DIR" ] && mkdir -m 755 -p "$SETTINGS_DIR" - if echo "$userInput" | grep -q '//' || \ - echo "$userInput" | grep -q '/$' || \ - ! echo "$userInput" | grep -q '^/' || \ - [ "${#userInput}" -lt 4 ] || \ - [ "$(echo "$userInput" | awk -F '/' '{print NF-1}')" -lt 2 ] - then - printf "\n${REDct}INVALID input.${NOct}\n" - _WaitForEnterKey_ - clear - continue - fi + if [ -f "$CONFIG_FILE" ] + then + matched_lines="$(grep -E "^${fullKeyName}=.*" "$CONFIG_FILE")" + if [ -n "$matched_lines" ] + then + # Extract the value from the first matched line # + setting_value="$(echo "$matched_lines" | head -n 1 | awk -F '=' '{print $2}' | tr -d '"')" + fi + fi + echo "$setting_value" +} - if [ -d "$userInput" ] - then newLogBaseDirPath="$userInput" ; break ; fi +##-------------------------------------## +## Added by Martinski W. [2025-Feb-23] ## +##-------------------------------------## +extCheckRETvarID=0x00 +extCheckZIPdirID=0x01 +extCheckLOGdirID=0x02 +extCheckALLvarID=0x0F +extCheckZIPdirOK=true +extCheckLOGdirOK=true +extCheckRETvarOK=true +extCheckZIPdirMG="OK" +extCheckLOGdirMG="OK" +extCheckRETvarMG="OK" - rootDir="${userInput%/*}" - if [ ! -d "$rootDir" ] - then - printf "\n${REDct}**ERROR**${NOct}: Root directory path [${REDct}${rootDir}${NOct}] does NOT exist.\n\n" - printf "\n${REDct}INVALID input.${NOct}\n" - _WaitForEnterKey_ - clear - continue - fi +##-------------------------------------## +## Added by Martinski W. [2025-Feb-25] ## +##-------------------------------------## +_WebUI_FW_UpdateZIPDirPathDefault_() +{ + local defltDirPath="/home/root" + if [ -n "$USBMountPoint" ] && \ + _ValidateUSBMountPoint_ "$FW_ZIP_BASE_DIR" + then defltDirPath="$FW_ZIP_BASE_DIR" ; fi + _WriteVarDefToHelperJSFile_ "defaultFWUpdateZIPdirPath" "$defltDirPath" +} - printf "The directory path '${REDct}${userInput}${NOct}' does NOT exist.\n\n" - if ! _WaitForYESorNO_ "Do you want to create it now" - then - printf "Directory was ${REDct}NOT${NOct} created.\n\n" - else - mkdir -m 755 "$userInput" 2>/dev/null - if [ -d "$userInput" ] - then newLogBaseDirPath="$userInput" ; break - else printf "\n${REDct}**ERROR**${NOct}: Could NOT create directory [${REDct}${userInput}${NOct}].\n\n" - fi - fi - done +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-25] ## +##----------------------------------------## +_InitHelperJSFile_() +{ + ! "$mountWebGUI_OK" && return 0 - # Double-check current directory indeed exists after menu selection # - if [ "$newLogBaseDirPath" = "$FW_LOG_BASE_DIR" ] && [ ! -d "$FW_LOG_DIR" ] - then mkdir -p -m 755 "$FW_LOG_DIR" ; fi + [ ! -s "$HELPER_JSFILE" ] && \ + { + echo "var externalCheckID = 0x00;" + echo "var externalCheckOK = true;" + echo "var externalCheckMsg = '';" + } > "$HELPER_JSFILE" - if [ "$newLogBaseDirPath" != "$FW_LOG_BASE_DIR" ] && [ -d "$newLogBaseDirPath" ] - then - if ! echo "$newLogBaseDirPath" | grep -qE "${FW_LOG_SUBDIR}$" - then newLogFileDirPath="${newLogBaseDirPath}/$FW_LOG_SUBDIR" ; fi - mkdir -p -m 755 "$newLogFileDirPath" 2>/dev/null - if [ ! -d "$newLogFileDirPath" ] - then - printf "\n${REDct}**ERROR**${NOct}: Could NOT create directory [${REDct}${newLogFileDirPath}${NOct}].\n" - _WaitForEnterKey_ - return 1 - fi - # Move any existing log files to new directory # - mv -f "${FW_LOG_DIR}"/*.log "$newLogFileDirPath" 2>/dev/null - # Remove now the obsolete directory path # - rm -fr "${FW_LOG_DIR:?}" - # Update the log directory path after validation # - Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$newLogBaseDirPath" - Update_Custom_Settings FW_New_Update_LOG_Preferred_Path "$newLogBaseDirPath" - echo "The directory path for the log files was updated successfully." - _WaitForEnterKey_ "$advnMenuReturnPromptStr" - fi - return 0 + _WebUI_FW_UpdateZIPDirPathDefault_ + _WebUI_SetEmailConfigFileFromAMTM_ + _WebUI_AutoScriptUpdateCronSchedule_ + _WebUI_AutoFWUpdateCheckCronSchedule_ } ##----------------------------------------## -## Modified by Martinski W. [2024-Jul-31] ## +## Modified by Martinski W. [2025-Feb-25] ## ##----------------------------------------## -_Set_FW_UpdateZIP_DirectoryPath_() +_UpdateHelperJSFile_() { - local newZIP_BaseDirPath="$FW_ZIP_BASE_DIR" newZIP_FileDirPath="" + if [ $# -lt 2 ] || \ + [ -z "$1" ] || [ -z "$2" ] || \ + ! "$mountWebGUI_OK" + then return 1; fi - while true + local extCheckMsg="" + if [ $# -gt 2 ] && [ -n "$3" ] + then extCheckMsg="$3" ; fi + + if [ "$(($1 & extCheckZIPdirID))" -gt 0 ] + then + extCheckZIPdirOK="$2" + extCheckZIPdirMG="$extCheckMsg" + fi + if [ "$(($1 & extCheckLOGdirID))" -gt 0 ] + then + extCheckLOGdirOK="$2" + extCheckLOGdirMG="$extCheckMsg" + fi + + if [ "$1" = "$extCheckALLvarID" ] || \ + [ "$extCheckZIPdirOK" = "$extCheckLOGdirOK" ] + then + extCheckRETvarOK="$extCheckZIPdirOK" + extCheckRETvarID="$((extCheckZIPdirID | extCheckLOGdirID))" + if "$extCheckZIPdirOK" + then + extCheckRETvarMG="$extCheckZIPdirMG" + else + extCheckRETvarMG="${extCheckZIPdirMG}\n\n${extCheckLOGdirMG}" + fi + elif ! "$extCheckZIPdirOK" + then + extCheckRETvarOK="$extCheckZIPdirOK" + extCheckRETvarID="$extCheckZIPdirID" + extCheckRETvarMG="$extCheckZIPdirMG" + elif ! "$extCheckLOGdirOK" + then + extCheckRETvarOK="$extCheckLOGdirOK" + extCheckRETvarID="$extCheckLOGdirID" + extCheckRETvarMG="$extCheckLOGdirMG" + fi + + { + echo "var externalCheckID = ${extCheckRETvarID};" + echo "var externalCheckOK = ${extCheckRETvarOK};" + echo "var externalCheckMsg = '${extCheckRETvarMG}';" + } > "$HELPER_JSFILE" + + _WebUI_FW_UpdateZIPDirPathDefault_ + _WebUI_SetEmailConfigFileFromAMTM_ + _WebUI_AutoScriptUpdateCronSchedule_ + _WebUI_AutoFWUpdateCheckCronSchedule_ +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-23] ## +##----------------------------------------## +_Validate_FW_UpdateLOG_DirectoryPath_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi + + local updateHelperJS=false + if [ $# -eq 2 ] && [ "$2" = "true" ] + then updateHelperJS=true ; fi + + if [ ! -d "$1" ] + then + if "$updateHelperJS" + then + checkErrorMsg="The directory path for F/W update log files is NOT found:\n[$1]" + _UpdateHelperJSFile_ "$extCheckLOGdirID" "false" "$checkErrorMsg" + fi + Say "${REDct}**ERROR**${NOct}: Directory path [${REDct}${1}${NOct}] for F/W update log files is NOT found." + _WaitForEnterKey_ + return 1 + fi + + if [ "$1" = "$FW_LOG_DIR" ] || [ "$1" = "$FW_LOG_BASE_DIR" ] + then + _UpdateHelperJSFile_ "$extCheckLOGdirID" "true" + return 0 + fi + + local newFullDirPath="" newBaseDirPath="$1" + + if echo "$newBaseDirPath" | grep -qE "/${FW_LOG_SUBDIR}$" + then newFullDirPath="$newBaseDirPath" + else newFullDirPath="${newBaseDirPath}/$FW_LOG_SUBDIR" + fi + mkdir -p -m 755 "$newFullDirPath" 2>/dev/null + if [ ! -d "$newFullDirPath" ] + then + if "$updateHelperJS" + then + checkErrorMsg="The directory path for F/W update log files cannot be created:\n[$newFullDirPath]" + _UpdateHelperJSFile_ "$extCheckLOGdirID" "false" "$checkErrorMsg" + fi + Say "${REDct}**ERROR**${NOct}: Could NOT create directory path [${REDct}${newFullDirPath}${NOct}] for F/W update log files." + _WaitForEnterKey_ + return 1 + fi + # Move any existing log files to new directory # + mv -f "${FW_LOG_DIR}"/*.log "$newFullDirPath" 2>/dev/null + # Remove now the obsolete directory path # + rm -fr "${FW_LOG_DIR:?}" + # Update the log directory paths after validation # + Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$newBaseDirPath" + Update_Custom_Settings FW_New_Update_LOG_Preferred_Path "$newBaseDirPath" + _UpdateHelperJSFile_ "$extCheckLOGdirID" "true" + return 0 +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-23] ## +##----------------------------------------## +_Set_FW_UpdateLOG_DirectoryPath_() +{ + local newLOG_BaseDirPath="$FW_LOG_BASE_DIR" + + while true do - if "$isGNUtonFW" + printf "\nEnter the directory path where the subdirectory [${GRNct}${FW_LOG_SUBDIR}${NOct}] will be located.\n" + printf "[${theLGExitStr}]\n" + printf "[Current Base Path: ${GRNct}${FW_LOG_BASE_DIR}${NOct}]: " + read -r userInput + + if [ -z "$userInput" ] ; then break ; fi + if echo "$userInput" | grep -qE "^(e|exit|Exit)$" ; then return 1 ; fi + + if echo "$userInput" | grep -q '/$' + then userInput="${userInput%/*}" ; fi + + if echo "$userInput" | grep -q '//' || \ + echo "$userInput" | grep -q '/$' || \ + ! echo "$userInput" | grep -q '^/' || \ + [ "${#userInput}" -lt 4 ] || \ + [ "$(echo "$userInput" | awk -F '/' '{print NF-1}')" -lt 2 ] then - printf "\nEnter the directory path where the update subdirectory [${GRNct}${FW_ZIP_SUBDIR}${NOct}] will be stored.\n" + printf "\n${REDct}INVALID input.${NOct}\n" + _WaitForEnterKey_ + clear + continue + fi + + if [ -d "$userInput" ] + then newLOG_BaseDirPath="$userInput" ; break ; fi + + rootDir="${userInput%/*}" + if [ ! -d "$rootDir" ] + then + printf "\n${REDct}**ERROR**${NOct}: Root directory path [${REDct}${rootDir}${NOct}] does NOT exist.\n\n" + printf "\n${REDct}INVALID input.${NOct}\n" + _WaitForEnterKey_ + clear + continue + fi + + printf "The directory path '${REDct}${userInput}${NOct}' does NOT exist.\n\n" + if ! _WaitForYESorNO_ "Do you want to create it now" + then + printf "Directory was ${REDct}NOT${NOct} created.\n\n" else - printf "\nEnter the directory path where the ZIP subdirectory [${GRNct}${FW_ZIP_SUBDIR}${NOct}] will be stored.\n" + mkdir -m 755 "$userInput" 2>/dev/null + if [ -d "$userInput" ] + then newLOG_BaseDirPath="$userInput" ; break + else printf "\n${REDct}**ERROR**${NOct}: Could NOT create directory [${REDct}${userInput}${NOct}].\n\n" + fi fi + done + + if [ -d "$newLOG_BaseDirPath" ] + then + if ! _Validate_FW_UpdateLOG_DirectoryPath_ "$newLOG_BaseDirPath" + then return 1 + fi + echo "The directory path for the log files was updated successfully." + _WaitForEnterKey_ "$logsMenuReturnPromptStr" + fi + return 0 +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-23] ## +##----------------------------------------## +_Validate_FW_UpdateZIP_DirectoryPath_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi + + local updateHelperJS=false + if [ $# -eq 2 ] && [ "$2" = "true" ] + then updateHelperJS=true ; fi + + if [ ! -d "$1" ] + then + if "$updateHelperJS" + then + checkErrorMsg="The directory path for F/W update files is NOT found:\n[$1]" + _UpdateHelperJSFile_ "$extCheckZIPdirID" "false" "$checkErrorMsg" + fi + Say "${REDct}**ERROR**${NOct}: Directory path [${REDct}${1}${NOct}] for F/W update files is NOT found." + _WaitForEnterKey_ + return 1 + fi + + if [ "$1" = "$FW_ZIP_DIR" ] || [ "$1" = "$FW_ZIP_BASE_DIR" ] + then + _UpdateHelperJSFile_ "$extCheckZIPdirID" "true" + return 0 + fi + + local newFullDirPath="" newBaseDirPath="$1" + + if echo "$newBaseDirPath" | grep -qE "/${FW_ZIP_SUBDIR}$" + then newFullDirPath="$newBaseDirPath" + else newFullDirPath="${newBaseDirPath}/$FW_ZIP_SUBDIR" + fi + mkdir -p -m 755 "$newFullDirPath" 2>/dev/null + if [ ! -d "$newFullDirPath" ] + then + if "$updateHelperJS" + then + checkErrorMsg="The directory path for F/W update files cannot be created:\n[$newFullDirPath]" + _UpdateHelperJSFile_ "$extCheckZIPdirID" "false" "$checkErrorMsg" + fi + Say "${REDct}**ERROR**${NOct}: Could NOT create directory path [${REDct}${newFullDirPath}${NOct}] for F/W update files." + _WaitForEnterKey_ + return 1 + fi + # Remove now the obsolete directory path # + rm -fr "${FW_ZIP_DIR:?}" + rm -f "${newFullDirPath}"/*.zip "${newFullDirPath}"/*.sha256 + Update_Custom_Settings FW_New_Update_ZIP_Directory_Path "$newBaseDirPath" + _UpdateHelperJSFile_ "$extCheckZIPdirID" "true" + return 0 +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-23] ## +##----------------------------------------## +_Set_FW_UpdateZIP_DirectoryPath_() +{ + local newZIP_BaseDirPath="$FW_ZIP_BASE_DIR" + + while true + do + printf "\nEnter the directory path where the update subdirectory [${GRNct}${FW_ZIP_SUBDIR}${NOct}] will be located.\n" if [ -n "$USBMountPoint" ] && _ValidateUSBMountPoint_ "$FW_ZIP_BASE_DIR" then printf "Default directory for USB-attached drive: [${GRNct}${FW_ZIP_BASE_DIR}${NOct}]\n" else printf "Default directory for 'Local' storage is: [${GRNct}/home/root${NOct}]\n" fi - printf "\n[${theADExitStr}] [CURRENT: ${GRNct}${FW_ZIP_BASE_DIR}${NOct}]: " - + printf "\n[${theADExitStr}]\n" + printf "[Current Base Path: ${GRNct}${FW_ZIP_BASE_DIR}${NOct}]: " read -r userInput if [ -z "$userInput" ] ; then break ; fi @@ -1359,21 +1838,11 @@ _Set_FW_UpdateZIP_DirectoryPath_() fi done - if [ "$newZIP_BaseDirPath" != "$FW_ZIP_BASE_DIR" ] && [ -d "$newZIP_BaseDirPath" ] + if [ -d "$newZIP_BaseDirPath" ] then - if ! echo "$newZIP_BaseDirPath" | grep -qE "${FW_ZIP_SUBDIR}$" - then newZIP_FileDirPath="${newZIP_BaseDirPath}/$FW_ZIP_SUBDIR" ; fi - mkdir -p -m 755 "$newZIP_FileDirPath" 2>/dev/null - if [ ! -d "$newZIP_FileDirPath" ] - then - printf "\n${REDct}**ERROR**${NOct}: Could NOT create directory [${REDct}${newZIP_FileDirPath}${NOct}].\n" - _WaitForEnterKey_ - return 1 + if ! _Validate_FW_UpdateZIP_DirectoryPath_ "$newZIP_BaseDirPath" + then return 1 fi - # Remove now the obsolete directory path # - rm -fr "${FW_ZIP_DIR:?}" - rm -f "${newZIP_FileDirPath}"/*.zip "${newZIP_FileDirPath}"/*.sha256 - Update_Custom_Settings FW_New_Update_ZIP_Directory_Path "$newZIP_BaseDirPath" if "$isGNUtonFW" then echo "The directory path for the F/W update file was updated successfully." @@ -1386,20 +1855,93 @@ _Set_FW_UpdateZIP_DirectoryPath_() return 0 } -_Init_Custom_Settings_Config_ +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-15] ## +##----------------------------------------## +## Function to migrate specific settings from old values to new standardized values. +## This function is meant to be only TEMPORARY. +## We should be safe to remove it after 3 months, or 5 version releases, +## whichever comes first. Similar to the migration function removed in v1.0.9 +##-----------------------------------------------------------------------------------## +_Migrate_Settings_() +{ + [ ! -s "$CONFIG_FILE" ] && return 1 -##------------------------------------------## -## Modified by ExtremeFiretop [2024-Jun-03] ## -##------------------------------------------## -# NOTE: -# ROG upgrades to 3006 codebase should have -# the ROG option deleted. -#----------------------------------------------------------- -if ! "$isGNUtonFW" -then - if [ "$fwInstalledBaseVers" -ge 3006 ] && grep -q "^ROGBuild" "$SETTINGSFILE" - then Delete_Custom_Settings "ROGBuild" ; fi -fi + ## Migrate Setting from [y|Y|n|N] to [ENABLED|DISABLED] ## + ROGBuild_Value="$(Get_Custom_Setting ROGBuild)" + if [ "$ROGBuild_Value" != "TBD" ] + then + case "$ROGBuild_Value" in + y|Y) New_ROGBuild_Value="ENABLED" ;; + n|N) New_ROGBuild_Value="DISABLED" ;; + *) + New_ROGBuild_Value="" + ! echo "$ROGBuild_Value" | grep -qE "^(ENABLED|DISABLED)$" && \ + Say "ROGBuild has a unknown value: '$ROGBuild_Value'. Skipping migration for this setting." + ;; + esac + if [ -n "$New_ROGBuild_Value" ] + then + if Update_Custom_Settings ROGBuild "$New_ROGBuild_Value" + then + Say "ROGBuild setting was successfully migrated to '$New_ROGBuild_Value'." + else + Say "Error occurred while migrating ROGBuild setting to '$New_ROGBuild_Value'." + fi + fi + fi + + ## Migrate Setting from [y|Y|n|N] to [ENABLED|DISABLED] ## + TUFBuild_Value="$(Get_Custom_Setting TUFBuild)" + if [ "$TUFBuild_Value" != "TBD" ] + then + case "$TUFBuild_Value" in + y|Y) New_TUFBuild_Value="ENABLED" ;; + n|N) New_TUFBuild_Value="DISABLED" ;; + *) + New_TUFBuild_Value="" + ! echo "$TUFBuild_Value" | grep -qE "^(ENABLED|DISABLED)$" && \ + Say "TUFBuild has a unknown value: '$TUFBuild_Value'. Skipping migration for this setting." + ;; + esac + if [ -n "$New_TUFBuild_Value" ] + then + if Update_Custom_Settings TUFBuild "$New_TUFBuild_Value" + then + Say "TUFBuild setting was successfully migrated to '$New_TUFBuild_Value'." + else + Say "Error occurred while migrating TUFBuild setting to '$New_TUFBuild_Value'." + fi + fi + fi + + ## Migrate Setting from [true|false] to [ENABLED|DISABLED] ## + EMailNotif_Value="$(grep '^FW_New_Update_EMail_Notification=' "$CONFIG_FILE" | cut -d'=' -f2 | tr -d '"')" + if [ -n "$EMailNotif_Value" ] && [ "$EMailNotif_Value" != "TBD" ] + then + case "$EMailNotif_Value" in + true|TRUE|True) New_EMailNotif_Value="ENABLED" ;; + false|FALSE|False) New_EMailNotif_Value="DISABLED" ;; + *) + New_EMailNotif_Value="" + ! echo "$EMailNotif_Value" | grep -qE "^(ENABLED|DISABLED)$" && \ + Say "FW_New_Update_EMail_Notification has a unknown value: '$EMailNotif_Value'. Skipping migration for this setting." + ;; + esac + if [ -n "$New_EMailNotif_Value" ] + then + sed -i '/^FW_New_Update_EMail_Notification .*/d' "$CONFIG_FILE" + sed -i "s/^FW_New_Update_EMail_Notification=.*/FW_New_Update_EMail_Notification $New_EMailNotif_Value/" "$CONFIG_FILE" + if [ $? -eq 0 ] + then + sendEMailNotificationsFlag="$New_EMailNotif_Value" + Say "EMail_Notification setting was successfully migrated to $New_EMailNotif_Value." + else + Say "Error occurred while migrating EMail_Notification setting to $New_EMailNotif_Value." + fi + fi + fi +} ##------------------------------------------## ## Modified by ExtremeFiretop [2024-Jan-27] ## @@ -1410,110 +1952,639 @@ fi # storage for the ZIP file so that it can be downloaded # in a separate directory from the firmware bin file. #----------------------------------------------------------- -FW_BIN_BASE_DIR="/home/root" -FW_ZIP_BASE_DIR="$(Get_Custom_Setting FW_New_Update_ZIP_Directory_Path)" -FW_LOG_BASE_DIR="$(Get_Custom_Setting FW_New_Update_LOG_Directory_Path)" - readonly FW_LOG_SUBDIR="${ScriptDirNameD}/logs" readonly FW_BIN_SUBDIR="${ScriptDirNameD}/$FW_FileName" readonly FW_ZIP_SUBDIR="${ScriptDirNameD}/$FW_FileName" +FW_BIN_BASE_DIR="/home/root" FW_BIN_DIR="${FW_BIN_BASE_DIR}/$FW_BIN_SUBDIR" -FW_LOG_DIR="${FW_LOG_BASE_DIR}/$FW_LOG_SUBDIR" -FW_ZIP_DIR="${FW_ZIP_BASE_DIR}/$FW_ZIP_SUBDIR" -FW_ZIP_FPATH="${FW_ZIP_DIR}/${FW_FileName}.zip" -##----------------------------------------------## -## Added/Modified by Martinski W. [2023-Nov-24] ## -##----------------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-15] ## +##----------------------------------------## +_SetUp_FW_UpdateZIP_DirectoryPaths_ +_SetUp_FW_UpdateLOG_DirectoryPaths_ + +##----------------------------------------## +## Modified by Martinski W. [2023-Nov-24] ## +##----------------------------------------## # The built-in F/W hook script file to be used for # setting up persistent jobs to run after a reboot. readonly hookScriptFName="services-start" readonly hookScriptFPath="${SCRIPTS_PATH}/$hookScriptFName" readonly hookScriptTagStr="#Added by $ScriptFNameTag#" -# Postponement Days for F/W Update Check # -FW_UpdatePostponementDays="$(Get_Custom_Setting FW_New_Update_Postponement_Days)" -FW_UpdateExpectedRunDate="$(Get_Custom_Setting FW_New_Update_Expected_Run_Date)" +# Postponement Days for F/W Update Check # +FW_UpdatePostponementDays="$(Get_Custom_Setting FW_New_Update_Postponement_Days)" +FW_UpdateExpectedRunDate="$(Get_Custom_Setting FW_New_Update_Expected_Run_Date)" + +##----------------------------------------## +## Modified by Martinski W. [2024-Feb-18] ## +##----------------------------------------## +# F/W Update Email Notifications # +isEMailFormatHTML=true +isEMailConfigEnabledInAMTM=false +sendEMailFormaType="$(Get_Custom_Setting FW_New_Update_EMail_FormatType)" +sendEMailNotificationsFlag="$(Get_Custom_Setting FW_New_Update_EMail_Notification)" +sendEMail_CC_Name="$(Get_Custom_Setting FW_New_Update_EMail_CC_Name)" +sendEMail_CC_Address="$(Get_Custom_Setting FW_New_Update_EMail_CC_Address)" +if [ "$sendEMailFormaType" = "HTML" ] +then isEMailFormatHTML=true +else isEMailFormatHTML=false +fi + +##----------------------------------------## +## Modified by Martinski W. [2024-Nov-27] ## +##----------------------------------------## +# Define the CRON job command to execute # +# Define the hook script file to be used # +ScriptAutoUpdateSetting="$(Get_Custom_Setting Allow_Script_Auto_Update)" +ScriptUpdateCronSchedDays="$(Get_Custom_Setting Script_Update_Cron_Job_SchedDays)" + +readonly SCRIPT_UP_CRON_JOB_RUN="sh $ScriptFilePath checkupdates" +readonly SCRIPT_UP_CRON_JOB_TAG="${ScriptFNameTag}_ScriptUpdate" +readonly DAILY_SCRIPT_UPDATE_CHECK_JOB="sh $ScriptFilePath scriptAUCronJob & $hookScriptTagStr" +readonly DAILY_SCRIPT_UPDATE_CHECK_HOOK="[ -f $ScriptFilePath ] && $DAILY_SCRIPT_UPDATE_CHECK_JOB" + +# Define the CRON job command to execute # +FW_UpdateCronJobSchedule="$(Get_Custom_Setting FW_New_Update_Cron_Job_Schedule)" +readonly CRON_JOB_RUN="sh $ScriptFilePath run_now" +readonly CRON_JOB_TAG_OLD="$ScriptFNameTag" +readonly CRON_JOB_TAG="${ScriptFNameTag}_FWUpdate" +readonly CRON_SCRIPT_JOB="sh $ScriptFilePath addCronJob & $hookScriptTagStr" +readonly CRON_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $CRON_SCRIPT_JOB" + +# Define post-reboot run job command to execute # +readonly POST_REBOOT_SCRIPT_JOB="sh $ScriptFilePath postRebootRun & $hookScriptTagStr" +readonly POST_REBOOT_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $POST_REBOOT_SCRIPT_JOB" + +# Define post-update email notification job command to execute # +readonly POST_UPDATE_EMAIL_SCRIPT_JOB="sh $ScriptFilePath postUpdateEmail & $hookScriptTagStr" +readonly POST_UPDATE_EMAIL_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $POST_UPDATE_EMAIL_SCRIPT_JOB" + +if [ -d "$FW_LOG_DIR" ] +then + # Log rotation - delete logs older than 30 days # + /usr/bin/find -L "$FW_LOG_DIR" -name '*.log' -mtime +30 -exec rm {} \; +fi + +##----------------------------------------## +## Modified by Martinski W. [2024-Jan-27] ## +##----------------------------------------## +#------------------------------------------------------------------------------------------- +# This code is in case the user-selected USB mount point isn't available anymore. +# If the USB drive is selected as the log location but it goes offline for some reason, +# any call to the "Say" function creates a new '/tmp/mnt/XXXX' directory. +# In such a case where the USB drive is unmounted, we need to change the log directory +# back to a local directory. First if-statement executes first and updates to local 'jffs' +# directory if no USB drives are found. If ANY DefaultUSBMountPoint found, then move the +# log files from their local jffs location to the default mount location. +# We don't know the user selected yet because it's local at this time and was changed +# by the else statement. Remove the old log directory location from jffs, and update the +# settings file again to the new default again. This creates a semi-permanent switch which +# can reset back to default if the user-selected mount points aren't valid anymore. +#------------------------------------------------------------------------------------------- +UserSelectedLogPath="$(Get_Custom_Setting FW_New_Update_LOG_Directory_Path)" +if [ ! -d "$UserSelectedLogPath" ] || [ ! -r "$UserSelectedLogPath" ]; then + Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$ADDONS_PATH" +fi + +UserPreferredLogPath="$(Get_Custom_Setting FW_New_Update_LOG_Preferred_Path)" +if echo "$UserPreferredLogPath" | grep -qE "^(/tmp/mnt/|/tmp/opt/|/opt/)" && \ + _ValidateUSBMountPoint_ "$UserPreferredLogPath" && \ + [ "$UserPreferredLogPath" != "$FW_LOG_BASE_DIR" ] +then + mv -f "${FW_LOG_DIR}"/*.log "${UserPreferredLogPath}/$FW_LOG_SUBDIR" 2>/dev/null + rm -fr "${FW_LOG_DIR:?}" + Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$UserPreferredLogPath" +fi + +##-------------------------------------## +## Added by Martinski W. [2025-Feb-12] ## +##-------------------------------------## +_Check_WebGUI_Page_Exists_() +{ + local webPageStr webPageFile theWebPage + + if [ ! -f "$TEMP_MENU_TREE" ] + then echo "NONE" ; return 1 ; fi + + theWebPage="NONE" + webPageStr="$(grep -E -m1 "^$webPageLineRegExp" "$TEMP_MENU_TREE")" + if [ -n "$webPageStr" ] + then + webPageFile="$(echo "$webPageStr" | grep -owE "$webPageFileRegExp" | head -n1)" + if [ -n "$webPageFile" ] && [ -s "${SHARED_WEB_DIR}/$webPageFile" ] + then theWebPage="$webPageFile" ; fi + fi + echo "$theWebPage" +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-12] ## +##----------------------------------------## +_GetWebUIPage_() +{ + local webPageFile webPagePath webPageTemp + + webPageFile="$(_Check_WebGUI_Page_Exists_)" + + for index in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + do + webPageTemp="user${index}.asp" + webPagePath="${SHARED_WEB_DIR}/$webPageTemp" + + if [ -s "$webPagePath" ] && \ + [ "$(md5sum < "$1")" = "$(md5sum < "$webPagePath")" ] + then + webPageFile="$webPageTemp" + break + elif [ "$webPageFile" = "NONE" ] && [ ! -s "$webPagePath" ] + then + webPageFile="$webPageTemp" + fi + done + echo "$webPageFile" +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-11] ## +##----------------------------------------## +_Mount_WebUI_() +{ + if [ ! -f "$SCRIPT_WEB_ASP_PATH" ] + then + Say "${CRITct}**ERROR**${NOct}: The WebUI page file for $SCRIPT_NAME is NOT found." + return 1 + fi + local webPageFile + + Say "Mounting WebUI page for ${SCRIPT_NAME}..." + + eval exec "$WEBUI_LOCKFD>$WEBUI_LOCKFILE" + flock -x "$WEBUI_LOCKFD" + + webPageFile="$(_GetWebUIPage_ "$SCRIPT_WEB_ASP_PATH")" + if [ -z "$webPageFile" ] || [ "$webPageFile" = "NONE" ] + then + Say "${CRITct}**ERROR**${NOct}: Unable to mount the $SCRIPT_NAME WebUI page." + flock -u "$WEBUI_LOCKFD" + return 1 + fi + + cp -fp "$SCRIPT_WEB_ASP_PATH" "${SHARED_WEB_DIR}/$webPageFile" + echo "$SCRIPT_NAME" > "${SHARED_WEB_DIR}/$(echo "$webPageFile" | cut -f1 -d'.').title" + + if [ ! -f "$TEMP_MENU_TREE" ] + then cp -fp "$ORIG_MENU_TREE" "$TEMP_MENU_TREE" + fi + sed -i "/url: \"$webPageFile\", tabName: \"$SCRIPT_NAME\"/d" "$TEMP_MENU_TREE" + + # Insert new page tab in the 'Administration' menu # + sed -i "/url: \"Advanced_FirmwareUpgrade_Content.asp\", tabName:/a {url: \"$webPageFile\", tabName: \"$SCRIPT_NAME\"}," "$TEMP_MENU_TREE" + + umount "$ORIG_MENU_TREE" 2>/dev/null + mount -o bind "$TEMP_MENU_TREE" "$ORIG_MENU_TREE" + flock -u "$WEBUI_LOCKFD" + + Say "${GRNct}$SCRIPT_NAME WebUI page was mounted as $webPageFile successfully." + return 0 +} + +##-------------------------------------## +## Added by Martinski W. [2025-Feb-12] ## +##-------------------------------------## +_CheckFor_WebGUI_Page_() +{ + if "$mountWebGUI_OK" && \ + [ "$(_Check_WebGUI_Page_Exists_)" = "NONE" ] + then _Mount_WebUI_ ; fi +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-11] ## +##----------------------------------------## +_Unmount_WebUI_() +{ + if [ ! -f "$SCRIPT_WEB_ASP_PATH" ] + then + Say "${CRITct}**ERROR**${NOct}: The WebUI page file for $SCRIPT_NAME is NOT found." + return 1 + fi + local webPageFile + + Say "Unmounting WebUI page for $SCRIPT_NAME" + + eval exec "$WEBUI_LOCKFD>$WEBUI_LOCKFILE" + flock -x "$WEBUI_LOCKFD" + + webPageFile="$(_GetWebUIPage_ "$SCRIPT_WEB_ASP_PATH")" + if [ -z "$webPageFile" ] || [ "$webPageFile" = "NONE" ] + then + Say "WebUI page file for $SCRIPT_NAME is NOT found to uninstall." + flock -u "$WEBUI_LOCKFD" + return 1 + fi + + if [ -f "$TEMP_MENU_TREE" ] + then + sed -i "/url: \"$webPageFile\", tabName: \"$SCRIPT_NAME\"/d" "$TEMP_MENU_TREE" + fi + rm -f "${SHARED_WEB_DIR}/$webPageFile" + rm -f "${SHARED_WEB_DIR}/$(echo "$webPageFile" | cut -f1 -d'.').title" + + umount "$ORIG_MENU_TREE" 2>/dev/null + mount -o bind "$TEMP_MENU_TREE" "$ORIG_MENU_TREE" + flock -u "$WEBUI_LOCKFD" + + Say "${GRNct}$SCRIPT_NAME WebUI page unmounted successfully." + /sbin/service restart_httpd >/dev/null 2>&1 & + return 0 +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-12] ## +##----------------------------------------## +_AutoStartupHook_() +{ + local theScriptNameTag="#${SCRIPT_NAME}#" + local theHookScriptFile="${SCRIPTS_PATH}/services-start" + local theScriptFilePath="${SCRIPTS_PATH}/${SCRIPT_NAME}.sh" + + case "$1" in + create) + if [ -f "$theHookScriptFile" ] + then + theLineCount="$(grep -c "$theScriptNameTag" "$theHookScriptFile")" + theLineCountEx="$(grep -cx '\[ -x '"$theScriptFilePath"' \] && '"$theScriptFilePath"' startup "$@" & '"$theScriptNameTag" "$theHookScriptFile")" + + if [ "$theLineCount" -gt 1 ] || { [ "$theLineCountEx" -eq 0 ] && [ "$theLineCount" -gt 0 ] ; } + then + sed -i "/${theScriptNameTag}/d" "$theHookScriptFile" + fi + if [ "$theLineCountEx" -eq 0 ] + then + { + echo '[ -x '"$theScriptFilePath"' ] && '"$theScriptFilePath"' startup "$@" & '"$theScriptNameTag" + } >> "$theHookScriptFile" + fi + else + { + echo "#!/bin/sh" ; echo + echo '[ -x '"$theScriptFilePath"' ] && '"$theScriptFilePath"' startup "$@" & '"$theScriptNameTag" + echo + } > "$theHookScriptFile" + fi + chmod 755 "$theHookScriptFile" + ;; + delete) + if [ -f "$theHookScriptFile" ] && \ + { grep -q "$theScriptNameTag" "$theHookScriptFile" || \ + grep -q "$theScriptFilePath" "$theHookScriptFile" ; } + then + theFixedPath="$(echo "$theScriptFilePath" | sed 's/[\/.]/\\&/g')" + sed -i "/${theScriptNameTag}/d" "$theHookScriptFile" + sed -i "/$theFixedPath startup/d" "$theHookScriptFile" + fi + ;; + esac +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-12] ## +##----------------------------------------## +_AutoServiceEvent_() +{ + local theScriptNameTag="#${SCRIPT_NAME}#" + local theHookScriptFile="${SCRIPTS_PATH}/service-event" + local theScriptFilePath="${SCRIPTS_PATH}/${SCRIPT_NAME}.sh" + + case "$1" in + create) + if [ -f "$theHookScriptFile" ] + then + theLineCount="$(grep -c "$theScriptNameTag" "$theHookScriptFile")" + theLineCountEx="$(grep -cx 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" ; then { '"$theScriptFilePath"' service_event "$@" & }; fi '"$theScriptNameTag" "$theHookScriptFile")" + + if [ "$theLineCount" -gt 1 ] || { [ "$theLineCountEx" -eq 0 ] && [ "$theLineCount" -gt 0 ]; } + then + sed -i "/${theScriptNameTag}/d" "$theHookScriptFile" + fi + if [ "$theLineCountEx" -eq 0 ] + then + { + echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" ; then { '"$theScriptFilePath"' service_event "$@" & }; fi '"$theScriptNameTag" + } >> "$theHookScriptFile" + fi + else + { + echo "#!/bin/sh" ; echo + echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" ; then { '"$theScriptFilePath"' service_event "$@" & }; fi '"$theScriptNameTag" + echo + } > "$theHookScriptFile" + fi + chmod 755 "$theHookScriptFile" + ;; + delete) + if [ -f "$theHookScriptFile" ] && \ + { grep -q "$theScriptNameTag" "$theHookScriptFile" || \ + grep -q "$theScriptFilePath" "$theHookScriptFile" ; } + then + theFixedPath="$(echo "$theScriptFilePath" | sed 's/[\/.]/\\&/g')" + sed -i "/${theScriptNameTag}/d" "$theHookScriptFile" + sed -i "/$theFixedPath service_event/d" "$theHookScriptFile" + fi + ;; + esac +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-05] ## +##----------------------------------------## +_SetVersionSharedSettings_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] || \ + ! echo "$1" | grep -qE "^(local|server|delete)$" + then return 1; fi + + if [ "$1" = "delete" ] + then + if [ -f "$SHARED_SETTINGS_FILE" ] + then + if grep -q "^MerlinAU_version_" "$SHARED_SETTINGS_FILE" + then + sed -i "/^MerlinAU_version_local/d" "$SHARED_SETTINGS_FILE" + sed -i "/^MerlinAU_version_server/d" "$SHARED_SETTINGS_FILE" + fi + fi + return 0 + fi + if [ $# -lt 2 ] || [ -z "$2" ] ; then return 1; fi + + local versionTypeStr="" + [ "$1" = "local" ] && versionTypeStr="MerlinAU_version_local" + [ "$1" = "server" ] && versionTypeStr="MerlinAU_version_server" + + if [ -f "$SHARED_SETTINGS_FILE" ] + then + if grep -q "^${versionTypeStr}.*" "$SHARED_SETTINGS_FILE" + then + if [ "$2" != "$(grep "^$versionTypeStr" "$SHARED_SETTINGS_FILE" | cut -f2 -d' ')" ] + then + sed -i "s/^${versionTypeStr}.*/$versionTypeStr $2/" "$SHARED_SETTINGS_FILE" + fi + else + echo "$versionTypeStr $2" >> "$SHARED_SETTINGS_FILE" + fi + else + echo "$versionTypeStr $2" > "$SHARED_SETTINGS_FILE" + fi + return 0 +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-20] ## +##----------------------------------------## +_CreateDirPaths_() +{ + if [ ! -d "$SETTINGS_DIR" ] + then + mkdir -p "$SETTINGS_DIR" + chmod 755 "$SETTINGS_DIR" + fi + ! "$mountWebGUI_OK" && return 0 + + if [ ! -d "$SCRIPT_WEB_DIR" ] + then + mkdir -p "$SCRIPT_WEB_DIR" + chmod 775 "$SCRIPT_WEB_DIR" + fi +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-07] ## +##----------------------------------------## +_CreateSymLinks_() +{ + if [ -d "$SCRIPT_WEB_DIR" ] + then + rm -rf "${SCRIPT_WEB_DIR:?}/"* 2>/dev/null + fi + ! "$mountWebGUI_OK" && return 0 + + ln -sf "$CONFIG_FILE" "${SCRIPT_WEB_DIR}/config.htm" 2>/dev/null + ln -sf "$HELPER_JSFILE" "${SCRIPT_WEB_DIR}/checkHelper.js" 2>/dev/null + ln -sf "$PSWD_CHECK_JS" "${SCRIPT_WEB_DIR}/pswdCheckStatus.js" 2>/dev/null + ln -sf "$CHANGELOG_PATH" "${SCRIPT_WEB_DIR}/changelog.htm" 2>/dev/null +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-07] ## +##----------------------------------------## +_WriteVarDefToHelperJSFile_() +{ + if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ] + then return 1; fi + + local varValue fixedVal + if [ $# -eq 3 ] && [ "$3" = "true" ] + then varValue="$2" + else varValue="'${2}'" + fi + + if [ ! -s "$HELPER_JSFILE" ] + then + echo "var $1 = ${varValue};" > "$HELPER_JSFILE" + elif ! grep -q "^var $1 =.*" "$HELPER_JSFILE" + then + echo "var $1 = ${varValue};" >> "$HELPER_JSFILE" + elif ! grep -q "^var $1 = ${varValue};" "$HELPER_JSFILE" + then + fixedVal="$(echo "$varValue" | sed 's/[\/&]/\\&/g')" + sed -i "s/^var $1 =.*/var $1 = ${fixedVal};/" "$HELPER_JSFILE" + fi +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-20] ## +##-------------------------------------## +_WebUI_AutoFWUpdateCheckCronSchedule_() +{ + ! "$mountWebGUI_OK" && return 0 + local fwUpdtCronScheduleRaw fwUpdtCronScheduleStr + fwUpdtCronScheduleRaw="$(Get_Custom_Setting FW_New_Update_Cron_Job_Schedule)" + fwUpdtCronScheduleStr="$(_TranslateCronSchedHR_ "$fwUpdtCronScheduleRaw")" + _WriteVarDefToHelperJSFile_ "fwAutoUpdateCheckCronSchedHR" "$fwUpdtCronScheduleStr" +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-20] ## +##-------------------------------------## +_WebUI_AutoScriptUpdateCronSchedule_() +{ + ! "$mountWebGUI_OK" && return 0 + local scriptUpdtCronSchedRaw scriptUpdtCronSchedStr + scriptUpdtCronSchedRaw="$(_GetScriptAutoUpdateCronSchedule_)" + scriptUpdtCronSchedStr="$(_TranslateCronSchedHR_ "$scriptUpdtCronSchedRaw")" + _WriteVarDefToHelperJSFile_ "scriptAutoUpdateCronSchedHR" "$scriptUpdtCronSchedStr" +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-27] ## +##-------------------------------------## +_WebUI_SetEmailConfigFileFromAMTM_() +{ + ! "$mountWebGUI_OK" && return 0 + _CheckEMailConfigFileFromAMTM_ 0 + _WriteVarDefToHelperJSFile_ "isEMailConfigEnabledInAMTM" "$isEMailConfigEnabledInAMTM" true +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-15] ## +##-------------------------------------## +_ActionsAfterNewConfigSettings_() +{ + if [ ! -s "${CONFIG_FILE}.bak" ] || \ + diff -q "$CONFIG_FILE" "${CONFIG_FILE}.bak" >/dev/null 2>&1 + then return 1 ; fi -##----------------------------------------## -## Modified by Martinski W. [2024-Feb-18] ## -##----------------------------------------## -# F/W Update Email Notifications # -isEMailFormatHTML=true -isEMailConfigEnabledInAMTM=false -sendEMailFormaType="$(Get_Custom_Setting FW_New_Update_EMail_FormatType)" -sendEMailNotificationsFlag="$(Get_Custom_Setting FW_New_Update_EMail_Notification)" -sendEMail_CC_Name="$(Get_Custom_Setting FW_New_Update_EMail_CC_Name)" -sendEMail_CC_Address="$(Get_Custom_Setting FW_New_Update_EMail_CC_Address)" -[ "$sendEMailFormaType" = "HTML" ] && \ -isEMailFormatHTML=true || isEMailFormatHTML=false + _ConfigOptionChanged_() + { + if diff "$CONFIG_FILE" "${CONFIG_FILE}.bak" | grep -q "$1" + then return 0 + else return 1 + fi + } + local ccNewEmailAddr ccNewEmailName newScriptAUpdateVal + + if _ConfigOptionChanged_ "FW_New_Update_EMail_CC_Address=" + then + ccNewEmailAddr="$(Get_Custom_Setting FW_New_Update_EMail_CC_Address)" + ccNewEmailName="${ccNewEmailAddr%%@*}" + Update_Custom_Settings FW_New_Update_EMail_CC_Name "$ccNewEmailName" + fi + if _ConfigOptionChanged_ "FW_New_Update_Postponement_Days=" + then + _Calculate_NextRunTime_ + fi + if _ConfigOptionChanged_ "Allow_Script_Auto_Update" + then + ScriptAutoUpdateSetting="$(Get_Custom_Setting Allow_Script_Auto_Update)" + if [ "$ScriptAutoUpdateSetting" = "DISABLED" ] + then + _DelScriptAutoUpdateHook_ + _DelScriptAutoUpdateCronJob_ + elif [ "$ScriptAutoUpdateSetting" = "ENABLED" ] + then + scriptUpdateCronSched="$(_GetScriptAutoUpdateCronSchedule_)" + if _ValidateCronJobSchedule_ "$scriptUpdateCronSched" + then + _AddScriptAutoUpdateCronJob_ && _AddScriptAutoUpdateHook_ + fi + fi + fi + if _ConfigOptionChanged_ "CheckChangeLog" + then + currentChangelogValue="$(Get_Custom_Setting CheckChangeLog)" + if [ "$currentChangelogValue" = "DISABLED" ] + then + Delete_Custom_Settings "FW_New_Update_Changelog_Approval" + elif [ "$currentChangelogValue" = "ENABLED" ] + then + Update_Custom_Settings FW_New_Update_Changelog_Approval TBD + fi + fi +} ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-27] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## -# Define the CRON job command to execute # -# Define the hook script file to be used # -ScriptAutoUpdateSetting="$(Get_Custom_Setting Allow_Script_Auto_Update)" -ScriptUpdateCronSchedDays="$(Get_Custom_Setting Script_Update_Cron_Job_SchedDays)" +_UpdateConfigFromWebUISettings_() +{ + [ ! -f "$SHARED_SETTINGS_FILE" ] && return 1 -readonly SCRIPT_UP_CRON_JOB_RUN="sh $ScriptFilePath checkupdates" -readonly SCRIPT_UP_CRON_JOB_TAG="${ScriptFNameTag}_ScriptUpdate" -readonly DAILY_SCRIPT_UPDATE_CHECK_JOB="sh $ScriptFilePath scriptAUCronJob & $hookScriptTagStr" -readonly DAILY_SCRIPT_UPDATE_CHECK_HOOK="[ -f $ScriptFilePath ] && $DAILY_SCRIPT_UPDATE_CHECK_JOB" + local settingsMergeOK=true logMsgTag="with errors." + local oldLoginCredsENC doRouterLoginTest=false -# Define the CRON job command to execute # -FW_UpdateCronJobSchedule="$(Get_Custom_Setting FW_New_Update_Cron_Job_Schedule)" -readonly CRON_JOB_RUN="sh $ScriptFilePath run_now" -readonly CRON_JOB_TAG_OLD="$ScriptFNameTag" -readonly CRON_JOB_TAG="${ScriptFNameTag}_FWUpdate" -readonly CRON_SCRIPT_JOB="sh $ScriptFilePath addCronJob & $hookScriptTagStr" -readonly CRON_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $CRON_SCRIPT_JOB" + # Check for 'MerlinAU_' entries excluding 'version' # + if [ "$(grep "^MerlinAU_" "$SHARED_SETTINGS_FILE" | grep -vc "_version")" -gt 0 ] + then + Say "Updated settings from WebUI found, merging into $CONFIG_FILE" + cp -a "$CONFIG_FILE" "${CONFIG_FILE}.bak" -# Define post-reboot run job command to execute # -readonly POST_REBOOT_SCRIPT_JOB="sh $ScriptFilePath postRebootRun & $hookScriptTagStr" -readonly POST_REBOOT_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $POST_REBOOT_SCRIPT_JOB" + # Extract 'MerlinAU_' entries excluding 'version' # + grep "^MerlinAU_" "$SHARED_SETTINGS_FILE" | grep -v "_version" > "$TEMPFILE" + sed -i 's/^MerlinAU_//g;s/ /=/g' "$TEMPFILE" -# Define post-update email notification job command to execute # -readonly POST_UPDATE_EMAIL_SCRIPT_JOB="sh $ScriptFilePath postUpdateEmail & $hookScriptTagStr" -readonly POST_UPDATE_EMAIL_SCRIPT_HOOK="[ -f $ScriptFilePath ] && $POST_UPDATE_EMAIL_SCRIPT_JOB" + while IFS='' read -r line || [ -n "$line" ] + do + keySettingName="$(echo "$line" | cut -f1 -d'=')" + keySettingValue="$(echo "$line" | cut -f2- -d'=')" -if [ -d "$FW_LOG_DIR" ] -then - # Log rotation - delete logs older than 30 days # - /usr/bin/find -L "$FW_LOG_DIR" -name '*.log' -mtime +30 -exec rm {} \; -fi + if [ "$keySettingName" = "FW_New_Update_ZIP_Directory_Path" ] + then + if _Validate_FW_UpdateZIP_DirectoryPath_ "$keySettingValue" true + then + Say "Directory path [$keySettingValue] was updated successfully." + else + settingsMergeOK=false + Say "**ERROR**: Could NOT update directory path [$keySettingValue]" + fi + continue + elif [ "$keySettingName" = "FW_New_Update_LOG_Directory_Path" ] + then + if _Validate_FW_UpdateLOG_DirectoryPath_ "$keySettingValue" true + then + Say "Directory path [$keySettingValue] was updated successfully." + else + settingsMergeOK=false + Say "**ERROR**: Could NOT update directory path [$keySettingValue]" + fi + continue + fi + if [ "$keySettingName" = "FW_New_Update_Cron_Job_Schedule" ] + then # Replace delimiter char placed by the WebGUI # + keySettingValue="$(echo "$keySettingValue" | sed 's/|/ /g')" + fi + if [ "$keySettingName" = "credentials_base64" ] + then + oldLoginCredsENC="$(Get_Custom_Setting credentials_base64)" + if [ "$oldLoginCredsENC" = "$keySettingValue" ] + then _UpdateLoginPswdCheckHelper_ OldPSWD + else _UpdateLoginPswdCheckHelper_ NewPSWD + fi + doRouterLoginTest="$runLoginCredentialsTest" + fi + Update_Custom_Settings "$keySettingName" "$keySettingValue" + done < "$TEMPFILE" -##----------------------------------------## -## Modified by Martinski W. [2024-Jan-27] ## -##----------------------------------------## -#------------------------------------------------------------------------------------------- -# This code is in case the user-selected USB mount point isn't available anymore. -# If the USB drive is selected as the log location but it goes offline for some reason, -# any call to the "Say" function creates a new '/tmp/mnt/XXXX' directory. -# In such a case where the USB drive is unmounted, we need to change the log directory -# back to a local directory. First if-statement executes first and updates to local 'jffs' -# directory if no USB drives are found. If ANY DefaultUSBMountPoint found, then move the -# log files from their local jffs location to the default mount location. -# We don't know the user selected yet because it's local at this time and was changed -# by the else statement. Remove the old log directory location from jffs, and update the -# settings file again to the new default again. This creates a semi-permanent switch which -# can reset back to default if the user-selected mount points aren't valid anymore. -#------------------------------------------------------------------------------------------- -UserSelectedLogPath="$(Get_Custom_Setting FW_New_Update_LOG_Directory_Path)" -if [ ! -d "$UserSelectedLogPath" ] || [ ! -r "$UserSelectedLogPath" ]; then - Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$ADDONS_PATH" -fi + # Extract 'MerlinAU_version_*' separately (if found) # + grep '^MerlinAU_version_.*' "$SHARED_SETTINGS_FILE" > "$TEMPFILE" + # Now remove all 'MerlinAU_*' entries # + sed -i "/^MerlinAU_.*/d" "$SHARED_SETTINGS_FILE" -UserPreferredLogPath="$(Get_Custom_Setting FW_New_Update_LOG_Preferred_Path)" -if echo "$UserPreferredLogPath" | grep -qE "^(/tmp/mnt/|/tmp/opt/|/opt/)" && \ - _ValidateUSBMountPoint_ "$UserPreferredLogPath" && \ - [ "$UserPreferredLogPath" != "$FW_LOG_BASE_DIR" ] -then - mv -f "${FW_LOG_DIR}"/*.log "${UserPreferredLogPath}/$FW_LOG_SUBDIR" 2>/dev/null - rm -fr "${FW_LOG_DIR:?}" - Update_Custom_Settings FW_New_Update_LOG_Directory_Path "$UserPreferredLogPath" -fi + # Reconstruct the shared settings file # + mv -f "$SHARED_SETTINGS_FILE" "${SHARED_SETTINGS_FILE}.bak" + cat "${SHARED_SETTINGS_FILE}.bak" "$TEMPFILE" > "$SHARED_SETTINGS_FILE" + rm -f "$TEMPFILE" "${SHARED_SETTINGS_FILE}.bak" + + _ActionsAfterNewConfigSettings_ + + "$settingsMergeOK" && logMsgTag="successfully." + Say "Merge of updated settings from WebUI was completed ${logMsgTag}" + + if ! "$settingsMergeOK" + then ## Reset for Next Check ## + { sleep 15 ; _UpdateHelperJSFile_ "$extCheckALLvarID" "true" ; } & + fi + + ## Do this ONLY IF requested by user ## + "$doRouterLoginTest" && _TestLoginCredentials_ + else + Say "No updated settings from WebUI found. No merge into $CONFIG_FILE necessary." + fi + return 0 +} ##-------------------------------------## ## Added by Martinski W. [2024-Dec-31] ## @@ -1539,6 +2610,24 @@ _CheckForNewGUIVersionUpdate_() return "$retCode" } +##-------------------------------------## +## Added by Martinski W. [2025-Mar-24] ## +##-------------------------------------## +_GetDLScriptVersion_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] || [ ! -s "$1" ] + then echo ; return 1 ; fi + + local DLversBuildNum=0 + if [ "$(wc -l < "$1")" -eq 2 ] + then + DLversBuildNum="$(tail -n1 "$1")" + [ -z "$DLversBuildNum" ] && DLversBuildNum=0 + fi + echo "$(head -n1 "$1")|$DLversBuildNum" + return 0 +} + ##----------------------------------------## ## Modified by Martinski W. [2025-Feb-15] ## ##----------------------------------------## @@ -1554,38 +2643,80 @@ _CurlFileDownload_() "$srceFilePathDL" -o "$tempFilePathDL" if [ $? -ne 0 ] || [ ! -s "$tempFilePathDL" ] || \ grep -iq "^404: Not Found" "$tempFilePathDL" - then rm -f "$tempFilePathDL" ; retCode=1 - else mv -f "$tempFilePathDL" "$2" ; retCode=0 + then + rm -f "$tempFilePathDL" + retCode=1 + else + if [ "$1" = "$SCRIPT_WEB_ASP_FILE" ] && \ + [ -f "$2" ] && [ -f "$TEMP_MENU_TREE" ] && \ + ! diff -q "$tempFilePathDL" "$2" >/dev/null 2>&1 + then updatedWebUIPage=true + else updatedWebUIPage=false + fi + mv -f "$tempFilePathDL" "$2" + retCode=0 fi + return "$retCode" } ##----------------------------------------## ## Modified by Martinski W. [2025-Mar-27] ## ##----------------------------------------## -_SCRIPTUPDATE_() +_DownloadScriptFiles_() { - local extraParam="" + local retCode isUpdateAction updatedWebUIPage theWebPage - _DownloadScriptFiles_() - { - local retCode - if _CurlFileDownload_ "version.txt" "$SCRIPT_VERPATH" - then - retCode=0 ; chmod 664 "$SCRIPT_VERPATH" - else - retCode=1 - Say "${REDct}**ERROR**${NOct}: Unable to download latest version file for $SCRIPT_NAME." - fi - if _CurlFileDownload_ "${SCRIPT_NAME}.sh" "$ScriptFilePath" - then - retCode=0 ; chmod 755 "$ScriptFilePath" - else - retCode=1 - Say "${REDct}**ERROR**${NOct}: Unable to download latest script file for $SCRIPT_NAME." - fi - return "$retCode" - } + if [ $# -gt 0 ] && [ "$1" = "update" ] + then isUpdateAction=true + else isUpdateAction=false + fi + updatedWebUIPage=false + + if _CurlFileDownload_ "version.txt" "$SCRIPT_VERPATH" + then + retCode=0 ; chmod 664 "$SCRIPT_VERPATH" + else + retCode=1 + Say "${REDct}**ERROR**${NOct}: Unable to download latest version file for $SCRIPT_NAME." + fi + if "$mountWebGUI_OK" && \ + _CurlFileDownload_ "$SCRIPT_WEB_ASP_FILE" "$SCRIPT_WEB_ASP_PATH" + then + chmod 664 "$SCRIPT_WEB_ASP_PATH" + if "$updatedWebUIPage" + then + theWebPage="$(_GetWebUIPage_ "$SCRIPT_WEB_ASP_PATH")" + if [ -n "$theWebPage" ] && [ "$theWebPage" != "NONE" ] + then + sed -i "/url: \"$theWebPage\", tabName: \"$SCRIPT_NAME\"/d" "$TEMP_MENU_TREE" + rm -f "${SHARED_WEB_DIR}/$theWebPage" + rm -f "${SHARED_WEB_DIR}/$(echo "$theWebPage" | cut -f1 -d'.').title" + fi + "$isUpdateAction" && _Mount_WebUI_ + fi + retCode=0 + elif "$mountWebGUI_OK" + then + retCode=1 + Say "${REDct}**ERROR**${NOct}: Unable to download latest WebUI ASP file for $SCRIPT_NAME." + fi + if _CurlFileDownload_ "${SCRIPT_NAME}.sh" "$ScriptFilePath" + then + retCode=0 ; chmod 755 "$ScriptFilePath" + else + retCode=1 + Say "${REDct}**ERROR**${NOct}: Unable to download latest script file for $SCRIPT_NAME." + fi + return "$retCode" +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-24] ## +##----------------------------------------## +_SCRIPT_UPDATE_() +{ + local extraParam="" if [ $# -gt 0 ] && [ "$1" = "force" ] then @@ -1596,9 +2727,14 @@ _SCRIPTUPDATE_() fi printf "${CYANct}Downloading latest version [$DLRepoVersion] of ${SCRIPT_NAME}${NOct}\n" - if _DownloadScriptFiles_ + if _DownloadScriptFiles_ update then - printf "${CYANct}$SCRIPT_NAME was successfully updated.${NOct}\n\n" + printf "${CYANct}$SCRIPT_NAME files were successfully updated.${NOct}\n\n" + if "$mountWebGUI_OK" + then + _SetVersionSharedSettings_ local "$DLRepoVersion" + _SetVersionSharedSettings_ server "$DLRepoVersion" + fi sleep 1 _ReleaseLock_ exec "$ScriptFilePath" $extraParam @@ -1610,11 +2746,15 @@ _SCRIPTUPDATE_() ! _CheckForNewScriptUpdates_ && return 1 clear - logo + _ShowLogo_ + printf "\n${YLWct}Script Update Utility${NOct}\n\n" printf "${CYANct}Version Currently Installed: ${YLWct}${SCRIPT_VERSION}${NOct}\n" printf "${CYANct}Update Version Available Now: ${YLWct}${DLRepoVersion}${NOct}\n\n" + if "$mountWebGUI_OK" + then _SetVersionSharedSettings_ server "$DLRepoVersion" ; fi + if [ "$SCRIPT_VERSION" = "$DLRepoVersion" ] then echo -e "${CYANct}You are on the latest version! Would you like to download anyways?${NOct}" @@ -1623,8 +2763,11 @@ _SCRIPTUPDATE_() then printf "\n\n${CYANct}Downloading $SCRIPT_NAME $DLRepoVersion version.${NOct}\n" - if _DownloadScriptFiles_ + if _DownloadScriptFiles_ update then + if "$mountWebGUI_OK" + then _SetVersionSharedSettings_ local "$DLRepoVersion" + fi printf "\n${CYANct}Download successful!${NOct}\n" printf "$(date) - Successfully downloaded $SCRIPT_NAME v${DLRepoVersion}\n" fi @@ -1642,8 +2785,11 @@ _SCRIPTUPDATE_() then printf "\n\n${CYANct}Downloading $SCRIPT_NAME $DLRepoVersion version.${NOct}\n" - if _DownloadScriptFiles_ + if _DownloadScriptFiles_ update then + if "$mountWebGUI_OK" + then _SetVersionSharedSettings_ local "$DLRepoVersion" + fi printf "\n$(date) - Successfully downloaded $SCRIPT_NAME v${DLRepoVersion}\n" printf "${CYANct}Update successful! Restarting script...${NOct}\n" sleep 1 @@ -1664,14 +2810,22 @@ _SCRIPTUPDATE_() } ##----------------------------------------## -## Modified by Martinski W. [2025-Mar-27] ## +## Modified by Martinski W. [2025-Mar-24] ## ##----------------------------------------## _CheckForNewScriptUpdates_() { - local DLScriptVerPath="${SCRIPT_VERPATH}.DL.tmp" + local verStr DLScriptVerPath="${SCRIPT_VERPATH}.DL.tmp" echo DLRepoVersion="$SCRIPT_VERSION" - [ -s "$SCRIPT_VERPATH" ] && DLRepoVersion="$(head -n1 "$SCRIPT_VERPATH")" + if [ -s "$SCRIPT_VERPATH" ] + then + if verStr="$(_GetDLScriptVersion_ "$SCRIPT_VERPATH")" + then + DLRepoVersion="$(echo "$verStr" | awk -F '|' '{print $1}')" + DLRepoBuildNum="$(echo "$verStr" | awk -F '|' '{print $2}')" + ScriptBuildNum="$DLRepoBuildNum" + fi + fi if ! _CurlFileDownload_ "version.txt" "$DLScriptVerPath" then @@ -1680,7 +2834,11 @@ _CheckForNewScriptUpdates_() return 1 fi - DLRepoVersion="$(head -n1 "$DLScriptVerPath")" + if verStr="$(_GetDLScriptVersion_ "$DLScriptVerPath")" + then + DLRepoVersion="$(echo "$verStr" | awk -F '|' '{print $1}')" + DLRepoBuildNum="$(echo "$verStr" | awk -F '|' '{print $2}')" + fi rm -f "$DLScriptVerPath" if [ -z "$DLRepoVersion" ] @@ -1693,7 +2851,11 @@ _CheckForNewScriptUpdates_() DLRepoVersionNum="$(_ScriptVersionStrToNum_ "$DLRepoVersion")" ScriptVersionNum="$(_ScriptVersionStrToNum_ "$SCRIPT_VERSION")" - if [ "$DLRepoVersionNum" -gt "$ScriptVersionNum" ] + if [ "$DLRepoVersionNum" -gt "$ScriptVersionNum" ] || \ + { + [ "$DLRepoBuildNum" -gt "$ScriptBuildNum" ] && \ + [ "$DLRepoVersionNum" -eq "$ScriptVersionNum" ] + } then scriptUpdateNotify="New script update available. ${REDct}v${SCRIPT_VERSION}${NOct} --> ${GRNct}v${DLRepoVersion}${NOct}" @@ -1704,7 +2866,7 @@ ${REDct}v${SCRIPT_VERSION}${NOct} --> ${GRNct}v${DLRepoVersion}${NOct}" Say "$myLAN_HostName - A new script version update (v$DLRepoVersion) is available to download." if [ "$ScriptAutoUpdateSetting" = "ENABLED" ] then - _SCRIPTUPDATE_ force + _SCRIPT_UPDATE_ force fi else scriptUpdateNotify=0 @@ -1734,7 +2896,7 @@ _GetLatestFWUpdateVersionFromRouter_() } ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Nov-26] ## +## Modified by ExtremeFiretop [2024-Dec-28] ## ##------------------------------------------## _CreateEMailContent_() { @@ -1786,7 +2948,7 @@ _CreateEMailContent_() } > "$tempEMailBodyMsg" ;; AGGREGATED_UPDATE_NOTIFICATION) - if "$inRouterSWmode" && [ -n "$node_list" ]; then + if "$aiMeshNodes_OK" && [ -n "$node_list" ]; then nodefwNewUpdateVersion="$(_GetLatestFWUpdateVersionFromNode_ 1)" fi if [ -z "$nodefwNewUpdateVersion" ] @@ -1810,7 +2972,7 @@ _CreateEMailContent_() ;; STOP_FW_UPDATE_APPROVAL) emailBodyTitle="WARNING" - if $isEMailFormatHTML + if "$isEMailFormatHTML" then # Highlight high-risk terms using HTML with a yellow background # highlighted_changelog_contents="$(echo "$changelog_contents" | sed -E "s/($high_risk_terms)/\1<\/span>/gi")" @@ -1995,7 +3157,7 @@ EOF } ##----------------------------------------## -## Modified by Martinski W. [2024-Feb-16] ## +## Modified by Martinski W. [2025-Jan-10] ## ##----------------------------------------## _CheckEMailConfigFileFromAMTM_() { @@ -2034,6 +3196,15 @@ _CheckEMailConfigFileFromAMTM_() return 1 fi + sendEMail_CC_Name="$(Get_Custom_Setting FW_New_Update_EMail_CC_Name)" + sendEMail_CC_Address="$(Get_Custom_Setting FW_New_Update_EMail_CC_Address)" + sendEMailNotificationsFlag="$(Get_Custom_Setting FW_New_Update_EMail_Notification)" + sendEMailFormaType="$(Get_Custom_Setting FW_New_Update_EMail_FormatType)" + if [ "$sendEMailFormaType" = "HTML" ] + then isEMailFormatHTML=true + else isEMailFormatHTML=false + fi + if [ -n "$sendEMail_CC_Name" ] && [ "$sendEMail_CC_Name" != "TBD" ] && \ [ -n "$sendEMail_CC_Address" ] && [ "$sendEMail_CC_Address" != "TBD" ] then @@ -2045,13 +3216,13 @@ _CheckEMailConfigFileFromAMTM_() return 0 } -##----------------------------------------## -## Modified by Martinski W. [2024-Jun-05] ## -##----------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2024-Dec-21] ## +##------------------------------------------## _SendEMailNotification_() { if [ $# -eq 0 ] || [ -z "$1" ] || \ - ! "$sendEMailNotificationsFlag" || \ + [ "$sendEMailNotificationsFlag" != "ENABLED" ] || \ ! _CheckEMailConfigFileFromAMTM_ 1 then return 1 ; fi @@ -2320,13 +3491,12 @@ _GetRequiredRAM_KB_() # Size of the ZIP file in bytes # zip_file_size_bytes="$(curl -LsI --retry 4 --retry-delay 5 "$theURL" | grep -i Content-Length | tail -1 | awk '{print $2}')" - # Convert bytes to KBytes # + # Bytes to KBytes # zip_file_size_kb="$((zip_file_size_bytes / 1024))" # Calculate overhead based on the percentage # overhead_kb="$((zip_file_size_kb * overhead_percentage / 100))" - # Calculate total required space # total_required_kb="$((zip_file_size_kb + overhead_kb))" echo "$total_required_kb" } @@ -2518,9 +3688,9 @@ check_memory_and_prompt_reboot() } ##----------------------------------------## -## Modified by Martinski W. [2024-May-31] ## +## Modified by Martinski W. [2025-Jan-05] ## ##----------------------------------------## -check_version_support() +_CheckForMinimumVersionSupport_() { local numOfFields current_version numCurrentVers numMinimumVers current_version="$(_GetCurrentFWInstalledLongVersion_)" @@ -2529,34 +3699,41 @@ check_version_support() numCurrentVers="$(_FWVersionStrToNum_ "$current_version" "$numOfFields")" numMinimumVers="$(_FWVersionStrToNum_ "$MinSupportedFirmwareVers" "$numOfFields")" - # If the current firmware version is lower than the minimum supported firmware version, exit. + # Check if current F/W version is lower than the minimum supported version # if [ "$numCurrentVers" -lt "$numMinimumVers" ] then MinFirmwareVerCheckFailed=true ; fi + + "$MinFirmwareVerCheckFailed" && return 1 || return 0 } -##------------------------------------------## -## Modified by ExtremeFiretop [2024-Oct-19] ## -##------------------------------------------## -check_model_support() +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-05] ## +##----------------------------------------## +_CheckForMinimumModelSupport_() { # List of unsupported models as a space-separated string local unsupported_models="RT-AC87U RT-AC56U RT-AC66U RT-AC3200 RT-AC88U RT-AC5300 RT-AC3100 RT-AC68U RT-AC66U_B1 RT-AC68UF RT-AC68P RT-AC1900P RT-AC1900 RT-N66U RT-N16 DSL-AC68U" - # Get the current model local current_model="$(_GetRouterProductID_)" - # Check if the current model is in the list of unsupported models # + # Check if current model is in the list of unsupported models # if echo "$unsupported_models" | grep -wq "$current_model" then routerModelCheckFailed=true ; fi + + "$routerModelCheckFailed" && return 1 || return 0 } ##----------------------------------------## -## Modified by Martinski W. [2024-Aug-15] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## _TestLoginCredentials_() { - local credsBase64="$1" - local curl_response routerURLstr + local credsENC curlResponse routerURLstr + + if [ $# -gt 0 ] && [ -n "$1" ] + then credsENC="$1" + else credsENC="$(Get_Custom_Setting credentials_base64)" + fi # Define routerURLstr # routerURLstr="$(_GetRouterURL_)" @@ -2565,31 +3742,32 @@ _TestLoginCredentials_() /sbin/service restart_httpd >/dev/null 2>&1 & sleep 5 - curl_response="$(curl -k "${routerURLstr}/login.cgi" \ + curlResponse="$(curl -k "${routerURLstr}/login.cgi" \ --referer "${routerURLstr}/Main_Login.asp" \ --user-agent 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0' \ -H 'Accept-Language: en-US,en;q=0.5' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H "Origin: ${routerURLstr}/" \ -H 'Connection: keep-alive' \ - --data-raw "group_id=&action_mode=&action_script=&action_wait=5¤t_page=Main_Login.asp&next_page=index.asp&login_authorization=${credsBase64}" \ + --data-raw "group_id=&action_mode=&action_script=&action_wait=5¤t_page=Main_Login.asp&next_page=index.asp&login_authorization=${credsENC}" \ --cookie-jar /tmp/cookie.txt)" - # Interpret the curl_response to determine login success or failure # - # This is a basic check # - if echo "$curl_response" | grep -Eq 'url=index\.asp|url=GameDashboard\.asp' + # Determine login success or failure. This is a basic check # + if echo "$curlResponse" | grep -Eq 'url=index\.asp|url=GameDashboard\.asp' then + _UpdateLoginPswdCheckHelper_ SUCCESS printf "\n${GRNct}Router Login test passed.${NOct}" printf "\nRestarting web server... Please wait.\n" /sbin/service restart_httpd >/dev/null 2>&1 & - sleep 1 + sleep 2 return 0 else + _UpdateLoginPswdCheckHelper_ FAILURE printf "\n${REDct}**ERROR**${NOct}: Router Login test failed.\n" printf "\n${routerLoginFailureMsg}\n\n" if _WaitForYESorNO_ "Would you like to try again?" then return 1 # Indicates failure but with intent to retry # - else return 0 # User opted not to retry; do a graceful exit # + else return 0 # User opted not to retry so just return # fi fi } @@ -2606,40 +3784,116 @@ _GetRawKeypress_() stty "$savedSettings" } +##-------------------------------------## +## Added by Martinski W. [2025-Jan-10] ## +##-------------------------------------## +_OfflineKeySeqHandler_() +{ + if [ $# -lt 3 ] || [ -z "$1" ] || \ + [ -z "$2" ] || [ -z "$3" ] + then return 1 ; fi + + local retCode=1 + local offlineKeySeqNum="27251624" offlineKeySeqCnt=4 + + case "$3" in + FOUNDOK) + if [ "$1" -eq "$offlineKeySeqCnt" ] && \ + [ "$2" -eq "$offlineKeySeqNum" ] + then retCode=0 + else retCode=1 + fi + ;; + NOTFOUND) + if [ "$1" -gt 4 ] || \ + { [ "$1" -eq "$offlineKeySeqCnt" ] && \ + [ "$2" -ne "$offlineKeySeqNum" ] ; } + then retCode=0 + else retCode=1 + fi + ;; + *) retCode=1 ;; + esac + + return "$retCode" +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-10] ## +##-------------------------------------## +_SetReloadKeySeqHandler_() +{ + if [ $# -lt 3 ] || [ -z "$1" ] || \ + [ -z "$2" ] || [ -z "$3" ] + then return 1 ; fi + + local retCode=1 + local setReloadKeySeqNum="1812" setReloadKeySeqCnt=2 + + case "$3" in + FOUNDOK) + if [ "$1" -eq "$setReloadKeySeqCnt" ] && \ + [ "$2" -eq "$setReloadKeySeqNum" ] + then retCode=0 + else retCode=1 + fi + ;; + NOTFOUND) + if [ "$1" -gt 4 ] || \ + { [ "$1" -eq "$setReloadKeySeqCnt" ] && \ + [ "$2" -ne "$setReloadKeySeqNum" ] ; } + then retCode=0 + else retCode=1 + fi + ;; + *) retCode=1 ;; + esac + + return "$retCode" +} + ##----------------------------------------## -## Modified by Martinski W. [2024-Jul-31] ## +## Modified by Martinski W. [2025-Jan-10] ## ##----------------------------------------## _GetKeypressInput_() { local inputStrLenMAX=16 inputString promptStr local charNum inputStrLen keypressCount - local theKeySeqCnt maxKeySeqCnt=4 retCode - local theKeySeqNum maxKeySeqNum="27251624" - local offlineModeFlag=false + local theKeySeqCnt theKeySeqNum retCode + local offlineUpdKeyFlag execReloadKeyFlag + + if [ -n "${offlineUpdTrigger:+xSETx}" ] + then + offlineUpdKeyFlag=true + offlineUpdTrigger=false + else + offlineUpdKeyFlag=false + unset offlineUpdTrigger + fi - if [ -n "${offlineUpdateFlag:+xSETx}" ] - then offlineModeFlag=true ; fi + if [ -n "${execReloadTrigger:+xSETx}" ] + then + execReloadKeyFlag=true + execReloadTrigger=false + else + execReloadKeyFlag=false + unset execReloadTrigger + fi if [ $# -eq 0 ] || [ -z "$1" ] then printf "\n**ERROR**: NO prompt string was provided.\n" return 1 fi - promptStr="$1" _ShowInputString_() { printf "\r\033[0K${promptStr} %s" "$inputString" ; } _ClearKeySeqState_() - { - theKeySeqNum=0 ; theKeySeqCnt=0 - if "$offlineModeFlag" - then offlineUpdateFlag=false - else unset offlineUpdateFlag - fi - } + { theKeySeqNum=0 ; theKeySeqCnt=0 ; } charNum="" + promptStr="$1" inputString="" inputStrLen=0 keypressCount=0 @@ -2697,26 +3951,37 @@ _GetKeypressInput_() continue fi - # Non-Printable ASCII Codes ## - if "$offlineModeFlag" && [ "$charNum" -gt 0 ] && [ "$charNum" -lt 32 ] + ## Non-Printable ASCII Codes ## + if [ "$charNum" -gt 0 ] && [ "$charNum" -lt 32 ] && \ + { "$offlineUpdKeyFlag" || "$execReloadKeyFlag" ; } then - offlineUpdateFlag=false + "$offlineUpdKeyFlag" && offlineUpdTrigger=false + "$execReloadKeyFlag" && execReloadTrigger=false + theKeySeqCnt="$((theKeySeqCnt + 1))" if [ "$theKeySeqCnt" -eq 1 ] then theKeySeqNum="$charNum" else theKeySeqNum="${theKeySeqNum}${charNum}" fi - if [ "$theKeySeqCnt" -eq "$maxKeySeqCnt" ] && \ - [ "$theKeySeqNum" -eq "$maxKeySeqNum" ] + if "$offlineUpdKeyFlag" && \ + _OfflineKeySeqHandler_ "$theKeySeqCnt" "$theKeySeqNum" FOUNDOK then _ClearKeySeqState_ if [ "$inputString" = "offline" ] - then offlineUpdateFlag=true ; fi + then offlineUpdTrigger=true ; fi continue fi - if [ "$theKeySeqCnt" -gt "$maxKeySeqCnt" ] || \ - { [ "$theKeySeqCnt" -eq "$maxKeySeqCnt" ] && \ - [ "$theKeySeqNum" -ne "$maxKeySeqNum" ] ; } + if "$execReloadKeyFlag" && \ + _SetReloadKeySeqHandler_ "$theKeySeqCnt" "$theKeySeqNum" FOUNDOK + then + _ClearKeySeqState_ + execReloadTrigger=true + continue + fi + if { "$offlineUpdKeyFlag" && \ + _OfflineKeySeqHandler_ "$theKeySeqCnt" "$theKeySeqNum" NOTFOUND ; } && \ + { "$execReloadKeyFlag" && \ + _SetReloadKeySeqHandler_ "$theKeySeqCnt" "$theKeySeqNum" NOTFOUND ; } then _ClearKeySeqState_ ; fi else _ClearKeySeqState_ @@ -2725,17 +3990,17 @@ _GetKeypressInput_() IFS="$savedIFS" theUserInputStr="$inputString" - return "$retCode" + echo ; return "$retCode" } ##----------------------------------------## -## Modified by Martinski W. [2024-Jul-31] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## _GetPasswordInput_() { - local PSWDstrLenMIN=1 PSWDstrLenMAX=64 - local newPSWDstring newPSWDtmpStr PSWDprompt - local retCode charNum newPSWDlength showPSWD + local PSWDstrLenMIN=5 PSWDstrLenMAX=64 + local newPSWDtmpStr PSWDprompt showPSWD + local retCode=1 charNum newPSWDlength # For more responsive TAB keypress debounce # local tabKeyDebounceSem="/tmp/var/tmp/${ScriptFNameTag}_TabKeySEM.txt" @@ -2749,7 +4014,7 @@ _GetPasswordInput_() _TabKeyDebounceWait_() { touch "$tabKeyDebounceSem" - usleep 300000 #0.3 sec# + usleep 333000 #~0.3 sec# rm -f "$tabKeyDebounceSem" } @@ -2764,16 +4029,23 @@ _GetPasswordInput_() _ShowPSWDPrompt_() { local pswdTemp LENct LENwd - [ "$showPSWD" = "1" ] && pswdTemp="$newPSWDstring" || pswdTemp="$newPSWDtmpStr" - if [ "$newPSWDlength" -lt "$PSWDstrLenMIN" ] || [ "$newPSWDlength" -gt "$PSWDstrLenMAX" ] - then LENct="$REDct" ; LENwd="" - else LENct="$GRNct" ; LENwd="02" + if [ "$showPSWD" = "1" ] + then pswdTemp="$newPSWDstring" + else pswdTemp="$newPSWDtmpStr" + fi + if [ "$newPSWDlength" -eq 0 ] + then LENwd="" ; else LENwd="02" + fi + if [ "$newPSWDlength" -lt "$PSWDstrLenMIN" ] || \ + [ "$newPSWDlength" -gt "$PSWDstrLenMAX" ] + then LENct="$REDct" ; else LENct="$GRNct" fi - printf "\r\033[0K$PSWDprompt [Length=${LENct}%${LENwd}d${NOct}]: %s" "$newPSWDlength" "$pswdTemp" + printf "\033[1A\r\033[0K${PSWDprompt}\n" + printf "\r\033[0K[Length=${LENct}%${LENwd}d${NOct}]: %s" "$newPSWDlength" "$pswdTemp" } - showPSWD=0 charNum="" + showPSWD=0 newPSWDstring="$thePWSDstring" newPSWDlength="${#newPSWDstring}" newPSWDtmpStr="$(_ShowAsterisks_ "$newPSWDlength")" @@ -2786,20 +4058,25 @@ _GetPasswordInput_() if [ "$charNum" -eq 0 ] || [ "$charNum" -eq 10 ] || [ "$charNum" -eq 13 ] then - if [ "$newPSWDlength" -ge "$PSWDstrLenMIN" ] && [ "$newPSWDlength" -le "$PSWDstrLenMAX" ] + if echo "$newPSWDstring" | grep -qE "^[[:blank:]]+$" + then + newPSWDstring="" + printf "\n\n${REDct}**ERROR**${NOct}: Password string cannot be all blank spaces.\n" + retCode=1 + elif [ "$newPSWDlength" -ge "$PSWDstrLenMIN" ] && [ "$newPSWDlength" -le "$PSWDstrLenMAX" ] then echo retCode=0 elif [ "$newPSWDlength" -lt "$PSWDstrLenMIN" ] then newPSWDstring="" - printf "\n${REDct}**ERROR**${NOct}: Password length is less than allowed minimum length " + printf "\n\n${REDct}**ERROR**${NOct}: Password length is less than allowed minimum length " printf "[MIN=${GRNct}${PSWDstrLenMIN}${NOct}].\n" retCode=1 elif [ "$newPSWDlength" -gt "$PSWDstrLenMAX" ] then newPSWDstring="" - printf "\n${REDct}**ERROR**${NOct}: Password length is greater than allowed maximum length " + printf "\n\n${REDct}**ERROR**${NOct}: Password length is greater than allowed maximum length " printf "[MAX=${GRNct}${PSWDstrLenMAX}${NOct}].\n" retCode=1 fi @@ -2855,7 +4132,7 @@ _GetPasswordInput_() ## ONLY 7-bit ASCII printable characters are VALID ## if [ "$charNum" -gt 31 ] && [ "$charNum" -lt 127 ] then - if [ "$newPSWDlength" -le "$PSWDstrLenMAX" ] + if [ "$newPSWDlength" -lt "$PSWDstrLenMAX" ] then newPSWDstring="${newPSWDstring}${theChar}" newPSWDlength="${#newPSWDstring}" @@ -2867,8 +4144,14 @@ _GetPasswordInput_() done IFS="$savedIFS" + if [ "$retCode" -ne 0 ] + then + _WaitForEnterKey_ + _GetPasswordInput_ "$1" + fi + thePWSDstring="$newPSWDstring" - return "$retCode" + return } ##-------------------------------------## @@ -2965,18 +4248,19 @@ with the \"${GRNct}Web UI${NOct}\" access type on the \"Access restriction list\ } ##----------------------------------------## -## Modified by Martinski W. [2024-Aug-16] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## _GetLoginCredentials_() { - local retry="yes" userName savedMsg - local oldPWSDstring thePWSDstring + local retry="yes" userName savedMsgStr local loginCredsENC loginCredsDEC + local oldPWSDstring newPSWDstring thePWSDstring # Check if WebGUI access is NOT restricted # if ! _CheckWebGUILoginAccessOK_ then _WaitForEnterKey_ "$mainMenuReturnPromptStr" + _UpdateLoginPswdCheckHelper_ NoACCESS return 1 fi @@ -2987,16 +4271,19 @@ _GetLoginCredentials_() if [ -z "$loginCredsENC" ] || [ "$loginCredsENC" = "TBD" ] then thePWSDstring="" + _UpdateLoginPswdCheckHelper_ InitPWD else loginCredsDEC="$(echo "$loginCredsENC" | openssl base64 -d)" thePWSDstring="$(echo "$loginCredsDEC" | sed "s/${userName}://")" fi oldPWSDstring="$thePWSDstring" + newPSWDstring="$thePWSDstring" while [ "$retry" = "yes" ] do - echo "=== Login Credentials ===" + printf "=== Login Credentials ===\n\n" _GetPasswordInput_ "Enter password for user ${GRNct}${userName}${NOct}" + if [ -z "$thePWSDstring" ] then printf "\nPassword string is ${REDct}NOT${NOct} valid. Credentials were not saved.\n" @@ -3007,14 +4294,17 @@ _GetLoginCredentials_() # Encode the Username and Password in Base64 # loginCredsENC="$(echo -n "${userName}:${thePWSDstring}" | openssl base64 -A)" - # Save the credentials to the SETTINGSFILE # Update_Custom_Settings credentials_base64 "$loginCredsENC" if [ "$thePWSDstring" != "$oldPWSDstring" ] - then savedMsg="${GRNct}New credentials saved.${NOct}" - else savedMsg="${GRNct}Credentials remain unchanged.${NOct}" + then + _UpdateLoginPswdCheckHelper_ NewPSWD + savedMsgStr="${GRNct}New credentials saved.${NOct}" + else + _UpdateLoginPswdCheckHelper_ OldPSWD + savedMsgStr="${GRNct}Credentials remain unchanged.${NOct}" fi - printf "\n${savedMsg}\n" + printf "\n${savedMsgStr}\n" printf "Encoded Credentials:\n" printf "${GRNct}$loginCredsENC${NOct}\n" @@ -3131,9 +4421,9 @@ _GetNodeURL_() echo "${urlProto}://${NodeIP_Address}${urlPort}" } -##---------------------------------------## -## Added by ExtremeFiretop [2024-Mar-26] ## -##---------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-07] ## +##----------------------------------------## _GetNodeInfo_() { local NodeIP_Address="$1" @@ -3161,6 +4451,7 @@ _GetNodeInfo_() credsBase64="$(Get_Custom_Setting credentials_base64)" if [ -z "$credsBase64" ] || [ "$credsBase64" = "TBD" ] then + _UpdateLoginPswdCheckHelper_ InitPWD Say "${REDct}**ERROR**${NOct}: No login credentials have been saved. Use the Main Menu to save login credentials." "$inMenuMode" && _WaitForEnterKey_ "$mainMenuReturnPromptStr" return 1 @@ -3180,9 +4471,11 @@ _GetNodeInfo_() if [ $? -ne 0 ] then + _UpdateLoginPswdCheckHelper_ FAILURE printf "\n${REDct}Login failed for AiMesh Node [$NodeIP_Address].${NOct}\n" return 1 fi + _UpdateLoginPswdCheckHelper_ SUCCESS # Retrieve the HTML content # htmlContent="$(curl -s -k "${NodeURLstr}/appGet.cgi?hook=nvram_get(productid)%3bnvram_get(asus_device_list)%3bnvram_get(cfg_device_list)%3bnvram_get(firmver)%3bnvram_get(buildno)%3bnvram_get(extendno)%3bnvram_get(webs_state_flag)%3bnvram_get(odmpid)%3bnvram_get(wps_modelnum)%3bnvram_get(model)%3bnvram_get(build_name)%3bnvram_get(lan_hostname)%3bnvram_get(webs_state_info)%3bnvram_get(label_mac)" \ @@ -3294,11 +4587,12 @@ _GetLatestFWUpdateVersionFromWebsite_() else versionStr="$(echo "${fileStr%.*}" | sed "s/\/${PRODUCT_ID}_//" | sed 's/_/./g')" fi - # Extracting the correct link from the page + # Extracting the correct link from the page # local correct_link="$(echo "$linkStr" | sed 's|^/|https://sourceforge.net/|')" if [ -z "$versionStr" ] || [ -z "$correct_link" ] - then echo "**ERROR** **NO_URL**" ; return 1 ; fi + then echo "**ERROR** **NO_URL**" ; return 1 + fi echo "$versionStr" echo "$correct_link" @@ -3314,10 +4608,11 @@ _GetLatestFWUpdateVersionFromGitHub_() local gitURL="$1" # GitHub URL for the latest release # local firmware_type="$2" # "tuf", "rog" or "pure" # - local search_type="$firmware_type" # Default to the input firmware_type + local search_type="$firmware_type" # Default to the input firmware_type # # If firmware_type is "pure", set search_type to include "squashfs" as well - if [ "$firmware_type" = "pure" ]; then + if [ "$firmware_type" = "pure" ] + then search_type="pure\|squashfs\|ubi" fi @@ -3327,7 +4622,6 @@ _GetLatestFWUpdateVersionFromGitHub_() else routerVersion="$(Get_Custom_Setting "FW_New_Update_Notification_Vers")" fi - if [ -z "$routerVersion" ] then echo "**ERROR** **NO_ROUTER_VERSION**" @@ -3379,8 +4673,9 @@ GetLatestFirmwareMD5URL() local search_type="$firmware_type" # Default to the input firmware_type # - # If firmware_type is "pure", set search_type to include "squashfs" as well # - if [ "$firmware_type" = "pure" ]; then + # If firmware_type is "pure", set search_type to include "squashfs" as well + if [ "$firmware_type" = "pure" ] + then search_type="pure\|squashfs\|ubi" fi @@ -3390,7 +4685,6 @@ GetLatestFirmwareMD5URL() else routerVersion="$(Get_Custom_Setting "FW_New_Update_Notification_Vers")" fi - if [ -z "$routerVersion" ] then echo "**ERROR** **NO_ROUTER_VERSION**" @@ -3752,7 +5046,7 @@ _toggle_change_log_check_() if _WaitForYESorNO_ "\nProceed to ${REDct}DISABLE${NOct}?" then Update_Custom_Settings "CheckChangeLog" "DISABLED" - Update_Custom_Settings "FW_New_Update_Changelog_Approval" "TBD" + Delete_Custom_Settings "FW_New_Update_Changelog_Approval" printf "Changelog verification check is now ${REDct}DISABLED.${NOct}\n" else printf "Changelog verification check remains ${GRNct}ENABLED.${NOct}\n" @@ -3762,6 +5056,7 @@ _toggle_change_log_check_() if _WaitForYESorNO_ "\nProceed to ${GRNct}ENABLE${NOct}?" then Update_Custom_Settings "CheckChangeLog" "ENABLED" + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "TBD" printf "Changelog verification check is now ${GRNct}ENABLED.${NOct}\n" else printf "Changelog verification check remains ${REDct}DISABLED.${NOct}\n" @@ -3881,18 +5176,18 @@ _ChangeBuildType_TUF_() # Use Get_Custom_Setting to retrieve the previous choice previous_choice="$(Get_Custom_Setting "TUFBuild")" - # If the previous choice is not set, default to 'n' # + # If the previous choice is not set, default to 'DISABLED' # if [ "$previous_choice" = "TBD" ]; then - previous_choice="n" + previous_choice="DISABLED" fi # Convert previous choice to a descriptive text # - if [ "$previous_choice" = "y" ]; then + if [ "$previous_choice" = "ENABLED" ]; then display_choice="TUF Build" else display_choice="Pure Build" fi - printf "\n\nCurrent Build Type: ${GRNct}${display_choice}${NOct}.\n" + printf "\nCurrent Build Type: ${GRNct}${display_choice}${NOct}.\n" doReturnToMenu=false while true @@ -3900,7 +5195,7 @@ _ChangeBuildType_TUF_() printf "\n${SEPstr}" printf "\nChoose your preferred option for the build type to flash:\n" printf "\n ${GRNct}1${NOct}. Original ${REDct}TUF${NOct} themed user interface" - printf "\n ${REDct}(Applies only if TUF F/W is available; otherwise, defaults to Pure build)${NOct}\n" + printf "\n ${REDct}(Applies only if TUF F/W is available)${NOct}\n" printf "\n ${GRNct}2${NOct}. Pure ${GRNct}Non-TUF${NOct} themed user interface" printf "\n ${GRNct}(Recommended)${NOct}\n" printf "\n ${GRNct}e${NOct}. Exit to Advanced Menu\n" @@ -3912,13 +5207,13 @@ _ChangeBuildType_TUF_() then doReturnToMenu=true ; break ; fi case $choice in - 1) buildtypechoice="y" + 1) buildtypechoice="ENABLED" printf "\n${InvBYLWct} NOTE: ${NOct}\n" printf "${CYANct}The TUF build will apply only if a compatible TUF firmware image is available." - printf "\nOtherwise, the Pure ${GRNct}Non-TUF${NOct}${CYANct} build will be used instead.${NOct}\n" + printf " Otherwise, the Pure ${GRNct}Non-TUF${NOct}${CYANct} build will be used instead.${NOct}\n" break ;; - 2) buildtypechoice="n" ; break + 2) buildtypechoice="DISABLED" ; break ;; *) echo ; _InvalidMenuSelection_ ;; @@ -3943,18 +5238,18 @@ _ChangeBuildType_ROG_() # Use Get_Custom_Setting to retrieve the previous choice previous_choice="$(Get_Custom_Setting "ROGBuild")" - # If the previous choice is not set, default to 'n' # + # If the previous choice is not set, default to 'DISABLED' # if [ "$previous_choice" = "TBD" ]; then - previous_choice="n" + previous_choice="DISABLED" fi # Convert previous choice to a descriptive text # - if [ "$previous_choice" = "y" ]; then + if [ "$previous_choice" = "ENABLED" ]; then display_choice="ROG Build" else display_choice="Pure Build" fi - printf "\n\nCurrent Build Type: ${GRNct}${display_choice}${NOct}.\n" + printf "\nCurrent Build Type: ${GRNct}${display_choice}${NOct}.\n" doReturnToMenu=false while true @@ -3962,7 +5257,7 @@ _ChangeBuildType_ROG_() printf "\n${SEPstr}" printf "\nChoose your preferred option for the build type to flash:\n" printf "\n ${GRNct}1${NOct}. Original ${REDct}ROG${NOct} themed user interface" - printf "\n ${REDct}(Applies only if ROG F/W is available; otherwise, defaults to Pure build)${NOct}\n" + printf "\n ${REDct}(Applies only if ROG F/W is available)${NOct}\n" printf "\n ${GRNct}2${NOct}. Pure ${GRNct}Non-ROG${NOct} themed user interface" printf "\n ${GRNct}(Recommended)${NOct}\n" printf "\n ${GRNct}e${NOct}. Exit to Advanced Menu\n" @@ -3974,13 +5269,13 @@ _ChangeBuildType_ROG_() then doReturnToMenu=true ; break ; fi case $choice in - 1) buildtypechoice="y" + 1) buildtypechoice="ENABLED" printf "\n${InvBYLWct} NOTE: ${NOct}\n" printf "${CYANct}The ROG build will apply only if a compatible ROG firmware image is available." - printf "\nOtherwise, the Pure ${GRNct}Non-ROG${NOct}${CYANct} build will be used instead.${NOct}\n" + printf " Otherwise, the Pure ${GRNct}Non-ROG${NOct}${CYANct} build will be used instead.${NOct}\n" break ;; - 2) buildtypechoice="n" ; break + 2) buildtypechoice="DISABLED" ; break ;; *) echo ; _InvalidMenuSelection_ ;; @@ -4658,12 +5953,12 @@ _GetScriptAutoUpdateCronSchedule_() } ##----------------------------------------## -## Modified by Martinski W. [2025-Mar-27] ## +## Modified by Martinski W. [2025-Feb-21] ## ##----------------------------------------## _AddScriptAutoUpdateCronJob_() { local newSchedule retCode=1 - + newSchedule="$(_GetScriptAutoUpdateCronSchedule_)" if [ -z "$newSchedule" ] || [ "$newSchedule" = "TBD" ] then @@ -4764,9 +6059,9 @@ _Set_FW_UpdatePostponementDays_() return 0 } -##-------------------------------------## -## Added by Martinski W. [2024-Nov-24] ## -##-------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-29] ## +##----------------------------------------## _TranslateCronSchedHR_() { if [ $# -eq 0 ] || [ -z "$1" ] @@ -4810,33 +6105,34 @@ _TranslateCronSchedHR_() theCronHOUR="$(echo "$1" | awk -F ' ' '{print $2}')" theCronDAYM="$(echo "$1" | awk -F ' ' '{print $3}')" theCronDAYW="$(echo "$1" | awk -F ' ' '{print $5}')" + theCronDAYW="$(_ConvertDayOfWeekToHR_ "$theCronDAYW")" if [ "$theCronDAYW" = "*" ] && [ "$theCronDAYM" = "*" ] then - infoStrDAYS="every day, in every month" + infoStrDAYS="every day, every month" elif [ "$theCronDAYW" != "*" ] then if echo "$theCronDAYW" | grep -qE "^[*]/.*" then freqNumDAYW="$(echo "$theCronDAYW" | cut -f2 -d'/')" - infoStrDAYS="every $freqNumDAYW days of the week, in every month" + infoStrDAYS="every $freqNumDAYW days of the week, every month" elif echo "$theCronDAYW" | grep -qE "[,-]" then - infoStrDAYS="days ${theCronDAYW}, in every month" + infoStrDAYS="on ${theCronDAYW}, every month" else - infoStrDAYS="day ${theCronDAYW}, in every month" + infoStrDAYS="on ${theCronDAYW}, every month" fi elif [ "$theCronDAYM" != "*" ] then if echo "$theCronDAYM" | grep -qE "^[*]/.*" then freqNumDAYM="$(echo "$theCronDAYM" | cut -f2 -d'/')" - infoStrDAYS="every $freqNumDAYM days of the month, in every month" + infoStrDAYS="every $freqNumDAYM days of the month, every month" elif echo "$theCronDAYM" | grep -qE "[,-]" then - infoStrDAYS="days ${theCronDAYM} of the month, in every month" + infoStrDAYS="days ${theCronDAYM} of the month, every month" else - infoStrDAYS="day ${theCronDAYM} of the month, in every month" + infoStrDAYS="day ${theCronDAYM} of the month, every month" fi fi @@ -4974,15 +6270,39 @@ _ValidateCronScheduleHOUR_() } ##-------------------------------------## -## Added by Martinski W. [2024-Nov-23] ## +## Added by Martinski W. [2025-Mar-29] ## +##-------------------------------------## +_ConvertDAYW_NumberToName_() +{ echo "$1" | sed 's/0/Sun/g;s/1/Mon/g;s/2/Tue/g;s/3/Wed/g;s/4/Thu/g;s/5/Fri/g;s/6/Sat/g' ; } + +##-------------------------------------## +## Added by Martinski W. [2024-Nov-24] ## ##-------------------------------------## +_ConvertDAYW_NameToNumber_() +{ + if [ $# -eq 0 ] && [ -z "$1" ] ; then echo ; return 1; fi + echo "$1" | sed 's/[Ss]un/0/g;s/[Mm]on/1/g;s/[Tt]ue/2/g;s/[Ww]ed/3/g;s/[Tt]hu/4/g;s/[Ff]ri/5/g;s/[Ss]at/6/g' +} + +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-29] ## +##----------------------------------------## _ConvertDAYW_NumToName_() { if [ $# -eq 0 ] && [ -z "$1" ] ; then echo ; return 1; fi - local rangeDays rangeFreq + local daysOfWeek rangeDays rangeFreq + + if ! echo "$1" | grep -q "/" && \ + echo "$1" | grep -q "[0-6]" + then + daysOfWeek="$(_ConvertDAYW_NumberToName_ "$1")" + echo "$daysOfWeek" + return 0 + fi + rangeDays="$(echo "$1" | awk -F '/' '{print $1}')" rangeFreq="$(echo "$1" | awk -F '/' '{print $2}')" - rangeDays="$(echo "$rangeDays" | sed 's/0/Sun/g;s/1/Mon/g;s/2/Tue/g;s/3/Wed/g;s/4/Thu/g;s/5/Fri/g;s/6/Sat/g')" + rangeDays="$(_ConvertDAYW_NumberToName_ "$rangeDays")" if [ -z "$rangeFreq" ] then echo "$rangeDays" else echo "${rangeDays}/$rangeFreq" @@ -4990,10 +6310,22 @@ _ConvertDAYW_NumToName_() return 0 } -_ConvertDAYW_NameToNum_() +##-------------------------------------## +## Added by Martinski W. [2025-Mar-29] ## +##-------------------------------------## +_ConvertDayOfWeekToHR_() { if [ $# -eq 0 ] && [ -z "$1" ] ; then echo ; return 1; fi - echo "$1" | sed 's/[Ss]un/0/g;s/[Mm]on/1/g;s/[Tt]ue/2/g;s/[Ww]ed/3/g;s/[Tt]hu/4/g;s/[Ff]ri/5/g;s/[Ss]at/6/g' + local daysOfWeek="$1" + if echo "$1" | grep -qE "^[fmstw]" + then + daysOfWeek="$(_CapitalizeFirstChar_ "$1")" + elif ! echo "$1" | grep -q '[*/]' && \ + echo "$1" | grep -q "[0-6]" + then + daysOfWeek="$(_ConvertDAYW_NumToName_ "$1")" + fi + echo "$daysOfWeek" return 0 } @@ -5006,7 +6338,7 @@ _ValidateCronNumOrderDAYW_() local numDAYS numDays1 numDays2 if ! echo "$1" | grep -qE "[a-zA-Z]" then numDAYS="$1" - else numDAYS="$(_ConvertDAYW_NameToNum_ "$1")" + else numDAYS="$(_ConvertDAYW_NameToNumber_ "$1")" fi numDays1="$(echo "$numDAYS" | awk -F '[-/]' '{print $1}')" numDays2="$(echo "$numDAYS" | awk -F '[-/]' '{print $2}')" @@ -5046,19 +6378,19 @@ _ValidateCronScheduleDAYofWEEK_() return 1 } -##-------------------------------------## -## Added by Martinski W. [2024-Nov-24] ## -##-------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Mar-30] ## +##----------------------------------------## #----------------------------------------------------------# # Allow ONLY full numbers within the range [1-31] -# some intervals [ * */[2-9] */10 */12 */15 ], +# some intervals [ * */[2-9] */10 */12 */14 */15 ], # lists and ranges for the purposes of doing F/W Updates. #----------------------------------------------------------# _ValidateCronScheduleDAYofMONTH_() { if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi if [ "$1" = "*" ] || \ - echo "$1" | grep -qE "^[*]/([2-9]|10|12|15)$" + echo "$1" | grep -qE "^[*]/([2-9]|10|12|14|15)$" then return 0 ; fi if echo "$1" | grep -qE "^${CRON_DAYofMONTH_RegEx}$" then @@ -5074,8 +6406,8 @@ _ValidateCronScheduleDAYofMONTH_() fi fi printf "\n${REDct}INVALID cron value for 'DAY of MONTH' [$1]${NOct}\n" - printf "${REDct}NOTE${NOct}: Only numbers within the range [1-31],\n" - printf "specific intervals (* */[2-9] */10 */12 */15),\n" + printf "${REDct}NOTE${NOct}: Only numbers within the range [1-31], some\n" + printf "specific intervals (* */[2-9] */10 */12 */14 */15),\n" printf "lists of numbers, and single ranges are valid.\n" return 1 } @@ -5229,20 +6561,20 @@ _CheckForReturnToBeginMenu_() _ShowCronMenuHeader_() { clear - logo + _ShowLogo_ printf "================ F/W Update Check Schedule ===============\n" printf "${SEPstr}\n" } ##----------------------------------------## -## Modified by Martinski W. [2024-Dec-20] ## +## Modified by Martinski W. [2025-Mar-29] ## ##----------------------------------------## _GetCronScheduleInputDAYofMONTH_() { if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi local oldSchedDAYM newSchedDAYM oldSchedDAYHR - _GetSchedDaysHR_() + _GetSchedDayOfMonthHR_() { local cruDAYS="$1" [ "$1" = "*" ] && cruDAYS="Every day" @@ -5251,7 +6583,7 @@ _GetCronScheduleInputDAYofMONTH_() newSchedDAYM="" oldSchedDAYM="$1" - oldSchedDAYHR="$(_GetSchedDaysHR_ "$1")" + oldSchedDAYHR="$(_GetSchedDayOfMonthHR_ "$1")" while true do @@ -5283,7 +6615,7 @@ _GetCronScheduleInputDAYofMONTH_() } ##----------------------------------------## -## Modified by Martinski W. [2024-Dec-20] ## +## Modified by Martinski W. [2025-Mar-29] ## ##----------------------------------------## _GetCronScheduleInputDAYofWEEK_() { @@ -5293,7 +6625,7 @@ _GetCronScheduleInputDAYofWEEK_() _DayOfWeekNumToDayName_() { echo "$1" | sed 's/0/Sun/;s/1/Mon/;s/2/Tue/;s/3/Wed/;s/4/Thu/;s/5/Fri/;s/6/Sat/;' ; } - _GetSchedDaysHR_() + _GetSchedDayOfWeekHR_() { local cruDAYS="$1" if [ "$1" = "*" ] @@ -5306,7 +6638,7 @@ _GetCronScheduleInputDAYofWEEK_() newSchedDAYW="" oldSchedDAYW="$1" - oldSchedDAYHR="$(_GetSchedDaysHR_ "$1")" + oldSchedDAYHR="$(_GetSchedDayOfWeekHR_ "$1")" while true do @@ -5327,7 +6659,9 @@ _GetCronScheduleInputDAYofWEEK_() then newSchedDAYW="$oldSchedDAYW" if _ValidateCronScheduleDAYofWEEK_ "$oldSchedDAYW" - then break #Keep Current Value# + then #Keep Current Value# + newSchedDAYW="$(_ConvertDayOfWeekToHR_ "$oldSchedDAYW")" + break fi elif _CheckForCancelAndExitMenu_ "$newSchedDAYW" || \ _CheckForReturnToBeginMenu_ "$newSchedDAYW" || \ @@ -5335,14 +6669,7 @@ _GetCronScheduleInputDAYofWEEK_() then break elif _ValidateCronScheduleDAYofWEEK_ "$newSchedDAYW" then - if echo "$newSchedDAYW" | grep -qE "[fmstw]" - then - newSchedDAYW="$(_CapitalizeFirstChar_ "$newSchedDAYW")" - elif ! echo "$newSchedDAYW" | grep -q '[*/]' && \ - echo "$newSchedDAYW" | grep -q "[0-6]" - then - newSchedDAYW="$(_ConvertDAYW_NumToName_ "$newSchedDAYW")" - fi + newSchedDAYW="$(_ConvertDayOfWeekToHR_ "$newSchedDAYW")" break fi _WaitForEnterKey_ @@ -5460,7 +6787,7 @@ _Set_FW_UpdateCronScheduleGuided_() cronSchedDAYM="$(echo "$currCronSched" | awk -F ' ' '{print $3}')" cronSchedDAYW="$(echo "$currCronSched" | awk -F ' ' '{print $5}')" - ## MONTH is FIXED to "every month" for F/W Update Purposes ## + ## MONTH is FIXED to "Every Month" for F/W Update Purposes ## cronSchedMNTH="*" ; nextSchedMNTH="*" _ClearCronSchedValues_() @@ -5660,7 +6987,7 @@ _Set_FW_AutoUpdateCronSchedule_() } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-26] ## +## Modified by Martinski W. [2025-Feb-18] ## ##----------------------------------------## _Toggle_ScriptAutoUpdate_Config_() { @@ -5671,25 +6998,26 @@ _Toggle_ScriptAutoUpdate_Config_() if [ "$currentSetting" = "DISABLED" ] then - printf "\n${REDct}*NOTICE*${NOct}\n" + printf "\n${InvMGNct} *NOTICE* ${NOct}\n" printf "Enabling this feature allows the MerlinAU script to self-update automatically\n" printf "without user action when a newer version becomes available. This means both the\n" printf "script and the firmware updates become fully automatic. Proceed with caution.\n" printf "The recommendation is to always read the changelogs on SNBForums or Github.\n" - if _WaitForYESorNO_ "\nProceed to ${MAGENTAct}ENABLE${NOct}?" + if _WaitForYESorNO_ "\nProceed to ${MGNTct}ENABLE${NOct}?" then scriptUpdateCronSched="$(_GetScriptAutoUpdateCronSchedule_)" cronSchedStrHR="$(_TranslateCronSchedHR_ "$scriptUpdateCronSched")" printf "\nCurrent Schedule: ${GRNct}${scriptUpdateCronSched}${NOct}\n" printf "[${GRNct}${cronSchedStrHR}${NOct}]\n" - if _WaitForYESorNO_ "\nConfirm the above schedule to check for automatic script updates?" + printf "\n${BOLDct}Confirm the above schedule to check for automatic script updates${NOct}" + if _WaitForYESorNO_ YES then if _ValidateCronJobSchedule_ "$scriptUpdateCronSched" then Update_Custom_Settings "Allow_Script_Auto_Update" "ENABLED" - printf "MerlinAU automatic script updates are now ${MAGENTAct}ENABLED${NOct}.\n" + printf "MerlinAU automatic script updates are now ${MGNTct}ENABLED${NOct}.\n" printf "Adding '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' cron job for automatic script updates...\n" if _AddScriptAutoUpdateCronJob_ then @@ -5712,13 +7040,13 @@ _Toggle_ScriptAutoUpdate_Config_() retCode=1 ; keepOptionDisabled=true fi else - printf "\n${REDct}*NOTICE*${NOct}\n" + printf "\n${InvREDct} *NOTICE* ${NOct}\n" printf "Disabling this feature will require user action to update the MerlinAU script\n" printf "when a newer version becomes available. This is the default setting.\n" - if _WaitForYESorNO_ "\nProceed to ${GRNct}DISABLE${NOct}?" + if _WaitForYESorNO_ "\nProceed to ${MGNTct}DISABLE${NOct}?" then Update_Custom_Settings "Allow_Script_Auto_Update" "DISABLED" - printf "MerlinAU automatic script updates are now ${GRNct}DISABLED${NOct}.\n" + printf "MerlinAU automatic script updates are now ${MGNTct}DISABLED${NOct}.\n" printf "Removing '${GRNct}${SCRIPT_UP_CRON_JOB_TAG}${NOct}' cron job for automatic script updates...\n" _DelScriptAutoUpdateHook_ if _DelScriptAutoUpdateCronJob_ @@ -5730,13 +7058,13 @@ _Toggle_ScriptAutoUpdate_Config_() # Error message is printed within function call # fi else - printf "MerlinAU automatic script updates remain ${MAGENTAct}ENABLED${NOct}.\n" + printf "MerlinAU automatic script updates remain ${MGNTct}ENABLED${NOct}.\n" fi fi if "$keepOptionDisabled" then - printf "MerlinAU automatic script updates remain ${GRNct}DISABLED.${NOct}\n" + printf "MerlinAU automatic script updates remain ${MGNTct}DISABLED.${NOct}\n" fi _WaitForEnterKey_ return "$retCode" @@ -5945,7 +7273,7 @@ _ManageChangelogMerlin_() # Create directory to download changelog if missing # if ! _CreateDirectory_ "$FW_BIN_DIR" ; then return 1 ; fi - if [ "$mode" = "view" ] + if [ "$mode" = "view" ] || [ "$mode" = "webuidownload" ] then if [ "$fwInstalledBaseVers" -eq 3006 ] then @@ -6005,7 +7333,9 @@ _ManageChangelogMerlin_() "$inMenuMode" && _WaitForEnterKey_ "$logsMenuReturnPromptStr" fi fi + cp -fp "$changeLogFile" "$CHANGELOG_PATH" rm -f "$changeLogFile" "$wgetLogFile" + ln -sf "$CHANGELOG_PATH" "${SCRIPT_WEB_DIR}/changelog.htm" 2>/dev/null return 0 } @@ -6018,7 +7348,6 @@ _ManageChangelogGnuton_() then echo "**ERROR** **NO_PARAMS**" ; return 1 ; fi local mode="$1" # Mode should be 'download' or 'view' # - local newUpdateVerStr="" local wgetLogFile changeLogFile changeLogTag # Create directory to download changelog if missing @@ -6064,8 +7393,10 @@ _ManageChangelogGnuton_() less "$FW_Changelog_GITHUB" fi fi + cp -fp "$changeLogFile" "$CHANGELOG_PATH" rm -f "$FW_Changelog_GITHUB" "$wgetLogFile" - return 1 + ln -sf "$CHANGELOG_PATH" "${SCRIPT_WEB_DIR}/changelog.htm" 2>/dev/null + return 0 } ##----------------------------------------## @@ -6112,12 +7443,12 @@ _CheckNewUpdateFirmwareNotification_() fwNewUpdateNotificationDate="$(date +"$FW_UpdateNotificationDateFormat")" Update_Custom_Settings FW_New_Update_Notification_Vers "$fwNewUpdateNotificationVers" Update_Custom_Settings FW_New_Update_Notification_Date "$fwNewUpdateNotificationDate" - "$inRouterSWmode" && sendNewUpdateStatusEmail=true + "$mountWebGUI_OK" && sendNewUpdateStatusEmail=true if ! "$FlashStarted" then if "$isGNUtonFW" then - _ManageChangelogGnuton_ "download" "$fwNewUpdateNotificationVers" + _ManageChangelogGnuton_ "download" else _ManageChangelogMerlin_ "download" "$fwNewUpdateNotificationVers" fi @@ -6130,12 +7461,12 @@ _CheckNewUpdateFirmwareNotification_() then fwNewUpdateNotificationDate="$(date +"$FW_UpdateNotificationDateFormat")" Update_Custom_Settings FW_New_Update_Notification_Date "$fwNewUpdateNotificationDate" - "$inRouterSWmode" && sendNewUpdateStatusEmail=true + "$mountWebGUI_OK" && sendNewUpdateStatusEmail=true if ! "$FlashStarted" then if "$isGNUtonFW" then - _ManageChangelogGnuton_ "download" "$fwNewUpdateNotificationVers" + _ManageChangelogGnuton_ "download" else _ManageChangelogMerlin_ "download" "$fwNewUpdateNotificationVers" fi @@ -6250,7 +7581,7 @@ _CheckNodeFWUpdateNotification_() } ##----------------------------------------## -## Modified by Martinski W. [2024-May-18] ## +## Modified by Martinski W. [2025-Jan-22] ## ##----------------------------------------## _CheckTimeToUpdateFirmware_() { @@ -6289,25 +7620,24 @@ _CheckTimeToUpdateFirmware_() upfwDateTimeStrn="$(date -d @$upfwDateTimeSecs +"%A, %Y-%b-%d %I:%M %p")" Say "The firmware update is expected to occur on or after ${GRNct}${upfwDateTimeStrn}${NOct}, depending on when your cron job is scheduled to check again." return 1 + else + Say "The firmware update is expected to occur on ${GRNct}${nextCronTimeSecs}${NOct}." fi - Say "The firmware update is expected to occur on ${GRNct}${nextCronTimeSecs}${NOct}." - echo "" - "$isInteractive" && \ printf "\n${BOLDct}Would you like to proceed with the update now${NOct}" - if _WaitForYESorNO_ + if _WaitForYESorNO_ "$("$bypassPostponedDays" && echo YES || echo NO)" then return 0 else return 1 fi } -##-------------------------------------## -## Added by Martinski W. [2024-Feb-16] ## -##-------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2024-Dec-21] ## +##------------------------------------------## _RunEMailNotificationTest_() { - ! "$sendEMailNotificationsFlag" && return 1 + [ "$sendEMailNotificationsFlag" != "ENABLED" ] && return 1 local retCode=1 if _WaitForYESorNO_ "\nWould you like to run a test of the email notification?" @@ -6318,19 +7648,19 @@ _RunEMailNotificationTest_() return "$retCode" } -##----------------------------------------## -## Modified by Martinski W. [2024-Feb-16] ## -##----------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2024-Dec-21] ## +##------------------------------------------## _Toggle_FW_UpdateEmailNotifications_() { local emailNotificationEnabled emailNotificationNewStateStr - if "$sendEMailNotificationsFlag" + if [ "$sendEMailNotificationsFlag" = "ENABLED" ] then - emailNotificationEnabled=true + emailNotificationEnabled="ENABLED" emailNotificationNewStateStr="${REDct}DISABLE${NOct}" else - emailNotificationEnabled=false + emailNotificationEnabled="DISABLED" emailNotificationNewStateStr="${GRNct}ENABLE${NOct}" fi @@ -6340,12 +7670,12 @@ _Toggle_FW_UpdateEmailNotifications_() return 1 fi - if "$emailNotificationEnabled" + if [ "$emailNotificationEnabled" = "ENABLED" ]; then - sendEMailNotificationsFlag=false + sendEMailNotificationsFlag="DISABLED" emailNotificationNewStateStr="${REDct}DISABLED${NOct}" else - sendEMailNotificationsFlag=true + sendEMailNotificationsFlag="ENABLED" emailNotificationNewStateStr="${GRNct}ENABLED${NOct}" fi @@ -6356,9 +7686,9 @@ _Toggle_FW_UpdateEmailNotifications_() _WaitForEnterKey_ "$advnMenuReturnPromptStr" } -##------------------------------------------## -## Modified by ExtremeFiretop [2023-Nov-26] ## -##------------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-05] ## +##----------------------------------------## _Toggle_FW_UpdateCheckSetting_() { local fwUpdateCheckEnabled fwUpdateCheckNewStateStr @@ -6381,12 +7711,14 @@ _Toggle_FW_UpdateCheckSetting_() runfwUpdateCheck=false FW_UpdateCheckState=0 fwUpdateCheckNewStateStr="${REDct}DISABLED${NOct}" + Update_Custom_Settings "FW_Update_Check" "DISABLED" _DelFWAutoUpdateHook_ _DelFWAutoUpdateCronJob_ else [ -x "$FW_UpdateCheckScript" ] && runfwUpdateCheck=true FW_UpdateCheckState=1 fwUpdateCheckNewStateStr="${GRNct}ENABLED${NOct}" + Update_Custom_Settings "FW_Update_Check" "ENABLED" if _AddFWAutoUpdateCronJob_ then printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" @@ -6403,7 +7735,7 @@ _Toggle_FW_UpdateCheckSetting_() if "$runfwUpdateCheck" then printf "\nChecking for new F/W Updates... Please wait.\n" - sh $FW_UpdateCheckScript 2>&1 + sh "$FW_UpdateCheckScript" 2>&1 fi _WaitForEnterKey_ "$mainMenuReturnPromptStr" } @@ -6591,7 +7923,7 @@ _GetOfflineFirmwareVersion_() if [ -z "$firmware_version" ] then - fwVersionFormat="${BLUEct}BASE${WHITEct}.${CYANct}MAJOR${WHITEct}.${MAGENTAct}MINOR${WHITEct}.${YLWct}PATCH${NOct}" + fwVersionFormat="${BLUEct}BASE${WHITEct}.${CYANct}MAJOR${WHITEct}.${MGNTct}MINOR${WHITEct}.${YLWct}PATCH${NOct}" # Prompt user for the firmware version if extraction fails # printf "\n${REDct}**WARNING**${NOct}\n" if "$isGNUtonFW" @@ -6739,7 +8071,7 @@ _SelectOfflineUpdateFile_() } ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Apr-18] ## +## Modified by ExtremeFiretop [2024-Dec-21] ## ##------------------------------------------## _GnutonBuildSelection_() { @@ -6749,13 +8081,13 @@ _GnutonBuildSelection_() # Fetch the previous choice from the settings file local previous_choice="$(Get_Custom_Setting "TUFBuild")" - if [ "$previous_choice" = "y" ] + if [ "$previous_choice" = "ENABLED" ] then - echo "TUF Build selected for flashing" + Say "TUF build selected for flashing" firmware_choice="tuf" - elif [ "$previous_choice" = "n" ] + elif [ "$previous_choice" = "DISABLED" ] then - echo "Pure Build selected for flashing" + Say "Pure build selected for flashing" firmware_choice="pure" elif [ "$inMenuMode" = true ] then @@ -6765,31 +8097,31 @@ _GnutonBuildSelection_() read -r choice if [ "$choice" = "y" ] || [ "$choice" = "Y" ] then - echo "TUF Build selected for flashing" + Say "TUF build selected for flashing" firmware_choice="tuf" - Update_Custom_Settings "TUFBuild" "y" + Update_Custom_Settings "TUFBuild" "ENABLED" else - echo "Pure Build selected for flashing" + Say "Pure build selected for flashing" firmware_choice="pure" - Update_Custom_Settings "TUFBuild" "n" + Update_Custom_Settings "TUFBuild" "DISABLED" fi else - echo "Defaulting to Pure Build due to non-interactive mode." + Say "Defaulting to Pure build due to non-interactive mode." firmware_choice="pure" - Update_Custom_Settings "TUFBuild" "n" + Update_Custom_Settings "TUFBuild" "DISABLED" fi elif echo "$PRODUCT_ID" | grep -q "^GT-" then # Fetch the previous choice from the settings file local previous_choice="$(Get_Custom_Setting "ROGBuild")" - if [ "$previous_choice" = "y" ] + if [ "$previous_choice" = "ENABLED" ] then - echo "ROG Build selected for flashing" + Say "ROG build selected for flashing" firmware_choice="rog" - elif [ "$previous_choice" = "n" ] + elif [ "$previous_choice" = "DISABLED" ] then - echo "Pure Build selected for flashing" + Say "Pure build selected for flashing" firmware_choice="pure" elif [ "$inMenuMode" = true ] then @@ -6799,18 +8131,18 @@ _GnutonBuildSelection_() read -r choice if [ "$choice" = "y" ] || [ "$choice" = "Y" ] then - echo "ROG Build selected for flashing" + Say "ROG build selected for flashing" firmware_choice="rog" - Update_Custom_Settings "ROGBuild" "y" + Update_Custom_Settings "ROGBuild" "ENABLED" else - echo "Pure Build selected for flashing" + Say "Pure build selected for flashing" firmware_choice="pure" - Update_Custom_Settings "ROGBuild" "n" + Update_Custom_Settings "ROGBuild" "DISABLED" fi else - echo "Defaulting to Pure Build due to non-interactive mode." + Say "Defaulting to Pure build due to non-interactive mode." firmware_choice="pure" - Update_Custom_Settings "ROGBuild" "n" + Update_Custom_Settings "ROGBuild" "DISABLED" fi else # If not a TUF model, process as usual @@ -6828,6 +8160,14 @@ _RunBackupmon_() if [ -f "/jffs/scripts/backupmon.sh" ] then local current_backup_settings="$(Get_Custom_Setting "FW_Auto_Backupmon")" + + # Default to ENABLED if the setting is empty + if [ "$current_backup_settings" = "TBD" ] + then + Update_Custom_Settings "FW_Auto_Backupmon" "ENABLED" + current_backup_settings="ENABLED" + fi + if [ "$current_backup_settings" = "ENABLED" ] then # Extract version number from backupmon.sh @@ -6884,6 +8224,11 @@ _RunBackupmon_() echo "" fi else + local current_backup_settings="$(Get_Custom_Setting "FW_Auto_Backupmon")" + if [ "$current_backup_settings" != "TBD" ] + then + Delete_Custom_Settings "FW_Auto_Backupmon" + fi Say "Backup script (BACKUPMON) is not installed. Skipping backup." echo "" fi @@ -6960,7 +8305,7 @@ _RunOfflineUpdateNow_() return 1 fi clear - logo + _ShowLogo_ printf "\n---------------------------------------------------\n" offlineUpdateTrigger=true @@ -7009,7 +8354,11 @@ _RunOfflineUpdateNow_() [ "$1" != "**ERROR**" ] && [ "$2" != "**NO_URL**" ] then release_link="$2" - _RunFirmwareUpdateNow_ + if _AcquireLock_ cliFileLock + then + _RunFirmwareUpdateNow_ + _ReleaseLock_ cliFileLock + fi _ClearOfflineUpdateState_ else Say "${REDct}**ERROR**${NOct}: No firmware release URL was found for [$MODEL_ID] router model." @@ -7027,7 +8376,7 @@ _RunOfflineUpdateNow_() } ##----------------------------------------## -## Modified by Martinski W. [2025-Feb-15] ## +## Modified by Martinski W. [2025-Mar-07] ## ##----------------------------------------## _RunFirmwareUpdateNow_() { @@ -7042,13 +8391,13 @@ _RunFirmwareUpdateNow_() # it has the minimum firmware version supported. if "$routerModelCheckFailed" then - Say "${REDct}WARNING:${NOct} The current router model is not supported by this script." + Say "${REDct}*WARNING*:${NOct} The current router model is not supported by this script." if "$inMenuMode" then printf "\nWould you like to uninstall the script now?" if _WaitForYESorNO_ then - _DoUninstall_ + _DoUnInstallation_ return 0 else Say "Uninstallation cancelled. Exiting script." @@ -7097,9 +8446,7 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or Say "Using temporary fallback directory: /home/root" "$inMenuMode" && { _WaitForYESorNO_ "Continue?" || return 1 ; } # Continue # - FW_ZIP_BASE_DIR="/home/root" - FW_ZIP_DIR="${FW_ZIP_BASE_DIR}/$FW_ZIP_SUBDIR" - FW_ZIP_FPATH="${FW_ZIP_DIR}/${FW_FileName}.zip" + _SetUp_FW_UpdateZIP_DirectoryPaths_ "/home/root" fi if ! node_online_status="$(_NodeActiveStatus_)" @@ -7204,6 +8551,7 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or credsBase64="$(Get_Custom_Setting credentials_base64)" if [ -z "$credsBase64" ] || [ "$credsBase64" = "TBD" ] then + _UpdateLoginPswdCheckHelper_ InitPWD Say "${REDct}**ERROR**${NOct}: No login credentials have been saved. Use the Main Menu to save login credentials." "$inMenuMode" && _WaitForEnterKey_ "$theMenuReturnPromptMsg" return 1 @@ -7333,76 +8681,52 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or Say "Required RAM: ${requiredRAM_kb} KB - RAM Free: ${freeRAM_kb} KB - RAM Available: ${availableRAM_kb} KB" check_memory_and_prompt_reboot "$requiredRAM_kb" "$availableRAM_kb" - ##----------------------------------------## - ## Modified by Martinski W. [2024-Jun-04] ## - ##----------------------------------------## + ##------------------------------------------## + ## Modified by ExtremeFiretop [2024-Dec-21] ## + ##------------------------------------------## pure_file="$(ls -1 | grep -iE '.*[.](w|pkgtb)$' | grep -iv 'rog')" + # Detect ROG firmware file # + rog_file="$(ls | grep -i '_rog_')" - if [ "$fwInstalledBaseVers" -le 3004 ] && [ "$fwUpdateBaseNum" -le 3004 ] + # Check if a ROG build is present # + if [ -n "$rog_file" ] then - # Handle upgrades from 3004 and lower # - - # Detect ROG firmware file # - rog_file="$(ls | grep -i '_rog_')" - # Fetch the previous choice from the settings file previous_choice="$(Get_Custom_Setting "ROGBuild")" - - # Check if a ROG build is present - if [ -n "$rog_file" ] + # Use the previous choice if it exists and valid, else prompt the user for their choice in interactive mode + if [ "$previous_choice" = "ENABLED" ] + then + Say "ROG build selected for flashing" + firmware_file="$rog_file" + elif [ "$previous_choice" = "DISABLED" ] + then + Say "Pure build selected for flashing" + firmware_file="$pure_file" + elif [ "$inMenuMode" = true ] then - # Use the previous choice if it exists and valid, else prompt the user for their choice in interactive mode - if [ "$previous_choice" = "y" ] + printf "${REDct}Found ROG build: $rog_file.${NOct}\n" + printf "${REDct}Would you like to use the ROG build?${NOct}\n" + printf "Enter your choice (y/n): " + read -r choice + if [ "$choice" = "y" ] || [ "$choice" = "Y" ] then - Say "ROG Build selected for flashing" + Say "ROG build selected for flashing" firmware_file="$rog_file" - elif [ "$previous_choice" = "n" ] - then - Say "Pure Build selected for flashing" - firmware_file="$pure_file" - elif [ "$inMenuMode" = true ] - then - printf "${REDct}Found ROG build: $rog_file.${NOct}\n" - printf "${REDct}Would you like to use the ROG build?${NOct}\n" - printf "Enter your choice (y/n): " - read -r choice - if [ "$choice" = "y" ] || [ "$choice" = "Y" ]; then - Say "ROG Build selected for flashing" - firmware_file="$rog_file" - Update_Custom_Settings "ROGBuild" "y" - else - Say "Pure Build selected for flashing" - firmware_file="$pure_file" - Update_Custom_Settings "ROGBuild" "n" - fi + Update_Custom_Settings "ROGBuild" "ENABLED" else - # Default to pure_file in non-interactive mode if no previous choice - Say "Pure Build selected for flashing" - Update_Custom_Settings "ROGBuild" "n" + Say "Pure build selected for flashing" firmware_file="$pure_file" + Update_Custom_Settings "ROGBuild" "DISABLED" fi else - # No ROG build found, use the pure build - Say "No ROG Build detected. Skipping." - firmware_file="$pure_file" - fi - elif [ "$fwInstalledBaseVers" -eq 3004 ] && [ "$fwUpdateBaseNum" -ge 3006 ] - then - # Handle upgrade from 3004 to 3006 - # Fetch the previous choice from the settings file - previous_choice="$(Get_Custom_Setting "ROGBuild")" - - # Handle upgrade from 3004 to 3006 if there is a ROG setting - if [ "$previous_choice" = "y" ] - then - Say "Upgrading from 3004 to 3006, ROG UI is no longer supported, auto-selecting Pure UI firmware." - firmware_file="$pure_file" - Update_Custom_Settings "ROGBuild" "n" - else + # Default to pure_file in non-interactive mode if no previous choice + Say "Defaulting to Pure build for flashing" + Update_Custom_Settings "ROGBuild" "DISABLED" firmware_file="$pure_file" fi else - # Handle upgrades from 3006 and higher # + # No ROG build found, use the pure build + Say "No ROG build found. Skipping." firmware_file="$pure_file" fi @@ -7489,6 +8813,7 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or if echo "$curl_response" | grep -Eq 'url=index\.asp|url=GameDashboard\.asp' then + _UpdateLoginPswdCheckHelper_ SUCCESS if [ -f /opt/bin/diversion ] then # Extract version number from Diversion @@ -7607,6 +8932,7 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or _ReleaseLock_ /sbin/service reboot else + _UpdateLoginPswdCheckHelper_ FAILURE Say "${REDct}**ERROR**${NOct}: Router Login failed." if "$inMenuMode" || "$isInteractive" then @@ -7642,7 +8968,11 @@ Please manually update to version ${GRNct}${MinSupportedFirmwareVers}${NOct} or _PostUpdateEmailNotification_() { _DelPostUpdateEmailNotifyScriptHook_ - Update_Custom_Settings FW_New_Update_Changelog_Approval TBD + currentChangelogValue="$(Get_Custom_Setting CheckChangeLog)" + if [ "$currentChangelogValue" = "ENABLED" ] + then + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "TBD" + fi local theWaitDelaySecs=10 local maxWaitDelaySecs=600 #10 minutes# @@ -7680,7 +9010,7 @@ _PostUpdateEmailNotification_() } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-17] ## +## Modified by Martinski W. [2024-Jan-05] ## ##----------------------------------------## _PostRebootRunNow_() { @@ -7720,7 +9050,11 @@ _PostRebootRunNow_() Say "END of $logMsg [$$curWaitDelaySecs sec.]" sleep 30 ## Let's wait a bit & proceed ## - _RunFirmwareUpdateNow_ + if _AcquireLock_ cliFileLock + then + _RunFirmwareUpdateNow_ + _ReleaseLock_ cliFileLock + fi } ##----------------------------------------## @@ -7738,10 +9072,10 @@ _DelFWAutoUpdateHook_() sed -i -e '/\/'"$ScriptFileName"' addCronJob & '"$hookScriptTagStr"'/d' "$hookScriptFile" if [ $? -eq 0 ] then - Say "Cron job hook was deleted successfully from '$hookScriptFile' script." + Say "F/W Update cron job hook was deleted successfully from '$hookScriptFile' script." fi else - printf "Cron job hook does not exist in '$hookScriptFile' script.\n" + printf "F/W Update cron job hook does not exist in '$hookScriptFile' script.\n" fi } @@ -7822,29 +9156,149 @@ _DelScriptAutoUpdateHook_() sed -i -e '/\/'"$ScriptFileName"' scriptAUCronJob & '"$hookScriptTagStr"'/d' "$hookScriptFile" if [ $? -eq 0 ] then - Say "Cron job hook was deleted successfully from '$hookScriptFile' script." + Say "ScriptAU cron job hook was deleted successfully from '$hookScriptFile' script." fi else - printf "Cron job hook does not exist in '$hookScriptFile' script.\n" + printf "ScriptAU cron job hook does not exist in '$hookScriptFile' script.\n" + fi +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-05] ## +##-------------------------------------## +_CheckForMinimumRequirements_() +{ + local requirementsCheckOK=true + + if [ "$(uname -o)" != "ASUSWRT-Merlin" ] + then + requirementsCheckOK=false + Say "\n${CRITct}*WARNING*:${NOct} The current firmware installed is NOT ASUSWRT-Merlin.\n" + fi + + if ! nvram get rc_support | grep -qwoF "am_addons" + then + requirementsCheckOK=false + Say "\n${CRITct}*WARNING*:${NOct} The current firmware installed does NOT support add-ons.\n" + fi + + if ! _CheckForMinimumModelSupport_ + then + requirementsCheckOK=false + Say "\n${CRITct}*WARNING*:${NOct} The current router model ${REDct}${FW_RouterModelID}${NOct} is NOT supported by this script.\n" + fi + + if ! _CheckForMinimumVersionSupport_ + then + requirementsCheckOK=false + Say "\n${CRITct}*WARNING*:${NOct} The current firmware version is below the minimum supported by this script." + printf "\nCurrent F/W version found: ${REDct}${FW_InstalledVersion}${NOct}" + printf "\nMinimum version required: ${GRNct}${MinSupportedFirmwareVers}${NOct}\n" + fi + + jffsScriptOK="$(nvram get jffs2_scripts)" + if [ -n "$jffsScriptOK" ] && [ "$jffsScriptOK" -ne 1 ] && "$requirementsCheckOK" + then + nvram set jffs2_scripts=1 + nvram commit + mkdir -p "$SCRIPTS_PATH" + chmod 755 "$SCRIPTS_PATH" + Say "\nThe NVRAM Custom JFFS Scripts option was ${GRNct}ENABLED${NOct}.\n" + fi + + "$requirementsCheckOK" && return 0 || return 1 +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-15] ## +##-------------------------------------## +_DoStartupInit_() +{ + _CreateDirPaths_ + _InitCustomSettingsConfig_ + _CreateSymLinks_ + _InitHelperJSFile_ + _SetVersionSharedSettings_ local "$SCRIPT_VERSION" + + if "$mountWebGUI_OK" + then + _Mount_WebUI_ + _AutoStartupHook_ create 2>/dev/null + _AutoServiceEvent_ create 2>/dev/null fi } ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-24] ## +## Modified by Martinski W. [2025-Jan-15] ## +##----------------------------------------## +_DoInstallation_() +{ + local webguiOK=true + + if ! _AcquireLock_ cliFileLock ; then return 1 ; fi + + _CreateDirPaths_ + _InitCustomSettingsConfig_ + _CreateSymLinks_ + _InitHelperJSFile_ + _SetVersionSharedSettings_ local "$SCRIPT_VERSION" + _SetVersionSharedSettings_ server "$SCRIPT_VERSION" + _DownloadScriptFiles_ install + + if "$mountWebGUI_OK" + then + ! _Mount_WebUI_ && webguiOK=false + _AutoStartupHook_ create 2>/dev/null + _AutoServiceEvent_ create 2>/dev/null + fi + ! "$webguiOK" && _DoExit_ 1 + + _ReleaseLock_ cliFileLock + _ReleaseLock_ cliOptsLock + + if ! _AcquireLock_ cliMenuLock + then Say "Exiting..." ; exit 1 ; fi + _ConfirmCronJobForFWAutoUpdates_ + _MainMenu_ +} + ##----------------------------------------## -_DoUninstall_() +## Modified by Martinski W. [2025-Jan-22] ## +##----------------------------------------## +_DoUnInstallation_() { - printf "Are you sure you want to uninstall $ScriptFileName script now" + "$isInteractive" && \ + printf "\n${BOLDct}Are you sure you want to uninstall $ScriptFileName script now${NOct}" ! _WaitForYESorNO_ && return 0 + if ! _AcquireLock_ cliFileLock ; then return 1 ; fi + + local savedCFGPath="${SCRIPTS_PATH}/${SCRIPT_NAME}_CFG.SAVED.TXT" + + printf "\n${BOLDct}Do you want to keep/save the $SCRIPT_NAME configuration file${NOct}" + if _WaitForYESorNO_ "$("$keepConfigFile" && echo YES || echo NO)" + then + keepConfigFile=true + mv -f "$CONFIG_FILE" "$savedCFGPath" + fi + _DelFWAutoUpdateHook_ _DelFWAutoUpdateCronJob_ _DelScriptAutoUpdateHook_ _DelScriptAutoUpdateCronJob_ _DelPostRebootRunScriptHook_ _DelPostUpdateEmailNotifyScriptHook_ + _SetVersionSharedSettings_ delete + + if "$mountWebGUI_OK" + then + _Unmount_WebUI_ + _AutoStartupHook_ delete 2>/dev/null + _AutoServiceEvent_ delete 2>/dev/null + fi if rm -fr "${SETTINGS_DIR:?}" && \ + rm -fr "${SCRIPT_WEB_DIR:?}" && \ rm -fr "${FW_BIN_BASE_DIR:?}/$ScriptDirNameD" && \ rm -fr "${FW_LOG_BASE_DIR:?}/$ScriptDirNameD" && \ rm -fr "${FW_ZIP_BASE_DIR:?}/$ScriptDirNameD" && \ @@ -7852,7 +9306,16 @@ _DoUninstall_() then Say "${GRNct}Successfully Uninstalled.${NOct}" else - Say "${REDct}Error: Uninstallation failed.${NOct}" + Say "${CRITct}**ERROR**: Uninstallation failed.${NOct}" + fi + + if "$keepConfigFile" + then + if mkdir -p "$SETTINGS_DIR" + then + chmod 755 "$SETTINGS_DIR" + mv -f "$savedCFGPath" "$CONFIG_FILE" + fi fi _DoExit_ 0 } @@ -7917,9 +9380,9 @@ _SetEMailFormatType_() _WaitForEnterKey_ "$advnMenuReturnPromptStr" } -##-------------------------------------## -## Added by Martinski W. [2024-Feb-16] ## -##-------------------------------------## +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-27] ## +##----------------------------------------## _SetSecondaryEMailAddress_() { local currCC_NameOpt currCC_AddrOpt @@ -7951,9 +9414,10 @@ _SetSecondaryEMailAddress_() do printf "\nEnter a secondary email address to receive email notifications.\n" if [ -z "$currCC_AddrOpt" ] - then printf "[${theADExitStr}] [${currCC_AddrStr}]: " - else printf "[${theADExitStr}] [${clearOptStr}] [${currCC_AddrStr}]: " + then printf "[${theADExitStr}]\n" + else printf "[${theADExitStr}] [${clearOptStr}]\n" fi + printf "[${currCC_AddrStr}]: " read -r userInput [ -z "$userInput" ] && break @@ -8019,7 +9483,7 @@ _SetSecondaryEMailAddress_() while true do printf "\nEnter a name or alias for the secondary email address.\n" - printf "[${theADExitStr}] [${currCC_NameStr}]: " + printf "[${theADExitStr}]\n[${currCC_NameStr}]: " read -r userInput if [ -z "$userInput" ] || echo "$userInput" | grep -qE "^(e|exit|Exit)$" @@ -8077,7 +9541,7 @@ _ProcessMeshNodes_() if ! node_list="$(_GetNodeIPv4List_)" then node_list="" ; fi - if "$inRouterSWmode" + if "$aiMeshNodes_OK" then if [ -n "$node_list" ] then @@ -8115,108 +9579,125 @@ keepWfile=0 trap '_DoCleanUp_ 0 "$keepZIPfile" "$keepWfile" ; _DoExit_ 0' HUP INT QUIT ABRT TERM ##----------------------------------------## -## Modified by Martinski W. [2024-Feb-28] ## +## Modified by Martinski W. [2025-Jan-05] ## ##----------------------------------------## -# Prevent running this script multiple times simultaneously # -if ! _AcquireLock_ +if [ $# -eq 1 ] && [ "$1" = "resetLockFile" ] then - if [ $# -eq 1 ] && [ "$1" = "resetLockFile" ] - then - _ReleaseLock_ - Say "Lock file has now been reset. Exiting..." - exit 0 - fi - Say "Exiting..." ; exit 1 + ! _AcquireLock_ && printf "\nReset Lock and Exit...\n" + _ReleaseLock_ ; exit 0 fi -# Check if the router model is supported OR if -# it has the minimum firmware version supported. -check_model_support -check_version_support - -##-------------------------------------## -## Added by Martinski W. [2024-Jan-24] ## -##-------------------------------------## -_CheckEMailConfigFileFromAMTM_ 0 +##---------------------------------------## +## Added by ExtremeFiretop [2025-Feb-08] ## +##---------------------------------------## +_CheckAndSetBackupOption_() +{ + local currentBackupOption + currentBackupOption="$(Get_Custom_Setting "FW_Auto_Backupmon")" + if [ -f "/jffs/scripts/backupmon.sh" ] + then + # If setting is empty, add it to the configuration file # + if [ "$currentBackupOption" = "TBD" ] + then Update_Custom_Settings FW_Auto_Backupmon "ENABLED" + fi + else + # If the configuration setting exists, delete it # + if [ "$currentBackupOption" != "TBD" ] + then Delete_Custom_Settings "FW_Auto_Backupmon" + fi + fi +} -##----------------------------------------## -## Modified by Martinski W. [2025-Feb-15] ## -##----------------------------------------## -if [ $# -gt 0 ] && [ -n "$1" ] -then - inMenuMode=false - case "$1" in - run_now) _RunFirmwareUpdateNow_ - ;; - processNodes) _ProcessMeshNodes_ 0 - ;; - addCronJob) _AddFWAutoUpdateCronJob_ - ;; - scriptAUCronJob) _AddScriptAutoUpdateCronJob_ - ;; - postRebootRun) _PostRebootRunNow_ - ;; - postUpdateEmail) _PostUpdateEmailNotification_ - ;; - about) _ShowAbout_ - ;; - help) _ShowHelp_ - ;; - checkupdates) _CheckForNewScriptUpdates_ - ;; - forceupdate) _SCRIPTUPDATE_ force - ;; - develop) _ChangeToDev_ - ;; - stable) _ChangeToStable_ - ;; - uninstall) _DoUninstall_ - ;; - *) printf "${REDct}INVALID Parameter.${NOct}\n" - ;; - esac - _DoExit_ 0 -fi +##---------------------------------------## +## Added by ExtremeFiretop [2025-Feb-08] ## +##---------------------------------------## +_SetDefaultBuildType_() +{ + if echo "$PRODUCT_ID" | grep -q "^TUF-" + then + if [ "$(Get_Custom_Setting "TUFBuild")" = "TBD" ] + then Update_Custom_Settings "TUFBuild" "DISABLED" + fi + elif echo "$PRODUCT_ID" | grep -q "^GT-" + then + if [ "$(Get_Custom_Setting "ROGBuild")" = "TBD" ] + then Update_Custom_Settings "ROGBuild" "DISABLED" + fi + fi +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-05] ## +##-------------------------------------## +_DisableFWAutoUpdateChecks_() +{ + _DelFWAutoUpdateHook_ + _DelFWAutoUpdateCronJob_ + Update_Custom_Settings "FW_Update_Check" "DISABLED" -# Download the latest version file from the source repository # -# to check if there's a new version update to notify the user # -_CheckForNewScriptUpdates_ + runfwUpdateCheck=false + if [ "$FW_UpdateCheckState" -ne 0 ] + then + FW_UpdateCheckState=0 + nvram set firmware_check_enable="$FW_UpdateCheckState" + nvram commit + fi +} ##-------------------------------------## -## Added by Martinski W. [2024-Nov-24] ## +## Added by Martinski W. [2025-Jan-05] ## ##-------------------------------------## -if [ "$ScriptAutoUpdateSetting" = "ENABLED" ] -then - _AddScriptAutoUpdateCronJob_ -fi +_EnableFWAutoUpdateChecks_() +{ + _AddFWAutoUpdateHook_ + Update_Custom_Settings "FW_Update_Check" "ENABLED" -# Check if the PREVIOUS Cron Job ID already exists # -if eval $cronListCmd | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG_OLD}#$" -then #If it exists, delete the OLD one & create a NEW one# - cru d "$CRON_JOB_TAG_OLD" ; sleep 1 ; _AddFWAutoUpdateCronJob_ -fi + runfwUpdateCheck=true + if [ "$FW_UpdateCheckState" -ne 1 ] + then + FW_UpdateCheckState=1 + nvram set firmware_check_enable="$FW_UpdateCheckState" + nvram commit + fi +} ##----------------------------------------## -## Modified by Martinski W. [2024-Nov-24] ## +## Modified by Martinski W. [2025-Jan-12] ## ##----------------------------------------## -FW_UpdateCheckState="$(nvram get firmware_check_enable)" -[ -z "$FW_UpdateCheckState" ] && FW_UpdateCheckState=0 -if [ "$FW_UpdateCheckState" -eq 1 ] -then - runfwUpdateCheck=true +_ConfirmCronJobForFWAutoUpdates_() +{ + if [ $# -gt 0 ] && [ -n "$1" ] && \ + echo "$1" | grep -qE "^(install|startup)$" + then return 1 ; fi + + # Check if the PREVIOUS Cron Job ID already exists # + if eval $cronListCmd | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG_OLD}#$" + then #If it exists, delete the OLD one & create a NEW one# + cru d "$CRON_JOB_TAG_OLD" ; sleep 1 ; _AddFWAutoUpdateCronJob_ + fi + + # Retrieve custom setting for automatic F/W update checks # + # Expected values: "ENABLED", "TBD", "DISABLED" (or fallback) # + fwUpdateCheckState="$(Get_Custom_Setting "FW_Update_Check")" + + # Always start with a default of "false" (do not run checks by default) # + runfwUpdateCheck=false + + FW_UpdateCheckState="$(nvram get firmware_check_enable)" + [ -z "$FW_UpdateCheckState" ] && FW_UpdateCheckState=0 - # Check if the F/W Update CRON job already exists # - if ! eval $cronListCmd | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG}#$" + ##------------------------------------------------------------## + # Priority is given to custom '$fwUpdateCheckState' value. + # The NVRAM variable is updated only after we've decided + # what to do based on the custom setting. + ##------------------------------------------------------------## + + # 1) "ENABLED": Automatically enable checks (no user prompt) # + if [ "$fwUpdateCheckState" = "ENABLED" ] then - logo - # If CRON job does not exist, ask user for permission to add # - printf "Do you want to enable automatic firmware update checks?\n" - printf "This will create a CRON job to check for updates regularly.\n" - printf "The CRON can be disabled at anytime via the main menu.\n" - if _WaitForYESorNO_ + if ! eval "$cronListCmd" | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG}#$" then - # Add the cron job since it doesn't exist and user consented - printf "Adding '${GRNct}${CRON_JOB_TAG}${NOct}' cron job...\n" + printf "Auto-enabling cron job '${GRNct}${CRON_JOB_TAG}${NOct}'...\n" if _AddFWAutoUpdateCronJob_ then printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" @@ -8225,39 +9706,75 @@ then else printf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" fi - _AddFWAutoUpdateHook_ else - printf "Automatic firmware update checks will be ${REDct}DISABLED${NOct}.\n" - printf "You can enable this feature later via the main menu.\n" - FW_UpdateCheckState=0 - runfwUpdateCheck=false - nvram set firmware_check_enable="$FW_UpdateCheckState" - nvram commit + printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" fi - else - printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" - _AddFWAutoUpdateHook_ - fi + _EnableFWAutoUpdateChecks_ - # Check if there's a new F/W update available # - "$runfwUpdateCheck" && [ -x "$FW_UpdateCheckScript" ] && sh $FW_UpdateCheckScript 2>&1 & - _WaitForEnterKey_ -fi + # 2) "TBD": Prompt the user (original behavior) # + elif [ "$fwUpdateCheckState" = "TBD" ] + then + if ! eval $cronListCmd | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG}#$" + then + _ShowLogo_ + printf "Do you want to enable automatic firmware update checks?\n" + printf "This will create a CRON job to check for updates regularly.\n" + printf "The CRON can be disabled at any time via the main menu.\n" -padStr=" " + if _WaitForYESorNO_ + then + # User said YES -> enable checks # + printf "Adding '${GRNct}${CRON_JOB_TAG}${NOct}' cron job...\n" + if ! eval "$cronListCmd" | grep -qE "$CRON_JOB_RUN #${CRON_JOB_TAG}#$" + then + if _AddFWAutoUpdateCronJob_ + then + printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' was added successfully.\n" + cronSchedStrHR="$(_TranslateCronSchedHR_ "$FW_UpdateCronJobSchedule")" + printf "Job Schedule: ${GRNct}${cronSchedStrHR}${NOct}\n" + else + printf "${REDct}**ERROR**${NOct}: Failed to add the cron job [${CRON_JOB_TAG}].\n" + fi + else + printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" + fi + _EnableFWAutoUpdateChecks_ + else + # User said NO -> disable checks # + printf "Automatic firmware update checks will be ${REDct}DISABLED${NOct}.\n" + printf "You can enable this feature later via the main menu.\n" + _DisableFWAutoUpdateChecks_ + fi + _WaitForEnterKey_ + else + printf "Cron job '${GRNct}${CRON_JOB_TAG}${NOct}' already exists.\n" + Update_Custom_Settings "FW_Update_Check" "ENABLED" + _AddFWAutoUpdateHook_ + runfwUpdateCheck=true + fi -##----------------------------------------## -## Modified by Martinski W. [2024-Jun-05] ## -##----------------------------------------## -FW_RouterProductID="${GRNct}${PRODUCT_ID}${NOct}" -# Some Model IDs have a lower case suffix of the same Product ID # -if [ "$PRODUCT_ID" = "$(echo "$MODEL_ID" | tr 'a-z' 'A-Z')" ] -then FW_RouterModelID="${FW_RouterProductID}" -else FW_RouterModelID="${FW_RouterProductID}/${GRNct}${MODEL_ID}${NOct}" -fi + # 3) "DISABLED": Perform the disable steps (same as _Toggle_FW_UpdateCheckSetting_) # + elif [ "$fwUpdateCheckState" = "DISABLED" ] + then + printf "Firmware update checks have been ${REDct}DISABLED${NOct}.\n" + _DisableFWAutoUpdateChecks_ -FW_InstalledVersion="$(_GetCurrentFWInstalledLongVersion_)" -FW_InstalledVerStr="${GRNct}${FW_InstalledVersion}${NOct}" + # 4) Unknown/fallback -> treat as DISABLED # + else + printf "Unknown FW_Update_Check value: '%s'. Disabling firmware checks.\n" "$fwUpdateCheckState" + _DisableFWAutoUpdateChecks_ + fi + + ##------------------------------------------------------------## + # If 'runfwUpdateCheck' is true and built-in script is found + # run the built-in F/W Update check in the background. + ##------------------------------------------------------------## + if "$runfwUpdateCheck" && [ -x "$FW_UpdateCheckScript" ] + then + sh "$FW_UpdateCheckScript" 2>&1 + sleep 1 + fi +} ##-------------------------------------## ## Added by Martinski W. [2024-May-03] ## @@ -8291,16 +9808,17 @@ _GetFileSelectionIndex_() if [ $# -lt 2 ] || [ "$2" != "-MULTIOK" ] then multiIndexListOK=false - promptStr="Enter selection [${selectStr}] [${theLGExitStr}]?" + promptStr="Enter selection [${selectStr}]?" else multiIndexListOK=true - promptStr="Enter selection [${selectStr} | ${theAllStr}] [${theLGExitStr}]?" + promptStr="Enter selection [${selectStr} | ${theAllStr}]?" fi fileIndex=0 multiIndex=false numRegEx="([1-9]|[1-9][0-9])" while true do + printf "[${theLGExitStr}]\n" printf "${promptStr} " ; read -r userInput if [ -z "$userInput" ] || \ @@ -8555,7 +10073,7 @@ _InvalidMenuSelection_() ##----------------------------------------## ## Modified by Martinski W. [2025-Feb-15] ## ##----------------------------------------## -_ShowMainMenu_() +_ShowMainMenuOptions_() { local FW_NewUpdateVerStr FW_NewUpdateVersion @@ -8563,12 +10081,16 @@ _ShowMainMenu_() # Check if router reports a new F/W update is available. # If yes, modify the notification settings accordingly. #-----------------------------------------------------------# - FW_NewUpdateVersion="$(_GetLatestFWUpdateVersionFromRouter_)" && \ - [ -n "$FW_InstalledVersion" ] && [ -n "$FW_NewUpdateVersion" ] && \ - _CheckNewUpdateFirmwareNotification_ "$FW_InstalledVersion" "$FW_NewUpdateVersion" + if FW_NewUpdateVersion="$(_GetLatestFWUpdateVersionFromRouter_)" && \ + [ -n "$FW_NewUpdateVersion" ] && [ -n "$FW_InstalledVersion" ] && \ + [ "$FW_NewUpdateVersion" != "$FW_NewUpdateVerInit" ] + then + FW_NewUpdateVerInit="$FW_NewUpdateVersion" + _CheckNewUpdateFirmwareNotification_ "$FW_InstalledVersion" "$FW_NewUpdateVersion" + fi clear - logo + _ShowLogo_ printf "${YLWct}============ By ExtremeFiretop & Martinski W. ============${NOct}\n\n" # New Script Update Notification # @@ -8610,7 +10132,7 @@ _ShowMainMenu_() fi if "$isGNUtonFW" - then FirmwareFlavor="${MAGENTAct}GNUton${NOct}" + then FirmwareFlavor="${MGNTct}GNUton${NOct}" else FirmwareFlavor="${BLUEct}Merlin${NOct}" fi @@ -8619,21 +10141,21 @@ _ShowMainMenu_() then if ! FW_NewUpdateVerStr="$(_GetLatestFWUpdateVersionFromRouter_ 1)" then FW_NewUpdateVerStr=" ${REDct}NONE FOUND${NOct}" - else FW_NewUpdateVerStr="${InvGRNct} ${FW_NewUpdateVerStr} ${NOct}$arrowStr" + else FW_NewUpdateVerStr="${InvBGRNct} ${FW_NewUpdateVerStr} ${NOct}$arrowStr" fi - printf "\n Router's Product Name/Model ID: ${FW_RouterModelID}${padStr}(H)ide" + printf "\n Router's Product Name/Model ID: ${FW_RouterModelIDstr}${padStr}(H)ide" printf "\n USB-Attached Storage Connected: $USBConnected" printf "\n F/W Variant Configuration Found: $FirmwareFlavor" printf "\n F/W Version Currently Installed: $FW_InstalledVerStr" printf "\n F/W Update Version Available: $FW_NewUpdateVerStr" printf "\n F/W Update Estimated Run Date: $ExpectedFWUpdateRuntime" else - printf "\n Router's Product Name/Model ID: ${FW_RouterModelID}${padStr}(S)how" + printf "\n Router's Product Name/Model ID: ${FW_RouterModelIDstr}${padStr}(S)how" fi printf "\n${SEPstr}" printf "\n ${GRNct}1${NOct}. Run F/W Update Check Now\n" - printf "\n ${GRNct}2${NOct}. Set Router Login Credentials\n" + printf "\n ${GRNct}2${NOct}. Set Router Login Password\n" # Enable/Disable the ASUS Router's built-in "F/W Update Check" # FW_UpdateCheckState="$(nvram get firmware_check_enable)" @@ -8644,10 +10166,11 @@ _ShowMainMenu_() printf "\n${padStr}[Currently ${InvREDct} DISABLED ${NOct}]" else printf "\n ${GRNct}3${NOct}. Toggle Automatic F/W Update Checks" - printf "\n${padStr}[Currently ${InvGRNct} ENABLED ${NOct}]" + printf "\n${padStr}[Currently ${InvBGRNct} ENABLED ${NOct}]" fi printf "\n${padStr}[Last Notification Date: $notificationStr]\n" + FW_UpdatePostponementDays="$(Get_Custom_Setting FW_New_Update_Postponement_Days)" printf "\n ${GRNct}4${NOct}. Set F/W Update Postponement Days" printf "\n${padStr}[Current Days: ${GRNct}${FW_UpdatePostponementDays}${NOct}]\n" @@ -8664,19 +10187,19 @@ _ShowMainMenu_() ChangelogApproval="$(Get_Custom_Setting "FW_New_Update_Changelog_Approval")" if [ "$ChangelogApproval" = "BLOCKED" ] then - printf "\n ${GRNct}6${NOct}. Toggle F/W Update Changelog Approval" - printf "\n${padStr}[Currently ${REDct}${ChangelogApproval}${NOct}]\n" + printf "\n ${GRNct}6${NOct}. Toggle F/W Update Changelog Approval" + printf "\n${padStr}[Currently ${REDct}${ChangelogApproval}${NOct}]\n" elif [ "$ChangelogApproval" = "APPROVED" ] then - printf "\n ${GRNct}6${NOct}. Toggle F/W Update Changelog Approval" - printf "\n${padStr}[Currently ${GRNct}${ChangelogApproval}${NOct}]\n" + printf "\n ${GRNct}6${NOct}. Toggle F/W Update Changelog Approval" + printf "\n${padStr}[Currently ${GRNct}${ChangelogApproval}${NOct}]\n" fi # Check for new script updates # if [ "$scriptUpdateNotify" != "0" ] then printf "\n ${GRNct}up${NOct}. Update $SCRIPT_NAME Script" - printf "\n${padStr}[Version ${InvGRNct} ${DLRepoVersion} ${NOct} Available for Download]\n" + printf "\n${padStr}[Version ${InvBGRNct} ${DLRepoVersion} ${NOct} Available for Download]\n" else printf "\n ${GRNct}up${NOct}. Force Update $SCRIPT_NAME Script" printf "\n${padStr}[No Update Available]\n" @@ -8686,7 +10209,7 @@ _ShowMainMenu_() printf "\n ${GRNct}ad${NOct}. Advanced Options\n" # Check for AiMesh Nodes # - if "$inRouterSWmode" && [ -n "$node_list" ]; then + if "$aiMeshNodes_OK" && [ -n "$node_list" ]; then printf "\n ${GRNct}mn${NOct}. AiMesh Node(s) Info\n" fi @@ -8698,7 +10221,7 @@ _ShowMainMenu_() } ##----------------------------------------## -## Modified by Martinski W. [2025-Feb-16] ## +## Modified by Martinski W. [2025-Feb-18] ## ##----------------------------------------## _ShowAdvancedOptionsMenu_() { @@ -8706,13 +10229,15 @@ _ShowAdvancedOptionsMenu_() local scriptUpdateCronSched current_build_type clear - logo + _ShowLogo_ printf "================== Advanced Options Menu =================\n" printf "${SEPstr}\n" + _SetUp_FW_UpdateZIP_DirectoryPaths_ printf "\n ${GRNct}1${NOct}. Set Directory for F/W Update File" printf "\n${padStr}[Current Path: ${GRNct}${FW_ZIP_DIR}${NOct}]\n" + FW_UpdateCronJobSchedule="$(Get_Custom_Setting FW_New_Update_Cron_Job_Schedule)" printf "\n ${GRNct}2${NOct}. Set F/W Update Cron Schedule" printf "\n${padStr}[Current Schedule: ${GRNct}${FW_UpdateCronJobSchedule}${NOct}]" printf "\n${padStr}[${GRNct}%s${NOct}]\n" "$(_TranslateCronSchedHR_ "$FW_UpdateCronJobSchedule")" @@ -8735,11 +10260,13 @@ _ShowAdvancedOptionsMenu_() printf "\n${padStr}[Currently ${REDct}ENABLED${NOct}]\n" fi + # Check if the file BACKUPMON exists # if [ -f "/jffs/scripts/backupmon.sh" ] then # Retrieve the current backup setting # currentBackupOption="$(Get_Custom_Setting "FW_Auto_Backupmon")" + # Display the backup option toggle menu printf "\n ${GRNct}ab${NOct}. Toggle Automatic Backups" if [ "$currentBackupOption" = "DISABLED" ] then printf "\n${padStr}[Currently ${REDct}${currentBackupOption}${NOct}]\n" @@ -8751,121 +10278,102 @@ _ShowAdvancedOptionsMenu_() printf "\n ${GRNct}au${NOct}. Toggle Auto-Updates for MerlinAU Script" if [ "$ScriptAutoUpdateSetting" = "DISABLED" ] then - printf "\n${padStr}[Currently ${GRNct}DISABLED${NOct}]\n" + printf "\n${padStr}[Currently ${InvBMGNct} DISABLED ${NOct}]\n" else scriptUpdateCronSched="$(_GetScriptAutoUpdateCronSchedule_)" printf "\n${padStr}[Current Schedule: ${GRNct}${scriptUpdateCronSched}${NOct}]" printf "\n${padStr}[${GRNct}%s${NOct}]\n" "$(_TranslateCronSchedHR_ "$scriptUpdateCronSched")" fi - if "$isGNUtonFW" - then - if [ "$fwInstalledBaseVers" -le 3004 ] - then - current_build_type="$(Get_Custom_Setting "TUFBuild")" - - # Convert the setting to a descriptive text - if [ "$current_build_type" = "y" ]; then - current_build_type_menu="TUF Build" - elif [ "$current_build_type" = "n" ]; then - current_build_type_menu="Pure Build" - else - current_build_type_menu="NOT SET" - fi - - if echo "$PRODUCT_ID" | grep -q "^TUF-" - then - printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" - if [ "$current_build_type_menu" = "NOT SET" ] - then printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menu}${NOct}]\n" - else printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menu}${NOct}]\n" - fi - fi - elif [ "$fwInstalledBaseVers" -ge 3006 ] - then - local current_build_typerog="$(Get_Custom_Setting "ROGBuild")" - - # Convert the setting to a descriptive text - if [ "$current_build_typerog" = "y" ]; then - current_build_type_menurog="ROG Build" - elif [ "$current_build_typerog" = "n" ]; then - current_build_type_menurog="Pure Build" - else - current_build_type_menurog="NOT SET" - fi - - if echo "$PRODUCT_ID" | grep -q "^GT-" - then - printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" - if [ "$current_build_type_menurog" = "NOT SET" ] - then printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menurog}${NOct}]\n" - else printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menurog}${NOct}]\n" - fi - fi - - local current_build_typetuf="$(Get_Custom_Setting "TUFBuild")" - - # Convert the setting to a descriptive text - if [ "$current_build_typetuf" = "y" ]; then - current_build_type_menutuf="TUF Build" - elif [ "$current_build_typetuf" = "n" ]; then - current_build_type_menutuf="Pure Build" - else - current_build_type_menutuf="NOT SET" - fi + if "$isGNUtonFW" + then + if echo "$PRODUCT_ID" | grep -q "^TUF-" + then + local current_build_type="$(Get_Custom_Setting "TUFBuild")" - if echo "$PRODUCT_ID" | grep -q "^TUF-" - then - printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" - if [ "$current_build_type_menutuf" = "NOT SET" ] - then printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menutuf}${NOct}]\n" - else printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menutuf}${NOct}]\n" - fi - fi - fi - else - if [ "$fwInstalledBaseVers" -le 3004 ] - then - current_build_type="$(Get_Custom_Setting "ROGBuild")" + # Convert the setting to a descriptive text + if [ "$current_build_type" = "ENABLED" ] + then + current_build_type_menu="TUF Build" + elif [ "$current_build_type" = "DISABLED" ] + then + current_build_type_menu="Pure Build" + else + current_build_type_menu="NOT SET" + fi + printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" + if [ "$current_build_type_menu" = "NOT SET" ] + then + printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menu}${NOct}]\n" + else + printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menu}${NOct}]\n" + fi + elif echo "$PRODUCT_ID" | grep -q "^GT-" + then + local current_build_typerog="$(Get_Custom_Setting "ROGBuild")" - # Convert the setting to a descriptive text - if [ "$current_build_type" = "y" ]; then - current_build_type_menu="ROG Build" - elif [ "$current_build_type" = "n" ]; then - current_build_type_menu="Pure Build" - else - current_build_type_menu="NOT SET" - fi + # Convert the setting to a descriptive text + if [ "$current_build_typerog" = "ENABLED" ] + then + current_build_type_menurog="ROG Build" + elif [ "$current_build_typerog" = "DISABLED" ] + then + current_build_type_menurog="Pure Build" + else + current_build_type_menurog="NOT SET" + fi + printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" + if [ "$current_build_type_menurog" = "NOT SET" ] + then + printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menurog}${NOct}]\n" + else + printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menurog}${NOct}]\n" + fi + fi + else + if echo "$PRODUCT_ID" | grep -q "^GT-" + then + local current_build_type="$(Get_Custom_Setting "ROGBuild")" - if echo "$PRODUCT_ID" | grep -q "^GT-" - then - printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" - if [ "$current_build_type_menu" = "NOT SET" ] - then printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menu}${NOct}]\n" - else printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menu}${NOct}]\n" - fi - fi - fi - fi + # Convert the setting to a descriptive text + if [ "$current_build_type" = "ENABLED" ] + then + current_build_type_menu="ROG Build" + elif [ "$current_build_type" = "DISABLED" ] + then + current_build_type_menu="Pure Build" + else + current_build_type_menu="NOT SET" + fi + printf "\n ${GRNct}bt${NOct}. Toggle F/W Build Type Preference" + if [ "$current_build_type_menu" = "NOT SET" ] + then + printf "\n${padStr}[Current Build Type: ${REDct}${current_build_type_menu}${NOct}]\n" + else + printf "\n${padStr}[Current Build Type: ${GRNct}${current_build_type_menu}${NOct}]\n" + fi + fi + fi # Additional Email Notification Options # + _WebUI_SetEmailConfigFileFromAMTM_ if _CheckEMailConfigFileFromAMTM_ 0 then # F/W Update Email Notifications # - if "$inRouterSWmode" + if "$mountWebGUI_OK" then printf "\n ${GRNct}em${NOct}. Toggle F/W Update Email Notifications" else printf "\n ${GRNct}em${NOct}. Toggle F/W Email Notifications" fi - if "$sendEMailNotificationsFlag" + if [ "$sendEMailNotificationsFlag" = "ENABLED" ] then printf "\n${padStr}[Currently ${GRNct}ENABLED${NOct}]\n" else printf "\n${padStr}[Currently ${REDct}DISABLED${NOct}]\n" fi - if "$sendEMailNotificationsFlag" + if [ "$sendEMailNotificationsFlag" = "ENABLED" ] then # Format Types: "HTML" or "Plain Text" # printf "\n ${GRNct}ef${NOct}. Set Email Format Type" @@ -8894,7 +10402,7 @@ _ShowAdvancedOptionsMenu_() _ShowNodesMenu_() { clear - logo + _ShowLogo_ printf "================ AiMesh Node(s) Info Menu ================\n" printf "${SEPstr}\n" @@ -8938,7 +10446,7 @@ _ShowNodesMenuOptions_() _ShowLogOptionsMenu_() { clear - logo + _ShowLogo_ printf "==================== Log Options Menu ====================\n" printf "${SEPstr}\n" @@ -8957,17 +10465,20 @@ _ShowLogOptionsMenu_() } ##----------------------------------------## -## Modified by Martinski W. [2024-May-04] ## +## Modified by Martinski W. [2025-Jan-15] ## ##----------------------------------------## _AdvancedLogsOptions_() { + local menuChoice="" + _SetUp_FW_UpdateLOG_DirectoryPaths_ + while true do _ShowLogOptionsMenu_ printf "\nEnter selection: " - read -r nodesChoice + read -r menuChoice echo - case $nodesChoice in + case "$menuChoice" in 1) _Set_FW_UpdateLOG_DirectoryPath_ ;; lg) if _CheckForUpdateLogFiles_ @@ -8995,17 +10506,20 @@ _AdvancedLogsOptions_() } ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Nov-18] ## +## Modified by ExtremeFiretop [2025-Jan-10] ## ##------------------------------------------## _AdvancedOptionsMenu_() { local theUserInputStr="" - local offlineUpdateFlag=false + local offlineUpdTrigger=false + local execReloadTrigger=false + while true do _ShowAdvancedOptionsMenu_ _GetKeypressInput_ "Enter selection:" echo + case "$theUserInputStr" in 1) _Set_FW_UpdateZIP_DirectoryPath_ ;; @@ -9024,11 +10538,7 @@ _AdvancedOptionsMenu_() ;; bt) if echo "$PRODUCT_ID" | grep -q "^TUF-" then _ChangeBuildType_TUF_ - elif [ "$fwInstalledBaseVers" -le 3004 ] && \ - echo "$PRODUCT_ID" | grep -q "^GT-" - then _ChangeBuildType_ROG_ - elif [ "$fwInstalledBaseVers" -ge 3006 ] && "$isGNUtonFW" && \ - echo "$PRODUCT_ID" | grep -q "^GT-" + elif echo "$PRODUCT_ID" | grep -q "^GT-" then _ChangeBuildType_ROG_ else _InvalidMenuSelection_ fi @@ -9039,25 +10549,33 @@ _AdvancedOptionsMenu_() fi ;; ef) if "$isEMailConfigEnabledInAMTM" && \ - "$sendEMailNotificationsFlag" + [ "$sendEMailNotificationsFlag" = "ENABLED" ] then _SetEMailFormatType_ else _InvalidMenuSelection_ fi ;; se) if "$isEMailConfigEnabledInAMTM" && \ - "$sendEMailNotificationsFlag" + [ "$sendEMailNotificationsFlag" = "ENABLED" ] then _SetSecondaryEMailAddress_ else _InvalidMenuSelection_ fi ;; - un) _DoUninstall_ && _WaitForEnterKey_ + un) _DoUnInstallation_ && _WaitForEnterKey_ ;; e|E|exit) break ;; - *) if [ -n "${offlineUpdateFlag:+OK}" ] && "$offlineUpdateFlag" + *) if "$offlineUpdTrigger" then _RunOfflineUpdateNow_ [ "$?" -eq 2 ] && _InvalidMenuSelection_ + ## + elif "$execReloadTrigger" + then + printf "Reloading configuration to refresh menu." + printf "\nPlease wait...\n" + _ReleaseLock_ + exec "$ScriptFilePath" reload advmenu + exit 0 else _InvalidMenuSelection_ fi @@ -9066,63 +10584,299 @@ _AdvancedOptionsMenu_() done } -##------------------------------------------## -## Modified by ExtremeFiretop [2024-May-25] ## -##------------------------------------------## -# Main Menu loop -inMenuMode=true -HIDE_ROUTER_SECTION=false -if ! node_list="$(_GetNodeIPv4List_)" -then node_list="" ; fi +##----------------------------------------## +## Modified by Martinski W. [2025-Jan-10] ## +##----------------------------------------## +_MainMenu_() +{ + local theUserInputStr="" jumpToAdvMenu + local execReloadTrigger=false -while true -do - # Check if the directory exists again before attempting to navigate to it - [ -d "$FW_BIN_DIR" ] && cd "$FW_BIN_DIR" + inMenuMode=true + HIDE_ROUTER_SECTION=false + if ! node_list="$(_GetNodeIPv4List_)" + then node_list="" ; fi - _ShowMainMenu_ - printf "Enter selection: " ; read -r userChoice - echo - case $userChoice in - s|S|h|H) - if [ "$userChoice" = "s" ] || [ "$userChoice" = "S" ]; then - HIDE_ROUTER_SECTION=false - elif [ "$userChoice" = "h" ] || [ "$userChoice" = "H" ]; then - HIDE_ROUTER_SECTION=true - fi - ;; - 1) _RunFirmwareUpdateNow_ - FlashStarted=false - ;; - 2) _GetLoginCredentials_ - ;; - 3) _Toggle_FW_UpdateCheckSetting_ - ;; - 4) _Set_FW_UpdatePostponementDays_ - ;; - 5) _toggle_change_log_check_ - ;; - 6) if [ "$ChangelogApproval" = "TBD" ] || [ -z "$ChangelogApproval" ] - then _InvalidMenuSelection_ - else _Approve_FW_Update_ - fi - ;; - up) _SCRIPTUPDATE_ - ;; - ad) _AdvancedOptionsMenu_ - ;; - mn) if "$inRouterSWmode" && [ -n "$node_list" ] - then _ShowNodesMenuOptions_ - else _InvalidMenuSelection_ - fi - ;; - lo) _AdvancedLogsOptions_ - ;; - e|E|exit) _DoExit_ 0 - ;; - *) _InvalidMenuSelection_ - ;; + if [ $# -gt 1 ] && \ + [ "$1" = "reload" ] && \ + [ "$2" = "advmenu" ] + then jumpToAdvMenu=true + else jumpToAdvMenu=false + fi + + while true + do + [ -d "$FW_BIN_DIR" ] && cd "$FW_BIN_DIR" + + if "$jumpToAdvMenu" + then + theUserInputStr=ad + jumpToAdvMenu=false + else + _ShowMainMenuOptions_ + _GetKeypressInput_ "Enter selection:" + echo + fi + + case "$theUserInputStr" in + s|S|h|H) + if [ "$theUserInputStr" = "s" ] || \ + [ "$theUserInputStr" = "S" ] + then + HIDE_ROUTER_SECTION=false + elif [ "$theUserInputStr" = "h" ] || \ + [ "$theUserInputStr" = "H" ] + then + HIDE_ROUTER_SECTION=true + fi + ;; + 1) if _AcquireLock_ cliFileLock + then + _RunFirmwareUpdateNow_ + _ReleaseLock_ cliFileLock + FlashStarted=false + fi + ;; + 2) _GetLoginCredentials_ + ;; + 3) _Toggle_FW_UpdateCheckSetting_ + ;; + 4) _Set_FW_UpdatePostponementDays_ + ;; + 5) _toggle_change_log_check_ + ;; + 6) if [ -z "$ChangelogApproval" ] || \ + [ "$ChangelogApproval" = "TBD" ] + then _InvalidMenuSelection_ + else _Approve_FW_Update_ + fi + ;; + up) if _AcquireLock_ cliFileLock + then + _SCRIPT_UPDATE_ + _ReleaseLock_ cliFileLock + fi + ;; + ad) _AdvancedOptionsMenu_ + ;; + mn) if "$aiMeshNodes_OK" && [ -n "$node_list" ] + then _ShowNodesMenuOptions_ + else _InvalidMenuSelection_ + fi + ;; + lo) _AdvancedLogsOptions_ + ;; + e|E|exit) _DoExit_ 0 + ;; + *) if "$execReloadTrigger" + then + printf "Reloading configuration to refresh menu." + printf "\nPlease wait...\n" + _ReleaseLock_ + exec "$ScriptFilePath" reload topmenu + exit 0 + else + _InvalidMenuSelection_ + fi + ;; + esac + done +} + +##-------------------------------------## +## Added by Martinski W. [2025-Jan-15] ## +##-------------------------------------## +_DoInitializationStartup_() +{ + if ! _CheckForMinimumRequirements_ + then + printf "\n${CRITct}Minimum requirements for $SCRIPT_NAME were not met. See the reason(s) above.${NOct}\n" + _DoExit_ 1 + fi + + if [ $# -gt 0 ] && [ -n "$1" ] && \ + echo "$1" | grep -qE "^(install|startup)$" + then return 1 ; fi + + _CreateDirPaths_ + _InitCustomSettingsConfig_ + _CreateSymLinks_ + _InitHelperJSFile_ + _SetVersionSharedSettings_ local "$SCRIPT_VERSION" + + if "$mountWebGUI_OK" + then + _AutoStartupHook_ create 2>/dev/null + _AutoServiceEvent_ create 2>/dev/null + fi + + _CheckAndSetBackupOption_ + _SetDefaultBuildType_ +} + +FW_InstalledVersion="$(_GetCurrentFWInstalledLongVersion_)" +FW_InstalledVerStr="${GRNct}${FW_InstalledVersion}${NOct}" +FW_NewUpdateVerInit=TBD + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-12] ## +##----------------------------------------## +if [ $# -eq 0 ] || [ -z "$1" ] || \ + { [ $# -gt 1 ] && [ "$1" = "reload" ] ; } +then + if ! _AcquireLock_ cliMenuLock + then Say "Exiting..." ; exit 1 ; fi + + inMenuMode=true + _DoInitializationStartup_ + if _AcquireLock_ cliFileLock + then + _CheckForNewScriptUpdates_ + _ReleaseLock_ cliFileLock + fi + if [ "$ScriptAutoUpdateSetting" = "ENABLED" ] + then _AddScriptAutoUpdateCronJob_ ; fi + _ConfirmCronJobForFWAutoUpdates_ + _CheckFor_WebGUI_Page_ + + _MainMenu_ "$@" + _DoExit_ 0 +fi + +##----------------------------------------## +## Modified by Martinski W. [2025-Feb-15] ## +##----------------------------------------## +if [ $# -gt 0 ] +then + if ! _AcquireLock_ cliOptsLock + then Say "Exiting..." ; exit 1 ; fi + + inMenuMode=false + _DoInitializationStartup_ "$1" + _ConfirmCronJobForFWAutoUpdates_ "$1" + + case "$1" in + run_now) + if _AcquireLock_ cliFileLock + then + _RunFirmwareUpdateNow_ + _ReleaseLock_ cliFileLock + fi + ;; + processNodes) _ProcessMeshNodes_ 0 + ;; + addCronJob) _AddFWAutoUpdateCronJob_ + ;; + scriptAUCronJob) _AddScriptAutoUpdateCronJob_ + ;; + postRebootRun) _PostRebootRunNow_ + ;; + postUpdateEmail) _PostUpdateEmailNotification_ + ;; + about) _ShowAbout_ + ;; + help) _ShowHelp_ + ;; + checkupdates) + if _AcquireLock_ cliFileLock + then + _CheckForNewScriptUpdates_ + _ReleaseLock_ cliFileLock + fi + ;; + forceupdate) + if _AcquireLock_ cliFileLock + then + _SCRIPT_UPDATE_ force + _ReleaseLock_ cliFileLock + fi + ;; + develop) _ChangeToDev_ + ;; + stable) _ChangeToStable_ + ;; + startup) _DoStartupInit_ + ;; + install) _DoInstallation_ + ;; + uninstall) _DoUnInstallation_ + ;; + service_event) + if [ "$2" = "start" ] + then + case "$3" in + "${SCRIPT_NAME}uninstall" | \ + "${SCRIPT_NAME}uninstall_keepConfig") + if [ "$3" = "${SCRIPT_NAME}uninstall_keepConfig" ] + then keepConfigFile=true + else keepConfigFile=false + fi + _DoUnInstallation_ + sleep 1 + ;; + "${SCRIPT_NAME}downloadchangelog") + if "$isGNUtonFW" + then + _ManageChangelogGnuton_ "webuidownload" + else + _ManageChangelogMerlin_ "webuidownload" + fi + ;; + "${SCRIPT_NAME}approvechangelog") + currApprovalStatus="$(Get_Custom_Setting "FW_New_Update_Changelog_Approval")" + if [ "$currApprovalStatus" = "BLOCKED" ] + then + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "APPROVED" + elif [ "$currApprovalStatus" = "APPROVED" ] + then + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "BLOCKED" + fi + ;; + "${SCRIPT_NAME}blockchangelog") + currApprovalStatus="$(Get_Custom_Setting "FW_New_Update_Changelog_Approval")" + if [ "$currApprovalStatus" = "APPROVED" ] + then + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "BLOCKED" + elif [ "$currApprovalStatus" = "BLOCKED" ] + then + Update_Custom_Settings "FW_New_Update_Changelog_Approval" "APPROVED" + fi + ;; + "${SCRIPT_NAME}checkupdate" | \ + "${SCRIPT_NAME}checkupdate_bypassDays") + if _AcquireLock_ cliFileLock + then + if [ "$3" = "${SCRIPT_NAME}checkupdate_bypassDays" ] + then bypassPostponedDays=true + else bypassPostponedDays=false + fi + _RunFirmwareUpdateNow_ + _ReleaseLock_ cliFileLock + fi + ;; + "${SCRIPT_NAME}config" | \ + "${SCRIPT_NAME}config_runLoginTest") + if _AcquireLock_ cliFileLock + then + if [ "$3" = "${SCRIPT_NAME}config_runLoginTest" ] + then runLoginCredentialsTest=true + else runLoginCredentialsTest=false + fi + _UpdateConfigFromWebUISettings_ + _ConfirmCronJobForFWAutoUpdates_ + _ReleaseLock_ cliFileLock + fi + ;; + *) + printf "${REDct}INVALID Parameters [$*].${NOct}\n" + ;; + esac + fi + ;; + *) printf "${REDct}INVALID Parameter [$*].${NOct}\n" + ;; esac -done + _DoExit_ 0 +fi #EOF# diff --git a/README.md b/README.md index 6a6d4075..f300d7d0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,19 @@ # MerlinAU - AsusWRT-Merlin Firmware Auto Updater -## v1.3.12 -## 2025-March-28 +## v1.4.0 +## 2025-Apr-06 -![image](https://github.com/user-attachments/assets/2eebe686-364a-4fb1-9493-e6b91c23b9fd) -![image](https://github.com/user-attachments/assets/5ca4587f-5b6e-45a6-9c2d-a889b91f8368) -![image](https://github.com/user-attachments/assets/959d7bb6-20b4-4e97-ab3e-b7b90cc08744) -![image](https://github.com/user-attachments/assets/2e92af06-a6b1-4573-9a89-6986e739b514) +## WebUI: +![image](https://github.com/user-attachments/assets/92701007-a902-4724-9bae-b255856a686a) + +## CLI Shell Script: +![image](https://github.com/user-attachments/assets/5c6460c1-d3c2-4374-a18a-14270dd578c6) +![image](https://github.com/user-attachments/assets/d05ec713-bacb-411b-903f-31bd1f04f49d) +![image](https://github.com/user-attachments/assets/8119aee0-e8ac-4bfb-a371-dfd74433ec5a) +![image](https://github.com/user-attachments/assets/33ef2735-acff-48c4-ab59-37622f2b8595) + +## Update Process (Triggered Manually): +![image](https://github.com/user-attachments/assets/62d60c39-9bb0-40db-bb80-dda3de08c508) +![image](https://github.com/user-attachments/assets/5661b0eb-0305-46c3-a474-0674719f41c0) ## SUPPORTED MERLIN MODELS (Multi-image models) - i.e. Any model that uses a .w or a .pkgtb file diff --git a/version.txt b/version.txt index 90a7f602..9c0eb14b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1,2 @@ -1.3.12 +1.4.0 +25040600