RewriteEngine on
diff --git a/boot.php b/boot.php
index 910de6f82..f976ef636 100755
--- a/boot.php
+++ b/boot.php
@@ -9,9 +9,9 @@ require_once('include/nav.php');
require_once('include/cache.php');
define ( 'FRIENDICA_PLATFORM', 'Friendica');
-define ( 'FRIENDICA_VERSION', '2.3.1288' );
+define ( 'FRIENDICA_VERSION', '2.3.1289' );
define ( 'DFRN_PROTOCOL_VERSION', '2.23' );
-define ( 'DB_UPDATE_VERSION', 1132 );
+define ( 'DB_UPDATE_VERSION', 1133 );
define ( 'EOL', " \r\n" );
define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
diff --git a/database.sql b/database.sql
index f058bc59e..327b482c0 100755
--- a/database.sql
+++ b/database.sql
@@ -861,3 +861,9 @@ INDEX ( `term` )
) ENGINE = MyISAM DEFAULT CHARSET=utf8;
+CREATE TABLE IF NOT EXISTS `userd` (
+`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
+`username` CHAR( 255 ) NOT NULL,
+INDEX ( `username` )
+) ENGINE = MyISAM DEFAULT CHARSET=utf8;
+
diff --git a/include/Contact.php b/include/Contact.php
index baccea305..d9949b1ef 100755
--- a/include/Contact.php
+++ b/include/Contact.php
@@ -15,6 +15,12 @@ function user_remove($uid) {
call_hooks('remove_user',$r[0]);
+ // save username (actually the nickname as it is guaranteed
+ // unique), so it cannot be re-registered in the future.
+
+ q("insert into userd ( username ) values ( '%s' )",
+ $r[0]['nickname']
+ );
q("DELETE FROM `contact` WHERE `uid` = %d", intval($uid));
q("DELETE FROM `group` WHERE `uid` = %d", intval($uid));
diff --git a/include/items.php b/include/items.php
index 5a297c83e..b96f88566 100755
--- a/include/items.php
+++ b/include/items.php
@@ -1582,9 +1582,10 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
if(count($r)) {
if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($datarray['title']),
dbesc($datarray['body']),
+ dbesc($datarray['tag']),
dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
dbesc($item_id),
intval($importer['uid'])
@@ -1727,9 +1728,10 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
if(count($r)) {
if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($datarray['title']),
dbesc($datarray['body']),
+ dbesc($datarray['tag']),
dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
dbesc($item_id),
intval($importer['uid'])
@@ -1800,7 +1802,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
// but otherwise there's a possible data mixup on the sender's system.
// the tgroup delivery code called from item_store will correct it if it's a forum,
// but we're going to unconditionally correct it here so that the post will always be owned by our contact.
- logger('local_delivery: Correcting item owner.', LOGGER_DEBUG);
+ logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
$datarray['owner-name'] = $contact['name'];
$datarray['owner-link'] = $contact['url'];
$datarray['owner-avatar'] = $contact['thumb'];
@@ -2332,9 +2334,10 @@ function local_delivery($importer,$data) {
if(count($r)) {
if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($datarray['title']),
dbesc($datarray['body']),
+ dbesc($datarray['tag']),
dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
dbesc($item_id),
intval($importer['importer_uid'])
@@ -2497,9 +2500,10 @@ function local_delivery($importer,$data) {
if(count($r)) {
if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
+ $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($datarray['title']),
dbesc($datarray['body']),
+ dbesc($datarray['tag']),
dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
dbesc($item_id),
intval($importer['importer_uid'])
diff --git a/include/text.php b/include/text.php
index 92a74eb49..6f66cef65 100644
--- a/include/text.php
+++ b/include/text.php
@@ -1338,3 +1338,16 @@ function file_tag_unsave_file($uid,$item,$file) {
function normalise_openid($s) {
return trim(str_replace(array('http://','https://'),array('',''),$s),'/');
}
+
+
+function undo_post_tagging($s) {
+ $matches = null;
+ $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER);
+ if($cnt) {
+ foreach($matches as $mtch) {
+ $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);
+ }
+ }
+ return $s;
+}
+
diff --git a/js/main.js b/js/main.js
index babd2a1c3..2b4b13791 100755
--- a/js/main.js
+++ b/js/main.js
@@ -28,13 +28,15 @@
var in_progress = false;
var langSelect = false;
var commentBusy = false;
+ var last_popup_menu = null;
+ var last_popup_button = null;
$(function() {
$.ajaxSetup({cache: false});
msie = $.browser.msie ;
- /* setup tooltips */
+ /* setup tooltips *//*
$("a,.tt").each(function(){
var e = $(this);
var pos="bottom";
@@ -43,7 +45,7 @@
if (e.hasClass("ttleft")) pos="left";
if (e.hasClass("ttright")) pos="right";
e.tipTip({defaultPosition: pos, edgeOffset: 8});
- });
+ });*/
@@ -76,8 +78,21 @@
if (menu.attr('popup')=="false") return false;
$(this).parent().toggleClass("selected");
menu.toggle();
+ if (menu.css("display") == "none") {
+ last_popup_menu = null;
+ last_popup_button = null;
+ } else {
+ last_popup_menu = menu;
+ last_popup_button = $(this).parent();
+ }
return false;
});
+ $('html').click(function() {
+ last_popup_menu.hide();
+ last_popup_button.removeClass("selected");
+ last_popup_menu = null;
+ last_popup_button = null;
+ });
// fancyboxes
$("a.popupbox").fancybox({
diff --git a/mod/delegate.php b/mod/delegate.php
index c19df0681..8c5031859 100644
--- a/mod/delegate.php
+++ b/mod/delegate.php
@@ -86,7 +86,7 @@ function delegate_content(&$a) {
$r = q("select nurl from contact where substring_index(contact.nurl,'/',3) = '%s'
and contact.uid = %d and contact.self = 0 and network = '%s' ",
- dbesc($a->get_baseurl()),
+ dbesc(normalise_link($a->get_baseurl())),
intval(local_user()),
dbesc(NETWORK_DFRN)
);
diff --git a/mod/editpost.php b/mod/editpost.php
index 778ac3dcc..2ddba36aa 100755
--- a/mod/editpost.php
+++ b/mod/editpost.php
@@ -104,7 +104,7 @@ function editpost_content(&$a) {
'$wait' => t('Please wait'),
'$permset' => t('Permission settings'),
'$ptyp' => $itm[0]['type'],
- '$content' => $itm[0]['body'],
+ '$content' => undo_post_tagging($itm[0]['body']),
'$post_id' => $post_id,
'$baseurl' => $a->get_baseurl(),
'$defloc' => $a->user['default-location'],
diff --git a/mod/item.php b/mod/item.php
index ee6c5c9a7..fc1c5fd0f 100755
--- a/mod/item.php
+++ b/mod/item.php
@@ -177,10 +177,11 @@ function item_post(&$a) {
$verb = $orig_post['verb'];
$emailcc = $orig_post['emailcc'];
$app = $orig_post['app'];
-
+ $categories = $orig_post['file'];
$body = escape_tags(trim($_REQUEST['body']));
$private = $orig_post['private'];
$pubmail_enable = $orig_post['pubmail'];
+
}
else {
@@ -213,8 +214,10 @@ function item_post(&$a) {
$coord = notags(trim($_REQUEST['coord']));
$verb = notags(trim($_REQUEST['verb']));
$emailcc = notags(trim($_REQUEST['emailcc']));
-
$body = escape_tags(trim($_REQUEST['body']));
+
+ // $categories = TODO
+
$private = ((strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) ? 1 : 0);
if(($parent_item) &&
@@ -242,8 +245,6 @@ function item_post(&$a) {
}
}
-
-
if(! strlen($body)) {
if($preview)
killme();
@@ -500,6 +501,7 @@ function item_post(&$a) {
$datarray['location'] = $location;
$datarray['coord'] = $coord;
$datarray['tag'] = $str_tags;
+ $datarray['file'] = $categories;
$datarray['inform'] = $inform;
$datarray['verb'] = $verb;
$datarray['allow_cid'] = $str_contact_allow;
@@ -559,9 +561,12 @@ function item_post(&$a) {
if($orig_post) {
- $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
- dbesc($title),
- dbesc($body),
+ $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `attach` = '%s', `file` = '%s', `edited` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ dbesc($datarray['title']),
+ dbesc($datarray['body']),
+ dbesc($datarray['tag']),
+ dbesc($datarray['attach']),
+ dbesc($datarray['file']),
dbesc(datetime_convert()),
intval($post_id),
intval($profile_uid)
diff --git a/mod/register.php b/mod/register.php
index 388b3e250..6d0e2700b 100755
--- a/mod/register.php
+++ b/mod/register.php
@@ -150,6 +150,16 @@ function register_post(&$a) {
if(count($r))
$err .= t('Nickname is already registered. Please choose another.') . EOL;
+ // Check deleted accounts that had this nickname. Doesn't matter to us,
+ // but could be a security issue for federated platforms.
+
+ $r = q("SELECT * FROM `userd`
+ WHERE `username` = '%s' LIMIT 1",
+ dbesc($nickname)
+ );
+ if(count($r))
+ $err .= t('Nickname was once registered here and may not be re-used. Please choose another.') . EOL;
+
if(strlen($err)) {
notice( $err );
return;
diff --git a/mod/regmod.php b/mod/regmod.php
index 17e728ba2..21f41eb01 100755
--- a/mod/regmod.php
+++ b/mod/regmod.php
@@ -64,6 +64,11 @@ function user_allow($hash) {
}
+
+// This does not have to go through user_remove() and save the nickname
+// permanently against re-registration, as the person was not yet
+// allowed to have friends on this system
+
function user_deny($hash) {
$register = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1",
diff --git a/mod/settings.php b/mod/settings.php
index 59ede4729..99bf8842d 100755
--- a/mod/settings.php
+++ b/mod/settings.php
@@ -263,7 +263,7 @@ function settings_post(&$a) {
$suggestme = ((x($_POST,'suggestme')) ? intval($_POST['suggestme']) : 0);
$hide_friends = (($_POST['hide-friends'] == 1) ? 1: 0);
$hidewall = (($_POST['hidewall'] == 1) ? 1: 0);
-
+ $nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0);
$notify = 0;
@@ -347,6 +347,7 @@ function settings_post(&$a) {
set_pconfig(local_user(),'system','suggestme', $suggestme);
set_pconfig(local_user(),'system','update_interval', $browser_update);
set_pconfig(local_user(),'system','itemspage_network', $itemspage_network);
+ set_pconfig(local_user(),'system','no_smilies',$nosmile);
$r = q("UPDATE `user` SET `username` = '%s', `email` = '%s', `openid` = '%s', `timezone` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `notify-flags` = %d, `page-flags` = %d, `default-location` = '%s', `allow_location` = %d, `theme` = '%s', `maxreq` = %d, `expire` = %d, `openidserver` = '%s', `blockwall` = %d, `hidewall` = %d, `blocktags` = %d WHERE `uid` = %d LIMIT 1",
dbesc($username),
@@ -674,6 +675,9 @@ function settings_content(&$a) {
$itemspage_network = intval(get_pconfig(local_user(), 'system','itemspage_network'));
$itemspage_network = (($itemspage_network > 0 && $itemspage_network < 101) ? $itemspage_network : 40); // default if not set: 40 items
+ $nosmile = get_pconfig(local_user(),'system','no_smilies');
+ $nosmile = (($nosmile===false)? '0': $nosmile); // default if not set: 0
+
if(! strlen($a->user['timezone']))
$timezone = date_default_timezone_get();
@@ -841,6 +845,7 @@ function settings_content(&$a) {
'$theme' => array('theme', t('Display Theme:'), $theme_selected, '', $themes),
'$ajaxint' => array('browser_update', t("Update browser every xx seconds"), $browser_update, t('Minimum of 10 seconds, no maximum')),
'$itemspage_network' => array('itemspage_network', t("Number of items to display on the network page:"), $itemspage_network, t('Maximum of 100 items')),
+ '$nosmile' => array('nosmile', t("Don't show emoticons"), $nosmile, ''),
'$h_prv' => t('Security and Privacy Settings'),
diff --git a/tests/template_test.php b/tests/template_test.php
new file mode 100755
index 000000000..1f9f80531
--- /dev/null
+++ b/tests/template_test.php
@@ -0,0 +1,224 @@
+assertTrue(is_null($second));
+ }
+
+ public function testSimpleVariableString() {
+ $tpl='Hello $name!';
+
+ $text=replace_macros($tpl, array('$name'=>'Anna'));
+
+ $this->assertEquals('Hello Anna!', $text);
+ }
+
+ public function testSimpleVariableInt() {
+ $tpl='There are $num new messages!';
+
+ $text=replace_macros($tpl, array('$num'=>172));
+
+ $this->assertEquals('There are 172 new messages!', $text);
+ }
+
+ public function testConditionalElse() {
+ $tpl='There{{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!';
+
+ $text1=replace_macros($tpl, array('$num'=>1));
+ $text22=replace_macros($tpl, array('$num'=>22));
+
+ $this->assertEquals('There is 1 new message!', $text1);
+ $this->assertEquals('There are 22 new messages!', $text22);
+ }
+
+ public function testConditionalNoElse() {
+ $tpl='{{ if $num!=0 }}There are $num new messages!{{ endif }}';
+
+ $text0=replace_macros($tpl, array('$num'=>0));
+ $text22=replace_macros($tpl, array('$num'=>22));
+
+ $this->assertEquals('', $text0);
+ $this->assertEquals('There are 22 new messages!', $text22);
+ }
+
+ public function testConditionalFail() {
+ $tpl='There {{ if $num!=1 }} are $num new messages{{ else }} is 1 new message{{ endif }}!';
+
+ $text1=replace_macros($tpl, array());
+
+ //$this->assertEquals('There is 1 new message!', $text1);
+ }
+
+ public function testSimpleFor() {
+ $tpl='{{ for $messages as $message }} $message {{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>array('message 1', 'message 2')));
+
+ $this->assertEquals(' message 1 message 2 ', $text);
+ }
+
+ public function testFor() {
+ $tpl='{{ for $messages as $message }} from: $message.from to $message.to {{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>array(array('from'=>'Mike', 'to'=>'Alex'), array('from'=>'Alex', 'to'=>'Mike'))));
+
+ $this->assertEquals(' from: Mike to Alex from: Alex to Mike ', $text);
+ }
+
+ public function testKeyedFor() {
+ $tpl='{{ for $messages as $from=>$to }} from: $from to $to {{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>array('Mike'=>'Alex', 'Sven'=>'Mike')));
+
+ $this->assertEquals(' from: Mike to Alex from: Sven to Mike ', $text);
+ }
+
+ public function testForEmpty() {
+ $tpl='messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>array()));
+
+ $this->assertEquals('messages: ', $text);
+ }
+
+ public function testForWrongType() {
+ $tpl='messages: {{for $messages as $message}} from: $message.from to $message.to {{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>11));
+
+ $this->assertEquals('messages: ', $text);
+ }
+
+ public function testForConditional() {
+ $tpl='new messages: {{for $messages as $message}}{{ if $message.new }} $message.text{{endif}}{{ endfor }}';
+
+ $text=replace_macros($tpl, array('$messages'=>array(
+ array('new'=>true, 'text'=>'new message'),
+ array('new'=>false, 'text'=>'old message'))));
+
+ $this->assertEquals('new messages: new message', $text);
+ }
+
+ public function testConditionalFor() {
+ $tpl='{{ if $enabled }}new messages:{{for $messages as $message}} $message.text{{ endfor }}{{endif}}';
+
+ $text=replace_macros($tpl, array('$enabled'=>true,
+ '$messages'=>array(
+ array('new'=>true, 'text'=>'new message'),
+ array('new'=>false, 'text'=>'old message'))));
+
+ $this->assertEquals('new messages: new message old message', $text);
+ }
+
+ public function testFantasy() {
+ $tpl='Fantasy: {{fantasy $messages}}';
+
+ $text=replace_macros($tpl, array('$messages'=>'no no'));
+
+ $this->assertEquals('Fantasy: {{fantasy no no}}', $text);
+ }
+
+ public function testInc() {
+ $tpl='{{inc field_input.tpl with $field=$myvar}}{{ endinc }}';
+
+ $text=replace_macros($tpl, array('$myvar'=>array('myfield', 'label', 'value', 'help')));
+
+ $this->assertEquals(" \n"
+ ." \n"
+ ." label \n"
+ ." \n"
+ ." help \n"
+ ."
\n", $text);
+ }
+
+ public function testIncNoVar() {
+ $tpl='{{inc field_input.tpl }}{{ endinc }}';
+
+ $text=replace_macros($tpl, array('$field'=>array('myfield', 'label', 'value', 'help')));
+
+ $this->assertEquals(" \n \n label \n"
+ ." \n"
+ ." help \n"
+ ."
\n", $text);
+ }
+
+ public function testDoubleUse() {
+ $tpl='Hello $name! {{ if $enabled }} I love you! {{ endif }}';
+
+ $text=replace_macros($tpl, array('$name'=>'Anna', '$enabled'=>false));
+
+ $this->assertEquals('Hello Anna! ', $text);
+
+ $tpl='Hey $name! {{ if $enabled }} I hate you! {{ endif }}';
+
+ $text=replace_macros($tpl, array('$name'=>'Max', '$enabled'=>true));
+
+ $this->assertEquals('Hey Max! I hate you! ', $text);
+ }
+
+ public function testIncDouble() {
+ $tpl='{{inc field_input.tpl with $field=$var1}}{{ endinc }}'
+ .'{{inc field_input.tpl with $field=$var2}}{{ endinc }}';
+
+ $text=replace_macros($tpl, array('$var1'=>array('myfield', 'label', 'value', 'help'),
+ '$var2'=>array('myfield2', 'label2', 'value2', 'help2')));
+
+ $this->assertEquals(" \n"
+ ." \n"
+ ." label \n"
+ ." \n"
+ ." help \n"
+ ."
\n"
+ ." \n"
+ ." \n"
+ ." label2 \n"
+ ." \n"
+ ." help2 \n"
+ ."
\n", $text);
+ }
+}
\ No newline at end of file
diff --git a/tests/xss_filter_test.php b/tests/xss_filter_test.php
index d7dcf0472..3fb6ac310 100644
--- a/tests/xss_filter_test.php
+++ b/tests/xss_filter_test.php
@@ -27,11 +27,32 @@ class AntiXSSTest extends PHPUnit_Framework_TestCase {
*/
public function testXmlify() {
$text="I want to break\n this!11! ";
- $xml=xmlify($text); //test whether it actually may be part of a xml document
+ $xml=xmlify($text);
$retext=unxmlify($text);
$this->assertEquals($text, $retext);
}
+
+ /**
+ * xmlify and put in a document
+ */
+ public function testXmlifyDocument() {
+ $tag="I want to break ";
+ $xml=xmlify($tag);
+ $text=''.$xml.' ';
+
+ $xml_parser=xml_parser_create();
+ //should be possible to parse it
+ $values=array(); $index=array();
+ $this->assertEquals(1, xml_parse_into_struct($xml_parser, $text, $values, $index));
+
+ $this->assertEquals(array('TEXT'=>array(0)),
+ $index);
+ $this->assertEquals(array(array('tag'=>'TEXT', 'type'=>'complete', 'level'=>1, 'value'=>$tag)),
+ $values);
+
+ xml_parser_free($xml_parser);
+ }
/**
* test hex2bin and reverse
diff --git a/update.php b/update.php
index 6a685a6ff..a69742a94 100755
--- a/update.php
+++ b/update.php
@@ -1,6 +1,6 @@
+
+