Merge branch 'master' of git://github.com/friendica/friendica
This commit is contained in:
commit
12474e3c2e
174 changed files with 18355 additions and 10130 deletions
|
@ -209,7 +209,7 @@ function contact_photo_menu($contact) {
|
|||
);
|
||||
|
||||
|
||||
$args = array('contact' => $contact, 'menu' => $menu);
|
||||
$args = array('contact' => $contact, 'menu' => &$menu);
|
||||
|
||||
call_hooks('contact_photo_menu', $args);
|
||||
|
||||
|
|
|
@ -87,6 +87,12 @@ class Photo {
|
|||
|
||||
}
|
||||
|
||||
public function rotate($degrees) {
|
||||
$this->image = imagerotate($this->image,$degrees,0);
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function scaleImageUp($min) {
|
||||
|
|
113
include/api.php
113
include/api.php
|
@ -4,26 +4,26 @@
|
|||
require_once("conversation.php");
|
||||
require_once("oauth.php");
|
||||
require_once("html2plain.php");
|
||||
/*
|
||||
/*
|
||||
* Twitter-Like API
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
$API = Array();
|
||||
$called_api = Null;
|
||||
$called_api = Null;
|
||||
|
||||
function api_date($str){
|
||||
//Wed May 23 06:01:13 +0000 2007
|
||||
return datetime_convert('UTC', 'UTC', $str, "D M d H:i:s +0000 Y" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function api_register_func($path, $func, $auth=false){
|
||||
global $API;
|
||||
$API[$path] = array('func'=>$func,
|
||||
'auth'=>$auth);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple HTTP Login
|
||||
*/
|
||||
|
@ -691,24 +691,24 @@
|
|||
'geo' => '',
|
||||
'coordinates' => $lastwall['coord'],
|
||||
'place' => $lastwall['location'],
|
||||
'contributors' => ''
|
||||
'contributors' => ''
|
||||
);
|
||||
}
|
||||
return api_apply_template("user", $type, array('$user' => $user_info));
|
||||
|
||||
|
||||
}
|
||||
api_register_func('api/users/show','api_users_show');
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* http://developer.twitter.com/doc/get/statuses/home_timeline
|
||||
*
|
||||
*
|
||||
* TODO: Optional parameters
|
||||
* TODO: Add reply info
|
||||
*/
|
||||
function api_statuses_home_timeline(&$a, $type){
|
||||
if (local_user()===false) return false;
|
||||
|
||||
|
||||
$user_info = api_get_user($a);
|
||||
// get last newtork messages
|
||||
|
||||
|
@ -720,7 +720,7 @@
|
|||
$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
$max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0);
|
||||
//$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
|
||||
|
||||
$start = $page*$count;
|
||||
|
||||
//$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false);
|
||||
|
@ -728,7 +728,7 @@
|
|||
if ($max_id > 0)
|
||||
$sql_extra = 'AND `item`.`id` <= '.intval($max_id);
|
||||
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
||||
`contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
|
||||
|
@ -747,7 +747,7 @@
|
|||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
|
||||
$data = array('$statuses' => $ret);
|
||||
switch($type){
|
||||
case "atom":
|
||||
|
@ -761,7 +761,7 @@
|
|||
return($as);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true);
|
||||
|
@ -769,7 +769,7 @@
|
|||
|
||||
function api_statuses_public_timeline(&$a, $type){
|
||||
if (local_user()===false) return false;
|
||||
|
||||
|
||||
$user_info = api_get_user($a);
|
||||
// get last newtork messages
|
||||
|
||||
|
@ -781,7 +781,7 @@
|
|||
$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
$max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0);
|
||||
//$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
|
||||
|
||||
$start = $page*$count;
|
||||
|
||||
//$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false);
|
||||
|
@ -789,7 +789,7 @@
|
|||
if ($max_id > 0)
|
||||
$sql_extra = 'AND `item`.`id` <= '.intval($max_id);
|
||||
|
||||
/*$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
/*$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
||||
`contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
|
||||
|
@ -806,17 +806,17 @@
|
|||
intval($since_id),
|
||||
intval($start), intval($count)
|
||||
);*/
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`,
|
||||
`contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`,
|
||||
`user`.`nickname`, `user`.`hidewall`
|
||||
FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
|
||||
LEFT JOIN `user` ON `user`.`uid` = `item`.`uid`
|
||||
WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0
|
||||
AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = ''
|
||||
AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''
|
||||
AND `item`.`private` = 0 AND `item`.`wall` = 1 AND `user`.`hidewall` = 0
|
||||
AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = ''
|
||||
AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''
|
||||
AND `item`.`private` = 0 AND `item`.`wall` = 1 AND `user`.`hidewall` = 0
|
||||
AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
|
||||
$sql_extra
|
||||
AND `item`.`id`>%d
|
||||
|
@ -827,7 +827,7 @@
|
|||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
|
||||
$data = array('$statuses' => $ret);
|
||||
switch($type){
|
||||
case "atom":
|
||||
|
@ -841,7 +841,7 @@
|
|||
return($as);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
api_register_func('api/statuses/public_timeline','api_statuses_public_timeline', true);
|
||||
|
@ -857,11 +857,11 @@
|
|||
// params
|
||||
$id = intval($a->argv[3]);
|
||||
|
||||
logger('API: api_statuses_show: '.$id);
|
||||
logger('API: api_statuses_show: '.$id);
|
||||
|
||||
//$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false);
|
||||
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
||||
`contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
|
||||
|
@ -875,7 +875,7 @@
|
|||
);
|
||||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
$data = array('$status' => $ret[0]);
|
||||
/*switch($type){
|
||||
case "atom":
|
||||
|
@ -976,7 +976,7 @@
|
|||
$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
$max_id = (x($_REQUEST,'max_id')?$_REQUEST['max_id']:0);
|
||||
//$since_id = 0;//$since_id = (x($_REQUEST,'since_id')?$_REQUEST['since_id']:0);
|
||||
|
||||
|
||||
$start = $page*$count;
|
||||
|
||||
//$include_entities = (x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:false);
|
||||
|
@ -985,11 +985,19 @@
|
|||
$myurl = substr($myurl,strpos($myurl,'://')+3);
|
||||
$myurl = str_replace(array('www.','.'),array('','\\.'),$myurl);
|
||||
$diasp_url = str_replace('/profile/','/u/',$myurl);
|
||||
$sql_extra .= sprintf(" AND `item`.`parent` IN (SELECT distinct(`parent`) from item where ( `author-link` regexp '%s' or `tag` regexp '%s' or tag regexp '%s' )) ",
|
||||
dbesc($myurl . '$'),
|
||||
dbesc($myurl . '\\]'),
|
||||
dbesc($diasp_url . '\\]')
|
||||
);
|
||||
|
||||
if (get_config('system','use_fulltext_engine'))
|
||||
$sql_extra .= sprintf(" AND `item`.`parent` IN (SELECT distinct(`parent`) from item where (MATCH(`author-link`) AGAINST ('".'"%s"'."' in boolean mode) or MATCH(`tag`) AGAINST ('".'"%s"'."' in boolean mode) or MATCH(tag) AGAINST ('".'"%s"'."' in boolean mode))) ",
|
||||
dbesc(protect_sprintf($myurl)),
|
||||
dbesc(protect_sprintf($myurl)),
|
||||
dbesc(protect_sprintf($diasp_url))
|
||||
);
|
||||
else
|
||||
$sql_extra .= sprintf(" AND `item`.`parent` IN (SELECT distinct(`parent`) from item where ( `author-link` like '%s' or `tag` like '%s' or tag like '%s' )) ",
|
||||
dbesc(protect_sprintf('%' . $myurl)),
|
||||
dbesc(protect_sprintf('%' . $myurl . ']%')),
|
||||
dbesc(protect_sprintf('%' . $diasp_url . ']%'))
|
||||
);
|
||||
|
||||
if ($max_id > 0)
|
||||
$sql_extra .= ' AND `item`.`id` <= '.intval($max_id);
|
||||
|
@ -1013,7 +1021,7 @@
|
|||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
|
||||
$data = array('$statuses' => $ret);
|
||||
switch($type){
|
||||
case "atom":
|
||||
|
@ -1027,7 +1035,7 @@
|
|||
return($as);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
api_register_func('api/statuses/mentions','api_statuses_mentions', true);
|
||||
|
@ -1078,14 +1086,14 @@
|
|||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
|
||||
$data = array('$statuses' => $ret);
|
||||
switch($type){
|
||||
case "atom":
|
||||
case "rss":
|
||||
$data = api_rss_extra($a, $data, $user_info);
|
||||
}
|
||||
|
||||
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
|
||||
|
@ -1094,25 +1102,25 @@
|
|||
|
||||
function api_favorites(&$a, $type){
|
||||
if (local_user()===false) return false;
|
||||
|
||||
|
||||
$user_info = api_get_user($a);
|
||||
// in friendica starred item are private
|
||||
// return favorites only for self
|
||||
logger('api_favorites: self:' . $user_info['self']);
|
||||
|
||||
|
||||
if ($user_info['self']==0) {
|
||||
$ret = array();
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
|
||||
// params
|
||||
$count = (x($_GET,'count')?$_GET['count']:20);
|
||||
$page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
|
||||
if ($page<0) $page=0;
|
||||
|
||||
|
||||
$start = $page*$count;
|
||||
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`,
|
||||
`contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
||||
`contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid`
|
||||
|
@ -1129,16 +1137,16 @@
|
|||
);
|
||||
|
||||
$ret = api_format_items($r,$user_info);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
$data = array('$statuses' => $ret);
|
||||
switch($type){
|
||||
case "atom":
|
||||
case "rss":
|
||||
$data = api_rss_extra($a, $data, $user_info);
|
||||
}
|
||||
|
||||
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
|
||||
|
@ -1208,7 +1216,7 @@
|
|||
$as['link']['type'] = "text/html";
|
||||
return($as);
|
||||
}
|
||||
|
||||
|
||||
function api_format_items($r,$user_info) {
|
||||
|
||||
//logger('api_format_items: ' . print_r($r,true));
|
||||
|
@ -1223,14 +1231,14 @@
|
|||
$status_user = (($item['cid']==$user_info['id'])?$user_info: api_item_get_user($a,$item));
|
||||
|
||||
if ($item['parent']!=$item['id']) {
|
||||
$r = q("select id from item where parent=%s and id<%s order by id desc limit 1",
|
||||
$r = q("select id from item where parent=%s and id<%s order by id desc limit 1",
|
||||
intval($item['parent']), intval($item['id']));
|
||||
if ($r)
|
||||
$in_reply_to_status_id = $r[0]['id'];
|
||||
else
|
||||
$in_reply_to_status_id = $item['parent'];
|
||||
|
||||
$r = q("select `item`.`contact-id`, `contact`.nick, `item`.`author-name` from item, contact
|
||||
$r = q("select `item`.`contact-id`, `contact`.nick, `item`.`author-name` from item, contact
|
||||
where `contact`.`id` = `item`.`contact-id` and `item`.id=%d", intval($in_reply_to_status_id));
|
||||
|
||||
$in_reply_to_screen_name = $r[0]['author-name'];
|
||||
|
@ -1251,6 +1259,9 @@
|
|||
else
|
||||
$statustext = trim($statustitle."\n\n".$statusbody);
|
||||
|
||||
if (($item["network"] == NETWORK_FEED) and (strlen($statustext)> 1000))
|
||||
$statustext = substr($statustext, 0, 1000)."... \n".$item["plink"];
|
||||
|
||||
$status = array(
|
||||
'text' => $statustext,
|
||||
'truncated' => False,
|
||||
|
|
|
@ -11,6 +11,13 @@ function nuke_session() {
|
|||
unset($_SESSION['cid']);
|
||||
unset($_SESSION['theme']);
|
||||
unset($_SESSION['page_flags']);
|
||||
unset($_SESSION['submanage']);
|
||||
unset($_SESSION['my_url']);
|
||||
unset($_SESSION['my_address']);
|
||||
unset($_SESSION['addr']);
|
||||
unset($_SESSION['return_url']);
|
||||
unset($_SESSION['theme']);
|
||||
unset($_SESSION['page_flags']);
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,6 +53,8 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
|
|||
$check = get_config('system','paranoia');
|
||||
// extra paranoia - if the IP changed, log them out
|
||||
if($check && ($_SESSION['addr'] != $_SERVER['REMOTE_ADDR'])) {
|
||||
logger('Session address changed. Paranoid setting in effect, blocking session. '
|
||||
. $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']);
|
||||
nuke_session();
|
||||
goaway(z_root());
|
||||
}
|
||||
|
|
|
@ -221,13 +221,18 @@ function bb2diaspora($Text,$preserve_nl = false) {
|
|||
|
||||
$Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism",'<$1$2=$3&$4>',$Text);
|
||||
|
||||
$Text = preg_replace('/\[(.*?)\]\((.*?)\\\\_(.*?)\)/ism','[$1]($2_$3)',$Text);
|
||||
$Text = preg_replace_callback('/\[(.*?)\]\((.*?)\)/ism','unescape_underscores_in_links',$Text);
|
||||
|
||||
call_hooks('bb2diaspora',$Text);
|
||||
|
||||
return $Text;
|
||||
}
|
||||
|
||||
function unescape_underscores_in_links($m) {
|
||||
$y = str_replace('\\_','_', $m[2]);
|
||||
return('[' . $m[1] . '](' . $y . ')');
|
||||
}
|
||||
|
||||
function format_event_diaspora($ev) {
|
||||
|
||||
$a = get_app();
|
||||
|
|
|
@ -52,6 +52,8 @@ function bb_unspacefy_and_trim($st) {
|
|||
|
||||
function bbcode($Text,$preserve_nl = false) {
|
||||
|
||||
$a = get_app();
|
||||
|
||||
// Hide all [noparse] contained bbtags spacefying them
|
||||
|
||||
$Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text);
|
||||
|
@ -114,6 +116,11 @@ function bbcode($Text,$preserve_nl = false) {
|
|||
$Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" target="external-link">$2</a>', $Text);
|
||||
//$Text = preg_replace("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank">$2</a>', $Text);
|
||||
|
||||
// we may need to restrict this further if it picks up too many strays
|
||||
// link acct:user@host to a webfinger profile redirector
|
||||
|
||||
$Text = preg_replace('/acct:(.*?)@(.*?)([ ,])/', '<a href="' . $a->get_baseurl() . '/acctlink?addr=' . "$1@$2"
|
||||
. '" target="extlink" >acct:' . "$1@$2$3" . '</a>',$Text);
|
||||
|
||||
// Perform MAIL Search
|
||||
$Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $Text);
|
||||
|
@ -226,7 +233,7 @@ function bbcode($Text,$preserve_nl = false) {
|
|||
$endlessloop = 0;
|
||||
while ((strpos($Text, "[/quote]")!== false) and (strpos($Text, "[quote=") !== false) and (++$endlessloop < 20))
|
||||
$Text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism",
|
||||
"<br /><strong class=".'"author"'.">" . $t_wrote . "</strong><blockquote class=".'"author"'.">$2</blockquote>",
|
||||
"<br /><strong class=".'"author"'.">" . $t_wrote . "</strong><blockquote>$2</blockquote>",
|
||||
$Text);
|
||||
|
||||
// [img=widthxheight]image source[/img]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Note:
|
||||
* Please do not store booleans - convert to 0/1 integer values
|
||||
* The get_?config() functions return boolean false for keys that are unset,
|
||||
* and this could lead to subtle bugs.
|
||||
* and this could lead to subtle bugs.
|
||||
*
|
||||
* There are a few places in the code (such as the admin panel) where boolean
|
||||
* configurations need to be fixed as of 10/08/2011.
|
||||
|
@ -30,6 +30,9 @@ function load_config($family) {
|
|||
$a->config[$family][$k] = $rr['v'];
|
||||
}
|
||||
}
|
||||
} else if ($rr['cat'] != 'config') {
|
||||
// Negative caching
|
||||
$a->config[$family] = "!<unset>!";
|
||||
}
|
||||
}}
|
||||
|
||||
|
@ -47,6 +50,13 @@ function get_config($family, $key, $instore = false) {
|
|||
global $a;
|
||||
|
||||
if(! $instore) {
|
||||
// Looking if the whole family isn't set
|
||||
if(isset($a->config[$family])) {
|
||||
if($a->config[$family] === '!<unset>!') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($a->config[$family][$key])) {
|
||||
if($a->config[$family][$key] === '!<unset>!') {
|
||||
return false;
|
||||
|
@ -77,11 +87,9 @@ function get_config($family, $key, $instore = false) {
|
|||
if(! function_exists('set_config')) {
|
||||
function set_config($family,$key,$value) {
|
||||
global $a;
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value)?serialize($value):$value);
|
||||
$dbvalue = (is_bool($value) ? intval($value) : $value);
|
||||
|
||||
$dbvalue = (is_bool($dbvalue) ? intval($dbvalue) : $dbvalue);
|
||||
if(get_config($family,$key,true) === false) {
|
||||
$a->config[$family][$key] = $value;
|
||||
$ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
|
||||
|
@ -89,11 +97,11 @@ function set_config($family,$key,$value) {
|
|||
dbesc($key),
|
||||
dbesc($dbvalue)
|
||||
);
|
||||
if($ret)
|
||||
if($ret)
|
||||
return $value;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
$ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
|
||||
dbesc($dbvalue),
|
||||
dbesc($family),
|
||||
|
@ -120,6 +128,9 @@ function load_pconfig($uid,$family) {
|
|||
$k = $rr['k'];
|
||||
$a->config[$uid][$family][$k] = $rr['v'];
|
||||
}
|
||||
} else if ($rr['cat'] != 'config') {
|
||||
// Negative caching
|
||||
$a->config[$uid][$family] = "!<unset>!";
|
||||
}
|
||||
}}
|
||||
|
||||
|
@ -131,6 +142,13 @@ function get_pconfig($uid,$family, $key, $instore = false) {
|
|||
global $a;
|
||||
|
||||
if(! $instore) {
|
||||
// Looking if the whole family isn't set
|
||||
if(isset($a->config[$uid][$family])) {
|
||||
if($a->config[$uid][$family] === '!<unset>!') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($a->config[$uid][$family][$key])) {
|
||||
if($a->config[$uid][$family][$key] === '!<unset>!') {
|
||||
return false;
|
||||
|
|
|
@ -173,6 +173,13 @@ function localize_item(&$item){
|
|||
$item['body'] = str_replace($mtch[0],'@[url=' . zrl($mtch[1]). ']',$item['body']);
|
||||
}
|
||||
}
|
||||
if(preg_match_all('/\[url=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[img(.*?)\]h(.*?)\[\/img\]\[\/url\]/is',$item['body'],$matches,PREG_SET_ORDER)) {
|
||||
logger('matched');
|
||||
foreach($matches as $mtch) {
|
||||
$item['body'] = str_replace($mtch[0],'[url=' . zrl($mtch[1] . '/photos/' . $mtch[2] . '/image/' . $mtch[3] ,true) . '][img' . $mtch[4] . ']h' . $mtch[5] . '[/img][/url]',$item['body']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -495,7 +502,7 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
|
|||
|
||||
// On the network page, I am the owner. On the display page it will be the profile owner.
|
||||
// This will have been stored in $a->page_contact by our calling page.
|
||||
// Put this person on the left of the wall-to-wall notice.
|
||||
// Put this person as the wall owner of the wall-to-wall notice.
|
||||
|
||||
$owner_url = zrl($a->page_contact['url']);
|
||||
$owner_photo = $a->page_contact['thumb'];
|
||||
|
@ -503,23 +510,38 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
|
|||
$template = $wallwall;
|
||||
$commentww = 'ww';
|
||||
}
|
||||
if((! $item['wall']) && (strlen($item['owner-link'])) && (! link_compare($item['owner-link'],$item['author-link']))) {
|
||||
|
||||
// Could be anybody.
|
||||
if((! $item['wall']) && $item['owner-link']) {
|
||||
|
||||
$owner_url = $item['owner-link'];
|
||||
$owner_photo = $item['owner-avatar'];
|
||||
$owner_name = $item['owner-name'];
|
||||
$template = $wallwall;
|
||||
$commentww = 'ww';
|
||||
// If it is our contact, use a friendly redirect link
|
||||
if((link_compare($item['owner-link'],$item['url']))
|
||||
&& ($item['network'] === NETWORK_DFRN)) {
|
||||
$owner_url = $redirect_url;
|
||||
$osparkle = ' sparkle';
|
||||
$owner_linkmatch = (($item['owner-link']) && link_compare($item['owner-link'],$item['author-link']));
|
||||
$alias_linkmatch = (($item['alias']) && link_compare($item['alias'],$item['author-link']));
|
||||
$owner_namematch = (($item['owner-name']) && $item['owner-name'] == $item['author-name']);
|
||||
if((! $owner_linkmatch) && (! $alias_linkmatch) && (! $owner_namematch)) {
|
||||
|
||||
// The author url doesn't match the owner (typically the contact)
|
||||
// and also doesn't match the contact alias.
|
||||
// The name match is a hack to catch several weird cases where URLs are
|
||||
// all over the park. It can be tricked, but this prevents you from
|
||||
// seeing "Bob Smith to Bob Smith via Wall-to-wall" and you know darn
|
||||
// well that it's the same Bob Smith.
|
||||
|
||||
// But it could be somebody else with the same name. It just isn't highly likely.
|
||||
|
||||
|
||||
$owner_url = $item['owner-link'];
|
||||
$owner_photo = $item['owner-avatar'];
|
||||
$owner_name = $item['owner-name'];
|
||||
$template = $wallwall;
|
||||
$commentww = 'ww';
|
||||
// If it is our contact, use a friendly redirect link
|
||||
if((link_compare($item['owner-link'],$item['url']))
|
||||
&& ($item['network'] === NETWORK_DFRN)) {
|
||||
$owner_url = $redirect_url;
|
||||
$osparkle = ' sparkle';
|
||||
}
|
||||
else
|
||||
$owner_url = zrl($owner_url);
|
||||
}
|
||||
else
|
||||
$owner_url = zrl($owner_url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -292,4 +292,38 @@ function zot_unencapsulate($data,$prvkey) {
|
|||
$ret['sender'] = $s;
|
||||
$ret['data'] = aes_unencapsulate($x,$prvkey);
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
function new_keypair($bits) {
|
||||
|
||||
$openssl_options = array(
|
||||
'digest_alg' => 'sha1',
|
||||
'private_key_bits' => $bits,
|
||||
'encrypt_key' => false
|
||||
);
|
||||
|
||||
$conf = get_config('system','openssl_conf_file');
|
||||
if($conf)
|
||||
$openssl_options['config'] = $conf;
|
||||
|
||||
$result = openssl_pkey_new($openssl_options);
|
||||
|
||||
if(empty($result)) {
|
||||
logger('new_keypair: failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get private key
|
||||
|
||||
$response = array('prvkey' => '', 'pubkey' => '');
|
||||
|
||||
openssl_pkey_export($result, $response['prvkey']);
|
||||
|
||||
// Get public key
|
||||
$pkey = openssl_pkey_get_details($result);
|
||||
$response['pubkey'] = $pkey["key"];
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ function timesel($pre,$h,$m) {
|
|||
// Limited to range of timestamps
|
||||
|
||||
if(! function_exists('relative_date')) {
|
||||
function relative_date($posted_date) {
|
||||
function relative_date($posted_date,$format = null) {
|
||||
|
||||
$localtime = datetime_convert('UTC',date_default_timezone_get(),$posted_date);
|
||||
|
||||
|
@ -274,7 +274,9 @@ function relative_date($posted_date) {
|
|||
if ($d >= 1) {
|
||||
$r = round($d);
|
||||
// translators - e.g. 22 hours ago, 1 minute ago
|
||||
return sprintf( t('%1$d %2$s ago'),$r, (($r == 1) ? $str[0] : $str[1]));
|
||||
if(! $format)
|
||||
$format = t('%1$d %2$s ago');
|
||||
return sprintf( $format,$r, (($r == 1) ? $str[0] : $str[1]));
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -32,9 +32,9 @@ class dba {
|
|||
if (!(strlen($server) && strlen($user))){
|
||||
$this->connected = false;
|
||||
$this->db = null;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if($install) {
|
||||
if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
|
||||
if(! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) {
|
||||
|
@ -71,23 +71,29 @@ class dba {
|
|||
}
|
||||
|
||||
public function q($sql) {
|
||||
|
||||
|
||||
if((! $this->db) || (! $this->connected))
|
||||
return false;
|
||||
|
||||
|
||||
$this->error = '';
|
||||
|
||||
//if (get_config("system", "db_log") != "")
|
||||
// @file_put_contents(get_config("system", "db_log"), datetime_convert().':'.session_id(). ' Start '.$sql."\n", FILE_APPEND);
|
||||
|
||||
if($this->mysqli)
|
||||
$result = @$this->db->query($sql);
|
||||
else
|
||||
$result = @mysql_query($sql,$this->db);
|
||||
|
||||
//if (get_config("system", "db_log") != "")
|
||||
// @file_put_contents(get_config("system", "db_log"), datetime_convert().':'.session_id(). ' Stop '."\n", FILE_APPEND);
|
||||
|
||||
if($this->mysqli) {
|
||||
if($this->db->errno)
|
||||
$this->error = $this->db->error;
|
||||
}
|
||||
elseif(mysql_errno($this->db))
|
||||
$this->error = mysql_error($this->db);
|
||||
$this->error = mysql_error($this->db);
|
||||
|
||||
if(strlen($this->error)) {
|
||||
logger('dba: ' . $this->error);
|
||||
|
@ -107,8 +113,8 @@ class dba {
|
|||
else
|
||||
$mesg = mysql_num_rows($result) . ' results' . EOL;
|
||||
}
|
||||
|
||||
$str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
|
||||
|
||||
$str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
|
||||
. (($this->error) ? ' error: ' . $this->error : '')
|
||||
. EOL;
|
||||
|
||||
|
@ -146,7 +152,7 @@ class dba {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($this->debug)
|
||||
logger('dba: ' . printable(print_r($r, true)));
|
||||
return($r);
|
||||
|
|
|
@ -38,164 +38,168 @@ function delivery_run($argv, $argc){
|
|||
|
||||
$cmd = $argv[1];
|
||||
$item_id = intval($argv[2]);
|
||||
$contact_id = intval($argv[3]);
|
||||
|
||||
// Some other process may have delivered this item already.
|
||||
for($x = 3; $x < $argc; $x ++) {
|
||||
|
||||
$r = q("select * from deliverq where cmd = '%s' and item = %d and contact = %d limit 1",
|
||||
dbesc($cmd),
|
||||
dbesc($item_id),
|
||||
dbesc($contact_id)
|
||||
);
|
||||
if(! count($r)) {
|
||||
return;
|
||||
}
|
||||
$contact_id = intval($argv[$x]);
|
||||
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
if(intval($load[0]) > $maxsysload) {
|
||||
logger('system: load ' . $load . ' too high. Delivery deferred to next queue run.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Some other process may have delivered this item already.
|
||||
|
||||
// It's ours to deliver. Remove it from the queue.
|
||||
|
||||
q("delete from deliverq where cmd = '%s' and item = %d and contact = %d limit 1",
|
||||
dbesc($cmd),
|
||||
dbesc($item_id),
|
||||
dbesc($contact_id)
|
||||
);
|
||||
|
||||
if((! $item_id) || (! $contact_id))
|
||||
return;
|
||||
|
||||
$expire = false;
|
||||
$top_level = false;
|
||||
$recipients = array();
|
||||
$url_recipients = array();
|
||||
|
||||
$normal_mode = true;
|
||||
|
||||
$recipients[] = $contact_id;
|
||||
|
||||
if($cmd === 'expire') {
|
||||
$normal_mode = false;
|
||||
$expire = true;
|
||||
$items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1
|
||||
AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 30 MINUTE",
|
||||
intval($item_id)
|
||||
$r = q("select * from deliverq where cmd = '%s' and item = %d and contact = %d limit 1",
|
||||
dbesc($cmd),
|
||||
dbesc($item_id),
|
||||
dbesc($contact_id)
|
||||
);
|
||||
$uid = $item_id;
|
||||
$item_id = 0;
|
||||
if(! count($items))
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(! count($r)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// find ancestors
|
||||
$r = q("SELECT * FROM `item` WHERE `id` = %d and visible = 1 and moderated = 0 LIMIT 1",
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if((! count($r)) || (! intval($r[0]['parent']))) {
|
||||
return;
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
if(function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
if(intval($load[0]) > $maxsysload) {
|
||||
logger('system: load ' . $load . ' too high. Delivery deferred to next queue run.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$target_item = $r[0];
|
||||
$parent_id = intval($r[0]['parent']);
|
||||
$uid = $r[0]['uid'];
|
||||
$updated = $r[0]['edited'];
|
||||
// It's ours to deliver. Remove it from the queue.
|
||||
|
||||
if(! $parent_id)
|
||||
return;
|
||||
|
||||
|
||||
$items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer`
|
||||
FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC",
|
||||
intval($parent_id)
|
||||
q("delete from deliverq where cmd = '%s' and item = %d and contact = %d limit 1",
|
||||
dbesc($cmd),
|
||||
dbesc($item_id),
|
||||
dbesc($contact_id)
|
||||
);
|
||||
|
||||
if(! count($items)) {
|
||||
return;
|
||||
}
|
||||
if((! $item_id) || (! $contact_id))
|
||||
continue;
|
||||
|
||||
$icontacts = null;
|
||||
$contacts_arr = array();
|
||||
foreach($items as $item)
|
||||
if(! in_array($item['contact-id'],$contacts_arr))
|
||||
$contacts_arr[] = intval($item['contact-id']);
|
||||
if(count($contacts_arr)) {
|
||||
$str_contacts = implode(',',$contacts_arr);
|
||||
$icontacts = q("SELECT * FROM `contact`
|
||||
WHERE `id` IN ( $str_contacts ) "
|
||||
$expire = false;
|
||||
$top_level = false;
|
||||
$recipients = array();
|
||||
$url_recipients = array();
|
||||
|
||||
$normal_mode = true;
|
||||
|
||||
$recipients[] = $contact_id;
|
||||
|
||||
if($cmd === 'expire') {
|
||||
$normal_mode = false;
|
||||
$expire = true;
|
||||
$items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1
|
||||
AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 30 MINUTE",
|
||||
intval($item_id)
|
||||
);
|
||||
$uid = $item_id;
|
||||
$item_id = 0;
|
||||
if(! count($items))
|
||||
continue;
|
||||
}
|
||||
if( ! ($icontacts && count($icontacts)))
|
||||
return;
|
||||
else {
|
||||
|
||||
// avoid race condition with deleting entries
|
||||
// find ancestors
|
||||
$r = q("SELECT * FROM `item` WHERE `id` = %d and visible = 1 and moderated = 0 LIMIT 1",
|
||||
intval($item_id)
|
||||
);
|
||||
|
||||
if($items[0]['deleted']) {
|
||||
if((! count($r)) || (! intval($r[0]['parent']))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$target_item = $r[0];
|
||||
$parent_id = intval($r[0]['parent']);
|
||||
$uid = $r[0]['uid'];
|
||||
$updated = $r[0]['edited'];
|
||||
|
||||
// The following seems superfluous. We've already checked for "if (! intval($r[0]['parent']))" a few lines up
|
||||
if(! $parent_id)
|
||||
continue;
|
||||
|
||||
|
||||
$items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer`
|
||||
FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC",
|
||||
intval($parent_id)
|
||||
);
|
||||
|
||||
if(! count($items)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$icontacts = null;
|
||||
$contacts_arr = array();
|
||||
foreach($items as $item)
|
||||
$item['deleted'] = 1;
|
||||
if(! in_array($item['contact-id'],$contacts_arr))
|
||||
$contacts_arr[] = intval($item['contact-id']);
|
||||
if(count($contacts_arr)) {
|
||||
$str_contacts = implode(',',$contacts_arr);
|
||||
$icontacts = q("SELECT * FROM `contact`
|
||||
WHERE `id` IN ( $str_contacts ) "
|
||||
);
|
||||
}
|
||||
if( ! ($icontacts && count($icontacts)))
|
||||
continue;
|
||||
|
||||
// avoid race condition with deleting entries
|
||||
|
||||
if($items[0]['deleted']) {
|
||||
foreach($items as $item)
|
||||
$item['deleted'] = 1;
|
||||
}
|
||||
|
||||
if((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) {
|
||||
logger('delivery: top level post');
|
||||
$top_level = true;
|
||||
}
|
||||
}
|
||||
|
||||
if((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) {
|
||||
logger('delivery: top level post');
|
||||
$top_level = true;
|
||||
}
|
||||
}
|
||||
$r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
|
||||
`user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
|
||||
`user`.`page-flags`, `user`.`prvnets`
|
||||
FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
$r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
|
||||
`user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
|
||||
`user`.`page-flags`, `user`.`prvnets`
|
||||
FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if(! count($r))
|
||||
continue;
|
||||
|
||||
if(! count($r))
|
||||
return;
|
||||
$owner = $r[0];
|
||||
|
||||
$owner = $r[0];
|
||||
$walltowall = ((($top_level) && ($owner['id'] != $items[0]['contact-id'])) ? true : false);
|
||||
|
||||
$walltowall = ((($top_level) && ($owner['id'] != $items[0]['contact-id'])) ? true : false);
|
||||
$public_message = true;
|
||||
|
||||
$public_message = true;
|
||||
// fill this in with a single salmon slap if applicable
|
||||
|
||||
// fill this in with a single salmon slap if applicable
|
||||
$slap = '';
|
||||
|
||||
$slap = '';
|
||||
require_once('include/group.php');
|
||||
|
||||
require_once('include/group.php');
|
||||
$parent = $items[0];
|
||||
|
||||
$parent = $items[0];
|
||||
// This is IMPORTANT!!!!
|
||||
|
||||
// This is IMPORTANT!!!!
|
||||
// We will only send a "notify owner to relay" or followup message if the referenced post
|
||||
// originated on our system by virtue of having our hostname somewhere
|
||||
// in the URI, AND it was a comment (not top_level) AND the parent originated elsewhere.
|
||||
// if $parent['wall'] == 1 we will already have the parent message in our array
|
||||
// and we will relay the whole lot.
|
||||
|
||||
// expire sends an entire group of expire messages and cannot be forwarded.
|
||||
// However the conversation owner will be a part of the conversation and will
|
||||
// be notified during this run.
|
||||
// Other DFRN conversation members will be alerted during polled updates.
|
||||
|
||||
// We will only send a "notify owner to relay" or followup message if the referenced post
|
||||
// originated on our system by virtue of having our hostname somewhere
|
||||
// in the URI, AND it was a comment (not top_level) AND the parent originated elsewhere.
|
||||
// if $parent['wall'] == 1 we will already have the parent message in our array
|
||||
// and we will relay the whole lot.
|
||||
|
||||
// expire sends an entire group of expire messages and cannot be forwarded.
|
||||
// However the conversation owner will be a part of the conversation and will
|
||||
// be notified during this run.
|
||||
// Other DFRN conversation members will be alerted during polled updates.
|
||||
|
||||
// Diaspora members currently are not notified of expirations, and other networks have
|
||||
// either limited or no ability to process deletions. We should at least fix Diaspora
|
||||
// by stringing togther an array of retractions and sending them onward.
|
||||
// Diaspora members currently are not notified of expirations, and other networks have
|
||||
// either limited or no ability to process deletions. We should at least fix Diaspora
|
||||
// by stringing togther an array of retractions and sending them onward.
|
||||
|
||||
|
||||
$localhost = $a->get_hostname();
|
||||
if(strpos($localhost,':'))
|
||||
$localhost = substr($localhost,0,strpos($localhost,':'));
|
||||
$localhost = $a->get_hostname();
|
||||
if(strpos($localhost,':'))
|
||||
$localhost = substr($localhost,0,strpos($localhost,':'));
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -205,174 +209,71 @@ function delivery_run($argv, $argc){
|
|||
*
|
||||
*/
|
||||
|
||||
if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) {
|
||||
logger('relay denied for delivery agent.');
|
||||
if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) {
|
||||
logger('relay denied for delivery agent.');
|
||||
|
||||
/* no relay allowed for direct contact delivery */
|
||||
return;
|
||||
}
|
||||
/* no relay allowed for direct contact delivery */
|
||||
continue;
|
||||
}
|
||||
|
||||
if((strlen($parent['allow_cid']))
|
||||
|| (strlen($parent['allow_gid']))
|
||||
|| (strlen($parent['deny_cid']))
|
||||
|| (strlen($parent['deny_gid']))) {
|
||||
$public_message = false; // private recipients, not public
|
||||
}
|
||||
if((strlen($parent['allow_cid']))
|
||||
|| (strlen($parent['allow_gid']))
|
||||
|| (strlen($parent['deny_cid']))
|
||||
|| (strlen($parent['deny_gid']))) {
|
||||
$public_message = false; // private recipients, not public
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0",
|
||||
intval($contact_id)
|
||||
);
|
||||
$r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0",
|
||||
intval($contact_id)
|
||||
);
|
||||
|
||||
if(count($r))
|
||||
$contact = $r[0];
|
||||
if(count($r))
|
||||
$contact = $r[0];
|
||||
|
||||
$hubxml = feed_hublinks();
|
||||
$hubxml = feed_hublinks();
|
||||
|
||||
logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA);
|
||||
logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA);
|
||||
|
||||
require_once('include/salmon.php');
|
||||
require_once('include/salmon.php');
|
||||
|
||||
if($contact['self'])
|
||||
return;
|
||||
if($contact['self'])
|
||||
continue;
|
||||
|
||||
$deliver_status = 0;
|
||||
$deliver_status = 0;
|
||||
|
||||
switch($contact['network']) {
|
||||
switch($contact['network']) {
|
||||
|
||||
case NETWORK_DFRN :
|
||||
logger('notifier: dfrndelivery: ' . $contact['name']);
|
||||
case NETWORK_DFRN :
|
||||
logger('notifier: dfrndelivery: ' . $contact['name']);
|
||||
|
||||
$feed_template = get_markup_template('atom_feed.tpl');
|
||||
$mail_template = get_markup_template('atom_mail.tpl');
|
||||
$feed_template = get_markup_template('atom_feed.tpl');
|
||||
$mail_template = get_markup_template('atom_mail.tpl');
|
||||
|
||||
$atom = '';
|
||||
$atom = '';
|
||||
|
||||
|
||||
$birthday = feed_birthday($owner['uid'],$owner['timezone']);
|
||||
$birthday = feed_birthday($owner['uid'],$owner['timezone']);
|
||||
|
||||
if(strlen($birthday))
|
||||
$birthday = '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>';
|
||||
if(strlen($birthday))
|
||||
$birthday = '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>';
|
||||
|
||||
$atom .= replace_macros($feed_template, array(
|
||||
'$version' => xmlify(FRIENDICA_VERSION),
|
||||
'$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner['nickname'] ),
|
||||
'$feed_title' => xmlify($owner['name']),
|
||||
'$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', $updated . '+00:00' , ATOM_TIME)) ,
|
||||
'$hub' => $hubxml,
|
||||
'$salmon' => '', // private feed, we don't use salmon here
|
||||
'$name' => xmlify($owner['name']),
|
||||
'$profile_page' => xmlify($owner['url']),
|
||||
'$photo' => xmlify($owner['photo']),
|
||||
'$thumb' => xmlify($owner['thumb']),
|
||||
'$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$birthday' => $birthday,
|
||||
'$community' => (($owner['page-flags'] == PAGE_COMMUNITY) ? '<dfrn:community>1</dfrn:community>' : '')
|
||||
));
|
||||
|
||||
foreach($items as $item) {
|
||||
if(! $item['parent'])
|
||||
continue;
|
||||
|
||||
// private emails may be in included in public conversations. Filter them.
|
||||
if(($public_message) && $item['private'])
|
||||
continue;
|
||||
|
||||
$item_contact = get_item_contact($item,$icontacts);
|
||||
if(! $item_contact)
|
||||
continue;
|
||||
|
||||
if($normal_mode) {
|
||||
if($item_id == $item['id'] || $item['id'] == $item['parent'])
|
||||
$atom .= atom_entry($item,'text',null,$owner,true);
|
||||
}
|
||||
else
|
||||
$atom .= atom_entry($item,'text',null,$owner,true);
|
||||
|
||||
}
|
||||
|
||||
$atom .= '</feed>' . "\r\n";
|
||||
|
||||
logger('notifier: ' . $atom, LOGGER_DATA);
|
||||
$basepath = implode('/', array_slice(explode('/',$contact['url']),0,3));
|
||||
|
||||
// perform local delivery if we are on the same site
|
||||
|
||||
if(link_compare($basepath,$a->get_baseurl())) {
|
||||
|
||||
$nickname = basename($contact['url']);
|
||||
if($contact['issued-id'])
|
||||
$sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id']));
|
||||
else
|
||||
$sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id']));
|
||||
|
||||
$x = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`,
|
||||
`contact`.`pubkey` AS `cpubkey`,
|
||||
`contact`.`prvkey` AS `cprvkey`,
|
||||
`contact`.`thumb` AS `thumb`,
|
||||
`contact`.`url` as `url`,
|
||||
`contact`.`name` as `senderName`,
|
||||
`user`.*
|
||||
FROM `contact`
|
||||
LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
|
||||
WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
|
||||
AND `contact`.`network` = '%s' AND `user`.`nickname` = '%s'
|
||||
$sql_extra
|
||||
AND `user`.`account_expired` = 0 LIMIT 1",
|
||||
dbesc(NETWORK_DFRN),
|
||||
dbesc($nickname)
|
||||
);
|
||||
|
||||
if(count($x)) {
|
||||
if($owner['page-flags'] == PAGE_COMMUNITY && ! $x[0]['writable']) {
|
||||
q("update contact set writable = 1 where id = %d limit 1",
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
$x[0]['writable'] = 1;
|
||||
}
|
||||
|
||||
$ssl_policy = get_config('system','ssl_policy');
|
||||
fix_contact_ssl_policy($x[0],$ssl_policy);
|
||||
|
||||
// If we are setup as a soapbox we aren't accepting input from this person
|
||||
|
||||
if($x[0]['page-flags'] == PAGE_SOAPBOX)
|
||||
break;
|
||||
|
||||
require_once('library/simplepie/simplepie.inc');
|
||||
logger('mod-delivery: local delivery');
|
||||
local_delivery($x[0],$atom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(! was_recently_delayed($contact['id']))
|
||||
$deliver_status = dfrn_deliver($owner,$contact,$atom);
|
||||
else
|
||||
$deliver_status = (-1);
|
||||
|
||||
logger('notifier: dfrn_delivery returns ' . $deliver_status);
|
||||
|
||||
if($deliver_status == (-1)) {
|
||||
logger('notifier: delivery failed: queuing message');
|
||||
add_to_queue($contact['id'],NETWORK_DFRN,$atom);
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_OSTATUS :
|
||||
|
||||
// Do not send to otatus if we are not configured to send to public networks
|
||||
if($owner['prvnets'])
|
||||
break;
|
||||
if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
|
||||
break;
|
||||
|
||||
// only send salmon if public - e.g. if it's ok to notify
|
||||
// a public hub, it's ok to send a salmon
|
||||
|
||||
if(($public_message) && (! $expire)) {
|
||||
$slaps = array();
|
||||
$atom .= replace_macros($feed_template, array(
|
||||
'$version' => xmlify(FRIENDICA_VERSION),
|
||||
'$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner['nickname'] ),
|
||||
'$feed_title' => xmlify($owner['name']),
|
||||
'$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', $updated . '+00:00' , ATOM_TIME)) ,
|
||||
'$hub' => $hubxml,
|
||||
'$salmon' => '', // private feed, we don't use salmon here
|
||||
'$name' => xmlify($owner['name']),
|
||||
'$profile_page' => xmlify($owner['url']),
|
||||
'$photo' => xmlify($owner['photo']),
|
||||
'$thumb' => xmlify($owner['thumb']),
|
||||
'$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) ,
|
||||
'$birthday' => $birthday,
|
||||
'$community' => (($owner['page-flags'] == PAGE_COMMUNITY) ? '<dfrn:community>1</dfrn:community>' : '')
|
||||
));
|
||||
|
||||
foreach($items as $item) {
|
||||
if(! $item['parent'])
|
||||
|
@ -386,156 +287,260 @@ function delivery_run($argv, $argc){
|
|||
if(! $item_contact)
|
||||
continue;
|
||||
|
||||
if(($top_level) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire))
|
||||
$slaps[] = atom_entry($item,'html',null,$owner,true);
|
||||
if($normal_mode) {
|
||||
if($item_id == $item['id'] || $item['id'] == $item['parent'])
|
||||
$atom .= atom_entry($item,'text',null,$owner,true,(($top_level) ? $contact['id'] : 0));
|
||||
}
|
||||
else
|
||||
$atom .= atom_entry($item,'text',null,$owner,true);
|
||||
|
||||
}
|
||||
|
||||
logger('notifier: slapdelivery: ' . $contact['name']);
|
||||
foreach($slaps as $slappy) {
|
||||
if($contact['notify']) {
|
||||
if(! was_recently_delayed($contact['id']))
|
||||
$deliver_status = slapper($owner,$contact['notify'],$slappy);
|
||||
else
|
||||
$deliver_status = (-1);
|
||||
$atom .= '</feed>' . "\r\n";
|
||||
|
||||
logger('notifier: ' . $atom, LOGGER_DATA);
|
||||
$basepath = implode('/', array_slice(explode('/',$contact['url']),0,3));
|
||||
|
||||
if($deliver_status == (-1)) {
|
||||
// queue message for redelivery
|
||||
add_to_queue($contact['id'],NETWORK_OSTATUS,$slappy);
|
||||
// perform local delivery if we are on the same site
|
||||
|
||||
if(link_compare($basepath,$a->get_baseurl())) {
|
||||
|
||||
$nickname = basename($contact['url']);
|
||||
if($contact['issued-id'])
|
||||
$sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id']));
|
||||
else
|
||||
$sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id']));
|
||||
|
||||
$x = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`,
|
||||
`contact`.`pubkey` AS `cpubkey`,
|
||||
`contact`.`prvkey` AS `cprvkey`,
|
||||
`contact`.`thumb` AS `thumb`,
|
||||
`contact`.`url` as `url`,
|
||||
`contact`.`name` as `senderName`,
|
||||
`user`.*
|
||||
FROM `contact`
|
||||
LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
|
||||
WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
|
||||
AND `contact`.`network` = '%s' AND `user`.`nickname` = '%s'
|
||||
$sql_extra
|
||||
AND `user`.`account_expired` = 0 LIMIT 1",
|
||||
dbesc(NETWORK_DFRN),
|
||||
dbesc($nickname)
|
||||
);
|
||||
|
||||
if(count($x)) {
|
||||
if($owner['page-flags'] == PAGE_COMMUNITY && ! $x[0]['writable']) {
|
||||
q("update contact set writable = 1 where id = %d limit 1",
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
$x[0]['writable'] = 1;
|
||||
}
|
||||
|
||||
$ssl_policy = get_config('system','ssl_policy');
|
||||
fix_contact_ssl_policy($x[0],$ssl_policy);
|
||||
|
||||
// If we are setup as a soapbox we aren't accepting input from this person
|
||||
|
||||
if($x[0]['page-flags'] == PAGE_SOAPBOX)
|
||||
break;
|
||||
|
||||
require_once('library/simplepie/simplepie.inc');
|
||||
logger('mod-delivery: local delivery');
|
||||
local_delivery($x[0],$atom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(! was_recently_delayed($contact['id']))
|
||||
$deliver_status = dfrn_deliver($owner,$contact,$atom);
|
||||
else
|
||||
$deliver_status = (-1);
|
||||
|
||||
logger('notifier: dfrn_delivery returns ' . $deliver_status);
|
||||
|
||||
if($deliver_status == (-1)) {
|
||||
logger('notifier: delivery failed: queuing message');
|
||||
add_to_queue($contact['id'],NETWORK_DFRN,$atom);
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_OSTATUS :
|
||||
|
||||
// Do not send to otatus if we are not configured to send to public networks
|
||||
if($owner['prvnets'])
|
||||
break;
|
||||
if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
|
||||
break;
|
||||
|
||||
// only send salmon if public - e.g. if it's ok to notify
|
||||
// a public hub, it's ok to send a salmon
|
||||
|
||||
if(($public_message) && (! $expire)) {
|
||||
$slaps = array();
|
||||
|
||||
foreach($items as $item) {
|
||||
if(! $item['parent'])
|
||||
continue;
|
||||
|
||||
// private emails may be in included in public conversations. Filter them.
|
||||
if(($public_message) && $item['private'])
|
||||
continue;
|
||||
|
||||
$item_contact = get_item_contact($item,$icontacts);
|
||||
if(! $item_contact)
|
||||
continue;
|
||||
|
||||
if(($top_level) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire))
|
||||
$slaps[] = atom_entry($item,'html',null,$owner,true);
|
||||
}
|
||||
|
||||
logger('notifier: slapdelivery: ' . $contact['name']);
|
||||
foreach($slaps as $slappy) {
|
||||
if($contact['notify']) {
|
||||
if(! was_recently_delayed($contact['id']))
|
||||
$deliver_status = slapper($owner,$contact['notify'],$slappy);
|
||||
else
|
||||
$deliver_status = (-1);
|
||||
|
||||
if($deliver_status == (-1)) {
|
||||
// queue message for redelivery
|
||||
add_to_queue($contact['id'],NETWORK_OSTATUS,$slappy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NETWORK_MAIL :
|
||||
case NETWORK_MAIL2:
|
||||
|
||||
if(get_config('system','dfrn_only'))
|
||||
break;
|
||||
// WARNING: does not currently convert to RFC2047 header encodings, etc.
|
||||
|
||||
$addr = $contact['addr'];
|
||||
if(! strlen($addr))
|
||||
break;
|
||||
|
||||
if($cmd === 'wall-new' || $cmd === 'comment-new') {
|
||||
case NETWORK_MAIL :
|
||||
case NETWORK_MAIL2:
|
||||
|
||||
$it = null;
|
||||
if($cmd === 'wall-new')
|
||||
$it = $items[0];
|
||||
else {
|
||||
$r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
intval($argv[2]),
|
||||
if(get_config('system','dfrn_only'))
|
||||
break;
|
||||
// WARNING: does not currently convert to RFC2047 header encodings, etc.
|
||||
|
||||
$addr = $contact['addr'];
|
||||
if(! strlen($addr))
|
||||
break;
|
||||
|
||||
if($cmd === 'wall-new' || $cmd === 'comment-new') {
|
||||
|
||||
$it = null;
|
||||
if($cmd === 'wall-new')
|
||||
$it = $items[0];
|
||||
else {
|
||||
$r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
intval($argv[2]),
|
||||
intval($uid)
|
||||
);
|
||||
if(count($r))
|
||||
$it = $r[0];
|
||||
}
|
||||
if(! $it)
|
||||
break;
|
||||
|
||||
|
||||
$local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if(count($r))
|
||||
$it = $r[0];
|
||||
}
|
||||
if(! $it)
|
||||
break;
|
||||
if(! count($local_user))
|
||||
break;
|
||||
|
||||
$reply_to = '';
|
||||
$r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if($r1 && $r1[0]['reply_to'])
|
||||
$reply_to = $r1[0]['reply_to'];
|
||||
|
||||
$local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if(! count($local_user))
|
||||
break;
|
||||
|
||||
$reply_to = '';
|
||||
$r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
if($r1 && $r1[0]['reply_to'])
|
||||
$reply_to = $r1[0]['reply_to'];
|
||||
$subject = (($it['title']) ? email_header_encode($it['title'],'UTF-8') : t("\x28no subject\x29")) ;
|
||||
|
||||
$subject = (($it['title']) ? email_header_encode($it['title'],'UTF-8') : t("\x28no subject\x29")) ;
|
||||
// only expose our real email address to true friends
|
||||
|
||||
// only expose our real email address to true friends
|
||||
if(($contact['rel'] == CONTACT_IS_FRIEND) && (! $contact['blocked']))
|
||||
$headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . $local_user[0]['email'] . '>' . "\n";
|
||||
else
|
||||
$headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . t('noreply') . '@' . $a->get_hostname() . '>' . "\n";
|
||||
|
||||
if(($contact['rel'] == CONTACT_IS_FRIEND) && (! $contact['blocked']))
|
||||
$headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . $local_user[0]['email'] . '>' . "\n";
|
||||
else
|
||||
$headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . t('noreply') . '@' . $a->get_hostname() . '>' . "\n";
|
||||
if($reply_to)
|
||||
$headers .= 'Reply-to: ' . $reply_to . "\n";
|
||||
|
||||
if($reply_to)
|
||||
$headers .= 'Reply-to: ' . $reply_to . "\n";
|
||||
// for testing purposes: Collect exported mails
|
||||
// $file = tempnam("/tmp/friendica/", "mail-out-");
|
||||
// file_put_contents($file, json_encode($it));
|
||||
|
||||
$headers .= 'Message-Id: <' . iri2msgid($it['uri']). '>' . "\n";
|
||||
|
||||
// for testing purposes: Collect exported mails
|
||||
// $file = tempnam("/tmp/friendica/", "mail-out-");
|
||||
// file_put_contents($file, json_encode($it));
|
||||
//logger("Mail: uri: ".$it['uri']." parent-uri ".$it['parent-uri'], LOGGER_DEBUG);
|
||||
//logger("Mail: Data: ".print_r($it, true), LOGGER_DEBUG);
|
||||
//logger("Mail: Data: ".print_r($it, true), LOGGER_DATA);
|
||||
|
||||
$headers .= 'Message-Id: <' . iri2msgid($it['uri']). '>' . "\n";
|
||||
if($it['uri'] !== $it['parent-uri']) {
|
||||
$headers .= 'References: <' . iri2msgid($it['parent-uri']) . '>' . "\n";
|
||||
if(!strlen($it['title'])) {
|
||||
$r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' LIMIT 1",
|
||||
dbesc($it['parent-uri']));
|
||||
|
||||
//logger("Mail: uri: ".$it['uri']." parent-uri ".$it['parent-uri'], LOGGER_DEBUG);
|
||||
//logger("Mail: Data: ".print_r($it, true), LOGGER_DEBUG);
|
||||
//logger("Mail: Data: ".print_r($it, true), LOGGER_DATA);
|
||||
|
||||
if($it['uri'] !== $it['parent-uri']) {
|
||||
$headers .= 'References: <' . iri2msgid($it['parent-uri']) . '>' . "\n";
|
||||
if(!strlen($it['title'])) {
|
||||
$r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' LIMIT 1",
|
||||
dbesc($it['parent-uri']));
|
||||
|
||||
if(count($r) AND ($r[0]['title'] != ''))
|
||||
$subject = $r[0]['title'];
|
||||
if(count($r) AND ($r[0]['title'] != ''))
|
||||
$subject = $r[0]['title'];
|
||||
}
|
||||
if(strncasecmp($subject,'RE:',3))
|
||||
$subject = 'Re: '.$subject;
|
||||
}
|
||||
if(strncasecmp($subject,'RE:',3))
|
||||
$subject = 'Re: '.$subject;
|
||||
email_send($addr, $subject, $headers, $it);
|
||||
}
|
||||
email_send($addr, $subject, $headers, $it);
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_DIASPORA :
|
||||
if($public_message)
|
||||
$loc = 'public batch ' . $contact['batch'];
|
||||
else
|
||||
$loc = $contact['name'];
|
||||
|
||||
logger('delivery: diaspora batch deliver: ' . $loc);
|
||||
|
||||
if(get_config('system','dfrn_only') || (! get_config('system','diaspora_enabled')) || (! $normal_mode))
|
||||
break;
|
||||
|
||||
if((! $contact['pubkey']) && (! $public_message))
|
||||
case NETWORK_DIASPORA :
|
||||
if($public_message)
|
||||
$loc = 'public batch ' . $contact['batch'];
|
||||
else
|
||||
$loc = $contact['name'];
|
||||
|
||||
logger('delivery: diaspora batch deliver: ' . $loc);
|
||||
|
||||
if(get_config('system','dfrn_only') || (! get_config('system','diaspora_enabled')) || (! $normal_mode))
|
||||
break;
|
||||
|
||||
if((! $contact['pubkey']) && (! $public_message))
|
||||
break;
|
||||
|
||||
if($target_item['verb'] === ACTIVITY_DISLIKE) {
|
||||
// unsupported
|
||||
break;
|
||||
}
|
||||
elseif(($target_item['deleted']) && ($target_item['uri'] === $target_item['parent-uri'])) {
|
||||
// top-level retraction
|
||||
logger('delivery: diaspora retract: ' . $loc);
|
||||
|
||||
diaspora_send_retraction($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
elseif($target_item['uri'] !== $target_item['parent-uri']) {
|
||||
// we are the relay - send comments, likes and relayable_retractions to our conversants
|
||||
logger('delivery: diaspora relay: ' . $loc);
|
||||
|
||||
diaspora_send_relay($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
elseif(($top_level) && (! $walltowall)) {
|
||||
// currently no workable solution for sending walltowall
|
||||
logger('delivery: diaspora status: ' . $loc);
|
||||
diaspora_send_status($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
|
||||
logger('delivery: diaspora unknown mode: ' . $contact['name']);
|
||||
|
||||
break;
|
||||
|
||||
if($target_item['verb'] === ACTIVITY_DISLIKE) {
|
||||
// unsupported
|
||||
case NETWORK_FEED :
|
||||
case NETWORK_FACEBOOK :
|
||||
if(get_config('system','dfrn_only'))
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
elseif(($target_item['deleted']) && ($target_item['verb'] !== ACTIVITY_LIKE)) {
|
||||
logger('delivery: diaspora retract: ' . $loc);
|
||||
// diaspora delete,
|
||||
diaspora_send_retraction($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
elseif($target_item['parent'] != $target_item['id']) {
|
||||
|
||||
logger('delivery: diaspora relay: ' . $loc);
|
||||
|
||||
// we are the relay - send comments, likes and unlikes to our conversants
|
||||
diaspora_send_relay($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
elseif(($top_level) && (! $walltowall)) {
|
||||
// currently no workable solution for sending walltowall
|
||||
logger('delivery: diaspora status: ' . $loc);
|
||||
diaspora_send_status($target_item,$owner,$contact,$public_message);
|
||||
break;
|
||||
}
|
||||
|
||||
logger('delivery: diaspora unknown mode: ' . $contact['name']);
|
||||
|
||||
break;
|
||||
|
||||
case NETWORK_FEED :
|
||||
case NETWORK_FACEBOOK :
|
||||
if(get_config('system','dfrn_only'))
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
217
include/diaspora.php
Normal file → Executable file
217
include/diaspora.php
Normal file → Executable file
|
@ -83,6 +83,9 @@ function diaspora_dispatch($importer,$msg) {
|
|||
elseif($xmlbase->signed_retraction) {
|
||||
$ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg);
|
||||
}
|
||||
elseif($xmlbase->relayable_retraction) {
|
||||
$ret = diaspora_signed_retraction($importer,$xmlbase->relayable_retraction,$msg);
|
||||
}
|
||||
elseif($xmlbase->photo) {
|
||||
$ret = diaspora_photo($importer,$xmlbase->photo,$msg);
|
||||
}
|
||||
|
@ -569,6 +572,14 @@ function diaspora_request($importer,$xml) {
|
|||
return;
|
||||
}
|
||||
|
||||
$g = q("select def_gid from user where uid = %d limit 1",
|
||||
intval($importer['uid'])
|
||||
);
|
||||
if($g && intval($g[0]['def_gid'])) {
|
||||
require_once('include/group.php');
|
||||
group_add_member($importer['uid'],'',$contact_record['id'],$g[0]['def_gid']);
|
||||
}
|
||||
|
||||
if($importer['page-flags'] == PAGE_NORMAL) {
|
||||
|
||||
$hash = random_string() . (string) time(); // Generate a confirm_key
|
||||
|
@ -669,7 +680,7 @@ function diaspora_post($importer,$xml) {
|
|||
return;
|
||||
}
|
||||
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// we're ignoring them
|
||||
|
||||
$g = q("select * from guid where guid = '%s' limit 1",
|
||||
|
@ -836,7 +847,7 @@ function diaspora_reshare($importer,$xml) {
|
|||
$prefix = '♲ ' . $details . "\n";
|
||||
|
||||
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// we're ignoring them
|
||||
|
||||
$g = q("select * from guid where guid = '%s' limit 1",
|
||||
|
@ -940,7 +951,7 @@ function diaspora_asphoto($importer,$xml) {
|
|||
return;
|
||||
}
|
||||
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// allocate a guid on our system - we aren't fixing any collisions.
|
||||
// we're ignoring them
|
||||
|
||||
$g = q("select * from guid where guid = '%s' limit 1",
|
||||
|
@ -1594,22 +1605,28 @@ function diaspora_like($importer,$xml,$msg) {
|
|||
logger('diaspora_like: duplicate like: ' . $guid);
|
||||
return;
|
||||
}
|
||||
// Note: I don't think "Like" objects with positive = "false" are ever actually used
|
||||
// It looks like "RelayableRetractions" are used for "unlike" instead
|
||||
if($positive === 'false') {
|
||||
q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
logger('diaspora_like: received a like with positive set to "false"...ignoring');
|
||||
/* q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
intval($r[0]['id']),
|
||||
intval($importer['uid'])
|
||||
);
|
||||
);*/
|
||||
// FIXME
|
||||
// send notification via proc_run()
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Note: I don't think "Like" objects with positive = "false" are ever actually used
|
||||
// It looks like "RelayableRetractions" are used for "unlike" instead
|
||||
if($positive === 'false') {
|
||||
logger('diaspora_like: unlike received with no corresponding like');
|
||||
logger('diaspora_like: received a like with positive set to "false"');
|
||||
logger('diaspora_like: unlike received with no corresponding like...ignoring');
|
||||
return;
|
||||
}
|
||||
|
||||
$author_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
|
||||
$signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
|
||||
|
||||
$author_signature = base64_decode($author_signature);
|
||||
|
||||
|
@ -1627,20 +1644,20 @@ function diaspora_like($importer,$xml,$msg) {
|
|||
}
|
||||
}
|
||||
|
||||
if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) {
|
||||
if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) {
|
||||
logger('diaspora_like: verification failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
if($parent_author_signature) {
|
||||
|
||||
$owner_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
|
||||
//$owner_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle;
|
||||
|
||||
$parent_author_signature = base64_decode($parent_author_signature);
|
||||
|
||||
$key = $msg['key'];
|
||||
|
||||
if(! rsa_verify($owner_signed_data,$parent_author_signature,$key,'sha256')) {
|
||||
if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
|
||||
logger('diaspora_like: owner verification failed.');
|
||||
return;
|
||||
}
|
||||
|
@ -1775,38 +1792,89 @@ function diaspora_signed_retraction($importer,$xml,$msg) {
|
|||
$type = notags(unxmlify($xml->target_type));
|
||||
$sig = notags(unxmlify($xml->target_author_signature));
|
||||
|
||||
$parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : '');
|
||||
|
||||
$contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle);
|
||||
if(! $contact) {
|
||||
logger('diaspora_signed_retraction: no contact');
|
||||
return;
|
||||
}
|
||||
|
||||
// this may not yet work for comments. Need to see how the relaying works
|
||||
// and figure out who signs it.
|
||||
|
||||
|
||||
$signed_data = $guid . ';' . $type ;
|
||||
|
||||
$sig = base64_decode($sig);
|
||||
$sig_decode = base64_decode($sig);
|
||||
|
||||
$key = $msg['key'];
|
||||
if(strcasecmp($diaspora_handle,$msg['author']) == 0) {
|
||||
$person = $contact;
|
||||
$key = $msg['key'];
|
||||
}
|
||||
else {
|
||||
$person = find_diaspora_person_by_handle($diaspora_handle);
|
||||
|
||||
if(! rsa_verify($signed_data,$sig,$key,'sha256')) {
|
||||
logger('diaspora_signed_retraction: owner verification failed.' . print_r($msg,true));
|
||||
if(is_array($person) && x($person,'pubkey'))
|
||||
$key = $person['pubkey'];
|
||||
else {
|
||||
logger('diaspora_signed_retraction: unable to find author details');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(! rsa_verify($signed_data,$sig_decode,$key,'sha256')) {
|
||||
logger('diaspora_signed_retraction: retraction-owner verification failed.' . print_r($msg,true));
|
||||
return;
|
||||
}
|
||||
|
||||
if($type === 'StatusMessage') {
|
||||
if($parent_author_signature) {
|
||||
$parent_author_signature = base64_decode($parent_author_signature);
|
||||
|
||||
$key = $msg['key'];
|
||||
|
||||
if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) {
|
||||
logger('diaspora_signed_retraction: failed to verify person relaying the retraction (e.g. owner of a post relaying a retracted comment');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
|
||||
$r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1",
|
||||
dbesc($guid),
|
||||
intval($importer['uid'])
|
||||
);
|
||||
if(count($r)) {
|
||||
if(link_compare($r[0]['author-link'],$contact['url'])) {
|
||||
q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d limit 1",
|
||||
q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d limit 1",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
|
||||
// Now check if the retraction needs to be relayed by us
|
||||
//
|
||||
// The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
|
||||
// return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
|
||||
// The only item with `parent` and `id` as the parent id is the parent item.
|
||||
$p = q("select origin from item where parent = %d and id = %d limit 1",
|
||||
$r[0]['parent'],
|
||||
$r[0]['parent']
|
||||
);
|
||||
if(count($p)) {
|
||||
if(($p[0]['origin']) && (! $parent_author_signature)) {
|
||||
q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
|
||||
$r[0]['id'],
|
||||
dbesc($signed_data),
|
||||
dbesc($sig),
|
||||
dbesc($diaspora_handle)
|
||||
);
|
||||
|
||||
// the existence of parent_author_signature would have meant the parent_author or owner
|
||||
// is already relaying.
|
||||
logger('diaspora_signed_retraction: relaying relayable_retraction');
|
||||
|
||||
proc_run('php','include/notifier.php','relayable_retraction',$r[0]['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2047,8 +2115,12 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
|
|||
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
|
||||
$theiraddr = $contact['addr'];
|
||||
|
||||
$p = q("select guid from item where parent = %d limit 1",
|
||||
$item['parent']
|
||||
// The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
|
||||
// return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
|
||||
// The only item with `parent` and `id` as the parent id is the parent item.
|
||||
$p = q("select guid from item where parent = %d and id = %d limit 1",
|
||||
intval($item['parent']),
|
||||
intval($item['parent'])
|
||||
);
|
||||
if(count($p))
|
||||
$parent_guid = $p[0]['guid'];
|
||||
|
@ -2059,7 +2131,11 @@ function diaspora_send_followup($item,$owner,$contact,$public_batch = false) {
|
|||
$tpl = get_markup_template('diaspora_like.tpl');
|
||||
$like = true;
|
||||
$target_type = 'Post';
|
||||
$positive = (($item['deleted']) ? 'false' : 'true');
|
||||
// $positive = (($item['deleted']) ? 'false' : 'true');
|
||||
$positive = 'true';
|
||||
|
||||
if(($item['deleted']))
|
||||
logger('diaspora_send_followup: received deleted "like". Those should go to diaspora_send_retraction');
|
||||
}
|
||||
else {
|
||||
$tpl = get_markup_template('diaspora_comment.tpl');
|
||||
|
@ -2099,41 +2175,51 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
|
|||
|
||||
|
||||
$a = get_app();
|
||||
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
|
||||
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
|
||||
$theiraddr = $contact['addr'];
|
||||
|
||||
|
||||
$p = q("select guid from item where parent = %d limit 1",
|
||||
$item['parent']
|
||||
// The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
|
||||
// return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
|
||||
// The only item with `parent` and `id` as the parent id is the parent item.
|
||||
$p = q("select guid from item where parent = %d and id = %d limit 1",
|
||||
intval($item['parent']),
|
||||
intval($item['parent'])
|
||||
);
|
||||
if(count($p))
|
||||
$parent_guid = $p[0]['guid'];
|
||||
else
|
||||
return;
|
||||
|
||||
if($item['verb'] === ACTIVITY_LIKE) {
|
||||
$like = false;
|
||||
$relay_retract = false;
|
||||
$sql_sign_id = 'iid';
|
||||
if( $item['deleted']) {
|
||||
$tpl = get_markup_template('diaspora_relayable_retraction.tpl');
|
||||
$relay_retract = true;
|
||||
$sql_sign_id = 'retract_iid';
|
||||
$target_type = ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
|
||||
}
|
||||
elseif($item['verb'] === ACTIVITY_LIKE) {
|
||||
$tpl = get_markup_template('diaspora_like_relay.tpl');
|
||||
$like = true;
|
||||
$target_type = 'Post';
|
||||
$positive = (($item['deleted']) ? 'false' : 'true');
|
||||
// $positive = (($item['deleted']) ? 'false' : 'true');
|
||||
$positive = 'true';
|
||||
}
|
||||
else {
|
||||
$tpl = get_markup_template('diaspora_comment_relay.tpl');
|
||||
$like = false;
|
||||
}
|
||||
|
||||
$body = $item['body'];
|
||||
|
||||
$text = html_entity_decode(bb2diaspora($body));
|
||||
|
||||
// fetch the original signature if somebody sent the post to us to relay
|
||||
// If we are relaying for a reply originating on our own account, there wasn't a 'send to relay'
|
||||
// action. It wasn't needed. In that case create the original signature and the
|
||||
// owner (parent author) signature
|
||||
// comments from other networks will be relayed under our name, with a brief
|
||||
// preamble to describe what's happening and noting the real author
|
||||
|
||||
$r = q("select * from sign where iid = %d limit 1",
|
||||
// fetch the original signature if the relayable was created by a Diaspora
|
||||
// or DFRN user. Relayables for other networks are not supported.
|
||||
|
||||
$r = q("select * from sign where " . $sql_sign_id . " = %d limit 1",
|
||||
intval($item['id'])
|
||||
);
|
||||
if(count($r)) {
|
||||
|
@ -2144,6 +2230,12 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
|
|||
}
|
||||
else {
|
||||
|
||||
// Author signature information (for likes, comments, and retractions of likes or comments,
|
||||
// whether from Diaspora or Friendica) must be placed in the `sign` table before this
|
||||
// function is called
|
||||
logger('diaspora_send_relay: original author signature not found, cannot send relayable');
|
||||
return;
|
||||
/*
|
||||
$itemcontact = q("select * from contact where `id` = %d limit 1",
|
||||
intval($item['contact-id'])
|
||||
);
|
||||
|
@ -2152,29 +2244,40 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
|
|||
$prefix = sprintf( t('[Relayed] Comment authored by %s from network %s'),
|
||||
'['. $item['author-name'] . ']' . '(' . $item['author-link'] . ')',
|
||||
network_to_name($itemcontact['network'])) . "\n";
|
||||
// "$body" was assigned to "$text" above. It isn't used after that, so I don't think
|
||||
// the following change will do anything
|
||||
$body = $prefix . $body;
|
||||
|
||||
// I think this comment will fail upon reaching Diaspora, because "$signed_text" is not defined
|
||||
}
|
||||
}
|
||||
else {
|
||||
// I'm confused about this "else." Since it sets "$handle = $myaddr," it seems like it should be for the case
|
||||
// where the top-level post owner commented on his own post, i.e. "$itemcontact[0]['self']" is true. But it's
|
||||
// positioned to be for the case where "count($itemcontact)" is 0.
|
||||
|
||||
$handle = $myaddr;
|
||||
|
||||
if($like)
|
||||
$signed_text = $item['guid'] . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $myaddr;
|
||||
$signed_text = $item['guid'] . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $handle;
|
||||
elseif($relay_retract)
|
||||
$signed_text = $item['guid'] . ';' . $target_type;
|
||||
else
|
||||
$signed_text = $item['guid'] . ';' . $parent_guid . ';' . $text . ';' . $myaddr;
|
||||
$signed_text = $item['guid'] . ';' . $parent_guid . ';' . $text . ';' . $handle;
|
||||
|
||||
$authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
|
||||
|
||||
q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
|
||||
q("insert into sign (`" . $sql_sign_id . "`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
|
||||
intval($item['id']),
|
||||
dbesc($signed_text),
|
||||
dbesc(base64_encode($authorsig)),
|
||||
dbesc($myaddr)
|
||||
dbesc($authorsig),
|
||||
dbesc($handle)
|
||||
);
|
||||
$handle = $myaddr;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// sign it
|
||||
// sign it with the top-level owner's signature
|
||||
|
||||
$parentauthorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'));
|
||||
|
||||
|
@ -2182,14 +2285,15 @@ function diaspora_send_relay($item,$owner,$contact,$public_batch = false) {
|
|||
'$guid' => xmlify($item['guid']),
|
||||
'$parent_guid' => xmlify($parent_guid),
|
||||
'$target_type' =>xmlify($target_type),
|
||||
'$authorsig' => xmlify($orig_sign['signature']),
|
||||
'$authorsig' => xmlify($authorsig),
|
||||
'$parentsig' => xmlify($parentauthorsig),
|
||||
'$body' => xmlify($text),
|
||||
'$positive' => xmlify($positive),
|
||||
'$handle' => xmlify($handle)
|
||||
));
|
||||
|
||||
logger('diaspora_relay_comment: base message: ' . $msg, LOGGER_DATA);
|
||||
logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA);
|
||||
|
||||
|
||||
$slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
|
||||
|
||||
|
@ -2204,14 +2308,25 @@ function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) {
|
|||
$a = get_app();
|
||||
$myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
|
||||
|
||||
$signed_text = $item['guid'] . ';' . 'StatusMessage';
|
||||
// Check whether the retraction is for a top-level post or whether it's a relayable
|
||||
if( $item['uri'] !== $item['parent-uri'] ) {
|
||||
|
||||
$tpl = get_markup_template('diaspora_relay_retraction.tpl');
|
||||
$target_type = (($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
|
||||
}
|
||||
else {
|
||||
|
||||
$tpl = get_markup_template('diaspora_signed_retract.tpl');
|
||||
$target_type = 'StatusMessage';
|
||||
}
|
||||
|
||||
$signed_text = $item['guid'] . ';' . $target_type;
|
||||
|
||||
$tpl = get_markup_template('diaspora_signed_retract.tpl');
|
||||
$msg = replace_macros($tpl, array(
|
||||
'$guid' => $item['guid'],
|
||||
'$type' => 'StatusMessage',
|
||||
'$handle' => $myaddr,
|
||||
'$signature' => base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'))
|
||||
'$guid' => xmlify($item['guid']),
|
||||
'$type' => xmlify($target_type),
|
||||
'$handle' => xmlify($myaddr),
|
||||
'$signature' => xmlify(base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')))
|
||||
));
|
||||
|
||||
$slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)));
|
||||
|
@ -2335,3 +2450,5 @@ function diaspora_transmit($owner,$contact,$slap,$public_batch) {
|
|||
|
||||
return(($return_code) ? $return_code : (-1));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@ function directory_run($argv, $argc){
|
|||
|
||||
load_config('system');
|
||||
|
||||
load_hooks();
|
||||
|
||||
|
||||
$a->set_baseurl(get_config('system','url'));
|
||||
|
||||
$dir = get_config('system','directory_submit_url');
|
||||
|
@ -31,7 +34,12 @@ function directory_run($argv, $argc){
|
|||
if(! strlen($dir))
|
||||
return;
|
||||
|
||||
fetch_url($dir . '?url=' . bin2hex($argv[1]));
|
||||
$arr = array('url' => $argv[1]);
|
||||
|
||||
call_hooks('globaldir_update', $arr);
|
||||
|
||||
if(strlen($arr['url']))
|
||||
fetch_url($dir . '?url=' . bin2hex($arr['url']));
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -13,13 +13,16 @@ function notification($params) {
|
|||
|
||||
$banner = t('Friendica Notification');
|
||||
$product = FRIENDICA_PLATFORM;
|
||||
$siteurl = z_path();
|
||||
$siteurl = $a->get_baseurl(true);
|
||||
$thanks = t('Thank You,');
|
||||
$sitename = get_config('config','sitename');
|
||||
$site_admin = sprintf( t('%s Administrator'), $sitename);
|
||||
|
||||
$sender_name = $product;
|
||||
$hostname = $a->get_hostname();
|
||||
if(strpos($hostname,':'))
|
||||
$hostname = substr($hostname,0,strpos($hostname,':'));
|
||||
|
||||
$sender_email = t('noreply') . '@' . $hostname;
|
||||
$additional_mail_header = "";
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ function format_event_html($ev) {
|
|||
return $o;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
function parse_event($h) {
|
||||
|
||||
require_once('include/Scrape.php');
|
||||
|
@ -108,7 +108,7 @@ function parse_event($h) {
|
|||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function format_event_bbcode($ev) {
|
||||
|
||||
|
@ -162,7 +162,6 @@ function bbtoevent($s) {
|
|||
$match = '';
|
||||
if(preg_match("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is",$s,$match))
|
||||
$ev['adjust'] = $match[1];
|
||||
$match = '';
|
||||
$ev['nofinish'] = (((x($ev, 'start') && $ev['start']) && (!x($ev, 'finish') || !$ev['finish'])) ? 1 : 0);
|
||||
return $ev;
|
||||
|
||||
|
@ -294,10 +293,14 @@ function event_store($arr) {
|
|||
intval($arr['uid'])
|
||||
);
|
||||
|
||||
return $r[0]['id'];
|
||||
$item_id = $r[0]['id'];
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
$item_id = 0;
|
||||
|
||||
call_hooks("event_updated", $arr['id']);
|
||||
|
||||
return $item_id;
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -361,7 +364,7 @@ function event_store($arr) {
|
|||
$item_arr['body'] = format_event_bbcode($event);
|
||||
|
||||
|
||||
$item_arr['object'] = '<object><type>' . xmlify(ACTIVITY_OBJ_EVENT) . '</type><title></title><id>' . xmlify($uri) . '</id>';
|
||||
$item_arr['object'] = '<object><type>' . xmlify(ACTIVITY_OBJ_EVENT) . '</type><title></title><id>' . xmlify($arr['uri']) . '</id>';
|
||||
$item_arr['object'] .= '<content>' . xmlify(format_event_bbcode($event)) . '</content>';
|
||||
$item_arr['object'] .= '</object>' . "\n";
|
||||
|
||||
|
@ -383,6 +386,8 @@ function event_store($arr) {
|
|||
);
|
||||
}
|
||||
|
||||
call_hooks("event_created", $event['id']);
|
||||
|
||||
return $item_id;
|
||||
}
|
||||
}
|
||||
|
|
243
include/follow.php
Normal file
243
include/follow.php
Normal file
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
|
||||
//
|
||||
// Takes a $uid and a url/handle and adds a new contact
|
||||
// Currently if the contact is DFRN, interactive needs to be true, to redirect to the
|
||||
// dfrn_request page.
|
||||
|
||||
// Otherwise this can be used to bulk add statusnet contacts, twitter contacts, etc.
|
||||
// Returns an array
|
||||
// $return['success'] boolean true if successful
|
||||
// $return['message'] error text if success is false.
|
||||
|
||||
|
||||
|
||||
function new_contact($uid,$url,$interactive = false) {
|
||||
|
||||
$result = array('success' => false,'message' => '');
|
||||
|
||||
$a = get_app();
|
||||
|
||||
// remove ajax junk, e.g. Twitter
|
||||
|
||||
$url = str_replace('/#!/','/',$url);
|
||||
|
||||
if(! allowed_url($url)) {
|
||||
$result['message'] = t('Disallowed profile URL.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
if(! $url) {
|
||||
$result['message'] = t('Connect URL missing.');
|
||||
return $result;
|
||||
}
|
||||
|
||||
$arr = array('url' => $url, 'contact' => array());
|
||||
|
||||
call_hooks('follow', $arr);
|
||||
|
||||
if(x($arr['contact'],'name'))
|
||||
$ret = $arr['contact'];
|
||||
else
|
||||
$ret = probe_url($url);
|
||||
|
||||
if($ret['network'] === NETWORK_DFRN) {
|
||||
if($interactive) {
|
||||
if(strlen($a->path))
|
||||
$myaddr = bin2hex($a->get_baseurl() . '/profile/' . $a->user['nickname']);
|
||||
else
|
||||
$myaddr = bin2hex($a->user['nickname'] . '@' . $a->get_hostname());
|
||||
|
||||
goaway($ret['request'] . "&addr=$myaddr");
|
||||
|
||||
// NOTREACHED
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(get_config('system','dfrn_only')) {
|
||||
$result['message'] = t('This site is not configured to allow communications with other networks.') . EOL;
|
||||
$result['message'] != t('No compatible communication protocols or feeds were discovered.') . EOL;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// This extra param just confuses things, remove it
|
||||
if($ret['network'] === NETWORK_DIASPORA)
|
||||
$ret['url'] = str_replace('?absolute=true','',$ret['url']);
|
||||
|
||||
|
||||
// do we have enough information?
|
||||
|
||||
if(! ((x($ret,'name')) && (x($ret,'poll')) && ((x($ret,'url')) || (x($ret,'addr'))))) {
|
||||
$result['message'] .= t('The profile address specified does not provide adequate information.') . EOL;
|
||||
if(! x($ret,'poll'))
|
||||
$result['message'] .= t('No compatible communication protocols or feeds were discovered.') . EOL;
|
||||
if(! x($ret,'name'))
|
||||
$result['message'] .= t('An author or name was not found.') . EOL;
|
||||
if(! x($ret,'url'))
|
||||
$result['message'] .= t('No browser URL could be matched to this address.') . EOL;
|
||||
if(strpos($url,'@') !== false) {
|
||||
$result['message'] .= t('Unable to match @-style Identity Address with a known protocol or email contact.') . EOL;
|
||||
$result['message'] .= t('Use mailto: in front of address to force email check.') . EOL;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
if($ret['network'] === NETWORK_OSTATUS && get_config('system','ostatus_disabled')) {
|
||||
$result['message'] .= t('The profile address specified belongs to a network which has been disabled on this site.') . EOL;
|
||||
$ret['notify'] = '';
|
||||
}
|
||||
|
||||
if(! $ret['notify']) {
|
||||
$result['message'] .= t('Limited profile. This person will be unable to receive direct/personal notifications from you.') . EOL;
|
||||
}
|
||||
|
||||
$writeable = ((($ret['network'] === NETWORK_OSTATUS) && ($ret['notify'])) ? 1 : 0);
|
||||
$hidden = (($ret['network'] === NETWORK_MAIL) ? 1 : 0);
|
||||
|
||||
if($ret['network'] === NETWORK_MAIL) {
|
||||
$writeable = 1;
|
||||
|
||||
}
|
||||
if($ret['network'] === NETWORK_DIASPORA)
|
||||
$writeable = 1;
|
||||
|
||||
// check if we already have a contact
|
||||
// the poll url is more reliable than the profile url, as we may have
|
||||
// indirect links or webfinger links
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($ret['poll'])
|
||||
);
|
||||
|
||||
|
||||
if(count($r)) {
|
||||
// update contact
|
||||
if($r[0]['rel'] == CONTACT_IS_FOLLOWER || ($network === NETWORK_DIASPORA && $r[0]['rel'] == CONTACT_IS_SHARING)) {
|
||||
q("UPDATE `contact` SET `rel` = %d , `readonly` = 0 WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
intval(CONTACT_IS_FRIEND),
|
||||
intval($r[0]['id']),
|
||||
intval($uid)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$new_relation = (($ret['network'] === NETWORK_MAIL) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING);
|
||||
if($ret['network'] === NETWORK_DIASPORA)
|
||||
$new_relation = CONTACT_IS_FOLLOWER;
|
||||
|
||||
// create contact record
|
||||
$r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `batch`, `notify`, `poll`, `poco`, `name`, `nick`, `photo`, `network`, `pubkey`, `rel`, `priority`,
|
||||
`writable`, `hidden`, `blocked`, `readonly`, `pending` )
|
||||
VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, 0, 0, 0 ) ",
|
||||
intval($uid),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($ret['url']),
|
||||
dbesc(normalise_link($ret['url'])),
|
||||
dbesc($ret['addr']),
|
||||
dbesc($ret['alias']),
|
||||
dbesc($ret['batch']),
|
||||
dbesc($ret['notify']),
|
||||
dbesc($ret['poll']),
|
||||
dbesc($ret['poco']),
|
||||
dbesc($ret['name']),
|
||||
dbesc($ret['nick']),
|
||||
dbesc($ret['photo']),
|
||||
dbesc($ret['network']),
|
||||
dbesc($ret['pubkey']),
|
||||
intval($new_relation),
|
||||
intval($ret['priority']),
|
||||
intval($writeable),
|
||||
intval($hidden)
|
||||
);
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
|
||||
dbesc($ret['url']),
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if(! count($r)) {
|
||||
$result['message'] .= t('Unable to retrieve contact information.') . EOL;
|
||||
return $result;
|
||||
}
|
||||
|
||||
$contact = $r[0];
|
||||
$contact_id = $r[0]['id'];
|
||||
|
||||
|
||||
$g = q("select def_gid from user where uid = %d limit 1",
|
||||
intval($uid)
|
||||
);
|
||||
if($g && intval($g[0]['def_gid'])) {
|
||||
require_once('include/group.php');
|
||||
group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
|
||||
}
|
||||
|
||||
require_once("Photo.php");
|
||||
|
||||
$photos = import_profile_photo($ret['photo'],$uid,$contact_id);
|
||||
|
||||
$r = q("UPDATE `contact` SET `photo` = '%s',
|
||||
`thumb` = '%s',
|
||||
`micro` = '%s',
|
||||
`name-date` = '%s',
|
||||
`uri-date` = '%s',
|
||||
`avatar-date` = '%s'
|
||||
WHERE `id` = %d LIMIT 1
|
||||
",
|
||||
dbesc($photos[0]),
|
||||
dbesc($photos[1]),
|
||||
dbesc($photos[2]),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
intval($contact_id)
|
||||
);
|
||||
|
||||
|
||||
// pull feed and consume it, which should subscribe to the hub.
|
||||
|
||||
proc_run('php',"include/poller.php","$contact_id");
|
||||
|
||||
// create a follow slap
|
||||
|
||||
$tpl = get_markup_template('follow_slap.tpl');
|
||||
$slap = replace_macros($tpl, array(
|
||||
'$name' => $a->user['username'],
|
||||
'$profile_page' => $a->get_baseurl() . '/profile/' . $a->user['nickname'],
|
||||
'$photo' => $a->contact['photo'],
|
||||
'$thumb' => $a->contact['thumb'],
|
||||
'$published' => datetime_convert('UTC','UTC', 'now', ATOM_TIME),
|
||||
'$item_id' => 'urn:X-dfrn:' . $a->get_hostname() . ':follow:' . random_string(),
|
||||
'$title' => '',
|
||||
'$type' => 'text',
|
||||
'$content' => t('following'),
|
||||
'$nick' => $a->user['nickname'],
|
||||
'$verb' => ACTIVITY_FOLLOW,
|
||||
'$ostat_follow' => ''
|
||||
));
|
||||
|
||||
$r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
|
||||
WHERE `user`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid)
|
||||
);
|
||||
|
||||
if(count($r)) {
|
||||
if(($contact['network'] == NETWORK_OSTATUS) && (strlen($contact['notify']))) {
|
||||
require_once('include/salmon.php');
|
||||
slapper($r[0],$contact['notify'],$slap);
|
||||
}
|
||||
if($contact['network'] == NETWORK_DIASPORA) {
|
||||
require_once('include/diaspora.php');
|
||||
$ret = diaspora_share($a->user,$contact);
|
||||
logger('mod_follow: diaspora_share returns: ' . $ret);
|
||||
}
|
||||
}
|
||||
|
||||
$result['success'] = true;
|
||||
return $result;
|
||||
}
|
|
@ -97,8 +97,9 @@ function group_rmv_member($uid,$name,$member) {
|
|||
}
|
||||
|
||||
|
||||
function group_add_member($uid,$name,$member) {
|
||||
$gid = group_byname($uid,$name);
|
||||
function group_add_member($uid,$name,$member,$gid = 0) {
|
||||
if(! $gid)
|
||||
$gid = group_byname($uid,$name);
|
||||
if((! $gid) || (! $uid) || (! $member))
|
||||
return false;
|
||||
|
||||
|
@ -154,6 +155,32 @@ function group_public_members($gid) {
|
|||
}
|
||||
|
||||
|
||||
function mini_group_select($uid,$gid = 0) {
|
||||
|
||||
$grps = array();
|
||||
$o = '';
|
||||
|
||||
$r = q("SELECT * FROM `group` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `name` ASC",
|
||||
intval($uid)
|
||||
);
|
||||
$grps[] = array('name' => '', 'id' => '0', 'selected' => '');
|
||||
if(count($r)) {
|
||||
foreach($r as $rr) {
|
||||
$grps[] = array('name' => $rr['name'], 'id' => $rr['id'], 'selected' => (($gid == $rr['id']) ? 'true' : ''));
|
||||
}
|
||||
|
||||
}
|
||||
logger('groups: ' . print_r($grps,true));
|
||||
|
||||
$o = replace_macros(get_markup_template('group_selection.tpl'), array(
|
||||
'$label' => t('Default privacy group for new contacts'),
|
||||
'$groups' => $grps
|
||||
));
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function group_side($every="contacts",$each="group",$edit = false, $group_id = 0, $cid = 0) {
|
||||
|
||||
|
|
174
include/items.php
Normal file → Executable file
174
include/items.php
Normal file → Executable file
|
@ -180,6 +180,10 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0)
|
|||
|
||||
foreach($items as $item) {
|
||||
|
||||
// prevent private email from leaking.
|
||||
if($item['network'] === NETWORK_MAIL)
|
||||
continue;
|
||||
|
||||
// public feeds get html, our own nodes use bbcode
|
||||
|
||||
if($public_feed) {
|
||||
|
@ -959,6 +963,8 @@ function tag_deliver($uid,$item_id) {
|
|||
return;
|
||||
|
||||
$community_page = (($u[0]['page-flags'] == PAGE_COMMUNITY) ? true : false);
|
||||
$prvgroup = (($u[0]['page-flags'] == PAGE_PRVGROUP) ? true : false);
|
||||
|
||||
|
||||
$i = q("select * from item where id = %d and uid = %d limit 1",
|
||||
intval($item_id),
|
||||
|
@ -1008,9 +1014,10 @@ function tag_deliver($uid,$item_id) {
|
|||
'otype' => 'item'
|
||||
));
|
||||
|
||||
if(! $community_page)
|
||||
if((! $community_page) && (! $prvgroup))
|
||||
return;
|
||||
|
||||
|
||||
// tgroup delivery - setup a second delivery chain
|
||||
// prevent delivery looping - only proceed
|
||||
// if the message originated elsewhere and is a top-level post
|
||||
|
@ -1031,8 +1038,11 @@ function tag_deliver($uid,$item_id) {
|
|||
|
||||
$private = ($u[0]['allow_cid'] || $u[0]['allow_gid'] || $u[0]['deny_cid'] || $u[0]['deny_gid']) ? 1 : 0;
|
||||
|
||||
q("update item set wall = 1, origin = 1, forum_mode = 1, `owner-name` = '%s', `owner-link` = '%s', `owner-avatar` = '%s',
|
||||
$forum_mode = (($prvgroup) ? 2 : 1);
|
||||
|
||||
q("update item set wall = 1, origin = 1, forum_mode = %d, `owner-name` = '%s', `owner-link` = '%s', `owner-avatar` = '%s',
|
||||
`private` = %d, `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' where id = %d limit 1",
|
||||
intval($forum_mode),
|
||||
dbesc($c[0]['name']),
|
||||
dbesc($c[0]['url']),
|
||||
dbesc($c[0]['thumb']),
|
||||
|
@ -1057,9 +1067,6 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
|
|||
|
||||
$a = get_app();
|
||||
|
||||
// if((! strlen($contact['issued-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY)))
|
||||
// return 3;
|
||||
|
||||
$idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
|
||||
|
||||
if($contact['duplex'] && $contact['dfrn-id'])
|
||||
|
@ -1124,6 +1131,9 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
|
|||
$rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
|
||||
$page = (($owner['page-flags'] == PAGE_COMMUNITY) ? 1 : 0);
|
||||
|
||||
if($owner['page-flags'] == PAGE_PRVGROUP)
|
||||
$page = 2;
|
||||
|
||||
$final_dfrn_id = '';
|
||||
|
||||
if($perm) {
|
||||
|
@ -1177,7 +1187,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
|
|||
$postvars['ssl_policy'] = $ssl_policy;
|
||||
|
||||
if($page)
|
||||
$postvars['page'] = '1';
|
||||
$postvars['page'] = $page;
|
||||
|
||||
if($rino && $rino_allowed && (! $dissolve)) {
|
||||
$key = substr(random_string(),0,16);
|
||||
|
@ -2194,7 +2204,7 @@ function local_delivery($importer,$data) {
|
|||
if($is_reply) {
|
||||
$community = false;
|
||||
|
||||
if($importer['page-flags'] == PAGE_COMMUNITY) {
|
||||
if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) {
|
||||
$sql_extra = '';
|
||||
$community = true;
|
||||
logger('local_delivery: possible community reply');
|
||||
|
@ -2221,8 +2231,8 @@ function local_delivery($importer,$data) {
|
|||
if($r && count($r))
|
||||
$is_a_remote_comment = true;
|
||||
|
||||
// Does this have the characteristics of a community comment?
|
||||
// If it's a reply to a wall post on a community page it's a
|
||||
// Does this have the characteristics of a community or private group comment?
|
||||
// If it's a reply to a wall post on a community/prvgroup page it's a
|
||||
// valid community comment. Also forum_mode makes it valid for sure.
|
||||
// If neither, it's not.
|
||||
|
||||
|
@ -2711,6 +2721,12 @@ function new_follower($importer,$contact,$datarray,$item,$sharing = false) {
|
|||
);
|
||||
$a = get_app();
|
||||
if(count($r)) {
|
||||
|
||||
if(intval($r[0]['def_gid'])) {
|
||||
require_once('include/group.php');
|
||||
group_add_member($r[0]['uid'],'',$contact_record['id'],$r[0]['def_gid']);
|
||||
}
|
||||
|
||||
if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) {
|
||||
$email_tpl = get_intltext_template('follow_notify_eml.tpl');
|
||||
$email = replace_macros($email_tpl, array(
|
||||
|
@ -2820,7 +2836,7 @@ function atom_author($tag,$name,$uri,$h,$w,$photo) {
|
|||
return $o;
|
||||
}
|
||||
|
||||
function atom_entry($item,$type,$author,$owner,$comment = false) {
|
||||
function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
|
||||
|
||||
$a = get_app();
|
||||
|
||||
|
@ -2832,7 +2848,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false) {
|
|||
|
||||
|
||||
if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid'])
|
||||
$body = fix_private_photos($item['body'],$owner['uid']);
|
||||
$body = fix_private_photos($item['body'],$owner['uid'],$item,$cid);
|
||||
else
|
||||
$body = $item['body'];
|
||||
|
||||
|
@ -2915,14 +2931,17 @@ function atom_entry($item,$type,$author,$owner,$comment = false) {
|
|||
return $o;
|
||||
}
|
||||
|
||||
function fix_private_photos($s,$uid) {
|
||||
function fix_private_photos($s,$uid, $item = null, $cid = 0) {
|
||||
$a = get_app();
|
||||
logger('fix_private_photos');
|
||||
|
||||
if(preg_match("/\[img\](.*?)\[\/img\]/is",$s,$matches)) {
|
||||
$image = $matches[1];
|
||||
logger('fix_private_photos: found photo ' . $image);
|
||||
if(stristr($image ,$a->get_baseurl() . '/photo/')) {
|
||||
logger('fix_private_photos', LOGGER_DEBUG);
|
||||
$site = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://'));
|
||||
|
||||
if(preg_match("/\[img(.*?)\](.*?)\[\/img\]/is",$s,$matches)) {
|
||||
$image = $matches[2];
|
||||
logger('fix_private_photos: found photo ' . $image, LOGGER_DEBUG);
|
||||
if(stristr($image , $site . '/photo/')) {
|
||||
$replace = false;
|
||||
$i = basename($image);
|
||||
$i = str_replace('.jpg','',$i);
|
||||
$x = strpos($i,'-');
|
||||
|
@ -2935,17 +2954,86 @@ function fix_private_photos($s,$uid) {
|
|||
intval($uid)
|
||||
);
|
||||
if(count($r)) {
|
||||
logger('replacing photo');
|
||||
$s = str_replace($image, 'data:image/jpg;base64,' . base64_encode($r[0]['data']), $s);
|
||||
|
||||
// Check to see if we should replace this photo link with an embedded image
|
||||
// 1. No need to do so if the photo is public
|
||||
// 2. If there's a contact-id provided, see if they're in the access list
|
||||
// for the photo. If so, embed it.
|
||||
// 3. Otherwise, if we have an item, see if the item permissions match the photo
|
||||
// permissions, regardless of order but first check to see if they're an exact
|
||||
// match to save some processing overhead.
|
||||
|
||||
// Currently we only embed one private photo per message so as not to hit import
|
||||
// size limits at the receiving end.
|
||||
|
||||
// To embed multiples, we would need to parse out the embedded photos on message
|
||||
// receipt and limit size based only on the text component. Would also need to
|
||||
// ignore all photos during bbcode translation and item localisation, as these
|
||||
// will hit internal regex backtrace limits.
|
||||
|
||||
if(has_permissions($r[0])) {
|
||||
if($cid) {
|
||||
$recips = enumerate_permissions($r[0]);
|
||||
if(in_array($cid, $recips)) {
|
||||
$replace = true;
|
||||
}
|
||||
}
|
||||
elseif($item) {
|
||||
if(compare_permissions($item,$r[0]))
|
||||
$replace = true;
|
||||
}
|
||||
}
|
||||
if($replace) {
|
||||
logger('fix_private_photos: replacing photo', LOGGER_DEBUG);
|
||||
$s = str_replace($image, 'data:image/jpg;base64,' . base64_encode($r[0]['data']), $s);
|
||||
logger('fix_private_photos: replaced: ' . $s, LOGGER_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger('fix_private_photos: replaced: ' . $s, LOGGER_DATA);
|
||||
}
|
||||
}
|
||||
return($s);
|
||||
}
|
||||
|
||||
|
||||
function has_permissions($obj) {
|
||||
if(($obj['allow_cid'] != '') || ($obj['allow_gid'] != '') || ($obj['deny_cid'] != '') || ($obj['deny_gid'] != ''))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function compare_permissions($obj1,$obj2) {
|
||||
// first part is easy. Check that these are exactly the same.
|
||||
if(($obj1['allow_cid'] == $obj2['allow_cid'])
|
||||
&& ($obj1['allow_gid'] == $obj2['allow_gid'])
|
||||
&& ($obj1['deny_cid'] == $obj2['deny_cid'])
|
||||
&& ($obj1['deny_gid'] == $obj2['deny_gid']))
|
||||
return true;
|
||||
|
||||
// This is harder. Parse all the permissions and compare the resulting set.
|
||||
|
||||
$recipients1 = enumerate_permissions($obj1);
|
||||
$recipients2 = enumerate_permissions($obj2);
|
||||
sort($recipients1);
|
||||
sort($recipients2);
|
||||
if($recipients1 == $recipients2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns an array of contact-ids that are allowed to see this object
|
||||
|
||||
function enumerate_permissions($obj) {
|
||||
require_once('include/group.php');
|
||||
$allow_people = expand_acl($obj['allow_cid']);
|
||||
$allow_groups = expand_groups(expand_acl($obj['allow_gid']));
|
||||
$deny_people = expand_acl($obj['deny_cid']);
|
||||
$deny_groups = expand_groups(expand_acl($obj['deny_gid']));
|
||||
$recipients = array_unique(array_merge($allow_people,$allow_groups));
|
||||
$deny = array_unique(array_merge($deny_people,$deny_groups));
|
||||
$recipients = array_diff($recipients,$deny);
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
function item_getfeedtags($item) {
|
||||
$ret = array();
|
||||
|
@ -2992,13 +3080,20 @@ function item_getfeedattach($item) {
|
|||
|
||||
function item_expire($uid,$days) {
|
||||
|
||||
if((! $uid) || (! $days))
|
||||
if((! $uid) || ($days < 1))
|
||||
return;
|
||||
|
||||
// $expire_network_only = save your own wall posts
|
||||
// and just expire conversations started by others
|
||||
|
||||
$expire_network_only = get_pconfig($uid,'expire','network_only');
|
||||
$sql_extra = ((intval($expire_network_only)) ? " AND wall = 0 " : "");
|
||||
|
||||
$r = q("SELECT * FROM `item`
|
||||
WHERE `uid` = %d
|
||||
AND `created` < UTC_TIMESTAMP() - INTERVAL %d DAY
|
||||
AND `id` = `parent`
|
||||
$sql_extra
|
||||
AND `deleted` = 0",
|
||||
intval($uid),
|
||||
intval($days)
|
||||
|
@ -3183,7 +3278,42 @@ function drop_item($id,$interactive = true) {
|
|||
q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
|
||||
intval($r[0]['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a relayable_retraction signature for Diaspora. Note that we can't add a target_author_signature
|
||||
// if the comment was deleted by a remote user. That should be ok, because if a remote user is deleting
|
||||
// the comment, that means we're the home of the post, and Diaspora will only
|
||||
// check the parent_author_signature of retractions that it doesn't have to relay further
|
||||
//
|
||||
// I don't think this function gets called for an "unlike," but I'll check anyway
|
||||
$signed_text = $item['guid'] . ';' . ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
|
||||
|
||||
if(local_user() == $item['uid']) {
|
||||
|
||||
$handle = $a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3);
|
||||
$authorsig = base64_encode(rsa_sign($signed_text,$a->user['prvkey'],'sha256'));
|
||||
}
|
||||
else {
|
||||
$r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1",
|
||||
$item['contact-id']
|
||||
);
|
||||
if(count($r)) {
|
||||
// The below handle only works for NETWORK_DFRN. I think that's ok, because this function
|
||||
// only handles DFRN deletes
|
||||
$handle_baseurl_start = strpos($r['url'],'://') + 3;
|
||||
$handle_baseurl_length = strpos($r['url'],'/profile') - $handle_baseurl_start;
|
||||
$handle = $r['nick'] . '@' . substr($r['url'], $handle_baseurl_start, $handle_baseurl_length);
|
||||
$authorsig = '';
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($handle))
|
||||
q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
|
||||
intval($item['id']),
|
||||
dbesc($signed_text),
|
||||
dbesc($authorsig),
|
||||
dbesc($handle)
|
||||
);
|
||||
}
|
||||
$drop_id = intval($item['id']);
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ function nav(&$a) {
|
|||
|
||||
/* only show friend requests for normal pages. Other page types have automatic friendship. */
|
||||
|
||||
if($_SESSION['page_flags'] == PAGE_NORMAL) {
|
||||
if($_SESSION['page_flags'] == PAGE_NORMAL || $_SESSION['page_flags'] == PAGE_PRVGROUP) {
|
||||
$nav['introductions'] = array('notifications/intros', t('Introductions'), "", t('Friend Requests'));
|
||||
$nav['notifications'] = array('notifications', t('Notifications'), "", t('Notifications'));
|
||||
$nav['notifications']['all']=array('notifications/system', t('See all notifications'), "", "");
|
||||
|
|
|
@ -876,3 +876,167 @@ function fix_contact_ssl_policy(&$contact,$new_policy) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* xml2array() will convert the given XML text to an array in the XML structure.
|
||||
* Link: http://www.bin-co.com/php/scripts/xml2array/
|
||||
* Portions significantly re-written by mike@macgirvin.com for Friendica (namespaces, lowercase tags, get_attribute default changed, more...)
|
||||
* Arguments : $contents - The XML text
|
||||
* $namespaces - true or false include namespace information in the returned array as array elements.
|
||||
* $get_attributes - 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value.
|
||||
* $priority - Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance.
|
||||
* Return: The parsed XML in an array form. Use print_r() to see the resulting array structure.
|
||||
* Examples: $array = xml2array(file_get_contents('feed.xml'));
|
||||
* $array = xml2array(file_get_contents('feed.xml', true, 1, 'attribute'));
|
||||
*/
|
||||
|
||||
function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') {
|
||||
if(!$contents) return array();
|
||||
|
||||
if(!function_exists('xml_parser_create')) {
|
||||
logger('xml2array: parser function missing');
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
if($namespaces)
|
||||
$parser = @xml_parser_create_ns("UTF-8",':');
|
||||
else
|
||||
$parser = @xml_parser_create();
|
||||
|
||||
if(! $parser) {
|
||||
logger('xml2array: xml_parser_create: no resource');
|
||||
return array();
|
||||
}
|
||||
|
||||
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
|
||||
// http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
|
||||
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
|
||||
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
|
||||
@xml_parse_into_struct($parser, trim($contents), $xml_values);
|
||||
@xml_parser_free($parser);
|
||||
|
||||
if(! $xml_values) {
|
||||
logger('xml2array: libxml: parse error: ' . $contents, LOGGER_DATA);
|
||||
foreach(libxml_get_errors() as $err)
|
||||
logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA);
|
||||
libxml_clear_errors();
|
||||
return;
|
||||
}
|
||||
|
||||
//Initializations
|
||||
$xml_array = array();
|
||||
$parents = array();
|
||||
$opened_tags = array();
|
||||
$arr = array();
|
||||
|
||||
$current = &$xml_array; // Reference
|
||||
|
||||
// Go through the tags.
|
||||
$repeated_tag_index = array(); // Multiple tags with same name will be turned into an array
|
||||
foreach($xml_values as $data) {
|
||||
unset($attributes,$value); // Remove existing values, or there will be trouble
|
||||
|
||||
// This command will extract these variables into the foreach scope
|
||||
// tag(string), type(string), level(int), attributes(array).
|
||||
extract($data); // We could use the array by itself, but this cooler.
|
||||
|
||||
$result = array();
|
||||
$attributes_data = array();
|
||||
|
||||
if(isset($value)) {
|
||||
if($priority == 'tag') $result = $value;
|
||||
else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
|
||||
}
|
||||
|
||||
//Set the attributes too.
|
||||
if(isset($attributes) and $get_attributes) {
|
||||
foreach($attributes as $attr => $val) {
|
||||
if($priority == 'tag') $attributes_data[$attr] = $val;
|
||||
else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
|
||||
}
|
||||
}
|
||||
|
||||
// See tag status and do the needed.
|
||||
if($namespaces && strpos($tag,':')) {
|
||||
$namespc = substr($tag,0,strrpos($tag,':'));
|
||||
$tag = strtolower(substr($tag,strlen($namespc)+1));
|
||||
$result['@namespace'] = $namespc;
|
||||
}
|
||||
$tag = strtolower($tag);
|
||||
|
||||
if($type == "open") { // The starting of the tag '<tag>'
|
||||
$parent[$level-1] = &$current;
|
||||
if(!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag
|
||||
$current[$tag] = $result;
|
||||
if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
|
||||
$repeated_tag_index[$tag.'_'.$level] = 1;
|
||||
|
||||
$current = &$current[$tag];
|
||||
|
||||
} else { // There was another element with the same tag name
|
||||
|
||||
if(isset($current[$tag][0])) { // If there is a 0th element it is already an array
|
||||
$current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
|
||||
$repeated_tag_index[$tag.'_'.$level]++;
|
||||
} else { // This section will make the value an array if multiple tags with the same name appear together
|
||||
$current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array
|
||||
$repeated_tag_index[$tag.'_'.$level] = 2;
|
||||
|
||||
if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
|
||||
$current[$tag]['0_attr'] = $current[$tag.'_attr'];
|
||||
unset($current[$tag.'_attr']);
|
||||
}
|
||||
|
||||
}
|
||||
$last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
|
||||
$current = &$current[$tag][$last_item_index];
|
||||
}
|
||||
|
||||
} elseif($type == "complete") { // Tags that ends in 1 line '<tag />'
|
||||
//See if the key is already taken.
|
||||
if(!isset($current[$tag])) { //New Key
|
||||
$current[$tag] = $result;
|
||||
$repeated_tag_index[$tag.'_'.$level] = 1;
|
||||
if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;
|
||||
|
||||
} else { // If taken, put all things inside a list(array)
|
||||
if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
|
||||
|
||||
// ...push the new element into that array.
|
||||
$current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
|
||||
|
||||
if($priority == 'tag' and $get_attributes and $attributes_data) {
|
||||
$current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
|
||||
}
|
||||
$repeated_tag_index[$tag.'_'.$level]++;
|
||||
|
||||
} else { // If it is not an array...
|
||||
$current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
|
||||
$repeated_tag_index[$tag.'_'.$level] = 1;
|
||||
if($priority == 'tag' and $get_attributes) {
|
||||
if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
|
||||
|
||||
$current[$tag]['0_attr'] = $current[$tag.'_attr'];
|
||||
unset($current[$tag.'_attr']);
|
||||
}
|
||||
|
||||
if($attributes_data) {
|
||||
$current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
|
||||
}
|
||||
}
|
||||
$repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 indexes are already taken
|
||||
}
|
||||
}
|
||||
|
||||
} elseif($type == 'close') { // End of tag '</tag>'
|
||||
$current = &$parent[$level-1];
|
||||
}
|
||||
}
|
||||
|
||||
return($xml_array);
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ function notifier_run($argv, $argc){
|
|||
$uid = $r[0]['uid'];
|
||||
$updated = $r[0]['edited'];
|
||||
|
||||
// The following seems superfluous. We've already checked for "if (! intval($r[0]['parent']))" a few lines up
|
||||
if(! $parent_id)
|
||||
return;
|
||||
|
||||
|
@ -220,7 +221,7 @@ function notifier_run($argv, $argc){
|
|||
}
|
||||
|
||||
|
||||
if(($cmd === 'uplink') && (intval($parent['forum_mode'])) && (! $top_level)) {
|
||||
if(($cmd === 'uplink') && (intval($parent['forum_mode']) == 1) && (! $top_level)) {
|
||||
$relay_to_owner = true;
|
||||
}
|
||||
|
||||
|
@ -265,10 +266,10 @@ function notifier_run($argv, $argc){
|
|||
$deny_people = expand_acl($parent['deny_cid']);
|
||||
$deny_groups = expand_groups(expand_acl($parent['deny_gid']));
|
||||
|
||||
// if our parent is a forum, uplink to the origional author causing
|
||||
// a delivery fork
|
||||
// if our parent is a public forum (forum_mode == 1), uplink to the origional author causing
|
||||
// a delivery fork. private groups (forum_mode == 2) do not uplink
|
||||
|
||||
if(intval($parent['forum_mode']) && (! $top_level) && ($cmd !== 'uplink')) {
|
||||
if((intval($parent['forum_mode']) == 1) && (! $top_level) && ($cmd !== 'uplink')) {
|
||||
proc_run('php','include/notifier','uplink',$item_id);
|
||||
}
|
||||
|
||||
|
@ -345,7 +346,7 @@ function notifier_run($argv, $argc){
|
|||
if($mail) {
|
||||
$public_message = false; // mail is not public
|
||||
|
||||
$body = fix_private_photos($item['body'],$owner['uid']);
|
||||
$body = fix_private_photos($item['body'],$owner['uid'],null,$message[0]['contact-id']);
|
||||
|
||||
$atom .= replace_macros($mail_template, array(
|
||||
'$name' => xmlify($owner['name']),
|
||||
|
@ -478,17 +479,42 @@ function notifier_run($argv, $argc){
|
|||
}
|
||||
}
|
||||
|
||||
foreach($r as $contact) {
|
||||
|
||||
// This controls the number of deliveries to execute with each separate delivery process.
|
||||
// By default we'll perform one delivery per process. Assuming a hostile shared hosting
|
||||
// provider, this provides the greatest chance of deliveries if processes start getting
|
||||
// killed. We can also space them out with the delivery_interval to also help avoid them
|
||||
// getting whacked.
|
||||
|
||||
// If $deliveries_per_process > 1, we will chain this number of multiple deliveries
|
||||
// together into a single process. This will reduce the overall number of processes
|
||||
// spawned for each delivery, but they will run longer.
|
||||
|
||||
$deliveries_per_process = intval(get_config('system','delivery_batch_count'));
|
||||
if($deliveries_per_process <= 0)
|
||||
$deliveries_per_process = 1;
|
||||
|
||||
$this_batch = array();
|
||||
|
||||
for($x = 0; $x < count($r); $x ++) {
|
||||
$contact = $r[$x];
|
||||
|
||||
if($contact['self'])
|
||||
continue;
|
||||
|
||||
// potentially more than one recipient. Start a new process and space them out a bit.
|
||||
// we will deliver single recipient types of message and email receipients here.
|
||||
|
||||
// we will deliver single recipient types of message and email recipients here.
|
||||
|
||||
if((! $mail) && (! $fsuggest) && (! $followup)) {
|
||||
proc_run('php','include/delivery.php',$cmd,$item_id,$contact['id']);
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
|
||||
$this_batch[] = $contact['id'];
|
||||
|
||||
if(count($this_batch) == $deliveries_per_process) {
|
||||
proc_run('php','include/delivery.php',$cmd,$item_id,$this_batch);
|
||||
$this_batch = array();
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -571,7 +597,7 @@ function notifier_run($argv, $argc){
|
|||
break;
|
||||
case NETWORK_OSTATUS:
|
||||
|
||||
// Do not send to otatus if we are not configured to send to public networks
|
||||
// Do not send to ostatus if we are not configured to send to public networks
|
||||
if($owner['prvnets'])
|
||||
break;
|
||||
if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
|
||||
|
@ -712,18 +738,19 @@ function notifier_run($argv, $argc){
|
|||
// unsupported
|
||||
break;
|
||||
}
|
||||
elseif(($target_item['deleted']) && ($target_item['verb'] !== ACTIVITY_LIKE)) {
|
||||
// diaspora delete,
|
||||
elseif(($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
|
||||
// send both top-level retractions and relayable retractions for owner to relay
|
||||
diaspora_send_retraction($target_item,$owner,$contact);
|
||||
break;
|
||||
}
|
||||
elseif($followup) {
|
||||
// send comments, likes and retractions of likes to owner to relay
|
||||
// send comments and likes to owner to relay
|
||||
diaspora_send_followup($target_item,$owner,$contact);
|
||||
break;
|
||||
}
|
||||
elseif($target_item['parent'] != $target_item['id']) {
|
||||
// we are the relay - send comments, likes and unlikes to our conversants
|
||||
elseif($target_item['uri'] !== $target_item['parent-uri']) {
|
||||
// we are the relay - send comments, likes and relayable_retractions
|
||||
// (of comments and likes) to our conversants
|
||||
diaspora_send_relay($target_item,$owner,$contact);
|
||||
break;
|
||||
}
|
||||
|
@ -833,6 +860,13 @@ function notifier_run($argv, $argc){
|
|||
|
||||
}
|
||||
|
||||
// If the item was deleted, clean up the `sign` table
|
||||
if($target_item['deleted']) {
|
||||
$r = q("DELETE FROM sign where `retract_iid` = %d",
|
||||
intval($target_item['id'])
|
||||
);
|
||||
}
|
||||
|
||||
logger('notifier: calling hooks', LOGGER_DEBUG);
|
||||
|
||||
if($normal_mode)
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
|
||||
|
||||
if(! function_exists('get_language')) {
|
||||
function get_language() {
|
||||
if(! function_exists('get_browser_language')) {
|
||||
function get_browser_language() {
|
||||
|
||||
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
if (x($_SERVER,'HTTP_ACCEPT_LANGUAGE')) {
|
||||
// break up string into pieces (languages and q factors)
|
||||
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
|
||||
|
|
|
@ -25,8 +25,8 @@ function advanced_profile(&$a) {
|
|||
|
||||
$val = ((intval($a->profile['dob']))
|
||||
? day_translate(datetime_convert('UTC','UTC',$a->profile['dob'] . ' 00:00 +00:00',$year_bd_format))
|
||||
: day_translate(datetime_convert('UTC','UTC','2001-' . substr($a->profile['dob'],6) . ' 00:00 +00:00',$short_bd_format)));
|
||||
|
||||
: day_translate(datetime_convert('UTC','UTC','2001-' . substr($a->profile['dob'],5) . ' 00:00 +00:00',$short_bd_format)));
|
||||
|
||||
$profile['birthday'] = array( t('Birthday:'), $val);
|
||||
|
||||
}
|
||||
|
@ -39,11 +39,16 @@ function advanced_profile(&$a) {
|
|||
|
||||
if($a->profile['with']) $profile['marital']['with'] = $a->profile['with'];
|
||||
|
||||
if(strlen($a->profile['howlong']) && $a->profile['howlong'] !== '0000-00-00 00:00:00') {
|
||||
$profile['howlong'] = relative_date($a->profile['howlong'], t('for %1$d %2$s'));
|
||||
}
|
||||
|
||||
if($a->profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), $a->profile['sexual'] );
|
||||
|
||||
if($a->profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify($a->profile['homepage']) );
|
||||
|
||||
if($a->profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify($a->profile['hometown']) );
|
||||
|
||||
if($a->profile['pub_keywords']) $profile['pub_keywords'] = array( t('Tags:'), $a->profile['pub_keywords']);
|
||||
|
||||
if($a->profile['politic']) $profile['politic'] = array( t('Political Views:'), $a->profile['politic']);
|
||||
|
|
|
@ -5,6 +5,8 @@ function gender_selector($current="",$suffix="") {
|
|||
$o = '';
|
||||
$select = array('', t('Male'), t('Female'), t('Currently Male'), t('Currently Female'), t('Mostly Male'), t('Mostly Female'), t('Transgender'), t('Intersex'), t('Transsexual'), t('Hermaphrodite'), t('Neuter'), t('Non-specific'), t('Other'), t('Undecided'));
|
||||
|
||||
call_hooks('gender_selector', $select);
|
||||
|
||||
$o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >";
|
||||
foreach($select as $selection) {
|
||||
if($selection !== 'NOTRANSLATION') {
|
||||
|
@ -20,6 +22,9 @@ function sexpref_selector($current="",$suffix="") {
|
|||
$o = '';
|
||||
$select = array('', t('Males'), t('Females'), t('Gay'), t('Lesbian'), t('No Preference'), t('Bisexual'), t('Autosexual'), t('Abstinent'), t('Virgin'), t('Deviant'), t('Fetish'), t('Oodles'), t('Nonsexual'));
|
||||
|
||||
|
||||
call_hooks('sexpref_selector', $select);
|
||||
|
||||
$o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >";
|
||||
foreach($select as $selection) {
|
||||
if($selection !== 'NOTRANSLATION') {
|
||||
|
@ -36,6 +41,8 @@ function marital_selector($current="",$suffix="") {
|
|||
$o = '';
|
||||
$select = array('', t('Single'), t('Lonely'), t('Available'), t('Unavailable'), t('Has crush'), t('Infatuated'), t('Dating'), t('Unfaithful'), t('Sex Addict'), t('Friends'), t('Friends/Benefits'), t('Casual'), t('Engaged'), t('Married'), t('Imaginarily married'), t('Partners'), t('Cohabiting'), t('Common law'), t('Happy'), t('Not looking'), t('Swinger'), t('Betrayed'), t('Separated'), t('Unstable'), t('Divorced'), t('Imaginarily divorced'), t('Widowed'), t('Uncertain'), t('It\'s complicated'), t('Don\'t care'), t('Ask me') );
|
||||
|
||||
call_hooks('marital_selector', $select);
|
||||
|
||||
$o .= "<select name=\"marital\" id=\"marital-select\" size=\"1\" >";
|
||||
foreach($select as $selection) {
|
||||
if($selection !== 'NOTRANSLATION') {
|
||||
|
|
|
@ -76,7 +76,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive
|
|||
header('X-Account-Management-Status: active; name="' . $a->user['username'] . '"; id="' . $a->user['nickname'] .'"');
|
||||
|
||||
if($login_initial) {
|
||||
$l = get_language();
|
||||
$l = get_browser_language();
|
||||
|
||||
q("UPDATE `user` SET `login_date` = '%s', `language` = '%s' WHERE `uid` = %d LIMIT 1",
|
||||
dbesc(datetime_convert()),
|
||||
|
|
|
@ -71,20 +71,24 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
|
||||
$name = $entry->displayName;
|
||||
|
||||
foreach($entry->urls as $url) {
|
||||
if($url->type == 'profile') {
|
||||
$profile_url = $url->value;
|
||||
continue;
|
||||
if(isset($entry->urls)) {
|
||||
foreach($entry->urls as $url) {
|
||||
if($url->type == 'profile') {
|
||||
$profile_url = $url->value;
|
||||
continue;
|
||||
}
|
||||
if($url->type == 'webfinger') {
|
||||
$connect_url = str_replace('acct:' , '', $url->value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if($url->type == 'webfinger') {
|
||||
$connect_url = str_replace('acct:' , '', $url->value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach($entry->photos as $photo) {
|
||||
if($photo->type == 'profile') {
|
||||
$profile_photo = $photo->value;
|
||||
continue;
|
||||
}
|
||||
if(isset($entry->photos)) {
|
||||
foreach($entry->photos as $photo) {
|
||||
if($photo->type == 'profile') {
|
||||
$profile_photo = $photo->value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -646,7 +646,7 @@ function search($s,$id='search-box',$url='/search',$save = false) {
|
|||
$a = get_app();
|
||||
$o = '<div id="' . $id . '">';
|
||||
$o .= '<form action="' . $a->get_baseurl((stristr($url,'network')) ? true : false) . $url . '" method="get" >';
|
||||
$o .= '<input type="text" name="search" id="search-text" value="' . $s .'" />';
|
||||
$o .= '<input type="text" name="search" id="search-text" placeholder="' . t('Search') . '" value="' . $s .'" />';
|
||||
$o .= '<input type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />';
|
||||
if($save)
|
||||
$o .= '<input type="submit" name="save" id="search-save" value="' . t('Save') . '" />';
|
||||
|
@ -742,6 +742,8 @@ function smilies($s, $sample = false) {
|
|||
':homebrew',
|
||||
':coffee',
|
||||
':facepalm',
|
||||
':like',
|
||||
':dislike',
|
||||
'~friendika',
|
||||
'~friendica'
|
||||
|
||||
|
@ -778,6 +780,8 @@ function smilies($s, $sample = false) {
|
|||
'<img src="' . $a->get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" />',
|
||||
'<img src="' . $a->get_baseurl() . '/images/coffee.gif" alt=":coffee" />',
|
||||
'<img src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
|
||||
'<img src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />',
|
||||
'<img src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />',
|
||||
'<a href="http://project.friendika.com">~friendika <img src="' . $a->get_baseurl() . '/images/friendika-16.png" alt="~friendika" /></a>',
|
||||
'<a href="http://friendica.com">~friendica <img src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>'
|
||||
);
|
||||
|
@ -887,6 +891,7 @@ function prepare_body($item,$attach = false) {
|
|||
} else
|
||||
$s = prepare_text($item['body']);
|
||||
|
||||
|
||||
$prep_arr = array('item' => $item, 'html' => $s);
|
||||
call_hooks('prepare_body', $prep_arr);
|
||||
$s = $prep_arr['html'];
|
||||
|
@ -901,24 +906,30 @@ function prepare_body($item,$attach = false) {
|
|||
foreach($arr as $r) {
|
||||
$matches = false;
|
||||
$icon = '';
|
||||
$cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
|
||||
$cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches, PREG_SET_ORDER);
|
||||
if($cnt) {
|
||||
$icontype = strtolower(substr($matches[3],0,strpos($matches[3],'/')));
|
||||
switch($icontype) {
|
||||
case 'video':
|
||||
case 'audio':
|
||||
case 'image':
|
||||
case 'text':
|
||||
$icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
|
||||
break;
|
||||
default:
|
||||
$icon = '<div class="attachtype icon s22 type-unkn"></div>';
|
||||
break;
|
||||
}
|
||||
$title = ((strlen(trim($matches[4]))) ? escape_tags(trim($matches[4])) : escape_tags($matches[1]));
|
||||
$title .= ' ' . $matches[2] . ' ' . t('bytes');
|
||||
foreach($matches as $mtch) {
|
||||
$icontype = strtolower(substr($mtch[3],0,strpos($mtch[3],'/')));
|
||||
switch($icontype) {
|
||||
case 'video':
|
||||
case 'audio':
|
||||
case 'image':
|
||||
case 'text':
|
||||
$icon = '<div class="attachtype icon s22 type-' . $icontype . '"></div>';
|
||||
break;
|
||||
default:
|
||||
$icon = '<div class="attachtype icon s22 type-unkn"></div>';
|
||||
break;
|
||||
}
|
||||
$title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
|
||||
$title .= ' ' . $mtch[2] . ' ' . t('bytes');
|
||||
if((local_user() == $item['uid']) && $item['contact-id'] != $a->contact['id'])
|
||||
$the_url = $a->get_baseurl() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
|
||||
else
|
||||
$the_url = $mtch[1];
|
||||
|
||||
$s .= '<a href="' . strip_tags($matches[1]) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
|
||||
$s .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="external-link" >' . $icon . '</a>';
|
||||
}
|
||||
}
|
||||
}
|
||||
$s .= '<div class="clear"></div></div>';
|
||||
|
|
327
include/user.php
Normal file
327
include/user.php
Normal file
|
@ -0,0 +1,327 @@
|
|||
<?php
|
||||
|
||||
require_once('include/config.php');
|
||||
require_once('include/network.php');
|
||||
require_once('include/plugin.php');
|
||||
require_once('include/text.php');
|
||||
require_once('include/pgettext.php');
|
||||
require_once('include/datetime.php');
|
||||
|
||||
function create_user($arr) {
|
||||
|
||||
// Required: { username, nickname, email } or { openid_url }
|
||||
|
||||
$a = get_app();
|
||||
$result = array('success' => false, 'user' => null, 'password' => '', 'message' => '');
|
||||
|
||||
$using_invites = get_config('system','invitation_only');
|
||||
$num_invites = get_config('system','number_invites');
|
||||
|
||||
|
||||
$invite_id = ((x($arr,'invite_id')) ? notags(trim($arr['invite_id'])) : '');
|
||||
$username = ((x($arr,'username')) ? notags(trim($arr['username'])) : '');
|
||||
$nickname = ((x($arr,'nickname')) ? notags(trim($arr['nickname'])) : '');
|
||||
$email = ((x($arr,'email')) ? notags(trim($arr['email'])) : '');
|
||||
$openid_url = ((x($arr,'openid_url')) ? notags(trim($arr['openid_url'])) : '');
|
||||
$photo = ((x($arr,'photo')) ? notags(trim($arr['photo'])) : '');
|
||||
$password = ((x($arr,'password')) ? trim($arr['password']) : '');
|
||||
$blocked = ((x($arr,'blocked')) ? intval($arr['blocked']) : 0);
|
||||
$verified = ((x($arr,'verified')) ? intval($arr['verified']) : 0);
|
||||
|
||||
$publish = ((x($arr,'profile_publish_reg') && intval($arr['profile_publish_reg'])) ? 1 : 0);
|
||||
$netpublish = ((strlen(get_config('system','directory_submit_url'))) ? $publish : 0);
|
||||
|
||||
$tmp_str = $openid_url;
|
||||
|
||||
if($using_invites) {
|
||||
if(! $invite_id) {
|
||||
$result['message'] .= t('An invitation is required.') . EOL;
|
||||
return $result;
|
||||
}
|
||||
$r = q("select * from register where `hash` = '%s' limit 1", dbesc($invite_id));
|
||||
if(! results($r)) {
|
||||
$result['message'] .= t('Invitation could not be verified.') . EOL;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if((! x($username)) || (! x($email)) || (! x($nickname))) {
|
||||
if($openid_url) {
|
||||
if(! validate_url($tmp_str)) {
|
||||
$result['message'] .= t('Invalid OpenID url') . EOL;
|
||||
return $result;
|
||||
}
|
||||
$_SESSION['register'] = 1;
|
||||
$_SESSION['openid'] = $openid_url;
|
||||
require_once('library/openid.php');
|
||||
$openid = new LightOpenID;
|
||||
$openid->identity = $openid_url;
|
||||
$openid->returnUrl = $a->get_baseurl() . '/openid';
|
||||
$openid->required = array('namePerson/friendly', 'contact/email', 'namePerson');
|
||||
$openid->optional = array('namePerson/first','media/image/aspect11','media/image/default');
|
||||
goaway($openid->authUrl());
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
notice( t('Please enter the required information.') . EOL );
|
||||
return;
|
||||
}
|
||||
|
||||
if(! validate_url($tmp_str))
|
||||
$openid_url = '';
|
||||
|
||||
|
||||
$err = '';
|
||||
|
||||
// collapse multiple spaces in name
|
||||
$username = preg_replace('/ +/',' ',$username);
|
||||
|
||||
if(mb_strlen($username) > 48)
|
||||
$result['message'] .= t('Please use a shorter name.') . EOL;
|
||||
if(mb_strlen($username) < 3)
|
||||
$result['message'] .= t('Name too short.') . EOL;
|
||||
|
||||
// I don't really like having this rule, but it cuts down
|
||||
// on the number of auto-registrations by Russian spammers
|
||||
|
||||
// Using preg_match was completely unreliable, due to mixed UTF-8 regex support
|
||||
// $no_utf = get_config('system','no_utf');
|
||||
// $pat = (($no_utf) ? '/^[a-zA-Z]* [a-zA-Z]*$/' : '/^\p{L}* \p{L}*$/u' );
|
||||
|
||||
// So now we are just looking for a space in the full name.
|
||||
|
||||
$loose_reg = get_config('system','no_regfullname');
|
||||
if(! $loose_reg) {
|
||||
$username = mb_convert_case($username,MB_CASE_TITLE,'UTF-8');
|
||||
if(! strpos($username,' '))
|
||||
$result['message'] .= t("That doesn't appear to be your full \x28First Last\x29 name.") . EOL;
|
||||
}
|
||||
|
||||
|
||||
if(! allowed_email($email))
|
||||
$result['message'] .= t('Your email domain is not among those allowed on this site.') . EOL;
|
||||
|
||||
if((! valid_email($email)) || (! validate_email($email)))
|
||||
$result['message'] .= t('Not a valid email address.') . EOL;
|
||||
|
||||
// Disallow somebody creating an account using openid that uses the admin email address,
|
||||
// since openid bypasses email verification. We'll allow it if there is not yet an admin account.
|
||||
|
||||
if((x($a->config,'admin_email')) && (strcasecmp($email,$a->config['admin_email']) == 0) && strlen($openid_url)) {
|
||||
$r = q("SELECT * FROM `user` WHERE `email` = '%s' LIMIT 1",
|
||||
dbesc($email)
|
||||
);
|
||||
if(count($r))
|
||||
$result['message'] .= t('Cannot use that email.') . EOL;
|
||||
}
|
||||
|
||||
$nickname = $arr['nickname'] = strtolower($nickname);
|
||||
|
||||
if(! preg_match("/^[a-z][a-z0-9\-\_]*$/",$nickname))
|
||||
$result['message'] .= t('Your "nickname" can only contain "a-z", "0-9", "-", and "_", and must also begin with a letter.') . EOL;
|
||||
$r = q("SELECT `uid` FROM `user`
|
||||
WHERE `nickname` = '%s' LIMIT 1",
|
||||
dbesc($nickname)
|
||||
);
|
||||
if(count($r))
|
||||
$result['message'] .= 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))
|
||||
$result['message'] .= t('Nickname was once registered here and may not be re-used. Please choose another.') . EOL;
|
||||
|
||||
if(strlen($result['message'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$new_password = ((strlen($password)) ? $password : autoname(6) . mt_rand(100,9999));
|
||||
$new_password_encoded = hash('whirlpool',$new_password);
|
||||
|
||||
$result['password'] = $new_password;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
|
||||
$keys = new_keypair(1024);
|
||||
|
||||
if($keys === false) {
|
||||
$result['message'] .= t('SERIOUS ERROR: Generation of security keys failed.') . EOL;
|
||||
return $result;
|
||||
}
|
||||
|
||||
$prvkey = $keys['prvkey'];
|
||||
$pubkey = $keys['pubkey'];
|
||||
|
||||
/**
|
||||
*
|
||||
* Create another keypair for signing/verifying
|
||||
* salmon protocol messages. We have to use a slightly
|
||||
* less robust key because this won't be using openssl
|
||||
* but the phpseclib. Since it is PHP interpreted code
|
||||
* it is not nearly as efficient, and the larger keys
|
||||
* will take several minutes each to process.
|
||||
*
|
||||
*/
|
||||
|
||||
$sres = new_keypair(512);
|
||||
$sprvkey = $sres['prvkey'];
|
||||
$spubkey = $sres['pubkey'];
|
||||
|
||||
$r = q("INSERT INTO `user` ( `guid`, `username`, `password`, `email`, `openid`, `nickname`,
|
||||
`pubkey`, `prvkey`, `spubkey`, `sprvkey`, `register_date`, `verified`, `blocked`, `timezone` )
|
||||
VALUES ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, 'UTC' )",
|
||||
dbesc(generate_user_guid()),
|
||||
dbesc($username),
|
||||
dbesc($new_password_encoded),
|
||||
dbesc($email),
|
||||
dbesc($openid_url),
|
||||
dbesc($nickname),
|
||||
dbesc($pubkey),
|
||||
dbesc($prvkey),
|
||||
dbesc($spubkey),
|
||||
dbesc($sprvkey),
|
||||
dbesc(datetime_convert()),
|
||||
intval($verified),
|
||||
intval($blocked)
|
||||
);
|
||||
|
||||
if($r) {
|
||||
$r = q("SELECT * FROM `user`
|
||||
WHERE `username` = '%s' AND `password` = '%s' LIMIT 1",
|
||||
dbesc($username),
|
||||
dbesc($new_password_encoded)
|
||||
);
|
||||
if($r !== false && count($r)) {
|
||||
$u = $r[0];
|
||||
$newuid = intval($r[0]['uid']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result['message'] .= t('An error occurred during registration. Please try again.') . EOL ;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* if somebody clicked submit twice very quickly, they could end up with two accounts
|
||||
* due to race condition. Remove this one.
|
||||
*/
|
||||
|
||||
$r = q("SELECT `uid` FROM `user`
|
||||
WHERE `nickname` = '%s' ",
|
||||
dbesc($nickname)
|
||||
);
|
||||
if((count($r) > 1) && $newuid) {
|
||||
$result['message'] .= t('Nickname is already registered. Please choose another.') . EOL;
|
||||
q("DELETE FROM `user` WHERE `uid` = %d LIMIT 1",
|
||||
intval($newuid)
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
|
||||
if(x($newuid) !== false) {
|
||||
$r = q("INSERT INTO `profile` ( `uid`, `profile-name`, `is-default`, `name`, `photo`, `thumb`, `publish`, `net-publish` )
|
||||
VALUES ( %d, '%s', %d, '%s', '%s', '%s', %d, %d ) ",
|
||||
intval($newuid),
|
||||
t('default'),
|
||||
1,
|
||||
dbesc($username),
|
||||
dbesc($a->get_baseurl() . "/photo/profile/{$newuid}.jpg"),
|
||||
dbesc($a->get_baseurl() . "/photo/avatar/{$newuid}.jpg"),
|
||||
intval($publish),
|
||||
intval($netpublish)
|
||||
|
||||
);
|
||||
if($r === false) {
|
||||
$result['message'] .= t('An error occurred creating your default profile. Please try again.') . EOL;
|
||||
// Start fresh next time.
|
||||
$r = q("DELETE FROM `user` WHERE `uid` = %d",
|
||||
intval($newuid));
|
||||
return $result;
|
||||
}
|
||||
$r = q("INSERT INTO `contact` ( `uid`, `created`, `self`, `name`, `nick`, `photo`, `thumb`, `micro`, `blocked`, `pending`, `url`, `nurl`,
|
||||
`request`, `notify`, `poll`, `confirm`, `poco`, `name-date`, `uri-date`, `avatar-date`, `closeness` )
|
||||
VALUES ( %d, '%s', 1, '%s', '%s', '%s', '%s', '%s', 0, 0, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', 0 ) ",
|
||||
intval($newuid),
|
||||
datetime_convert(),
|
||||
dbesc($username),
|
||||
dbesc($nickname),
|
||||
dbesc($a->get_baseurl() . "/photo/profile/{$newuid}.jpg"),
|
||||
dbesc($a->get_baseurl() . "/photo/avatar/{$newuid}.jpg"),
|
||||
dbesc($a->get_baseurl() . "/photo/micro/{$newuid}.jpg"),
|
||||
dbesc($a->get_baseurl() . "/profile/$nickname"),
|
||||
dbesc(normalise_link($a->get_baseurl() . "/profile/$nickname")),
|
||||
dbesc($a->get_baseurl() . "/dfrn_request/$nickname"),
|
||||
dbesc($a->get_baseurl() . "/dfrn_notify/$nickname"),
|
||||
dbesc($a->get_baseurl() . "/dfrn_poll/$nickname"),
|
||||
dbesc($a->get_baseurl() . "/dfrn_confirm/$nickname"),
|
||||
dbesc($a->get_baseurl() . "/poco/$nickname"),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert()),
|
||||
dbesc(datetime_convert())
|
||||
);
|
||||
|
||||
// Create a group with no members. This allows somebody to use it
|
||||
// right away as a default group for new contacts.
|
||||
|
||||
require_once('include/group.php');
|
||||
group_add($newuid, t('Friends'));
|
||||
|
||||
}
|
||||
|
||||
// if we have no OpenID photo try to look up an avatar
|
||||
if(! strlen($photo))
|
||||
$photo = avatar_img($email);
|
||||
|
||||
// unless there is no avatar-plugin loaded
|
||||
if(strlen($photo)) {
|
||||
require_once('include/Photo.php');
|
||||
$photo_failure = false;
|
||||
|
||||
$filename = basename($photo);
|
||||
$img_str = fetch_url($photo,true);
|
||||
$img = new Photo($img_str);
|
||||
if($img->is_valid()) {
|
||||
|
||||
$img->scaleImageSquare(175);
|
||||
|
||||
$hash = photo_new_resource();
|
||||
|
||||
$r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 4 );
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
$img->scaleImage(80);
|
||||
|
||||
$r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 5 );
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
$img->scaleImage(48);
|
||||
|
||||
$r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 6 );
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
if(! $photo_failure) {
|
||||
q("UPDATE `photo` SET `profile` = 1 WHERE `resource-id` = '%s' ",
|
||||
dbesc($hash)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_hooks('register_account', $newuid);
|
||||
|
||||
$result['success'] = true;
|
||||
$result['user'] = $u;
|
||||
return $result;
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue