Merge branch 'origin' into 'master'

marge from origin 4.0

See merge request harukin/core!42
This commit is contained in:
harukin 2019-03-09 17:59:08 +09:00
commit 23fc7e65d4
172 changed files with 25203 additions and 20790 deletions

View File

@ -1,36 +1,126 @@
# Select image from https://hub.docker.com/_/php/ # Select image from https://hub.docker.com/_/php/
image: php:7.1 #image: php:7.2
# Use a prepared Hubzilla image to optimise pipeline run
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.2
stages:
- test
- deploy
# Select what we should cache # Select what we should cache
cache: cache:
paths: paths:
- vendor/ - vendor/
- .cache/
# global variables for all jobs, if no job specific variables
variables: variables:
# Configure mysql service (https://hub.docker.com/_/mysql/) # Tell composer to use the project workspace .cache folder
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
# Ignore a Composer warning
COMPOSER_ALLOW_SUPERUSER: 1
# Configure MySQL/MariaDB service (https://hub.docker.com/_/mysql/, https://hub.docker.com/_/mariadb/)
MYSQL_DATABASE: hello_world_test MYSQL_DATABASE: hello_world_test
MYSQL_ROOT_PASSWORD: mysql MYSQL_ROOT_PASSWORD: mysql
# Configure PostgreSQL service (https://hub.docker.com/_/postgres/)
POSTGRES_DB: ci-db
POSTGRES_USER: ci-user
POSTGRES_PASSWORD: ci-pass
services:
- mysql:5.7
before_script: before_script:
- apt-get update -yqq
- apt-get install -yqq git mysql-server mysql-client libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev
# Install PHP extensions
- docker-php-ext-install mbstring mcrypt pdo_mysql pdo_pgsql curl json intl gd xml zip bz2 opcache
# Install & enable Xdebug for code coverage reports # Install & enable Xdebug for code coverage reports
- pecl install xdebug - pecl install xdebug
- docker-php-ext-enable xdebug - docker-php-ext-enable xdebug
# Install and run Composer # Install composer
- curl -sS https://getcomposer.org/installer | php - curl -sS https://getcomposer.org/installer | php
- php composer.phar install # Install dev libraries from composer
- php composer.phar install --no-progress
# We test PHP7 with MySQL, but we allow it to fail
test:php:mysql: # test PHP7 with MySQL 5.7
php7.2_mysql 1/2:
stage: test
services:
- mysql:5.7
script: script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text - vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with MySQL latest (8)
php7.2_mysql 2/2:
stage: test
services:
- name: mysql:latest
command: ["--default-authentication-plugin=mysql_native_password"]
script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with MariaDB latest (10.3)
php7.2_mariadb:
stage: test
services:
- name: mariadb:latest
alias: mysql
script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with PostgreSQL latest
php7.2_postgres:
stage: test
services:
- postgres:latest
script:
- export PGPASSWORD=$POSTGRES_PASSWORD
- psql --version
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();"
# Import hubzilla's DB schema
- psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql
# Show databases and relations/tables of hubzilla's database
#- psql -h "postgres" -U "$POSTGRES_USER" -l
#- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;"
# Run the actual tests
- vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox
artifacts:
expire_in: 1 week
# Gitlab should show the results, but has problems parsing PHPUnit's junit file.
reports:
junit: tests/results/junit.xml
# Archive test results (coverage, testdox, junit)
name: "$CI_COMMIT_REF_SLUG-$CI_JOB_NAME"
paths:
- tests/results/
# Generate Doxygen API Documentation and deploy it at GitLab pages
pages:
stage: deploy
cache: {}
image: php:7-cli-alpine
before_script:
- apk update
- apk add doxygen ttf-freefont graphviz
script:
- doxygen util/Doxyfile
- mv doc/html/ public/
- echo "API documentation should be accessible at https://hubzilla.frama.io/core/ soon"
artifacts:
paths:
- public
only:
# Only generate it on main repo's master branch
- master@hubzilla/core

View File

@ -5,8 +5,8 @@ Run hubzilla-setup.sh for an unattended installation of hubzilla.
The script is known to work without adjustments with The script is known to work without adjustments with
+ Hardware + Hardware
- Mini-PC with Debian-9.2-amd64, or - Mini-PC with Debian-9.5-amd64, or
- Rapberry 3 with Raspbian, Debian-9.3 - Rapberry 3 with Raspbian, Debian-9.5
+ DynDNS + DynDNS
- selfHOST.de - selfHOST.de
- freedns.afraid.org - freedns.afraid.org
@ -38,7 +38,7 @@ Software
- apt-get install git - apt-get install git
- mkdir -p /var/www - mkdir -p /var/www
- cd /var/www - cd /var/www
- git clone https://github.com/redmatrix/hubzilla.git html - git clone https://framagit.org/hubzilla/core.git html
- cd html/.homeinstall - cd html/.homeinstall
- cp hubzilla-config.txt.template hubzilla-config.txt - cp hubzilla-config.txt.template hubzilla-config.txt
- nano hubzilla-config.txt - nano hubzilla-config.txt
@ -100,7 +100,7 @@ Create bootable USB drive with Debian on it.You could use
Example for command dd... Example for command dd...
su - su -
dd if=2017-11-29-raspbian-stretch.img of=/dev/mmcblk0 dd if=2018-10-09-raspbian-stretch.img of=/dev/mmcblk0
Do not forget to unmount the SD card before and check if unmounted like in this example... Do not forget to unmount the SD card before and check if unmounted like in this example...
@ -164,7 +164,7 @@ Make the directory for apache and change diretory to it
Clone hubzilla from git ("git pull" will update it later) Clone hubzilla from git ("git pull" will update it later)
git clone https://framagit.org/hubzilla/core html git clone https://framagit.org/hubzilla/core.git html
Change to the install script Change to the install script
@ -217,14 +217,20 @@ After the daily script was executed at 05:30 (am)
- optionally view the daily log under yourdomain.org/admin/logs/ - optionally view the daily log under yourdomain.org/admin/logs/
- set the logfile to var/www/html/hubzilla-daily.log - set the logfile to var/www/html/hubzilla-daily.log
## Install Hubzilla in a Virtual Machine for Test Purposes
Modify the file "hubzilla-config.txt".
nano hubzilla-config.txt
There use
le_domain=localhost
## Note for the Rasperry ## Note for the Rasperry
The script was tested with an Raspberry 3 under Raspian (Debian 9.3, 2017-11-29-raspbian-stretch.img). The script was tested with an Raspberry 3 under Raspian (Debian 9.5, 2018-10-09-raspbian-stretch.img).
It is recommended to deinstall these programms to avoid endless updates. Use...
sudo apt-get purge wolfram-engine sonic-pi
sudo apt-get autoremove
It is recommended to run the Raspi without graphical frontend (X-Server). Use... It is recommended to run the Raspi without graphical frontend (X-Server). Use...
@ -234,7 +240,7 @@ to boot the Rapsi to the client console.
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI! DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!
On a Raspian Stretch (Debian 9) the validation of the mail address fails for the very first user. If the validation of the mail address fails for the very first registered user...
This used to happen on some *bsd distros but there was some work to fix that a year ago (2017). This used to happen on some *bsd distros but there was some work to fix that a year ago (2017).
So if your system isn't registered in DNS or DNS isn't active do So if your system isn't registered in DNS or DNS isn't active do

View File

@ -18,6 +18,8 @@ db_pass=
# Example: my.cooldomain.org # Example: my.cooldomain.org
# Example: cooldomain.org # Example: cooldomain.org
# #
# Example: localhost (test installation without certificates for httpS)
#
# Email is optional # Email is optional
# #
# #

View File

@ -816,15 +816,35 @@ install_run_selfhost
ping_domain ping_domain
configure_cron_freedns configure_cron_freedns
configure_cron_selfhost configure_cron_selfhost
install_letsencrypt
configure_apache_for_https if [ "$le_domain" != "localhost" ]
check_https then
install_letsencrypt
configure_apache_for_https
check_https
else
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
fi
install_hubzilla install_hubzilla
rewrite_to_https
install_rsnapshot if [ "$le_domain" != "localhost" ]
then
rewrite_to_https
install_rsnapshot
else
print_info "is localhost - skipped rewrite to https and installation of rsnapshot"
fi
configure_cron_daily configure_cron_daily
install_cryptosetup
write_uninstall_script if [ "$le_domain" != "localhost" ]
then
install_cryptosetup
write_uninstall_script
else
print_info "is localhost - skipped installation of cryptosetup"
fi
#set +x # stop debugging from here #set +x # stop debugging from here

115
CHANGELOG
View File

@ -1,3 +1,118 @@
Hubzilla 4.0 (2019-03-08)
- Add CURLOPT_CONNECTTIMEOUT option
- Allow parameters as final path argument in API router
- Remove clones from delivery recipients for top-level posts in favor of clone sync
- Mention php-zip module dependency in administrator guide
- Iron out some kinks with scrollToItem() in combination with collapsed content and images
- Zot API changes to support combined content (items+files) import addon
- Update PHP Version check during setup - min version is now 7.1
- Urlencode links in category widget
- Implement ability for channel visitors to be able to delete their own content
- Support zot location independent urls
- MySQL 8 admin summary compatibility
- Improved gitlab-ci environment
- Deprecate and remove addon settings in favour of per app settings
- Refactor PhotoDriver class and add tests
- Convert affinity tool to app
- Refactor linkify_tags() so it works with xchans across multiple protocols
- Add the actual mid to viewsrc for debuging reasons
- Add filter hooks and the ability to add buttons to the default status editor
- Prevent Hubzilla usage for SEO backlinks
- Implement privacy warning for forum posts via !-tag
- Set document title when title changes on a page update
- Cache embeds in the background on initial storage rather than on first access
- Custom sessionhandler support
- Update nginx and lighttpd sample server configs to explicit disallow access to util
- Introduce command line tool for managing site admins
- Various doxygen improvements
- Add privacygroup_extras_post/drop hooks
- Add collect_public_recipients hook
- Prevent memory exhaustion on zot message pickup with large message queue
- Remove experimental worker queue from core
- Add get_base_apps hook
- Improve handling of notification updates while commenting
- Add warning if upload_filesize < 4MB
- Add ITEM_TYPE_CUSTOM and hooks for processing custom item types
- Set min/maxversion for plugins to STD_VERSION unless otherwise specified
- Add option to make affinity slider 'sticky' across page loads
- Add photo_view_filter hook
- Reset page title if article has no title
- Implement the zot6 protocol
- Add PHOTO_CACHE photo type
- Basic support for HTTP3
- Add native summary support
- Disable image caching if personal or group permissions enabled
Bugfixes
- Fix guest access token xchan record not created on URL login
- Fix regression where mod oep was still using hex2bin/bin2hex instead of album hash
- Fix regression when selecting multiple images in embed images
- Fix broken sync_an_item()
- Fix page jumping on like if comments are expanded (show all x comments)
- Fix regression in mod display where an page update could display items from multiple channels
- Fix starring and filing allowed for other unsupported item types
- Fix wrong variable in z_get_temp_dir()
- Fix page jumping when liking a collapsed/expanded post
- Fix tags detection in URL
- Fix warnings in mod embedphotos
- Fix wrong variable in can_comment_on_post()
- Fix mod new_channel counting removed channels
- Fix regression where not all content variables were sslified
- Fix default values for affinity tool and other information which could be lost when approving a connection
- Fix regression in linkdropper()
- Fix issue with unset auto_save_draft variable which resultet in a javascript error
- Fix home notifications won't expand if there are more than 300 unseen network notifications ahead of them
- Fix total_identities count
- Fix delayed items propagate before publication on cloned channels
Addons
- twitter_api: fedilab needs profile_image_url_https
- New addon: content_import - imports items and files to cloned channels (this obsoletes hzfiles)
- Diaspora: prevent processing of incomplete messages in various places
- hzfiles: fix add missing load/unload functions
- chess: do not look for games if we have no game_id - improves initial pageload performance
- chess: convert to app
- channelreputation: convert to app
- irc: convert to per app
- Provide the addon_common directory for common addon libraries
- fuzzloc: convert to app
- flattrwidget: convert to app
- jappixmini: convert to app
- xmpp: convert to app
- visage: convert to app
- diaspora: reflect linkify_tags() rewrite
- twitter: convert to app
- smileybutton: convert to app
- skeleton: convert to app
- planets: convert to app
- pumpio: convert to app
- pageheader: convert to app
- nsabait: convert to app
- dwpost: convert to app
- diaspora: set the preserve_lf option in various places
- diaspora: fix comments from unknown persons are not accpted
- nofed: convert to app
- ljpost: convert to app
- diaspora: call update_queue_item() if delivery failed
- pubcrawl: call update_queue_item() if delivery failed
- libertree: convert to app
- New addon: queueworker advanced - queue handling (experimental)
- gallery: extended functionality: implements stream image viewer, converts images at the beginning of a post to a gallery
- authchoose: correction to query, add affinity setting
- New addon: photocache - local caching for public photos
- New addon: totp - two factor authentication using time-based one-time passwords
Hubzilla 3.8.9 (2018-02-03)
- Fix typos in mod oep
- Fix page jumping when liking collapsed/expanded post
- Fix failure to import mail in mod import
- Fix wrong channel count in mod new_channel
- Fix diaspora addon regression
- Remove deprecated diaspora addon endpoint
- Fix wrong function call in gallery addon
Hubzilla 3.8.8 (2018-12-22) Hubzilla 3.8.8 (2018-12-22)
- Fix issue with linkinfo - Fix issue with linkinfo
- Fix cURL with HTTP/2 - Fix cURL with HTTP/2

View File

@ -0,0 +1,27 @@
<?php /** @file */
namespace Zotlabs\Daemon;
class Cache_embeds {
static public function run($argc,$argv) {
if(! $argc == 2)
return;
$c = q("select body from item where id = %d ",
dbesc(intval($argv[1]))
);
if(! $c)
return;
$item = $c[0];
// bbcode conversion by default processes embeds that aren't already cached.
// Ignore the returned html output.
bbcode($item['body']);
}
}

View File

@ -95,6 +95,29 @@ class Cron {
} }
} }
// Clean expired photos from cache
$age = get_config('system','active_expire_days', '30');
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
);
if($r) {
foreach($r as $rr) {
$file = dbunescbin($rr['content']);
if(is_file($file)) {
@unlink($file);
logger('info: deleted cached photo file ' . $file, LOGGER_DEBUG);
}
}
}
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
);
// publish any applicable items that were set to be published in the future // publish any applicable items that were set to be published in the future
// (time travel posts). Restrict to items that have come of age in the last // (time travel posts). Restrict to items that have come of age in the last
// couple of days to limit the query to something reasonable. // couple of days to limit the query to something reasonable.

View File

@ -2,6 +2,8 @@
namespace Zotlabs\Daemon; namespace Zotlabs\Daemon;
use Zotlabs\Lib\DReport;
require_once('include/zot.php'); require_once('include/zot.php');
require_once('include/queue_fn.php'); require_once('include/queue_fn.php');
@ -58,11 +60,12 @@ class Deliver {
foreach($dresult as $xx) { foreach($dresult as $xx) {
if(is_array($xx) && array_key_exists('message_id',$xx)) { if(is_array($xx) && array_key_exists('message_id',$xx)) {
if(delivery_report_is_storable($xx)) { if(DReport::is_storable($xx)) {
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s' ) ", q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
dbesc($xx['message_id']), dbesc($xx['message_id']),
dbesc($xx['location']), dbesc($xx['location']),
dbesc($xx['recipient']), dbesc($xx['recipient']),
dbesc(($xx['name']) ? $xx['name'] : EMPTY_STR),
dbesc($xx['status']), dbesc($xx['status']),
dbesc(datetime_convert($xx['date'])), dbesc(datetime_convert($xx['date'])),
dbesc($xx['sender']) dbesc($xx['sender'])

View File

@ -16,8 +16,6 @@ if(array_search( __file__ , get_included_files()) === 0) {
class Master { class Master {
static public $queueworker = null;
static public function Summon($arr) { static public function Summon($arr) {
proc_run('php','Zotlabs/Daemon/Master.php',$arr); proc_run('php','Zotlabs/Daemon/Master.php',$arr);
} }
@ -25,126 +23,21 @@ class Master {
static public function Release($argc,$argv) { static public function Release($argc,$argv) {
cli_startup(); cli_startup();
$maxworkers = get_config('system','max_queue_workers'); $hookinfo = [
'argv'=>$argv
];
if (!$maxworkers || $maxworkers == 0) { call_hooks ('daemon_master_release',$hookinfo);
logger('Master: release: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$cls::run($argc,$argv);
self::ClearQueue();
} else {
logger('Master: enqueue: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
$workinfo = ['argc'=>$argc,'argv'=>$argv];
q("insert into config (cat,k,v) values ('queuework','%s','%s')",
dbesc(uniqid('workitem:',true)),
dbesc(serialize($workinfo)));
self::Process();
}
}
static public function GetWorkerID() { $argv = $hookinfo['argv'];
$maxworkers = get_config('system','max_queue_workers'); $argc = count($argv);
$maxworkers = ($maxworkers) ? $maxworkers : 3;
$workermaxage = get_config('system','max_queue_worker_age'); if ((!is_array($argv) || (count($argv) < 1))) {
$workermaxage = ($workermaxage) ? $workermaxage : 300; return;
$workers = q("select * from config where cat='queueworkers' and k like '%s'", 'workerstarted_%');
if (count($workers) > $maxworkers) {
foreach ($workers as $idx => $worker) {
$curtime = time();
$age = (intval($curtime) - intval($worker['v']));
if ( $age > $workermaxage) {
logger("Prune worker: ".$worker['k'], LOGGER_ALL, LOGGER_DEBUG);
$k = explode('_',$worker['k']);
q("delete from config where cat='queueworkers' and k='%s'",
'workerstarted_'.$k[1]);
q("update config set k='%s' where cat='queuework' and k='%s'",
dbesc(uniqid('workitem:',true)),
'workitem_'.$k[1]);
unset($workers[$idx]);
}
}
if (count($workers) > $maxworkers) {
return false;
}
}
return uniqid('',true);
}
static public function Process() {
self::$queueworker = self::GetWorkerID();
if (!self::$queueworker) {
logger('Master: unable to obtain worker ID.');
killme();
} }
set_config('queueworkers','workerstarted_'.self::$queueworker,time()); logger('Master: release: ' . json_encode($argv), LOGGER_ALL,LOG_DEBUG);
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$workersleep = get_config('system','queue_worker_sleep'); $cls::run($argc,$argv);
$workersleep = ($workersleep) ? $workersleep : 5;
cli_startup();
$work = q("update config set k='%s' where cat='queuework' and k like '%s' limit 1",
'workitem_'.self::$queueworker,
dbesc('workitem:%'));
$jobs = 0;
while ($work) {
$workitem = q("select * from config where cat='queuework' and k='%s'",
'workitem_'.self::$queueworker);
if (isset($workitem[0])) {
$jobs++;
$workinfo = unserialize($workitem[0]['v']);
$argc = $workinfo['argc'];
$argv = $workinfo['argv'];
logger('Master: process: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
//Delete unclaimed duplicate workitems.
q("delete from config where cat='queuework' and k='workitem' and v='%s'",
serialize($argv));
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$cls::run($argc,$argv);
//Right now we assume that if we get a return, everything is OK.
//At some point we may want to test whether the run returns true/false
// and requeue the work to be tried again. But we probably want
// to implement some sort of "retry interval" first.
q("delete from config where cat='queuework' and k='%s'",
'workitem_'.self::$queueworker);
} else {
break;
}
sleep ($workersleep);
$work = q("update config set k='%s' where cat='queuework' and k like '%s' limit 1",
'workitem_'.self::$queueworker,
dbesc('workitem:%'));
}
logger('Master: Worker Thread: queue items processed:' . $jobs);
q("delete from config where cat='queueworkers' and k='%s'",
'workerstarted_'.self::$queueworker);
} }
static public function ClearQueue() {
$work = q("select * from config where cat='queuework' and k like '%s'",
dbesc('workitem%'));
foreach ($work as $workitem) {
$workinfo = unserialize($workitem['v']);
$argc = $workinfo['argc'];
$argv = $workinfo['argv'];
logger('Master: process: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$cls::run($argc,$argv);
}
$work = q("delete from config where cat='queuework' and k like '%s'",
dbesc('workitem%'));
}
} }

View File

@ -2,6 +2,8 @@
namespace Zotlabs\Daemon; namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzot;
require_once('include/queue_fn.php'); require_once('include/queue_fn.php');
require_once('include/html2plain.php'); require_once('include/html2plain.php');
require_once('include/conversation.php'); require_once('include/conversation.php');
@ -345,6 +347,15 @@ class Notifier {
$encoded_item = encode_item($target_item); $encoded_item = encode_item($target_item);
// activitystreams version
$m = get_iconfig($target_item,'activitystreams','signed_data');
if($m) {
$activity = json_decode($m,true);
}
else {
$activity = \Zotlabs\Lib\Activity::encode_activity($target_item);
}
// Send comments to the owner to re-deliver to everybody in the conversation // Send comments to the owner to re-deliver to everybody in the conversation
// We only do this if the item in question originated on this site. This prevents looping. // We only do this if the item in question originated on this site. This prevents looping.
// To clarify, a site accepting a new comment is responsible for sending it to the owner for relay. // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
@ -401,6 +412,12 @@ class Notifier {
$private = false; $private = false;
$recipients = collect_recipients($parent_item,$private); $recipients = collect_recipients($parent_item,$private);
if ($top_level_post) {
// remove clones who will receive the post via sync
$recipients = array_diff($recipients, [ $target_item['owner_xchan'] ]);
}
// FIXME add any additional recipients such as mentions, etc. // FIXME add any additional recipients such as mentions, etc.
// don't send deletions onward for other people's stuff // don't send deletions onward for other people's stuff
@ -423,6 +440,8 @@ class Notifier {
$x['body'] = 'private'; $x['body'] = 'private';
logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
//logger('notifier: encoded activity: ' . print_r($activity,true), LOGGER_DATA, LOG_DEBUG);
stringify_array_elms($recipients); stringify_array_elms($recipients);
if(! $recipients) { if(! $recipients) {
logger('no recipients'); logger('no recipients');
@ -561,7 +580,7 @@ class Notifier {
logger('notifier_hub: ' . $hub['hubloc_url'],LOGGER_DEBUG); logger('notifier_hub: ' . $hub['hubloc_url'],LOGGER_DEBUG);
if($hub['hubloc_network'] !== 'zot') { if(! in_array($hub['hubloc_network'], [ 'zot','zot6' ])) {
$narr = [ $narr = [
'channel' => $channel, 'channel' => $channel,
'upstream' => $upstream, 'upstream' => $upstream,
@ -610,20 +629,32 @@ class Notifier {
continue; continue;
} }
// default: zot protocol if(! in_array($hub['hubloc_network'], [ 'zot','zot6' ])) {
continue;
}
// Do not change this to a uuid as long as we have traditional zot servers
// in the loop. The signature verification step can't handle dashes in the
// hashes.
$hash = random_string(48);
$hash = random_string();
$packet = null; $packet = null;
$pmsg = ''; $pmsg = '';
if($packet_type === 'refresh' || $packet_type === 'purge') { if($packet_type === 'refresh' || $packet_type === 'purge') {
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); if($hub['hubloc_network'] === 'zot6') {
$packet = Libzot::build_packet($channel, $packet_type, ids_to_array($packet_recips,'hash'));
}
else {
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null));
}
} }
if($packet_type === 'keychange') { if($packet_type === 'keychange' && $hub['hubloc_network'] === 'zot') {
$pmsg = get_pconfig($channel['channel_id'],'system','keychange'); $pmsg = get_pconfig($channel['channel_id'],'system','keychange');
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null));
} }
elseif($packet_type === 'request') { elseif($packet_type === 'request' && $hub['hubloc_network'] === 'zot') {
$env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : '');
$packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'], $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'],
$hash, array('message_id' => $request_message_id) $hash, array('message_id' => $request_message_id)
@ -636,6 +667,7 @@ class Notifier {
'account_id' => $channel['channel_account_id'], 'account_id' => $channel['channel_account_id'],
'channel_id' => $channel['channel_id'], 'channel_id' => $channel['channel_id'],
'posturl' => $hub['hubloc_callback'], 'posturl' => $hub['hubloc_callback'],
'driver' => $hub['hubloc_network'],
'notify' => $packet, 'notify' => $packet,
'msg' => (($pmsg) ? json_encode($pmsg) : '') 'msg' => (($pmsg) ? json_encode($pmsg) : '')
)); ));
@ -643,18 +675,43 @@ class Notifier {
else { else {
$env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : '');
// currently zot6 delivery is only performed on normal items and not sync items or mail or anything else
// Eventually we will do this for all deliveries, but for now ensure this is precisely what we are dealing
// with before switching to zot6 as the primary zot6 handler checks for the existence of a message delivery report
// to trigger dequeue'ing
$z6 = (($encoded_item && $encoded_item['type'] === 'activity' && (! array_key_exists('allow_cid',$encoded_item))) ? true : false); if($hub['hubloc_network'] === 'zot6') {
if($z6) { $zenv = [];
$packet = zot6_build_packet($channel,'notify',$env, json_encode($encoded_item), (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash); if($env) {
foreach($env as $e) {
$zenv[] = $e['hash'];
}
}
$packet_type = (($upstream || $uplink) ? 'response' : 'activity');
// block zot private reshares from zot6, as this could cause a number of privacy issues
// due to parenting differences between the reshare implementations. In zot a reshare is
// a standalone parent activity and in zot6 it is a followup/child of the original activity.
// For public reshares, some comments to the reshare on the zot fork will not make it to zot6
// due to these different message models. This cannot be prevented at this time.
if($packet_type === 'activity' && $activity['type'] === 'Announce' && intval($target_item['item_private'])) {
continue;
}
$packet = Libzot::build_packet($channel,$packet_type,$zenv,$activity,'activitystreams',(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto']);
} }
else { else {
$packet = zot_build_packet($channel,'notify',$env, (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash); // currently zot6 delivery is only performed on normal items and not sync items or mail or anything else
// Eventually we will do this for all deliveries, but for now ensure this is precisely what we are dealing
// with before switching to zot6 as the primary zot6 handler checks for the existence of a message delivery report
// to trigger dequeue'ing
$z6 = (($encoded_item && $encoded_item['type'] === 'activity' && (! array_key_exists('allow_cid',$encoded_item))) ? true : false);
if($z6) {
$packet = zot6_build_packet($channel,'notify',$env, json_encode($encoded_item), (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash);
}
else {
$packet = zot_build_packet($channel,'notify',$env, (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash);
}
} }
queue_insert( queue_insert(
@ -663,6 +720,7 @@ class Notifier {
'account_id' => $target_item['aid'], 'account_id' => $target_item['aid'],
'channel_id' => $target_item['uid'], 'channel_id' => $target_item['uid'],
'posturl' => $hub['hubloc_callback'], 'posturl' => $hub['hubloc_callback'],
'driver' => $hub['hubloc_network'],
'notify' => $packet, 'notify' => $packet,
'msg' => json_encode($encoded_item) 'msg' => json_encode($encoded_item)
] ]

View File

@ -69,7 +69,7 @@ class Onepoll {
return; return;
} }
if($contact['xchan_network'] !== 'zot') if(! in_array($contact['xchan_network'],['zot','zot6']))
return; return;
// update permissions // update permissions

View File

@ -110,7 +110,7 @@ class Poller {
} }
if($contact['xchan_network'] !== 'zot') if(! in_array($contact['xchan_network'],['zot','zot6']))
continue; continue;
if($c == $t) { if($c == $t) {

View File

@ -12,7 +12,6 @@ class Queue {
require_once('include/items.php'); require_once('include/items.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
if($argc > 1) if($argc > 1)
$queue_id = $argv[1]; $queue_id = $argv[1];
else else
@ -62,9 +61,19 @@ class Queue {
// the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
// or twice a day. // or twice a day.
$r = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ", $sqlrandfunc = db_getfunc('rand');
$r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
db_utcnow() db_utcnow()
); );
while ($r) {
foreach($r as $rv) {
queue_deliver($rv);
}
$r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1",
db_utcnow()
);
}
} }
if(! $r) if(! $r)
return; return;

View File

@ -2,18 +2,21 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use Zotlabs\Lib\Libzot; use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Group;
class Activity { class Activity {
static function encode_object($x) { static function encode_object($x) {
if(($x) && (! is_array($x)) && (substr(trim($x),0,1)) === '{' ) { if(($x) && (! is_array($x)) && (substr(trim($x),0,1)) === '{' ) {
$x = json_decode($x,true); $x = json_decode($x,true);
} }
if(is_array($x) && array_key_exists('asld',$x)) {
$x = $x['asld'];
}
if($x['type'] === ACTIVITY_OBJ_PERSON) { if($x['type'] === ACTIVITY_OBJ_PERSON) {
return self::fetch_person($x); return self::fetch_person($x);
} }
@ -26,11 +29,57 @@ class Activity {
if($x['type'] === ACTIVITY_OBJ_THING) { if($x['type'] === ACTIVITY_OBJ_THING) {
return self::fetch_thing($x); return self::fetch_thing($x);
} }
if($x['type'] === ACTIVITY_OBJ_EVENT) {
return self::fetch_event($x);
}
if($x['type'] === ACTIVITY_OBJ_PHOTO) {
return self::fetch_image($x);
}
return $x; return $x;
} }
static function fetch($url,$channel = null) {
$redirects = 0;
if(! check_siteallowed($url)) {
logger('blacklisted: ' . $url);
return null;
}
if(! $channel) {
$channel = get_sys_channel();
}
logger('fetch: ' . $url, LOGGER_DEBUG);
if(strpos($url,'x-zot:') === 0) {
$x = ZotURL::fetch($url,$channel);
}
else {
$m = parse_url($url);
$headers = [
'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'Host' => $m['host'],
'(request-target)' => 'get ' . get_request_string($url),
'Date' => datetime_convert('UTC','UTC','now','D, d M Y H:i:s') . ' UTC'
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
$x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] );
}
if($x['success']) {
$y = json_decode($x['body'],true);
logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
return json_decode($x['body'], true);
}
else {
logger('fetch failed: ' . $url);
}
return null;
}
static function fetch_person($x) { static function fetch_person($x) {
return self::fetch_profile($x); return self::fetch_profile($x);
@ -93,6 +142,63 @@ class Activity {
} }
} }
static function fetch_image($x) {
$ret = [
'type' => 'Image',
'id' => $x['id'],
'name' => $x['title'],
'content' => bbcode($x['body']),
'source' => [ 'mediaType' => 'text/bbcode', 'content' => $x['body'] ],
'published' => datetime_convert('UTC','UTC',$x['created'],ATOM_TIME),
'updated' => datetime_convert('UTC','UTC', $x['edited'],ATOM_TIME),
'url' => [
'type' => 'Link',
'mediaType' => $x['link'][0]['type'],
'href' => $x['link'][0]['href'],
'width' => $x['link'][0]['width'],
'height' => $x['link'][0]['height']
]
];
return $ret;
}
static function fetch_event($x) {
// convert old Zot event objects to ActivityStreams Event objects
if (array_key_exists('content',$x) && array_key_exists('dtstart',$x)) {
$ev = bbtoevent($x['content']);
if($ev) {
$actor = null;
if(array_key_exists('author',$x) && array_key_exists('link',$x['author'])) {
$actor = $x['author']['link'][0]['href'];
}
$y = [
'type' => 'Event',
'id' => z_root() . '/event/' . $ev['event_hash'],
'summary' => bbcode($ev['summary']),
// RFC3339 Section 4.3
'startTime' => (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')),
'content' => bbcode($ev['description']),
'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location']) ],
'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ],
'actor' => $actor,
];
if($actor) {
return $y;
}
}
}
return $x;
}
static function encode_item_collection($items,$id,$type,$extra = null) { static function encode_item_collection($items,$id,$type,$extra = null) {
$ret = [ $ret = [
@ -167,7 +273,7 @@ class Activity {
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
if($i['title']) if($i['title'])
$ret['title'] = bbcode($i['title']); $ret['name'] = $i['title'];
$ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME);
if($i['created'] !== $i['edited']) if($i['created'] !== $i['edited'])
@ -342,10 +448,17 @@ class Activity {
$ret['type'] = 'Tombstone'; $ret['type'] = 'Tombstone';
$ret['formerType'] = self::activity_obj_mapper($i['obj_type']); $ret['formerType'] = self::activity_obj_mapper($i['obj_type']);
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
$actor = self::encode_person($i['author'],false);
if($actor)
$ret['actor'] = $actor;
else
return [];
return $ret; return $ret;
} }
$ret['type'] = self::activity_mapper($i['verb']); $ret['type'] = self::activity_mapper($i['verb']);
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
if($i['title']) if($i['title'])
@ -417,6 +530,10 @@ class Activity {
if(! is_array($i['obj'])) { if(! is_array($i['obj'])) {
$i['obj'] = json_decode($i['obj'],true); $i['obj'] = json_decode($i['obj'],true);
} }
if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) {
$i['obj']['id'] = $i['id'];
}
$obj = self::encode_object($i['obj']); $obj = self::encode_object($i['obj']);
if($obj) if($obj)
$ret['object'] = $obj; $ret['object'] = $obj;
@ -540,6 +657,12 @@ class Activity {
} }
static function activity_mapper($verb) { static function activity_mapper($verb) {
if(strpos($verb,'/') === false) { if(strpos($verb,'/') === false) {
@ -556,6 +679,9 @@ class Activity {
'http://activitystrea.ms/schema/1.0/tag' => 'Add', 'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow', 'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow', 'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
]; ];
@ -582,6 +708,70 @@ class Activity {
} }
static function activity_decode_mapper($verb) {
$acts = [
'http://activitystrea.ms/schema/1.0/post' => 'Create',
'http://activitystrea.ms/schema/1.0/share' => 'Announce',
'http://activitystrea.ms/schema/1.0/update' => 'Update',
'http://activitystrea.ms/schema/1.0/like' => 'Like',
'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
'http://purl.org/zot/activity/dislike' => 'Dislike',
'http://activitystrea.ms/schema/1.0/tag' => 'Add',
'http://activitystrea.ms/schema/1.0/follow' => 'Follow',
'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow',
'http://purl.org/zot/activity/attendyes' => 'Accept',
'http://purl.org/zot/activity/attendno' => 'Reject',
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
];
foreach($acts as $k => $v) {
if($verb === $v) {
return $k;
}
}
logger('Unmapped activity: ' . $verb);
return 'Create';
}
static function activity_obj_decode_mapper($obj) {
$objs = [
'http://activitystrea.ms/schema/1.0/note' => 'Note',
'http://activitystrea.ms/schema/1.0/note' => 'Article',
'http://activitystrea.ms/schema/1.0/comment' => 'Note',
'http://activitystrea.ms/schema/1.0/person' => 'Person',
'http://purl.org/zot/activity/profile' => 'Profile',
'http://activitystrea.ms/schema/1.0/photo' => 'Image',
'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon',
'http://activitystrea.ms/schema/1.0/event' => 'Event',
'http://activitystrea.ms/schema/1.0/wiki' => 'Document',
'http://purl.org/zot/activity/location' => 'Place',
'http://purl.org/zot/activity/chessgame' => 'Game',
'http://purl.org/zot/activity/tagterm' => 'zot:Tag',
'http://purl.org/zot/activity/thing' => 'Object',
'http://purl.org/zot/activity/file' => 'zot:File',
'http://purl.org/zot/activity/mood' => 'zot:Mood',
];
foreach($objs as $k => $v) {
if($obj === $v) {
return $k;
}
}
logger('Unmapped activity object: ' . $obj);
return 'Note';
}
static function activity_obj_mapper($obj) { static function activity_obj_mapper($obj) {
if(strpos($obj,'/') === false) { if(strpos($obj,'/') === false) {
@ -1149,7 +1339,7 @@ class Activity {
} }
if($act->obj['type'] === 'Note' && $s['attach']) { if($act->obj['type'] === 'Note' && $s['attach']) {
$s['body'] .= self::bb_attach($s['attach']); $s['body'] .= self::bb_attach($s['attach'],$s['body']);
} }
// we will need a hook here to extract magnet links e.g. peertube // we will need a hook here to extract magnet links e.g. peertube
@ -1230,21 +1420,39 @@ class Activity {
} }
static function get_actor_bbmention($id) {
$x = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' or hubloc_id_url = '%s' limit 1",
dbesc($id),
dbesc($id)
);
if($x) {
return sprintf('@[zrl=%s]%s[/zrl]',$x[0]['xchan_url'],$x[0]['xchan_name']);
}
return '@{' . $id . '}';
}
static function decode_note($act) { static function decode_note($act) {
$response_activity = false;
$s = []; $s = [];
if(is_array($act->obj)) {
$content = self::get_content($act->obj);
$content = self::get_content($act->obj); }
$s['owner_xchan'] = $act->actor['id']; $s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id']; $s['author_xchan'] = $act->actor['id'];
$s['mid'] = $act->id; // ensure we store the original actor
$s['parent_mid'] = $act->parent_id; self::actor_store($act->actor['id'],$act->actor);
$s['mid'] = $act->obj['id'];
$s['parent_mid'] = $act->parent_id;
if($act->data['published']) { if($act->data['published']) {
$s['created'] = datetime_convert('UTC','UTC',$act->data['published']); $s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
@ -1259,103 +1467,323 @@ class Activity {
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']); $s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
} }
if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept' ])) {
$response_activity = true;
$s['mid'] = $act->id;
$s['parent_mid'] = $act->obj['id'];
// over-ride the object timestamp with the activity
if($act->data['published']) {
$s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
}
if($act->data['updated']) {
$s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']);
}
$obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj));
// ensure we store the original actor
self::actor_store($obj_actor['id'],$obj_actor);
$mention = self::get_actor_bbmention($obj_actor['id']);
if($act->type === 'Like') {
$content['content'] = sprintf( t('Likes %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
if($act->type === 'Dislike') {
$content['content'] = sprintf( t('Doesn\'t like %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
if($act->type === 'Accept' && $act->obj['type'] === 'Event' ) {
$content['content'] = sprintf( t('Will attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
if($act->type === 'Reject' && $act->obj['type'] === 'Event' ) {
$content['content'] = sprintf( t('Will not attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
if($act->type === 'TentativeAccept' && $act->obj['type'] === 'Event' ) {
$content['content'] = sprintf( t('May attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
}
if($act->type === 'Announce') {
$content['content'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
}
}
if(! $s['created']) if(! $s['created'])
$s['created'] = datetime_convert(); $s['created'] = datetime_convert();
if(! $s['edited']) if(! $s['edited'])
$s['edited'] = $s['created']; $s['edited'] = $s['created'];
if(in_array($act->type,['Announce'])) { $s['title'] = self::bb_content($content,'name');
$root_content = self::get_content($act->raw); $s['summary'] = self::bb_content($content,'summary');
$s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content'));
$s['title'] = self::bb_content($root_content,'name'); $s['verb'] = self::activity_decode_mapper($act->type);
$s['summary'] = self::bb_content($root_content,'summary');
$s['body'] = (self::bb_content($root_content,'bbcode') ? : self::bb_content($root_content,'content'));
if(strpos($s['body'],'[share') === false) {
// @fixme - error check and set defaults if($act->type === 'Tombstone' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) {
$name = urlencode($act->obj['actor']['name']);
$profile = $act->obj['actor']['id'];
$photo = $act->obj['icon']['url'];
$s['body'] .= "\r\n[share author='" . $name .
"' profile='" . $profile .
"' avatar='" . $photo .
"' link='" . $act->obj['id'] .
"' auth='" . ((is_matrix_url($act->obj['id'])) ? 'true' : 'false' ) .
"' posted='" . $act->obj['published'] .
"' message_id='" . $act->obj['id'] .
"']";
}
}
else {
$s['title'] = self::bb_content($content,'name');
$s['summary'] = self::bb_content($content,'summary');
$s['body'] = (self::bb_content($content,'bbcode') ? : self::bb_content($content,'content'));
}
$s['verb'] = self::activity_mapper($act->type);
if($act->type === 'Tombstone') {
$s['item_deleted'] = 1; $s['item_deleted'] = 1;
} }
$s['obj_type'] = self::activity_obj_mapper($act->obj['type']); $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']);
$s['obj'] = $act->obj; if($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
}
if($act->obj['type'] === 'Event') {
$s['obj'] = [];
$s['obj']['asld'] = $act->obj;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
$s['obj']['id'] = $act->obj['id'];
$s['obj']['title'] = $act->obj['summary'];
if(strpos($act->obj['startTime'],'Z'))
$s['obj']['adjust'] = true;
else
$s['obj']['adjust'] = false;
$s['obj']['dtstart'] = datetime_convert('UTC','UTC',$act->obj['startTime']);
if($act->obj['endTime'])
$s['obj']['dtend'] = datetime_convert('UTC','UTC',$act->obj['endTime']);
else
$s['obj']['nofinish'] = true;
$s['obj']['description'] = $act->obj['content'];
if(array_path_exists('location/content',$act->obj))
$s['obj']['location'] = $act->obj['location']['content'];
}
else {
$s['obj'] = $act->obj;
}
$instrument = $act->get_property_obj('instrument'); $instrument = $act->get_property_obj('instrument');
if(! $instrument) if((! $instrument) && (! $response_activity)) {
$instrument = $act->get_property_obj('instrument',$act->obj); $instrument = $act->get_property_obj('instrument',$act->obj);
}
if($instrument && array_key_exists('type',$instrument) if($instrument && array_key_exists('type',$instrument)
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) { && $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) {
$s['app'] = escape_tags($instrument['name']); $s['app'] = escape_tags($instrument['name']);
} }
$a = self::decode_taxonomy($act->obj);
if($a) { if(! $response_activity) {
$s['term'] = $a; $a = self::decode_taxonomy($act->obj);
if($a) {
$s['term'] = $a;
foreach($a as $b) {
if($b['ttype'] === TERM_EMOJI) {
$s['title'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['title']);
$s['summary'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['summary']);
$s['body'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['body']);
}
}
}
$a = self::decode_attachment($act->obj);
if($a) {
$s['attach'] = $a;
}
} }
$a = self::decode_attachment($act->obj); if($act->obj['type'] === 'Note' && $s['attach']) {
if($a) { $s['body'] .= self::bb_attach($s['attach'],$s['body']);
$s['attach'] = $a;
} }
// we will need a hook here to extract magnet links e.g. peertube // we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our // right now just link to the largest mp4 we find that will fit in our
// standard content region // standard content region
if($act->obj['type'] === 'Video') { if(! $response_activity) {
if($act->obj['type'] === 'Video') {
$vtypes = [ $vtypes = [
'video/mp4', 'video/mp4',
'video/ogg', 'video/ogg',
'video/webm' 'video/webm'
]; ];
$mps = []; $mps = [];
if(array_key_exists('url',$act->obj) && is_array($act->obj['url'])) { $ptr = null;
foreach($act->obj['url'] as $vurl) {
if(in_array($vurl['mimeType'], $vtypes)) { if(array_key_exists('url',$act->obj)) {
if(! array_key_exists('width',$vurl)) { if(is_array($act->obj['url'])) {
$vurl['width'] = 0; if(array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
} }
$mps[] = $vurl; else {
$ptr = [ $act->obj['url'] ];
}
foreach($ptr as $vurl) {
// peertube uses the non-standard element name 'mimeType' here
if(array_key_exists('mimeType',$vurl)) {
if(in_array($vurl['mimeType'], $vtypes)) {
if(! array_key_exists('width',$vurl)) {
$vurl['width'] = 0;
}
$mps[] = $vurl;
}
}
elseif(array_key_exists('mediaType',$vurl)) {
if(in_array($vurl['mediaType'], $vtypes)) {
if(! array_key_exists('width',$vurl)) {
$vurl['width'] = 0;
}
$mps[] = $vurl;
}
}
}
}
if($mps) {
usort($mps,[ __CLASS__, 'vid_sort' ]);
foreach($mps as $m) {
if(intval($m['width']) < 500 && self::media_not_in_body($m['href'],$s['body'])) {
$s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
break;
}
}
}
elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) {
$s['body'] .= "\n\n" . '[video]' . $act->obj['url'] . '[/video]';
} }
} }
} }
if($mps) {
usort($mps,'as_vid_sort'); if($act->obj['type'] === 'Audio') {
foreach($mps as $m) {
if(intval($m['width']) < 500) { $atypes = [
$s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]'; 'audio/mpeg',
break; 'audio/ogg',
'audio/wav'
];
$ptr = null;
if(array_key_exists('url',$act->obj)) {
if(is_array($act->obj['url'])) {
if(array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
}
else {
$ptr = [ $act->obj['url'] ];
}
foreach($ptr as $vurl) {
if(in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'],$s['body'])) {
$s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]';
break;
}
}
}
elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) {
$s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]';
}
}
}
// avoid double images from hubzilla to zap/osada
if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) {
$ptr = null;
if(array_key_exists('url',$act->obj)) {
if(is_array($act->obj['url'])) {
if(array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
}
else {
$ptr = [ $act->obj['url'] ];
}
foreach($ptr as $vurl) {
if(strpos($s['body'],$vurl['href']) === false) {
$s['body'] .= "\n\n" . '[zmg]' . $vurl['href'] . '[/zmg]';
break;
}
}
}
elseif(is_string($act->obj['url'])) {
if(strpos($s['body'],$act->obj['url']) === false) {
$s['body'] .= "\n\n" . '[zmg]' . $act->obj['url'] . '[/zmg]';
}
} }
} }
} }
if($act->obj['type'] === 'Page' && ! $s['body']) {
$ptr = null;
$purl = EMPTY_STR;
if(array_key_exists('url',$act->obj)) {
if(is_array($act->obj['url'])) {
if(array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
}
else {
$ptr = [ $act->obj['url'] ];
}
foreach($ptr as $vurl) {
if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') {
$purl = $vurl['href'];
break;
}
elseif(array_key_exists('mimeType',$vurl) && $vurl['mimeType'] === 'text/html') {
$purl = $vurl['href'];
break;
}
}
}
elseif(is_string($act->obj['url'])) {
$purl = $act->obj['url'];
}
if($purl) {
$li = z_fetch_url(z_root() . '/linkinfo?binurl=' . bin2hex($purl));
if($li['success'] && $li['body']) {
$s['body'] .= "\n" . $li['body'];
}
else {
$s['body'] .= "\n\n" . $purl;
}
}
}
}
}
if(in_array($act->obj['type'],[ 'Note','Article','Page' ])) {
$ptr = null;
if(array_key_exists('url',$act->obj)) {
if(is_array($act->obj['url'])) {
if(array_key_exists(0,$act->obj['url'])) {
$ptr = $act->obj['url'];
}
else {
$ptr = [ $act->obj['url'] ];
}
foreach($ptr as $vurl) {
if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') {
$s['plink'] = $vurl['href'];
break;
}
}
}
elseif(is_string($act->obj['url'])) {
$s['plink'] = $act->obj['url'];
}
}
}
if(! $s['plink']) {
$s['plink'] = $s['mid'];
} }
if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips))) if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
@ -1371,8 +1799,6 @@ class Activity {
} }
static function announce_note($channel,$observer_hash,$act) { static function announce_note($channel,$observer_hash,$act) {
$s = []; $s = [];
@ -1464,7 +1890,7 @@ class Activity {
$body .= self::bb_content($content,'content'); $body .= self::bb_content($content,'content');
if($act->obj['type'] === 'Note' && $s['attach']) { if($act->obj['type'] === 'Note' && $s['attach']) {
$body .= self::bb_attach($s['attach']); $body .= self::bb_attach($s['attach'],$body);
} }
$body .= "[/share]"; $body .= "[/share]";
@ -1642,19 +2068,26 @@ class Activity {
} }
static function bb_attach($attach) {
static function bb_attach($attach,$body) {
$ret = false; $ret = false;
foreach($attach as $a) { foreach($attach as $a) {
if(strpos($a['type'],'image') !== false) { if(strpos($a['type'],'image') !== false) {
$ret .= "\n\n" . '[img]' . $a['href'] . '[/img]'; if(self::media_not_in_body($a['href'],$body)) {
$ret .= "\n\n" . '[img]' . $a['href'] . '[/img]';
}
} }
if(array_key_exists('type',$a) && strpos($a['type'], 'video') === 0) { if(array_key_exists('type',$a) && strpos($a['type'], 'video') === 0) {
$ret .= "\n\n" . '[video]' . $a['href'] . '[/video]'; if(self::media_not_in_body($a['href'],$body)) {
$ret .= "\n\n" . '[video]' . $a['href'] . '[/video]';
}
} }
if(array_key_exists('type',$a) && strpos($a['type'], 'audio') === 0) { if(array_key_exists('type',$a) && strpos($a['type'], 'audio') === 0) {
$ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]'; if(self::media_not_in_body($a['href'],$body)) {
$ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]';
}
} }
} }
@ -1662,16 +2095,31 @@ class Activity {
} }
// check for the existence of existing media link in body
static function media_not_in_body($s,$body) {
if((strpos($body,']' . $s . '[/img]') === false) &&
(strpos($body,']' . $s . '[/zmg]') === false) &&
(strpos($body,']' . $s . '[/video]') === false) &&
(strpos($body,']' . $s . '[/audio]') === false)) {
return true;
}
return false;
}
static function bb_content($content,$field) { static function bb_content($content,$field) {
require_once('include/html2bbcode.php'); require_once('include/html2bbcode.php');
require_once('include/event.php');
$ret = false; $ret = false;
if(is_array($content[$field])) { if(is_array($content[$field])) {
foreach($content[$field] as $k => $v) { foreach($content[$field] as $k => $v) {
$ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]'; $ret .= html2bbcode($v);
// save this for auto-translate or dynamic filtering
// $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]';
} }
} }
else { else {
@ -1682,6 +2130,9 @@ class Activity {
$ret = html2bbcode($content[$field]); $ret = html2bbcode($content[$field]);
} }
} }
if($field === 'content' && $content['event'] && (! strpos($ret,'[event'))) {
$ret .= format_event_bbcode($content['event']);
}
return $ret; return $ret;
} }
@ -1690,21 +2141,52 @@ class Activity {
static function get_content($act) { static function get_content($act) {
$content = []; $content = [];
if (! $act) { $event = null;
if ((! $act) || (! is_array($act))) {
return $content; return $content;
} }
if($act['type'] === 'Event') {
$adjust = false;
$event = [];
$event['event_hash'] = $act['id'];
if(array_key_exists('startTime',$act) && strpos($act['startTime'],-1,1) === 'Z') {
$adjust = true;
$event['adjust'] = 1;
$event['dtstart'] = datetime_convert('UTC','UTC',$event['startTime'] . (($adjust) ? '' : 'Z'));
}
if(array_key_exists('endTime',$act)) {
$event['dtend'] = datetime_convert('UTC','UTC',$event['endTime'] . (($adjust) ? '' : 'Z'));
}
else {
$event['nofinish'] = true;
}
}
foreach ([ 'name', 'summary', 'content' ] as $a) { foreach ([ 'name', 'summary', 'content' ] as $a) {
if (($x = self::get_textfield($act,$a)) !== false) { if (($x = self::get_textfield($act,$a)) !== false) {
$content[$a] = $x; $content[$a] = $x;
} }
} }
if($event) {
$event['summary'] = html2bbcode($content['summary']);
$event['description'] = html2bbcode($content['content']);
if($event['summary'] && $event['dtstart']) {
$content['event'] = $event;
}
}
if (array_key_exists('source',$act) && array_key_exists('mediaType',$act['source'])) { if (array_key_exists('source',$act) && array_key_exists('mediaType',$act['source'])) {
if ($act['source']['mediaType'] === 'text/bbcode') { if ($act['source']['mediaType'] === 'text/bbcode') {
$content['bbcode'] = purify_html($act['source']['content']); $content['bbcode'] = purify_html($act['source']['content']);
} }
} }
return $content; return $content;
} }
@ -1722,4 +2204,6 @@ class Activity {
} }
return $content; return $content;
} }
} }

View File

@ -7,22 +7,25 @@ namespace Zotlabs\Lib;
* *
* Parses an ActivityStream JSON string. * Parses an ActivityStream JSON string.
*/ */
class ActivityStreams { class ActivityStreams {
public $raw = null; public $raw = null;
public $data; public $data = null;
public $valid = false; public $valid = false;
public $id = ''; public $deleted = false;
public $type = ''; public $id = '';
public $actor = null; public $parent_id = '';
public $obj = null; public $type = '';
public $tgt = null; public $actor = null;
public $origin = null; public $obj = null;
public $owner = null; public $tgt = null;
public $signer = null; public $origin = null;
public $ldsig = null; public $owner = null;
public $sigok = false; public $signer = null;
public $recips = null; public $ldsig = null;
public $sigok = false;
public $recips = null;
public $raw_recips = null; public $raw_recips = null;
/** /**
@ -35,16 +38,49 @@ class ActivityStreams {
function __construct($string) { function __construct($string) {
$this->raw = $string; $this->raw = $string;
$this->data = json_decode($string, true);
if(is_array($string)) {
$this->data = $string;
}
else {
$this->data = json_decode($string, true);
}
if($this->data) { if($this->data) {
// verify and unpack JSalmon signature if present
if(is_array($this->data) && array_key_exists('signed',$this->data)) {
$ret = JSalmon::verify($this->data);
$tmp = JSalmon::unpack($this->data['data']);
if($ret && $ret['success']) {
if($ret['signer']) {
$saved = json_encode($this->data,JSON_UNESCAPED_SLASHES);
$this->data = $tmp;
$this->data['signer'] = $ret['signer'];
$this->data['signed_data'] = $saved;
if($ret['hubloc']) {
$this->data['hubloc'] = $ret['hubloc'];
}
}
}
}
$this->valid = true; $this->valid = true;
if(array_key_exists('type',$this->data) && array_key_exists('actor',$this->data) && array_key_exists('object',$this->data)) {
if($this->data['type'] === 'Delete' && $this->data['actor'] === $this->data['object']) {
$this->deleted = $this->data['actor'];
$this->valid = false;
}
}
} }
if($this->is_valid()) { if($this->is_valid()) {
$this->id = $this->get_property_obj('id'); $this->id = $this->get_property_obj('id');
$this->type = $this->get_primary_type(); $this->type = $this->get_primary_type();
$this->actor = $this->get_compound_property('actor'); $this->actor = $this->get_actor('actor','','');
$this->obj = $this->get_compound_property('object'); $this->obj = $this->get_compound_property('object');
$this->tgt = $this->get_compound_property('target'); $this->tgt = $this->get_compound_property('target');
$this->origin = $this->get_compound_property('origin'); $this->origin = $this->get_compound_property('origin');
@ -53,14 +89,31 @@ class ActivityStreams {
$this->ldsig = $this->get_compound_property('signature'); $this->ldsig = $this->get_compound_property('signature');
if($this->ldsig) { if($this->ldsig) {
$this->signer = $this->get_compound_property('creator',$this->ldsig); $this->signer = $this->get_compound_property('creator',$this->ldsig);
if($this->signer && $this->signer['publicKey'] && $this->signer['publicKey']['publicKeyPem']) { if($this->signer && is_array($this->signer) && array_key_exists('publicKey',$this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
$this->sigok = \Zotlabs\Lib\LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']); $this->sigok = LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']);
} }
} }
if(($this->type === 'Note') && (! $this->obj)) { if(! $this->obj) {
$this->obj = $this->data; $this->obj = $this->data;
$this->type = 'Create'; $this->type = 'Create';
if(! $this->actor) {
$this->actor = $this->get_actor('attributedTo',$this->obj);
}
}
if($this->obj && is_array($this->obj) && $this->obj['actor'])
$this->obj['actor'] = $this->get_actor('actor',$this->obj);
if($this->tgt && is_array($this->tgt) && $this->tgt['actor'])
$this->tgt['actor'] = $this->get_actor('actor',$this->tgt);
$this->parent_id = $this->get_property_obj('inReplyTo');
if((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['inReplyTo'];
}
if((! $this->parent_id) && is_array($this->obj)) {
$this->parent_id = $this->obj['id'];
} }
} }
} }
@ -190,28 +243,32 @@ class ActivityStreams {
$base = (($base) ? $base : $this->data); $base = (($base) ? $base : $this->data);
$propname = (($prefix) ? $prefix . ':' : '') . $property; $propname = (($prefix) ? $prefix . ':' : '') . $property;
if(! is_array($base)) {
btlogger('not an array: ' . print_r($base,true));
return null;
}
return ((array_key_exists($propname, $base)) ? $base[$propname] : null); return ((array_key_exists($propname, $base)) ? $base[$propname] : null);
} }
/** /**
* @brief Fetches a property from an URL. * @brief Fetches a property from an URL.
* *
* @param string $url * @param string $url
* @return NULL|mixed * @return NULL|mixed
*/ */
function fetch_property($url) { function fetch_property($url) {
$redirects = 0; return self::fetch($url);
if(! check_siteallowed($url)) { }
logger('blacklisted: ' . $url);
return null;
}
$x = z_fetch_url($url, true, $redirects, static function fetch($url,$channel = null) {
['headers' => [ 'Accept: application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ]]); return Activity::fetch($url,$channel);
if($x['success']) }
return json_decode($x['body'], true);
return null; static function is_an_actor($s) {
return(in_array($s,[ 'Application','Group','Organization','Person','Service' ]));
} }
/** /**
@ -222,12 +279,70 @@ class ActivityStreams {
* @param string $namespace (optional) default empty * @param string $namespace (optional) default empty
* @return NULL|mixed * @return NULL|mixed
*/ */
function get_compound_property($property, $base = '', $namespace = '') {
function get_actor($property,$base='',$namespace = '') {
$x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) {
// SECURITY: If we have already stored the actor profile, re-generate it
// from cached data - don't refetch it from the network
$r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_id_url = '%s' limit 1",
dbesc($x)
);
if($r) {
$y = Activity::encode_person($r[0]);
$y['cached'] = true;
return $y;
}
}
$actor = $this->get_compound_property($property,$base,$namespace,true);
if(is_array($actor) && self::is_an_actor($actor['type'])) {
if(array_key_exists('id',$actor) && (! array_key_exists('inbox',$actor))) {
$actor = $this->fetch_property($actor['id']);
}
return $actor;
}
return null;
}
/**
* @brief
*
* @param string $property
* @param array $base
* @param string $namespace (optional) default empty
* @param boolean $first (optional) default false, if true and result is a sequential array return only the first element
* @return NULL|mixed
*/
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
$x = $this->get_property_obj($property, $base, $namespace); $x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) { if($this->is_url($x)) {
$x = $this->fetch_property($x); $x = $this->fetch_property($x);
} }
// verify and unpack JSalmon signature if present
if(is_array($x) && array_key_exists('signed',$x)) {
$ret = JSalmon::verify($x);
$tmp = JSalmon::unpack($x['data']);
if($ret && $ret['success']) {
if($ret['signer']) {
$saved = json_encode($x,JSON_UNESCAPED_SLASHES);
$x = $tmp;
$x['signer'] = $ret['signer'];
$x['signed_data'] = $saved;
if($ret['hubloc']) {
$x['hubloc'] = $ret['hubloc'];
}
}
}
}
if($first && is_array($x) && array_key_exists(0,$x)) {
return $x[0];
}
return $x; return $x;
} }
@ -273,4 +388,18 @@ class ActivityStreams {
return $x; return $x;
} }
static function is_as_request() {
$x = getBestSupportedMimeType([
'application/ld+json;profile="https://www.w3.org/ns/activitystreams"',
'application/activity+json',
'application/ld+json;profile="http://www.w3.org/ns/activitystreams"'
]);
return(($x) ? true : false);
}
} }

View File

@ -12,8 +12,16 @@ class Api_router {
} }
static function find($path) { static function find($path) {
if(array_key_exists($path,self::$routes)) if (array_key_exists($path,self::$routes)) {
return self::$routes[$path]; return self::$routes[$path];
}
$with_params = dirname($path) . '/[id]';
if (array_key_exists($with_params,self::$routes)) {
return self::$routes[$with_params];
}
return null; return null;
} }

View File

@ -1,32 +1,34 @@
<?php /** @file */ <?php
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
/**
* Apps
*
*/
require_once('include/plugin.php'); require_once('include/plugin.php');
require_once('include/channel.php'); require_once('include/channel.php');
/**
* @brief Apps class.
*
*/
class Apps { class Apps {
static public $available_apps = null; static public $available_apps = null;
static public $installed_apps = null; static public $installed_apps = null;
static public $base_apps = null; static public $base_apps = null;
/**
* @brief
*
* @param boolean $translate (optional) default true
* @return array
*/
static public function get_system_apps($translate = true) { static public function get_system_apps($translate = true) {
$ret = [];
$ret = array();
if(is_dir('apps')) if(is_dir('apps'))
$files = glob('apps/*.apd'); $files = glob('apps/*.apd');
else else
$files = glob('app/*.apd'); $files = glob('app/*.apd');
if($files) { if($files) {
foreach($files as $f) { foreach($files as $f) {
$x = self::parse_app_description($f,$translate); $x = self::parse_app_description($f,$translate);
@ -50,14 +52,17 @@ class Apps {
} }
} }
call_hooks('get_system_apps',$ret); /**
* @hooks get_system_apps
* Hook to manipulate the system apps array.
*/
call_hooks('get_system_apps', $ret);
return $ret; return $ret;
} }
static public function get_base_apps() { static public function get_base_apps() {
return get_config('system','base_apps',[ $x = get_config('system','base_apps',[
'Connections', 'Connections',
'Network', 'Network',
'Settings', 'Settings',
@ -72,6 +77,14 @@ class Apps {
'Mail', 'Mail',
'Profile Photo' 'Profile Photo'
]); ]);
/**
* @hooks get_base_apps
* Hook to manipulate the base apps array.
*/
call_hooks('get_base_apps', $x);
return $x;
} }
static public function import_system_apps() { static public function import_system_apps() {
@ -104,6 +117,7 @@ class Apps {
// $id will be boolean true or false to install an app, or an integer id to update an existing app // $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id === false) if($id === false)
continue; continue;
if($id !== true) { if($id !== true) {
// if we already installed this app, but it changed, preserve any categories we created // if we already installed this app, but it changed, preserve any categories we created
$s = EMPTY_STR; $s = EMPTY_STR;
@ -124,7 +138,6 @@ class Apps {
$app['guid'] = hash('whirlpool',$app['name']); $app['guid'] = hash('whirlpool',$app['name']);
$app['system'] = 1; $app['system'] = 1;
self::app_install(local_channel(),$app); self::app_install(local_channel(),$app);
} }
} }
} }
@ -132,8 +145,10 @@ class Apps {
/** /**
* Install the system app if no system apps have been installed, or if a new system app * Install the system app if no system apps have been installed, or if a new system app
* is discovered, or if the version of a system app changes. * is discovered, or if the version of a system app changes.
*
* @param array $app
* @return boolean|int
*/ */
static public function check_install_system_app($app) { static public function check_install_system_app($app) {
if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) { if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) {
return true; return true;
@ -157,14 +172,13 @@ class Apps {
return $notfound; return $notfound;
} }
/** /**
* Install the system app if no system apps have been installed, or if a new system app * Install the personal app if no personal apps have been installed, or if a new personal app
* is discovered, or if the version of a system app changes. * is discovered, or if the version of a personal app changes.
*
* @param array $app
* @return boolean|int
*/ */
static public function check_install_personal_app($app) { static public function check_install_personal_app($app) {
$installed = false; $installed = false;
foreach(self::$installed_apps as $iapp) { foreach(self::$installed_apps as $iapp) {
@ -187,20 +201,25 @@ class Apps {
return strcasecmp($a['name'],$b['name']); return strcasecmp($a['name'],$b['name']);
} }
/**
static public function parse_app_description($f,$translate = true) { * @brief Parse app description.
*
$ret = array(); * @param string $f filename
* @param boolean $translate (optional) default true
* @return boolean|array
*/
static public function parse_app_description($f, $translate = true) {
$ret = [];
$matches = [];
$baseurl = z_root(); $baseurl = z_root();
$channel = \App::get_channel(); //$channel = \App::get_channel();
$address = (($channel) ? $channel['channel_address'] : ''); //$address = (($channel) ? $channel['channel_address'] : '');
//future expansion //future expansion
$observer = \App::get_observer(); $observer = \App::get_observer();
$lines = @file($f); $lines = @file($f);
if($lines) { if($lines) {
foreach($lines as $x) { foreach($lines as $x) {
@ -290,8 +309,10 @@ class Apps {
if($ret) { if($ret) {
if($translate) if($translate)
self::translate_system_apps($ret); self::translate_system_apps($ret);
return $ret; return $ret;
} }
return false; return false;
} }
@ -299,12 +320,14 @@ class Apps {
static public function translate_system_apps(&$arr) { static public function translate_system_apps(&$arr) {
$apps = array( $apps = array(
'Apps' => t('Apps'), 'Apps' => t('Apps'),
'Affinity Tool' => t('Affinity Tool'),
'Articles' => t('Articles'), 'Articles' => t('Articles'),
'Cards' => t('Cards'), 'Cards' => t('Cards'),
'Admin' => t('Site Admin'), 'Admin' => t('Site Admin'),
'Report Bug' => t('Report Bug'), 'Report Bug' => t('Report Bug'),
'Bookmarks' => t('Bookmarks'), 'Bookmarks' => t('Bookmarks'),
'Chatrooms' => t('Chatrooms'), 'Chatrooms' => t('Chatrooms'),
'Content Filter' => t('Content Filter'),
'Connections' => t('Connections'), 'Connections' => t('Connections'),
'Remote Diagnostics' => t('Remote Diagnostics'), 'Remote Diagnostics' => t('Remote Diagnostics'),
'Suggest Channels' => t('Suggest Channels'), 'Suggest Channels' => t('Suggest Channels'),
@ -364,30 +387,31 @@ class Apps {
if(array_key_exists($arr[$x]['name'],$apps)) { if(array_key_exists($arr[$x]['name'],$apps)) {
$arr[$x]['name'] = $apps[$arr[$x]['name']]; $arr[$x]['name'] = $apps[$arr[$x]['name']];
} else { } else {
// Try to guess by app name if not in list // Try to guess by app name if not in list
$arr[$x]['name'] = t(trim($arr[$x]['name'])); $arr[$x]['name'] = t(trim($arr[$x]['name']));
} }
} }
} }
} }
/**
// papp is a portable app * @brief
*
static public function app_render($papp,$mode = 'view') { * @param array $papp
* papp is a portable app
/** * @param string $mode (optional) default 'view'
* modes: * Render modes:
* view: normal mode for viewing an app via bbcode from a conversation or page * * \b view: normal mode for viewing an app via bbcode from a conversation or page
* provides install/update button if you're logged in locally * provides install/update button if you're logged in locally
* install: like view but does not display app-bin options if they are present * * \b install: like view but does not display app-bin options if they are present
* list: normal mode for viewing an app on the app page * * \b list: normal mode for viewing an app on the app page
* no buttons are shown * no buttons are shown
* edit: viewing the app page in editing mode provides a delete button * * \b edit: viewing the app page in editing mode provides a delete button
* nav: render apps for app-bin * * \b nav: render apps for app-bin
*/ *
* @return void|string Parsed HTML
*/
static public function app_render($papp, $mode = 'view') {
$installed = false; $installed = false;
if(! $papp) if(! $papp)
@ -425,7 +449,6 @@ class Apps {
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
foreach($papp as $k => $v) { foreach($papp as $k => $v) {
if(strpos($v,'http') === 0 && $k != 'papp') { if(strpos($v,'http') === 0 && $k != 'papp') {
if(! (local_channel() && strpos($v,z_root()) === 0)) { if(! (local_channel() && strpos($v,z_root()) === 0)) {
@ -590,8 +613,14 @@ class Apps {
return false; return false;
} }
/**
static public function can_delete($uid,$app) { * @brief
*
* @param mixed $uid If not set return false, otherwise no influence
* @param array $app
* @return boolean
*/
static public function can_delete($uid, $app) {
if(! $uid) { if(! $uid) {
return false; return false;
} }
@ -599,7 +628,7 @@ class Apps {
$base_apps = self::get_base_apps(); $base_apps = self::get_base_apps();
if($base_apps) { if($base_apps) {
foreach($base_apps as $b) { foreach($base_apps as $b) {
if($app['guid'] === hash('whirlpool',$b)) { if($app['guid'] === hash('whirlpool', $b)) {
return false; return false;
} }
} }
@ -611,7 +640,6 @@ class Apps {
static public function app_destroy($uid,$app) { static public function app_destroy($uid,$app) {
if($uid && $app['guid']) { if($uid && $app['guid']) {
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
@ -620,7 +648,7 @@ class Apps {
if(! intval($x[0]['app_deleted'])) { if(! intval($x[0]['app_deleted'])) {
$x[0]['app_deleted'] = 1; $x[0]['app_deleted'] = 1;
if(self::can_delete($uid,$app)) { if(self::can_delete($uid,$app)) {
$r = q("delete from app where app_id = '%s' and app_channel = %d", q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -628,10 +656,15 @@ class Apps {
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($x[0]['id']) intval($x[0]['id'])
); );
/**
* @hooks app_destroy
* Called after app entry got removed from database
* and provide app array from database.
*/
call_hooks('app_destroy', $x[0]); call_hooks('app_destroy', $x[0]);
} }
else { else {
$r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -645,22 +678,23 @@ class Apps {
} }
} }
} }
} }
static public function app_undestroy($uid,$app) { /**
* @brief Undelete a system app.
// undelete a system app *
* @param int $uid
* @param array $app
*/
static public function app_undestroy($uid, $app) {
if($uid && $app['guid']) { if($uid && $app['guid']) {
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
if($x) { if($x) {
if($x[0]['app_system']) { if($x[0]['app_system']) {
$r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -669,7 +703,15 @@ class Apps {
} }
} }
static public function app_feature($uid,$app,$term) { /**
* @brief
*
* @param int $uid
* @param array $app
* @param string $term
* @return void
*/
static public function app_feature($uid, $app, $term) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
@ -693,23 +735,37 @@ class Apps {
} }
} }
static public function app_installed($uid,$app,$bypass_filter=false) { /**
* @brief
*
* @param int $uid
* @param array $app
* @param boolean $bypass_filter (optional) default false
* @return boolean
*/
static public function app_installed($uid, $app, $bypass_filter = false) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''), dbesc((array_key_exists('guid', $app)) ? $app['guid'] : ''),
intval($uid) intval($uid)
); );
if (!$bypass_filter) { if(!$bypass_filter) {
$filter_arr = [ $filter_arr = [
'uid'=>$uid, 'uid' => $uid,
'app'=>$app, 'app' => $app,
'installed'=>$r 'installed' => $r
]; ];
call_hooks('app_installed_filter',$filter_arr); /**
* @hooks app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
@ -725,11 +781,17 @@ class Apps {
'app'=>$app, 'app'=>$app,
'installed'=>$r 'installed'=>$r
]; ];
call_hooks('addon_app_installed_filter',$filter_arr); /**
* @hooks addon_app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('addon_app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
static public function system_app_installed($uid,$app,$bypass_filter=false) { static public function system_app_installed($uid,$app,$bypass_filter=false) {
@ -744,28 +806,39 @@ class Apps {
'app'=>$app, 'app'=>$app,
'installed'=>$r 'installed'=>$r
]; ];
call_hooks('system_app_installed_filter',$filter_arr); /**
* @hooks system_app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('system_app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
/**
* @brief
*
* @param int $uid
* @param boolean $deleted
* @param array $cats
* @return boolean|array
*/
static public function app_list($uid, $deleted = false, $cats = []) { static public function app_list($uid, $deleted = false, $cats = []) {
if($deleted) if($deleted)
$sql_extra = ""; $sql_extra = '';
else else
$sql_extra = " and app_deleted = 0 "; $sql_extra = ' and app_deleted = 0 ';
if($cats) { if($cats) {
$cat_sql_extra = ' and ( ';
$cat_sql_extra = " and ( ";
foreach($cats as $cat) { foreach($cats as $cat) {
if(strpos($cat_sql_extra, 'term')) if(strpos($cat_sql_extra, 'term'))
$cat_sql_extra .= "or "; $cat_sql_extra .= 'or ';
$cat_sql_extra .= "term = '" . dbesc($cat) . "' "; $cat_sql_extra .= "term = '" . dbesc($cat) . "' ";
} }
@ -777,11 +850,13 @@ class Apps {
); );
if(! $r) if(! $r)
return $r; return $r;
$sql_extra .= " and app.id in ( ";
$sql_extra .= ' and app.id in ( ';
$s = ''; $s = '';
foreach($r as $rr) { foreach($r as $rr) {
if($s) if($s)
$s .= ','; $s .= ',';
$s .= intval($rr['oid']); $s .= intval($rr['oid']);
} }
$sql_extra .= $s . ') '; $sql_extra .= $s . ') ';
@ -792,12 +867,26 @@ class Apps {
); );
if($r) { if($r) {
$hookinfo = Array('uid'=>$uid,'deleted'=>$deleted,'cats'=>$cats,'apps'=>$r); $hookinfo = [
call_hooks('app_list',$hookinfo); 'uid' => $uid,
'deleted' => $deleted,
'cats' => $cats,
'apps' => $r,
];
/**
* @hooks app_list
* * \e int \b uid
* * \e boolean \b deleted
* * \e array \b cats
* * \e array \b apps - return value
*/
call_hooks('app_list', $hookinfo);
$r = $hookinfo['apps']; $r = $hookinfo['apps'];
for($x = 0; $x < count($r); $x ++) {
for($x = 0; $x < count($r); $x++) {
if(! $r[$x]['app_system']) if(! $r[$x]['app_system'])
$r[$x]['type'] = 'personal'; $r[$x]['type'] = 'personal';
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d", $r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($r[$x]['id']) intval($r[$x]['id'])
@ -805,7 +894,7 @@ class Apps {
} }
} }
return($r); return $r;
} }
static public function app_order($uid,$apps,$menu) { static public function app_order($uid,$apps,$menu) {
@ -837,13 +926,14 @@ class Apps {
$ret[] = $ap; $ret[] = $ap;
} }
} }
return $ret;
return $ret;
} }
static function find_app_in_array($name,$arr) { static function find_app_in_array($name,$arr) {
if(! $arr) if(! $arr)
return false; return false;
foreach($arr as $x) { foreach($arr as $x) {
if($x['name'] === $name) { if($x['name'] === $name) {
return $x; return $x;
@ -852,8 +942,16 @@ class Apps {
return false; return false;
} }
static function moveup($uid,$guid,$menu) { /**
$syslist = array(); * @brief
*
* @param int $uid
* @param int $guid
* @param string $menu
* @return void
*/
static function moveup($uid, $guid, $menu) {
$syslist = [];
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
@ -863,6 +961,7 @@ class Apps {
$papp = self::app_encode($li); $papp = self::app_encode($li);
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
continue; continue;
$syslist[] = $papp; $syslist[] = $papp;
} }
} }
@ -875,8 +974,6 @@ class Apps {
if(! $syslist) if(! $syslist)
return; return;
$newlist = [];
foreach($syslist as $k => $li) { foreach($syslist as $k => $li) {
if($li['guid'] === $guid) { if($li['guid'] === $guid) {
$position = $k; $position = $k;
@ -885,6 +982,7 @@ class Apps {
} }
if(! $position) if(! $position)
return; return;
$dest_position = $position - 1; $dest_position = $position - 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -896,11 +994,18 @@ class Apps {
} }
set_pconfig($uid,'system',$conf,implode(',',$narr)); set_pconfig($uid,'system',$conf,implode(',',$narr));
} }
static function movedown($uid,$guid,$menu) { /**
$syslist = array(); * @brief
*
* @param int $uid
* @param int $guid
* @param string $menu
* @return void
*/
static function movedown($uid, $guid, $menu) {
$syslist = [];
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
@ -910,6 +1015,7 @@ class Apps {
$papp = self::app_encode($li); $papp = self::app_encode($li);
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
continue; continue;
$syslist[] = $papp; $syslist[] = $papp;
} }
} }
@ -922,8 +1028,6 @@ class Apps {
if(! $syslist) if(! $syslist)
return; return;
$newlist = [];
foreach($syslist as $k => $li) { foreach($syslist as $k => $li) {
if($li['guid'] === $guid) { if($li['guid'] === $guid) {
$position = $k; $position = $k;
@ -932,6 +1036,7 @@ class Apps {
} }
if($position >= count($syslist) - 1) if($position >= count($syslist) - 1)
return; return;
$dest_position = $position + 1; $dest_position = $position + 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -943,7 +1048,6 @@ class Apps {
} }
set_pconfig($uid,'system',$conf,implode(',',$narr)); set_pconfig($uid,'system',$conf,implode(',',$narr));
} }
static public function app_decode($s) { static public function app_decode($s) {
@ -951,8 +1055,14 @@ class Apps {
return json_decode($x,true); return json_decode($x,true);
} }
/**
static public function app_macros($uid,&$arr) { * @brief
*
* @param int $uid
* @param[in,out] array $arr
* @return void
*/
static public function app_macros($uid, &$arr) {
if(! intval($uid)) if(! intval($uid))
return; return;
@ -963,18 +1073,14 @@ class Apps {
//future expansion //future expansion
$observer = \App::get_observer(); //$observer = \App::get_observer();
$arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']);
$arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']);
} }
static public function app_store($arr) { static public function app_store($arr) {
//logger('app_store: ' . print_r($arr,true)); //logger('app_store: ' . print_r($arr,true));
@ -1158,13 +1264,17 @@ class Apps {
} }
return $ret; return $ret;
} }
/**
static public function app_encode($app,$embed = false) { * @brief
*
$ret = array(); * @param array $app
* @param boolean $embed (optional) default false
* @return array|string
*/
static public function app_encode($app, $embed = false) {
$ret = [];
$ret['type'] = 'personal'; $ret['type'] = 'personal';
@ -1224,12 +1334,12 @@ class Apps {
foreach($app['term'] as $t) { foreach($app['term'] as $t) {
if($s) if($s)
$s .= ','; $s .= ',';
$s .= $t['term']; $s .= $t['term'];
} }
$ret['categories'] = $s; $ret['categories'] = $s;
} }
if(! $embed) if(! $embed)
return $ret; return $ret;
@ -1239,16 +1349,13 @@ class Apps {
unset($ret['categories']); unset($ret['categories']);
$j = json_encode($ret); $j = json_encode($ret);
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
} }
static public function papp_encode($papp) { static public function papp_encode($papp) {
return chunk_split(base64_encode(json_encode($papp)),72,"\n"); return chunk_split(base64_encode(json_encode($papp)),72,"\n");
} }
} }

View File

@ -14,6 +14,7 @@ class DReport {
$this->location = $location; $this->location = $location;
$this->sender = $sender; $this->sender = $sender;
$this->recipient = $recipient; $this->recipient = $recipient;
$this->name = EMPTY_STR;
$this->message_id = $message_id; $this->message_id = $message_id;
$this->status = $status; $this->status = $status;
$this->date = datetime_convert(); $this->date = datetime_convert();
@ -24,8 +25,8 @@ class DReport {
$this->date = datetime_convert(); $this->date = datetime_convert();
} }
function addto_recipient($name) { function set_name($name) {
$this->recipient = $this->recipient . ' ' . $name; $this->name = $name;
} }
function addto_update($status) { function addto_update($status) {
@ -37,6 +38,7 @@ class DReport {
$this->location = $arr['location']; $this->location = $arr['location'];
$this->sender = $arr['sender']; $this->sender = $arr['sender'];
$this->recipient = $arr['recipient']; $this->recipient = $arr['recipient'];
$this->name = $arr['name'];
$this->message_id = $arr['message_id']; $this->message_id = $arr['message_id'];
$this->status = $arr['status']; $this->status = $arr['status'];
$this->date = $arr['date']; $this->date = $arr['date'];
@ -47,9 +49,99 @@ class DReport {
'location' => $this->location, 'location' => $this->location,
'sender' => $this->sender, 'sender' => $this->sender,
'recipient' => $this->recipient, 'recipient' => $this->recipient,
'name' => $this->name,
'message_id' => $this->message_id, 'message_id' => $this->message_id,
'status' => $this->status, 'status' => $this->status,
'date' => $this->date 'date' => $this->date
); );
} }
/**
* @brief decide whether to store a returned delivery report
*
* @param array $dr
* @return boolean
*/
static function is_storable($dr) {
if(get_config('system', 'disable_dreport'))
return false;
/**
* @hooks dreport_is_storable
* Called before storing a dreport record to determine whether to store it.
* * \e array
*/
call_hooks('dreport_is_storable', $dr);
// let plugins accept or reject - if neither, continue on
if(array_key_exists('accept',$dr) && intval($dr['accept']))
return true;
if(array_key_exists('reject',$dr) && intval($dr['reject']))
return false;
if(! ($dr['sender']))
return false;
// Is the sender one of our channels?
$c = q("select channel_id from channel where channel_hash = '%s' or channel_portable_id = '%s' limit 1",
dbesc($dr['sender']),
dbesc($dr['sender'])
);
if(! $c)
return false;
// legacy zot recipients add a space and their name to the xchan. remove it if true.
$legacy_recipient = strpos($dr['recipient'], ' ');
if($legacy_recipient !== false) {
$legacy_recipient_parts = explode(' ', $dr['recipient'], 2);
$rxchan = $legacy_recipient_parts[0];
}
else {
$rxchan = $dr['recipient'];
}
// is the recipient one of our connections, or do we want to store every report?
$pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all');
if($pcf)
return true;
// We always add ourself as a recipient to private and relayed posts
// So if a remote site says they can't find us, that's no big surprise
// and just creates a lot of extra report noise
if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient_not_found'))
return false;
// If you have a private post with a recipient list, every single site is going to report
// back a failed delivery for anybody on that list that isn't local to them. We're only
// concerned about this if we have a local hubloc record which says we expected them to
// have a channel on that site.
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'",
dbesc($rxchan),
dbesc($dr['location'])
);
if((! $r) && ($dr['status'] === 'recipient_not_found'))
return false;
$r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($rxchan),
intval($c[0]['channel_id'])
);
if($r)
return true;
return false;
}
} }

View File

@ -2,15 +2,13 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use Zotlabs\Zot6\HTTPSig;
class JSalmon { class JSalmon {
static function sign($data,$key_id,$key) { static function sign($data,$key_id,$key,$data_type = 'application/x-zot+json') {
$arr = $data; $data = base64url_encode(json_encode($data,true),true); // strip padding
$data = json_encode($data,JSON_UNESCAPED_SLASHES);
$data = base64url_encode($data, false); // do not strip padding
$data_type = 'application/x-zot+json';
$encoding = 'base64url'; $encoding = 'base64url';
$algorithm = 'RSA-SHA256'; $algorithm = 'RSA-SHA256';
@ -18,9 +16,9 @@ class JSalmon {
// precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods
$precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; $precomputed = '.' . base64url_encode($data_type,true) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng';
$signature = base64url_encode(rsa_sign($data . $precomputed, $key), false); $signature = base64url_encode(rsa_sign($data . $precomputed, $key), true);
return ([ return ([
'signed' => true, 'signed' => true,
@ -30,9 +28,45 @@ class JSalmon {
'alg' => $algorithm, 'alg' => $algorithm,
'sigs' => [ 'sigs' => [
'value' => $signature, 'value' => $signature,
'key_id' => base64url_encode($key_id) 'key_id' => base64url_encode($key_id, true)
] ]
]); ]);
} }
static function verify($x) {
logger('verify');
$ret = [ 'results' => [] ];
if(! is_array($x)) {
return $false;
}
if(! ( array_key_exists('signed',$x) && $x['signed'])) {
return $false;
}
$signed_data = preg_replace('/\s+/','',$x['data']) . '.'
. base64url_encode($x['data_type'],true) . '.'
. base64url_encode($x['encoding'],true) . '.'
. base64url_encode($x['alg'],true);
$key = HTTPSig::get_key(EMPTY_STR,base64url_decode($x['sigs']['key_id']));
logger('key: ' . print_r($key,true));
if($key['portable_id'] && $key['public_key']) {
if(rsa_verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) {
logger('verified');
$ret = [ 'success' => true, 'signer' => $key['portable_id'], 'hubloc' => $key['hubloc'] ];
}
}
return $ret;
}
static function unpack($data) {
return json_decode(base64url_decode($data),true);
}
} }

View File

@ -2,11 +2,6 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
/**
* @brief lowlevel implementation of Zot6 protocol.
*
*/
use Zotlabs\Zot6\HTTPSig; use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Access\Permissions; use Zotlabs\Access\Permissions;
use Zotlabs\Access\PermissionLimits; use Zotlabs\Access\PermissionLimits;
@ -14,14 +9,17 @@ use Zotlabs\Daemon\Master;
require_once('include/crypto.php'); require_once('include/crypto.php');
/**
* @brief Lowlevel implementation of Zot6 protocol.
*
*/
class Libzot { class Libzot {
/** /**
* @brief Generates a unique string for use as a zot guid. * @brief Generates a unique string for use as a zot guid.
* *
* Generates a unique string for use as a zot guid using our DNS-based url, the * Generates a unique string for use as a zot guid using our DNS-based url,
* channel nickname and some entropy. * the channel nickname and some entropy.
* The entropy ensures uniqueness against re-installs where the same URL and * The entropy ensures uniqueness against re-installs where the same URL and
* nickname are chosen. * nickname are chosen.
* *
@ -32,9 +30,8 @@ class Libzot {
* immediate universe. * immediate universe.
* *
* @param string $channel_nick a unique nickname of controlling entity * @param string $channel_nick a unique nickname of controlling entity
* @returns string * @return string
*/ */
static function new_uid($channel_nick) { static function new_uid($channel_nick) {
$rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand();
return(base64url_encode(hash('whirlpool', $rawstr, true), true)); return(base64url_encode(hash('whirlpool', $rawstr, true), true));
@ -52,8 +49,8 @@ class Libzot {
* *
* @param string $guid * @param string $guid
* @param string $pubkey * @param string $pubkey
* @return string
*/ */
static function make_xchan_hash($guid, $pubkey) { static function make_xchan_hash($guid, $pubkey) {
return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); return base64url_encode(hash('whirlpool', $guid . $pubkey, true));
} }
@ -65,10 +62,8 @@ class Libzot {
* should only be used by channels which are defined on this hub. * should only be used by channels which are defined on this hub.
* *
* @param string $hash - xchan_hash * @param string $hash - xchan_hash
* @returns array of hubloc (hub location structures) * @return array of hubloc (hub location structures)
*
*/ */
static function get_hublocs($hash) { static function get_hublocs($hash) {
/* Only search for active hublocs - e.g. those that haven't been marked deleted */ /* Only search for active hublocs - e.g. those that haven't been marked deleted */
@ -92,16 +87,17 @@ class Libzot {
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check'
* @param array $recipients * @param array $recipients
* envelope recipients, array of portable_id's; empty for public posts * envelope recipients, array of portable_id's; empty for public posts
* @param string msg * @param string $msg
* optional message * optional message
* @param string $encoding
* optional encoding, default 'activitystreams'
* @param string $remote_key * @param string $remote_key
* optional public site key of target hub used to encrypt entire packet * optional public site key of target hub used to encrypt entire packet
* NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others
* @param string $methods * @param string $methods
* optional comma separated list of encryption methods @ref self::best_algorithm() * optional comma separated list of encryption methods @ref best_algorithm()
* @returns string json encoded zot packet * @returns string json encoded zot packet
*/ */
static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') { static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') {
$sig_method = get_config('system','signature_algorithm','sha256'); $sig_method = get_config('system','signature_algorithm','sha256');
@ -109,7 +105,7 @@ class Libzot {
$data = [ $data = [
'type' => $type, 'type' => $type,
'encoding' => $encoding, 'encoding' => $encoding,
'sender' => $channel['channel_hash'], 'sender' => $channel['channel_portable_id'],
'site_id' => self::make_xchan_hash(z_root(), get_config('system','pubkey')), 'site_id' => self::make_xchan_hash(z_root(), get_config('system','pubkey')),
'version' => System::get_zot_revision(), 'version' => System::get_zot_revision(),
]; ];
@ -146,11 +142,10 @@ class Libzot {
* @brief Choose best encryption function from those available on both sites. * @brief Choose best encryption function from those available on both sites.
* *
* @param string $methods * @param string $methods
* comma separated list of encryption methods * Comma separated list of encryption methods
* @return string first match from our site method preferences crypto_methods() array * @return string first match from our site method preferences crypto_methods() array
* of a method which is common to both sites; or 'aes256cbc' if no matches are found. * of a method which is common to both sites; or 'aes256cbc' if no matches are found.
*/ */
static function best_algorithm($methods) { static function best_algorithm($methods) {
$x = [ $x = [
@ -164,7 +159,6 @@ class Libzot {
* * \e string \b methods - comma separated list of encryption methods * * \e string \b methods - comma separated list of encryption methods
* * \e string \b result - the algorithm to return * * \e string \b result - the algorithm to return
*/ */
call_hooks('zot_best_algorithm', $x); call_hooks('zot_best_algorithm', $x);
if($x['result']) if($x['result'])
@ -190,7 +184,7 @@ class Libzot {
/** /**
* @brief send a zot message * @brief Send a zot message.
* *
* @see z_post_url() * @see z_post_url()
* *
@ -200,7 +194,6 @@ class Libzot {
* @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements)
* @return array see z_post_url() for returned data format * @return array see z_post_url() for returned data format
*/ */
static function zot($url, $data, $channel = null,$crypto = null) { static function zot($url, $data, $channel = null,$crypto = null) {
if($channel) { if($channel) {
@ -227,7 +220,6 @@ class Libzot {
/** /**
* @brief Refreshes after permission changed or friending, etc. * @brief Refreshes after permission changed or friending, etc.
* *
*
* refresh is typically invoked when somebody has changed permissions of a channel and they are notified * refresh is typically invoked when somebody has changed permissions of a channel and they are notified
* to fetch new permissions via a finger/discovery operation. This may result in a new connection * to fetch new permissions via a finger/discovery operation. This may result in a new connection
* (abook entry) being added to a local channel and it may result in auto-permissions being granted. * (abook entry) being added to a local channel and it may result in auto-permissions being granted.
@ -251,7 +243,6 @@ class Libzot {
* * \b true if successful * * \b true if successful
* * otherwise \b false * * otherwise \b false
*/ */
static function refresh($them, $channel = null, $force = false) { static function refresh($them, $channel = null, $force = false) {
logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG);
@ -271,7 +262,7 @@ class Libzot {
// correct hubloc. If this doesn't work we may have to re-write this section to try them all. // correct hubloc. If this doesn't work we may have to re-write this section to try them all.
if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) { if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) {
$r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' order by hubloc_id desc",
dbesc($them['xchan_addr']) dbesc($them['xchan_addr'])
); );
} }
@ -317,7 +308,7 @@ class Libzot {
if(! $hsig_valid) { if(! $hsig_valid) {
logger('http signature not valid: ' . print_r($hsig,true)); logger('http signature not valid: ' . print_r($hsig,true));
return $result; return false;
} }
@ -329,8 +320,14 @@ class Libzot {
return false; return false;
if($channel && $record['data']['permissions']) { if($channel && $record['data']['permissions']) {
$old_read_stream_perm = their_perms_contains($channel['channel_id'],$x['hash'],'view_stream'); $permissions = explode(',',$record['data']['permissions']);
set_abconfig($channel['channel_id'],$x['hash'],'system','their_perms',$record['data']['permissions']); if($permissions && is_array($permissions)) {
$old_read_stream_perm = get_abconfig($channel['channel_id'],$x['hash'],'their_perms','view_stream');
foreach($permissions as $p) {
set_abconfig($channel['channel_id'],$x['hash'],'their_perms',$p,'1');
}
}
if(array_key_exists('profile',$record['data']) && array_key_exists('next_birthday',$record['data']['profile'])) { if(array_key_exists('profile',$record['data']) && array_key_exists('next_birthday',$record['data']['profile'])) {
$next_birthday = datetime_convert('UTC','UTC',$record['data']['profile']['next_birthday']); $next_birthday = datetime_convert('UTC','UTC',$record['data']['profile']['next_birthday']);
@ -379,19 +376,19 @@ class Libzot {
else { else {
$p = Permissions::connect_perms($channel['channel_id']); $p = Permissions::connect_perms($channel['channel_id']);
$my_perms = Permissions::serialise($p['perms']); $my_perms = $p['perms'];
$automatic = $p['automatic']; $automatic = $p['automatic'];
// new connection // new connection
if($my_perms) { if($my_perms) {
set_abconfig($channel['channel_id'],$x['hash'],'system','my_perms',$my_perms); foreach($my_perms as $k => $v) {
set_abconfig($channel['channel_id'],$x['hash'],'my_perms',$k,$v);
}
} }
$closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness'); $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness',80);
if($closeness === false)
$closeness = 80;
$y = abook_store_lowlevel( $y = abook_store_lowlevel(
[ [
@ -409,7 +406,7 @@ class Libzot {
if($y) { if($y) {
logger("New introduction received for {$channel['channel_name']}"); logger("New introduction received for {$channel['channel_name']}");
$new_perms = get_all_perms($channel['channel_id'],$x['hash']); $new_perms = get_all_perms($channel['channel_id'],$x['hash'],false);
// Send a clone sync packet and a permissions update if permissions have changed // Send a clone sync packet and a permissions update if permissions have changed
@ -425,7 +422,7 @@ class Libzot {
[ [
'type' => NOTIFY_INTRO, 'type' => NOTIFY_INTRO,
'from_xchan' => $x['hash'], 'from_xchan' => $x['hash'],
'to_xchan' => $channel['channel_hash'], 'to_xchan' => $channel['channel_portable_id'],
'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'] 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id']
] ]
); );
@ -500,6 +497,7 @@ class Libzot {
$r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url
where hubloc_guid = '%s' and hubloc_guid_sig = '%s' where hubloc_guid = '%s' and hubloc_guid_sig = '%s'
and hubloc_url = '%s' and hubloc_url_sig = '%s' and hubloc_url = '%s' and hubloc_url_sig = '%s'
and hubloc_network = 'zot6'
and hubloc_site_id = '%s' $limit", and hubloc_site_id = '%s' $limit",
dbesc($arr['id']), dbesc($arr['id']),
dbesc($arr['id_sig']), dbesc($arr['id_sig']),
@ -517,10 +515,14 @@ class Libzot {
return false; return false;
} }
/**
* @brief
*
static function valid_hub($sender,$site_id) { * @param string $sender
* @param string $site_id
* @return null|array
*/
static function valid_hub($sender, $site_id) {
$r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1",
dbesc($sender), dbesc($sender),
@ -541,7 +543,6 @@ class Libzot {
} }
return $r[0]; return $r[0];
} }
/** /**
@ -552,21 +553,14 @@ class Libzot {
* origination address. This will fetch the discovery packet of the sender, * origination address. This will fetch the discovery packet of the sender,
* which contains the public key we need to verify our guid and url signatures. * which contains the public key we need to verify our guid and url signatures.
* *
* @param array $arr an associative array which must contain: * @param string $id
* * \e string \b guid => guid of conversant
* * \e string \b guid_sig => guid signed with conversant's private key
* * \e string \b url => URL of the origination hub of this communication
* * \e string \b url_sig => URL signed with conversant's private key
* *
* @return array An associative array with * @return array An associative array with
* * \b success boolean true or false * * \e boolean \b success
* * \b message (optional) error string only if success is false * * \e string \b message (optional, unused) error string only if success is false
*/ */
static function register_hub($id) { static function register_hub($id) {
$id_hash = false;
$valid = false;
$hsig_valid = false; $hsig_valid = false;
$result = [ 'success' => false ]; $result = [ 'success' => false ];
@ -691,9 +685,27 @@ class Libzot {
$adult_changed = 1; $adult_changed = 1;
if(intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) if(intval($r[0]['xchan_deleted']) != intval($arr['deleted']))
$deleted_changed = 1; $deleted_changed = 1;
// new style 6-MAR-2019
if(array_key_exists('channel_type',$arr)) {
if($arr['channel_type'] === 'collection') {
// do nothing at this time.
}
elseif($arr['channel_type'] === 'group') {
$arr['public_forum'] = 1;
}
else {
$arr['public_forum'] = 0;
}
}
// old style
if(intval($r[0]['xchan_pubforum']) != intval($arr['public_forum'])) if(intval($r[0]['xchan_pubforum']) != intval($arr['public_forum']))
$pubforum_changed = 1; $pubforum_changed = 1;
if($arr['protocols']) { if($arr['protocols']) {
$protocols = implode(',',$arr['protocols']); $protocols = implode(',',$arr['protocols']);
if($protocols !== 'zot6') { if($protocols !== 'zot6') {
@ -776,7 +788,7 @@ class Libzot {
// see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections // see if this is a channel clone that's hosted locally - which we treat different from other xchans/connections
$local = q("select channel_account_id, channel_id from channel where channel_hash = '%s' limit 1", $local = q("select channel_account_id, channel_id from channel where channel_portable_id = '%s' limit 1",
dbesc($xchan_hash) dbesc($xchan_hash)
); );
if($local) { if($local) {
@ -947,8 +959,8 @@ class Libzot {
* @param string $hub - url of site we just contacted * @param string $hub - url of site we just contacted
* @param array $arr - output of z_post_url() * @param array $arr - output of z_post_url()
* @param array $outq - The queue structure attached to this request * @param array $outq - The queue structure attached to this request
* @return void
*/ */
static function process_response($hub, $arr, $outq) { static function process_response($hub, $arr, $outq) {
logger('remote: ' . print_r($arr,true),LOGGER_DATA); logger('remote: ' . print_r($arr,true),LOGGER_DATA);
@ -966,6 +978,7 @@ class Libzot {
} }
$x = crypto_unencapsulate($x, get_config('system','prvkey')); $x = crypto_unencapsulate($x, get_config('system','prvkey'));
if(! is_array($x)) { if(! is_array($x)) {
$x = json_decode($x,true); $x = json_decode($x,true);
} }
@ -987,16 +1000,26 @@ class Libzot {
} }
if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) {
foreach($x['delivery_report'] as $xx) { foreach($x['delivery_report'] as $xx) {
call_hooks('dreport_process',$xx); call_hooks('dreport_process',$xx);
if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) { if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) {
// legacy recipients add a space and their name to the xchan. split those if true.
$legacy_recipient = strpos($xx['recipient'], ' ');
if($legacy_recipient !== false) {
$legacy_recipient_parts = explode(' ', $xx['recipient'], 2);
$xx['recipient'] = $legacy_recipient_parts[0];
$xx['name'] = $legacy_recipient_parts[1];
}
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ", q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ",
dbesc($xx['message_id']), dbesc($xx['message_id']),
dbesc($xx['location']), dbesc($xx['location']),
dbesc($xx['recipient']), dbesc($xx['recipient']),
dbesc($xx['name']), dbesc($xx['name']),
dbesc($xx['status']), dbesc($xx['status']),
dbesc(datetime_convert($xx['date'])), dbesc(datetime_convert('UTC','UTC',$xx['date'])),
dbesc($xx['sender']) dbesc($xx['sender'])
); );
} }
@ -1065,11 +1088,6 @@ class Libzot {
* *
* @param array $arr * @param array $arr
* 'pickup' structure returned from remote site * 'pickup' structure returned from remote site
* @param string $sender_url
* the url specified by the sender in the initial communication.
* We will verify the sender and url in each returned message structure and
* also verify that all the messages returned match the site url that we are
* currently processing.
* *
* @returns array * @returns array
* Suitable for logging remotely, enumerating the processing results of each message/recipient combination * Suitable for logging remotely, enumerating the processing results of each message/recipient combination
@ -1077,7 +1095,6 @@ class Libzot {
* * [1] => \e string $delivery_status * * [1] => \e string $delivery_status
* * [2] => \e string $address * * [2] => \e string $address
*/ */
static function import($arr) { static function import($arr) {
$env = $arr; $env = $arr;
@ -1108,9 +1125,14 @@ class Libzot {
logger('Activity rejected: ' . print_r($data,true)); logger('Activity rejected: ' . print_r($data,true));
return; return;
} }
$arr = Activity::decode_note($AS); if (is_array($AS->obj)) {
$arr = Activity::decode_note($AS);
}
else {
$arr = [];
}
logger($AS->debug()); logger($AS->debug(),LOGGER_DATA);
} }
@ -1129,7 +1151,7 @@ class Libzot {
if($recip_arr) { if($recip_arr) {
stringify_array_elms($recip_arr,true); stringify_array_elms($recip_arr,true);
$recips = implode(',',$recip_arr); $recips = implode(',',$recip_arr);
$r = q("select channel_hash as hash from channel where channel_hash in ( " . $recips . " ) and channel_removed = 0 "); $r = q("select channel_portable_id as hash from channel where channel_portable_id in ( " . $recips . " ) and channel_removed = 0 ");
} }
if(! $r) { if(! $r) {
@ -1157,7 +1179,6 @@ class Libzot {
$deliveries = self::public_recips($env,$AS); $deliveries = self::public_recips($env,$AS);
} }
$deliveries = array_unique($deliveries); $deliveries = array_unique($deliveries);
@ -1176,7 +1197,7 @@ class Libzot {
//logger($AS->debug()); //logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
@ -1185,7 +1206,7 @@ class Libzot {
} }
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($env['sender']) dbesc($env['sender'])
); );
@ -1200,7 +1221,7 @@ class Libzot {
if($private) { if($private) {
$arr['item_private'] = true; $arr['item_private'] = true;
} }
// @fixme - spoofable /// @FIXME - spoofable
if($AS->data['hubloc']) { if($AS->data['hubloc']) {
$arr['item_verified'] = true; $arr['item_verified'] = true;
} }
@ -1214,7 +1235,7 @@ class Libzot {
$relay = (($env['type'] === 'response') ? true : false ); $relay = (($env['type'] === 'response') ? true : false );
$result = self::process_delivery($env['sender'],$arr,$deliveries,$relay,false,$message_request); $result = self::process_delivery($env['sender'],$AS,$arr,$deliveries,$relay,false,$message_request);
} }
elseif($env['type'] === 'sync') { elseif($env['type'] === 'sync') {
// $arr = get_channelsync_elements($data); // $arr = get_channelsync_elements($data);
@ -1234,7 +1255,14 @@ class Libzot {
} }
static function is_top_level($env,$act) { /**
* @brief
*
* @param array $env
* @param object $act
* @return boolean
*/
static function is_top_level($env, $act) {
if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) {
return true; return true;
} }
@ -1277,9 +1305,9 @@ class Libzot {
* Some of these will be rejected, but this gives us a place to start. * Some of these will be rejected, but this gives us a place to start.
* *
* @param array $msg * @param array $msg
* @return NULL|array * @param object $act
* @return array
*/ */
static function public_recips($msg, $act) { static function public_recips($msg, $act) {
require_once('include/channel.php'); require_once('include/channel.php');
@ -1303,12 +1331,12 @@ class Libzot {
$r = []; $r = [];
$c = q("select channel_id, channel_hash from channel where channel_removed = 0"); $c = q("select channel_id, channel_portable_id from channel where channel_removed = 0");
if($c) { if($c) {
foreach($c as $cc) { foreach($c as $cc) {
if(perm_is_allowed($cc['channel_id'],$msg['sender'],$perm)) { if(perm_is_allowed($cc['channel_id'],$msg['sender'],$perm)) {
$r[] = $cc['channel_hash']; $r[] = $cc['channel_portable_id'];
} }
} }
} }
@ -1316,7 +1344,7 @@ class Libzot {
if($include_sys) { if($include_sys) {
$sys = get_sys_channel(); $sys = get_sys_channel();
if($sys) if($sys)
$r[] = $sys['channel_hash']; $r[] = $sys['channel_portable_id'];
} }
@ -1332,7 +1360,7 @@ class Libzot {
if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) { if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) {
$address = basename($tag['href']); $address = basename($tag['href']);
if($address) { if($address) {
$z = q("select channel_hash as hash from channel where channel_address = '%s' $z = q("select channel_portable_id as hash from channel where channel_address = '%s'
and channel_removed = 0 limit 1", and channel_removed = 0 limit 1",
dbesc($address) dbesc($address)
); );
@ -1353,7 +1381,7 @@ class Libzot {
$thread_parent = self::find_parent($msg,$act); $thread_parent = self::find_parent($msg,$act);
if($thread_parent) { if($thread_parent) {
$z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ", $z = q("select channel_portable_id as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ",
dbesc($thread_parent), dbesc($thread_parent),
dbesc($thread_parent) dbesc($thread_parent)
); );
@ -1380,7 +1408,7 @@ class Libzot {
/** /**
* @brief * @brief
* *
* @param array $sender * @param string $sender
* @param array $arr * @param array $arr
* @param array $deliveries * @param array $deliveries
* @param boolean $relay * @param boolean $relay
@ -1389,7 +1417,7 @@ class Libzot {
* @return array * @return array
*/ */
static function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $request = false) { static function process_delivery($sender, $act, $arr, $deliveries, $relay, $public = false, $request = false) {
$result = []; $result = [];
@ -1408,7 +1436,7 @@ class Libzot {
$DR = new DReport(z_root(),$sender,$d,$arr['mid']); $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$channel = channelx_by_hash($d); $channel = channelx_by_portid($d);
if (! $channel) { if (! $channel) {
$DR->update('recipient not found'); $DR->update('recipient not found');
@ -1418,6 +1446,24 @@ class Libzot {
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>'); $DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
if(($act) && ($act->obj) && (! is_array($act->obj))) {
// The initial object fetch failed using the sys channel credentials.
// Try again using the delivery channel credentials.
// We will also need to re-parse the $item array,
// but preserve any values that were set during anonymous parsing.
$o = Activity::fetch($act->obj,$channel);
if($o) {
$act->obj = $o;
$arr = array_merge(Activity::decode_note($act),$arr);
}
else {
$DR->update('Incomplete or corrupt activity');
$result[] = $DR->get();
continue;
}
}
/** /**
* We need to block normal top-level message delivery from our clones, as the delivered * We need to block normal top-level message delivery from our clones, as the delivered
* message doesn't have ACL information in it as the cloned copy does. That copy * message doesn't have ACL information in it as the cloned copy does. That copy
@ -1427,7 +1473,7 @@ class Libzot {
* access checks. * access checks.
*/ */
if($sender === $channel['channel_hash'] && $arr['author_xchan'] === $channel['channel_hash'] && $arr['mid'] === $arr['parent_mid']) { if($sender === $channel['channel_portable_id'] && $arr['author_xchan'] === $channel['channel_portable_id'] && $arr['mid'] === $arr['parent_mid']) {
$DR->update('self delivery ignored'); $DR->update('self delivery ignored');
$result[] = $DR->get(); $result[] = $DR->get();
continue; continue;
@ -1479,11 +1525,34 @@ class Libzot {
intval($channel['channel_id']) intval($channel['channel_id'])
); );
if ($parent) { if ($parent) {
$allowed = can_comment_on_post($d,$parent[0]); $allowed = can_comment_on_post($sender,$parent[0]);
} }
} }
if($request) {
$allowed = true; if ($request) {
// Conversation fetches (e.g. $request == true) take place for
// a) new comments on expired posts
// b) hyperdrive (friend-of-friend) conversations
// c) Repeats of posts by others
// over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations
// (if hyperdrive is enabled) and repeated posts by a friend.
// If $allowed is already true, this is probably the conversation of a direct friend or a
// conversation fetch for a new comment on an expired post
// Comments of all these activities are allowed and will only be rejected (later) if the parent
// doesn't exist.
if ($perm === 'send_stream') {
if (get_pconfig($channel['channel_id'],'system','hyperdrive',false) || $arr['verb'] === ACTIVITY_SHARE) {
$allowed = true;
}
}
else {
$allowed = true;
}
$friendofriend = true; $friendofriend = true;
} }
@ -1495,7 +1564,11 @@ class Libzot {
} }
} }
if($arr['mid'] != $arr['parent_mid']) { // logger('item: ' . print_r($arr,true), LOGGER_DATA);
if($arr['mid'] !== $arr['parent_mid']) {
logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"');
// check source route. // check source route.
// We are only going to accept comments from this sender if the comment has the same route as the top-level-post, // We are only going to accept comments from this sender if the comment has the same route as the top-level-post,
@ -1517,10 +1590,7 @@ class Libzot {
// have the copy and we don't want the request to loop. // have the copy and we don't want the request to loop.
// Also don't do this if this comment came from a conversation request packet. // Also don't do this if this comment came from a conversation request packet.
// It's possible that comments are allowed but posting isn't and that could // It's possible that comments are allowed but posting isn't and that could
// cause a conversation fetch loop. We can detect these packets since they are // cause a conversation fetch loop.
// delivered via a 'notify' packet type that has a message_id element in the
// initial zot packet (just like the corresponding 'request' packet type which
// makes the request).
// We'll also check the send_stream permission - because if it isn't allowed, // We'll also check the send_stream permission - because if it isn't allowed,
// the top level post is unlikely to be imported and // the top level post is unlikely to be imported and
// this is just an exercise in futility. // this is just an exercise in futility.
@ -1709,7 +1779,7 @@ class Libzot {
$stored = (($item_result && $item_result['item']) ? $item_result['item'] : false); $stored = (($item_result && $item_result['item']) ? $item_result['item'] : false);
if((is_array($stored)) && ($stored['id'] != $stored['parent']) if((is_array($stored)) && ($stored['id'] != $stored['parent'])
&& ($stored['author_xchan'] === $channel['channel_hash'])) { && ($stored['author_xchan'] === $channel['channel_hash'] || $stored['author_xchan'] === $channel['channel_portable_id'])) {
retain_item($stored['item']['parent']); retain_item($stored['item']['parent']);
} }
@ -1759,14 +1829,14 @@ class Libzot {
logger($AS->debug()); logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
if(! $r) { if(! $r) {
$y = import_author_xchan([ 'url' => $AS->actor['id'] ]); $y = import_author_xchan([ 'url' => $AS->actor['id'] ]);
if($y) { if($y) {
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
} }
@ -1789,7 +1859,7 @@ class Libzot {
$arr['author_xchan'] = $r[0]['hubloc_hash']; $arr['author_xchan'] = $r[0]['hubloc_hash'];
} }
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($a['signature']['signer']) dbesc($a['signature']['signer'])
); );
@ -1800,7 +1870,8 @@ class Libzot {
$arr['owner_xchan'] = $a['signature']['signer']; $arr['owner_xchan'] = $a['signature']['signer'];
} }
// @fixme - spoofable
/// @FIXME - spoofable
if($AS->data['hubloc']) { if($AS->data['hubloc']) {
$arr['item_verified'] = true; $arr['item_verified'] = true;
} }
@ -1809,9 +1880,9 @@ class Libzot {
} }
logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG); logger('FOF Activity recipient: ' . $channel['channel_portable_id'], LOGGER_DATA, LOG_DEBUG);
$result = self::process_delivery($arr['owner_xchan'],$arr, [ $channel['channel_hash'] ],false,false,true); $result = self::process_delivery($arr['owner_xchan'],$AS, $arr, [ $channel['channel_portable_id'] ],false,false,true);
if ($result) { if ($result) {
$ret = array_merge($ret, $result); $ret = array_merge($ret, $result);
} }
@ -1824,15 +1895,14 @@ class Libzot {
/** /**
* @brief Remove community tag. * @brief Remove community tag.
* *
* @param array $sender an associative array with * @param string $sender
* * \e string \b hash a xchan_hash
* @param array $arr an associative array * @param array $arr an associative array
* * \e int \b verb * * \e int \b verb
* * \e int \b obj_type * * \e int \b obj_type
* * \e int \b mid * * \e int \b mid
* @param int $uid * @param int $uid
* @return void
*/ */
static function remove_community_tag($sender, $arr, $uid) { static function remove_community_tag($sender, $arr, $uid) {
if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM)))
@ -1898,13 +1968,13 @@ class Libzot {
* *
* @see item_store_update() * @see item_store_update()
* *
* @param array $sender * @param string $sender
* @param array $item * @param array $item
* @param array $orig * @param array $orig
* @param int $uid * @param int $uid
* @param boolean $tag_delivery * @param boolean $tag_delivery
* @return void|array
*/ */
static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) {
// If this is a comment being updated, remove any privacy information // If this is a comment being updated, remove any privacy information
@ -1949,7 +2019,7 @@ class Libzot {
/** /**
* @brief Deletes an imported item. * @brief Deletes an imported item.
* *
* @param array $sender * @param string $sender
* * \e string \b hash a xchan_hash * * \e string \b hash a xchan_hash
* @param array $item * @param array $item
* @param int $uid * @param int $uid
@ -1967,9 +2037,9 @@ class Libzot {
$r = q("select id, author_xchan, owner_xchan, source_xchan, item_deleted from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' ) $r = q("select id, author_xchan, owner_xchan, source_xchan, item_deleted from item where ( author_xchan = '%s' or owner_xchan = '%s' or source_xchan = '%s' )
and mid = '%s' and uid = %d limit 1", and mid = '%s' and uid = %d limit 1",
dbesc($sender['hash']), dbesc($sender),
dbesc($sender['hash']), dbesc($sender),
dbesc($sender['hash']), dbesc($sender),
dbesc($item['mid']), dbesc($item['mid']),
intval($uid) intval($uid)
); );
@ -2047,7 +2117,7 @@ class Libzot {
$DR = new DReport(z_root(),$sender,$d,$arr['mid']); $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$r = q("select * from channel where channel_hash = '%s' limit 1", $r = q("select * from channel where channel_portable_id = '%s' limit 1",
dbesc($d['hash']) dbesc($d['hash'])
); );
@ -2123,18 +2193,18 @@ class Libzot {
* @brief Processes delivery of profile. * @brief Processes delivery of profile.
* *
* @see import_directory_profile() * @see import_directory_profile()
* @param array $sender an associative array *
* * \e string \b hash a xchan_hash * @param string $sender
* @param array $arr * @param array $arr
* @param array $deliveries (unused) * @param array $deliveries (unused)
* @return void
*/ */
static function process_profile_delivery($sender, $arr, $deliveries) { static function process_profile_delivery($sender, $arr, $deliveries) {
logger('process_profile_delivery', LOGGER_DEBUG); logger('process_profile_delivery', LOGGER_DEBUG);
$r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1", $r = q("select xchan_addr from xchan where xchan_hash = '%s' limit 1",
dbesc($sender['hash']) dbesc($sender)
); );
if($r) { if($r) {
Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0); Libzotdir::import_directory_profile($sender, $arr, $r[0]['xchan_addr'], UPDATE_FLAGS_UPDATED, 0);
@ -2145,10 +2215,10 @@ class Libzot {
/** /**
* @brief * @brief
* *
* @param array $sender an associative array * @param string $sender
* * \e string \b hash a xchan_hash
* @param array $arr * @param array $arr
* @param array $deliveries (unused) deliveries is irrelevant * @param array $deliveries (unused) deliveries is irrelevant
* @return void
*/ */
static function process_location_delivery($sender, $arr, $deliveries) { static function process_location_delivery($sender, $arr, $deliveries) {
@ -2166,7 +2236,7 @@ class Libzot {
$x = Libsync::sync_locations($xchan,$arr,true); $x = Libsync::sync_locations($xchan,$arr,true);
logger('results: ' . print_r($x,true), LOGGER_DEBUG); logger('results: ' . print_r($x,true), LOGGER_DEBUG);
if($x['changed']) { if($x['changed']) {
$guid = random_string() . '@' . App::get_hostname(); //$guid = random_string() . '@' . App::get_hostname();
Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED); Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED);
} }
} }
@ -2190,8 +2260,8 @@ class Libzot {
* *
* @param string $sender_hash A channel hash * @param string $sender_hash A channel hash
* @param array $locations * @param array $locations
* @return void
*/ */
static function check_location_move($sender_hash, $locations) { static function check_location_move($sender_hash, $locations) {
if(! $locations) if(! $locations)
@ -2202,7 +2272,7 @@ class Libzot {
$loc = $locations[0]; $loc = $locations[0];
$r = q("select * from channel where channel_hash = '%s' limit 1", $r = q("select * from channel where channel_portable_id = '%s' limit 1",
dbesc($sender_hash) dbesc($sender_hash)
); );
@ -2210,7 +2280,7 @@ class Libzot {
return; return;
if($loc['url'] !== z_root()) { if($loc['url'] !== z_root()) {
$x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", $x = q("update channel set channel_moved = '%s' where channel_portable_id = '%s' limit 1",
dbesc($loc['url']), dbesc($loc['url']),
dbesc($sender_hash) dbesc($sender_hash)
); );
@ -2233,7 +2303,6 @@ class Libzot {
} }
/** /**
* @brief Returns an array with all known distinct hubs for this channel. * @brief Returns an array with all known distinct hubs for this channel.
* *
@ -2242,11 +2311,10 @@ class Libzot {
* * \e string \b channel_hash the hash of the channel * * \e string \b channel_hash the hash of the channel
* @return array an array with associative arrays * @return array an array with associative arrays
*/ */
static function encode_locations($channel) { static function encode_locations($channel) {
$ret = []; $ret = [];
$x = self::get_hublocs($channel['channel_hash']); $x = self::get_hublocs($channel['channel_portable_id']);
if($x && count($x)) { if($x && count($x)) {
foreach($x as $hub) { foreach($x as $hub) {
@ -2296,10 +2364,8 @@ class Libzot {
* @brief * @brief
* *
* @param array $arr * @param array $arr
* @param string $pubkey
* @return boolean true if updated or inserted * @return boolean true if updated or inserted
*/ */
static function import_site($arr) { static function import_site($arr) {
if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig']))
@ -2557,6 +2623,9 @@ class Libzot {
static function zotinfo($arr) { static function zotinfo($arr) {
logger('arr: ' . print_r($arr,true));
$ret = []; $ret = [];
$zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : ''); $zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : '');
@ -2571,7 +2640,7 @@ class Libzot {
$feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0);
if($ztarget) { if($ztarget) {
$t = q("select * from hubloc where hubloc_id_url = '%s' limit 1", $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($ztarget) dbesc($ztarget)
); );
if($t) { if($t) {
@ -2593,13 +2662,13 @@ class Libzot {
$r = null; $r = null;
if(strlen($zhash)) { if(strlen($zhash)) {
$r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash $r = q("select channel.*, xchan.* from channel left join xchan on channel_portable_id = xchan_hash
where channel_hash = '%s' limit 1", where channel_portable_id = '%s' limit 1",
dbesc($zhash) dbesc($zhash)
); );
} }
elseif(strlen($zguid) && strlen($zguid_sig)) { elseif(strlen($zguid) && strlen($zguid_sig)) {
$r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash $r = q("select channel.*, xchan.* from channel left join xchan on channel_portable_id = xchan_hash
where channel_guid = '%s' and channel_guid_sig = '%s' limit 1", where channel_guid = '%s' and channel_guid_sig = '%s' limit 1",
dbesc($zguid), dbesc($zguid),
dbesc($zguid_sig) dbesc($zguid_sig)
@ -2607,7 +2676,7 @@ class Libzot {
} }
elseif(strlen($zaddr)) { elseif(strlen($zaddr)) {
if(strpos($zaddr,'[system]') === false) { /* normal address lookup */ if(strpos($zaddr,'[system]') === false) { /* normal address lookup */
$r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash $r = q("select channel.*, xchan.* from channel left join xchan on channel_portable_id = xchan_hash
where ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", where ( channel_address = '%s' or xchan_addr = '%s' ) limit 1",
dbesc($zaddr), dbesc($zaddr),
dbesc($zaddr) dbesc($zaddr)
@ -2627,10 +2696,10 @@ class Libzot {
* *
*/ */
$r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash $r = q("select channel.*, xchan.* from channel left join xchan on channel_portable_id = xchan_hash
where channel_system = 1 order by channel_id limit 1"); where channel_system = 1 order by channel_id limit 1");
if(! $r) { if(! $r) {
$r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash $r = q("select channel.*, xchan.* from channel left join xchan on channel_portable_id = xchan_hash
where channel_removed = 0 order by channel_id limit 1"); where channel_removed = 0 order by channel_id limit 1");
} }
} }
@ -2749,7 +2818,7 @@ class Libzot {
]; ];
$ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom'); $ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom');
$ret['protocols'] = [ 'zot6' ]; $ret['protocols'] = [ 'zot', 'zot6' ];
$ret['searchable'] = $searchable; $ret['searchable'] = $searchable;
$ret['adult_content'] = $adult_channel; $ret['adult_content'] = $adult_channel;
$ret['public_forum'] = $public_forum; $ret['public_forum'] = $public_forum;
@ -2774,7 +2843,7 @@ class Libzot {
if(! $ret['follow_url']) if(! $ret['follow_url'])
$ret['follow_url'] = z_root() . '/follow?f=&url=%s'; $ret['follow_url'] = z_root() . '/follow?f=&url=%s';
$permissions = get_all_perms($e['channel_id'],$ztarget_hash,false); $permissions = get_all_perms($e['channel_id'],$ztarget_hash,false,false);
if($ztarget_hash) { if($ztarget_hash) {
$permissions['connected'] = false; $permissions['connected'] = false;
@ -2811,14 +2880,20 @@ class Libzot {
$ret['locations'] = $x; $ret['locations'] = $x;
$ret['site'] = self::site_info(); $ret['site'] = self::site_info();
/**
* @hooks zotinfo
* Hook to manipulate the zotinfo array before it is returned.
*/
call_hooks('zotinfo', $ret);
call_hooks('zotinfo',$ret); return $ret;
return($ret);
} }
/**
* @brief Get siteinfo.
*
* @return array
*/
static function site_info() { static function site_info() {
$signing_key = get_config('system','prvkey'); $signing_key = get_config('system','prvkey');
@ -2902,18 +2977,16 @@ class Libzot {
} }
return $ret['site']; return $ret['site'];
} }
/** /**
* @brief * @brief
* *
* @param array $hub * @param array $hub
* @param string $sitekey (optional, default empty) * @param string $site_id (optional, default empty)
* *
* @return string hubloc_url * @return string hubloc_url
*/ */
static function update_hub_connected($hub, $site_id = '') { static function update_hub_connected($hub, $site_id = '') {
if ($site_id) { if ($site_id) {
@ -2972,12 +3045,21 @@ class Libzot {
return $hub['hubloc_url']; return $hub['hubloc_url'];
} }
/**
* @brief
*
* @param string $data
* @param string $key
* @param string $alg (optional) default 'sha256'
* @return string
*/
static function sign($data,$key,$alg = 'sha256') { static function sign($data,$key,$alg = 'sha256') {
if(! $key) if(! $key)
return 'no key'; return 'no key';
$sig = ''; $sig = '';
openssl_sign($data,$sig,$key,$alg); openssl_sign($data,$sig,$key,$alg);
return $alg . '.' . base64url_encode($sig); return $alg . '.' . base64url_encode($sig);
} }
@ -3003,11 +3085,14 @@ class Libzot {
return(($verify > 0) ? true : false); return(($verify > 0) ? true : false);
} }
/**
* @brief
*
* @return boolean
*/
static function is_zot_request() { static function is_zot_request() {
$x = getBestSupportedMimeType([ 'application/x-zot+json' ]); $x = getBestSupportedMimeType([ 'application/x-zot+json' ]);
return(($x) ? true : false); return(($x) ? true : false);
} }

View File

@ -40,26 +40,17 @@ class NativeWiki {
function create_wiki($channel, $observer_hash, $wiki, $acl) { function create_wiki($channel, $observer_hash, $wiki, $acl) {
// Generate unique resource_id using the same method as item_message_id() $resource_id = new_uuid();
do { $uuid = new_uuid();
$dups = false;
$resource_id = random_string();
$r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1",
dbesc($resource_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
intval($channel['channel_id'])
);
if($r)
$dups = true;
} while($dups == true);
$ac = $acl->get(); $ac = $acl->get();
$mid = item_message_id(); $mid = z_root() . '/item/' . $uuid;
$arr = array(); // Initialize the array of parameters for the post $arr = array(); // Initialize the array of parameters for the post
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
$wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName'];
$arr['aid'] = $channel['channel_account_id']; $arr['aid'] = $channel['channel_account_id'];
$arr['uuid'] = $uuid;
$arr['uid'] = $channel['channel_id']; $arr['uid'] = $channel['channel_id'];
$arr['mid'] = $mid; $arr['mid'] = $mid;
$arr['parent_mid'] = $mid; $arr['parent_mid'] = $mid;

View File

@ -112,9 +112,11 @@ class PConfig {
* The configuration key to set * The configuration key to set
* @param string $value * @param string $value
* The value to store * The value to store
* @param string $updated (optional)
* The datetime to store
* @return mixed Stored $value or false * @return mixed Stored $value or false
*/ */
static public function Set($uid, $family, $key, $value, $updated=NULL) { static public function Set($uid, $family, $key, $value, $updated = NULL) {
// this catches subtle errors where this function has been called // this catches subtle errors where this function has been called
// with local_channel() when not logged in (which returns false) // with local_channel() when not logged in (which returns false)
@ -131,14 +133,19 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
$now = datetime_convert();
if (! $updated) { if (! $updated) {
$updated = datetime_convert(); //Sometimes things happen fast... very fast.
//To make sure legitimate updates aren't rejected
//because not enough time has passed. We say our updates
//happened just a short time in the past rather than right now.
$updated = datetime_convert('UTC','UTC','-2 seconds');
} }
$hash = hash('sha256',$family.':'.$key); $hash = hash('sha256',$family.':'.$key);
if (self::Get($uid, 'hz_delpconfig', $hash) !== false) { if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
if (self::Get($uid, 'hz_delpconfig', $hash) > $updated) { if (self::Get($uid, 'hz_delpconfig', $hash) > $now) {
logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR); logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
return self::Get($uid,$family,$key); return self::Get($uid,$family,$key);
} else { } else {
@ -173,7 +180,7 @@ class PConfig {
} }
else { else {
$new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated); $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if ($new) { if ($new) {
@ -234,16 +241,18 @@ class PConfig {
* The category of the configuration value * The category of the configuration value
* @param string $key * @param string $key
* The configuration key to delete * The configuration key to delete
* @return mixed * @param string $updated (optional)
* The datetime to store
* @return boolean
*/ */
static public function Delete($uid, $family, $key, $updated = NULL) { static public function Delete($uid, $family, $key, $updated = NULL) {
if(is_null($uid) || $uid === false) if(is_null($uid) || $uid === false)
return false; return false;
$updated = ($updated) ? $updated : datetime_convert(); $updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds');
$now = datetime_convert();
$newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated); $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if (! $newer) { if (! $newer) {
logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR); logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
@ -266,22 +275,13 @@ class PConfig {
dbesc($key) dbesc($key)
); );
// Synchronize delete with clones.
if ($family != 'hz_delpconfig') { if ($family != 'hz_delpconfig') {
$hash = hash('sha256',$family.':'.$key); $hash = hash('sha256',$family.':'.$key);
set_pconfig($uid,'hz_delpconfig',$hash,$updated); set_pconfig($uid,'hz_delpconfig',$hash,$updated);
} }
// Synchronize delete with clones.
if(! array_key_exists('transient', \App::$config[$uid]))
\App::$config[$uid]['transient'] = array();
if(! array_key_exists($family, \App::$config[$uid]['transient']))
\App::$config[$uid]['transient'][$family] = array();
if ($new) {
\App::$config[$uid]['transient'][$family]['pcfgdel:'.$key] = $updated;
}
return $ret; return $ret;
} }

View File

@ -54,6 +54,7 @@ class Share {
if(! $this->item) if(! $this->item)
return $obj; return $obj;
$obj['asld'] = $this->item['mid'];
$obj['type'] = $this->item['obj_type']; $obj['type'] = $this->item['obj_type'];
$obj['id'] = $this->item['mid']; $obj['id'] = $this->item['mid'];
$obj['content'] = $this->item['body']; $obj['content'] = $this->item['body'];

View File

@ -39,6 +39,8 @@ class ThreadItem {
$this->data = $data; $this->data = $data;
$this->toplevel = ($this->get_id() == $this->get_data_value('parent')); $this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
$observer = \App::get_observer();
// Prepare the children // Prepare the children
if($data['children']) { if($data['children']) {
foreach($data['children'] as $item) { foreach($data['children'] as $item) {
@ -51,6 +53,7 @@ class ThreadItem {
continue; continue;
} }
$child = new ThreadItem($item); $child = new ThreadItem($item);
$this->add_child($child); $this->add_child($child);
} }
@ -73,7 +76,7 @@ class ThreadItem {
* _ false on failure * _ false on failure
*/ */
public function get_template_data($conv_responses, $thread_level=1) { public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
$result = array(); $result = array();
@ -98,6 +101,7 @@ class ThreadItem {
|| strlen($item['deny_cid']) || strlen($item['deny_gid'])))) || strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? t('Private Message') ? t('Private Message')
: false); : false);
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
// allow an exemption for sharing stuff from your private feeds // allow an exemption for sharing stuff from your private feeds
@ -112,6 +116,19 @@ class ThreadItem {
$privacy_warning = true; $privacy_warning = true;
} }
if ($lock) {
if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) {
$privacy_warning = true;
$conv_flags['parent_privacy_warning'] = true;
}
}
$privacy_warning = (isset($conv_flags['parent_privacy_warning'])) ? $conv_flags['parent_privacy_warning'] : $privacy_warning;
if ($lock && $privacy_warning) {
$lock = t('Privacy conflict. Discretion advised.');
}
$mode = $conv->get_mode(); $mode = $conv->get_mode();
switch($item['item_type']) { switch($item['item_type']) {
@ -290,8 +307,15 @@ class ThreadItem {
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike")); $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
} }
if ($shareable) if ($shareable) {
$share = array( t('Share This'), t('share')); // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
// Will allow it only for uri resolvable sources.
if(strpos($item['mid'],'http') === 0) {
$share = []; //Not yet ready for primetime
//$share = array( t('Repeat This'), t('repeat'));
}
$embed = array( t('Share This'), t('share'));
}
$dreport = ''; $dreport = '';
@ -299,8 +323,10 @@ class ThreadItem {
if($keep_reports === 0) if($keep_reports === 0)
$keep_reports = 10; $keep_reports = 10;
if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
$dreport = t('Delivery Report'); $dreport = t('Delivery Report');
$dreport_link = gen_link_id($item['mid']);
}
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
$is_new = true; $is_new = true;
@ -316,7 +342,7 @@ class ThreadItem {
$owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@')); $owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@'));
$viewthread = $item['llink']; $viewthread = $item['llink'];
if($conv->get_mode() === 'channel') if($conv->get_mode() === 'channel')
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode($item['mid']); $viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
$list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : ''); $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : '');
@ -364,6 +390,7 @@ class ThreadItem {
'thread_action_menu' => thread_action_menu($item,$conv->get_mode()), 'thread_action_menu' => thread_action_menu($item,$conv->get_mode()),
'thread_author_menu' => thread_author_menu($item,$conv->get_mode()), 'thread_author_menu' => thread_author_menu($item,$conv->get_mode()),
'dreport' => $dreport, 'dreport' => $dreport,
'dreport_link' => $dreport_link,
'name' => $profile_name, 'name' => $profile_name,
'thumb' => $profile_avatar, 'thumb' => $profile_avatar,
'osparkle' => $osparkle, 'osparkle' => $osparkle,
@ -402,12 +429,13 @@ class ThreadItem {
'like' => $like, 'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''), 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'share' => $share, 'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'], 'rawmid' => $item['mid'],
'plink' => get_plink($item), 'plink' => get_plink($item),
'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''), 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''),
'addtocal' => (($has_event) ? t('Add to Calendar') : ''), 'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
'drop' => $drop, 'drop' => $drop,
@ -464,7 +492,7 @@ class ThreadItem {
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) { foreach($children as $child) {
$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1); $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
} }
// Collapse // Collapse
if(($nb_children > $visible_comments) || ($thread_level > 1)) { if(($nb_children > $visible_comments) || ($thread_level > 1)) {

View File

@ -0,0 +1,53 @@
<?php
namespace Zotlabs\Lib;
class ThreadListener {
static public function store($target_id,$portable_id,$ltype = 0) {
$x = self::fetch($target_id,$portable_id,$ltype = 0);
if(! $x) {
$r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ",
dbesc($target_id),
dbesc($portable_id),
intval($ltype)
);
}
}
static public function fetch($target_id,$portable_id,$ltype = 0) {
$x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1",
dbesc($target_id),
dbesc($portable_id),
intval($ltype)
);
if($x) {
return $x[0];
}
return false;
}
static public function fetch_by_target($target_id,$ltype = 0) {
$x = q("select * from listeners where target_id = '%s' and ltype = %d",
dbesc($target_id),
intval($ltype)
);
return $x;
}
static public function delete_by_target($target_id, $ltype = 0) {
return q("delete from listeners where target_id = '%s' and ltype = %d",
dbesc($target_id),
intval($ltype)
);
}
static public function delete_by_pid($portable_id, $ltype = 0) {
return q("delete from listeners where portable_id = '%s' and ltype = %d",
dbesc($portable_id),
intval($ltype)
);
}
}

91
Zotlabs/Lib/ZotURL.php Normal file
View File

@ -0,0 +1,91 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\Zot6\HTTPSig;
class ZotURL {
static public function fetch($url,$channel) {
$ret = [ 'success' => false ];
if(strpos($url,'x-zot:') !== 0) {
return $ret;
}
if(! $url) {
return $ret;
}
$portable_url = substr($url,6);
$u = explode('/',$portable_url);
$portable_id = $u[0];
$hosts = self::lookup($portable_id);
if(! $hosts) {
return $ret;
}
foreach($hosts as $h) {
$newurl = $h . '/id/' . $portable_url;
$m = parse_url($newurl);
$data = json_encode([ 'zot_token' => random_string() ]);
if($channel && $m) {
$headers = [
'Accept' => 'application/x-zot+json',
'Content-Type' => 'application/x-zot+json',
'X-Zot-Token' => random_string(),
'Digest' => HTTPSig::generate_digest_header($data),
'Host' => $m['host'],
'(request-target)' => 'post ' . get_request_string($newurl)
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
}
else {
$h = [ 'Accept: application/x-zot+json' ];
}
$result = [];
$redirects = 0;
$x = z_post_url($newurl,$data,$redirects, [ 'headers' => $h ] );
if($x['success']) {
return $x;
}
}
return $ret;
}
static public function is_zoturl($s) {
if(strpos($url,'x-zot:') === 0) {
return true;
}
return false;
}
static public function lookup($portable_id) {
$r = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0 order by hubloc_primary desc",
dbesc($portable_id)
);
if(! $r) {
// extend to network lookup
return false;
}
return ids_to_array($r,'hubloc_url');
}
}

View File

@ -2,7 +2,7 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use Zotlabs\Web\HTTPSig; use Zotlabs\Zot6\HTTPSig;
class Zotfinger { class Zotfinger {
@ -12,10 +12,19 @@ class Zotfinger {
return false; return false;
} }
if($channel) { $m = parse_url($resource);
$data = json_encode([ 'zot_token' => random_string() ]);
if($channel && $m) {
$headers = [ $headers = [
'Accept' => 'application/x-zot+json', 'Accept' => 'application/x-zot+json',
'X-Zot-Token' => random_string(), 'Content-Type' => 'application/x-zot+json',
'X-Zot-Token' => random_string(),
'Digest' => HTTPSig::generate_digest_header($data),
'Host' => $m['host'],
'(request-target)' => 'post ' . get_request_string($resource)
]; ];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false); $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
} }
@ -27,7 +36,9 @@ class Zotfinger {
$redirects = 0; $redirects = 0;
$x = z_fetch_url($resource,false,$redirects, [ 'headers' => $h ] ); $x = z_post_url($resource,$data,$redirects, [ 'headers' => $h ] );
logger('fetch: ' . print_r($x,true));
if($x['success']) { if($x['success']) {
@ -39,6 +50,8 @@ class Zotfinger {
$result['data'] = json_decode(crypto_unencapsulate($result['data'],get_config('system','prvkey')),true); $result['data'] = json_decode(crypto_unencapsulate($result['data'],get_config('system','prvkey')),true);
} }
logger('decrypted: ' . print_r($result,true));
return $result; return $result;
} }

View File

@ -86,7 +86,7 @@ class Admin extends \Zotlabs\Web\Controller {
// list total user accounts, expirations etc. // list total user accounts, expirations etc.
$accounts = array(); $accounts = array();
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account", $r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
db_utcnow(), db_utcnow(),
db_utcnow(), db_utcnow(),
dbesc(NULL_DATE), dbesc(NULL_DATE),

View File

@ -375,6 +375,9 @@ class Addons {
if($files) { if($files) {
foreach($files as $file) { foreach($files as $file) {
if (is_dir($file)){ if (is_dir($file)){
if($file == 'addon/addon_common/')
continue;
list($tmp, $id) = array_map('trim', explode('/', $file)); list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id); $info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins); $enabled = in_array($id,\App::$plugins);

View File

@ -119,7 +119,7 @@ class Site {
del_config('system', 'admininfo'); del_config('system', 'admininfo');
} else { } else {
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $admininfo, local_channel()); linkify_tags($admininfo, local_channel());
set_config('system', 'admininfo', $admininfo); set_config('system', 'admininfo', $admininfo);
} }
set_config('system','siteinfo',$siteinfo); set_config('system','siteinfo',$siteinfo);

View File

@ -0,0 +1,94 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
class Affinity extends \Zotlabs\Web\Controller {
function post() {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
return;
check_form_security_token_redirectOnErr('affinity', 'affinity');
$cmax = intval($_POST['affinity_cmax']);
if($cmax < 0 || $cmax > 99)
$cmax = 99;
$cmin = intval($_POST['affinity_cmin']);
if($cmin < 0 || $cmin > 99)
$cmin = 0;
$lock = intval($_POST['affinity_lock']);
set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
set_pconfig(local_channel(),'affinity','lock',$lock);
info( t('Affinity Tool settings updated.') . EOL);
Libsync::build_sync_packet();
}
function get() {
if(! local_channel())
return;
$desc = t('This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.');
if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) {
//Do not display any associated widgets at this point
App::$pdl = '';
$o = '<b>' . t('Affinity Tool App') . ' (' . t('Not Installed') . '):</b><br>';
$o .= $desc;
return $o;
}
$text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.');
$content = '<div class="section-content-info-wrapper">' . $text . '</div>';
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
$cmax = (($cmax) ? $cmax : 99);
$content .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
));
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
$cmin = (($cmin) ? $cmin : 0);
$content .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
));
$lock = intval(get_pconfig(local_channel(),'affinity','lock',1));
$content .= replace_macros(get_markup_template('field_checkbox.tpl'), array(
'$field' => array('affinity_lock', t('Persistent affinity levels'), $lock, t('If disabled the max and min levels will be reset to default after page reload'), ['No','Yes'])
));
$tpl = get_markup_template("settings_addon.tpl");
$o = replace_macros($tpl, array(
'$action_url' => 'affinity',
'$form_security_token' => get_form_security_token("affinity"),
'$title' => t('Affinity Tool Settings'),
'$content' => $content,
'$baseurl' => z_root(),
'$submit' => t('Submit'),
));
return $o;
}
}

View File

@ -10,9 +10,13 @@ require_once('include/channel.php');
require_once('include/conversation.php'); require_once('include/conversation.php');
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
/**
* @brief Provides the Cards module.
*
*/
class Cards extends Controller { class Cards extends Controller {
function init() { public function init() {
if(argc() > 1) if(argc() > 1)
$which = argv(1); $which = argv(1);
@ -20,14 +24,15 @@ class Cards extends Controller {
return; return;
profile_load($which); profile_load($which);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
* @see \Zotlabs\Web\Controller::get() * @see \\Zotlabs\\Web\\Controller::get()
*
* @return string Parsed HTML from template 'cards.tpl'
*/ */
function get($update = 0, $load = false) { public function get($update = 0, $load = false) {
if(observer_prohibited(true)) { if(observer_prohibited(true)) {
return login(); return login();
@ -99,7 +104,6 @@ class Cards extends Controller {
} }
if(perm_is_allowed($owner, $ob_hash, 'write_pages')) { if(perm_is_allowed($owner, $ob_hash, 'write_pages')) {
$x = [ $x = [

View File

@ -6,6 +6,8 @@ namespace Zotlabs\Module;
use App; use App;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Lib\PermissionDescription; use Zotlabs\Lib\PermissionDescription;
use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Lib\Libzot;
require_once('include/items.php'); require_once('include/items.php');
require_once('include/security.php'); require_once('include/security.php');
@ -43,6 +45,48 @@ class Channel extends Controller {
$profile = 0; $profile = 0;
$channel = App::get_channel(); $channel = App::get_channel();
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
$which = $channel['channel_address'];
$profile = argv(1);
}
$channel = channelx_by_nick($which);
if(! $channel) {
http_status_exit(404, 'Not found');
}
// handle zot6 channel discovery
if(Libzot::is_zot_request()) {
$sigdata = HTTPSig::verify(file_get_contents('php://input'));
if($sigdata && $sigdata['signer'] && $sigdata['header_valid']) {
$data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'], 'target_url' => $sigdata['signer'] ]));
$s = q("select site_crypto, hubloc_sitekey from site left join hubloc on hubloc_url = site_url where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($sigdata['signer'])
);
if($s) {
$data = json_encode(crypto_encapsulate($data,$s[0]['hubloc_sitekey'],Libzot::best_algorithm($s[0]['site_crypto'])));
}
}
else {
$data = json_encode(Libzot::zotinfo([ 'address' => $channel['channel_address'] ]));
}
$headers = [
'Content-Type' => 'application/x-zot+json',
'Digest' => HTTPSig::generate_digest_header($data),
'(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']
];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
HTTPSig::set_headers($h);
echo $data;
killme();
}
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) { if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
$which = $channel['channel_address']; $which = $channel['channel_address'];
$profile = argv(1); $profile = argv(1);

View File

@ -106,7 +106,7 @@ class Chanview extends \Zotlabs\Web\Controller {
if (\App::$poi) { if (\App::$poi) {
$url = \App::$poi['xchan_url']; $url = \App::$poi['xchan_url'];
if(\App::$poi['xchan_network'] === 'zot') { if(in_array(\App::$poi['xchan_network'], ['zot', 'zot6'])) {
$is_zot = true; $is_zot = true;
} }
if(local_channel()) { if(local_channel()) {

View File

@ -8,6 +8,7 @@ namespace Zotlabs\Module;
*/ */
use Zotlabs\Lib\Apps; use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libzot;
require_once('include/socgraph.php'); require_once('include/socgraph.php');
require_once('include/selectors.php'); require_once('include/selectors.php');
@ -100,7 +101,8 @@ class Connedit extends \Zotlabs\Web\Controller {
} }
$profile_id = $_POST['profile_assign']; $profile_id = ((array_key_exists('profile_assign',$_POST)) ? $_POST['profile_assign'] : $orig_record[0]['abook_profile']);
if($profile_id) { if($profile_id) {
$r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1", $r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1",
dbesc($profile_id), dbesc($profile_id),
@ -112,8 +114,9 @@ class Connedit extends \Zotlabs\Web\Controller {
} }
} }
$abook_incl = escape_tags($_POST['abook_incl']); $abook_incl = ((array_key_exists('abook_incl',$_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record[0]['abook_incl']);
$abook_excl = escape_tags($_POST['abook_excl']); $abook_excl = ((array_key_exists('abook_excl',$_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record[0]['abook_excl']);
$hidden = intval($_POST['hidden']); $hidden = intval($_POST['hidden']);
@ -121,9 +124,13 @@ class Connedit extends \Zotlabs\Web\Controller {
if($priority > 5 || $priority < 0) if($priority > 5 || $priority < 0)
$priority = 0; $priority = 0;
if(! array_key_exists('closeness',$_POST)) {
$_POST['closeness'] = 80;
}
$closeness = intval($_POST['closeness']); $closeness = intval($_POST['closeness']);
if($closeness < 0) if($closeness < 0 || $closeness > 99) {
$closeness = 99; $closeness = 80;
}
$rating = intval($_POST['rating']); $rating = intval($_POST['rating']);
if($rating < (-10)) if($rating < (-10))
@ -231,6 +238,8 @@ class Connedit extends \Zotlabs\Web\Controller {
$abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']); $abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']);
$r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d,
abook_incl = '%s', abook_excl = '%s' abook_incl = '%s', abook_excl = '%s'
where abook_id = %d AND abook_channel = %d", where abook_id = %d AND abook_channel = %d",
@ -475,6 +484,10 @@ class Connedit extends \Zotlabs\Web\Controller {
if(! zot_refresh($orig_record[0],\App::get_channel())) if(! zot_refresh($orig_record[0],\App::get_channel()))
notice( t('Refresh failed - channel is currently unavailable.') ); notice( t('Refresh failed - channel is currently unavailable.') );
} }
elseif($orig_record[0]['xchan_network'] === 'zot6') {
if(! Libzot::refresh($orig_record[0],\App::get_channel()))
notice( t('Refresh failed - channel is currently unavailable.') );
}
else { else {
// if you are on a different network we'll force a refresh of the connection basic info // if you are on a different network we'll force a refresh of the connection basic info
@ -697,7 +710,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$tpl = get_markup_template("abook_edit.tpl"); $tpl = get_markup_template("abook_edit.tpl");
if(feature_enabled(local_channel(),'affinity')) { if(Apps::system_app_installed(local_channel(),'Affinity Tool')) {
$sections['affinity'] = [ $sections['affinity'] = [
'label' => t('Affinity'), 'label' => t('Affinity'),
@ -728,9 +741,12 @@ class Connedit extends \Zotlabs\Web\Controller {
} }
$slider_tpl = get_markup_template('contact_slider.tpl'); $slider_tpl = get_markup_template('contact_slider.tpl');
$slideval = intval($contact['abook_closeness']);
$slide = replace_macros($slider_tpl,array( $slide = replace_macros($slider_tpl,array(
'$min' => 1, '$min' => 1,
'$val' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 99), '$val' => $slideval,
'$labels' => $label_str, '$labels' => $label_str,
)); ));
} }
@ -887,7 +903,7 @@ class Connedit extends \Zotlabs\Web\Controller {
'$inherited' => t('inherited'), '$inherited' => t('inherited'),
'$submit' => t('Submit'), '$submit' => t('Submit'),
'$lbl_vis2' => sprintf( t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']), '$lbl_vis2' => sprintf( t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']),
'$close' => $contact['abook_closeness'], '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80),
'$them' => t('Their Settings'), '$them' => t('Their Settings'),
'$me' => t('My Settings'), '$me' => t('My Settings'),
'$perms' => $perms, '$perms' => $perms,

View File

@ -179,7 +179,6 @@ class Display extends \Zotlabs\Web\Controller {
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
$simple_update = (($update) ? " AND item_unseen = 1 " : ''); $simple_update = (($update) ? " AND item_unseen = 1 " : '');
if($update && $_SESSION['loadtime']) if($update && $_SESSION['loadtime'])
@ -314,7 +313,7 @@ class Display extends \Zotlabs\Web\Controller {
} }
} }
if(! $r) { if($r === null) {
// in case somebody turned off public access to sys channel content using permissions // in case somebody turned off public access to sys channel content using permissions
// make that content unsearchable by ensuring the owner_xchan can't match // make that content unsearchable by ensuring the owner_xchan can't match
if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))

View File

@ -17,9 +17,17 @@ class Dreport extends \Zotlabs\Web\Controller {
$mid = ((argc() > 1) ? argv(1) : ''); $mid = ((argc() > 1) ? argv(1) : '');
if(strpos($mid,'b64.') === 0)
$mid = @base64url_decode(substr($mid,4));
if($mid === 'push') { if($mid === 'push') {
$table = 'push'; $table = 'push';
$mid = ((argc() > 2) ? argv(2) : ''); $mid = ((argc() > 2) ? argv(2) : '');
if(strpos($mid,'b64.') === 0)
$mid = @base64url_decode(substr($mid,4));
if($mid) { if($mid) {
$i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ", $i = q("select id from item where mid = '%s' and uid = %d and ( author_xchan = '%s' or ( owner_xchan = '%s' and item_wall = 1 )) ",
dbesc($mid), dbesc($mid),
@ -38,6 +46,9 @@ class Dreport extends \Zotlabs\Web\Controller {
if($mid === 'mail') { if($mid === 'mail') {
$table = 'mail'; $table = 'mail';
$mid = ((argc() > 2) ? argv(2) : ''); $mid = ((argc() > 2) ? argv(2) : '');
if(strpos($mid,'b64.') === 0)
$mid = @base64url_decode(substr($mid,4));
} }
@ -69,8 +80,9 @@ class Dreport extends \Zotlabs\Web\Controller {
return; return;
} }
$r = q("select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s'", $r = q("select * from dreport where (dreport_xchan = '%s' or dreport_xchan = '%s') and dreport_mid = '%s'",
dbesc($channel['channel_hash']), dbesc($channel['channel_hash']),
dbesc($channel['channel_portable_id']),
dbesc($mid) dbesc($mid)
); );
@ -80,7 +92,6 @@ class Dreport extends \Zotlabs\Web\Controller {
} }
for($x = 0; $x < count($r); $x++ ) { for($x = 0; $x < count($r); $x++ ) {
$r[$x]['name'] = escape_tags(substr($r[$x]['dreport_recip'],strpos($r[$x]['dreport_recip'],' ')));
// This has two purposes: 1. make the delivery report strings translateable, and // This has two purposes: 1. make the delivery report strings translateable, and
// 2. assign an ordering to item delivery results so we can group them and provide // 2. assign an ordering to item delivery results so we can group them and provide
@ -138,14 +149,14 @@ class Dreport extends \Zotlabs\Web\Controller {
$entries = array(); $entries = array();
foreach($r as $rr) { foreach($r as $rr) {
$entries[] = [ $entries[] = [
'name' => $rr['name'], 'name' => escape_tags($rr['dreport_name'] ?: $rr['dreport_recip']),
'result' => escape_tags($rr['dreport_result']), 'result' => escape_tags($rr['dreport_result']),
'time' => escape_tags(datetime_convert('UTC',date_default_timezone_get(),$rr['dreport_time'])) 'time' => escape_tags(datetime_convert('UTC',date_default_timezone_get(),$rr['dreport_time']))
]; ];
} }
$o = replace_macros(get_markup_template('dreport.tpl'), array( $o = replace_macros(get_markup_template('dreport.tpl'), array(
'$title' => sprintf( t('Delivery report for %1$s'),substr($mid,0,32)) . '...', '$title' => sprintf( t('Delivery report for %1$s'),basename($mid)) . '...',
'$table' => $table, '$table' => $table,
'$mid' => urlencode($mid), '$mid' => urlencode($mid),
'$options' => t('Options'), '$options' => t('Options'),
@ -162,9 +173,9 @@ class Dreport extends \Zotlabs\Web\Controller {
private static function dreport_gravity_sort($a,$b) { private static function dreport_gravity_sort($a,$b) {
if($a['gravity'] == $b['gravity']) { if($a['gravity'] == $b['gravity']) {
if($a['name'] === $b['name']) if($a['dreport_name'] === $b['dreport_name'])
return strcmp($a['dreport_time'],$b['dreport_time']); return strcmp($a['dreport_time'],$b['dreport_time']);
return strcmp($a['name'],$b['name']); return strcmp($a['dreport_name'],$b['dreport_name']);
} }
return (($a['gravity'] > $b['gravity']) ? 1 : (-1)); return (($a['gravity'] > $b['gravity']) ? 1 : (-1));
} }

22
Zotlabs/Module/Embed.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace Zotlabs\Module;
require_once('include/security.php');
require_once('include/bbcode.php');
class Embed extends \Zotlabs\Web\Controller {
function init() {
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
if(! $post_id)
killme();
echo '[share=' . $post_id . '][/share]';
killme();
}
}

View File

@ -3,8 +3,10 @@
namespace Zotlabs\Module; namespace Zotlabs\Module;
/** /**
* @brief * @brief Embedphoto endpoint.
* *
* Provide an AJAX endpoint to fill the embedPhotoModal with folders and photos
* selection.
*/ */
class Embedphotos extends \Zotlabs\Web\Controller { class Embedphotos extends \Zotlabs\Web\Controller {
@ -13,42 +15,42 @@ class Embedphotos extends \Zotlabs\Web\Controller {
} }
/** /**
* @brief This is the POST destination for the embedphotos button.
* *
* This is the POST destination for the embedphotos button * @return string A JSON string.
*
*/ */
function post() { public function post() {
if (argc() > 1 && argv(1) === 'album') { if (argc() > 1 && argv(1) === 'album') {
// API: /embedphotos/album // API: /embedphotos/album
$name = (x($_POST,'name') ? $_POST['name'] : null ); $name = (x($_POST, 'name') ? $_POST['name'] : null );
if(!$name) { if (!$name) {
json_return_and_die(array('errormsg' => 'Error retrieving album', 'status' => false)); json_return_and_die(array('errormsg' => 'Error retrieving album', 'status' => false));
} }
$album = $this->embedphotos_widget_album(array('channel' => \App::get_channel(), 'album' => $name)); $album = $this->embedphotos_widget_album(array('channel' => \App::get_channel(), 'album' => $name));
json_return_and_die(array('status' => true, 'content' => $album)); json_return_and_die(array('status' => true, 'content' => $album));
} }
if(argc() > 1 && argv(1) === 'albumlist') { if (argc() > 1 && argv(1) === 'albumlist') {
// API: /embedphotos/albumlist // API: /embedphotos/albumlist
$album_list = $this->embedphotos_album_list($a); $album_list = $this->embedphotos_album_list();
json_return_and_die(array('status' => true, 'albumlist' => $album_list)); json_return_and_die(array('status' => true, 'albumlist' => $album_list));
} }
if(argc() > 1 && argv(1) === 'photolink') { if (argc() > 1 && argv(1) === 'photolink') {
// API: /embedphotos/photolink // API: /embedphotos/photolink
$href = (x($_POST,'href') ? $_POST['href'] : null ); $href = (x($_POST, 'href') ? $_POST['href'] : null );
if(!$href) { if (!$href) {
json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false));
} }
$resource_id = array_pop(explode("/", $href)); $resource_id = array_pop(explode('/', $href));
$r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1", $r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1",
dbesc($resource_id) dbesc($resource_id)
); );
if(!$r) { if (!$r) {
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
} }
$obj = json_decode($r[0]['obj'], true); $obj = json_decode($r[0]['obj'], true);
if(x($obj,'body')) { if (x($obj, 'body')) {
$photolink = $obj['body']; $photolink = $obj['body'];
} elseif (x($obj,'bbcode')) { } elseif (x($obj, 'bbcode')) {
$photolink = $obj['bbcode']; $photolink = $obj['bbcode'];
} else { } else {
json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false));
@ -58,48 +60,51 @@ class Embedphotos extends \Zotlabs\Web\Controller {
} }
/** /**
* Copied from include/widgets.php::widget_album() with a modification to get the profile_uid from * @brief Get photos from an album.
* the input array as in widget_item()
* *
* @param array $args * @see \\Zotlabs\\Widget\\Album::widget()
* @return string with HTML *
* @param array $args associative array with
* * \e array \b channel
* * \e string \b album
* @return string with HTML code from 'photo_album.tpl'
*/ */
function embedphotos_widget_album($args) { protected function embedphotos_widget_album($args) {
$channel_id = 0; $channel_id = 0;
if(array_key_exists('channel', $args))
if (array_key_exists('channel', $args)) {
$channel = $args['channel']; $channel = $args['channel'];
$channel_id = intval($channel['channel_id']); $channel_id = intval($channel['channel_id']);
if(! $channel_id) }
if (! $channel_id)
$channel_id = \App::$profile_uid; $channel_id = \App::$profile_uid;
if(! $channel_id) if (! $channel_id)
return ''; return '';
$owner_uid = $channel_id;
require_once('include/security.php'); require_once('include/security.php');
$sql_extra = permissions_sql($channel_id); $sql_extra = permissions_sql($channel_id);
if(! perm_is_allowed($channel_id,get_observer_hash(),'view_storage')) if (! perm_is_allowed($channel_id, get_observer_hash(), 'view_storage'))
return ''; return '';
if($args['album']) if (isset($args['album']))
$album = (($args['album'] === '/') ? '' : $args['album']); $album = (($args['album'] === '/') ? '' : $args['album']);
if($args['title']) if (isset($args['title']))
$title = $args['title']; $title = $args['title'];
/** /**
* This may return incorrect permissions if you have multiple directories of the same name. * @note This may return incorrect permissions if you have multiple directories of the same name.
* It is a limitation of the photo table using a name for a photo album instead of a folder hash * It is a limitation of the photo table using a name for a photo album instead of a folder hash
*/ */
if($album) { if ($album) {
require_once('include/attach.php'); require_once('include/attach.php');
$x = q("select hash from attach where filename = '%s' and uid = %d limit 1", $x = q("select hash from attach where filename = '%s' and uid = %d limit 1",
dbesc($album), dbesc($album),
intval($owner_uid) intval($channel_id)
); );
if($x) { if ($x) {
$y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); $y = attach_can_view_folder($channel_id, get_observer_hash(), $x[0]['hash']);
if(! $y) if (! $y)
return ''; return '';
} }
} }
@ -110,30 +115,33 @@ class Embedphotos extends \Zotlabs\Web\Controller {
(SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph
ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale)
ORDER BY created $order", ORDER BY created $order",
intval($owner_uid), intval($channel_id),
dbesc($album), dbesc($album),
intval(PHOTO_NORMAL), intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE) intval(PHOTO_PROFILE)
); );
$photos = array(); $photos = [];
if(count($r)) { if (count($r)) {
$twist = 'rotright'; $twist = 'rotright';
foreach($r as $rr) { foreach ($r as $rr) {
if($twist == 'rotright') if ($twist == 'rotright')
$twist = 'rotleft'; $twist = 'rotleft';
else else
$twist = 'rotright'; $twist = 'rotright';
$ph = photo_factory('');
$phototypes = $ph->supportedTypes();
$ext = $phototypes[$rr['mimetype']]; $ext = $phototypes[$rr['mimetype']];
$imgalt_e = $rr['filename']; $imgalt_e = $rr['filename'];
$desc_e = $rr['description']; $desc_e = $rr['description'];
$imagelink = (z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] $imagelink = (z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $rr['resource_id']
. (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''));
$photos[] = array( $photos[] = [
'id' => $rr['id'], 'id' => $rr['id'],
'twist' => ' ' . $twist . rand(2,4), 'twist' => ' ' . $twist . rand(2,4),
'link' => $imagelink, 'link' => $imagelink,
@ -143,35 +151,43 @@ class Embedphotos extends \Zotlabs\Web\Controller {
'desc'=> $desc_e, 'desc'=> $desc_e,
'ext' => $ext, 'ext' => $ext,
'hash'=> $rr['resource_id'], 'hash'=> $rr['resource_id'],
'unknown' => t('Unknown') 'unknown' => t('Unknown'),
); ];
} }
} }
$tpl = get_markup_template('photo_album.tpl'); $tpl = get_markup_template('photo_album.tpl');
$o .= replace_macros($tpl, array( $o = replace_macros($tpl, [
'$photos' => $photos, '$photos' => $photos,
'$album' => (($title) ? $title : $album), '$album' => (($title) ? $title : $album),
'$album_id' => rand(), '$album_id' => rand(),
'$album_edit' => array(t('Edit Album'), $album_edit), '$album_edit' => array(t('Edit Album'), false),
'$can_post' => false, '$can_post' => false,
'$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)), '$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)),
'$order' => false, '$order' => false,
'$upload_form' => $upload_form, '$upload_form' => '',
'$no_fullscreen_btn' => true '$no_fullscreen_btn' => true,
)); ]);
return $o; return $o;
} }
function embedphotos_album_list($a) { /**
* @brief Get albums observer is allowed to see.
*
* @see photos_albums_list()
*
* @return NULL|array
*/
protected function embedphotos_album_list() {
require_once('include/photos.php'); require_once('include/photos.php');
$p = photos_albums_list(\App::get_channel(), \App::get_observer()); $p = photos_albums_list(\App::get_channel(), \App::get_observer());
if($p['success']) {
if ($p['success']) {
return $p['albums']; return $p['albums'];
} else {
return null;
} }
return null;
} }
} }

View File

@ -97,8 +97,8 @@ class Events extends \Zotlabs\Web\Controller {
$type = escape_tags(trim($_POST['type'])); $type = escape_tags(trim($_POST['type']));
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $desc, local_channel()); linkify_tags($desc, local_channel());
linkify_tags($a, $location, local_channel()); linkify_tags($location, local_channel());
//$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash;

View File

@ -5,6 +5,8 @@ namespace Zotlabs\Module;
* *
*/ */
class Filestorage extends \Zotlabs\Web\Controller { class Filestorage extends \Zotlabs\Web\Controller {
function post() { function post() {
@ -71,14 +73,6 @@ class Filestorage extends \Zotlabs\Web\Controller {
return; return;
} }
// Since we have ACL'd files in the wild, but don't have ACL here yet, we
// need to return for anyone other than the owner, despite the perms check for now.
$is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
if(! ($is_owner || is_site_admin())){
info( t('Permission Denied.') . EOL );
return;
}
if(argc() > 3 && argv(3) === 'delete') { if(argc() > 3 && argv(3) === 'delete') {
@ -101,18 +95,31 @@ class Filestorage extends \Zotlabs\Web\Controller {
} }
$file = intval(argv(2)); $file = intval(argv(2));
$r = q("SELECT hash FROM attach WHERE id = %d AND uid = %d LIMIT 1", $r = q("SELECT hash, creator FROM attach WHERE id = %d AND uid = %d LIMIT 1",
dbesc($file), dbesc($file),
intval($owner) intval($owner)
); );
if(! $r) { if(! $r) {
notice( t('File not found.') . EOL);
if($json_return) if($json_return)
json_return_and_die([ 'success' => false ]); json_return_and_die([ 'success' => false ]);
notice( t('File not found.') . EOL);
goaway(z_root() . '/cloud/' . $which); goaway(z_root() . '/cloud/' . $which);
} }
if(local_channel() !== $owner) {
if($r[0]['creator'] && $r[0]['creator'] !== $ob_hash) {
notice( t('Permission denied.') . EOL);
if($json_return)
json_return_and_die([ 'success' => false ]);
goaway(z_root() . '/cloud/' . $which);
}
}
$f = $r[0]; $f = $r[0];
$channel = channelx_by_n($owner); $channel = channelx_by_n($owner);
@ -134,6 +141,19 @@ class Filestorage extends \Zotlabs\Web\Controller {
goaway(dirname($url)); goaway(dirname($url));
} }
// Since we have ACL'd files in the wild, but don't have ACL here yet, we
// need to return for anyone other than the owner, despite the perms check for now.
$is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
if(! ($is_owner || is_site_admin())){
info( t('Permission Denied.') . EOL );
return;
}
if(argc() > 3 && argv(3) === 'edit') { if(argc() > 3 && argv(3) === 'edit') {
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
if(! $perms['write_storage']) { if(! $perms['write_storage']) {

View File

@ -66,6 +66,9 @@ class Group extends Controller {
$groupname = notags(trim($_POST['groupname'])); $groupname = notags(trim($_POST['groupname']));
$public = intval($_POST['public']); $public = intval($_POST['public']);
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras_post',$hookinfo);
if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) {
$r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", $r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d",
dbesc($groupname), dbesc($groupname),
@ -75,6 +78,8 @@ class Group extends Controller {
); );
if($r) if($r)
info( t('Privacy group updated.') . EOL ); info( t('Privacy group updated.') . EOL );
build_sync_packet(local_channel(),null,true); build_sync_packet(local_channel(),null,true);
} }
@ -127,6 +132,10 @@ class Group extends Controller {
$i++; $i++;
} }
$hookinfo = [ 'pgrp_extras' => '', 'group'=>argv(1) ];
call_hooks ('privacygroup_extras',$hookinfo);
$pgrp_extras = $hookinfo['pgrp_extras'];
$tpl = get_markup_template('privacy_groups.tpl'); $tpl = get_markup_template('privacy_groups.tpl');
$o = replace_macros($tpl, [ $o = replace_macros($tpl, [
'$title' => t('Privacy Groups'), '$title' => t('Privacy Groups'),
@ -136,6 +145,7 @@ class Group extends Controller {
// new group form // new group form
'$gname' => array('groupname',t('Privacy group name')), '$gname' => array('groupname',t('Privacy group name')),
'$public' => array('public',t('Members are visible to other channels'), false), '$public' => array('public',t('Members are visible to other channels'), false),
'$pgrp_extras' => $pgrp_extras,
'$form_security_token' => get_form_security_token("group_edit"), '$form_security_token' => get_form_security_token("group_edit"),
'$submit' => t('Submit'), '$submit' => t('Submit'),
@ -166,8 +176,11 @@ class Group extends Controller {
); );
if($r) if($r)
$result = group_rmv(local_channel(),$r[0]['gname']); $result = group_rmv(local_channel(),$r[0]['gname']);
if($result) if($result) {
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(2) ];
call_hooks ('privacygroup_extras_drop',$hookinfo);
info( t('Privacy group removed.') . EOL); info( t('Privacy group removed.') . EOL);
}
else else
notice( t('Unable to remove privacy group.') . EOL); notice( t('Unable to remove privacy group.') . EOL);
} }
@ -230,6 +243,10 @@ class Group extends Controller {
} }
} }
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras',$hookinfo);
$pgrp_extras = $hookinfo['pgrp_extras'];
$context = $context + array( $context = $context + array(
'$title' => sprintf(t('Privacy Group: %s'), $group['gname']), '$title' => sprintf(t('Privacy Group: %s'), $group['gname']),
'$details_label' => t('Edit'), '$details_label' => t('Edit'),
@ -240,6 +257,7 @@ class Group extends Controller {
'$form_security_token_edit' => get_form_security_token('group_edit'), '$form_security_token_edit' => get_form_security_token('group_edit'),
'$delete' => t('Delete Group'), '$delete' => t('Delete Group'),
'$form_security_token_drop' => get_form_security_token("group_drop"), '$form_security_token_drop' => get_form_security_token("group_drop"),
'$pgrp_extras' => $pgrp_extras,
); );
} }
@ -283,6 +301,7 @@ class Group extends Controller {
$context['$groupeditor'] = $groupeditor; $context['$groupeditor'] = $groupeditor;
$context['$desc'] = t('Click a channel to toggle membership'); $context['$desc'] = t('Click a channel to toggle membership');
$context['$pgrp_extras'] = $pgrp_extras;
if($change) { if($change) {
$tpl = get_markup_template('groupeditor.tpl'); $tpl = get_markup_template('groupeditor.tpl');

119
Zotlabs/Module/Id.php Normal file
View File

@ -0,0 +1,119 @@
<?php
namespace Zotlabs\Module;
/**
*
* Controller for responding to x-zot: protocol requests
* x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77
*
*/
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Lib\IConfig;
use Zotlabs\Lib\Enotify;
use App;
require_once('include/attach.php');
require_once('include/bbcode.php');
require_once('include/security.php');
class Id extends Controller {
function init() {
if(Libzot::is_zot_request()) {
$conversation = false;
$request_portable_id = argv(1);
if(argc() > 2) {
$item_id = argv(2);
}
$portable_id = EMPTY_STR;
$sigdata = HTTPSig::verify(EMPTY_STR);
if($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
}
$chan = channelx_by_hash($request_portable_id);
if($chan) {
$channel_id = $chan['channel_id'];
if(! $item_id) {
$handler = new Channel();
App::$argc = 2;
App::$argv[0] = 'channel';
App::$argv[1] = $chan['channel_address'];
$handler->init();
}
}
else {
http_status_exit(404, 'Not found');
}
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 ";
$sql_extra = item_permissions_sql(0);
$r = q("select * from item where uuid = '%s' $item_normal $sql_extra and uid = %d limit 1",
dbesc($item_id),
intval($channel_id)
);
if(! $r) {
$r = q("select * from item where uuid = '%s' $item_normal and uid = %d limit 1",
dbesc($item_id),
intval($channel_id)
);
if($r) {
http_status_exit(403, 'Forbidden');
}
http_status_exit(404, 'Not found');
}
if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
http_status_exit(403, 'Forbidden');
xchan_query($r,true);
$items = fetch_post_tags($r,true);
$i = Activity::encode_item($items[0]);
if(! $i)
http_status_exit(404, 'Not found');
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], $i);
$headers = [];
$headers['Content-Type'] = 'application/x-zot+json' ;
$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();
}
}
}

View File

@ -133,9 +133,11 @@ class Impel extends \Zotlabs\Web\Controller {
$arr['author_xchan'] = (($j['author_xchan']) ? $j['author_xchan'] : get_observer_hash()); $arr['author_xchan'] = (($j['author_xchan']) ? $j['author_xchan'] : get_observer_hash());
$arr['mimetype'] = (($j['mimetype']) ? $j['mimetype'] : 'text/bbcode'); $arr['mimetype'] = (($j['mimetype']) ? $j['mimetype'] : 'text/bbcode');
if(! $j['mid']) if(! $j['mid']) {
$j['mid'] = item_message_id(); $j['uuid'] = item_message_id();
$j['mid'] = z_root() . '/item/' . $j['uuid'];
}
$arr['uuid'] = $j['uuid'];
$arr['mid'] = $arr['parent_mid'] = $j['mid']; $arr['mid'] = $arr['parent_mid'] = $j['mid'];

View File

@ -2,6 +2,23 @@
namespace Zotlabs\Module; namespace Zotlabs\Module;
use Zotlabs\Lib\IConfig;
use Zotlabs\Lib\Enotify;
use Zotlabs\Web\Controller;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\ThreadListener;
use App;
require_once('include/crypto.php');
require_once('include/items.php');
require_once('include/security.php');
/** /**
* *
* This is the POST destination for most all locally posted * This is the POST destination for most all locally posted
@ -17,16 +34,146 @@ namespace Zotlabs\Module;
* *
*/ */
require_once('include/crypto.php');
require_once('include/items.php'); class Item extends Controller {
require_once('include/attach.php');
require_once('include/bbcode.php');
require_once('include/security.php');
use \Zotlabs\Lib as Zlib; function init() {
if(Libzot::is_zot_request()) {
$conversation = false;
$item_id = argv(1);
if(! $item_id)
http_status_exit(404, 'Not found');
$portable_id = EMPTY_STR;
$sigdata = HTTPSig::verify(EMPTY_STR);
if($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
}
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 ";
$sql_extra = item_permissions_sql(0);
$r = q("select * from item where mid = '%s' $item_normal $sql_extra limit 1",
dbesc(z_root() . '/item/' . $item_id)
);
if(! $r) {
$r = q("select * from item where mid = '%s' $item_normal limit 1",
dbesc(z_root() . '/item/' . $item_id)
);
if($r) {
http_status_exit(403, 'Forbidden');
}
http_status_exit(404, 'Not found');
}
$items = q("select parent as item_id from item where mid = '%s' and uid = %d $item_normal $sql_extra ",
dbesc($r[0]['parent_mid']),
intval($r[0]['uid'])
);
if(! $items) {
http_status_exit(404, 'Not found');
}
$r = $items;
$parents_str = ids_to_querystr($r,'item_id');
$items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal $sql_extra ",
dbesc($parents_str)
);
if(! $items) {
http_status_exit(404, 'Not found');
}
$r = $items;
xchan_query($r,true);
$items = fetch_post_tags($r,true);
$observer = App::get_observer();
$parent = $items[0];
$recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []);
$to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null);
$nitems = [];
foreach($items as $i) {
$mids = [];
if(intval($i['item_private'])) {
if(! $observer) {
continue;
}
// ignore private reshare, possibly from hubzilla
if($i['verb'] === 'Announce') {
if(! in_array($i['thr_parent'],$mids)) {
$mids[] = $i['thr_parent'];
}
continue;
}
// also ignore any children of the private reshares
if(in_array($i['thr_parent'],$mids)) {
continue;
}
if((! $to) || (! in_array($observer['xchan_url'],$to))) {
continue;
}
}
$nitems[] = $i;
}
if(! $nitems)
http_status_exit(404, 'Not found');
$chan = channelx_by_n($nitems[0]['uid']);
if(! $chan)
http_status_exit(404, 'Not found');
if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
http_status_exit(403, 'Forbidden');
$i = Activity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection',( defined('NOMADIC') ? false : true));
if($portable_id) {
ThreadListener::store(z_root() . '/item/' . $item_id,$portable_id);
}
if(! $i)
http_status_exit(404, 'Not found');
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], $i);
$headers = [];
$headers['Content-Type'] = 'application/x-zot+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();
}
}
class Item extends \Zotlabs\Web\Controller {
function post() { function post() {
@ -392,6 +539,7 @@ class Item extends \Zotlabs\Web\Controller {
$verb = $orig_post['verb']; $verb = $orig_post['verb'];
$app = $orig_post['app']; $app = $orig_post['app'];
$title = escape_tags(trim($_REQUEST['title'])); $title = escape_tags(trim($_REQUEST['title']));
$summary = trim($_REQUEST['summary']);
$body = trim($_REQUEST['body']); $body = trim($_REQUEST['body']);
$item_flags = $orig_post['item_flags']; $item_flags = $orig_post['item_flags'];
@ -454,6 +602,7 @@ class Item extends \Zotlabs\Web\Controller {
$coord = notags(trim($_REQUEST['coord'])); $coord = notags(trim($_REQUEST['coord']));
$verb = notags(trim($_REQUEST['verb'])); $verb = notags(trim($_REQUEST['verb']));
$title = escape_tags(trim($_REQUEST['title'])); $title = escape_tags(trim($_REQUEST['title']));
$summary = trim($_REQUEST['summary']);
$body = trim($_REQUEST['body']); $body = trim($_REQUEST['body']);
$body .= trim($_REQUEST['attachment']); $body .= trim($_REQUEST['attachment']);
$postopts = ''; $postopts = '';
@ -505,12 +654,14 @@ class Item extends \Zotlabs\Web\Controller {
&& ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false); && ($channel['channel_pageflags'] & PAGE_ALLOWCODE)) ? true : false);
if($preview) { if($preview) {
$summary = z_input_filter($summary,$mimetype,$execflag);
$body = z_input_filter($body,$mimetype,$execflag); $body = z_input_filter($body,$mimetype,$execflag);
} }
$arr = [ 'profile_uid' => $profile_uid, 'content' => $body, 'mimetype' => $mimetype ]; $arr = [ 'profile_uid' => $profile_uid, 'summary' => $summary, 'content' => $body, 'mimetype' => $mimetype ];
call_hooks('post_content',$arr); call_hooks('post_content',$arr);
$summary = $arr['summary'];
$body = $arr['content']; $body = $arr['content'];
$mimetype = $arr['mimetype']; $mimetype = $arr['mimetype'];
@ -531,10 +682,24 @@ class Item extends \Zotlabs\Web\Controller {
// we may need virtual or template classes to implement the possible alternatives // we may need virtual or template classes to implement the possible alternatives
if(strpos($body,'[/summary]') !== false) {
$match = '';
$cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match);
if($cnt) {
$summary .= $match[1];
}
$body_content = preg_replace("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/ism", '',$body);
$body = trim($body_content);
}
$summary = cleanup_bbcode($summary);
$body = cleanup_bbcode($body); $body = cleanup_bbcode($body);
// Look for tags and linkify them // Look for tags and linkify them
$results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid);
$results = linkify_tags($summary, ($uid) ? $uid : $profile_uid);
$results = linkify_tags($body, ($uid) ? $uid : $profile_uid);
if($results) { if($results) {
@ -579,6 +744,9 @@ class Item extends \Zotlabs\Web\Controller {
if(! $preview) { if(! $preview) {
fix_attached_photo_permissions($profile_uid,$owner_xchan['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny); fix_attached_photo_permissions($profile_uid,$owner_xchan['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny);
fix_attached_photo_permissions($profile_uid,$owner_xchan['xchan_hash'],((strpos($summary,'[/crypt]')) ? $_POST['media_str'] : $summary),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny);
fix_attached_file_permissions($channel,$observer['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny); fix_attached_file_permissions($channel,$observer['xchan_hash'],((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny);
} }
@ -616,9 +784,9 @@ class Item extends \Zotlabs\Web\Controller {
if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) { if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) {
// process share by id // process share by id
$verb = ACTIVITY_SHARE;
$i = 0; $i = 0;
foreach($match[2] as $mtch) { foreach($match[2] as $mtch) {
$reshare = new \Zotlabs\Lib\Share($mtch); $reshare = new \Zotlabs\Lib\Share($mtch);
@ -711,7 +879,8 @@ class Item extends \Zotlabs\Web\Controller {
$notify_type = (($parent) ? 'comment-new' : 'wall-new' ); $notify_type = (($parent) ? 'comment-new' : 'wall-new' );
if(! $mid) { if(! $mid) {
$mid = (($message_id) ? $message_id : item_message_id()); $uuid = (($message_id) ? $message_id : item_message_id());
$mid = z_root() . '/item/' . $uuid;
} }
@ -736,7 +905,7 @@ class Item extends \Zotlabs\Web\Controller {
// fix permalinks for cards // fix permalinks for cards
if($webpage == ITEM_TYPE_CARD) { if($webpage == ITEM_TYPE_CARD) {
$plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16)); $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid);
} }
if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_CARD)) { if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_CARD)) {
$r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1", $r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1",
@ -748,7 +917,7 @@ class Item extends \Zotlabs\Web\Controller {
} }
if($webpage == ITEM_TYPE_ARTICLE) { if($webpage == ITEM_TYPE_ARTICLE) {
$plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16)); $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid);
} }
if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) { if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) {
$r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1", $r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1",
@ -760,12 +929,13 @@ class Item extends \Zotlabs\Web\Controller {
} }
if ((! $plink) && ($item_thread_top)) { if ((! $plink) && ($item_thread_top)) {
$plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid; $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . gen_link_id($mid);
$plink = substr($plink,0,190); $plink = substr($plink,0,190);
} }
$datarray['aid'] = $channel['channel_account_id']; $datarray['aid'] = $channel['channel_account_id'];
$datarray['uid'] = $profile_uid; $datarray['uid'] = $profile_uid;
$datarray['uuid'] = $uuid;
$datarray['owner_xchan'] = (($owner_hash) ? $owner_hash : $owner_xchan['xchan_hash']); $datarray['owner_xchan'] = (($owner_hash) ? $owner_hash : $owner_xchan['xchan_hash']);
$datarray['author_xchan'] = $observer['xchan_hash']; $datarray['author_xchan'] = $observer['xchan_hash'];
$datarray['created'] = $created; $datarray['created'] = $created;
@ -778,6 +948,7 @@ class Item extends \Zotlabs\Web\Controller {
$datarray['parent_mid'] = $parent_mid; $datarray['parent_mid'] = $parent_mid;
$datarray['mimetype'] = $mimetype; $datarray['mimetype'] = $mimetype;
$datarray['title'] = $title; $datarray['title'] = $title;
$datarray['summary'] = $summary;
$datarray['body'] = $body; $datarray['body'] = $body;
$datarray['app'] = $app; $datarray['app'] = $app;
$datarray['location'] = $location; $datarray['location'] = $location;
@ -887,12 +1058,12 @@ class Item extends \Zotlabs\Web\Controller {
$datarray['title'] = mb_substr($datarray['title'],0,191); $datarray['title'] = mb_substr($datarray['title'],0,191);
if($webpage) { if($webpage) {
Zlib\IConfig::Set($datarray,'system', webpage_to_namespace($webpage), IConfig::Set($datarray,'system', webpage_to_namespace($webpage),
(($pagetitle) ? $pagetitle : substr($datarray['mid'],0,16)),true); (($pagetitle) ? $pagetitle : basename($datarray['mid'])), true);
} }
elseif($namespace) { elseif($namespace) {
Zlib\IConfig::Set($datarray,'system', $namespace, IConfig::Set($datarray,'system', $namespace,
(($remote_id) ? $remote_id : substr($datarray['mid'],0,16)),true); (($remote_id) ? $remote_id : basename($datarray['mid'])), true);
} }
@ -924,7 +1095,7 @@ class Item extends \Zotlabs\Web\Controller {
} }
} }
if(! $nopush) if(! $nopush)
\Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_post', $post_id)); Master::Summon([ 'Notifier', 'edit_post', $post_id ]);
if($api_source) if($api_source)
@ -959,7 +1130,7 @@ class Item extends \Zotlabs\Web\Controller {
// otherwise it will happen during delivery // otherwise it will happen during delivery
if(($datarray['owner_xchan'] != $datarray['author_xchan']) && (intval($parent_item['item_wall']))) { if(($datarray['owner_xchan'] != $datarray['author_xchan']) && (intval($parent_item['item_wall']))) {
Zlib\Enotify::submit(array( Enotify::submit(array(
'type' => NOTIFY_COMMENT, 'type' => NOTIFY_COMMENT,
'from_xchan' => $datarray['author_xchan'], 'from_xchan' => $datarray['author_xchan'],
'to_xchan' => $datarray['owner_xchan'], 'to_xchan' => $datarray['owner_xchan'],
@ -977,7 +1148,7 @@ class Item extends \Zotlabs\Web\Controller {
$parent = $post_id; $parent = $post_id;
if(($datarray['owner_xchan'] != $datarray['author_xchan']) && ($datarray['item_type'] == ITEM_TYPE_POST)) { if(($datarray['owner_xchan'] != $datarray['author_xchan']) && ($datarray['item_type'] == ITEM_TYPE_POST)) {
Zlib\Enotify::submit(array( Enotify::submit(array(
'type' => NOTIFY_WALL, 'type' => NOTIFY_WALL,
'from_xchan' => $datarray['author_xchan'], 'from_xchan' => $datarray['author_xchan'],
'to_xchan' => $datarray['owner_xchan'], 'to_xchan' => $datarray['owner_xchan'],
@ -1039,7 +1210,7 @@ class Item extends \Zotlabs\Web\Controller {
call_hooks('post_local_end', $datarray); call_hooks('post_local_end', $datarray);
if(! $nopush) if(! $nopush)
\Zotlabs\Daemon\Master::Summon(array('Notifier', $notify_type, $post_id)); Master::Summon([ 'Notifier', $notify_type, $post_id ]);
logger('post_complete'); logger('post_complete');

View File

@ -1,6 +1,8 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use Zotlabs\Lib\Activity;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
require_once('include/items.php'); require_once('include/items.php');
@ -374,10 +376,13 @@ class Like extends \Zotlabs\Web\Controller {
} }
} }
$mid = item_message_id(); $uuid = item_message_id();
$arr = array(); $arr = array();
$arr['uuid'] = $uuid;
$arr['mid'] = z_root() . '/item/' . $uuid;
if($extended_like) { if($extended_like) {
$arr['item_thread_top'] = 1; $arr['item_thread_top'] = 1;
$arr['item_origin'] = 1; $arr['item_origin'] = 1;
@ -400,6 +405,7 @@ class Like extends \Zotlabs\Web\Controller {
$object = json_encode(array( $object = json_encode(array(
'type' => $objtype, 'type' => $objtype,
'id' => $item['mid'], 'id' => $item['mid'],
'asld' => Activity::fetch_item( [ 'id' => $item['mid'] ] ),
'parent' => (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']), 'parent' => (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']),
'link' => $links, 'link' => $links,
'title' => $item['title'], 'title' => $item['title'],
@ -479,7 +485,6 @@ class Like extends \Zotlabs\Web\Controller {
} }
$arr['mid'] = $mid;
$arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid); $arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid);
$arr['uid'] = $owner_uid; $arr['uid'] = $owner_uid;

View File

@ -34,7 +34,7 @@ class Mail extends \Zotlabs\Web\Controller {
} }
else { else {
$body = cleanup_bbcode($body); $body = cleanup_bbcode($body);
$results = linkify_tags($a, $body, local_channel()); $results = linkify_tags($body, local_channel());
if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) {
$attachments = array(); $attachments = array();
@ -111,7 +111,7 @@ class Mail extends \Zotlabs\Web\Controller {
} }
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $body, local_channel()); linkify_tags($body, local_channel());
if(! $recipient) { if(! $recipient) {

View File

@ -70,7 +70,8 @@ class Mood extends Controller {
$poster = App::get_observer(); $poster = App::get_observer();
$mid = item_message_id(); $uuid = item_message_id();
$mid = z_root() . '/item/' . $uuid;
$action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]); $action = sprintf( t('%1$s is %2$s','mood'), '[zrl=' . $poster['xchan_url'] . ']' . $poster['xchan_name'] . '[/zrl]' , $verbs[$verb]);
@ -78,6 +79,7 @@ class Mood extends Controller {
$arr['aid'] = get_account_id(); $arr['aid'] = get_account_id();
$arr['uid'] = $uid; $arr['uid'] = $uid;
$arr['uuid'] = $uuid;
$arr['mid'] = $mid; $arr['mid'] = $mid;
$arr['parent_mid'] = (($parent_mid) ? $parent_mid : $mid); $arr['parent_mid'] = (($parent_mid) ? $parent_mid : $mid);
$arr['author_xchan'] = $poster['xchan_hash']; $arr['author_xchan'] = $poster['xchan_hash'];

View File

@ -1,6 +1,8 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use Zotlabs\Lib\Group;
use Zotlabs\Lib\Apps;
use App; use App;
require_once('include/items.php'); require_once('include/items.php');
@ -114,8 +116,8 @@ class Network extends \Zotlabs\Web\Controller {
$def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
} }
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1));
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1));
$cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0); $cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0);
$star = ((x($_GET,'star')) ? intval($_GET['star']) : 0); $star = ((x($_GET,'star')) ? intval($_GET['star']) : 0);
@ -132,6 +134,13 @@ class Network extends \Zotlabs\Web\Controller {
$deftag = ''; $deftag = '';
if (Apps::system_app_installed(local_channel(),'Affinity Tool')) {
$affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
if ($affinity_locked) {
set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
}
}
if(x($_GET,'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen) if(x($_GET,'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen)
$nouveau = true; $nouveau = true;

View File

@ -1,15 +0,0 @@
<?php
namespace Zotlabs\Module;
class Nojs extends \Zotlabs\Web\Controller {
function init() {
$n = ((argc() > 1) ? intval(argv(1)) : 1);
setcookie('jsdisabled', $n, 0, '/');
$p = hex2bin($_GET['redir']);
$hasq = strpbrk($p,'?&');
goaway(z_root() . (($p) ? '/' . $p : '') . (($hasq) ? '' : '?f=' ) . '&jsdisabled=' . $n);
}
}

View File

@ -1,10 +1,13 @@
<?php <?php
namespace Zotlabs\Module; /** @file */ namespace Zotlabs\Module;
use App; use App;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Lib\Apps; use Zotlabs\Lib\Apps;
/**
* @brief Notes Module controller.
*/
class Notes extends Controller { class Notes extends Controller {
function post() { function post() {
@ -40,11 +43,9 @@ class Notes extends Controller {
logger('notes saved.', LOGGER_DEBUG); logger('notes saved.', LOGGER_DEBUG);
json_return_and_die($ret); json_return_and_die($ret);
} }
function get() { function get() {
if(! local_channel()) if(! local_channel())
return EMPTY_STR; return EMPTY_STR;
@ -61,7 +62,6 @@ class Notes extends Controller {
$arr = ['app' => true]; $arr = ['app' => true];
return $w->widget($arr); return $w->widget($arr);
} }
} }

View File

@ -456,7 +456,7 @@ class Oep extends \Zotlabs\Web\Controller {
if(preg_match('|//(.*?)/(.*?)/(.*?)/album/|',$url,$matches)) { if(preg_match('|//(.*?)/(.*?)/(.*?)/album/|',$url,$matches)) {
$chn = $matches[3]; $chn = $matches[3];
$res = hex2bin(basename($url)); $res = basename($url);
} }
if(! ($chn && $res)) if(! ($chn && $res))

View File

@ -24,7 +24,7 @@ class Pconfig extends \Zotlabs\Web\Controller {
$aj = intval($_POST['aj']); $aj = intval($_POST['aj']);
// Do not store "serialized" data received in the $_POST // Do not store "serialized" data received in the $_POST
if (preg_match('|^a:[0-9]+:{.*}$|s',$v) || preg_match('O:8:"stdClass":[0-9]+:{.*}$|s',$v)) { if (preg_match('|^a:[0-9]+:{.*}$|s',$v) || preg_match('|O:8:"stdClass":[0-9]+:{.*}$|s',$v)) {
return; return;
} }
@ -48,7 +48,7 @@ class Pconfig extends \Zotlabs\Web\Controller {
} }
function get() { function get() {
if(! local_channel()) { if(! local_channel()) {
return login(); return login();

View File

@ -1,19 +1,20 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/attach.php'); require_once('include/attach.php');
require_once('include/photo/photo_driver.php'); require_once('include/photo/photo_driver.php');
class Photo extends \Zotlabs\Web\Controller { class Photo extends \Zotlabs\Web\Controller {
function init() { function init() {
$prvcachecontrol = false;
$streaming = null; $streaming = null;
$channel = null; $channel = null;
$person = 0; $person = 0;
$renew = false;
switch(argc()) { switch(argc()) {
case 4: case 4:
@ -30,6 +31,14 @@ class Photo extends \Zotlabs\Web\Controller {
// NOTREACHED // NOTREACHED
} }
$cache_mode = array(
'on' => false,
'age' => 86400,
'exp' => true,
'leak' => false
);
call_hooks('cache_mode_hook', $cache_mode);
$observer_xchan = get_observer_hash(); $observer_xchan = get_observer_hash();
$ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE']; $ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
@ -106,13 +115,14 @@ class Photo extends \Zotlabs\Web\Controller {
License link: http://creativecommons.org/licenses/by/3.0/ License link: http://creativecommons.org/licenses/by/3.0/
*/ */
// @FIXME It seems this part doesn't work because we are not setting such cookie
$cookie_value = false; $cookie_value = false;
if (isset($_COOKIE['devicePixelRatio'])) { if (isset($_COOKIE['devicePixelRatio'])) {
$cookie_value = intval($_COOKIE['devicePixelRatio']); $cookie_value = intval($_COOKIE['devicePixelRatio']);
} }
else { else {
// Force revalidation of cache on next request // Force revalidation of cache on next request
$cache_directive = 'no-cache'; // $prvcachecontrol = 'no-cache';
$status = 'no cookie'; $status = 'no cookie';
} }
@ -129,27 +139,42 @@ class Photo extends \Zotlabs\Web\Controller {
$resolution = 1; $resolution = 1;
} }
$r = q("SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", $r = q("SELECT uid, photo_usage, display_path FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo), dbesc($photo),
intval($resolution) intval($resolution)
); );
if($r) { if($r) {
$allowed = (-1); $allowed = (-1);
if(intval($r[0]['photo_usage'])) { $u = intval($r[0]['photo_usage']);
if($u) {
$allowed = 1; $allowed = 1;
if(intval($r[0]['photo_usage']) === PHOTO_COVER) if($u === PHOTO_COVER)
if($resolution < PHOTO_RES_COVER_1200) if($resolution < PHOTO_RES_COVER_1200)
$allowed = (-1); $allowed = (-1);
if(intval($r[0]['photo_usage']) === PHOTO_PROFILE) if($u === PHOTO_PROFILE)
if(! in_array($resolution,[4,5,6])) if(! in_array($resolution,[4,5,6]))
$allowed = (-1); $allowed = (-1);
if($u === PHOTO_CACHE) {
// Validate cache
$cache = array(
'resid' => $photo,
'status' => false
);
if($cache_mode['on'])
call_hooks('cache_url_hook', $cache);
if(! $cache['status']) {
$url = htmlspecialchars_decode($r[0]['display_path']);
if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false)
$url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url);
header("Location: " . $url);
killme();
}
}
} }
if($allowed === (-1)) { if($allowed === (-1))
$allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo); $allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo);
}
$channel = channelx_by_n($r[0]['uid']); $channel = channelx_by_n($r[0]['uid']);
@ -162,14 +187,17 @@ class Photo extends \Zotlabs\Web\Controller {
$exists = (($e) ? true : false); $exists = (($e) ? true : false);
if($exists && $allowed) { if($exists && $allowed) {
$expires = strtotime($e[0]['expires'] . 'Z');
$data = dbunescbin($e[0]['content']); $data = dbunescbin($e[0]['content']);
$filesize = $e[0]['filesize']; $filesize = $e[0]['filesize'];
$mimetype = $e[0]['mimetype']; $mimetype = $e[0]['mimetype'];
$modified = strtotime($e[0]['edited'] . 'Z'); $modified = strtotime($e[0]['edited'] . 'Z');
if(intval($e[0]['os_storage']))
if(intval($e[0]['os_storage'])) {
$streaming = $data; $streaming = $data;
}
if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '') if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
$prvcachecontrol = true; $prvcachecontrol = 'no-store, no-cache, must-revalidate';
} }
else { else {
if(! $allowed) { if(! $allowed) {
@ -180,9 +208,9 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
} else {
http_status_exit(404,'not found');
} }
else
http_status_exit(404,'not found');
} }
header_remove('Pragma'); header_remove('Pragma');
@ -226,23 +254,13 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
// @FIXME Seems never invoked if(isset($prvcachecontrol)) {
// Writing in cachefile
if (isset($cachefile) && $cachefile != '') {
file_put_contents($cachefile, $data);
$modified = filemtime($cachefile);
}
header("Content-type: " . $mimetype);
if($prvcachecontrol) {
// it is a private photo that they have no permission to view. // it is a private photo that they have no permission to view.
// tell the browser not to cache it, in case they authenticate // tell the browser not to cache it, in case they authenticate
// and subsequently have permission to see it // and subsequently have permission to see it
header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: " . $prvcachecontrol);
} }
else { else {
@ -255,18 +273,23 @@ class Photo extends \Zotlabs\Web\Controller {
// This has performance considerations but we highly recommend you // This has performance considerations but we highly recommend you
// leave it alone. // leave it alone.
$cache = get_config('system','photo_cache_time', 86400); // 1 day by default $maxage = $cache_mode['age'];
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT"); if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time()))
header("Cache-Control: max-age=" . $cache); $expires = time() + $maxage;
else
$maxage = $expires - time();
header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
header("Cache-Control: max-age=" . $maxage);
} }
header("Content-type: " . $mimetype);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT");
header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data))); header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));
// If it's a file resource, stream it. // If it's a file resource, stream it.
if($streaming && $channel) { if($streaming && $channel) {
if(strpos($streaming,'store') !== false) if(strpos($streaming,'store') !== false)
$istream = fopen($streaming,'rb'); $istream = fopen($streaming,'rb');

View File

@ -422,7 +422,7 @@ class Photos extends \Zotlabs\Web\Controller {
require_once('include/text.php'); require_once('include/text.php');
$profile_uid = \App::$profile['profile_uid']; $profile_uid = \App::$profile['profile_uid'];
$results = linkify_tags($a, $rawtags, (local_channel()) ? local_channel() : $profile_uid); $results = linkify_tags($rawtags, (local_channel()) ? local_channel() : $profile_uid);
$success = $results['success']; $success = $results['success'];
$post_tags = array(); $post_tags = array();

View File

@ -354,20 +354,20 @@ class Profiles extends \Zotlabs\Web\Controller {
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $likes, local_channel()); linkify_tags($likes, local_channel());
linkify_tags($a, $dislikes, local_channel()); linkify_tags($dislikes, local_channel());
linkify_tags($a, $about, local_channel()); linkify_tags($about, local_channel());
linkify_tags($a, $interest, local_channel()); linkify_tags($interest, local_channel());
linkify_tags($a, $interest, local_channel()); linkify_tags($interest, local_channel());
linkify_tags($a, $contact, local_channel()); linkify_tags($contact, local_channel());
linkify_tags($a, $channels, local_channel()); linkify_tags($channels, local_channel());
linkify_tags($a, $music, local_channel()); linkify_tags($music, local_channel());
linkify_tags($a, $book, local_channel()); linkify_tags($book, local_channel());
linkify_tags($a, $tv, local_channel()); linkify_tags($tv, local_channel());
linkify_tags($a, $film, local_channel()); linkify_tags($film, local_channel());
linkify_tags($a, $romance, local_channel()); linkify_tags($romance, local_channel());
linkify_tags($a, $work, local_channel()); linkify_tags($work, local_channel());
linkify_tags($a, $education, local_channel()); linkify_tags($education, local_channel());
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : ''); $with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');

View File

@ -44,6 +44,7 @@ class React extends \Zotlabs\Web\Controller {
return; return;
} }
$uuid = item_message_id();
$n = array(); $n = array();
$n['aid'] = $channel['channel_account_id']; $n['aid'] = $channel['channel_account_id'];
@ -52,7 +53,8 @@ class React extends \Zotlabs\Web\Controller {
$n['item_type'] = $i[0]['item_type']; $n['item_type'] = $i[0]['item_type'];
$n['parent'] = $postid; $n['parent'] = $postid;
$n['parent_mid'] = $i[0]['mid']; $n['parent_mid'] = $i[0]['mid'];
$n['mid'] = item_message_id(); $n['uuid'] = $uuid;
$n['mid'] = z_root() . '/item/' . $uuid;
$n['verb'] = ACTIVITY_REACT . '#' . $emoji; $n['verb'] = ACTIVITY_REACT . '#' . $emoji;
$n['body'] = "\n\n[zmg=32x32]" . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]' . "\n\n"; $n['body'] = "\n\n[zmg=32x32]" . z_root() . '/images/emoji/' . $emoji . '.png[/zmg]' . "\n\n";
$n['author_xchan'] = $channel['channel_hash']; $n['author_xchan'] = $channel['channel_hash'];

View File

@ -14,6 +14,15 @@ class Rmagic extends \Zotlabs\Web\Controller {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1", $r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($me) dbesc($me)
); );
if(! $r) {
$w = discover_by_webbie($me);
if($w) {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($me)
);
}
}
if($r) { if($r) {
if($r[0]['hubloc_url'] === z_root()) if($r[0]['hubloc_url'] === z_root())
goaway(z_root() . '/login'); goaway(z_root() . '/login');
@ -49,7 +58,16 @@ class Rmagic extends \Zotlabs\Web\Controller {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1", $r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($address) dbesc($address)
); );
if(! $r) {
$w = discover_by_webbie($address);
if($w) {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($address)
);
}
}
} }
if($r) { if($r) {
$url = $r[0]['hubloc_url']; $url = $r[0]['hubloc_url'];
} }

View File

@ -10,20 +10,6 @@ class Featured {
call_hooks('feature_settings_post', $_POST); call_hooks('feature_settings_post', $_POST);
if($_POST['affinity_slider-submit']) {
$cmax = intval($_POST['affinity_cmax']);
if($cmax < 0 || $cmax > 99)
$cmax = 99;
$cmin = intval($_POST['affinity_cmin']);
if($cmin < 0 || $cmin > 99)
$cmin = 0;
set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
info( t('Affinity Slider settings updated.') . EOL);
}
build_sync_packet(); build_sync_packet();
return; return;
} }
@ -37,30 +23,10 @@ class Featured {
if(! $r) if(! $r)
$settings_addons = t('No feature settings configured'); $settings_addons = t('No feature settings configured');
if(feature_enabled(local_channel(),'affinity')) {
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
$cmax = (($cmax) ? $cmax : 99);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
));
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
$cmin = (($cmin) ? $cmin : 0);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
));
$settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array(
'$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')),
'$content' => $setting_fields
));
}
call_hooks('feature_settings', $settings_addons); call_hooks('feature_settings', $settings_addons);
$this->sortpanels($settings_addons); $this->sortpanels($settings_addons);
$tpl = get_markup_template("settings_addons.tpl"); $tpl = get_markup_template("settings_addons.tpl");
$o .= replace_macros($tpl, array( $o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("settings_featured"), '$form_security_token' => get_form_security_token("settings_featured"),

View File

@ -39,12 +39,12 @@ class Setup extends \Zotlabs\Web\Controller {
ini_set('display_errors', '1'); ini_set('display_errors', '1');
// $baseurl/setup/testrewrite to test if rewrite in .htaccess is working // $baseurl/setup/testrewrite to test if rewrite in .htaccess is working
if (argc() == 2 && argv(1) == "testrewrite") { if(argc() == 2 && argv(1) == 'testrewrite') {
echo 'ok'; echo 'ok';
killme(); killme();
} }
if (x($_POST, 'pass')) { if(x($_POST, 'pass')) {
$this->install_wizard_pass = intval($_POST['pass']); $this->install_wizard_pass = intval($_POST['pass']);
} else { } else {
$this->install_wizard_pass = 1; $this->install_wizard_pass = 1;
@ -63,7 +63,6 @@ class Setup extends \Zotlabs\Web\Controller {
return; return;
// implied break; // implied break;
case 3: case 3:
$urlpath = \App::get_path();
$dbhost = trim($_POST['dbhost']); $dbhost = trim($_POST['dbhost']);
$dbport = intval(trim($_POST['dbport'])); $dbport = intval(trim($_POST['dbport']));
$dbuser = trim($_POST['dbuser']); $dbuser = trim($_POST['dbuser']);
@ -89,7 +88,6 @@ class Setup extends \Zotlabs\Web\Controller {
return; return;
// implied break; // implied break;
case 4: case 4:
$urlpath = \App::get_path();
$dbhost = trim($_POST['dbhost']); $dbhost = trim($_POST['dbhost']);
$dbport = intval(trim($_POST['dbport'])); $dbport = intval(trim($_POST['dbport']));
$dbuser = trim($_POST['dbuser']); $dbuser = trim($_POST['dbuser']);
@ -162,7 +160,6 @@ class Setup extends \Zotlabs\Web\Controller {
* *
* @return string parsed HTML output * @return string parsed HTML output
*/ */
function get() { function get() {
$o = ''; $o = '';
@ -213,10 +210,10 @@ class Setup extends \Zotlabs\Web\Controller {
} }
if(x(\App::$data, 'txt') && strlen(\App::$data['txt'])) { if(x(\App::$data, 'txt') && strlen(\App::$data['txt'])) {
$db_return_text .= $this->manual_config($a); $db_return_text .= $this->manual_config();
} }
if ($db_return_text != "") { if($db_return_text != '') {
$tpl = get_markup_template('install.tpl'); $tpl = get_markup_template('install.tpl');
return replace_macros($tpl, array( return replace_macros($tpl, array(
'$title' => $install_title, '$title' => $install_title,
@ -242,7 +239,7 @@ class Setup extends \Zotlabs\Web\Controller {
$this->check_keys($checks); $this->check_keys($checks);
if (x($_POST, 'phpath')) if(x($_POST, 'phpath'))
$phpath = notags(trim($_POST['phpath'])); $phpath = notags(trim($_POST['phpath']));
$this->check_php($phpath, $checks); $this->check_php($phpath, $checks);
@ -278,7 +275,6 @@ class Setup extends \Zotlabs\Web\Controller {
$dbtype = intval(trim($_POST['dbtype'])); $dbtype = intval(trim($_POST['dbtype']));
$phpath = trim($_POST['phpath']); $phpath = trim($_POST['phpath']);
$adminmail = trim($_POST['adminmail']); $adminmail = trim($_POST['adminmail']);
$siteurl = trim($_POST['siteurl']);
$tpl = get_markup_template('install_db.tpl'); $tpl = get_markup_template('install_db.tpl');
$o .= replace_macros($tpl, array( $o .= replace_macros($tpl, array(
@ -320,7 +316,6 @@ class Setup extends \Zotlabs\Web\Controller {
$phpath = trim($_POST['phpath']); $phpath = trim($_POST['phpath']);
$adminmail = trim($_POST['adminmail']); $adminmail = trim($_POST['adminmail']);
$siteurl = trim($_POST['siteurl']);
$timezone = ((x($_POST,'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles'); $timezone = ((x($_POST,'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles');
@ -363,12 +358,12 @@ class Setup extends \Zotlabs\Web\Controller {
* @param string $help optional help string * @param string $help optional help string
*/ */
function check_add(&$checks, $title, $status, $required, $help = '') { function check_add(&$checks, $title, $status, $required, $help = '') {
$checks[] = array( $checks[] = [
'title' => $title, 'title' => $title,
'status' => $status, 'status' => $status,
'required' => $required, 'required' => $required,
'help' => $help 'help' => $help
); ];
} }
/** /**
@ -380,12 +375,12 @@ class Setup extends \Zotlabs\Web\Controller {
function check_php(&$phpath, &$checks) { function check_php(&$phpath, &$checks) {
$help = ''; $help = '';
if(version_compare(PHP_VERSION, '5.5') < 0) { if(version_compare(PHP_VERSION, '7.1') < 0) {
$help .= t('PHP version 5.5 or greater is required.'); $help .= t('PHP version 7.1 or greater is required.');
$this->check_add($checks, t('PHP version'), false, false, $help); $this->check_add($checks, t('PHP version'), false, false, $help);
} }
if (strlen($phpath)) { if(strlen($phpath)) {
$passed = file_exists($phpath); $passed = file_exists($phpath);
} }
elseif(function_exists('shell_exec')) { elseif(function_exists('shell_exec')) {
@ -419,6 +414,7 @@ class Setup extends \Zotlabs\Web\Controller {
$result = trim(shell_exec($cmd)); $result = trim(shell_exec($cmd));
else else
$help .= t('Unable to check command line PHP, as shell_exec() is disabled. This is required.') . EOL; $help .= t('Unable to check command line PHP, as shell_exec() is disabled. This is required.') . EOL;
$passed2 = (($result == $str) ? true : false); $passed2 = (($result == $str) ? true : false);
if(!$passed2) { if(!$passed2) {
$help .= t('The command line version of PHP on your system does not have "register_argc_argv" enabled.'). EOL; $help .= t('The command line version of PHP on your system does not have "register_argc_argv" enabled.'). EOL;
@ -441,13 +437,18 @@ class Setup extends \Zotlabs\Web\Controller {
require_once 'include/environment.php'; require_once 'include/environment.php';
$help = ''; $help = '';
$mem_warning = '';
$result = getPhpiniUploadLimits(); $result = getPhpiniUploadLimits();
if($result['post_max_size'] < 4194304 || $result['max_upload_filesize'] < 4194304) {
$mem_warning = '<strong>' .t('This is not sufficient to upload larger images or files. You should be able to upload at least 4 MB at once.') . '</strong>';
}
$help = sprintf(t('Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'), $help = sprintf(t('Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'),
userReadableSize($result['post_max_size']), userReadableSize($result['post_max_size']),
userReadableSize($result['max_upload_filesize']), userReadableSize($result['max_upload_filesize']),
$result['max_file_uploads'] $result['max_file_uploads']
); );
$help .= $mem_warning;
$help .= '<br><br>' . t('You can adjust these settings in the server php.ini file.'); $help .= '<br><br>' . t('You can adjust these settings in the server php.ini file.');
$this->check_add($checks, t('PHP upload limits'), true, false, $help); $this->check_add($checks, t('PHP upload limits'), true, false, $help);
@ -462,7 +463,7 @@ class Setup extends \Zotlabs\Web\Controller {
$help = ''; $help = '';
$res = false; $res = false;
if (function_exists('openssl_pkey_new')) { if(function_exists('openssl_pkey_new')) {
$res = openssl_pkey_new(array( $res = openssl_pkey_new(array(
'digest_alg' => 'sha1', 'digest_alg' => 'sha1',
'private_key_bits' => 4096, 'private_key_bits' => 4096,
@ -472,7 +473,7 @@ class Setup extends \Zotlabs\Web\Controller {
// Get private key // Get private key
if (! $res) { if(! $res) {
$help .= t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys'). EOL; $help .= t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys'). EOL;
$help .= t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".'); $help .= t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".');
} }
@ -503,7 +504,7 @@ class Setup extends \Zotlabs\Web\Controller {
$this->check_add($ck_funcs, t('zip PHP module'), true, true); $this->check_add($ck_funcs, t('zip PHP module'), true, true);
if(function_exists('apache_get_modules')){ if(function_exists('apache_get_modules')){
if (! in_array('mod_rewrite', apache_get_modules())) { if(! in_array('mod_rewrite', apache_get_modules())) {
$this->check_add($ck_funcs, t('Apache mod_rewrite module'), false, true, t('Error: Apache webserver mod-rewrite module is required but not installed.')); $this->check_add($ck_funcs, t('Apache mod_rewrite module'), false, true, t('Error: Apache webserver mod-rewrite module is required but not installed.'));
} else { } else {
$this->check_add($ck_funcs, t('Apache mod_rewrite module'), true, true); $this->check_add($ck_funcs, t('Apache mod_rewrite module'), true, true);
@ -638,7 +639,7 @@ class Setup extends \Zotlabs\Web\Controller {
$url = z_root() . '/setup/testrewrite'; $url = z_root() . '/setup/testrewrite';
if (function_exists('curl_init')){ if(function_exists('curl_init')){
$test = z_fetch_url($url); $test = z_fetch_url($url);
if(! $test['success']) { if(! $test['success']) {
if(strstr($url,'https://')) { if(strstr($url,'https://')) {
@ -661,14 +662,13 @@ class Setup extends \Zotlabs\Web\Controller {
$help .= t('If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues.') . EOL; $help .= t('If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues.') . EOL;
$help .= t('This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement.') .EOL; $help .= t('This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement.') .EOL;
$help .= t('Providers are available that issue free certificates which are browser-valid.'). EOL; $help .= t('Providers are available that issue free certificates which are browser-valid.'). EOL;
$help .= t('If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications.') . EOL; $help .= t('If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications.') . EOL;
$this->check_add($checks, t('SSL certificate validation'), false, true, $help); $this->check_add($checks, t('SSL certificate validation'), false, true, $help);
} }
} }
if ((! $test['success']) || ($test['body'] != "ok")) { if((! $test['success']) || ($test['body'] != "ok")) {
$status = false; $status = false;
$help = t('Url rewrite in .htaccess is not working. Check your server configuration.'.'Test: '.var_export($test,true)); $help = t('Url rewrite in .htaccess is not working. Check your server configuration.'.'Test: '.var_export($test,true));
} }
@ -682,10 +682,9 @@ class Setup extends \Zotlabs\Web\Controller {
/** /**
* @brief * @brief
* *
* @param App &$a
* @return string with paresed HTML * @return string with paresed HTML
*/ */
function manual_config(&$a) { function manual_config() {
$data = htmlspecialchars(\App::$data['txt'], ENT_COMPAT, 'UTF-8'); $data = htmlspecialchars(\App::$data['txt'], ENT_COMPAT, 'UTF-8');
$o = t('The database configuration file ".htconfig.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.'); $o = t('The database configuration file ".htconfig.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.');
$o .= "<textarea rows=\"24\" cols=\"80\" >$data</textarea>"; $o .= "<textarea rows=\"24\" cols=\"80\" >$data</textarea>";
@ -695,14 +694,19 @@ class Setup extends \Zotlabs\Web\Controller {
function load_database_rem($v, $i){ function load_database_rem($v, $i){
$l = trim($i); $l = trim($i);
if (strlen($l)>1 && ($l[0]=="-" || ($l[0]=="/" && $l[1]=="*"))){ if(strlen($l)>1 && ($l[0]=="-" || ($l[0]=="/" && $l[1]=="*"))){
return $v; return $v;
} else { } else {
return $v."\n".$i; return $v."\n".$i;
} }
} }
/**
* @brief Executes the SQL install script and create database tables.
*
* @param dba_driver $db (unused)
* @return boolean|string false on success or error message as string
*/
function load_database($db) { function load_database($db) {
$str = file_get_contents(\DBA::$dba->get_install_script()); $str = file_get_contents(\DBA::$dba->get_install_script());
$arr = explode(';', $str); $arr = explode(';', $str);
@ -762,12 +766,12 @@ class Setup extends \Zotlabs\Web\Controller {
/** /**
* @brief * @brief
* *
* @param unknown $v * @param array $v
* @param array $c * @param array $c
* @return array * @return array
*/ */
static private function check_passed($v, $c) { static private function check_passed($v, $c) {
if ($c['required']) if($c['required'])
$v = $v && $c['status']; $v = $v && $c['status'];
return $v; return $v;

View File

@ -1,6 +1,11 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Activity;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
@ -14,23 +19,23 @@ class Share extends \Zotlabs\Web\Controller {
if(! $post_id) if(! $post_id)
killme(); killme();
echo '[share=' . $post_id . '][/share]'; if(! local_channel()) {
killme();
/**
* The remaining code is deprecated and handled in Zotlabs/Lib/Share.php at post
* submission time.
*/
if(! (local_channel() || remote_channel()))
killme(); killme();
}
$observer = App::get_observer();
$channel = App::get_channel();
$r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1",
intval($post_id) intval($post_id)
); );
if(! $r) if(! $r)
killme(); killme();
if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss'))
killme(); killme();
@ -47,58 +52,85 @@ class Share extends \Zotlabs\Web\Controller {
if($r[0]['mimetype'] !== 'text/bbcode') if($r[0]['mimetype'] !== 'text/bbcode')
killme(); killme();
/** @FIXME eventually we want to post remotely via rpost on your home site */
// When that works remove this next bit:
if(! local_channel())
killme();
xchan_query($r); xchan_query($r);
$is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false); $arr = [];
if($is_photo) {
$object = json_decode($r[0]['obj'],true);
$photo_bb = $object['body'];
}
if (strpos($r[0]['body'], "[/share]") !== false) { $item = $r[0];
$pos = strpos($r[0]['body'], "[share");
$o = substr($r[0]['body'], $pos);
} else {
$o = "[share author='" . urlencode($r[0]['author']['xchan_name']) .
"' profile='" . $r[0]['author']['xchan_url'] .
"' avatar='" . $r[0]['author']['xchan_photo_s'] .
"' link='" . $r[0]['plink'] .
"' auth='" . (($r[0]['author']['network'] === 'zot') ? 'true' : 'false') .
"' posted='" . $r[0]['created'] .
"' message_id='" . $r[0]['mid'] .
"']";
if($r[0]['title'])
$o .= '[b]'.$r[0]['title'].'[/b]'."\r\n";
$o .= (($is_photo) ? $photo_bb . "\r\n" . $r[0]['body'] : $r[0]['body']);
$o .= "[/share]";
}
if(local_channel()) { $owner_uid = $r[0]['uid'];
echo $o; $owner_aid = $r[0]['aid'];
$can_comment = false;
if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self']))
$can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments');
else
$can_comment = can_comment_on_post($observer['xchan_hash'],$item);
if(! $can_comment) {
notice( t('Permission denied') . EOL);
killme(); killme();
} }
$observer = \App::get_observer(); $r = q("select * from xchan where xchan_hash = '%s' limit 1",
$parsed = $observer['xchan_url']; dbesc($item['owner_xchan'])
if($parsed) { );
$post_url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '')
. '/rpost';
/** if($r)
* @FIXME we were probably called from JS so we don't know the return page. $thread_owner = $r[0];
* In fact we won't be able to load the remote page. else
* we might need an iframe
*/
$x = z_post_url($post_url, array('f' => '', 'body' => $o ));
killme(); killme();
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($item['author_xchan'])
);
if($r)
$item_author = $r[0];
else
killme();
$arr['aid'] = $owner_aid;
$arr['uid'] = $owner_uid;
$arr['item_origin'] = 1;
$arr['item_wall'] = $item['item_wall'];
$arr['uuid'] = item_message_id();
$arr['mid'] = z_root() . '/activity/' . $arr['uuid'];
$arr['parent_mid'] = $item['mid'];
$mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]';
$arr['body'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, Activity::activity_obj_mapper($item['obj_type']));
$arr['author_xchan'] = $channel['channel_hash'];
$arr['owner_xchan'] = $item['author_xchan'];
$arr['obj'] = Activity::encode_item($item);
$arr['obj_type'] = $item['obj_type'];
$arr['verb'] = 'Announce';
$post = item_store($arr);
$post_id = $post['item_id'];
$arr['id'] = $post_id;
call_hooks('post_local_end', $arr);
info( t('Post repeated') . EOL);
$r = q("select * from item where id = %d",
intval($post_id)
);
if($r) {
xchan_query($r);
$sync_item = fetch_post_tags($r);
build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]);
} }
Master::Summon([ 'Notifier','like',$post_id ]);
killme();
} }
} }

View File

@ -12,10 +12,16 @@ class Sslify extends \Zotlabs\Web\Controller {
list($k,$v) = array_map("trim", explode(":", trim($l), 2)); list($k,$v) = array_map("trim", explode(":", trim($l), 2));
$hdrs[strtolower($k)] = $v; $hdrs[strtolower($k)] = $v;
} }
if (array_key_exists('content-type', $hdrs)) {
$type = $hdrs['content-type']; if (array_key_exists('content-type', $hdrs))
header('Content-Type: ' . $type); header('Content-Type: ' . $hdrs['content-type']);
} if (array_key_exists('last-modified', $hdrs))
header('Last-Modified: ' . $hdrs['last-modified']);
if (array_key_exists('cache-control', $hdrs))
header('Cache-Control: ' . $hdrs['cache-control']);
if (array_key_exists('expires', $hdrs))
header('Expires: ' . $hdrs['expires']);
echo $x['body']; echo $x['body'];
killme(); killme();

View File

@ -106,7 +106,8 @@ class Subthread extends \Zotlabs\Web\Controller {
$mid = item_message_id(); $uuid = item_message_id();
$mid = z_root() . '/item/' . $uuid;
$post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status')); $post_type = (($item['resource_type'] === 'photo') ? t('photo') : t('status'));
@ -145,6 +146,7 @@ class Subthread extends \Zotlabs\Web\Controller {
$arr = array(); $arr = array();
$arr['uuid'] = $uuid;
$arr['mid'] = $mid; $arr['mid'] = $mid;
$arr['aid'] = $owner_aid; $arr['aid'] = $owner_aid;
$arr['uid'] = $owner_uid; $arr['uid'] = $owner_uid;

View File

@ -25,10 +25,10 @@ class Viewsrc extends \Zotlabs\Web\Controller {
notice( t('Item not found.') . EOL); notice( t('Item not found.') . EOL);
} }
$item_normal = item_normal(); $item_normal = item_normal_search();
if(local_channel() && $item_id) { if(local_channel() && $item_id) {
$r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", $r = q("select id, mid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
intval(local_channel()), intval(local_channel()),
intval($sys['channel_id']), intval($sys['channel_id']),
intval($item_id) intval($item_id)
@ -53,7 +53,7 @@ class Viewsrc extends \Zotlabs\Web\Controller {
if(is_ajax()) { if(is_ajax()) {
echo '<div class="p-1">'; echo '<div class="p-1">';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a></div>'; echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '</div>';
echo '<hr>'; echo '<hr>';
echo '<pre class="p-1">' . $o . '</pre>'; echo '<pre class="p-1">' . $o . '</pre>';
echo '</div>'; echo '</div>';

View File

@ -43,6 +43,9 @@ class Wfinger extends \Zotlabs\Web\Controller {
if(strpos($resource,'acct:') === 0) { if(strpos($resource,'acct:') === 0) {
$channel = str_replace('acct:','',$resource); $channel = str_replace('acct:','',$resource);
if(substr($channel,0,1) === '@' && strpos(substr($channel,1),'@')) {
$channel = substr($channel,1);
}
if(strpos($channel,'@') !== false) { if(strpos($channel,'@') !== false) {
$host = substr($channel,strpos($channel,'@')+1); $host = substr($channel,strpos($channel,'@')+1);
@ -204,6 +207,12 @@ class Wfinger extends \Zotlabs\Web\Controller {
'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'], 'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'],
], ],
[
'rel' => 'http://purl.org/zot/protocol/6.0',
'type' => 'application/x-zot+json',
'href' => channel_url($r[0])
],
[ [
'rel' => 'http://purl.org/openwebauth/v1', 'rel' => 'http://purl.org/openwebauth/v1',
'type' => 'application/x-zot+json', 'type' => 'application/x-zot+json',

View File

@ -0,0 +1,498 @@
<?php
namespace Zotlabs\Photo;
/**
* @brief Abstract photo driver class.
*
* Inheritance seems not to be the best design pattern for such photo drivers.
*/
abstract class PhotoDriver {
/**
* @brief This variable keeps the image.
*
* For GD it is a PHP image resource.
* For ImageMagick it is an \Imagick object.
*
* @var resource|\Imagick
*/
protected $image;
/**
* @var integer
*/
protected $width;
/**
* @var integer
*/
protected $height;
/**
* @var boolean
*/
protected $valid;
/**
* @brief The mimetype of the image.
*
* @var string
*/
protected $type;
/**
* @brief Supported mimetypes by the used photo driver.
*
* @var array
*/
protected $types;
/**
* @brief Return an array with supported mimetypes.
*
* @return array
* Associative array with mimetype as key and file extension as value.
*/
abstract public function supportedTypes();
abstract protected function load($data, $type);
abstract protected function destroy();
abstract protected function setDimensions();
/**
* @brief Return the current image.
*
* @fixme Shouldn't his method be protected, because outside of the current
* driver it makes no sense at all because of the different return values.
*
* @return boolean|resource|\Imagick
* false on failure, a PHP image resource for GD driver, an \Imagick object
* for ImageMagick driver.
*/
abstract public function getImage();
abstract public function doScaleImage($new_width, $new_height);
abstract public function rotate($degrees);
abstract public function flip($horiz = true, $vert = false);
/**
* @brief Crops the image.
*
* @param int $maxx width of the new image
* @param int $maxy height of the new image
* @param int $x x-offset for region
* @param int $y y-offset for region
* @param int $w width of region
* @param int $h height of region
*
* @return boolean|void false on failure
*/
abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h);
/**
* @brief Return a binary string from the image resource.
*
* @return string A Binary String.
*/
abstract public function imageString();
abstract public function clearexif();
/**
* @brief PhotoDriver constructor.
*
* @param string $data Image
* @param string $type mimetype
*/
public function __construct($data, $type = '') {
$this->types = $this->supportedTypes();
if(! array_key_exists($type, $this->types)) {
$type = 'image/jpeg';
}
$this->type = $type;
$this->valid = false;
$this->load($data, $type);
}
public function __destruct() {
if($this->is_valid())
$this->destroy();
}
/**
* @brief Is it a valid image object.
*
* @return boolean
*/
public function is_valid() {
return $this->valid;
}
/**
* @brief Get the width of the image.
*
* @return boolean|number Width of image in pixels, or false on failure
*/
public function getWidth() {
if(! $this->is_valid())
return false;
return $this->width;
}
/**
* @brief Get the height of the image.
*
* @return boolean|number Height of image in pixels, or false on failure
*/
public function getHeight() {
if(! $this->is_valid())
return false;
return $this->height;
}
/**
* @brief Saves the image resource to a file in filesystem.
*
* @param string $path Path and filename where to save the image
* @return boolean False on failure, otherwise true
*/
public function saveImage($path) {
if(! $this->is_valid())
return false;
return (file_put_contents($path, $this->imageString()) ? true : false);
}
/**
* @brief Return mimetype of the image resource.
*
* @return boolean|string False on failure, otherwise mimetype.
*/
public function getType() {
if(! $this->is_valid())
return false;
return $this->type;
}
/**
* @brief Return file extension of the image resource.
*
* @return boolean|string False on failure, otherwise file extension.
*/
public function getExt() {
if(! $this->is_valid())
return false;
return $this->types[$this->getType()];
}
/**
* @brief Scale image to max pixel size in either dimension.
*
* @param int $max maximum pixel size in either dimension
* @param boolean $float_height (optional)
* If true allow height to float to any length on tall images, constraining
* only the width
* @return boolean|void false on failure, otherwise void
*/
public function scaleImage($max, $float_height = true) {
if(! $this->is_valid())
return false;
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width) || (! $height))
return false;
if($width > $max && $height > $max) {
// very tall image (greater than 16:9)
// constrain the width - let the height float.
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} // else constrain both dimensions
elseif($width > $height) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} else {
$dest_width = intval(($width * $max) / $height);
$dest_height = $max;
}
} else {
if($width > $max) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} else {
if($height > $max) {
// very tall image (greater than 16:9)
// but width is OK - don't do anything
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $width;
$dest_height = $height;
} else {
$dest_width = intval(($width * $max) / $height);
$dest_height = $max;
}
} else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width, $dest_height);
}
public function scaleImageUp($min) {
if(! $this->is_valid()) {
return false;
}
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width) || (! $height))
return false;
if($width < $min && $height < $min) {
if($width > $height) {
$dest_width = $min;
$dest_height = intval(($height * $min) / $width);
} else {
$dest_width = intval(($width * $min) / $height);
$dest_height = $min;
}
} else {
if($width < $min) {
$dest_width = $min;
$dest_height = intval(($height * $min) / $width);
} else {
if($height < $min) {
$dest_width = intval(($width * $min) / $height);
$dest_height = $min;
} else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width, $dest_height);
}
/**
* @brief Scales image to a square.
*
* @param int $dim Pixel of square image
* @return boolean|void false on failure, otherwise void
*/
public function scaleImageSquare($dim) {
if(! $this->is_valid())
return false;
$this->doScaleImage($dim, $dim);
}
/**
* @brief Crops a square image.
*
* @see cropImageRect()
*
* @param int $max size of the new image
* @param int $x x-offset for region
* @param int $y y-offset for region
* @param int $w width of region
* @param int $h height of region
*
* @return boolean|void false on failure
*/
public function cropImage($max, $x, $y, $w, $h) {
if(! $this->is_valid())
return false;
$this->cropImageRect($max, $max, $x, $y, $w, $h);
}
/**
* @brief Reads exif data from a given filename.
*
* @param string $filename
* @return boolean|array
*/
public function exif($filename) {
if((! function_exists('exif_read_data'))
|| (! in_array($this->getType(), ['image/jpeg', 'image/tiff'])))
{
return false;
}
/*
* PHP 7.2 allows you to use a stream resource, which should reduce/avoid
* memory exhaustion on large images.
*/
if(version_compare(PHP_VERSION, '7.2.0') >= 0) {
$f = @fopen($filename, 'rb');
} else {
$f = $filename;
}
if($f) {
return @exif_read_data($f, null, true);
}
return false;
}
/**
* @brief Orients current image based on exif orientation information.
*
* @param array $exif
* @return boolean true if oriented, otherwise false
*/
public function orient($exif) {
if(! ($this->is_valid() && $exif)) {
return false;
}
$ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
if(! $ort) {
return false;
}
switch($ort) {
case 1 : // nothing
break;
case 2 : // horizontal flip
$this->flip();
break;
case 3 : // 180 rotate left
$this->rotate(180);
break;
case 4 : // vertical flip
$this->flip(false, true);
break;
case 5 : // vertical flip + 90 rotate right
$this->flip(false, true);
$this->rotate(-90);
break;
case 6 : // 90 rotate right
$this->rotate(-90);
break;
case 7 : // horizontal flip + 90 rotate right
$this->flip();
$this->rotate(-90);
break;
case 8 : // 90 rotate left
$this->rotate(90);
break;
default :
break;
}
return true;
}
/**
* @brief Save photo to database.
*
* @param array $arr
* @param boolean $skipcheck (optional) default false
* @return boolean|array
*/
public function save($arr, $skipcheck = false) {
if(! ($skipcheck || $this->is_valid())) {
logger('Attempt to store invalid photo.');
return false;
}
$p = [];
$p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
$p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
$p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
$p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
$p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
$p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
$p['album'] = (($arr['album']) ? $arr['album'] : '');
$p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
$p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
$p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
$p['title'] = (($arr['title']) ? $arr['title'] : '');
$p['description'] = (($arr['description']) ? $arr['description'] : '');
$p['photo_usage'] = intval($arr['photo_usage']);
$p['os_storage'] = intval($arr['os_storage']);
$p['os_path'] = $arr['os_path'];
$p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : '');
$p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400)));
if(! intval($p['imgscale']))
logger('save: ' . print_r($arr, true), LOGGER_DATA);
$x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale']));
if($x) {
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
$r = q("UPDATE photo set
aid = %d,
uid = %d,
xchan = '%s',
resource_id = '%s',
created = '%s',
edited = '%s',
filename = '%s',
mimetype = '%s',
album = '%s',
height = %d,
width = %d,
content = '%s',
os_storage = %d,
filesize = %d,
imgscale = %d,
photo_usage = %d,
title = '%s',
description = '%s',
os_path = '%s',
display_path = '%s',
allow_cid = '%s',
allow_gid = '%s',
deny_cid = '%s',
deny_gid = '%s',
expires = '%s'
where id = %d",
intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($x[0]['id']));
} else {
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
$r = q("INSERT INTO photo
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']));
}
logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
return $r;
}
}

176
Zotlabs/Photo/PhotoGd.php Normal file
View File

@ -0,0 +1,176 @@
<?php
namespace Zotlabs\Photo;
/**
* @brief GD photo driver.
*
*/
class PhotoGd extends PhotoDriver {
/**
* {@inheritDoc}
* @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
*/
public function supportedTypes() {
$t = [];
$t['image/jpeg'] = 'jpg';
if(imagetypes() & IMG_PNG)
$t['image/png'] = 'png';
if(imagetypes() & IMG_GIF)
$t['image/gif'] = 'gif';
return $t;
}
protected function load($data, $type) {
$this->valid = false;
if(! $data)
return;
$this->image = @imagecreatefromstring($data);
if($this->image !== false) {
$this->valid = true;
$this->setDimensions();
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
}
}
protected function setDimensions() {
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
}
/**
* @brief GD driver does not preserve EXIF, so not need to clear it.
*
* @return void
*/
public function clearexif() {
return;
}
protected function destroy() {
if($this->is_valid()) {
imagedestroy($this->image);
}
}
/**
* @brief Return a PHP image resource of the current image.
*
* @see \Zotlabs\Photo\PhotoDriver::getImage()
*
* @return boolean|resource
*/
public function getImage() {
if(! $this->is_valid())
return false;
return $this->image;
}
public function doScaleImage($dest_width, $dest_height) {
$dest = imagecreatetruecolor($dest_width, $dest_height);
$width = imagesx($this->image);
$height = imagesy($this->image);
imagealphablending($dest, false);
imagesavealpha($dest, true);
if($this->type == 'image/png')
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
public function rotate($degrees) {
if(! $this->is_valid())
return false;
$this->image = imagerotate($this->image, $degrees, 0);
$this->setDimensions();
}
public function flip($horiz = true, $vert = false) {
if(! $this->is_valid())
return false;
$w = imagesx($this->image);
$h = imagesy($this->image);
$flipped = imagecreate($w, $h);
if($horiz) {
for($x = 0; $x < $w; $x++) {
imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
}
}
if($vert) {
for($y = 0; $y < $h; $y++) {
imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
}
}
$this->image = $flipped;
$this->setDimensions(); // Shouldn't really be necessary
}
public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
if(! $this->is_valid())
return false;
$dest = imagecreatetruecolor($maxx, $maxy);
imagealphablending($dest, false);
imagesavealpha($dest, true);
if($this->type == 'image/png')
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
/**
* {@inheritDoc}
* @see \Zotlabs\Photo\PhotoDriver::imageString()
*/
public function imageString() {
if(! $this->is_valid())
return false;
$quality = false;
ob_start();
switch($this->getType()){
case 'image/png':
$quality = get_config('system', 'png_quality');
if((! $quality) || ($quality > 9))
$quality = PNG_QUALITY;
\imagepng($this->image, NULL, $quality);
break;
case 'image/jpeg':
// gd can lack imagejpeg(), but we verify during installation it is available
default:
$quality = get_config('system', 'jpeg_quality');
if((! $quality) || ($quality > 100))
$quality = JPEG_QUALITY;
\imagejpeg($this->image, NULL, $quality);
break;
}
$string = ob_get_contents();
ob_end_clean();
return $string;
}
}

View File

@ -1,45 +1,44 @@
<?php /** @file */ <?php
namespace Zotlabs\Photo;
require_once('include/photo/photo_driver.php'); /**
* @brief ImageMagick photo driver.
*/
class PhotoImagick extends PhotoDriver {
public function supportedTypes() {
class photo_imagick extends photo_driver { return [
function supportedTypes() {
return array(
'image/jpeg' => 'jpg', 'image/jpeg' => 'jpg',
'image/png' => 'png', 'image/png' => 'png',
'image/gif' => 'gif' 'image/gif' => 'gif',
); ];
} }
public function get_FormatsMap() { private function get_FormatsMap() {
return array( return [
'image/jpeg' => 'JPG', 'image/jpeg' => 'JPG',
'image/png' => 'PNG', 'image/png' => 'PNG',
'image/gif' => 'GIF' 'image/gif' => 'GIF',
); ];
} }
function load($data, $type) { protected function load($data, $type) {
$this->valid = false; $this->valid = false;
$this->image = new Imagick(); $this->image = new \Imagick();
if(! $data) if(! $data)
return; return;
try { try {
$this->image->readImageBlob($data); $this->image->readImageBlob($data);
} } catch(\Exception $e) {
catch (Exception $e) { logger('Imagick readImageBlob() exception:' . print_r($e, true));
logger('imagick readImageBlob() exception:' . print_r($e,true));
return; return;
} }
/** /*
* Setup the image to the format it will be saved to * Setup the image to the format it will be saved to
*/ */
@ -52,19 +51,18 @@ class photo_imagick extends photo_driver {
// Always coalesce, if it is not a multi-frame image it won't hurt anyway // Always coalesce, if it is not a multi-frame image it won't hurt anyway
$this->image = $this->image->coalesceImages(); $this->image = $this->image->coalesceImages();
$this->valid = true; $this->valid = true;
$this->setDimensions(); $this->setDimensions();
/** /*
* setup the compression here, so we'll do it only once * setup the compression here, so we'll do it only once
*/ */
switch($this->getType()) { switch($this->getType()) {
case "image/png": case 'image/png':
$quality = get_config('system','png_quality'); $quality = get_config('system', 'png_quality');
if((! $quality) || ($quality > 9)) if((! $quality) || ($quality > 9))
$quality = PNG_QUALITY; $quality = PNG_QUALITY;
/** /*
* From http://www.imagemagick.org/script/command-line-options.php#quality: * From http://www.imagemagick.org/script/command-line-options.php#quality:
* *
* 'For the MNG and PNG image formats, the quality value sets * 'For the MNG and PNG image formats, the quality value sets
@ -75,56 +73,64 @@ class photo_imagick extends photo_driver {
$quality = $quality * 10; $quality = $quality * 10;
$this->image->setCompressionQuality($quality); $this->image->setCompressionQuality($quality);
break; break;
case "image/jpeg": case 'image/jpeg':
$quality = get_config('system','jpeg_quality'); $quality = get_config('system', 'jpeg_quality');
if((! $quality) || ($quality > 100)) if((! $quality) || ($quality > 100))
$quality = JPEG_QUALITY; $quality = JPEG_QUALITY;
$this->image->setCompressionQuality($quality); $this->image->setCompressionQuality($quality);
default: default:
break; break;
} }
} }
} }
public function destroy() { protected function destroy() {
if($this->is_valid()) { if($this->is_valid()) {
$this->image->clear(); $this->image->clear();
$this->image->destroy(); $this->image->destroy();
} }
} }
protected function setDimensions() {
public function setDimensions() {
$this->width = $this->image->getImageWidth(); $this->width = $this->image->getImageWidth();
$this->height = $this->image->getImageHeight(); $this->height = $this->image->getImageHeight();
} }
/**
* @brief Strips the image of all profiles and comments.
*
* Keep ICC profile for better colors.
*
* @see \Zotlabs\Photo\PhotoDriver::clearexif()
*/
public function clearexif() { public function clearexif() {
$profiles = $this->image->getImageProfiles('icc', true);
$profiles = $this->image->getImageProfiles("icc", true);
$this->image->stripImage(); $this->image->stripImage();
if(!empty($profiles)) { if(! empty($profiles)) {
$this->image->profileImage("icc", $profiles['icc']); $this->image->profileImage('icc', $profiles['icc']);
} }
} }
/**
* @brief Return a \Imagick object of the current image.
*
* @see \Zotlabs\Photo\PhotoDriver::getImage()
*
* @return boolean|\Imagick
*/
public function getImage() { public function getImage() {
if(!$this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image = $this->image->deconstructImages(); $this->image = $this->image->deconstructImages();
return $this->image; return $this->image;
} }
public function doScaleImage($dest_width,$dest_height) { public function doScaleImage($dest_width, $dest_height) {
/*
/**
* If it is not animated, there will be only one iteration here, * If it is not animated, there will be only one iteration here,
* so don't bother checking * so don't bother checking
*/ */
@ -132,82 +138,63 @@ class photo_imagick extends photo_driver {
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
$this->image->scaleImage($dest_width, $dest_height); $this->image->scaleImage($dest_width, $dest_height);
} while ($this->image->nextImage()); } while($this->image->nextImage());
$this->setDimensions(); $this->setDimensions();
} }
public function rotate($degrees) { public function rotate($degrees) {
if(!$this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
// ImageMagick rotates in the opposite direction of imagerotate() // ImageMagick rotates in the opposite direction of imagerotate()
$this->image->rotateImage(new ImagickPixel(), -$degrees); $this->image->rotateImage(new \ImagickPixel(), -$degrees);
} while ($this->image->nextImage()); } while($this->image->nextImage());
$this->setDimensions(); $this->setDimensions();
} }
public function flip($horiz = true, $vert = false) { public function flip($horiz = true, $vert = false) {
if(!$this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
if($horiz) $this->image->flipImage(); if($horiz) $this->image->flipImage();
if($vert) $this->image->flopImage(); if($vert) $this->image->flopImage();
} while ($this->image->nextImage()); } while($this->image->nextImage());
$this->setDimensions(); // Shouldn't really be necessary $this->setDimensions(); // Shouldn't really be necessary
} }
public function cropImage($max,$x,$y,$w,$h) { public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
if(!$this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
$this->image->cropImage($w, $h, $x, $y); $this->image->cropImage($w, $h, $x, $y);
/** /*
* We need to remove the canvas, * We need to remove the canvas,
* or the image is not resized to the crop: * or the image is not resized to the crop:
* http://php.net/manual/en/imagick.cropimage.php#97232 * http://php.net/manual/en/imagick.cropimage.php#97232
*/ */
$this->image->setImagePage(0, 0, 0, 0); $this->image->setImagePage(0, 0, 0, 0);
} while ($this->image->nextImage()); } while($this->image->nextImage());
$this->doScaleImage($max,$max); $this->doScaleImage($maxx, $maxy);
}
public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) {
if(!$this->is_valid())
return FALSE;
$this->image->setFirstIterator();
do {
$this->image->cropImage($w, $h, $x, $y);
/**
* We need to remove the canvas,
* or the image is not resized to the crop:
* http://php.net/manual/en/imagick.cropimage.php#97232
*/
$this->image->setImagePage(0, 0, 0, 0);
} while ($this->image->nextImage());
$this->doScaleImage($maxx,$maxy);
} }
public function imageString() { public function imageString() {
if(!$this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
/* Clean it */ /* Clean it */
$this->image = $this->image->deconstructImages(); $this->image = $this->image->deconstructImages();
return $this->image->getImagesBlob(); return $this->image->getImagesBlob();
} }
} }

View File

@ -205,17 +205,21 @@ class Browser extends DAV\Browser\Plugin {
// upload access. system.thumbnail_security should be set to 1 if you want to include these // upload access. system.thumbnail_security should be set to 1 if you want to include these
// types // types
$is_creator = false;
$photo_icon = ''; $photo_icon = '';
$preview_style = intval(get_config('system','thumbnail_security',0)); $preview_style = intval(get_config('system','thumbnail_security',0));
$r = q("select content from attach where hash = '%s' and uid = %d limit 1", $r = q("select content, creator from attach where hash = '%s' and uid = %d limit 1",
dbesc($attachHash), dbesc($attachHash),
intval($owner) intval($owner)
); );
if($r && file_exists(dbunescbin($r[0]['content']) . '.thumb')) { if($r) {
$photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb')); $is_creator = (($r[0]['creator'] === get_observer_hash()) ? true : false);
// logger('found thumb: ' . $photo_icon); if(file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
$photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
// logger('found thumb: ' . $photo_icon);
}
} }
if(strpos($type,'image/') === 0 && $attachHash) { if(strpos($type,'image/') === 0 && $attachHash) {
@ -247,6 +251,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['attachIcon'] = (($size) ? $attachIcon : ''); $ft['attachIcon'] = (($size) ? $attachIcon : '');
// @todo Should this be an item value, not a global one? // @todo Should this be an item value, not a global one?
$ft['is_owner'] = $is_owner; $ft['is_owner'] = $is_owner;
$ft['is_creator'] = $is_creator;
$ft['fullPath'] = $fullPath; $ft['fullPath'] = $fullPath;
$ft['displayName'] = $displayName; $ft['displayName'] = $displayName;
$ft['type'] = $type; $ft['type'] = $type;
@ -256,6 +261,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['iconFromType'] = getIconFromType($type); $ft['iconFromType'] = getIconFromType($type);
$f[] = $ft; $f[] = $ft;
} }

View File

@ -224,7 +224,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo
} }
$filesize = 0; $filesize = 0;
$hash = random_string(); $hash = new_uuid();
$f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash;

78
Zotlabs/Update/_1226.php Normal file
View File

@ -0,0 +1,78 @@
<?php
namespace Zotlabs\Update;
use Zotlabs\Lib\Libzot;
class _1226 {
function run() {
q("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE channel ADD channel_portable_id text NOT NULL DEFAULT '' ");
$r2 = q("create index \"channel_portable_id_idx\" on channel (\"channel_portable_id\")");
$r = ($r1 && $r2);
}
else {
$r = q("ALTER TABLE `channel` ADD `channel_portable_id` char(191) NOT NULL DEFAULT '' ,
ADD INDEX `channel_portable_id` (`channel_portable_id`)");
}
if($r) {
q("COMMIT");
self::upgrade();
return UPDATE_SUCCESS;
}
q("ROLLBACK");
return UPDATE_FAILED;
}
static function upgrade() {
$r = q("select * from channel where channel_portable_id = '' ");
if($r) {
foreach($r as $rv) {
$zhash = Libzot::make_xchan_hash($rv['channel_guid'],$rv['channel_pubkey']);
q("update channel set channel_portable_id = '%s' where channel_id = %d",
dbesc($zhash),
intval($rv['channel_id'])
);
$x = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($rv['channel_hash'])
);
if($x) {
$rec = $x[0];
$rec['xchan_hash'] = $zhash;
$rec['xchan_guid_sig'] = 'sha256.' . $rec['xchan_guid_sig'];
$rec['xchan_network'] = 'zot6';
xchan_store_lowlevel($rec);
}
$x = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' limit 1",
dbesc($rv['channel_hash']),
dbesc(z_root())
);
if($x) {
$rec = $x[0];
$rec['hubloc_hash'] = $zhash;
$rec['hubloc_guid_sig'] = 'sha256.' . $rec['hubloc_guid_sig'];
$rec['hubloc_network'] = 'zot6';
$rec['hubloc_url_sig'] = 'sha256.' . $rec['hubloc_url_sig'];
$rec['hubloc_callback'] = z_root() . '/zot';
$rec['hubloc_id_url'] = channel_url($rv);
$rec['hubloc_site_id'] = Libzot::make_xchan_hash(z_root(),get_config('system','pubkey'));
hubloc_store_lowlevel($rec);
}
}
}
}
}

30
Zotlabs/Update/_1227.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace Zotlabs\Update;
class _1227 {
function run() {
q("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r = q("ALTER TABLE dreport ADD dreport_name text NOT NULL DEFAULT '' ");
}
else {
$r = q("ALTER TABLE `dreport` ADD `dreport_name` char(191) NOT NULL DEFAULT ''");
}
if($r) {
q("COMMIT");
return UPDATE_SUCCESS;
}
q("ROLLBACK");
return UPDATE_FAILED;
}
}

36
Zotlabs/Update/_1228.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace Zotlabs\Update;
class _1228 {
function run() {
q("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE item ADD uuid text NOT NULL DEFAULT '' ");
$r2 = q("create index \"uuid_idx\" on item (\"uuid\")");
$r3 = q("ALTER TABLE item add summary TEXT NOT NULL DEFAULT ''");
$r = ($r1 && $r2 && $r3);
}
else {
$r1 = q("ALTER TABLE `item` ADD `uuid` char(191) NOT NULL DEFAULT '' ,
ADD INDEX `uuid` (`uuid`)");
$r2 = q("ALTER TABLE `item` ADD `summary` mediumtext NOT NULL");
$r = ($r1 && $r2);
}
if($r) {
q("COMMIT");
return UPDATE_SUCCESS;
}
q("ROLLBACK");
return UPDATE_FAILED;
}
}

32
Zotlabs/Update/_1229.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace Zotlabs\Update;
class _1229 {
function run() {
q("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE photo ADD expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' ");
$r2 = q("create index \"photo_expires_idx\" on photo (\"expires\")");
$r = ($r1 && $r2);
}
else {
$r = q("ALTER TABLE `photo` ADD `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' ,
ADD INDEX `expires` (`expires`)");
}
if($r) {
q("COMMIT");
return UPDATE_SUCCESS;
}
q("ROLLBACK");
return UPDATE_FAILED;
}
}

12
Zotlabs/Update/_1230.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace Zotlabs\Update;
class _1230 {
function run() {
q("update abook set abook_closeness = 80 where abook_closeness = 0 and abook_self = 0");
return UPDATE_SUCCESS;
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace Zotlabs\Web;
class CheckJS {
private static $jsdisabled = 0;
function __construct($test = 0) {
if(intval($_REQUEST['jsdisabled']))
$this->jsdisabled = 1;
else
$this->jsdisabled = 0;
if(intval($_COOKIE['jsdisabled']))
$this->jsdisabled = 1;
else
$this->jsdisabled = 0;
$page = bin2hex(\App::$query_string);
if(! $this->jsdisabled) {
if($test) {
$this->jsdisabled = 1;
if(array_key_exists('jsdisabled',$_COOKIE))
$this->jsdisabled = $_COOKIE['jsdisabled'];
if(! array_key_exists('jsdisabled',$_COOKIE)) {
\App::$page['htmlhead'] .= "\r\n" . '<script>document.cookie="jsdisabled=0; path=/"; var jsMatch = /\&jsdisabled=0/; if (!jsMatch.exec(location.href)) { location.href = "' . z_root() . '/nojs/0?f=&redir=' . $page . '" ; }</script>' . "\r\n";
/* emulate JS cookie if cookies are not accepted */
if (array_key_exists('jsdisabled',$_GET)) {
$_COOKIE['jsdisabled'] = $_GET['jsdisabled'];
}
}
}
else {
\App::$page['htmlhead'] .= "\r\n" . '<noscript><meta http-equiv="refresh" content="0; url=' . z_root() . '/nojs?f=&redir=' . $page . '"></noscript>' . "\r\n";
}
}
}
function disabled() {
return $this->jsdisabled;
}
}

View File

@ -342,6 +342,10 @@ class HTTPSig {
*/ */
static function parse_sigheader($header) { static function parse_sigheader($header) {
if(is_array($header)) {
btlogger('is_array: ' . print_r($header,true));
}
$ret = []; $ret = [];
$matches = []; $matches = [];
@ -397,7 +401,7 @@ class HTTPSig {
$data = $matches[1]; $data = $matches[1];
if($iv && $key && $alg && $data) { if($iv && $key && $alg && $data) {
return crypto_unencapsulate([ 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); return crypto_unencapsulate([ 'encrypted' => true, 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey);
} }
return ''; return '';

View File

@ -15,7 +15,7 @@ class Session {
private $handler = null; private $handler = null;
private $session_started = false; private $session_started = false;
private $custom_handler = false;
public function init() { public function init() {
$gc_probability = 50; $gc_probability = 50;
@ -24,17 +24,38 @@ class Session {
ini_set('session.use_only_cookies', 1); ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1); ini_set('session.cookie_httponly', 1);
$this->custom_handler = boolval(get_config('system', 'session_custom', false));
/* /*
* Set our session storage functions. * Set our session storage functions.
*/ */
$handler = new \Zotlabs\Web\SessionHandler(); if($this->custom_handler) {
/* Custom handler (files, memached, redis..) */
$this->handler = $handler; $session_save_handler = strval(get_config('system', 'session_save_handler', Null));
$session_save_path = strval(get_config('system', 'session_save_path', Null));
$session_gc_probability = intval(get_config('system', 'session_gc_probability', 1));
$session_gc_divisor = intval(get_config('system', 'session_gc_divisor', 100));
if(!$session_save_handler || !$session_save_path) {
logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR);
}
else {
ini_set('session.save_handler', $session_save_handler);
ini_set('session.save_path', $session_save_path);
ini_set('session.gc_probability', $session_gc_probability);
ini_set('session.gc_divisor', $session_gc_divisor);
}
}
else {
$handler = new \Zotlabs\Web\SessionHandler();
$x = session_set_save_handler($handler,false); $this->handler = $handler;
if(! $x)
logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); $x = session_set_save_handler($handler,false);
if(! $x)
logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
}
// Force cookies to be secure (https only) if this site is SSL enabled. // Force cookies to be secure (https only) if this site is SSL enabled.
// Must be done before session_start(). // Must be done before session_start().
@ -86,14 +107,15 @@ class Session {
$arr = session_get_cookie_params(); $arr = session_get_cookie_params();
if($this->handler && $this->session_started) { if(($this->handler || $this->custom_handler) && $this->session_started) {
session_regenerate_id(true); session_regenerate_id(true);
// force SessionHandler record creation with the new session_id // force SessionHandler record creation with the new session_id
// which occurs as a side effect of read() // which occurs as a side effect of read()
if (! $this->custom_handler) {
$this->handler->read(session_id()); $this->handler->read(session_id());
}
} }
else else
logger('no session handler'); logger('no session handler');

View File

@ -2,55 +2,65 @@
namespace Zotlabs\Widget; namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;
class Affinity { class Affinity {
function widget($arr) { function widget($arr) {
if(! local_channel()) if(! local_channel())
return ''; return;
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); return;
$default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
$default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
$cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin); $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin);
$cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax); $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax);
$affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
if(feature_enabled(local_channel(),'affinity')) { if ($affinity_locked) {
set_pconfig(local_channel(),'affinity','cmin',$cmin);
$labels = array( set_pconfig(local_channel(),'affinity','cmax',$cmax);
t('Me'),
t('Family'),
t('Friends'),
t('Acquaintances'),
t('All')
);
call_hooks('affinity_labels',$labels);
$label_str = '';
if($labels) {
foreach($labels as $l) {
if($label_str) {
$label_str .= ", '|'";
$label_str .= ", '" . $l . "'";
}
else
$label_str .= "'" . $l . "'";
}
}
$tpl = get_markup_template('main_slider.tpl');
$x = replace_macros($tpl,array(
'$val' => $cmin . ',' . $cmax,
'$refresh' => t('Refresh'),
'$labels' => $label_str,
));
$arr = array('html' => $x);
call_hooks('main_slider',$arr);
return $arr['html'];
} }
return '';
$labels = array(
t('Me'),
t('Family'),
t('Friends'),
t('Acquaintances'),
t('All')
);
call_hooks('affinity_labels',$labels);
$label_str = '';
if($labels) {
foreach($labels as $l) {
if($label_str) {
$label_str .= ", '|'";
$label_str .= ", '" . $l . "'";
}
else
$label_str .= "'" . $l . "'";
}
}
$tpl = get_markup_template('main_slider.tpl');
$x = replace_macros($tpl,array(
'$val' => $cmin . ',' . $cmax,
'$refresh' => t('Refresh'),
'$labels' => $label_str,
));
$arr = array('html' => $x);
call_hooks('main_slider',$arr);
return $arr['html'];
} }
} }

View File

@ -42,19 +42,12 @@ class Settings_menu {
); );
$tabs[] = array( $tabs[] = array(
'label' => t('Display settings'), 'label' => t('Display settings'),
'url' => z_root().'/settings/display', 'url' => z_root().'/settings/display',
'selected' => ((argv(1) === 'display') ? 'active' : ''), 'selected' => ((argv(1) === 'display') ? 'active' : ''),
); );
$tabs[] = array(
'label' => t('Addon settings'),
'url' => z_root().'/settings/featured',
'selected' => ((argv(1) === 'featured') ? 'active' : ''),
);
if($hublocs) { if($hublocs) {
$tabs[] = array( $tabs[] = array(
'label' => t('Manage locations'), 'label' => t('Manage locations'),

View File

@ -55,7 +55,7 @@ class Finger {
$r = q("select xchan.*, hubloc.* from xchan $r = q("select xchan.*, hubloc.* from xchan
left join hubloc on xchan_hash = hubloc_hash left join hubloc on xchan_hash = hubloc_hash
where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 limit 1", where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 and hubloc_network = 'zot' limit 1",
dbesc($xchan_addr) dbesc($xchan_addr)
); );

View File

@ -48,12 +48,14 @@ class HTTPSig {
$h = new HTTPHeaders($data['header']); $h = new HTTPHeaders($data['header']);
$headers = $h->fetcharr(); $headers = $h->fetcharr();
$body = $data['body']; $body = $data['body'];
$headers['(request-target)'] = $data['request_target'];
} }
else { else {
$headers = []; $headers = [];
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$headers['content-type'] = $_SERVER['CONTENT_TYPE']; $headers['content-type'] = $_SERVER['CONTENT_TYPE'];
$headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
foreach($_SERVER as $k => $v) { foreach($_SERVER as $k => $v) {
if(strpos($k,'HTTP_') === 0) { if(strpos($k,'HTTP_') === 0) {
@ -121,6 +123,17 @@ class HTTPSig {
if(array_key_exists($h,$headers)) { if(array_key_exists($h,$headers)) {
$signed_data .= $h . ': ' . $headers[$h] . "\n"; $signed_data .= $h . ': ' . $headers[$h] . "\n";
} }
if($h === 'date') {
$d = new \DateTime($headers[$h]);
$d->setTimeZone(new \DateTimeZone('UTC'));
$dplus = datetime_convert('UTC','UTC','now + 1 day');
$dminus = datetime_convert('UTC','UTC','now - 1 day');
$c = $d->format('Y-m-d H:i:s');
if($c > $dplus || $c < $dminus) {
logger('bad time: ' . $c);
return $result;
}
}
} }
$signed_data = rtrim($signed_data,"\n"); $signed_data = rtrim($signed_data,"\n");
@ -147,8 +160,15 @@ class HTTPSig {
logger('verified: ' . $x, LOGGER_DEBUG); logger('verified: ' . $x, LOGGER_DEBUG);
if(! $x) if(! $x) {
logger('verify failed for ' . $result['signer'] . ' alg=' . $algorithm . (($key['public_key']) ? '' : ' no key'));
$sig_block['signature'] = base64_encode($sig_block['signature']);
logger('affected sigblock: ' . print_r($sig_block,true));
logger('signed_data: ' . print_r($signed_data,true));
logger('headers: ' . print_r($headers,true));
logger('server: ' . print_r($_SERVER,true));
return $result; return $result;
}
$result['portable_id'] = $key['portable_id']; $result['portable_id'] = $key['portable_id'];
$result['header_valid'] = true; $result['header_valid'] = true;
@ -180,7 +200,9 @@ class HTTPSig {
return [ 'public_key' => $key ]; return [ 'public_key' => $key ];
} }
$key = self::get_webfinger_key($id); if(strpos($id,'#') === false) {
$key = self::get_webfinger_key($id);
}
if(! $key) { if(! $key) {
$key = self::get_activitystreams_key($id); $key = self::get_activitystreams_key($id);
@ -216,25 +238,29 @@ class HTTPSig {
function get_activitystreams_key($id) { function get_activitystreams_key($id) {
// remove fragment
$url = ((strpos($id,'#')) ? substr($id,0,strpos($id,'#')) : $id);
$x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' limit 1", $x = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' or hubloc_id_url = '%s' limit 1",
dbesc(str_replace('acct:','',$id)), dbesc(str_replace('acct:','',$url)),
dbesc($id) dbesc($url)
); );
if($x && $x[0]['xchan_pubkey']) { if($x && $x[0]['xchan_pubkey']) {
return [ 'portable_id' => $x[0]['xchan_hash'], 'public_key' => $x[0]['xchan_pubkey'] , 'hubloc' => $x[0] ]; return [ 'portable_id' => $x[0]['xchan_hash'], 'public_key' => $x[0]['xchan_pubkey'] , 'hubloc' => $x[0] ];
} }
$r = ActivityStreams::fetch_property($id); $r = ActivityStreams::fetch($id);
if($r) { if($r) {
if(array_key_exists('publicKey',$j) && array_key_exists('publicKeyPem',$j['publicKey']) && array_key_exists('id',$j['publicKey'])) { if(array_key_exists('publicKey',$r) && array_key_exists('publicKeyPem',$r['publicKey']) && array_key_exists('id',$r['publicKey'])) {
if($j['publicKey']['id'] === $id || $j['id'] === $id) { if($r['publicKey']['id'] === $id || $r['id'] === $id) {
return [ 'public_key' => self::convertKey($j['publicKey']['publicKeyPem']), 'portable_id' => '', 'hubloc' => [] ]; $portable_id = ((array_key_exists('owner',$r['publicKey'])) ? $r['publicKey']['owner'] : EMPTY_STR);
return [ 'public_key' => self::convertKey($r['publicKey']['publicKeyPem']), 'portable_id' => $portable_id, 'hubloc' => [] ];
} }
} }
} }
return false; return false;
} }
@ -409,6 +435,8 @@ class HTTPSig {
$headers = ''; $headers = '';
$fields = ''; $fields = '';
logger('signing: ' . print_r($head,true), LOGGER_DATA);
if($head) { if($head) {
foreach($head as $k => $v) { foreach($head as $k => $v) {
$headers .= strtolower($k) . ': ' . trim($v) . "\n"; $headers .= strtolower($k) . ': ' . trim($v) . "\n";

View File

@ -4,7 +4,7 @@ namespace Zotlabs\Zot6;
use Zotlabs\Lib\Config; use Zotlabs\Lib\Config;
use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libzot;
use Zotlabs\Web\HTTPSig;
class Receiver { class Receiver {

View File

@ -58,11 +58,11 @@ class Zot6Handler implements IHandler {
* *
* @param array $sender * @param array $sender
* @param array $recipients * @param array $recipients
* @param array $hub
* *
* @return json_return_and_die() * @return array
*/ */
static function reply_refresh($sender, $recipients, $hub) {
static function reply_refresh($sender, $recipients,$hub) {
$ret = array('success' => false); $ret = array('success' => false);
if($recipients) { if($recipients) {
@ -71,17 +71,17 @@ class Zot6Handler implements IHandler {
foreach ($recipients as $recip) { foreach ($recipients as $recip) {
$r = q("select channel.*,xchan.* from channel $r = q("select channel.*,xchan.* from channel
left join xchan on channel_hash = xchan_hash left join xchan on channel_portable_id = xchan_hash
where channel_hash ='%s' limit 1", where xchan_hash ='%s' limit 1",
dbesc($recip) dbesc($recip)
); );
/// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false)); $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false));
} }
} }
else { else {
// system wide refresh // system wide refresh
/// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false)); $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false));
} }
@ -104,12 +104,11 @@ class Zot6Handler implements IHandler {
* this 'message_list' at the destination and split it into individual messages which are * this 'message_list' at the destination and split it into individual messages which are
* processed/delivered in order. * processed/delivered in order.
* *
*
* @param array $data * @param array $data
* @param array $hub
* @return array * @return array
*/ */
static function reply_message_request($data, $hub) {
static function reply_message_request($data,$hub) {
$ret = [ 'success' => false ]; $ret = [ 'success' => false ];
$message_id = EMPTY_STR; $message_id = EMPTY_STR;
@ -140,7 +139,7 @@ class Zot6Handler implements IHandler {
$arr = $data['recipients'][0]; $arr = $data['recipients'][0];
$c = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_hash = '%s' limit 1", $c = q("select * from channel left join xchan on channel_portable_id = xchan_hash where channel_portable_id = '%s' limit 1",
dbesc($arr['portable_id']) dbesc($arr['portable_id'])
); );
if (! $c) { if (! $c) {
@ -152,11 +151,10 @@ class Zot6Handler implements IHandler {
/* /*
* fetch the requested conversation * fetch the requested conversation
*/ */
/// @FIXME $sender_hash is undefined
$messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]); $messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]);
return (($messages) ? : [] ); return (($messages) ? : [] );
} }
static function rekey_request($sender,$data,$hub) { static function rekey_request($sender,$data,$hub) {
@ -218,10 +216,10 @@ class Zot6Handler implements IHandler {
* *
* @param array $sender * @param array $sender
* @param array $recipients * @param array $recipients
* @param array $hub
* *
* return json_return_and_die() * @return array
*/ */
static function reply_purge($sender, $recipients, $hub) { static function reply_purge($sender, $recipients, $hub) {
$ret = array('success' => false); $ret = array('success' => false);
@ -230,8 +228,8 @@ class Zot6Handler implements IHandler {
// basically this means "unfriend" // basically this means "unfriend"
foreach ($recipients as $recip) { foreach ($recipients as $recip) {
$r = q("select channel.*,xchan.* from channel $r = q("select channel.*,xchan.* from channel
left join xchan on channel_hash = xchan_hash left join xchan on channel_portable_id = xchan_hash
where channel_hash = '%s' and channel_guid_sig = '%s' limit 1", where channel_portable_id = '%s' limit 1",
dbesc($recip) dbesc($recip)
); );
if ($r) { if ($r) {
@ -258,9 +256,4 @@ class Zot6Handler implements IHandler {
return $ret; return $ret;
} }
} }

7
app/affinity.apd Normal file
View File

@ -0,0 +1,7 @@
version: 1
url: $baseurl/affinity
requires: local_channel
name: Affinity Tool
photo: icon:arrows-h
categories: Networking
desc: This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.

View File

@ -50,10 +50,10 @@ require_once('include/attach.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '3.8.9' ); define ( 'STD_VERSION', '4.0' );
define ( 'ZOT_REVISION', '6.0a' ); define ( 'ZOT_REVISION', '6.0a' );
define ( 'DB_UPDATE_VERSION', 1225 ); define ( 'DB_UPDATE_VERSION', 1230 );
define ( 'PROJECT_BASE', __DIR__ ); define ( 'PROJECT_BASE', __DIR__ );
@ -83,8 +83,8 @@ define ( 'DIRECTORY_REALM', 'RED_GLOBAL');
define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net'); define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net');
$DIRECTORY_FALLBACK_SERVERS = array( $DIRECTORY_FALLBACK_SERVERS = array(
'https://hubzilla.zottel.net', 'https://zotadel.net',
'https://zotadel.net' 'https://zotsite.net'
); );
@ -217,6 +217,7 @@ define ( 'PHOTO_PROFILE', 0x0001 );
define ( 'PHOTO_XCHAN', 0x0002 ); define ( 'PHOTO_XCHAN', 0x0002 );
define ( 'PHOTO_THING', 0x0004 ); define ( 'PHOTO_THING', 0x0004 );
define ( 'PHOTO_COVER', 0x0010 ); define ( 'PHOTO_COVER', 0x0010 );
define ( 'PHOTO_CACHE', 0x0020 );
define ( 'PHOTO_ADULT', 0x0008 ); define ( 'PHOTO_ADULT', 0x0008 );
define ( 'PHOTO_FLAG_OS', 0x4000 ); define ( 'PHOTO_FLAG_OS', 0x4000 );
@ -438,6 +439,7 @@ define ( 'TERM_OBJ_APP', 7 );
* various namespaces we may need to parse * various namespaces we may need to parse
*/ */
define ( 'PROTOCOL_ZOT', 'http://purl.org/zot/protocol' ); define ( 'PROTOCOL_ZOT', 'http://purl.org/zot/protocol' );
define ( 'PROTOCOL_ZOT6', 'http://purl.org/zot/protocol/6.0' );
define ( 'NAMESPACE_ZOT', 'http://purl.org/zot' ); define ( 'NAMESPACE_ZOT', 'http://purl.org/zot' );
define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' ); define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' );
define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' ); define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' );
@ -465,7 +467,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' );
define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' ); define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' );
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.2' ); define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.3' );
/** /**
* activity stream defines * activity stream defines
*/ */
@ -510,6 +512,7 @@ define ( 'ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood' );
define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' ); define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' );
define ( 'ACTIVITY_OBJ_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' ); define ( 'ACTIVITY_OBJ_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' );
define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' ); define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' );
define ( 'ACTIVITY_OBJ_ARTICLE', NAMESPACE_ACTIVITY_SCHEMA . 'article' );
define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' ); define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' );
define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' ); define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' ); define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
@ -574,6 +577,8 @@ define ( 'ITEM_TYPE_BUG', 4 );
define ( 'ITEM_TYPE_DOC', 5 ); define ( 'ITEM_TYPE_DOC', 5 );
define ( 'ITEM_TYPE_CARD', 6 ); define ( 'ITEM_TYPE_CARD', 6 );
define ( 'ITEM_TYPE_ARTICLE', 7 ); define ( 'ITEM_TYPE_ARTICLE', 7 );
//OSADA ITEM_TYPE_MAIL = 8
define ( 'ITEM_TYPE_CUSTOM', 9 );
define ( 'ITEM_IS_STICKY', 1000 ); define ( 'ITEM_IS_STICKY', 1000 );
@ -728,11 +733,11 @@ class App {
private static $perms = null; // observer permissions private static $perms = null; // observer permissions
private static $widgets = array(); // widgets for this page private static $widgets = array(); // widgets for this page
public static $config = array(); // config cache public static $config = array(); // config cache
public static $override_intltext_templates = array(); public static $override_intltext_templates = array();
public static $override_markup_templates = array(); public static $override_markup_templates = array();
public static $override_templateroot = null; public static $override_templateroot = null;
public static $override_helproot = null; public static $override_helproot = null;
public static $override_helpfiles = array(); public static $override_helpfiles = array();
public static $session = null; public static $session = null;
public static $groups; public static $groups;
@ -1828,8 +1833,6 @@ function proc_run(){
$args = func_get_args(); $args = func_get_args();
$newargs = array();
if(! count($args)) if(! count($args))
return; return;
@ -2277,7 +2280,7 @@ function construct_page() {
$cspheader = "Content-Security-Policy:"; $cspheader = "Content-Security-Policy:";
foreach ($cspsettings as $cspdirective => $csp) { foreach ($cspsettings as $cspdirective => $csp) {
if (!in_array($cspdirective,$validcspdirectives)) { if (!in_array($cspdirective,$validcspdirectives)) {
logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_DEBUG); logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_DEBUG);
continue; continue;
} }
$cspsettingsarray=array_unique($cspsettings[$cspdirective]); $cspsettingsarray=array_unique($cspsettings[$cspdirective]);
@ -2396,7 +2399,7 @@ function z_get_temp_dir() {
if(! $temp_dir) if(! $temp_dir)
$temp_dir = sys_get_temp_dir(); $temp_dir = sys_get_temp_dir();
return $upload_dir; return $temp_dir;
} }

340
composer.lock generated
View File

@ -63,16 +63,16 @@
}, },
{ {
"name": "bshaffer/oauth2-server-php", "name": "bshaffer/oauth2-server-php",
"version": "v1.10.0", "version": "v1.11.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/bshaffer/oauth2-server-php.git", "url": "https://github.com/bshaffer/oauth2-server-php.git",
"reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9" "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/d158878425392fe5a0cc34f15dbaf46315ae0ed9", "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/5a0c8000d4763b276919e2106f54eddda6bc50fa",
"reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9", "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -117,7 +117,7 @@
"oauth", "oauth",
"oauth2" "oauth2"
], ],
"time": "2017-11-15T01:41:02+00:00" "time": "2018-12-04T00:29:32+00:00"
}, },
{ {
"name": "commerceguys/intl", "name": "commerceguys/intl",
@ -251,16 +251,16 @@
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
"version": "4.8.0", "version": "4.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git", "url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "f9a879a068c68ff47b722de63f58bec79e448f9d" "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/f9a879a068c68ff47b722de63f58bec79e448f9d", "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/250d1bf45f80d15594fb6b316df777d6d4c97ad1",
"reference": "f9a879a068c68ff47b722de63f58bec79e448f9d", "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -311,7 +311,7 @@
"html", "html",
"markdown" "markdown"
], ],
"time": "2018-09-18T12:18:08+00:00" "time": "2018-12-24T17:21:44+00:00"
}, },
{ {
"name": "lukasreschke/id3parser", "name": "lukasreschke/id3parser",
@ -1925,32 +1925,33 @@
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "1.4.2", "version": "1.5.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/psr7.git", "url": "https://github.com/guzzle/psr7.git",
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" "reference": "9f83dded91781a01c63574e387eaa769be769115"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "reference": "9f83dded91781a01c63574e387eaa769be769115",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.4.0", "php": ">=5.4.0",
"psr/http-message": "~1.0" "psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5"
}, },
"provide": { "provide": {
"psr/http-message-implementation": "1.0" "psr/http-message-implementation": "1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.4-dev" "dev-master": "1.5-dev"
} }
}, },
"autoload": { "autoload": {
@ -1980,13 +1981,14 @@
"keywords": [ "keywords": [
"http", "http",
"message", "message",
"psr-7",
"request", "request",
"response", "response",
"stream", "stream",
"uri", "uri",
"url" "url"
], ],
"time": "2017-03-20T17:10:46+00:00" "time": "2018-12-04T20:46:45+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -2571,6 +2573,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"abandoned": true,
"time": "2018-02-07T06:47:59+00:00" "time": "2018-02-07T06:47:59+00:00"
}, },
{ {
@ -2827,16 +2830,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "7.4.4", "version": "7.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd" "reference": "c23d78776ad415d5506e0679723cb461d71f488f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b1be2c8530c4c29c3519a052c9fb6cee55053bbd", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c23d78776ad415d5506e0679723cb461d71f488f",
"reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd", "reference": "c23d78776ad415d5506e0679723cb461d71f488f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2857,7 +2860,7 @@
"phpunit/php-timer": "^2.0", "phpunit/php-timer": "^2.0",
"sebastian/comparator": "^3.0", "sebastian/comparator": "^3.0",
"sebastian/diff": "^3.0", "sebastian/diff": "^3.0",
"sebastian/environment": "^3.1 || ^4.0", "sebastian/environment": "^4.0",
"sebastian/exporter": "^3.1", "sebastian/exporter": "^3.1",
"sebastian/global-state": "^2.0", "sebastian/global-state": "^2.0",
"sebastian/object-enumerator": "^3.0.3", "sebastian/object-enumerator": "^3.0.3",
@ -2881,7 +2884,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "7.4-dev" "dev-master": "7.5-dev"
} }
}, },
"autoload": { "autoload": {
@ -2907,7 +2910,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2018-11-14T16:52:02+00:00" "time": "2018-12-12T07:20:32+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -3008,6 +3011,46 @@
], ],
"time": "2016-08-06T14:39:51+00:00" "time": "2016-08-06T14:39:51+00:00"
}, },
{
"name": "ralouphie/getallheaders",
"version": "2.0.5",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
"reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "~3.7.0",
"satooshi/php-coveralls": ">=1.0"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"time": "2016-02-11T07:05:27+00:00"
},
{ {
"name": "sebastian/code-unit-reverse-lookup", "name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.1", "version": "1.0.1",
@ -3175,28 +3218,28 @@
}, },
{ {
"name": "sebastian/environment", "name": "sebastian/environment",
"version": "3.1.0", "version": "4.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/environment.git", "url": "https://github.com/sebastianbergmann/environment.git",
"reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/febd209a219cea7b56ad799b30ebbea34b71eb8f",
"reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.0" "php": "^7.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6.1" "phpunit/phpunit": "^7.4"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.1.x-dev" "dev-master": "4.0-dev"
} }
}, },
"autoload": { "autoload": {
@ -3221,7 +3264,7 @@
"environment", "environment",
"hhvm" "hhvm"
], ],
"time": "2017-07-01T08:51:00+00:00" "time": "2018-11-25T09:31:21+00:00"
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
@ -3573,16 +3616,16 @@
}, },
{ {
"name": "symfony/browser-kit", "name": "symfony/browser-kit",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/browser-kit.git", "url": "https://github.com/symfony/browser-kit.git",
"reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd" "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/c55fe9257003b2d95c0211b3f6941e8dfd26dffd", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/db7e59fec9c82d45e745eb500e6ede2d96f4a6e9",
"reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd", "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3599,7 +3642,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3626,20 +3669,20 @@
], ],
"description": "Symfony BrowserKit Component", "description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-07-26T09:10:45+00:00" "time": "2018-11-26T11:49:31+00:00"
}, },
{ {
"name": "symfony/class-loader", "name": "symfony/class-loader",
"version": "v3.4.18", "version": "v3.4.20",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/class-loader.git", "url": "https://github.com/symfony/class-loader.git",
"reference": "5605edec7b8f034ead2497ff4aab17bb70d558c1" "reference": "420458095cf60025eb0841276717e0da7f75e50e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/5605edec7b8f034ead2497ff4aab17bb70d558c1", "url": "https://api.github.com/repos/symfony/class-loader/zipball/420458095cf60025eb0841276717e0da7f75e50e",
"reference": "5605edec7b8f034ead2497ff4aab17bb70d558c1", "reference": "420458095cf60025eb0841276717e0da7f75e50e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3682,20 +3725,20 @@
], ],
"description": "Symfony ClassLoader Component", "description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-31T09:06:03+00:00" "time": "2018-11-11T19:48:54+00:00"
}, },
{ {
"name": "symfony/config", "name": "symfony/config",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/config.git", "url": "https://github.com/symfony/config.git",
"reference": "991fec8bbe77367fc8b48ecbaa8a4bd6e905a238" "reference": "005d9a083d03f588677d15391a716b1ac9b887c0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/991fec8bbe77367fc8b48ecbaa8a4bd6e905a238", "url": "https://api.github.com/repos/symfony/config/zipball/005d9a083d03f588677d15391a716b1ac9b887c0",
"reference": "991fec8bbe77367fc8b48ecbaa8a4bd6e905a238", "reference": "005d9a083d03f588677d15391a716b1ac9b887c0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3718,7 +3761,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3745,24 +3788,25 @@
], ],
"description": "Symfony Config Component", "description": "Symfony Config Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-31T09:09:42+00:00" "time": "2018-11-30T22:21:14+00:00"
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "432122af37d8cd52fba1b294b11976e0d20df595" "reference": "4dff24e5d01e713818805c1862d2e3f901ee7dd0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/432122af37d8cd52fba1b294b11976e0d20df595", "url": "https://api.github.com/repos/symfony/console/zipball/4dff24e5d01e713818805c1862d2e3f901ee7dd0",
"reference": "432122af37d8cd52fba1b294b11976e0d20df595", "reference": "4dff24e5d01e713818805c1862d2e3f901ee7dd0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1.3", "php": "^7.1.3",
"symfony/contracts": "^1.0",
"symfony/polyfill-mbstring": "~1.0" "symfony/polyfill-mbstring": "~1.0"
}, },
"conflict": { "conflict": {
@ -3786,7 +3830,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3813,20 +3857,88 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-31T09:30:44+00:00" "time": "2018-11-27T07:40:44+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/contracts",
"version": "v3.4.18", "version": "v1.0.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/css-selector.git", "url": "https://github.com/symfony/contracts.git",
"reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb" "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/3503415d4aafabc31cd08c3a4ebac7f43fde8feb", "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf",
"reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb", "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf",
"shasum": ""
},
"require": {
"php": "^7.1.3"
},
"require-dev": {
"psr/cache": "^1.0",
"psr/container": "^1.0"
},
"suggest": {
"psr/cache": "When using the Cache contracts",
"psr/container": "When using the Service contracts",
"symfony/cache-contracts-implementation": "",
"symfony/service-contracts-implementation": "",
"symfony/translation-contracts-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\": ""
},
"exclude-from-classmap": [
"**/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A set of abstractions extracted out of the Symfony components",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"time": "2018-12-05T08:06:11+00:00"
},
{
"name": "symfony/css-selector",
"version": "v3.4.20",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "345b9a48595d1ab9630db791dbc3e721bf0233e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/345b9a48595d1ab9630db791dbc3e721bf0233e8",
"reference": "345b9a48595d1ab9630db791dbc3e721bf0233e8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3866,37 +3978,39 @@
], ],
"description": "Symfony CssSelector Component", "description": "Symfony CssSelector Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-02T16:33:53+00:00" "time": "2018-11-11T19:48:54+00:00"
}, },
{ {
"name": "symfony/dependency-injection", "name": "symfony/dependency-injection",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dependency-injection.git", "url": "https://github.com/symfony/dependency-injection.git",
"reference": "e72ee2c23d952e4c368ee98610fa22b79b89b483" "reference": "e4adc57a48d3fa7f394edfffa9e954086d7740e5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e72ee2c23d952e4c368ee98610fa22b79b89b483", "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e4adc57a48d3fa7f394edfffa9e954086d7740e5",
"reference": "e72ee2c23d952e4c368ee98610fa22b79b89b483", "reference": "e4adc57a48d3fa7f394edfffa9e954086d7740e5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1.3", "php": "^7.1.3",
"psr/container": "^1.0" "psr/container": "^1.0",
"symfony/contracts": "^1.0"
}, },
"conflict": { "conflict": {
"symfony/config": "<4.1.1", "symfony/config": "<4.2",
"symfony/finder": "<3.4", "symfony/finder": "<3.4",
"symfony/proxy-manager-bridge": "<3.4", "symfony/proxy-manager-bridge": "<3.4",
"symfony/yaml": "<3.4" "symfony/yaml": "<3.4"
}, },
"provide": { "provide": {
"psr/container-implementation": "1.0" "psr/container-implementation": "1.0",
"symfony/service-contracts-implementation": "1.0"
}, },
"require-dev": { "require-dev": {
"symfony/config": "~4.1", "symfony/config": "~4.2",
"symfony/expression-language": "~3.4|~4.0", "symfony/expression-language": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0" "symfony/yaml": "~3.4|~4.0"
}, },
@ -3910,7 +4024,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3937,20 +4051,20 @@
], ],
"description": "Symfony DependencyInjection Component", "description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-31T10:54:16+00:00" "time": "2018-12-02T15:59:36+00:00"
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dom-crawler.git", "url": "https://github.com/symfony/dom-crawler.git",
"reference": "80e60271bb288de2a2259662cff125cff4f93f95" "reference": "7438a32108fdd555295f443605d6de2cce473159"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/80e60271bb288de2a2259662cff125cff4f93f95", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7438a32108fdd555295f443605d6de2cce473159",
"reference": "80e60271bb288de2a2259662cff125cff4f93f95", "reference": "7438a32108fdd555295f443605d6de2cce473159",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3967,7 +4081,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3994,24 +4108,25 @@
], ],
"description": "Symfony DomCrawler Component", "description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-02T12:40:59+00:00" "time": "2018-11-26T10:55:26+00:00"
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "552541dad078c85d9414b09c041ede488b456cd5" "reference": "921f49c3158a276d27c0d770a5a347a3b718b328"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/552541dad078c85d9414b09c041ede488b456cd5", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/921f49c3158a276d27c0d770a5a347a3b718b328",
"reference": "552541dad078c85d9414b09c041ede488b456cd5", "reference": "921f49c3158a276d27c0d770a5a347a3b718b328",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1.3" "php": "^7.1.3",
"symfony/contracts": "^1.0"
}, },
"conflict": { "conflict": {
"symfony/dependency-injection": "<3.4" "symfony/dependency-injection": "<3.4"
@ -4030,7 +4145,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4057,20 +4172,20 @@
], ],
"description": "Symfony EventDispatcher Component", "description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-10T13:52:42+00:00" "time": "2018-12-01T08:52:38+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/filesystem.git", "url": "https://github.com/symfony/filesystem.git",
"reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981" "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/fd7bd6535beb1f0a0a9e3ee960666d0598546981", "url": "https://api.github.com/repos/symfony/filesystem/zipball/2f4c8b999b3b7cadb2a69390b01af70886753710",
"reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981", "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4080,7 +4195,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4107,7 +4222,7 @@
], ],
"description": "Symfony Filesystem Component", "description": "Symfony Filesystem Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-30T13:18:25+00:00" "time": "2018-11-11T19:52:12+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
@ -4170,20 +4285,21 @@
}, },
{ {
"name": "symfony/translation", "name": "symfony/translation",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/translation.git", "url": "https://github.com/symfony/translation.git",
"reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c" "reference": "c0e2191e9bed845946ab3d99767513b56ca7dcd6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", "url": "https://api.github.com/repos/symfony/translation/zipball/c0e2191e9bed845946ab3d99767513b56ca7dcd6",
"reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", "reference": "c0e2191e9bed845946ab3d99767513b56ca7dcd6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1.3", "php": "^7.1.3",
"symfony/contracts": "^1.0.2",
"symfony/polyfill-mbstring": "~1.0" "symfony/polyfill-mbstring": "~1.0"
}, },
"conflict": { "conflict": {
@ -4191,6 +4307,9 @@
"symfony/dependency-injection": "<3.4", "symfony/dependency-injection": "<3.4",
"symfony/yaml": "<3.4" "symfony/yaml": "<3.4"
}, },
"provide": {
"symfony/translation-contracts-implementation": "1.0"
},
"require-dev": { "require-dev": {
"psr/log": "~1.0", "psr/log": "~1.0",
"symfony/config": "~3.4|~4.0", "symfony/config": "~3.4|~4.0",
@ -4208,7 +4327,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4235,20 +4354,20 @@
], ],
"description": "Symfony Translation Component", "description": "Symfony Translation Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-28T18:38:52+00:00" "time": "2018-12-06T10:45:32+00:00"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v4.1.7", "version": "v4.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/yaml.git",
"reference": "367e689b2fdc19965be435337b50bc8adf2746c9" "reference": "c41175c801e3edfda90f32e292619d10c27103d7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/367e689b2fdc19965be435337b50bc8adf2746c9", "url": "https://api.github.com/repos/symfony/yaml/zipball/c41175c801e3edfda90f32e292619d10c27103d7",
"reference": "367e689b2fdc19965be435337b50bc8adf2746c9", "reference": "c41175c801e3edfda90f32e292619d10c27103d7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4267,7 +4386,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4294,7 +4413,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-10-02T16:36:10+00:00" "time": "2018-11-11T19:52:12+00:00"
}, },
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
@ -4338,20 +4457,21 @@
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
"version": "1.3.0", "version": "1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/webmozart/assert.git", "url": "https://github.com/webmozart/assert.git",
"reference": "0df1908962e7a3071564e857d86874dad1ef204a" "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
"reference": "0df1908962e7a3071564e857d86874dad1ef204a", "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^5.3.3 || ^7.0" "php": "^5.3.3 || ^7.0",
"symfony/polyfill-ctype": "^1.8"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.6", "phpunit/phpunit": "^4.6",
@ -4384,7 +4504,7 @@
"check", "check",
"validate" "validate"
], ],
"time": "2018-01-29T19:49:41+00:00" "time": "2018-12-25T11:19:39+00:00"
} }
], ],
"aliases": [], "aliases": [],

View File

@ -95,7 +95,7 @@ PHP might differ from the _webserver_ version
php.ini file * and with no hosting provider restrictions on the use of php.ini file * and with no hosting provider restrictions on the use of
exec() and proc_open(). exec() and proc_open().
* curl, gd (with at least jpeg and png support), mysqli, mbstring, mcrypt, * curl, gd (with at least jpeg and png support), mysqli, mbstring, mcrypt, zip,
and openssl extensions. The imagick extension is not required but desirable. and openssl extensions. The imagick extension is not required but desirable.
* xml extension is required if you want webdav to work. * xml extension is required if you want webdav to work.

View File

@ -21,7 +21,7 @@ If you get a blank white screen when doing something, this is almost always a co
[h3]I'm stumped. I can't figure out what is wrong.[/h3] [h3]I'm stumped. I can't figure out what is wrong.[/h3]
At this point it might be worthwhile discussing the issue on one of the online forums. There may be several of these and some may be more suited to your spoken language. At this time, the 'Hubzilla Support' channel (support@gravizot.de) is the recommended forum for discussing bugs. At this point it might be worthwhile discussing the issue on one of the online forums. There may be several of these and some may be more suited to your spoken language. At this time, the 'Hubzilla Support' channel (support@zotadel.net) is the recommended forum for discussing bugs.
If community members with software engineering training/expertise can't help you right away, understand that they are volunteers and may have a lot of other work and demands on their time. At this point you need to file a bug report. You will need an account on framagit.org to do this. So register, and then visit https://framagit.org/hubzilla/core/issues . Create an issue here and provide all the same information that you provided online. Don't leave out anything. If community members with software engineering training/expertise can't help you right away, understand that they are volunteers and may have a lot of other work and demands on their time. At this point you need to file a bug report. You will need an account on framagit.org to do this. So register, and then visit https://framagit.org/hubzilla/core/issues . Create an issue here and provide all the same information that you provided online. Don't leave out anything.

View File

@ -0,0 +1,42 @@
[h2]collect_public_recipients[/h2]
Replace the default list of public recipients (i.e., all contacts).
Allow plugins to create a list of recipients for public messages instead of the default
of all channel connections.
Called with the following array:
[
'recipients' => [],
'item' => $item,
'private_envelope' => $private_envelope,
'include_groups' => $include_groups
];
[code]
if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') {
$hookinfo = [
'recipients' => [],
'item' => $item,
'private_envelope' => $private_envelope,
'include_groups' => $include_groups
];
call_hooks('collect_public_recipients',$hookinfo);
if ($hookinfo['recipients']) {
$r = $hookinfo['recipients'];
} else {
$r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 ",
intval($item['uid'])
);
}
if($r) {
. . .
[/code]
see: include/item.php

View File

@ -0,0 +1,5 @@
[h2]daemon_master_release[/h2]
Permit filtering or alternate methods of processing of background processes when [code] \Zotlabs\Daemon\Master::Release() [/code] is called.
Default behavior is for a new PHP process to fire immediately upon a call to Master::Summon(). This hook permits pre-emption and the ability to provide queuing or other alternatives to this procedure.

24
doc/hook/item_custom.bb Normal file
View File

@ -0,0 +1,24 @@
[h2]item_custom[/h2]
Allow addons to create and process custom item types.
Addon authors will need to use iconfig meta data (with sharing on) or some other method
to specify and determine whether the custom item is destined for their addon.
It is fed an array of ['item' => ${item_array}, 'allow_exec' => {true/false}]
By default $arr['item']['cancel'] is set to TRUE which will abort storage of the
custom item in the item table unless the addon unsets it or sets it to false.
[code]
if ($arr['item_type']==ITEM_TYPE_CUSTOM) {
/* Custom items are not stored by default
because they require an addon to process. */
$d['item']['cancel']=true;
call_hooks('item_custom',$d);
}
[/code]
see: include/items.php

18
doc/hook/item_stored.bb Normal file
View File

@ -0,0 +1,18 @@
[h2]item_stored[/h2]
Allow addons to continue processing after an item has been stored in the event
that they need access to the item_id or other data that gets assigned during
the storage process.
It is fed an array of type item (including terms and iconfig data).
[code]
/**
* @hooks item_stored
* Called after new item is stored in the database.
* (By this time we have an item_id and other frequently needed info.)
*/
call_hooks('item_stored',$arr);
[/code]
see: include/items.php

View File

@ -0,0 +1,15 @@
[h2]item_stored_update[/h2]
Allow addons to continue processing after an item update has been stored
It is fed an array of type item (including terms and iconfig data).
[code]
/**
* @hooks item_stored_update
* Called after updated item is stored in the database.
*/
call_hooks('item_stored_update',$arr);
[/code]
see: include/items.php

View File

@ -0,0 +1,5 @@
[h2]jot_header_tpl_filter[/h2]
Allows addon developers to modify the values of replacements fed into jot-header.tpl
cxref: include/conversation.php

View File

@ -0,0 +1,5 @@
[h2]jot_tpl_filter[/h2]
Allows addon developers to alter the macro replacements prior to being fed into jot.tpl
cxref: include/conversation.php

View File

@ -0,0 +1,12 @@
[h2]privacygroup_extras[/h2]
Add items to the Privacy Group edit form
[code]
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(1) ];
call_hooks ('privacygroup_extras',$hookinfo);
$pgrp_extras = $hookinfo['pgrp_extras'];
[/code]
see: Zotlabs/Module/Group.php
see: view/tpl/privacy_groups.tpl

View File

@ -0,0 +1,11 @@
[h2]privacygroup_extras_drop[/h2]
Called after privacy group is dropped
[code]
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(2) ];
call_hooks ('privacygroup_extras_drop',$hookinfo);
[/code]
see: Zotlabs/Module/Group.php
see: view/tpl/privacy_groups.tpl

View File

@ -0,0 +1,11 @@
[h2]privacygroup_extras_post[/h2]
Called as privacy group edit form is edited.
[code]
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras_post',$hookinfo);
[/code]
see: Zotlabs/Module/Group.php
see: view/tpl/privacy_groups.tpl

View File

@ -136,6 +136,9 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/check_siteallowed]check_siteallowed[/zrl] [zrl=[baseurl]/help/hook/check_siteallowed]check_siteallowed[/zrl]
Used to over-ride or bypass the site black/white block lists Used to over-ride or bypass the site black/white block lists
[zrl=[baseurl]/help/hook/collect_public_recipients]collect_public_recipients[/zrl]
Used to establish a list of recipients to send a public message to.
[zrl=[baseurl]/help/hook/comment_buttons]comment_buttons[/zrl] [zrl=[baseurl]/help/hook/comment_buttons]comment_buttons[/zrl]
Called when rendering the edit buttons for comments Called when rendering the edit buttons for comments
@ -190,6 +193,9 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/daemon_addon]daemon_addon[/zrl] [zrl=[baseurl]/help/hook/daemon_addon]daemon_addon[/zrl]
Called when invoking the extensible background daemon Called when invoking the extensible background daemon
[zrl=[baseurl]/help/hook/daemon_master_release]daemon_master_release[/zrl]
Called at the start of processing \Zotlabs\Daemon\Master::Release()
[zrl=[baseurl]/help/hook/directory_item]directory_item[/zrl] [zrl=[baseurl]/help/hook/directory_item]directory_item[/zrl]
Called when generating a directory listing for display Called when generating a directory listing for display
@ -343,9 +349,18 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/item_store]item_store[/zrl] [zrl=[baseurl]/help/hook/item_store]item_store[/zrl]
Called when item_store() stores a record of type item Called when item_store() stores a record of type item
[zrl=[baseurl]/help/hook/item_stored]item_stored[/zrl]
Called after item_store() has stored a record of type item in the database.
[zrl=[baseurl]/help/hook/item_custom]item_custom[/zrl]
Called before item_store() stores a record of type item (allowing addons to process ITEM_TYPE_CUSTOM items).
[zrl=[baseurl]/help/hook/item_store_update]item_store_update[/zrl] [zrl=[baseurl]/help/hook/item_store_update]item_store_update[/zrl]
Called when item_store_update() is called to update a stored item. Called when item_store_update() is called to update a stored item.
[zrl=[baseurl]/help/hook/item_stored_update]item_stored_update[/zrl]
Called after item_store_update() has updated a stored item.
[zrl=[baseurl]/help/hook/item_translate]item_translate[/zrl] [zrl=[baseurl]/help/hook/item_translate]item_translate[/zrl]
Called from item_store and item_store_update after the post language has been autodetected Called from item_store and item_store_update after the post language has been autodetected
@ -355,6 +370,12 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/jot_tool]jot_tool[/zrl] [zrl=[baseurl]/help/hook/jot_tool]jot_tool[/zrl]
Deprecated and possibly obsolete. Allows one to add action buttons to the post editor. Deprecated and possibly obsolete. Allows one to add action buttons to the post editor.
[zrl=[baseurl]/help/hook/jot_tpl_filter]jot_tpl_filter[/zrl]
Called to filter template vars before replacement in jot.tpl.
[zrl=[baseurl]/help/hook/jot_header_tpl_filter]jot_header_tpl_filter[/zrl]
Called to filter template vars before replacement in jot_header.tpl.
[zrl=[baseurl]/help/hook/legal_webbie]legal_webbie[/zrl] [zrl=[baseurl]/help/hook/legal_webbie]legal_webbie[/zrl]
Called to validate a channel address Called to validate a channel address
@ -544,6 +565,15 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/prepare_body_init]prepare_body_init[/zrl] [zrl=[baseurl]/help/hook/prepare_body_init]prepare_body_init[/zrl]
Called before generating the HTML for a displayed conversation item Called before generating the HTML for a displayed conversation item
[zrl=[baseurl]/help/hook/privacygroup_extras]privacygroup_extras[/zrl]
Called before generating the HTML for the Privacy Group edit options
[zrl=[baseurl]/help/hook/privacygroup_extras_delete]privacygroup_extras_delete[/zrl]
Called after privacy group is dropped.
[zrl=[baseurl]/help/hook/privacygroup_extras_post]privacygroup_extras_post[/zrl]
Called when privacy group edit form is submitted.
[zrl=[baseurl]/help/hook/proc_run]proc_run[/zrl] [zrl=[baseurl]/help/hook/proc_run]proc_run[/zrl]
Called when invoking PHP sub processes Called when invoking PHP sub processes

Some files were not shown because too many files have changed in this diff Show More