Index: /trunk/lib/Utilities.inc.php
===================================================================
--- /trunk/lib/Utilities.inc.php	(revision 807)
+++ /trunk/lib/Utilities.inc.php	(revision 808)
@@ -499,4 +499,5 @@
  * @param   int     $len    Maximum string length.
  * @param   string  $where  Where to cut the string. One of: 'start', 'middle', or 'end'.
+ * @param   string  $delim  The delimiter to print where content is truncated.
  * @return  string          Truncated output string.
  * @author  Quinn Comendant <quinn@strangecode.com>
@@ -1481,6 +1482,6 @@
 * Returns the remote IP address, taking into consideration proxy servers.
 *
-* If strict checking is enabled, we will only trust REMOTE_ADDR or an HTTP header
-* value if REMOTE_ADDR is a trusted proxy (configured as an array in $cfg['trusted_proxies']).
+* If strict checking is enabled, we will only trust REMOTE_ADDR or an HTTP header value if
+* REMOTE_ADDR is a trusted proxy (configured as an array via $app->setParam(['trusted_proxies' => ['1.2.3.4', '5.6.7.8']]).
 *
 * @access   public
@@ -1496,5 +1497,5 @@
 function getRemoteAddr($dolookup=false, $trust_all_proxies=true)
 {
-    global $cfg;
+    $app =& App::getInstance();
 
     if (!isset($_SERVER['REMOTE_ADDR'])) {
@@ -1503,7 +1504,8 @@
     }
 
-    // Use an HTTP header value only if $trust_all_proxies is true or when REMOTE_ADDR is in our $cfg['trusted_proxies'] array.
-    // $cfg['trusted_proxies'] is an array of proxy server addresses we expect to see in REMOTE_ADDR.
-    if ($trust_all_proxies || isset($cfg['trusted_proxies']) && is_array($cfg['trusted_proxies']) && in_array($_SERVER['REMOTE_ADDR'], $cfg['trusted_proxies'], true)) {
+    // Use an HTTP header value only if $trust_all_proxies is true or when REMOTE_ADDR is in our $trusted_proxies array.
+    // $trusted_proxies is an array of proxy server addresses we expect to see in REMOTE_ADDR.
+    $trusted_proxies = $app->getParam('trusted_proxies', []);
+    if ($trust_all_proxies || is_array($trusted_proxies) && in_array($_SERVER['REMOTE_ADDR'], $trusted_proxies, true)) {
         // Then it's probably safe to use an IP address value set in an HTTP header.
         // Loop through possible IP address headers from those most likely to contain the correct value first.
@@ -1512,11 +1514,11 @@
         // HTTP_CF_CONNECTING_IP: set by Cloudflare proxy
         // HTTP_X_FORWARDED_FOR: defacto standard for web proxies
-        foreach (array('HTTP_CLIENT_IP', 'HTTP_REAL_IP', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED') as $key) {
-            // Loop through and if
-            if (array_key_exists($key, $_SERVER)) {
+        foreach (['HTTP_CLIENT_IP', 'HTTP_REAL_IP', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED'] as $key) {
+            if (isset($_SERVER[$key]) && '' != $_SERVER[$key]) {
                 foreach (explode(',', $_SERVER[$key]) as $addr) {
                     // Strip non-address data to avoid "PHP Warning:  inet_pton(): Unrecognized address for=189.211.197.173 in ./Utilities.inc.php on line 1293"
                     $addr = preg_replace('/[^=]=/', '', $addr);
                     $addr = canonicalIPAddr(trim($addr));
+                    // Exclude invalid, private, or reserved IP addresses (a proxy server may be using a private IP).
                     if (false !== filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                         return $dolookup && '' != $addr ? gethostbyaddr($addr) : $addr;
@@ -1528,5 +1530,5 @@
 
     $addr = canonicalIPAddr(trim($_SERVER['REMOTE_ADDR']));
-    if (false !== filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+    if (false !== filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_IPV4)) {
         return $dolookup && '' != $addr ? gethostbyaddr($addr) : $addr;
     }
@@ -1671,8 +1673,46 @@
         (isset($u['scheme'])    && '' != $u['scheme']   ? $u['scheme'] . '://' : ''),
         (isset($u['host'])      && '' != $u['host']     ? $u['host']           : ''),
-        (isset($u['path'])      && '' != $u['path']     ? $u['path']           : '/'),
+        (isset($u['path'])      && '' != $u['path']     ? $u['path']           : ''),
         (isset($u['query'])     && '' != $u['query']    ? '?' . $u['query']    : ''),
         (isset($u['fragment'])  && '' != $u['fragment'] ? '#' . $u['fragment'] : '')
     );
+}
+
+/*
+* Strip tracking query parameters from a URL.
+*
+* @access   public
+* @param string $url                URL which may contain query parameters.
+* @param mixed  $tracking_params    An array of tracking parameters to remove, or null to use a default set.
+* @return string The URL with query params removed.
+* @author   Quinn Comendant <quinn@strangecode.com>
+* @since    02 Mar 2024 16:11:27
+*/
+function removeURLTrackingParameters($url, $tracking_params=null)
+{
+    // Use a default set of tracking params if not specified.
+    $tracking_params = isset($tracking_params) ? $tracking_params : [
+        'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id', 'utm_source_platform', 'utm_marketing_tactic', 'utm_creative_format',
+        'gclid', 'gbraid', 'wbraid', 'dclid', 'fbclid', 'msclkid', 'awc', 'pclk', 'mc_eid', 'twclid', 'igshid',
+    ];
+
+    $u = parse_url($url);
+    if (isset($u['query']) && '' != $u['query']) {
+        parse_str($u['query'], $params);
+        foreach ($tracking_params as $p) {
+            unset($params[$p]);
+        }
+        $u['query'] = http_build_query($params);
+
+        return sprintf('%s%s%s%s%s',
+            (isset($u['scheme'])    && '' != $u['scheme']   ? $u['scheme'] . '://' : ''),
+            (isset($u['host'])      && '' != $u['host']     ? $u['host']           : ''),
+            (isset($u['path'])      && '' != $u['path']     ? $u['path']           : ''),
+            (isset($u['query'])     && '' != $u['query']    ? '?' . $u['query']    : ''),
+            (isset($u['fragment'])  && '' != $u['fragment'] ? '#' . $u['fragment'] : '')
+        );
+    }
+
+    return $url;
 }
 
