aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/config.default.php15
-rw-r--r--data/schema/6.sql4
-rw-r--r--data/tables.sql8
-rw-r--r--doc/ChangeLog4
-rw-r--r--doc/UPGRADE.txt11
-rw-r--r--src/SemanticScuttle/Model/Bookmark.php46
-rw-r--r--src/SemanticScuttle/Service/Bookmark.php10
-rw-r--r--src/SemanticScuttle/Service/User.php88
-rw-r--r--src/SemanticScuttle/constants.php5
-rw-r--r--src/SemanticScuttle/header.php1
-rw-r--r--tests/Api/OpenSearchTest.php2
-rw-r--r--tests/BookmarkTest.php15
-rw-r--r--tests/Model/BookmarkTest.php65
-rw-r--r--tests/TestBaseApi.php17
-rw-r--r--www/bookmarks.php7
15 files changed, 268 insertions, 30 deletions
diff --git a/data/config.default.php b/data/config.default.php
index b975fac..8c47e0b 100644
--- a/data/config.default.php
+++ b/data/config.default.php
@@ -470,6 +470,21 @@ $filetypes = array(
);
/**
+ * Link protocols that are allowed for newly added bookmarks.
+ * This prevents i.e. adding javascript: links.
+ *
+ * @link http://en.wikipedia.org/wiki/URI_scheme
+ *
+ * @var array
+ */
+$allowedProtocols = array(
+ 'ftp', 'ftps',
+ 'http', 'https',
+ 'mailto', 'nntp',
+ 'xmpp'
+);
+
+/**
* Enable the "common bookmark description" functionality
*
* @var boolean
diff --git a/data/schema/6.sql b/data/schema/6.sql
new file mode 100644
index 0000000..4ae7cb9
--- /dev/null
+++ b/data/schema/6.sql
@@ -0,0 +1,4 @@
+CREATE TABLE `sc_version` (
+ `schema_version` int(11) NOT NULL
+) DEFAULT CHARSET=utf8;
+INSERT INTO `sc_version` (`schema_version`) VALUES ('6');
diff --git a/data/tables.sql b/data/tables.sql
index c61c2f5..7a9c5bd 100644
--- a/data/tables.sql
+++ b/data/tables.sql
@@ -182,4 +182,10 @@ CREATE TABLE `sc_votes` (
UNIQUE KEY `bid_2` (`bId`,`uId`),
KEY `bid` (`bId`),
KEY `uid` (`uId`)
-) CHARACTER SET utf8 COLLATE utf8_general_ci ; \ No newline at end of file
+) CHARACTER SET utf8 COLLATE utf8_general_ci ;
+
+
+CREATE TABLE `sc_version` (
+ `schema_version` int(11) NOT NULL
+) DEFAULT CHARSET=utf8;
+INSERT INTO `sc_version` (`schema_version`) VALUES ('6');
diff --git a/doc/ChangeLog b/doc/ChangeLog
index a7374e2..a54e71e 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -7,12 +7,16 @@ ChangeLog for SemantiScuttle
- Fix bug #3187177: Wrong URL / Export XML Bookmarks
- Fix bug in getTagsForBookmarks() that fetched all tags
- Fix bug #3097187: Using opensearch with two tags does not work in Firefox
+- Fix bug #3251877: French translation JavaScript Bug when editing bookmarks
- Implement request #3054906: Show user's full name instead of nickname
- Implement patch #3059829: update FR_CA translation
- Show error message on mysqli connection errors
- Update php-gettext library to 1.0.10
- api/posts/add respects the "replace" parameter now
- Fix privacy issue when fetching tags of several users
+- Only URLs with an allowed protocol may be added to the database
+- Support HTTPS connections when $root is not configured
+- SQL schema version table to ease future database upgrades
0.97.2 - 2011-02-17
diff --git a/doc/UPGRADE.txt b/doc/UPGRADE.txt
index c4470f9..3be6654 100644
--- a/doc/UPGRADE.txt
+++ b/doc/UPGRADE.txt
@@ -2,6 +2,17 @@ Upgrading SemanticScuttle from a previous version
=================================================
+From version 0.97 to 0.98
+-------------------------
+Database updates: Apply data/schema/6.sql or do the following:
+
+ CREATE TABLE `sc_version` (
+ `schema_version` int(11) NOT NULL
+ ) DEFAULT CHARSET=utf8;
+
+ INSERT INTO `sc_version` (`schema_version`) VALUES ('6');
+
+
From version 0.96 to 0.97
-------------------------
No database changes necessary.
diff --git a/src/SemanticScuttle/Model/Bookmark.php b/src/SemanticScuttle/Model/Bookmark.php
new file mode 100644
index 0000000..8bda0b3
--- /dev/null
+++ b/src/SemanticScuttle/Model/Bookmark.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Bookmark model class, keeping the data of a single bookmark.
+ * It will slowly replace the old array style format.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_Bookmark
+{
+ /**
+ * Checks if the given URL is valid and may be used with this
+ * SemanticScuttle installation.
+ *
+ * @param string $url URL to verify.
+ *
+ * @return boolean True if the URL is allowed, false if not
+ */
+ public static function isValidUrl($url)
+ {
+ $scheme = parse_url($url, PHP_URL_SCHEME);
+ if (array_search($scheme, $GLOBALS['allowedProtocols']) === false) {
+ return false;
+ }
+ return true;
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php
index a30ad5f..919ca7a 100644
--- a/src/SemanticScuttle/Service/Bookmark.php
+++ b/src/SemanticScuttle/Service/Bookmark.php
@@ -435,6 +435,10 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService
/**
* Adds a bookmark to the database.
*
+ * Security checks are being made here, but no error reasons will be
+ * returned. It is the responsibility of the code that calls
+ * addBookmark() to verify the data.
+ *
* @param string $address Full URL of the bookmark
* @param string $title Bookmark title
* @param string $description Long bookmark description
@@ -453,7 +457,8 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService
* @param boolean $fromImport True when the bookmark is from an import.
* @param integer $sId ID of user who creates the bookmark.
*
- * @return integer Bookmark ID
+ * @return mixed Integer bookmark ID if saving succeeded, false in
+ * case of an error. Error reasons are not returned.
*/
public function addBookmark(
$address, $title, $description, $privateNote, $status, $tags,
@@ -466,6 +471,9 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService
}
$address = $this->normalize($address);
+ if (!SemanticScuttle_Model_Bookmark::isValidUrl($address)) {
+ return false;
+ }
/*
* Note that if date is NULL, then it's added with a date and
diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php
index 9ef8430..072ce85 100644
--- a/src/SemanticScuttle/Service/User.php
+++ b/src/SemanticScuttle/Service/User.php
@@ -29,6 +29,14 @@ require_once 'SemanticScuttle/Model/User.php';
class SemanticScuttle_Service_User extends SemanticScuttle_DbService
{
/**
+ * The ID of the currently logged on user.
+ * NULL when not logged in.
+ *
+ * @var integer
+ */
+ protected $currentuserId = null;
+
+ /**
* Currently logged on user from database
*
* @var array
@@ -363,10 +371,17 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
*/
public function getCurrentUserId()
{
+ if ($this->currentuserId !== null) {
+ return $this->currentuserId;
+ }
+
if (isset($_SESSION[$this->getSessionKey()])) {
- return (int)$_SESSION[$this->getSessionKey()];
+ $this->currentuserId = (int)$_SESSION[$this->getSessionKey()];
+ return $this->currentuserId;
+
+ }
- } else if (isset($_COOKIE[$this->getCookieKey()])) {
+ if (isset($_COOKIE[$this->getCookieKey()])) {
$cook = explode(':', $_COOKIE[$this->getCookieKey()]);
//cookie looks like this: 'id:md5(username+password)'
$query = 'SELECT * FROM '. $this->getTableName() .
@@ -385,10 +400,10 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
if ($row = $this->db->sql_fetchrow($dbresult)) {
$this->setCurrentUserId(
- (int)$row[$this->getFieldName('primary')]
+ (int)$row[$this->getFieldName('primary')], true
);
$this->db->sql_freeresult($dbresult);
- return (int)$_SESSION[$this->getSessionKey()];
+ return $this->currentuserId;
}
}
return false;
@@ -402,16 +417,23 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
* @internal
* No ID verification is being done.
*
- * @param integer $user User ID or null to unset the user
+ * @param integer $user User ID or null to unset the user
+ * @param boolean $storeInSession Store the user ID in the session
*
* @return void
*/
- public function setCurrentUserId($user)
+ public function setCurrentUserId($user, $storeInSession = false)
{
if ($user === null) {
- unset($_SESSION[$this->getSessionKey()]);
+ $this->currentuserId = null;
+ if ($storeInSession) {
+ unset($_SESSION[$this->getSessionKey()]);
+ }
} else {
- $_SESSION[$this->getSessionKey()] = (int)$user;
+ $this->currentuserId = (int)$user;
+ if ($storeInSession) {
+ $_SESSION[$this->getSessionKey()] = $this->currentuserId;
+ }
}
//reload user object
$this->getCurrentUser(true);
@@ -449,10 +471,9 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
$this->db->sql_freeresult($dbresult);
if ($row) {
- $id = $_SESSION[$this->getSessionKey()]
- = $row[$this->getFieldName('primary')];
+ $this->setCurrentUserId($row[$this->getFieldName('primary')], true);
if ($remember) {
- $cookie = $id .':'. md5($username.$password);
+ $cookie = $this->currentuserId . ':' . md5($username.$password);
setcookie(
$this->cookiekey, $cookie,
time() + $this->cookietime, '/'
@@ -464,7 +485,13 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
}
}
- function logout() {
+ /**
+ * Logs the user off
+ *
+ * @return void
+ */
+ public function logout()
+ {
@setcookie($this->getCookiekey(), '', time() - 1, '/');
unset($_COOKIE[$this->getCookiekey()]);
session_unset();
@@ -492,10 +519,18 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
return $arrWatch;
}
- function getWatchNames($uId, $watchedby = false) {
- // Gets the list of user names being watched by the given user.
- // - If $watchedby is false get the list of users that $uId watches
- // - If $watchedby is true get the list of users that watch $uId
+
+ /**
+ * Gets the list of user names being watched by the given user.
+ *
+ * @param integer $uId User ID
+ * @param boolean $watchedby if false: get the list of users that $uId watches
+ * if true: get the list of users that watch $uId
+ *
+ * @return array Array of user names
+ */
+ public function getWatchNames($uId, $watchedby = false)
+ {
if ($watchedby) {
$table1 = 'b';
$table2 = 'a';
@@ -503,10 +538,22 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
$table1 = 'a';
$table2 = 'b';
}
- $query = 'SELECT '. $table1 .'.'. $this->getFieldName('username') .' FROM '. $GLOBALS['tableprefix'] .'watched AS W, '. $this->getTableName() .' AS a, '. $this->getTableName() .' AS b WHERE W.watched = a.'. $this->getFieldName('primary') .' AND W.uId = b.'. $this->getFieldName('primary') .' AND '. $table2 .'.'. $this->getFieldName('primary') .' = '. intval($uId) .' ORDER BY '. $table1 .'.'. $this->getFieldName('username');
+ $primary = $this->getFieldName('primary');
+ $userfield = $this->getFieldName('username');
+ $query = 'SELECT '. $table1 .'.'. $userfield
+ . ' FROM '. $GLOBALS['tableprefix'] . 'watched AS W,'
+ . ' ' . $this->getTableName() .' AS a,'
+ . ' ' . $this->getTableName() .' AS b'
+ . ' WHERE W.watched = a.' . $primary
+ . ' AND W.uId = b.' . $primary
+ . ' AND ' . $table2 . '.' . $primary . ' = '. intval($uId)
+ . ' ORDER BY '. $table1 . '.' . $userfield;
- if (!($dbresult =& $this->db->sql_query($query))) {
- message_die(GENERAL_ERROR, 'Could not get watchlist', '', __LINE__, __FILE__, $query, $this->db);
+ if (!($dbresult = $this->db->sql_query($query))) {
+ message_die(
+ GENERAL_ERROR, 'Could not get watchlist',
+ '', __LINE__, __FILE__, $query, $this->db
+ );
return false;
}
@@ -515,13 +562,14 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
$this->db->sql_freeresult($dbresult);
return $arrWatch;
}
- while ($row =& $this->db->sql_fetchrow($dbresult)) {
+ while ($row = $this->db->sql_fetchrow($dbresult)) {
$arrWatch[] = $row[$this->getFieldName('username')];
}
$this->db->sql_freeresult($dbresult);
return $arrWatch;
}
+
function getWatchStatus($watcheduser, $currentuser) {
// Returns true if the current user is watching the given user, and false otherwise.
$query = 'SELECT watched FROM '. $GLOBALS['tableprefix'] .'watched AS W INNER JOIN '. $this->getTableName() .' AS U ON U.'. $this->getFieldName('primary') .' = W.watched WHERE U.'. $this->getFieldName('primary') .' = '. intval($watcheduser) .' AND W.uId = '. intval($currentuser);
diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php
index b023840..f8567d9 100644
--- a/src/SemanticScuttle/constants.php
+++ b/src/SemanticScuttle/constants.php
@@ -41,7 +41,10 @@ if (!isset($GLOBALS['root'])) {
$rootTmp .= '/';
}
- define('ROOT', 'http://'. $_SERVER['HTTP_HOST'] . $rootTmp);
+ //we do not prepend http since we also want to support https connections
+ // "http" is not required; it's automatically determined by the browser
+ // depending on the current connection.
+ define('ROOT', '//'. $_SERVER['HTTP_HOST'] . $rootTmp);
} else {
define('ROOT', $GLOBALS['root']);
}
diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php
index 02d77f5..e931594 100644
--- a/src/SemanticScuttle/header.php
+++ b/src/SemanticScuttle/header.php
@@ -82,6 +82,7 @@ require_once 'SemanticScuttle/Service.php';
require_once 'SemanticScuttle/DbService.php';
require_once 'SemanticScuttle/Service/Factory.php';
require_once 'SemanticScuttle/functions.php';
+require_once 'SemanticScuttle/Model/Bookmark.php';
require_once 'SemanticScuttle/Model/UserArray.php';
if (count($GLOBALS['serviceoverrides']) > 0
diff --git a/tests/Api/OpenSearchTest.php b/tests/Api/OpenSearchTest.php
index 050713b..f438b46 100644
--- a/tests/Api/OpenSearchTest.php
+++ b/tests/Api/OpenSearchTest.php
@@ -27,7 +27,7 @@ class Api_OpenSearchTest extends TestBaseApi
1, count($arElements),
'OpenSearch link in HTML is missing'
);
- $searchDescUrl = (string)$arElements[0]['href'];
+ $searchDescUrl = $this->completeUrl((string)$arElements[0]['href']);
$this->assertNotNull($searchDescUrl, 'Search description URL is empty');
$req = new HTTP_Request2($searchDescUrl);
diff --git a/tests/BookmarkTest.php b/tests/BookmarkTest.php
index e7ce488..b50dab2 100644
--- a/tests/BookmarkTest.php
+++ b/tests/BookmarkTest.php
@@ -50,8 +50,6 @@ class BookmarkTest extends TestBase
/**
* Tests if adding a bookmark with short url name
* saves it in the database.
- *
- * @return void
*/
public function testAddBookmarkShort()
{
@@ -65,7 +63,16 @@ class BookmarkTest extends TestBase
$this->assertEquals('myShortName', $bm['bShort']);
}
- public function testHardCharactersInBookmarks()
+ public function testAddBookmarkInvalidUrl()
+ {
+ $retval = $this->bs->addBookmark(
+ 'javascript:alert(123)', 'title', 'desc', 'priv',
+ 0, array()
+ );
+ $this->assertFalse($retval, 'Bookmark with invalid URL was accepted');
+ }
+
+ public function testAddBookmarkWithSpecialCharacters()
{
$bs = $this->bs;
$title = "title&é\"'(-è_çà)=";
@@ -95,7 +102,7 @@ class BookmarkTest extends TestBase
);
}
- public function testUnificationOfBookmarks()
+ public function testAddBookmarkUnification()
{
$bs = $this->bs;
diff --git a/tests/Model/BookmarkTest.php b/tests/Model/BookmarkTest.php
new file mode 100644
index 0000000..9f55143
--- /dev/null
+++ b/tests/Model/BookmarkTest.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * SemanticScuttle - your social bookmark manager.
+ *
+ * PHP version 5.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * Unit tests for the SemanticScuttle Bookmark model
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license GPL http://www.gnu.org/licenses/gpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class Model_BookmarkTest extends TestBase
+{
+ public function testIsValidUrlValid()
+ {
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'http://example.org/foo/bar?baz=foorina'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'https://example.org/'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'ftp://user:pass@example.org/'
+ )
+ );
+ $this->assertTrue(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'mailto:cweiske@example.org'
+ )
+ );
+ }
+
+ public function testIsValidUrlInvalid()
+ {
+ $this->assertFalse(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'javascript:alert("foo")'
+ )
+ );
+ $this->assertFalse(
+ SemanticScuttle_Model_Bookmark::isValidUrl(
+ 'foo://example.org/foo/bar'
+ )
+ );
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/tests/TestBaseApi.php b/tests/TestBaseApi.php
index 23e1812..2caa701 100644
--- a/tests/TestBaseApi.php
+++ b/tests/TestBaseApi.php
@@ -85,6 +85,23 @@ class TestBaseApi extends TestBase
}
+ /**
+ * Completes an URL that's missing the protocol.
+ * Useful when re-using URLs extracted from HTML
+ *
+ * @param string $url Potentially partial URL
+ *
+ * @return string Full URL
+ */
+ protected function completeUrl($url)
+ {
+ if (substr($url, 0, 2) == '//') {
+ $url = 'http:' . $url;
+ }
+ return $url;
+ }
+
+
/**
* Creates a user and a HTTP GET request object and prepares
diff --git a/www/bookmarks.php b/www/bookmarks.php
index 3092004..efc1680 100644
--- a/www/bookmarks.php
+++ b/www/bookmarks.php
@@ -135,8 +135,11 @@ if ($userservice->isLoggedOn() && POST_SUBMITTED != '') {
$templatename = 'editbookmark.tpl';
} else {
$address = trim(POST_ADDRESS);
- // If the bookmark exists already, edit the original
- if ($bookmarkservice->bookmarkExists($address, $currentUserID)) {
+ if (!SemanticScuttle_Model_Bookmark::isValidUrl($address)) {
+ $tplVars['error'] = T_('This bookmark URL may not be added');
+ $templatename = 'editbookmark.tpl';
+ } else if ($bookmarkservice->bookmarkExists($address, $currentUserID)) {
+ // If the bookmark exists already, edit the original
$bookmark = $bookmarkservice->getBookmarkByAddress($address);
header('Location: '. createURL('edit', $bookmark['bId']));
exit();