<?php
/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Normalize an URL to remove dublicates.
 * No relative URLs supported.
 * Some techniques from http://en.wikipedia.org/wiki/URL_normalization are used.
 * 
 * @author Mario Volke 
 * @copyright Copyright (c) 2009, Mario Volke
 * @link http://www.webholics.de/2009/02/12/urls-richtig-normalisieren/
 * @version 1.0      
 *
 * @param string $url The URL to normalize.
 * @return mixed The normalized URL or false if URL is not correct.
 */
function normalizeUrl($url) {
    
// we assume the $uri is validated and correct!
    
    // try to split URL into its components
    
try {
        
$comp parse_url($url);
    }
    catch(
Exception $e) {
        return 
false;
    }

    
// converting scheme and host to lower case
    // those components are required, return false if not set
    
if(isset($comp['scheme'])) {
        
$comp['scheme'] = strtolower($comp['scheme']);
    }
    else {
        return 
false;
    }
    
    if(isset(
$comp['host'])) {
        
$comp['host'] = strtolower($comp['host']);
    }
    else {
        return 
false;
    }

    
// add trailing slash if not set
    
if(!isset($comp['path'])) {
        
$comp['path'] = '/';
    }
    else {
        
// remove dots as described in
        // http://tools.ietf.org/html/rfc3986#section-5.2.4
        
$ibuffer $comp['path'];
        
$obuffer '';
        while(!empty(
$ibuffer)) {
            if(
substr($ibuffer02) == './') {
                
$ibuffer substr($ibuffer2);
            }
            else if(
substr($ibuffer03) == '../') {
                
$ibuffer substr($ibuffer3);
            }
            else if(
substr($ibuffer03) == '/./') {
                
$ibuffer '/' substr($ibuffer3);
            }
            else if(
$ibuffer == '/.') {
                
$ibuffer '/';
            }
            else if(
substr($ibuffer04) == '/../') {
                
$ibuffer '/' substr($ibuffer4);
                
$pos strrpos($obuffer'/');
                if(
$pos !== false) {
                    
$obuffer substr($obuffer0$pos);
                }
            }
            else if(
$ibuffer == '/..') {
                
$ibuffer '/';
                
$pos strrpos($obuffer'/');
                if(
$pos !== false) {
                    
$obuffer substr($obuffer0$pos);
                }
            }
            else if(
$ibuffer == '.' || $ibuffer == '..') {
                
$ibuffer '';
            }
            else {
                if(
$ibuffer[0] == '/') {
                    
$ibuffer substr($ibuffer1);
                    
$obuffer .= '/';
                }
                
$pos strpos($ibuffer'/');
                if(
$pos !== false) {
                    
$obuffer .= substr($ibuffer0$pos);
                    
$ibuffer substr($ibuffer$pos);
                }
                else {
                    
$obuffer .= $ibuffer;
                    
$ibuffer '';
                }
            }
        }

        
// if last path segment is a directory and not a file
        // then add a trailing slash
        
$seg explode('/'$obuffer);
        
$last_seg $seg[sizeof($seg)-1];
        if(!empty(
$last_seg) && strpos($last_seg'.') === false) {
            
$obuffer .= '/';
        }

        
// capitalize escape sequences
        
for($i 0$i sizeof($obuffer); $i++) {
            if(
$obuffer[$i] == '%') {
                
$obuffer[$i+1] = strtoupper($obuffer[$i+1]);
                
$obuffer[$i+2] = strtoupper($obuffer[$i+2]);
                
$i += 3;
            }
        }

        
// remove directory index
        
$seg explode('/'$obuffer);
        
$last_seg $seg[sizeof($seg)-1];
        
$pos strrpos($last_seg'.');
        if(
$pos !== false && substr($last_seg0$pos) == 'index') {
            
$pos strrpos($obuffer'/');
            if(
$pos === false) {
                
$obuffer '/';
            }
            else {
                
$obuffer substr($obuffer0$pos+1);
            }
        }    

        
$comp['path'] = $obuffer;
    }

    if(isset(
$comp['query']) && !empty($comp['query'])) {
        
$pairs explode('&'$comp['query']);
        
$seg = array();
        foreach(
$pairs as $pair) {
            
$split explode('='$pair);

            
// try to remove session variables
            
if(strtolower($split[0]) != 'phpsessid') {
                
$seg[] = $pair;
            }
        }
        
        
// sort query variables in ascending order
        
sort($seg);

        
// rebuild query string
        
$comp['query'] = '';
        
$first true;
        foreach(
$seg as $s) {
            if(
$first) {
                
$first false;
            }
            else {
                
$comp['query'] .= '&';
            }
            
$comp['query'] .= $s;
        }
    }

    
// rebuild URL ignoring fragment (#), user and password
    
$url $comp['scheme'] . '://' $comp['host'];
    
    
// ignore default port
    
if(isset($comp['port']) && 
        ((
$comp['scheme'] == 'http' && $comp['port'] != 80) || 
            (
$comp['scheme'] == 'https' && $comp['port'] != 443))) {
        
$url .= ':' $comp['port'];
    }
    
    
$url .= $comp['path'];

    if(isset(
$comp['query']) && !empty($comp['query'])) {
        
$url .= '?' $comp['query'];
    }

    return 
$url;
}