mirror of
https://codeberg.org/streams/streams.git
synced 2024-09-21 11:15:22 +00:00
Merge branch 'dev' into release
This commit is contained in:
commit
7cac6004cf
17 changed files with 255 additions and 117 deletions
|
@ -3,6 +3,7 @@
|
||||||
namespace Code\Extend;
|
namespace Code\Extend;
|
||||||
|
|
||||||
use App;
|
use App;
|
||||||
|
use DBA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hook class.
|
* @brief Hook class.
|
||||||
|
|
|
@ -625,7 +625,7 @@ class Account {
|
||||||
Channel::auto_create($register[0]['uid']);
|
Channel::auto_create($register[0]['uid']);
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['login_return_url'] = 'new_channel';
|
$_SESSION['login_return_url'] = 'new_channel';
|
||||||
authenticate_success($account[0], null, true, true, false, true);
|
authenticate_success($account[0], false, true, true, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -778,13 +778,14 @@ class Activity
|
||||||
|
|
||||||
if (!in_array($ret['type'], ['Create', 'Update', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject'])) {
|
if (!in_array($ret['type'], ['Create', 'Update', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject'])) {
|
||||||
$ret['inReplyTo'] = $i['thr_parent'];
|
$ret['inReplyTo'] = $i['thr_parent'];
|
||||||
$cnv = get_iconfig($i['parent'], 'activitypub', 'context');
|
}
|
||||||
if (!$cnv) {
|
|
||||||
$cnv = get_iconfig($i['parent'], 'ostatus', 'conversation');
|
$cnv = get_iconfig($i['parent'], 'activitypub', 'context');
|
||||||
}
|
if (!$cnv) {
|
||||||
if (!$cnv) {
|
$cnv = get_iconfig($i['parent'], 'ostatus', 'conversation');
|
||||||
$cnv = $ret['parent_mid'];
|
}
|
||||||
}
|
if (!$cnv) {
|
||||||
|
$cnv = $ret['parent_mid'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3822,17 +3823,24 @@ class Activity
|
||||||
$item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = '';
|
$item['allow_gid'] = $item['deny_cid'] = $item['deny_gid'] = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private conversation, but this comment went rogue and was published publicly
|
|
||||||
// Set item_restrict to indicate this condition so we can flag it in the UI
|
|
||||||
|
|
||||||
if (intval($parent[0]['item_private']) !== 0 && $act->recips && (in_array(ACTIVITY_PUBLIC_INBOX, $act->recips) || in_array('Public', $act->recips) || in_array('as:Public', $act->recips))) {
|
|
||||||
$item['item_restrict'] = $item['item_restrict'] | 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::rewrite_mentions($item);
|
self::rewrite_mentions($item);
|
||||||
|
|
||||||
|
if (! isset($item['replyto'])) {
|
||||||
|
if (strpos($item['owner_xchan'],'http') === 0) {
|
||||||
|
$item['replyto'] = $item['owner_xchan'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$r = q("select hubloc_id_url from hubloc where hubloc_hash = '%s' and hubloc_primary = 1",
|
||||||
|
dbesc($item['owner_xchan'])
|
||||||
|
);
|
||||||
|
if ($r) {
|
||||||
|
$item['replyto'] = $r[0]['hubloc_id_url'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$r = q(
|
$r = q(
|
||||||
"select id, created, edited from item where mid = '%s' and uid = %d limit 1",
|
"select id, created, edited from item where mid = '%s' and uid = %d limit 1",
|
||||||
dbesc($item['mid']),
|
dbesc($item['mid']),
|
||||||
|
@ -3887,13 +3895,16 @@ class Activity
|
||||||
// We are the owner of this conversation, so send all received comments back downstream
|
// We are the owner of this conversation, so send all received comments back downstream
|
||||||
Run::Summon(['Notifier', 'comment-import', $x['item_id']]);
|
Run::Summon(['Notifier', 'comment-import', $x['item_id']]);
|
||||||
}
|
}
|
||||||
$r = q(
|
}
|
||||||
"select * from item where id = %d limit 1",
|
elseif ($act->client && $channel['channel_hash'] === $observer_hash) {
|
||||||
intval($x['item_id'])
|
Run::Summon(['Notifier', 'wall-new', $x['item_id']]);
|
||||||
);
|
}
|
||||||
if ($r) {
|
$r = q(
|
||||||
send_status_notifications($x['item_id'], $r[0]);
|
"select * from item where id = %d limit 1",
|
||||||
}
|
intval($x['item_id'])
|
||||||
|
);
|
||||||
|
if ($r) {
|
||||||
|
send_status_notifications($x['item_id'], $r[0]);
|
||||||
}
|
}
|
||||||
sync_an_item($channel['channel_id'], $x['item_id']);
|
sync_an_item($channel['channel_id'], $x['item_id']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class ActivityStreams
|
||||||
public $data = null;
|
public $data = null;
|
||||||
public $meta = null;
|
public $meta = null;
|
||||||
public $hub = null;
|
public $hub = null;
|
||||||
|
public $client = false;
|
||||||
public $valid = false;
|
public $valid = false;
|
||||||
public $deleted = false;
|
public $deleted = false;
|
||||||
public $id = '';
|
public $id = '';
|
||||||
|
@ -48,7 +49,8 @@ class ActivityStreams
|
||||||
|
|
||||||
$this->raw = $string;
|
$this->raw = $string;
|
||||||
$this->hub = $hub;
|
$this->hub = $hub;
|
||||||
|
$this->client = $client;
|
||||||
|
|
||||||
if (is_array($string)) {
|
if (is_array($string)) {
|
||||||
$this->data = $string;
|
$this->data = $string;
|
||||||
$this->raw = json_encode($string, JSON_UNESCAPED_SLASHES);
|
$this->raw = json_encode($string, JSON_UNESCAPED_SLASHES);
|
||||||
|
|
|
@ -219,7 +219,8 @@ class Item extends Controller
|
||||||
dbesc($r[0]['parent_mid']),
|
dbesc($r[0]['parent_mid']),
|
||||||
dbesc($portable_id)
|
dbesc($portable_id)
|
||||||
);
|
);
|
||||||
} elseif (Config::get('system', 'require_authenticated_fetch', false)) {
|
}
|
||||||
|
elseif (Config::get('system', 'require_authenticated_fetch', false)) {
|
||||||
http_status_exit(403, 'Permission denied');
|
http_status_exit(403, 'Permission denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,7 +739,7 @@ class Item extends Controller
|
||||||
dbesc($channel['channel_hash'])
|
dbesc($channel['channel_hash'])
|
||||||
);
|
);
|
||||||
if ($r && count($r)) {
|
if ($r && count($r)) {
|
||||||
$owner_xchan = $r[0];
|
$owner_xchan = array_shift($r);
|
||||||
} else {
|
} else {
|
||||||
logger("mod_item: no owner.");
|
logger("mod_item: no owner.");
|
||||||
if ($api_source) {
|
if ($api_source) {
|
||||||
|
@ -775,15 +776,6 @@ class Item extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($replyto)) {
|
|
||||||
if (strpos($owner_xchan['xchan_hash'], 'http') === 0) {
|
|
||||||
$replyto = $owner_xchan['xchan_hash'];
|
|
||||||
} else {
|
|
||||||
$replyto = $owner_xchan['xchan_url'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$acl = new AccessControl($channel);
|
$acl = new AccessControl($channel);
|
||||||
|
|
||||||
$view_policy = PermissionLimits::Get($channel['channel_id'], 'view_stream');
|
$view_policy = PermissionLimits::Get($channel['channel_id'], 'view_stream');
|
||||||
|
@ -1396,6 +1388,24 @@ class Item extends Controller
|
||||||
$datarray['obj']['id'] = $mid;
|
$datarray['obj']['id'] = $mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (! (isset($replyto) && $replyto)) {
|
||||||
|
if ($owner_hash && strpos($owner_hash,'http') === 0) {
|
||||||
|
$replyto = $owner_hash;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$tmp = $owner_hash ? $owner_hash : $owner_xchan['xchan_hash'];
|
||||||
|
if ($tmp) {
|
||||||
|
$r = q("select hubloc_id_url from hubloc where hubloc_hash = '%s' and hubloc_primary = 1",
|
||||||
|
dbesc($tmp)
|
||||||
|
);
|
||||||
|
if ($r) {
|
||||||
|
$replyto = $r[0]['hubloc_id_url'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($private && !$parent) {
|
if ($private && !$parent) {
|
||||||
if ( intval($private) === 1 && (!$str_group_allow) && in_array(substr_count($str_contact_allow,'<'), [ 1, 2 ])) {
|
if ( intval($private) === 1 && (!$str_group_allow) && in_array(substr_count($str_contact_allow,'<'), [ 1, 2 ])) {
|
||||||
$private = 2;
|
$private = 2;
|
||||||
|
|
|
@ -98,7 +98,7 @@ class Lockview extends Controller
|
||||||
$l = array_merge($l, $recips['cc']);
|
$l = array_merge($l, $recips['cc']);
|
||||||
}
|
}
|
||||||
for ($x = 0; $x < count($l); $x++) {
|
for ($x = 0; $x < count($l); $x++) {
|
||||||
if ($l[$x] === ACTIVITY_PUBLIC_INBOX) {
|
if ($l[$x] === ACTIVITY_PUBLIC_INBOX || $l[$x] === 'Public' || $l[$x] === 'as:Public') {
|
||||||
$l[$x] = '<strong><em>' . t('Everybody') . '</em></strong>';
|
$l[$x] = '<strong><em>' . t('Everybody') . '</em></strong>';
|
||||||
} else {
|
} else {
|
||||||
$l[$x] = '<a href="' . $l[$x] . '">' . $l[$x] . '</a>';
|
$l[$x] = '<a href="' . $l[$x] . '">' . $l[$x] . '</a>';
|
||||||
|
|
|
@ -11,20 +11,34 @@ use Code\Web\HTTPSig;
|
||||||
use Code\Lib\Activity;
|
use Code\Lib\Activity;
|
||||||
use Code\Lib\ActivityPub;
|
use Code\Lib\ActivityPub;
|
||||||
use Code\Lib\Config;
|
use Code\Lib\Config;
|
||||||
|
use Code\Lib\PConfig;
|
||||||
use Code\Lib\Channel;
|
use Code\Lib\Channel;
|
||||||
|
|
||||||
|
require_once('include/api_auth.php');
|
||||||
|
require_once('include/api.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements an ActivityPub outbox.
|
* Implements an ActivityPub outbox.
|
||||||
*/
|
*/
|
||||||
class Outbox extends Controller
|
class Outbox extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
if (! api_user()) {
|
||||||
|
api_login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function post()
|
public function post()
|
||||||
{
|
{
|
||||||
if (argc() < 2) {
|
if (argc() < 2) {
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! api_user()) {
|
||||||
|
killme();
|
||||||
|
}
|
||||||
|
|
||||||
$channel = Channel::from_username(argv(1));
|
$channel = Channel::from_username(argv(1));
|
||||||
if (!$channel) {
|
if (!$channel) {
|
||||||
killme();
|
killme();
|
||||||
|
@ -34,25 +48,20 @@ class Outbox extends Controller
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point there is unlikely to be an authenticated observer using the C2S ActivityPub API.
|
|
||||||
// Mostly we're protecting the page from malicious mischief until the project's OAuth2 interface
|
|
||||||
// is linked to this page.
|
|
||||||
|
|
||||||
$observer = App::get_observer();
|
$observer = App::get_observer();
|
||||||
if (!$observer) {
|
if (!$observer) {
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($observer['xchan_hash'] !== $channel['channel_hash']) {
|
if ($observer['xchan_hash'] !== $channel['channel_hash']) {
|
||||||
if (!perm_is_allowed($channel['channel_id'], $observer['xchan_hash'], 'post_wall')) {
|
if (!perm_is_allowed($channel['channel_id'], $observer['xchan_hash'], 'post_wall')) {
|
||||||
logger('outbox post permission denied to ' . $observer['xchan_name']);
|
logger('outbox post permission denied to ' . $observer['xchan_name']);
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// disable C2S until we've fully tested it.
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
|
||||||
|
$observer_hash = get_observer_hash();
|
||||||
|
|
||||||
$data = file_get_contents('php://input');
|
$data = file_get_contents('php://input');
|
||||||
if (!$data) {
|
if (!$data) {
|
||||||
return;
|
return;
|
||||||
|
@ -60,7 +69,8 @@ class Outbox extends Controller
|
||||||
|
|
||||||
logger('outbox_activity: ' . jindent($data), LOGGER_DATA);
|
logger('outbox_activity: ' . jindent($data), LOGGER_DATA);
|
||||||
|
|
||||||
$AS = new ActivityStreams($data);
|
// the third parameter signals to the parser that we are using C2S and that implied Create activities are supported
|
||||||
|
$AS = new ActivityStreams($data, null, true);
|
||||||
|
|
||||||
if (!$AS->is_valid()) {
|
if (!$AS->is_valid()) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,41 +80,57 @@ class Outbox extends Controller
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure the posted activity has required attributes
|
||||||
|
|
||||||
|
$uuid = new_uuid();
|
||||||
|
|
||||||
|
if (! $AS->id) {
|
||||||
|
$AS->id = z_root() . '/activity/' . $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($AS->obj) && (! isset($AS->obj['id']))) {
|
||||||
|
$AS->obj['id'] = z_root() . '/item/' . $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isset($AS->actor)) {
|
||||||
|
$AS->actor = Channel::url($channel);
|
||||||
|
}
|
||||||
|
|
||||||
logger('outbox_channel: ' . $channel['channel_address'], LOGGER_DEBUG);
|
logger('outbox_channel: ' . $channel['channel_address'], LOGGER_DEBUG);
|
||||||
|
|
||||||
// switch ($AS->type) {
|
switch ($AS->type) {
|
||||||
// case 'Follow':
|
case 'Follow':
|
||||||
// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) && isset($AS->obj['id'])) {
|
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) && isset($AS->obj['id'])) {
|
||||||
// // do follow activity
|
// do follow activity
|
||||||
// Activity::follow($channel,$AS);
|
Activity::follow($channel,$AS);
|
||||||
// }
|
}
|
||||||
// break;
|
break;
|
||||||
// case 'Invite':
|
case 'Invite':
|
||||||
// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') {
|
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') {
|
||||||
// // do follow activity
|
// do follow activity
|
||||||
// Activity::follow($channel,$AS);
|
Activity::follow($channel,$AS);
|
||||||
// }
|
}
|
||||||
// break;
|
break;
|
||||||
// case 'Join':
|
case 'Join':
|
||||||
// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') {
|
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') {
|
||||||
// // do follow activity
|
// do follow activity
|
||||||
// Activity::follow($channel,$AS);
|
Activity::follow($channel,$AS);
|
||||||
// }
|
}
|
||||||
// break;
|
break;
|
||||||
// case 'Accept':
|
case 'Accept':
|
||||||
// // Activitypub for wordpress sends lowercase 'follow' on accept.
|
// Activitypub for wordpress sends lowercase 'follow' on accept.
|
||||||
// // https://github.com/pfefferle/wordpress-activitypub/issues/97
|
// https://github.com/pfefferle/wordpress-activitypub/issues/97
|
||||||
// // Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group
|
// Mobilizon sends Accept/"Member" (not in vocabulary) in response to Join/Group
|
||||||
// if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow','follow', 'Member'])) {
|
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && in_array($AS->obj['type'], ['Follow','follow', 'Member'])) {
|
||||||
// // do follow activity
|
// do follow activity
|
||||||
// Activity::follow($channel,$AS);
|
Activity::follow($channel,$AS);
|
||||||
// }
|
}
|
||||||
// break;
|
break;
|
||||||
// case 'Reject':
|
case 'Reject':
|
||||||
// default:
|
default:
|
||||||
// break;
|
break;
|
||||||
//
|
|
||||||
// }
|
}
|
||||||
|
|
||||||
// These activities require permissions
|
// These activities require permissions
|
||||||
|
|
||||||
|
@ -113,10 +139,7 @@ class Outbox extends Controller
|
||||||
switch ($AS->type) {
|
switch ($AS->type) {
|
||||||
case 'Update':
|
case 'Update':
|
||||||
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) {
|
if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type'])) {
|
||||||
// pretend this is an old cache entry to force an update of all the actor details
|
Activity::actor_store($AS->obj['id'], $AS->obj, true /* force cache refresh */);
|
||||||
$AS->obj['cached'] = true;
|
|
||||||
$AS->obj['updated'] = datetime_convert('UTC', 'UTC', '1980-01-01', ATOM_TIME);
|
|
||||||
Activity::actor_store($AS->obj['id'], $AS->obj);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'Accept':
|
case 'Accept':
|
||||||
|
@ -186,8 +209,69 @@ class Outbox extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($item) {
|
if ($item) {
|
||||||
|
// fixup some of the item fields when using C2S
|
||||||
|
|
||||||
|
if (! (isset($item['parent_mid']) && $item['parent_mid'])) {
|
||||||
|
$item['parent_mid'] = $item['mid'];
|
||||||
|
}
|
||||||
|
// map ActivityPub recipients to Nomad ACLs to the extent possible.
|
||||||
|
if (isset($AS->recips)) {
|
||||||
|
$item['item_private'] = ((in_array(ACTIVITY_PUBLIC_INBOX, $AS->recips)
|
||||||
|
|| in_array('Public', $AS->recips)
|
||||||
|
|| in_array('as:Public', $AS->recips))
|
||||||
|
? 0
|
||||||
|
: 1
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($item['item_private']) {
|
||||||
|
foreach ($AS->recips as $recip) {
|
||||||
|
if (strpos($recip,'/lists/')) {
|
||||||
|
$r = q("select * from pgrp where hash = '%s' and uid = %d",
|
||||||
|
dbesc(basename($recip)),
|
||||||
|
intval($channel['channel_id'])
|
||||||
|
);
|
||||||
|
if ($r) {
|
||||||
|
if (! isset($item['allow_gid'])) {
|
||||||
|
$item['allow_gid'] = EMPTY_STR;
|
||||||
|
}
|
||||||
|
$item['allow_gid'] .= '<' . $r[0]['hash'] . '>';
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($recip === z_root() . '/followers/' . $channel['channel_address']) {
|
||||||
|
// map to a virtual list/group even if the app isn't installed. This should do the right
|
||||||
|
// thing and create a followers-only post with the correct ACL as long as the public stream
|
||||||
|
// isn't addressed. And if it is, the post will still go to all your connections - so the ACL isn't
|
||||||
|
// necessary.
|
||||||
|
if (! isset($item['allow_gid'])) {
|
||||||
|
$item['allow_gid'] = EMPTY_STR;
|
||||||
|
}
|
||||||
|
$item['allow_gid'] .= '<connections:' . $channel['channel_hash'] . '>';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$r = q("select * from hubloc where hubloc_id_url = '%s'",
|
||||||
|
dbesc($recip)
|
||||||
|
);
|
||||||
|
if ($r) {
|
||||||
|
if (! isset($item['allow_cid'])) {
|
||||||
|
$item['allow_cid'] = EMPTY_STR;
|
||||||
|
}
|
||||||
|
$item['allow_cid'] .= '<' . $r[0]['hubloc_hash'] . '>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set the DM flag if needed
|
||||||
|
if ($item['item_private'] && isset($item['allow_cid']) && ! isset($item['allow_gid'])
|
||||||
|
&& in_array(substr_count($item['allow_cid'],'<'), [ 1, 2 ])) {
|
||||||
|
$item['item_private'] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$item['item_wall'] = 1;
|
||||||
|
|
||||||
logger('parsed_item: ' . print_r($item, true), LOGGER_DATA);
|
logger('parsed_item: ' . print_r($item, true), LOGGER_DATA);
|
||||||
Activity::store($channel, $observer_hash, $AS, $item);
|
Activity::store($channel, $observer_hash, $AS, $item);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
http_status_exit(200, 'OK');
|
http_status_exit(200, 'OK');
|
||||||
|
|
|
@ -174,7 +174,7 @@ class Register extends Controller
|
||||||
|
|
||||||
// fall through and authenticate if no approvals or verifications were required.
|
// fall through and authenticate if no approvals or verifications were required.
|
||||||
|
|
||||||
authenticate_success($result['account'], null, true, false, true);
|
authenticate_success($result['account'], false, true, false, true);
|
||||||
|
|
||||||
$new_channel = false;
|
$new_channel = false;
|
||||||
$next_page = 'new_channel';
|
$next_page = 'new_channel';
|
||||||
|
|
|
@ -12,6 +12,8 @@ use Code\Daemon\Run;
|
||||||
use Code\Lib\Channel;
|
use Code\Lib\Channel;
|
||||||
use Code\Lib\Navbar;
|
use Code\Lib\Navbar;
|
||||||
use Code\Render\Theme;
|
use Code\Render\Theme;
|
||||||
|
use Code\Lib\LDSignatures;
|
||||||
|
use Code\Web\HTTPSig;
|
||||||
|
|
||||||
|
|
||||||
require_once("include/bbcode.php");
|
require_once("include/bbcode.php");
|
||||||
|
@ -52,7 +54,7 @@ class Search extends Controller
|
||||||
}
|
}
|
||||||
Navbar::set_selected('Search');
|
Navbar::set_selected('Search');
|
||||||
|
|
||||||
$format = (($_REQUEST['format']) ? $_REQUEST['format'] : '');
|
$format = (($_REQUEST['module_format']) ? $_REQUEST['module_format'] : '');
|
||||||
if ($format !== '') {
|
if ($format !== '') {
|
||||||
$this->updating = $this->loading = 1;
|
$this->updating = $this->loading = 1;
|
||||||
}
|
}
|
||||||
|
@ -66,15 +68,19 @@ class Search extends Controller
|
||||||
|
|
||||||
if (x(App::$data, 'search')) {
|
if (x(App::$data, 'search')) {
|
||||||
$search = trim(App::$data['search']);
|
$search = trim(App::$data['search']);
|
||||||
|
$saved_id = 'search=' . urlencode($_GET['search']);
|
||||||
} else {
|
} else {
|
||||||
$search = ((x($_GET, 'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : '');
|
$search = ((x($_GET, 'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : '');
|
||||||
|
$saved_id = 'search=' . urlencode($_GET['search']);
|
||||||
}
|
}
|
||||||
$tag = false;
|
$tag = false;
|
||||||
if (x($_GET, 'tag')) {
|
if (x($_GET, 'tag')) {
|
||||||
$tag = true;
|
$tag = true;
|
||||||
$search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
|
$search = ((x($_GET, 'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
|
||||||
|
$saved_id = 'tag=' . urlencode($_GET['tag']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0);
|
$static = ((array_key_exists('static', $_REQUEST)) ? intval($_REQUEST['static']) : 0);
|
||||||
|
|
||||||
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
$o .= search($search, 'search-box', '/search', ((local_channel()) ? true : false));
|
||||||
|
@ -199,21 +205,21 @@ class Search extends Controller
|
||||||
$tag = true;
|
$tag = true;
|
||||||
$search = substr($search, 1);
|
$search = substr($search, 1);
|
||||||
}
|
}
|
||||||
if (strpos($search, '@') === 0) {
|
if (strpos($search, '@') === 0 && $format === '') {
|
||||||
$search = substr($search, 1);
|
$search = substr($search, 1);
|
||||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||||
}
|
}
|
||||||
if (strpos($search, '!') === 0) {
|
if (strpos($search, '!') === 0 && $format === '') {
|
||||||
$search = substr($search, 1);
|
$search = substr($search, 1);
|
||||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||||
}
|
}
|
||||||
if (strpos($search, '?') === 0) {
|
if (strpos($search, '?') === 0 && $format === '') {
|
||||||
$search = substr($search, 1);
|
$search = substr($search, 1);
|
||||||
goaway(z_root() . '/help' . '?f=1&navsearch=1&search=' . $search);
|
goaway(z_root() . '/help' . '?f=1&navsearch=1&search=' . $search);
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for a naked webbie
|
// look for a naked webbie
|
||||||
if (strpos($search, '@') !== false && strpos($search, 'http') !== 0) {
|
if (strpos($search, '@') !== false && strpos($search, 'http') !== 0 && $format === '') {
|
||||||
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,16 +341,28 @@ class Search extends Controller
|
||||||
$items = [];
|
$items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($format == 'json') {
|
if ($format === 'json') {
|
||||||
$result = [];
|
|
||||||
require_once('include/conversation.php');
|
$chan = Channel::get_system();
|
||||||
foreach ($items as $item) {
|
|
||||||
$item['html'] = zidify_links(bbcode($item['body']));
|
$i = Activity::encode_item_collection($items, 'search?' . $saved_id , 'OrderedCollection', true, count($items));
|
||||||
$x = encode_item($item);
|
|
||||||
$x['html'] = prepare_text($item['body'], $item['mimetype']);
|
$x = array_merge(['@context' => [
|
||||||
$result[] = $x;
|
ACTIVITYSTREAMS_JSONLD_REV,
|
||||||
}
|
'https://w3id.org/security/v1',
|
||||||
json_return_and_die(array('success' => true, 'messages' => $result));
|
Activity::ap_schema()
|
||||||
|
]], $i);
|
||||||
|
|
||||||
|
$headers = [];
|
||||||
|
$headers['Content-Type'] = 'application/x-nomad+json';
|
||||||
|
$x['signature'] = LDSignatures::sign($x, $chan);
|
||||||
|
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
|
||||||
|
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
|
||||||
|
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
|
||||||
|
$h = HTTPSig::create_sig($headers, $chan['channel_prvkey'], Channel::url($chan));
|
||||||
|
HTTPSig::set_headers($h);
|
||||||
|
echo $ret;
|
||||||
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($tag) {
|
if ($tag) {
|
||||||
|
|
|
@ -3,7 +3,19 @@ Federation
|
||||||
|
|
||||||
The ActivityPub implementation in this project strives to be compliant to the core spec where possible, while offering a range of services and features which normally aren't provided by ActivityPub projects.
|
The ActivityPub implementation in this project strives to be compliant to the core spec where possible, while offering a range of services and features which normally aren't provided by ActivityPub projects.
|
||||||
|
|
||||||
|
C2S
|
||||||
|
|
||||||
|
This project supports ActivityPub C2S. You may authenticate with HTTP basic-auth, OAuth2, or OpenWebAuth. There is no media upload endpoint since the (deprecated) specification of that service has no workarounds for working in memory-restricted environments and most mobile phone photos exceed PHP's default upload size limits.
|
||||||
|
|
||||||
|
Client search interface
|
||||||
|
|
||||||
|
If public access is allowed to the content search interface (a site security setting), clients may search the content of public messages or tags and are returned an ActivityStreams Collection of search results. When authenticated via OpenWebAuth, the search results may contain their own content or private content which they are permitted to access.
|
||||||
|
|
||||||
|
The URL endpoints are:
|
||||||
|
|
||||||
|
https://example.com/search?search=banana
|
||||||
|
https://example.com/search?tag=banana
|
||||||
|
|
||||||
Direct Messages
|
Direct Messages
|
||||||
|
|
||||||
Direct Messages (DM) are differentiated from other private messaging using the zot:directMessage flag (boolean). This is compatible with the same facility provided by other projects in other namespaces and is not prefixed within activities so that these may potentially be aggregated.
|
Direct Messages (DM) are differentiated from other private messaging using the zot:directMessage flag (boolean). This is compatible with the same facility provided by other projects in other namespaces and is not prefixed within activities so that these may potentially be aggregated.
|
||||||
|
|
|
@ -14,7 +14,7 @@ require_once('include/security.php');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API Login via basic-auth or OAuth
|
* API Login via basic-auth, OpenWebAuth, or OAuth2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function api_login()
|
function api_login()
|
||||||
|
@ -58,8 +58,7 @@ function api_login()
|
||||||
intval($record['channel_account_id'])
|
intval($record['channel_account_id'])
|
||||||
);
|
);
|
||||||
if ($x) {
|
if ($x) {
|
||||||
require_once('include/security.php');
|
authenticate_success($x[0], false, true, false, true, true);
|
||||||
authenticate_success($x[0], null, true, false, true, true);
|
|
||||||
$_SESSION['allow_api'] = true;
|
$_SESSION['allow_api'] = true;
|
||||||
Hook::call('logged_in', App::$user);
|
Hook::call('logged_in', App::$user);
|
||||||
return;
|
return;
|
||||||
|
@ -164,8 +163,8 @@ function api_login()
|
||||||
|
|
||||||
function retry_basic_auth($method = 'Basic')
|
function retry_basic_auth($method = 'Basic')
|
||||||
{
|
{
|
||||||
header('WWW-Authenticate: ' . $method . ' realm="' . System::get_platform_name() . '"');
|
header('WWW-Authenticate: ' . $method . ' realm="' . System::get_project_name() . '"');
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
echo( t('This api method requires authentication'));
|
echo( t('This API method requires authentication.'));
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,15 +43,10 @@ function account_verify_password($login, $pass)
|
||||||
{
|
{
|
||||||
|
|
||||||
$ret = [ 'account' => null, 'channel' => null, 'xchan' => null ];
|
$ret = [ 'account' => null, 'channel' => null, 'xchan' => null ];
|
||||||
$login = punify($login);
|
|
||||||
|
|
||||||
$email_verify = get_config('system', 'verify_email');
|
$email_verify = get_config('system', 'verify_email');
|
||||||
$register_policy = get_config('system', 'register_policy');
|
$register_policy = get_config('system', 'register_policy');
|
||||||
|
|
||||||
if (! $login) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$account = null;
|
$account = null;
|
||||||
$channel = null;
|
$channel = null;
|
||||||
$xchan = null;
|
$xchan = null;
|
||||||
|
@ -76,7 +71,13 @@ function account_verify_password($login, $pass)
|
||||||
if (($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) {
|
if (($addon_auth['authenticated']) && is_array($addon_auth['user_record']) && (! empty($addon_auth['user_record']))) {
|
||||||
$ret['account'] = $addon_auth['user_record'];
|
$ret['account'] = $addon_auth['user_record'];
|
||||||
return $ret;
|
return $ret;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
|
if (! $login) {
|
||||||
|
logger('No login identity provided or authenticate addon failed.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$login = punify($login);
|
||||||
if (! strpos($login, '@')) {
|
if (! strpos($login, '@')) {
|
||||||
$channel = Channel::from_username($login);
|
$channel = Channel::from_username($login);
|
||||||
if (! $channel) {
|
if (! $channel) {
|
||||||
|
@ -230,7 +231,7 @@ if (
|
||||||
App::$session->new_cookie(60 * 60 * 24); // one day
|
App::$session->new_cookie(60 * 60 * 24); // one day
|
||||||
$_SESSION['last_login_date'] = datetime_convert();
|
$_SESSION['last_login_date'] = datetime_convert();
|
||||||
unset($_SESSION['visitor_id']); // no longer a visitor
|
unset($_SESSION['visitor_id']); // no longer a visitor
|
||||||
authenticate_success($x[0], null, true, true);
|
authenticate_success($x[0], false, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (array_key_exists('atoken', $_SESSION)) {
|
if (array_key_exists('atoken', $_SESSION)) {
|
||||||
|
@ -279,7 +280,7 @@ if (
|
||||||
$login_refresh = true;
|
$login_refresh = true;
|
||||||
}
|
}
|
||||||
$ch = (($_SESSION['uid']) ? Channel::from_id($_SESSION['uid']) : null);
|
$ch = (($_SESSION['uid']) ? Channel::from_id($_SESSION['uid']) : null);
|
||||||
authenticate_success($r[0], null, $ch, false, false, $login_refresh);
|
authenticate_success($r[0], false, $ch, false, false, $login_refresh);
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['account_id'] = 0;
|
$_SESSION['account_id'] = 0;
|
||||||
App::$session->nuke();
|
App::$session->nuke();
|
||||||
|
|
|
@ -17,7 +17,7 @@ use Code\Extend\Hook;
|
||||||
* @param bool $return
|
* @param bool $return
|
||||||
* @param bool $update_lastlog
|
* @param bool $update_lastlog
|
||||||
*/
|
*/
|
||||||
function authenticate_success($user_record, $channel = null, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false)
|
function authenticate_success($user_record, $channel = false, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
|
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
|
||||||
|
|
|
@ -948,7 +948,7 @@ function get_tags($s)
|
||||||
// Pull out single word tags. These can be @nickname, @first_last
|
// Pull out single word tags. These can be @nickname, @first_last
|
||||||
// and #hash tags.
|
// and #hash tags.
|
||||||
|
|
||||||
if (preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;])([@#]\!?[^ \x0D\x0A,;:\?\[\{\&]+)/u', $s, $match)) {
|
if (preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;\#])([@#]\!?[^ \x0D\x0A,;:\?\[\{\&]+)/u', $s, $match)) {
|
||||||
foreach ($match[1] as $mtch) {
|
foreach ($match[1] as $mtch) {
|
||||||
// Cleanup/ignore false positives
|
// Cleanup/ignore false positives
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ cli_startup();
|
||||||
list($tmp, $id) = array_map('trim', explode('/', $file));
|
list($tmp, $id) = array_map('trim', explode('/', $file));
|
||||||
$info = Addon::get_info($id);
|
$info = Addon::get_info($id);
|
||||||
$enabled = in_array($id,$installed);
|
$enabled = in_array($id,$installed);
|
||||||
$x = check_plugin_versions($info);
|
$x = Addon::check_versions($info);
|
||||||
if($enabled && ! $x) {
|
if($enabled && ! $x) {
|
||||||
$enabled = false;
|
$enabled = false;
|
||||||
Addon::uninstall($id);
|
Addon::uninstall($id);
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<?php
|
<?php
|
||||||
define ( 'STD_VERSION', '22.03.12' );
|
define ( 'STD_VERSION', '22.03.15' );
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
use Code\Lib\Channel;
|
use Code\Lib\Channel;
|
||||||
|
|
||||||
if(! App::$install) {
|
if (! App::$install) {
|
||||||
|
|
||||||
// Get the UID of the channel owner
|
// Get the UID of the channel owner
|
||||||
$uid = Channel::get_theme_uid();
|
$uid = Channel::get_theme_uid();
|
||||||
|
@ -36,9 +36,9 @@ if(! App::$install) {
|
||||||
// Setting $schema to '' wasn't working for some reason, so we'll check it's
|
// Setting $schema to '' wasn't working for some reason, so we'll check it's
|
||||||
// not --- like the mobile theme does instead.
|
// not --- like the mobile theme does instead.
|
||||||
|
|
||||||
// Allow layouts to over-ride the schema
|
// Allow layouts to over-ride the schema - used as a filename component so sanitize.
|
||||||
|
|
||||||
$schema = ((isset($_REQUEST['schema']) && $_REQUEST['schema']) ? $_REQUEST['schema'] : EMPTY_STR);
|
$schema = str_replace(['/', '.'], [ '', '' ], ((isset($_REQUEST['schema']) && $_REQUEST['schema']) ? $_REQUEST['schema'] : EMPTY_STR));
|
||||||
|
|
||||||
|
|
||||||
if (($schema) && ($schema != '---')) {
|
if (($schema) && ($schema != '---')) {
|
||||||
|
|
Loading…
Reference in a new issue