Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 123 additions & 34 deletions plugins/ca.mover.tuning.plg
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,26 @@
echo "********************************************************************\n";
exit(1);
}

// NEW: Breaking-changes notice for Unraid >= 7.2.1
if ( version_compare($version['version'], "7.2.1", ">=") )
{
echo "=============================================================\n";
echo " IMPORTANT: Starting with Unraid 7.2.1, Mover Tuning behavior\n";
echo " has changed:\n";
echo " ------------------------------------------------------------\n";
echo " • The plugin is now separated from the built-in Unraid mover.\n";
echo " • “Move Now” in Mover Settings runs the built-in Unraid mover.\n";
echo " • “Move” on the Main page also runs the Unraid mover.\n";
echo " • “Move Now” in Mover Tuning → Options runs age_mover.\n";
echo " • To disable the built-in mover schedule, set it to Disabled\n";
echo " in Mover Settings.\n";
echo " • Use the “Mover Tuning Schedule” (Plugin Settings) to\n";
echo " schedule Mover Tuning instead.\n";
echo " • The Unraid mover can still be triggered through the\n";
echo " “Force move all files on a schedule” option.\n";
echo "=============================================================\n\n";
}
?>
]]>
</INLINE>
Expand Down Expand Up @@ -414,45 +434,15 @@ The 'post-install' script
<FILE Run="/bin/bash">
<INLINE>
echo ""
if [[ -e /usr/local/sbin/mover ]]; then
if [[ ! -f /usr/local/sbin/mover.old ]]; then
echo "Backing up current /usr/local/sbin/mover script"
mv /usr/local/sbin/mover /usr/local/sbin/mover.old
fi
elif [[ -e /usr/local/bin/mover ]]; then
if [[ ! -f /usr/local/bin/mover.old ]]; then
echo "Backing up current /usr/local/bin/mover script"
mv /usr/local/bin/mover /usr/local/bin/mover.old
fi
fi
echo "Installing tuned mover script"
cp /usr/local/emhttp/plugins/&name;/mover /usr/local/sbin/mover

# Cleaning eventual leftovers
if ! pgrep age_mover; then
echo "Cleaning lock and stop files"
for file in mover.pid moversoft.stop; do
if [[ -e "/var/run/$file" ]]; then rm -f "/var/run/$file"; fi
done
fi

# Renaming mover.cron.disabled to mover.cron if exists (from old cron fix)
if [[ -e /boot/config/plugins/dynamix/mover.cron.disabled ]]; then mv -f /boot/config/plugins/dynamix/mover.cron.disabled /boot/config/plugins/dynamix/mover.cron; fi

echo "Fixing permissions"
chmod +x /usr/local/sbin/mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/*.php
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/age_mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/share_mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/debug_mover

# Config file
config_file=/boot/config/plugins/&name;/&name;.cfg;

if [[ $(cat $config_file | wc -l) -eq 1 ]]; then
rm $config_file
# Remove legacy single-line configs if present
if [[ -f "$config_file" &amp;&amp; $(wc -l &lt; "$config_file") -eq 1 ]]; then
rm "$config_file"
fi

if [[ ! -f "$config_file" ]]; then
echo "Creating file $config_file with default configuration."
# Create the file with some default values
Expand Down Expand Up @@ -493,6 +483,105 @@ else
fi
fi

# Read Unraid version
UNRAID_VERSION=$(grep '^version=' /etc/unraid-version | cut -d'"' -f2)

# Safe version compare function (no symbols that break XML)
version_ge() {
[ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
}

Comment on lines +486 to +493
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add validation for empty version string.

The version extraction from /etc/unraid-version could potentially result in an empty UNRAID_VERSION if the file format is unexpected or missing. This would cause version_ge to produce incorrect results and the subsequent conditional logic to fail silently.

Consider adding validation:

 # Read Unraid version
 UNRAID_VERSION=$(grep '^version=' /etc/unraid-version | cut -d'"' -f2)
+
+if [[ -z "$UNRAID_VERSION" ]]; then
+  echo "ERROR: Could not detect Unraid version. Defaulting to legacy behavior."
+  UNRAID_VERSION="6.0.0"
+fi
 
 # Safe version compare function (no symbols that break XML)
 version_ge() {
   [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
 }
🤖 Prompt for AI Agents
In plugins/ca.mover.tuning.plg around lines 486-493, the extracted
UNRAID_VERSION may be empty which can make version_ge produce wrong results;
after UNRAID_VERSION=$(grep '^version=' /etc/unraid-version | cut -d'"' -f2) add
a validation block that checks if UNRAID_VERSION is empty (e.g. [ -z
"$UNRAID_VERSION" ]), and if so log an error to stderr or the plugin log and
exit non-zero (or set a safe fallback version like "0.0.0" if you prefer), and
also harden version_ge to return false if either argument is empty (e.g. check [
-z "$1" ] || [ -z "$2" ] && return 1) before performing the sort-based
comparison.

echo "Unraid version detected: $UNRAID_VERSION"

# Backup built-in mover ONLY on older Unraid
if version_ge "$UNRAID_VERSION" "7.2.1"; then
echo "Unraid 7.2.1 or newer — skipping mover script backup."

TUNING_CRON_FILE="/boot/config/plugins/&name;/mover.tuning.cron"
DYNAMIX_CRON_FILE="/boot/config/plugins/dynamix/mover.cron"
DYNAMIX_CRON_OLD="/boot/config/plugins/dynamix/mover.cron.old"

# Only perform migration if mover.tuning.cron does NOT exist
# AND old dynamix/mover.cron exists
if [[ ! -f "$TUNING_CRON_FILE" &amp;&amp; -f "$DYNAMIX_CRON_FILE" ]]; then
echo "Detected old Unraid mover schedule — migrating into Mover Tuning."

# Read value from var.ini
SHARE_MOV_SCHED=$(grep '^shareMoverSchedule=' /var/local/emhttp/var.ini | cut -d'"' -f2)

if [[ -n "$SHARE_MOV_SCHED" ]]; then
echo "Found shareMoverSchedule: $SHARE_MOV_SCHED"

# Add moverTuneCron only if missing
if [[ -f "$config_file" ]] &amp;&amp; grep -q '^moverTuneCron=' "$config_file"; then
echo "moverTuneCron already exists — skipping update."
else
# Only append if file exists
echo "Setting moverTuneCron to: $SHARE_MOV_SCHED"
if [[ -f "$config_file" ]]; then
echo "moverTuneCron=\"$SHARE_MOV_SCHED\"" >> "$config_file"
else
echo "Config file not found."
fi
fi

# Create cron file for Mover Tuning
echo "Creating Mover Tuning cron file: $TUNING_CRON_FILE"
{
echo "# Generated schedule for Mover Tuning move:"
echo "$SHARE_MOV_SCHED /usr/local/emhttp/plugins/ca.mover.tuning/age_mover start |&amp; logger -t move"
} > "$TUNING_CRON_FILE"
Comment on lines +528 to +533
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bash-specific syntax in cron command may fail.

The cron command on line 532 uses |& (bash-specific pipe operator for both stdout and stderr), but cron typically executes commands using /bin/sh, not bash. This could cause the cron job to fail silently.

Apply this diff to use POSIX-compatible syntax:

             echo "Creating Mover Tuning cron file: $TUNING_CRON_FILE"
             {
                 echo "# Generated schedule for Mover Tuning move:"
-                echo "$SHARE_MOV_SCHED /usr/local/emhttp/plugins/ca.mover.tuning/age_mover start |&amp; logger -t move"
+                echo "$SHARE_MOV_SCHED /usr/local/emhttp/plugins/ca.mover.tuning/age_mover start 2>&1 | logger -t move"
             } > "$TUNING_CRON_FILE"

Note: 2>&1 | is POSIX-compatible and works in both sh and bash.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Create cron file for Mover Tuning
echo "Creating Mover Tuning cron file: $TUNING_CRON_FILE"
{
echo "# Generated schedule for Mover Tuning move:"
echo "$SHARE_MOV_SCHED /usr/local/emhttp/plugins/ca.mover.tuning/age_mover start |&amp; logger -t move"
} > "$TUNING_CRON_FILE"
# Create cron file for Mover Tuning
echo "Creating Mover Tuning cron file: $TUNING_CRON_FILE"
{
echo "# Generated schedule for Mover Tuning move:"
echo "$SHARE_MOV_SCHED /usr/local/emhttp/plugins/ca.mover.tuning/age_mover start 2>&1 | logger -t move"
} > "$TUNING_CRON_FILE"
🤖 Prompt for AI Agents
In plugins/ca.mover.tuning.plg around lines 528 to 533, the cron entry uses the
bash-only pipe operator `|&` which will fail under /bin/sh used by cron; replace
the `|&` with a POSIX-compatible redirection by redirecting stderr to stdout
before the pipe (i.e. change to `2>&1 |`) so the cron command will pipe both
stdout and stderr into logger reliably under sh; preserve existing
quoting/spacing and write the updated line back into the generated cron file.


else
echo "shareMoverSchedule is empty — nothing to migrate."
fi

# Rename old dynamix cron file
echo "Renaming old schedule: mover.cron → mover.cron.old"
mv "$DYNAMIX_CRON_FILE" "$DYNAMIX_CRON_OLD"
fi

else
echo "Unraid version below 7.2.1 — performing mover script backup."

if [[ -e /usr/local/sbin/mover ]]; then
if [[ ! -f /usr/local/sbin/mover.old ]]; then
echo "Backing up /usr/local/sbin/mover"
mv /usr/local/sbin/mover /usr/local/sbin/mover.old
fi

elif [[ -e /usr/local/bin/mover ]]; then
if [[ ! -f /usr/local/bin/mover.old ]]; then
echo "Backing up /usr/local/bin/mover"
mv /usr/local/bin/mover /usr/local/bin/mover.old
fi
fi

echo "Installing tuned mover script"
cp /usr/local/emhttp/plugins/&name;/mover /usr/local/sbin/mover

echo "Fixing permissions for mover"
chmod +x /usr/local/sbin/mover
fi

# Cleaning eventual leftovers
if ! pgrep age_mover; then
echo "Cleaning lock and stop files"
for file in mover.pid moversoft.stop; do
if [[ -e "/var/run/$file" ]]; then rm -f "/var/run/$file"; fi
done
fi

# Renaming mover.cron.disabled to mover.cron if exists (from old cron fix)
if [[ -e /boot/config/plugins/dynamix/mover.cron.disabled ]]; then mv -f /boot/config/plugins/dynamix/mover.cron.disabled /boot/config/plugins/dynamix/mover.cron; fi

echo "Fixing permissions for ca.mover.tuning files"
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/*.php
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/age_mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/share_mover
chmod +x /usr/local/emhttp/plugins/ca.mover.tuning/debug_mover

echo ""
echo "----------------------------------------------------"
echo " &name; has been installed."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if ($var['shareCacheEnabled']!='yes') {
} elseif ($var['shareUser']=='-') {
echo "<p class='notice'>User shares not enabled!</p>";
}
$vars = @parse_ini_file("/var/local/emhttp/var.ini") ?: [];
$plugin = 'ca.mover.tuning';
$config_file = '/boot/config/plugins/' . $plugin . '/' . $plugin . '.cfg';
$config_default_file = '/usr/local/emhttp/plugins/' . $plugin . '/default.cfg';
Expand All @@ -30,6 +31,8 @@ $sizefDisabled = ($cfg['sizef'] != "yes") ? "disabled" : "";
$sizefSyncDisabled = ($cfg['sizefSync'] != "yes") ? "disabled" : "";
$sparsnessfDisabled = ($cfg['sparsnessf'] != "yes") ? "disabled" : "";
$version = ($cfg['version']);
$moverRunning = file_exists('/var/run/mover.pid');
$showMoverButton = version_compare($vars['version'] ?? '0.0.0', '7.2.1', '>=');

?>

Expand All @@ -49,15 +52,20 @@ $.fn.toggleAttr = function(attr) {

function updateCron() {
var cron = $("#cronSchedule").val();
var tune_cron = $("#tune_cronSchedule").val();
var cronEnabled = $("#forceCron").val();
var ismoverDisabled = $("#moverDisabled").val();
$.post("/plugins/ca.mover.tuning/updateCron.php",{cronEnabled:cronEnabled,cron:cron,ismoverDisabled:ismoverDisabled});
$.post("/plugins/ca.mover.tuning/updateCron.php",{cronEnabled:cronEnabled,cron:cron,tune_cron:tune_cron,ismoverDisabled:ismoverDisabled});
}

function moveNow() {
$.post("/plugins/ca.mover.tuning/mover.php crond start");
}

function startTuneMover() {
$.post("/plugins/ca.mover.tuning/mover.php", { cmdStartTuneMover: 1 });
}
Comment on lines +65 to +67
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

location.reload() may fire before POST completes.

The startTuneMover() function triggers an async POST but location.reload() is called synchronously in the onclick handler (line 787). This could reload the page before the server processes the request.

Consider using a callback or promise to reload after the POST completes:

 function startTuneMover() {
-$.post("/plugins/ca.mover.tuning/mover.php", { cmdStartTuneMover: 1 });
+$.post("/plugins/ca.mover.tuning/mover.php", { cmdStartTuneMover: 1 }, function() {
+    location.reload();
+});
 }

Then update line 787:

-<input type="button" name="StartTuneMover" value="_(Move now)_" onclick="startTuneMover(); location.reload()" ...>
+<input type="button" name="StartTuneMover" value="_(Move now)_" onclick="startTuneMover()" ...>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function startTuneMover() {
$.post("/plugins/ca.mover.tuning/mover.php", { cmdStartTuneMover: 1 });
}
function startTuneMover() {
$.post("/plugins/ca.mover.tuning/mover.php", { cmdStartTuneMover: 1 }, function() {
location.reload();
});
}
🤖 Prompt for AI Agents
In
source/ca.mover.tuning/usr/local/emhttp/plugins/ca.mover.tuning/Mover.tuning.page
around lines 65-67 (and update the onclick at line 787), the startTuneMover()
function issues an async POST but the page is reloaded synchronously in the
onclick handler; change startTuneMover() to wait for the POST to complete and
then reload the page (use the $.post promise callback or $.ajax success/done to
call location.reload()), and remove the immediate synchronous location.reload()
from the onclick so the reload happens only after the POST finishes.


function resetDefaults() {
$.post("/plugins/ca.mover.tuning/reset.php");
}
Expand Down Expand Up @@ -126,6 +134,66 @@ $(function() {
});
</script>

<!-- Condition A: Unraid native Mover schedule enabled -->
<span id="mover_schedule_warning" class="orange-text"
style="display:<?= (!empty($vars['shareMoverSchedule']) && version_compare($vars['version'], '7.2.1', '>=')) ? 'block' : 'none' ?>; font-style:italic;">
<i class="fa fa-warning"></i>
<?= _('Unraid Mover schedule is currently') ?> <b>enabled</b>
(<?= htmlspecialchars($vars['shareMoverSchedule']) ?>). &nbsp;
<?= _('If you want Mover Tuning to fully manage scheduling, disable it in Mover Settings.') ?>
</span>

<!-- Breaking changes notice for Unraid 7.2.1+ -->
<div id="mover_tuning_breaking_notice"
style="display:<?= (version_compare($vars['version'], '7.2.1', '>=')) ? 'block' : 'none' ?>;
position:relative;
background-color: var(--title-header-background-color);
border: 1px solid var(--border-color);
border-radius:10px;
box-shadow: 0.05em 0.2em 0.6em var(--dynamix-awesomplete-list-shadow-color);
padding:15px 40px 15px 15px;
margin-bottom:10px;
color:var(--ui-info);
font-size:1.5rem;
line-height:1.5em;">
<i class="fa fa-info-circle" style="margin-right:5px;"></i>
<?= _('**Important: &nbsp;*Starting with Unraid 7.2.1, Mover Tuning plugin behavior has changed:***') ?>
<br><br>
<?= _('• The plugin is now separated from the built-in Unraid mover.') ?>
<br>
<?= _('• **“Move Now”** in the main Mover Settings runs the built-in Unraid mover.') ?>
<br>
<?= _('• **“Move”** on the Main page also runs the built-in Unraid mover.') ?>
<br>
<?= _('• **“Move Now”** in Mover Tuning → Options runs the plugin age_mover (Mover Tuning).') ?>
<br>
<?= _('• To disable the built-in Unraid mover schedule, set **“Mover schedule”** in Mover Settings to **Disabled**.') ?>
<br>
<?= _('• Use the **“Mover Tuning Schedule”** in the plugin settings to schedule **Mover Tuning** instead.') ?>
<br>
<?= _('• The built-in Unraid mover can still be triggered using **“Force move all files on a schedule”** (Cron schedule) in Plugin Settings if needed.') ?>

<!-- Close button -->
<span id="close_mover_notice"
class="fa fa-times"
style="position:absolute; top:5px; right:10px; cursor:pointer; font-size:1.3rem;"
title="Close"></span>
</div>

<script>
document.getElementById('close_mover_notice').addEventListener('click', function() {
var notice = document.getElementById('mover_tuning_breaking_notice');
notice.style.display = 'none';
localStorage.setItem('mover_notice_closed', 'yes');
});

// Hide automatically if previously closed
if (localStorage.getItem('mover_notice_closed') === 'yes') {
var notice = document.getElementById('mover_tuning_breaking_notice');
notice.style.display = 'none';
}
</script>

<div class="title">
<span class="left"><i class="fa fa-cog title"></i>Mover Tuning - Plugin Settings</span>
<span class="right">Version: <?=$version?></span>
Expand All @@ -147,6 +215,16 @@ _(Disable Mover running on a schedule)_:
:end

<div markdown="1" id="moverTuningSettings">
<?php if (version_compare($vars['version'], '7.2.1', '>=')): ?>
_(Mover Tuning schedule)_:
: <input type='text' id='tune_cronSchedule' name='moverTuneCron' size='1' class='tune_mycron' placeholder='0 */4 * * *' value='<?=htmlspecialchars($cfg['moverTuneCron'])?>'>

<blockquote class="inline_help">
<p>Runs the <strong><code>age_mover</code></strong> schedule from the Mover Tuning plugin using your custom cron entry (includes all plugin filters).</p>
<p> Cron Schedule entry example <strong>0 */4 * * *</strong>.&nbsp; To run at <em>every</em><strong> 4 hours</strong>.&nbsp; <a href="https://crontab.guru/" target="_blank" rel="noopener noreferrer"><i class="fa fa-clock-o"></i> What Is Cron</a></p>
</blockquote>
<?php endif; ?>

_(Test Mode (dry run))_:
: <select name='testmode' size='1' id='testmode' onchange="toggleTestModeWarning(this.value)">
<?=mk_option($cfg['testmode'],"no",_('No'))?>
Expand Down Expand Up @@ -704,6 +782,10 @@ _(Resynchronize all Primary files to Secondary)_:
<input type="submit" name="#apply" value="_(Apply)_" id='Apply'>
<input type="button" id="DONE" value="_(Done)_" onclick="done()">
<input type="button" id="Defaults" value="_(Defaults)_" onclick="resetDefaults(); location.reload()">
<?if ($showMoverButton):?>
<input type="button" name="StartTuneMover" value="_(Move now)_" onclick="startTuneMover(); location.reload()" title="Run Mover Tuning ..."<?if ($moverRunning):?> disabled<?endif;?>>
<?if ($moverRunning):?><span>_(Mover is running)_</span><?endif;?>
<?endif;?>
</span>

</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ sparsnessv="1"
filetypesv=""
omoverthresh=""
cron=""
moverTuneCron=""
forceParity="no"
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#!/usr/bin/php
<?PHP
exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
$vars = @parse_ini_file("/var/local/emhttp/var.ini");
if (version_compare($vars['version'], '7.2.1', '<')) {
exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
} else {
exec("/usr/local/sbin/mover start >> /var/log/syslog &", $output, $retval);
}
Comment on lines +3 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add null check for parse_ini_file failure.

If /var/local/emhttp/var.ini is missing or unreadable, $vars will be false, and accessing $vars['version'] will trigger a PHP warning. While version_compare(null, '7.2.1', '<') happens to return true (falling back to mover.old), this should be explicit.

 $vars = @parse_ini_file("/var/local/emhttp/var.ini");
+if (!$vars || !isset($vars['version'])) {
+    exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
+    exit;
+}
 if (version_compare($vars['version'], '7.2.1', '<')) {
     exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
 } else {
     exec("/usr/local/sbin/mover start >> /var/log/syslog &", $output, $retval);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$vars = @parse_ini_file("/var/local/emhttp/var.ini");
if (version_compare($vars['version'], '7.2.1', '<')) {
exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
} else {
exec("/usr/local/sbin/mover start >> /var/log/syslog &", $output, $retval);
}
$vars = @parse_ini_file("/var/local/emhttp/var.ini");
if (!$vars || !isset($vars['version'])) {
exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
exit;
}
if (version_compare($vars['version'], '7.2.1', '<')) {
exec("/usr/local/sbin/mover.old start >> /var/log/syslog &", $output, $retval);
} else {
exec("/usr/local/sbin/mover start >> /var/log/syslog &", $output, $retval);
}
🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[warning] 3-3: source/ca.mover.tuning/usr/local/emhttp/plugins/ca.mover.tuning/moveNow.php#L3
The use of function parse_ini_file() is discouraged


[warning] 4-4: source/ca.mover.tuning/usr/local/emhttp/plugins/ca.mover.tuning/moveNow.php#L4
Implicit true comparisons prohibited; use === TRUE instead


[failure] 7-7: source/ca.mover.tuning/usr/local/emhttp/plugins/ca.mover.tuning/moveNow.php#L7
System program execution function exec() detected with dynamic parameter

🤖 Prompt for AI Agents
In source/ca.mover.tuning/usr/local/emhttp/plugins/ca.mover.tuning/moveNow.php
around lines 3 to 8, add an explicit null/false check after parse_ini_file to
handle failure: test that $vars is an array (or not false) and that a 'version'
key exists, then set a safe default (e.g. null or '0.0.0') or log an error; use
that vetted value in version_compare so you don't index into a non-array and so
behavior is explicit (fallback to using mover.old when version is missing or
below 7.2.1). Ensure any error logging or default choice is minimal and keeps
the same exec calls unchanged.

?>
Loading