private function _getScheme()
 {
     if ($schemeHeader = $this->_getSchemeHeader()) {
         return $schemeHeader;
     } else {
         $requestUrl = new Url($this->_server['REQUEST_URI']);
         return $requestUrl->hasScheme() ? $requestUrl->getScheme() : 'http';
     }
 }
Exemple #2
0
 /**
  * 
  * @return boolean
  */
 private function areSchemesEquivalent()
 {
     if ($this->sourceUrl->getScheme() === $this->comparatorUrl->getScheme()) {
         return true;
     }
     foreach ($this->equivalentSchemes as $equivalentSchemeSet) {
         if (in_array($this->sourceUrl->getScheme(), $equivalentSchemeSet) && in_array($this->comparatorUrl->getScheme(), $equivalentSchemeSet)) {
             return true;
         }
     }
     return false;
 }
Exemple #3
0
 function testUrl()
 {
     $url = new Url(self::TEST_URL);
     $this->assertEquals($url->getFull(), self::TEST_URL);
     $this->assertEquals($url->getScheme(), 'https');
     $this->assertEquals($url->getHost(), 'www.google.com');
     $this->assertEquals($url->getPort(), '80');
     $this->assertEquals($url->getPath(), '/some_path');
     // The HTML entities remain intact
     $this->assertEquals($url->getQuery(), 'some=query&something=%20weird');
     // The HTML entities are resolved
     $this->assertEquals($url->getParam('some'), 'query');
     $this->assertEquals($url->getParam('something'), ' weird');
     $this->assertEquals($url->getParams(), ['some' => 'query', 'something' => ' weird']);
     $this->assertEquals($url->getFragment(), 'some_fragment');
     $this->assertEquals($url->getUser(), 'user');
     $this->assertEquals($url->getPass(), 'pass');
 }
 /**
  * Generate a base string for a RSA-SHA1 signature
  * based on the given a url, method, and any parameters.
  *
  * @param Url    $url
  * @param string $method
  * @param array  $parameters
  *
  * @return string
  */
 protected function baseString(Url $url, $method = 'POST', array $parameters = [])
 {
     $baseString = rawurlencode($method) . '&';
     $schemeHostPath = $url->getScheme() . '://' . $url->getHost();
     if ($url->getPort() != '') {
         $schemeHostPath .= ':' . $url->getPort();
     }
     if ($url->getPath() != '') {
         $schemeHostPath .= $url->getPath();
     }
     $baseString .= rawurlencode($schemeHostPath) . '&';
     $data = [];
     parse_str($url->getQuery(), $query);
     foreach (array_merge($query, $parameters) as $key => $value) {
         $data[rawurlencode($key)] = rawurlencode($value);
     }
     ksort($data);
     array_walk($data, function (&$value, $key) {
         $value = $key . '=' . $value;
     });
     $baseString .= rawurlencode(implode('&', $data));
     return $baseString;
 }
Exemple #5
0
 /**
  * Test default http scheme.
  */
 public function testDefaultScheme()
 {
     $url = new Url(self::$baseUrl);
     $this->assertEquals('http', $url->getScheme());
     $url = new Url('domain.tld');
     $this->assertEquals('http', $url->getScheme());
     $url = new Url('ssh://domain.tld');
     $this->assertEquals('ssh', $url->getScheme());
     $url = new Url('ftp://domain.tld');
     $this->assertEquals('ftp', $url->getScheme());
     $url = new Url('git://domain.tld/push?pull=clone#checkout');
     $this->assertEquals('git', $url->getScheme());
 }
Exemple #6
0
 /**
  * Checks this url is base uri for the given url.
  * 
  * @param Url $targetUri The url to inspect the base part.
  * 
  * @return boolean
  */
 public function isBaseOf(Url $targetUri)
 {
     if ($this->_parts['scheme'] !== $targetUri->getScheme() || $this->_parts['host'] !== $targetUri->getHost() || $this->getPort() !== $targetUri->getPort()) {
         return false;
     }
     $srcSegmentCount = count($this->_segments);
     $targetSegments = $targetUri->getSegments();
     $targetSegmentCount = count($targetSegments);
     if ($srcSegmentCount > $targetSegmentCount) {
         return false;
     }
     for ($i = 0; $i < $srcSegmentCount; $i++) {
         if ($this->_segments[$i] !== $targetSegments[$i]) {
             return false;
         }
     }
     return true;
 }
Exemple #7
0
 /**
  * get a link from the current URL to another one
  * @param  UrlInterface|string $url the URL to link to
  * @param  boolean $forceAbsolute   should an absolute path be used, defaults to `true`)
  * @return string  the link
  */
 public function linkTo($url, $forceAbsolute = true)
 {
     if (is_string($url)) {
         $url = new Url($url);
     }
     $str = (string) $url;
     if ($this->getScheme() !== $url->getScheme()) {
         return $str;
     }
     $str = preg_replace('(^[^/]+//)', '', $str);
     if ($this->getHost() !== $url->getHost() || $this->getPort() !== $url->getPort()) {
         return '//' . $str;
     }
     $str = preg_replace('(^[^/]+)', '', $str);
     if ($this->getPath() !== $url->getPath()) {
         if ($forceAbsolute) {
             return $str;
         }
         $cnt = 0;
         $tseg = $this->getSegments();
         $useg = $url->getSegments();
         foreach ($tseg as $k => $v) {
             if (!isset($useg[$k]) || $useg[$k] !== $v) {
                 break;
             }
             $cnt++;
         }
         $str = './' . str_repeat('../', count($useg) - $cnt) . implode('/', array_slice($useg, $cnt));
         if ($url->getQuery()) {
             $str .= '?' . $url->getQuery();
         }
         if ($url->getFragment()) {
             $str .= '#' . $url->getFragment();
         }
         return $str;
     }
     $str = preg_replace('(^[^?]+)', '', $str);
     if ($this->getQuery() !== $url->getQuery() || $url->getFragment() === null) {
         return $str;
     }
     return '#' . $url->getFragment();
 }
Exemple #8
0
 /**
  * @param $url
  * @param $expectedScheme
  *
  * @dataProvider dataProviderForTestGetScheme
  */
 public function testGetScheme($url, $expectedScheme)
 {
     $url = new Url($url);
     $this->assertEquals($expectedScheme, $url->getScheme());
 }
Exemple #9
0
 public function testUrlFragmentEncoding()
 {
     $url = new Url('http://127.0.0.1/foobar?bar=foo#!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~');
     $this->assertEquals('http', $url->getScheme());
     $this->assertEquals(null, $url->getUserInfo());
     $this->assertEquals('127.0.0.1', $url->getHost());
     $this->assertEquals(null, $url->getPort());
     $this->assertEquals('/foobar', $url->getPath());
     $this->assertEquals(array('bar' => 'foo'), $url->getParameters());
     $this->assertEquals('!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', $url->getFragment());
 }
Exemple #10
0
/**
 * Get URL scheme.
 *
 * @param string url Url for which the scheme is requested
 *
 * @return mixed the URL scheme or false if none is provided.
 */
function get_url_scheme($url)
{
    $obj_url = new Url($url);
    return $obj_url->getScheme();
}
Exemple #11
0
function renderPage()
{
    $LINKSDB = new LinkDB($GLOBALS['config']['DATASTORE'], isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI'], $GLOBALS['config']['HIDE_PUBLIC_LINKS']);
    // -------- Display login form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=login')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            header('Location: ?');
            exit;
        }
        // No need to login for open Shaarli
        $token = '';
        if (ban_canLogin()) {
            $token = getToken();
        }
        // Do not waste token generation if not useful.
        $PAGE = new pageBuilder();
        $PAGE->assign('token', $token);
        $PAGE->assign('returnurl', isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '');
        $PAGE->renderPage('loginform');
        exit;
    }
    // -------- User wants to logout.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=logout')) {
        invalidateCaches($GLOBALS['config']['PAGECACHE']);
        logout();
        header('Location: ?');
        exit;
    }
    // -------- Picture wall
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=picwall')) {
        // Optionally filter the results:
        $links = array();
        if (!empty($_GET['searchterm'])) {
            $links = $LINKSDB->filterFulltext($_GET['searchterm']);
        } elseif (!empty($_GET['searchtags'])) {
            $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
        } else {
            $links = $LINKSDB;
        }
        $body = '';
        $linksToDisplay = array();
        // Get only links which have a thumbnail.
        foreach ($links as $link) {
            $permalink = '?' . escape(smallhash($link['linkdate']));
            $thumb = lazyThumbnail($link['url'], $permalink);
            if ($thumb != '') {
                $link['thumbnail'] = $thumb;
                // Thumbnail HTML code.
                $linksToDisplay[] = $link;
                // Add to array.
            }
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('linksToDisplay', $linksToDisplay);
        $PAGE->renderPage('picwall');
        exit;
    }
    // -------- Tag cloud
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tagcloud')) {
        $tags = $LINKSDB->allTags();
        // We sort tags alphabetically, then choose a font size according to count.
        // First, find max value.
        $maxcount = 0;
        foreach ($tags as $key => $value) {
            $maxcount = max($maxcount, $value);
        }
        ksort($tags);
        $tagList = array();
        foreach ($tags as $key => $value) {
            $tagList[$key] = array('count' => $value, 'size' => log($value, 15) / log($maxcount, 30) * (22 - 6) + 6);
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('tags', $tagList);
        $PAGE->renderPage('tagcloud');
        exit;
    }
    // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
    if (isset($_GET['addtag'])) {
        // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?searchtags=' . urlencode($_GET['addtag']));
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        // Prevent redirection loop
        if (isset($params['addtag'])) {
            unset($params['addtag']);
        }
        // Check if this tag is already in the search query and ignore it if it is.
        // Each tag is always separated by a space
        if (isset($params['searchtags'])) {
            $current_tags = explode(' ', $params['searchtags']);
        } else {
            $current_tags = array();
        }
        $addtag = true;
        foreach ($current_tags as $value) {
            if ($value === $_GET['addtag']) {
                $addtag = false;
                break;
            }
        }
        // Append the tag if necessary
        if (empty($params['searchtags'])) {
            $params['searchtags'] = trim($_GET['addtag']);
        } else {
            if ($addtag) {
                $params['searchtags'] = trim($params['searchtags']) . ' ' . trim($_GET['addtag']);
            }
        }
        unset($params['page']);
        // We also remove page (keeping the same page has no sense, since the results are different)
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
    if (isset($_GET['removetag'])) {
        // Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?');
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        // Prevent redirection loop
        if (isset($params['removetag'])) {
            unset($params['removetag']);
        }
        if (isset($params['searchtags'])) {
            $tags = explode(' ', $params['searchtags']);
            $tags = array_diff($tags, array($_GET['removetag']));
            // Remove value from array $tags.
            if (count($tags) == 0) {
                unset($params['searchtags']);
            } else {
                $params['searchtags'] = implode(' ', $tags);
            }
            unset($params['page']);
            // We also remove page (keeping the same page has no sense, since the results are different)
        }
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User wants to change the number of links per page (linksperpage=...)
    if (isset($_GET['linksperpage'])) {
        if (is_numeric($_GET['linksperpage'])) {
            $_SESSION['LINKS_PER_PAGE'] = abs(intval($_GET['linksperpage']));
        }
        header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage')));
        exit;
    }
    // -------- User wants to see only private links (toggle)
    if (isset($_GET['privateonly'])) {
        if (empty($_SESSION['privateonly'])) {
            $_SESSION['privateonly'] = 1;
            // See only private links
        } else {
            unset($_SESSION['privateonly']);
            // See all links
        }
        header('Location: ' . generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')));
        exit;
    }
    // -------- Handle other actions allowed for non-logged in users:
    if (!isLoggedIn()) {
        // User tries to post new link but is not logged in:
        // Show login screen, then redirect to ?post=...
        if (isset($_GET['post'])) {
            header('Location: ?do=login&post=' . urlencode($_GET['post']) . (!empty($_GET['title']) ? '&title=' . urlencode($_GET['title']) : '') . (!empty($_GET['description']) ? '&description=' . urlencode($_GET['description']) : '') . (!empty($_GET['source']) ? '&source=' . urlencode($_GET['source']) : ''));
            // Redirect to login page, then back to post link.
            exit;
        }
        // Same case as above except that user tried to access ?do=addlink without being logged in
        // Note: passing empty parameters makes Shaarli generate default URLs and descriptions.
        if (isset($_GET['do']) && $_GET['do'] === 'addlink') {
            header('Location: ?do=login&post=');
            exit;
        }
        if (isset($_GET['edit_link'])) {
            header('Location: ?do=login&edit_link=' . escape($_GET['edit_link']));
            exit;
        }
        $PAGE = new pageBuilder();
        buildLinkList($PAGE, $LINKSDB);
        // Compute list of links to display
        $PAGE->renderPage('linklist');
        exit;
        // Never remove this one! All operations below are reserved for logged in user.
    }
    // -------- All other functions are reserved for the registered user:
    // -------- Display the Tools menu if requested (import/export/bookmarklet...)
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tools')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('pageabsaddr', indexUrl());
        $PAGE->renderPage('tools');
        exit;
    }
    // -------- User wants to change his/her password.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changepasswd')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            die('You are not supposed to change a password on an Open Shaarli.');
        }
        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away!
            // Make sure old password is correct.
            $oldhash = sha1($_POST['oldpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            if ($oldhash != $GLOBALS['hash']) {
                echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>';
                exit;
            }
            // Save new password
            $GLOBALS['salt'] = sha1(uniqid('', true) . '_' . mt_rand());
            // Salt renders rainbow-tables attacks useless.
            $GLOBALS['hash'] = sha1($_POST['setpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            try {
                writeConfig($GLOBALS, isLoggedIn());
            } catch (Exception $e) {
                error_log('ERROR while writing config file after changing password.' . PHP_EOL . $e->getMessage());
                // TODO: do not handle exceptions/errors in JS.
                echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>';
                exit;
            }
            echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changepassword');
            exit;
        }
    }
    // -------- User wants to change configuration
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=configure')) {
        if (!empty($_POST['title'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away!
            $tz = 'UTC';
            if (!empty($_POST['continent']) && !empty($_POST['city'])) {
                if (isTimeZoneValid($_POST['continent'], $_POST['city'])) {
                    $tz = $_POST['continent'] . '/' . $_POST['city'];
                }
            }
            $GLOBALS['timezone'] = $tz;
            $GLOBALS['title'] = $_POST['title'];
            $GLOBALS['titleLink'] = $_POST['titleLink'];
            $GLOBALS['redirector'] = $_POST['redirector'];
            $GLOBALS['disablesessionprotection'] = !empty($_POST['disablesessionprotection']);
            $GLOBALS['privateLinkByDefault'] = !empty($_POST['privateLinkByDefault']);
            $GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = !empty($_POST['enableRssPermalinks']);
            $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']);
            $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = !empty($_POST['hidePublicLinks']);
            try {
                writeConfig($GLOBALS, isLoggedIn());
            } catch (Exception $e) {
                error_log('ERROR while writing config file after configuration update.' . PHP_EOL . $e->getMessage());
                // TODO: do not handle exceptions/errors in JS.
                echo '<script>alert("' . $e->getMessage() . '");document.location=\'?do=tools\';</script>';
                exit;
            }
            echo '<script>alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title']);
            $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']);
            list($timezone_form, $timezone_js) = generateTimeZoneForm($GLOBALS['timezone']);
            $PAGE->assign('timezone_form', $timezone_form);
            $PAGE->assign('timezone_js', $timezone_js);
            $PAGE->renderPage('configure');
            exit;
        }
    }
    // -------- User wants to rename a tag or delete it
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changetag')) {
        if (empty($_POST['fromtag'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->assign('tags', $LINKSDB->allTags());
            $PAGE->renderPage('changetag');
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Delete a tag:
        if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // True for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                unset($tags[array_search($needle, $tags)]);
                // Remove tag.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
            // Save to disk.
            echo '<script>alert("Tag was removed from ' . count($linksToAlter) . ' links.");document.location=\'?\';</script>';
            exit;
        }
        // Rename a tag:
        if (!empty($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // true for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                $tags[array_search($needle, $tags)] = trim($_POST['totag']);
                // Replace tags value.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
            // Save to disk.
            echo '<script>alert("Tag was renamed in ' . count($linksToAlter) . ' links.");document.location=\'?searchtags=' . urlencode($_POST['totag']) . '\';</script>';
            exit;
        }
    }
    // -------- User wants to add a link without using the bookmarklet: Show form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=addlink')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->renderPage('addlink');
        exit;
    }
    // -------- User clicked the "Save" button when editing a link: Save link to database.
    if (isset($_POST['save_edit'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Go away!
        $tags = trim(preg_replace('/\\s\\s+/', ' ', $_POST['lf_tags']));
        // Remove multiple spaces.
        $tags = implode(' ', array_unique(explode(' ', $tags)));
        // Remove duplicates.
        $linkdate = $_POST['lf_linkdate'];
        $url = trim($_POST['lf_url']);
        if (!startsWith($url, 'http:') && !startsWith($url, 'https:') && !startsWith($url, 'ftp:') && !startsWith($url, 'magnet:') && !startsWith($url, '?') && !startsWith($url, 'javascript:')) {
            $url = 'http://' . $url;
        }
        $link = array('title' => trim($_POST['lf_title']), 'url' => $url, 'description' => trim($_POST['lf_description']), 'private' => isset($_POST['lf_private']) ? 1 : 0, 'linkdate' => $linkdate, 'tags' => str_replace(',', ' ', $tags));
        if ($link['title'] == '') {
            $link['title'] = $link['url'];
        }
        // If title is empty, use the URL as title.
        $LINKSDB[$linkdate] = $link;
        $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
        // Save to disk.
        pubsubhub();
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        $returnurl = !empty($_POST['returnurl']) ? escape($_POST['returnurl']) : '?';
        $returnurl .= '#' . smallHash($_POST['lf_linkdate']);
        // Scroll to the link which has been edited.
        $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
        header('Location: ' . $location);
        // After saving the link, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Cancel" button when editing a link.
    if (isset($_POST['cancel_edit'])) {
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        $returnurl .= '#' . smallHash($_POST['lf_linkdate']);
        // Scroll to the link which has been edited.
        $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
        header('Location: ' . $returnurl);
        // After canceling, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Delete" button when editing a link: Delete link from database.
    if (isset($_POST['delete_link'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // We do not need to ask for confirmation:
        // - confirmation is handled by JavaScript
        // - we are protected from XSRF by the token.
        $linkdate = $_POST['lf_linkdate'];
        unset($LINKSDB[$linkdate]);
        $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']);
        // save to disk
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && ($_GET['source'] == 'bookmarklet' || $_GET['source'] == 'firefoxsocialapi')) {
            echo '<script>self.close();</script>';
            exit;
        }
        // Pick where we're going to redirect
        // =============================================================
        // Basically, we can't redirect to where we were previously if it was a permalink
        // or an edit_link, because it would 404.
        // Cases:
        //    - /             : nothing in $_GET, redirect to self
        //    - /?page        : redirect to self
        //    - /?searchterm  : redirect to self (there might be other links)
        //    - /?searchtags  : redirect to self
        //    - /permalink    : redirect to / (the link does not exist anymore)
        //    - /?edit_link   : redirect to / (the link does not exist anymore)
        // PHP treats the permalink as a $_GET variable, so we need to check if every condition for self
        // redirect is not satisfied, and only then redirect to /
        $location = "?";
        // Self redirection
        if (count($_GET) == 0 || isset($_GET['page']) || isset($_GET['searchterm']) || isset($_GET['searchtags'])) {
            if (isset($_POST['returnurl'])) {
                $location = $_POST['returnurl'];
                // Handle redirects given by the form
            } else {
                $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link'));
            }
        }
        header('Location: ' . $location);
        // After deleting the link, redirect to appropriate location
        exit;
    }
    // -------- User clicked the "EDIT" button on a link: Display link edit form.
    if (isset($_GET['edit_link'])) {
        $link = $LINKSDB[$_GET['edit_link']];
        // Read database
        if (!$link) {
            header('Location: ?');
            exit;
        }
        // Link not found in database.
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', false);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '');
        $PAGE->assign('tags', $LINKSDB->allTags());
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- User want to post a new link: Display link edit form.
    if (isset($_GET['post'])) {
        $url = new Url($_GET['post']);
        $url->cleanup();
        $link_is_new = false;
        // Check if URL is not already in database (in this case, we will edit the existing link)
        $link = $LINKSDB->getLinkFromUrl((string) $url);
        if (!$link) {
            $link_is_new = true;
            $linkdate = strval(date('Ymd_His'));
            // Get title if it was provided in URL (by the bookmarklet).
            $title = empty($_GET['title']) ? '' : $_GET['title'];
            // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
            $description = empty($_GET['description']) ? '' : $_GET['description'];
            $tags = empty($_GET['tags']) ? '' : $_GET['tags'];
            $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0;
            // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
            if (empty($title) && strpos($url->getScheme(), 'http') !== false) {
                list($status, $headers, $data) = getHTTP($url, 4);
                // Short timeout to keep the application responsive.
                // FIXME: Decode charset according to specified in either 1) HTTP response headers or 2) <head> in html
                if (strpos($status, '200 OK') !== false) {
                    // Look for charset in html header.
                    preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
                    // If found, extract encoding.
                    if (!empty($meta[0])) {
                        // Get encoding specified in header.
                        preg_match('#charset="?(.*)"#si', $meta[0], $enc);
                        // If charset not found, use utf-8.
                        $html_charset = !empty($enc[1]) ? strtolower($enc[1]) : 'utf-8';
                    } else {
                        $html_charset = 'utf-8';
                    }
                    // Extract title
                    $title = html_extract_title($data);
                    if (!empty($title)) {
                        // Re-encode title in utf-8 if necessary.
                        $title = $html_charset == 'iso-8859-1' ? utf8_encode($title) : $title;
                    }
                }
            }
            if ($url == '') {
                $url = '?' . smallHash($linkdate);
                $title = 'Note: ';
            }
            $link = array('linkdate' => $linkdate, 'title' => $title, 'url' => (string) $url, 'description' => $description, 'tags' => $tags, 'private' => $private);
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', $link_is_new);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->assign('source', isset($_GET['source']) ? $_GET['source'] : '');
        $PAGE->assign('tags', $LINKSDB->allTags());
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- Export as Netscape Bookmarks HTML file.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=export')) {
        if (empty($_GET['what'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->renderPage('export');
            exit;
        }
        $exportWhat = $_GET['what'];
        if (!array_intersect(array('all', 'public', 'private'), array($exportWhat))) {
            die('What are you trying to export???');
        }
        header('Content-Type: text/html; charset=utf-8');
        header('Content-disposition: attachment; filename=bookmarks_' . $exportWhat . '_' . strval(date('Ymd_His')) . '.html');
        $currentdate = date('Y/m/d H:i:s');
        echo <<<HTML
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<!-- Shaarli {$exportWhat} bookmarks export on {$currentdate} -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
HTML;
        foreach ($LINKSDB as $link) {
            if ($exportWhat == 'all' || $exportWhat == 'private' && $link['private'] != 0 || $exportWhat == 'public' && $link['private'] == 0) {
                echo '<DT><A HREF="' . $link['url'] . '" ADD_DATE="' . linkdate2timestamp($link['linkdate']) . '" PRIVATE="' . $link['private'] . '"';
                if ($link['tags'] != '') {
                    echo ' TAGS="' . str_replace(' ', ',', $link['tags']) . '"';
                }
                echo '>' . $link['title'] . "</A>\n";
                if ($link['description'] != '') {
                    echo '<DD>' . $link['description'] . "\n";
                }
            }
        }
        exit;
    }
    // -------- User is uploading a file for import
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=upload')) {
        // If file is too big, some form field may be missing.
        if (!isset($_POST['token']) || !isset($_FILES) || isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
            $returnurl = empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'];
            echo '<script>alert("The file you are trying to upload is probably bigger than what this webserver can accept (' . getMaxFileSize() . ' bytes). Please upload in smaller chunks.");document.location=\'' . escape($returnurl) . '\';</script>';
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        importFile();
        exit;
    }
    // -------- Show upload/import dialog:
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=import')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('token', getToken());
        $PAGE->assign('maxfilesize', getMaxFileSize());
        $PAGE->renderPage('import');
        exit;
    }
    // -------- Otherwise, simply display search form and links:
    $PAGE = new pageBuilder();
    buildLinkList($PAGE, $LINKSDB);
    // Compute list of links to display
    $PAGE->renderPage('linklist');
    exit;
}