Compare commits

..

16 Commits
11.2RC ... 8.0

Author SHA1 Message Date
Mario
2805520d1b Merge remote-tracking branch 'origin/8.0RC' 2023-01-13 20:01:05 +00:00
Mario
fb7ca18820 version 8.0 2023-01-13 19:56:11 +00:00
Mario
1b00d5657f Merge branch 'dev' into 8.0RC 2023-01-13 19:55:44 +00:00
Mario
9ec516e5a4 Merge branch 'dev' into 8.0RC 2023-01-12 16:12:26 +00:00
Mario
5cb92b6e21 Merge branch 'dev' into 8.0RC 2023-01-12 11:55:13 +00:00
Mario
2ddb88a34d RC2 2023-01-11 19:57:35 +00:00
Mario
71761c9039 Merge branch 'dev' into 8.0RC 2023-01-11 16:35:21 +00:00
Mario
bd5e834b42 Merge branch 'dev' into 8.0RC 2023-01-08 08:20:05 +00:00
Mario
4c434129a6 Merge branch 'dev' into 8.0RC 2023-01-05 18:25:45 +01:00
Mario
66333aedb7 fix version 2023-01-05 18:23:00 +01:00
Mario
f6d9406063 streamline ap followers only privacy warning with current handling. also fixes possible php error.
(cherry picked from commit 34125177e8)
2022-12-19 10:05:02 +01:00
Mario
6e881bcef2 exclude not tagable xchan networks handle_tag()
(cherry picked from commit 4f9a933108)
2022-12-08 21:41:56 +01:00
Mario
12a963cc40 version 7.8.7 2022-12-03 09:01:18 +00:00
Mario
f89975fd0e changelog 7.8.7
(cherry picked from commit b4dffe5946)
2022-12-03 09:59:06 +01:00
Mario
00512579f3 fix regression when adding feed contacts
(cherry picked from commit 5216c5b232)
2022-12-02 18:47:40 +01:00
Mario
697a74f37e fix regression: new event not created
(cherry picked from commit d16b6c3838)
2022-12-02 18:06:20 +01:00
11454 changed files with 376556 additions and 821563 deletions

View File

@@ -1,156 +0,0 @@
# How to use
## Disclaimers
- **This script does work with a fresh install of Debian 12 only**.
- Do not use if you have already installed and configured a webserver or sql server that was not installed by this script.
### Keep it Simple and Stupid
The script keeps everything as simple as possible (KISS):
- Apache as webserver (there is no choice to use another webserver like nginx)
- default PHP version of Debian
- one single Hubzilla intance only
- re-running the script does no harm
### When to use other Scripts
Use the scripts under [homeinstall](https://framagit.org/hubzilla/core/-/tree/master/.homeinstall)
if you look for more choices. The main differences are:
- Apache or nginx as webserver
- install multiple instances (domains) that run side by side on the server
- adds apache vhosts (instead of using the standard doc root /var/www/html)
- install PHP from https://packages.sury.org/php/ (instead of using the Debian repository)
- graphical installer whiptail
- The script stops (fails) if it finds results of a previous installation. (The [debian-setup.sh](https://framagit.org/ojrandom/core/-/tree/dev/.debianinstall) will just jump over it.)
- If something fails the script tries to clean up everything that was installed up to the point of failure. (That might cause trouble if certbot registered a certificate already.)
- The script under [homeinstall](https://framagit.org/hubzilla/core/-/tree/master/.homeinstall) seems to be an older version of the scripts used for Streams, i.e. [autoinstall](https://codeberg.org/streams/streams/src/branch/dev/contrib/autoinstall) and [easyinstall](https://codeberg.org/streams/streams/src/branch/dev/contrib/easyinstall)
## Preconditions
Hardware
+ internet connection and router at home
+ computer connected to your router (a Raspberry 4 will do for very small Hubs)
Software
+ fresh installation of Debian 12 (bookworm) or Raspberry Pi OS
+ router with open ports 80 and 443 for your web server
You can of course run the script on a VPS or any distant server as long as the above software requirements are satisfied.
## How to run the script
+ Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS)
+ Log on to your fresh Debian
- apt-get install git
- mkdir -p /var/www
- cd /var/www
- git clone https://framagit.org/hubzilla/core.git html
- cd html/.debianinstall
- cp config.txt.template config.txt
- nano config.txt
- read the comments carefully
- enter your values: db pass, domain
- (optionally) Enter your values for dyn DNS
- ./debian-setup.sh as root
- ... wait, wait, wait until the script is finished
+ Open your domain with a browser and step throught the initial configuration of your hubzilla instance.
- default database name = hubzilla
- default dababase user = hubzilla
## Optional - Switch verification of email on/off
Do this just before you register the first user without email verification.
In a terminal
su -
cd /var/www/html
Check the current setting
util/config system verify_email
Switch the verification off
util/config system verify_email 0
Check if updates from the repository do work
util/udall
## What the script will do for you...
+ install everything required by your hubzilla instance, basically a web server (Apache), PHP, a database (MySQL), certbot,...
+ create a database
+ run certbot to have everything for a secure connection (httpS)
+ create a script for daily maintenance
- renew certfificate (letsencrypt)
- update of your hubzilla instance for core and addons (git)
- update of Debian
- restart
+ create cron jobs for
- DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes
- Master.php for your hubzilla instance every 10 minutes
- daily maintenance script every day at 05:30
The script is known to work without adjustments with
+ Hardware
- standard PC with Debian 12 (bookworm)
- Raspberry 5 with Raspberry Pi OS, Debian 12
- for tesing purposes: under localhost inside a virtual machine, [KVM](https://wiki.debian.org/KVM)
+ DynDNS
- selfHOST.de
- freedns.afraid.org
# Step-by-Step - some Details
## Preparations
## Configure your Router
Your webserver has to be visible in the internet.
Open the ports 80 and 443 on your router for your Debian. Make sure your web server is marked as "exposed host".
## Preparations Dynamic IP Address
Follow the instructions in .debianinstall/config.txt.
In short...
Your Hubzilla server must be reachable by a domain that you can type in your browser
cooldomain.org
You can use subdomains as well
my.cooldomain.org
There are two ways to get a domain...
### Method 1: Buy a Domain
...for example buy at selfHOST.de
The cost is 1,50 € per month (2019).
### Method 2: Register a free subdomain
...for example register at freedns.afraid.org
## Note on Rasperry
It is recommended to run the Raspi without graphical frontend (X-Server). Use...
sudo raspi-config
to boot the Rapsi to the client console.

View File

@@ -1,110 +0,0 @@
###############################################
### MANDATORY - database password #############
#
# Please give your database password
# It is better to not use blanks inside the password.
# Example: db_pass=pass_word_with_no_blanks_in_it
db_pass=
###############################################
### MANDATORY - let's encrypt #################
#
# Zot requires encrypted communication via secure HTTP (HTTPS).
# This script automates installation of an SSL certificate from
# Let's Encrypt (https://letsencrypt.org)
#
# Please give the domain name of your hub/instance
#
# Example: my.cooldomain.org
# Example: cooldomain.org
#
# You might use "localhost" for a LOCAL TEST installation.
# This is usefull if you want to debug the server inside a VM.
#
# Example: localhost
#
# Email is optional if you use "localhost".
#
#
le_domain=
le_email=
###############################################
### OPTIONAL - selfHOST - dynamic IP address ##
#
# 1. Register a domain at selfhost.de
# - choose offer "DOMAIN dynamisch" 1,50€/mon at 04/2019
# 2. Get your configuration for dynamic IP update
# - Log in at selfhost.de
# - go to "DynDNS Accounte"
# - klick "Details" of your (freshly) registered domain
# - You will find the configuration there
# - Benutzername (user name) > use this for "selfhost_user="
# - Passwort (pass word) > use this for "selfhost_pass="
#
#
selfhost_user=
selfhost_pass=
###############################################
### OPTIONAL - FreeDNS - dynamic IP address ###
#
# Please give the alpha-numeric-key of freedns
#
# Get a free subdomain from freedns and use it for your dynamic ip address
# Documentation under http://www.techjawab.com/2013/06/setup-dynamic-dns-dyndns-for-free-on.html
#
# - Register for a Free domain at http://freedns.afraid.org/signup/
# - WATCH THIS: Make sure you choose a domain with as less subdomains as
# possible. Why? Let's encrpyt issues a limited count of certificates each
# day. Possible other users of this domain will try to issue a certificate
# at the same day.
# - Logon to FreeDNS (where you just registered)
# - Goto http://freedns.afraid.org/dynamic/
# - Right click on "Direct Link" and copy the URL and paste it somewhere.
# - You should notice a large and unique alpha-numeric key in the URL
#
# http://freedns.afraid.org/dynamic/update.php?alpha-numeric-key
#
# Provided your url from freedns is
#
# http://freedns.afraid.org/dynamic/update.php?U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
#
# Then you have to provide
#
# freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
#
#
freedns_key=
###############################################
### OPTIONAL - do not mess with things below ##
# (...if you are not certain)
#
# Usually you are done here
# Everything below is OPTIONAL
#
###############################################
#
# Database for your hub/instance
# If left empty, both your database and user will be named after your zot instance (hubzilla, zap or misty)
# Use custom name, at least fo the database, if you plan to run more than one hub/instance on the same server
#
db_name=hubzilla
db_user=hubzilla
#
#
# Password for package mysql-server
# Example: mysqlpass=aberhallo
# Example: mysqlpass="aber hallo has blanks in it"
#
mysqlpass=$db_pass
# Password for package phpmyadmin
# Example: phpmyadminpass=aberhallo
# Example: phpmyadminpass="aber hallo has blanks in it"
phpmyadminpass=$db_pass

View File

@@ -1,543 +0,0 @@
#!/bin/bash
#
# How to use
# ----------
#
# This file automates the installation of hubzilla: https://framagit.org/hubzilla/core
# under Debian Linux "bookworm"
#
# 1) Copy the file "config.txt.template" to "config.txt"
# Follow the instuctions there
#
# 2) Switch to user "root" by typing "su -"
#
# 3) Run with "./debian-setup.sh"
# If this fails check if you can execute the script.
# - To make it executable type "chmod +x debian-setup.sh"
# - or run "bash debian-setup.sh"
#
#
# What does this script do basically?
# -----------------------------------
#
# This file automates the installation of a Hubzilla instance under Debian Linux
# - install
# * apache webserver,
# * php,
# * mariadb,
# * adminer,
# * addons
# - configure cron
# * "Master.php" for regular background processes of your hubzilla instance
# * "apt-get update" and "apt-get dist-upgrade" and "apt-get autoremove" to keep linux up-to-date
# * run command to keep the IP up-to-date > DynDNS provided by selfHOST.de or freedns.afraid.org
# - run letsencrypt to create, register and use a certifacte for https
#
#
# Credits
# -------
#
# The script is derived from the easyinstall script of the Streams repository, which is based on
# - Tom Wiedenhöfts (OJ Random) script homeinstall (for Hubzilla, ZAP,...) that was based on
# - Thomas Willinghams script "debian-setup.sh" which he used to install the red#matrix.
function check_sanity {
# Do some sanity checking.
print_info "Sanity check..."
if [ $(/usr/bin/id -u) != "0" ]
then
die 'Must be run by root user'
fi
if [ -f /etc/lsb-release ]
then
die "Distribution is not supported"
fi
if [ ! -f /etc/debian_version ]
then
die "Debian is supported only"
fi
if ! grep -q 'Linux 12' /etc/issue
then
die "Linux 12 (bookworm) is supported only"x
fi
}
function check_config {
print_info "config check..."
# Check for required parameters
if [ -z "$db_pass" ]
then
die "db_pass not set in $configfile"
fi
if [ -z "$le_domain" ]
then
die "le_domain not set in $configfile"
fi
}
function die {
echo "ERROR: $1" > /dev/null 1>&2
exit 1
}
function update_upgrade {
print_info "updated and upgrade..."
# Run through the apt-get update/upgrade first. This should be done before
# we try to install any package
apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove
print_info "updated and upgraded linux"
}
function nocheck_install {
# export DEBIAN_FRONTEND=noninteractive ... answers from the package configuration database
# - q ... without progress information
# - y ... answer interactive questions with "yes"
# DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -q -y install $2
# DEBIAN_FRONTEND=noninteractive apt-get --install-suggests -q -y install $1
DEBIAN_FRONTEND=noninteractive apt-get -q -y install $1
print_info "installed $1"
}
function print_info {
echo -n -e '\e[1;34m'
echo -n $1
echo -e '\e[0m'
}
function print_warn {
echo -n -e '\e[1;31m'
echo -n $1
echo -e '\e[0m'
}
function stop_zotserver {
print_info "stopping apache..."
systemctl stop apache2
print_info "stopping mysql db..."
systemctl stop mariadb
}
function install_apache {
print_info "installing apache..."
nocheck_install "apache2 apache2-utils"
a2enmod rewrite
systemctl restart apache2
}
function install_imagemagick {
print_info "installing imagemagick..."
nocheck_install "imagemagick"
}
function install_curl {
print_info "installing curl..."
nocheck_install "curl"
}
function install_wget {
print_info "installing wget..."
nocheck_install "wget"
}
function install_sendmail {
print_info "installing sendmail..."
nocheck_install "sendmail sendmail-bin"
}
function install_php {
# openssl and mbstring are included in libapache2-mod-php
print_info "installing php..."
nocheck_install "libapache2-mod-php php php-pear php-curl php-gd php-mbstring php-xml php-zip php-intl php-bcmath"
phpversion=$(php -v|grep --only-matching --perl-regexp "(PHP )\d+\.\\d+\.\\d+"|cut -c 5-7)
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/$phpversion/apache2/php.ini
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/$phpversion/apache2/php.ini
}
function install_composer {
print_info "We check if Composer is already downloaded"
if [ ! -f /usr/local/bin/composer ]
then
EXPECTED_CHECKSUM="`wget -qO- https://composer.github.io/installer.sig`"
wget https://getcomposer.org/installer -O composer-setup.php
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
>&2 echo 'ERROR: Invalid installer checksum'
rm composer-setup.php
die 'ERROR: Invalid installer checksum'
fi
php composer-setup.php --quiet
RESULT=$?
composer --version
rm composer-setup.php
# exit $RESULT
# We install Composer globally
mv composer.phar /usr/local/bin/composer
print_info "Composer was successfully downloaded."
else
print_info "Composer is already downloaded on this system."
fi
cd $install_path
export COMPOSER_ALLOW_SUPERUSER=1;
/usr/local/bin/composer install --no-dev --quiet
/usr/local/bin/composer show
export COMPOSER_ALLOW_SUPERUSER=0;
}
function install_mysql {
print_info "installing mysql..."
if [ -z "$mysqlpass" ]
then
die "mysqlpass not set in $configfile"
fi
if type mysql ; then
echo "Yes, mysql is installed"
else
echo "mariadb-server"
nocheck_install "mariadb-server"
systemctl status mariadb
systemctl start mariadb
mysql --user=root <<_EOF_
UPDATE mysql.user SET Password=PASSWORD('${mysqlpass}') WHERE User='root';
DELETE FROM mysql.user WHERE User='';
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
_EOF_
fi
}
function install_adminer {
print_info "installing adminer..."
nocheck_install "adminer"
if [ ! -f /etc/adminer/adminer.conf ]
then
echo "Alias /adminer /usr/share/adminer/adminer" > /etc/adminer/adminer.conf
ln -s /etc/adminer/adminer.conf /etc/apache2/conf-available/adminer.conf
else
print_info "file /etc/adminer/adminer.conf exists already"
fi
a2enmod rewrite
if [ ! -f /etc/apache2/apache2.conf ]
then
die "could not find file /etc/apache2/apache2.conf"
fi
sed -i \
"s/AllowOverride None/AllowOverride all/" \
/etc/apache2/apache2.conf
a2enconf adminer
systemctl restart mariadb
systemctl reload apache2
}
function create_zotserver_db {
print_info "creating zotserver database..."
if [ -z "$db_name" ]
then
die "db_name not set in $configfile"
fi
if [ -z "$db_user" ]
then
die "db_user not set in $configfile"
fi
if [ -z "$db_pass" ]
then
die "db_pass not set in $configfile"
fi
systemctl restart mariadb
# Make sure we don't write over an already existing database if we install more than one Zot hub/instance
if [ -z $(mysql -h localhost -u root -p$mysqlpass -e "SHOW DATABASES;" | grep $db_name) ]
then
Q1="CREATE DATABASE IF NOT EXISTS $db_name;"
Q2="GRANT USAGE ON *.* TO $db_user@localhost IDENTIFIED BY '$db_pass';"
Q3="GRANT ALL PRIVILEGES ON $db_name.* to $db_user@localhost identified by '$db_pass';"
Q4="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}"
mysql -uroot -p$mysqlpass -e "$SQL"
else
echo "database $db_name does exist already"
Q1="CREATE DATABASE IF NOT EXISTS $db_name;"
Q2="GRANT USAGE ON *.* TO $db_user@localhost IDENTIFIED BY '$db_pass';"
Q3="GRANT ALL PRIVILEGES ON $db_name.* to $db_user@localhost identified by '$db_pass';"
Q4="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}"
mysql -uroot -p$mysqlpass -e "$SQL"
fi
}
function run_freedns {
print_info "run freedns (dynamic IP)..."
if [ -z "$freedns_key" ]
then
print_info "freedns was not started because 'freedns_key' is empty in $configfile"
else
if [ -n "$selfhost_user" ]
then
die "You can not use freeDNS AND selfHOST for dynamic IP updates ('freedns_key' AND 'selfhost_user' set in $configfile)"
fi
wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key
fi
}
function install_run_selfhost {
print_info "install and start selfhost (dynamic IP)..."
if [ -z "$selfhost_user" ]
then
print_info "selfHOST was not started because 'selfhost_user' is empty in $configfile"
else
if [ -n "$freedns_key" ]
then
die "You can not use freeDNS AND selfHOST for dynamic IP updates ('freedns_key' AND 'selfhost_user' set in $configfile)"
fi
if [ -z "$selfhost_pass" ]
then
die "selfHOST was not started because 'selfhost_pass' is empty in $configfile"
fi
if [ ! -d $selfhostdir ]
then
mkdir $selfhostdir
fi
# the old way
# https://carol.selfhost.de/update?username=123456&password=supersafe
#
# the prefered way
if [ ! -f $selfhostdir/$selfhostscript ]
then
wget --output-document=$selfhostdir/$selfhostscript https://jonaspasche.de/selfhost-updater
if [ ! -s $selfhostdir/$selfhostscript ]
then
die "Failed to download selfHOST file for dynDNS"
fi
echo "router" > $selfhostdir/device
echo "$selfhost_user" > $selfhostdir/user
echo "$selfhost_pass" > $selfhostdir/pass
print_info "Wrote file to update dynamic IP. File: $selfhostdir/$selfhostscript"
fi
print_info "executing $selfhostdir/$selfhostscript update..."
bash $selfhostdir/$selfhostscript update
fi
}
function ping_domain {
print_info "ping domain $domain..."
# Is the domain resolved? Try to ping 6 times à 10 seconds
COUNTER=0
for i in {1..6}
do
print_info "loop $i for ping -c 1 $domain ..."
if ping -c 4 -W 1 $le_domain
then
print_info "$le_domain resolved"
break
else
if [ $i -gt 5 ]
then
die "Failed to: ping -c 1 $domain not resolved"
fi
fi
sleep 10
done
sleep 5
}
function configure_cron_freedns {
print_info "configure cron for freedns..."
if [ -z "$freedns_key" ]
then
print_info "freedns is not configured because freedns_key is empty in $configfile"
else
# Use cron for dynamich ip update
# - at reboot
# - every 30 minutes
if [ -z "`grep 'freedns.afraid.org' /etc/crontab`" ]
then
echo "@reboot root http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
echo "*/30 * * * * root wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
else
print_info "cron for freedns was configured already"
fi
fi
}
function configure_cron_selfhost {
print_info "configure cron for selfhost..."
if [ -z "$selfhost_user" ]
then
print_info "selfhost is not configured because selfhost_key is empty in $configfile"
else
# Use cron for dynamich ip update
# - at reboot
# - every 5 minutes
if [ -z "`grep 'selfhost-updater.sh' /etc/crontab`" ]
then
echo "@reboot root bash /etc/selfhost/selfhost-updater.sh update > /dev/null 2>&1" >> /etc/crontab
echo "*/5 * * * * root /bin/bash /etc/selfhost/selfhost-updater.sh update > /dev/null 2>&1" >> /etc/crontab
else
print_info "cron for selfhost was configured already"
fi
fi
}
function install_letsencrypt {
print_info "installing let's encrypt ..."
# check if user gave domain
if [ -z "$le_domain" ]
then
die "Failed to install let's encrypt: 'le_domain' is empty in $configfile"
fi
if [ -z "$le_email" ]
then
die "Failed to install let's encrypt: 'le_email' is empty in $configfile"
fi
nocheck_install "certbot python3-certbot-apache"
print_info "run certbot ..."
certbot --apache -w $install_path -d $le_domain -m $le_email --agree-tos --non-interactive --redirect --hsts --uir
service apache2 restart
}
function check_https {
print_info "checking httpS > testing ..."
url_https=https://$le_domain
wget_output=$(wget -nv --spider --max-redirect 0 $url_https)
if [ $? -ne 0 ]
then
print_warn "check not ok"
else
print_info "check ok"
fi
}
function install_zotserver {
print_info "installing addons..."
cd $install_path
util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons
mkdir -p "store/[data]/smarty3"
# chmod -R 777 store
touch .htconfig.php
# The next run of $cron_job (daily-update script) will correct the permissions of the next line
chmod ou+w .htconfig.php
cd /var/www/
chown -R www-data:www-data $install_path
chown root:www-data $install_path/
chown root:www-data $install_path/.htaccess
chmod 0644 $install_path/.htaccess
print_info "installed addons"
}
function configure_cron_daily {
print_info "configuring cron..."
# every 10 min for poller.php
if [ -z "`grep 'php Zotlabs/Daemon/Master.php' /etc/crontab`" ]
then
echo "*/10 * * * * www-data cd $install_path; php Zotlabs/Daemon/Master.php Cron >> /dev/null 2>&1" >> /etc/crontab
fi
# Run external script daily at 05:30
# - stop apache/nginx and mysql-server
# - renew the certificate of letsencrypt
# - update repository core and addon
# - update and upgrade linux
# - reboot is done by "shutdown -h now" because "reboot" hangs sometimes depending on the system
echo "#!/bin/sh" > /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \" \"" >> /var/www/$cron_job
echo "echo \"+++ \$(date) +++\"" >> /var/www/$cron_job
echo "echo \" \"" >> /var/www/$cron_job
echo "echo \"\$(date) - stopping apache and mysql...\"" >> /var/www/$cron_job
echo "service apache2 stop" >> /var/www/$cron_job
echo "systemctl stop mysql.service # to avoid inconsistencies" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$cron_job
echo "certbot renew --noninteractive" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \"\$(date) - db size...\"" >> /var/www/$cron_job
echo "du -h /var/lib/mysql/ | grep mysql/" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "# update of $le_domain Zot hub/instance" >> /var/www/$cron_job
echo "echo \"\$(date) - updating core and addons...\"" >> /var/www/$cron_job
echo "echo \"reaching git repository for $le_domain $zotserver hub/instance...\"" >> /var/www/$cron_job
echo "(cd $install_path ; util/udall)" >> /var/www/$cron_job
echo "chown -R www-data:www-data $install_path # make all accessible for the webserver" >> /var/www/$cron_job
echo "chown root:www-data $install_path/.htaccess" >> /var/www/$cron_job
echo "chmod 0644 $install_path/.htaccess # www-data can read but not write it" >> /var/www/$cron_job
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$cron_job
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$cron_job
echo "echo \"\$(date) - Update finished. Rebooting...\"" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "shutdown -r now" >> /var/www/$cron_job
chmod a+x /var/www/$cron_job
# If global cron job does not exist we add it to /etc/crontab
if grep -q $cron_job /etc/crontab
then
echo "cron job already in /etc/crontab"
else
echo "30 05 * * * root /bin/bash /var/www/$cron_job >> /var/www/daily-updates.log 2>&1" >> /etc/crontab
echo "0 0 1 * * root rm /var/www/daily-updates.log" >> /etc/crontab
fi
# This is active after either "reboot" or cron reload"
systemctl restart cron
print_info "configured cron for updates/upgrades"
}
########################################################################
# START OF PROGRAM
########################################################################
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
check_sanity
print_info "We're installing a $zotserver instance"
install_path="$(dirname "$(pwd)")"
# Read config file edited by user
configfile=config.txt
source $configfile
selfhostdir=/etc/selfhost
selfhostscript=selfhost-updater.sh
cron_job="cron_job.sh"
#set -x # activate debugging from here
zotserver=hubzilla
check_config
stop_zotserver
update_upgrade
install_curl
install_wget
install_sendmail
install_apache
install_imagemagick
install_php
install_composer
install_mysql
install_adminer
create_zotserver_db
run_freedns
install_run_selfhost
ping_domain
configure_cron_freedns
configure_cron_selfhost
if [ "$le_domain" != "localhost" ]
then
install_letsencrypt
check_https
else
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
fi
install_zotserver
configure_cron_daily
#set +x # stop debugging from here

31
.gitignore vendored
View File

@@ -48,10 +48,7 @@ doc/html/
.zotshrc
# external repositories for themes/addons
extend/
# exclude test results and cache
tests/.cache
tests/.phpunit*
# files generated by phpunit
tests/results/
## exclude IDE files
@@ -68,6 +65,7 @@ nbproject/
# PHPStorm
.idea/
## composer
# locally installed composer binary
composer.phar
@@ -80,30 +78,5 @@ composer.phar
vendor/**/tests/
vendor/**/Test/
vendor/sabre/*/examples/
# Exclude dev dependencies
vendor/bin/pdepend
vendor/bin/php-parse
vendor/bin/phpcbf
vendor/bin/phpcs
vendor/bin/phpmd
vendor/bin/phpstan*
vendor/bin/phpunit
vendor/composer/pcre/
vendor/composer/xdebug-handler/
vendor/dms/
vendor/doctrine/
vendor/myclabs/
vendor/nikic/
vendor/pdepend/
vendor/phar-io/
vendor/php-mock/
vendor/phpmd/
vendor/phpstan
vendor/phpunit/
vendor/psr/container/
vendor/sebastian/
vendor/squizlabs/
vendor/theseer/
# /info is a directory containing site-specific HTML documents
/info/

View File

@@ -1,5 +1,11 @@
# Select image from https://hub.docker.com/_/php/
#image: php:7.3
# Use a prepared Hubzilla image to optimise pipeline duration
# image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
image: php:8.0
stages:
- pretest
- test
- deploy
@@ -18,7 +24,6 @@ variables:
# Ignore a Composer warning
COMPOSER_ALLOW_SUPERUSER: 1
# Configure MySQL/MariaDB service (https://hub.docker.com/_/mysql/, https://hub.docker.com/_/mariadb/)
DB_HOST: mysql
MYSQL_DATABASE: hello_world_test
MYSQL_ROOT_PASSWORD: mysql
# Configure PostgreSQL service (https://hub.docker.com/_/postgres/)
@@ -26,55 +31,61 @@ variables:
POSTGRES_USER: ci-user
POSTGRES_PASSWORD: ci-pass
# hidden job definition with template for MySQL/MariaDB
.job_template_mysql: &job_definition_mysql
stage: test
variables:
HZ_TEST_DB_HOST: $DB_HOST
HZ_TEST_DB_TYPE: mysql
HZ_TEST_DB_USER: root
HZ_TEST_DB_PASS: $MYSQL_ROOT_PASSWORD
HZ_TEST_DB_DATABASE: $MYSQL_DATABASE
script:
# Import hubzilla's DB schema
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
# Show databases and relations/tables of hubzilla's database
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DB_HOST" --skip-ssl "$MYSQL_DATABASE"
# Run the actual tests
- touch dbfail.out
- vendor/bin/phpunit -d memory_limit=256M --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
before_script:
# pecl and composer do not work with PHP production restrictions (from Hubzilla Docker image)
- if [ -f /usr/local/etc/php/conf.d/z_prod.ini ]; then mv /usr/local/etc/php/conf.d/z_prod.ini /usr/local/etc/php/conf.d/z_prod.ini.off; fi
# Install & enable Xdebug for code coverage reports
- pecl install xdebug
- apt-get update
- apt-get install zip unzip libjpeg-dev libpng-dev -yqq
- docker-php-ext-enable xdebug
- docker-php-ext-install gd
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer
- php ./composer.phar install --no-progress
# php.ini settings
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
# hidden job definition with template for PHP
.job_template_php: &job_definition_php
stage: test
script:
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# hidden job definition with template for MySQL/MariaDB
#.job_template_mysql: &job_definition_mysql
# stage: test
# 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
# hidden job definition with template for PostgreSQL
.job_template_postgres: &job_definition_postgres
stage: test
variables:
HZ_TEST_DB_HOST: postgres
HZ_TEST_DB_TYPE: postgres
HZ_TEST_DB_USER: $POSTGRES_USER
HZ_TEST_DB_PASS: $POSTGRES_PASSWORD
HZ_TEST_DB_DATABASE: $POSTGRES_DB
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
- touch dbfail.out
- vendor/bin/phpunit -d memory_limit=256M --configuration tests/phpunit.xml --no-progress --stop-on-error --coverage-text --colors=never --log-junit tests/results/junit.xml || exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Test barfed!"; cat dbfail.out; exit $exit_code; fi
coverage: '/^\s*Lines:\s*\d+.\d+\%/'
#.job_template_postgres: &job_definition_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
# hidden job definition with artifacts config template
.artifacts_template: &artifacts_template
artifacts:
.artifacts_template:
artifacts: &artifacts_template
expire_in: 1 week
# Gitlab should show the results, but has problems parsing PHPUnit's junit file.
reports:
@@ -84,57 +95,55 @@ variables:
paths:
- tests/results/
default:
image: php:8.2
before_script:
# Install & enable Xdebug for code coverage reports
- apt-get update
- apt-get install -yqq libicu-dev libjpeg-dev libpng-dev libpq-dev libyaml-dev libgmp-dev libzip-dev mariadb-client postgresql-client unzip zip
- pecl install xdebug yaml
- docker-php-ext-enable xdebug yaml
- docker-php-ext-configure gd --with-jpeg=/usr/include/
- docker-php-ext-install gd gmp intl pdo_mysql pdo_pgsql zip exif
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer
- php ./composer.phar install --no-progress
# php.ini settings
- echo 'xdebug.mode=coverage' >> /usr/local/etc/php/php.ini
# PHP8.0
php8.0:
<<: *job_definition_php
check_templates:
stage: pretest
script:
- touch .htconfig.php
- php util/precompile_smarty3.php
# PHP8.0 with MySQL 5.7
#php8.0_mysql5.7:
# <<: *job_definition_mysql
# services:
# - mysql:5.7
phpstan:
stage: pretest
script:
- touch .htconfig.php
- vendor/bin/phpstan --memory-limit=512M
# PHP8.2 with MySQL 8.0
php8.2_mysql8.0.22:
services:
- mysql:8.0
<<: *job_definition_mysql
<<: *artifacts_template
# PHP8.0 with MySQL 8 (latest)
#php8.0_mysql8:
# <<: *job_definition_mysql
# services:
# - name: mysql:8
# command: ["--default-authentication-plugin=mysql_native_password"]
# PHP8.2 with MariaDB 10.6
php8.2_mariadb10.6:
services:
- name: mariadb:10.6
alias: mysql
<<: *job_definition_mysql
<<: *artifacts_template
# PHP8.2 with PostgreSQL 14
php8.2_postgres14:
services:
- postgres:14-alpine
<<: *job_definition_postgres
<<: *artifacts_template
# PHP8.0 with MariaDB 10.2
#php8.0_mariadb10.2:
# <<: *job_definition_mysql
# services:
# - name: mariadb:10.2
# alias: mysql
# PHP8.0 with MariaDB 10.3 (latest)
#php8.0_mariadb10.3:
# <<: *job_definition_mysql
# image: php:8.0
#image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
# services:
# - name: mariadb:10.3
# alias: mysql
# PHP7.3 with PostgreSQL latest (11)
#php7.3_postgres11:
# <<: *job_definition_postgres
# artifacts: *artifacts_template
# PHP7.3 with PostgreSQL latest (11)
#php7.3_postgres11:
# <<: *job_definition_postgres
# image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.3
# artifacts: *artifacts_template
# Generate Doxygen API Documentation and deploy it as GitLab pages

View File

@@ -25,10 +25,10 @@ AddType audio/ogg .oga
# in CGI mode.
RewriteCond %{REQUEST_URI} ^/\.well\-known/.*
RewriteRule ^(.*)$ index.php?q=$1 "[E=REMOTE_USER:%{HTTP:Authorization},L,QSA,B= ?]"
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 "[E=REMOTE_USER:%{HTTP:Authorization},L,QSA,B= ?]"
RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA]
</IfModule>

View File

@@ -1,82 +0,0 @@
<?xml version="1.0"?>
<ruleset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="PHP_CodeSniffer"
xsi:noNamespaceSchemaLocation="phpcs.xsd"
>
<description>PHP CodeSniffer config for Hubzilla</description>
<file>app</file>
<file>boot.php</file>
<file>include</file>
<file>index.php</file>
<file>install</file>
<file>library</file>
<file>tests</file>
<file>util</file>
<file>view</file>
<file>Zotlabs</file>
<rule ref="Generic">
<exclude name="Generic.Arrays.ArrayIndent"/>
<exclude name="Generic.Arrays.DisallowLongArraySyntax"/>
<exclude name="Generic.Arrays.DisallowShortArraySyntax"/>
<exclude name="Generic.Files.EndFileNoNewline"/>
<exclude name="Generic.Files.LowercasedFilename"/>
<exclude name="Generic.Formatting.MultipleStatementAlignment"/>
<exclude name="Generic.Formatting.SpaceAfterNot"/>
<exclude name="Generic.Functions.FunctionCallArgumentSpacing"/>
<exclude name="Generic.Functions.OpeningFunctionBraceBsdAllman"/>
<exclude name="Generic.NamingConventions.CamelCapsFunctionName"/>
<exclude name="Generic.PHP.ClosingPHPTag"/>
<exclude name="Generic.PHP.RequireStrictTypes"/>
<exclude name="Generic.PHP.UpperCaseConstant"/>
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
<exclude name="Generic.WhiteSpace.ScopeIndent"/>
<exclude name="Generic.Commenting.DocComment.ContentAfterOpen"/>
<exclude name="Generic.Commenting.DocComment.ContentBeforeClose"/>
<exclude name="Generic.Commenting.DocComment.LongNotCapital"/>
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
<exclude name="Generic.Commenting.DocComment.NonParamGroup"/>
<exclude name="Generic.Commenting.DocComment.ParamNotFirst"/>
<exclude name="Generic.Commenting.DocComment.ShortNotCapital"/>
<exclude name="Generic.Commenting.DocComment.SpacingAfter"/>
<exclude name="Generic.Commenting.DocComment.SpacingBeforeShort"/>
<exclude name="Generic.Commenting.DocComment.TagValueIndent"/>
<exclude name="Generic.ControlStructures.InlineControlStructure.NotAllowed"/>
<exclude name="Generic.Files.OneClassPerFile.MultipleFound"/>
<exclude name="Generic.Files.OneObjectStructurePerFile.MultipleFound"/>
<exclude name="Generic.Formatting.SpaceAfterCast.NoSpace"/>
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine" />
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed" />
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine" />
</rule>
<!--
Warn about lines longer than 100 columns, lines longer than 150
columns will flag an error.
-->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="100" />
<property name="absoluteLineLimit" value="150" />
</properties>
</rule>
<!--
Mark deprecated functions.
-->
<rule ref="Generic.PHP.DeprecatedFunctions">
<properties>
<property name="forbiddenFunctions" type="array" extend="true">
<element key="load_config" value="Zotlabs\Lib\Config::Load" />
<element key="get_config" value="Zotlabs\Lib\Config::Get" />
<element key="set_config" value="Zotlabs\Lib\Config::Set" />
<element key="del_config" value="Zotlabs\Lib\Config::Delete" />
</property>
</properties>
</rule>
</ruleset>

1120
CHANGELOG

File diff suppressed because it is too large Load Diff

48
CHANGELOG.air Normal file
View File

@@ -0,0 +1,48 @@
"air" is a branch name for revision of Account-Invite-Register at the Hubzilla project
Invite:
* Rewritten and now language template driven
* Selectable templates for the invite mails
* Invitor may add personal notes in the mailtext
+ Invite codes are bound to the recipients email address
* Invite mod never more creates accounts
* new db scheme for table register
* existing register table will be migrated to the new schema even when detected at runtime
* Bugfix: creating invite codes when admin only calls Invite w/o any further action
* account library revision also together with invite mod
* Depending on config: Users may send invitations also
* Invitations expires, controlled by the invitor
* Changed and new configs:
* * invitation_only As usual before
* * invitation_also Beside other registration policies, invitations may be used also
* * invitation_max_per_day defaults 50, may be changed in adminUI admin>site
* * invitation_max_per_user defaults 4
* Requirements:
* * Addon language has to be installed
Register:
* Register panel (form) and js interaction changed
* Unused registrations expire
* Depending on config, anonymous registrations (w/o email) are supported
* :... dont't panic, that may let grow security
* Even anonymous users have to confirm their registration
* Registrations may be enabled / disabled time driven for each day in the week (dudy)
* Unsoliced registration floods may be blocked
* Limited registrations from one single source ip
* Using one additional log file, to easy interfare with f2b
Account:
* An user account always becomes created only if all depending conditions are satisfied
* AdminUI for site configuration, accounts and registrations enhancements
* Still untouched, but accountUI needs enhanced async control in case for mass delete
with deep level of recursion cascade of the dependencies (channels etc). An open TODO
since years for instances with many much users and channels.
History:
2020.03 Hubzilla Prod version 4.6 (master branch) of hubzilla/core was the base for AIR
that was assigned Version 4.6.2 at sn/core
2021.02 Hubzilla Prod version 5.2.1 (master branch) of hubzilla/core was new base for AIR
that was assigned version 5.2.2 at sn/core (air.5)
plus adjustment of hubzilla 5.2.2 (master) to sn/core (air.5) version 5.2.9

View File

@@ -1,31 +0,0 @@
# Hubzilla Security Policy
The [Hubzilla] Project takes security, privacy and user control over personal data seriously. We ask that any security issues be disclosed to us in a responsible manner to allow us time to remediate the issues, and site administrators time to upgrade before information about the issue is made public.
This document outlines security procedures and policies for the Hubzilla project. It covers the following components:
* The Hubzilla core repository: https://framagit.org/hubzilla/core
* The official addon repository: https://framagit.org/hubzilla/addons
* The official themes repository: https://framagit.org/hubzilla/themes
* The official widgets repository: https://framagit.org/hubzilla/widgets
## Coordinated Disclosure Guidelines
We are committed to working with security researchers to verify, reproduce, and respond to legitimate reported vulnerabilities. You can help us by following these simple guidelines:
* Submit suspected vulnerabilities by email to `security@hubzilla.org`, or as a confidential issue in the relevant repository listed above.
* Provide clear instructions on how to reproduce the issue, and if possible, a minimal Proof of Concept (PoC) exploit.
* We will acknowledge your submission as soon as we can, and will keep you updated as it is being processed. We may ask for more information, or clarifications about the issue or the steps to reproduce it during this time.
* We will assign a CVE to the issue once it is confirmed.
* We will do our best to fix the issue as soon as we can after it has been confirmed. We request that information about the vulnerability or details about how to exploit it is not disclosed to other parties until after the fix is released and some time has passed, to allow site administrators to upgrade. We will normally make the CVE public one month after a fix has been released. (This grace period can differ based on severity, and can be negotiated.)
* Please perform all tests against a local instance of the software, and refrain from running any Denial of Service or automated testing tools against public hubs or the project managers (and their partners') infrastructure.
* If the issue belongs to a third party module that we depend on, we may help with reporting it upstream if the submitter wants us to.
## Comments on this Policy
We welcome comments and suggestions for improving this policy. You can reach us at:
* Our ticketing system: https://framagit.org/hubzilla/core/-/issues
* By sending us an email at `security@hubzilla.org`.
[Hubzilla]: https://hubzilla.org

View File

@@ -2,7 +2,6 @@
namespace Zotlabs\Access;
/**
* @brief AccessList class which represents individual content ACLs.
*
@@ -18,48 +17,29 @@ class AccessList {
* @brief Allow contacts
* @var string
*/
private ?string $allow_cid;
private $allow_cid;
/**
* @brief Allow groups
* @var string
*/
private ?string $allow_gid;
private $allow_gid;
/**
* @brief Deny contacts
* @var string
*/
private ?string $deny_cid;
private $deny_cid;
/**
* @brief Deny groups
* @var string
*/
private ?string $deny_gid;
private $deny_gid;
/**
* @brief Indicates if we are using the default constructor values or
* values that have been set explicitly.
* @var boolean
*/
private bool $explicit;
private $explicit;
/**
* @brief Keys required by the constructor if the channel array is given.
*/
private const REQUIRED_KEYS_CONSTRUCTOR = [
'channel_allow_cid',
'channel_allow_gid',
'channel_deny_cid',
'channel_deny_gid'
];
/**
* @brief Keys required by the set method.
*/
private const REQUIRED_KEYS_SET = [
'allow_cid',
'allow_gid',
'deny_cid',
'deny_gid'
];
/**
* @brief Constructor for AccessList class.
@@ -73,9 +53,8 @@ class AccessList {
* * \e string \b channel_deny_cid => string of denied cids
* * \e string \b channel_deny_gid => string of denied gids
*/
function __construct(array $channel) {
function __construct($channel) {
if ($channel) {
$this->validate_input_array($channel, self::REQUIRED_KEYS_CONSTRUCTOR);
$this->allow_cid = $channel['channel_allow_cid'];
$this->allow_gid = $channel['channel_allow_gid'];
$this->deny_cid = $channel['channel_deny_cid'];
@@ -91,24 +70,13 @@ class AccessList {
$this->explicit = false;
}
private function validate_input_array(array $arr, array $required_keys) : void {
$missing_keys = array_diff($required_keys, array_keys($arr));
if (!empty($missing_keys)) {
throw new \Exception(
'Invalid AccessList object: Expected array with keys: '
. implode(', ', $missing_keys)
);
}
}
/**
* @brief Get if we are using the default constructor values
* or values that have been set explicitly.
*
* @return boolean
*/
function get_explicit() : bool {
function get_explicit() {
return $this->explicit;
}
@@ -126,9 +94,7 @@ class AccessList {
* * \e string \b deny_gid => string of denied gids
* @param boolean $explicit (optional) default true
*/
function set(array $arr, bool $explicit = true) : void {
$this->validate_input_array($arr, self::REQUIRED_KEYS_SET);
function set($arr, $explicit = true) {
$this->allow_cid = $arr['allow_cid'];
$this->allow_gid = $arr['allow_gid'];
$this->deny_cid = $arr['deny_cid'];
@@ -146,7 +112,7 @@ class AccessList {
* * \e string \b deny_cid => string of denied cids
* * \e string \b deny_gid => string of denied gids
*/
function get() : array {
function get() {
return [
'allow_cid' => $this->allow_cid,
'allow_gid' => $this->allow_gid,
@@ -172,7 +138,7 @@ class AccessList {
* * \e array|string \b group_deny => array with gids or comma-seperated string
* @param boolean $explicit (optional) default true
*/
function set_from_array(array $arr, bool $explicit = true) : void {
function set_from_array($arr, $explicit = true) {
$arr['contact_allow'] = $arr['contact_allow'] ?? [];
$arr['group_allow'] = $arr['group_allow'] ?? [];
$arr['contact_deny'] = $arr['contact_deny'] ?? [];
@@ -195,7 +161,7 @@ class AccessList {
*
* @return boolean Return true if any of allow_* deny_* values is set.
*/
function is_private() : bool {
function is_private() {
return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false);
}

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Access;
use Zotlabs\Lib\Config;
/**
* @brief PermissionRoles class.
*
@@ -38,6 +36,7 @@ class PermissionRoles {
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['post_comments'] = PERMS_AUTHED;
$ret['limits']['post_mail'] = PERMS_AUTHED;
$ret['limits']['post_like'] = PERMS_AUTHED;
$ret['limits']['chat'] = PERMS_AUTHED;
break;
@@ -248,7 +247,7 @@ class PermissionRoles {
break;
}
$x = Config::Get('system','role_perms');
$x = get_config('system','role_perms');
// let system settings over-ride any or all
if($x && is_array($x) && array_key_exists($role,$x))

View File

@@ -212,7 +212,6 @@ class Permissions {
* @return array Associative array with
* * \e array \b perms Permission array
* * \e int \b automatic 0 or 1
* * \e srtring \b role
*/
static public function connect_perms($channel_id) {
@@ -231,6 +230,70 @@ class Permissions {
}
}
// look up the permission role to see if it specified auto-connect
// and if there was no permcat or a default permcat, set the perms
// from the role
/*
$role = get_pconfig($channel_id, 'system', 'permissions_role');
if ($role) {
$xx = PermissionRoles::role_perms($role);
if ($xx['perms_auto'])
$automatic = 1;
if ((!$my_perms) && ($xx['perms_connect'])) {
$default_perms = $xx['perms_connect'];
$my_perms = Permissions::FilledPerms($default_perms);
}
}
*/
// If we reached this point without having any permission information,
// it is likely a custom permissions role. First see if there are any
// automatic permissions.
/*
if (!$my_perms) {
$m = Permissions::FilledAutoperms($channel_id);
if ($m) {
$automatic = 1;
$my_perms = $m;
}
}
*/
// If we reached this point with no permissions, the channel is using
// custom perms but they are not automatic. They will be stored in abconfig with
// the channel's channel_hash (the 'self' connection).
/*
if (!$my_perms) {
$r = q("select channel_hash from channel where channel_id = %d",
intval($channel_id)
);
if ($r) {
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
intval($channel_id),
dbesc($r[0]['channel_hash'])
);
if ($x) {
foreach ($x as $xv) {
$my_perms[$xv['k']] = intval($xv['v']);
}
}
}
}
*/
return (['perms' => $my_perms, 'automatic' => $automatic, 'role' => $pc]);
}
/*
static public function serialise($p) {
$n = [];
if ($p) {
foreach ($p as $k => $v) {
if (intval($v)) {
$n[] = $k;
}
}
}
return implode(',', $n);
}
*/
}

View File

@@ -1,776 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
use Zotlabs\Lib\BaseObject;
class ASObject extends BaseObject
{
public $id;
public $type;
public $attachment;
public $attributedTo;
public $audience;
public $content;
public $context;
public $name;
public $endTime;
public $generator;
public $icon;
public $image;
public $inReplyTo;
public $location;
public $preview;
public $published;
public $replies;
public $startTime;
public $summary;
public $tag;
public $updated;
public $url;
public $to;
public $bto;
public $cc;
public $bcc;
public $mediaType;
public $duration;
public $source;
// Extension properties
public $signature;
public $proof;
public $sensitive;
public $replyTo;
public $wall;
public $isContainedConversation;
public $expires;
public $canReply;
public $canSearch;
public $directMessage;
public $commentPolicy;
/**
* @return mixed
*/
public function getDirectMessage()
{
return $this->directMessage;
}
/**
* @param mixed $directMessage
* @return ASObject
*/
public function setDirectMessage($directMessage)
{
$this->directMessage = $directMessage;
return $this;
}
/**
* @return mixed
*/
public function getSignature()
{
return $this->signature;
}
/**
* @param mixed $signature
* @return ASObject
*/
public function setSignature($signature)
{
$this->signature = $signature;
return $this;
}
/**
* @return mixed
*/
public function getProof()
{
return $this->proof;
}
/**
* @param mixed $proof
* @return ASObject
*/
public function setProof($proof)
{
$this->proof = $proof;
return $this;
}
/**
* @return mixed
*/
public function getSensitive()
{
return $this->sensitive;
}
/**
* @param mixed $sensitive
* @return ASObject
*/
public function setSensitive($sensitive)
{
$this->sensitive = $sensitive;
return $this;
}
/**
* @return mixed
*/
public function getReplyTo()
{
return $this->replyTo;
}
/**
* @param mixed $replyTo
* @return ASObject
*/
public function setReplyTo($replyTo)
{
$this->replyTo = $replyTo;
return $this;
}
/**
* @return mixed
*/
public function getWall()
{
return $this->wall;
}
/**
* @param mixed $wall
* @return ASObject
*/
public function setWall($wall)
{
$this->wall = $wall;
return $this;
}
/**
* @return mixed
*/
public function getIsContainedConversation()
{
return $this->isContainedConversation;
}
/**
* @param mixed $isContainedConversation
* @return ASObject
*/
public function setIsContainedConversation($isContainedConversation)
{
$this->isContainedConversation = $isContainedConversation;
return $this;
}
/**
* @return mixed
*/
public function getExpires()
{
return $this->expires;
}
/**
* @param mixed $expires
* @return ASObject
*/
public function setExpires($expires)
{
$this->expires = $expires;
return $this;
}
/**
* @return mixed
*/
public function getCanReply()
{
return $this->canReply;
}
/**
* @param mixed $canReply
* @return ASObject
*/
public function setCanReply($canReply)
{
$this->canReply = $canReply;
return $this;
}
/**
* @return mixed
*/
public function getCanSearch()
{
return $this->canSearch;
}
/**
* @param mixed $canSearch
* @return ASObject
*/
public function setCanSearch($canSearch)
{
$this->canSearch = $canSearch;
return $this;
}
/**
* @return mixed
*/
public function getCommentPolicy()
{
return $this->commentPolicy;
}
/**
* @param mixed $commentPolicy
* @return ASObject
*/
public function setCommentPolicy($commentPolicy)
{
$this->commentPolicy = $commentPolicy;
return $this;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
* @return ASObject
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* @param mixed $type
* @return ASObject
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return mixed
*/
public function getAttachment()
{
return $this->attachment;
}
/**
* @param mixed $attachment
* @return ASObject
*/
public function setAttachment($attachment)
{
$this->attachment = $attachment;
return $this;
}
/**
* @return mixed
*/
public function getAttributedTo()
{
return $this->attributedTo;
}
/**
* @param mixed $attributedTo
* @return ASObject
*/
public function setAttributedTo($attributedTo)
{
$this->attributedTo = $attributedTo;
return $this;
}
/**
* @return mixed
*/
public function getAudience()
{
return $this->audience;
}
/**
* @param mixed $audience
* @return ASObject
*/
public function setAudience($audience)
{
$this->audience = $audience;
return $this;
}
/**
* @return mixed
*/
public function getContent()
{
return $this->content;
}
/**
* @param mixed $content
* @return ASObject
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* @return mixed
*/
public function getContext()
{
return $this->context;
}
/**
* @param mixed $context
* @return ASObject
*/
public function setContext($context)
{
$this->context = $context;
return $this;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
* @return ASObject
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return mixed
*/
public function getEndTime()
{
return $this->endTime;
}
/**
* @param mixed $endTime
* @return ASObject
*/
public function setEndTime($endTime)
{
$this->endTime = $endTime;
return $this;
}
/**
* @return mixed
*/
public function getGenerator()
{
return $this->generator;
}
/**
* @param mixed $generator
* @return ASObject
*/
public function setGenerator($generator)
{
$this->generator = $generator;
return $this;
}
/**
* @return mixed
*/
public function getIcon()
{
return $this->icon;
}
/**
* @param mixed $icon
* @return ASObject
*/
public function setIcon($icon)
{
$this->icon = $icon;
return $this;
}
/**
* @return mixed
*/
public function getImage()
{
return $this->image;
}
/**
* @param mixed $image
* @return ASObject
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* @return mixed
*/
public function getInReplyTo()
{
return $this->inReplyTo;
}
/**
* @param mixed $inReplyTo
* @return ASObject
*/
public function setInReplyTo($inReplyTo)
{
$this->inReplyTo = $inReplyTo;
return $this;
}
/**
* @return mixed
*/
public function getLocation()
{
return $this->location;
}
/**
* @param mixed $location
* @return ASObject
*/
public function setLocation($location)
{
$this->location = $location;
return $this;
}
/**
* @return mixed
*/
public function getPreview()
{
return $this->preview;
}
/**
* @param mixed $preview
* @return ASObject
*/
public function setPreview($preview)
{
$this->preview = $preview;
return $this;
}
/**
* @return mixed
*/
public function getPublished()
{
return $this->published;
}
/**
* @param mixed $published
* @return ASObject
*/
public function setPublished($published)
{
$this->published = $published;
return $this;
}
/**
* @return mixed
*/
public function getReplies()
{
return $this->replies;
}
/**
* @param mixed $replies
* @return ASObject
*/
public function setReplies($replies)
{
$this->replies = $replies;
return $this;
}
/**
* @return mixed
*/
public function getStartTime()
{
return $this->startTime;
}
/**
* @param mixed $startTime
* @return ASObject
*/
public function setStartTime($startTime)
{
$this->startTime = $startTime;
return $this;
}
/**
* @return mixed
*/
public function getSummary()
{
return $this->summary;
}
/**
* @param mixed $summary
* @return ASObject
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @return mixed
*/
public function getTag()
{
return $this->tag;
}
/**
* @param mixed $tag
* @return ASObject
*/
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
/**
* @return mixed
*/
public function getUpdated()
{
return $this->updated;
}
/**
* @param mixed $updated
* @return ASObject
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* @return mixed
*/
public function getUrl()
{
return $this->url;
}
/**
* @param mixed $url
* @return ASObject
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @return mixed
*/
public function getTo()
{
return $this->to;
}
/**
* @param mixed $to
* @return ASObject
*/
public function setTo($to)
{
$this->to = $to;
return $this;
}
/**
* @return mixed
*/
public function getBto()
{
return $this->bto;
}
/**
* @param mixed $bto
* @return ASObject
*/
public function setBto($bto)
{
$this->bto = $bto;
return $this;
}
/**
* @return mixed
*/
public function getCc()
{
return $this->cc;
}
/**
* @param mixed $cc
* @return ASObject
*/
public function setCc($cc)
{
$this->cc = $cc;
return $this;
}
/**
* @return mixed
*/
public function getBcc()
{
return $this->bcc;
}
/**
* @param mixed $bcc
* @return ASObject
*/
public function setBcc($bcc)
{
$this->bcc = $bcc;
return $this;
}
/**
* @return mixed
*/
public function getMediaType()
{
return $this->mediaType;
}
/**
* @param mixed $mediaType
* @return ASObject
*/
public function setMediaType($mediaType)
{
$this->mediaType = $mediaType;
return $this;
}
/**
* @return mixed
*/
public function getDuration()
{
return $this->duration;
}
/**
* @param mixed $duration
* @return ASObject
*/
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
/**
* @return mixed
*/
public function getSource()
{
return $this->source;
}
/**
* @param mixed $source
* @return ASObject
*/
public function setSource($source)
{
$this->source = $source;
return $this;
}
}

View File

@@ -1,122 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Activity extends ASObject
{
public $actor;
public $object;
public $target;
public $result;
public $origin;
public $instrument;
/**
* @return mixed
*/
public function getActor()
{
return $this->actor;
}
/**
* @param mixed $actor
* @return Activity
*/
public function setActor($actor)
{
$this->actor = $actor;
return $this;
}
/**
* @return mixed
*/
public function getObject()
{
return $this->object;
}
/**
* @param mixed $object
* @return Activity
*/
public function setObject($object)
{
$this->object = $object;
return $this;
}
/**
* @return mixed
*/
public function getTarget()
{
return $this->target;
}
/**
* @param mixed $target
* @return Activity
*/
public function setTarget($target)
{
$this->target = $target;
return $this;
}
/**
* @return mixed
*/
public function getResult()
{
return $this->result;
}
/**
* @param mixed $result
* @return Activity
*/
public function setResult($result)
{
$this->result = $result;
return $this;
}
/**
* @return mixed
*/
public function getOrigin()
{
return $this->origin;
}
/**
* @param mixed $origin
* @return Activity
*/
public function setOrigin($origin)
{
$this->origin = $origin;
return $this;
}
/**
* @return mixed
*/
public function getInstrument()
{
return $this->instrument;
}
/**
* @param mixed $instrument
* @return Activity
*/
public function setInstrument($instrument)
{
$this->instrument = $instrument;
return $this;
}
}

View File

@@ -1,428 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Actor extends ASObject
{
public $inbox;
public $outbox;
public $followers;
public $following;
public $permissions; /* extension property */
public $endpoints;
public $publicKey;
public $preferredUsername;
public $alsoKnownAs;
// Extension properties
public $movedTo;
public $copiedTo;
public $discoverable;
public $manuallyApprovesFollowers;
public $webfinger;
public $canSearch;
public $indexable;
public $assertionMethod;
public $gateways;
public $openwebauth;
public $authredirect;
/**
* @return mixed
*/
public function getAlsoKnownAs()
{
return $this->alsoKnownAs;
}
/**
* @param mixed $alsoKnownAs
* @return Actor
*/
public function setAlsoKnownAs($alsoKnownAs)
{
$this->alsoKnownAs = $alsoKnownAs;
return $this;
}
/**
* @return mixed
*/
public function getMovedTo()
{
return $this->movedTo;
}
/**
* @return mixed
*/
public function getCopiedTo()
{
return $this->copiedTo;
}
/**
* @param mixed $copiedTo
* @return Actor
*/
public function setCopiedTo($copiedTo)
{
$this->copiedTo = $copiedTo;
return $this;
}
/**
* @param mixed $movedTo
* @return Actor
*/
public function setMovedTo($movedTo)
{
$this->movedTo = $movedTo;
return $this;
}
/**
* @return mixed
*/
public function getDiscoverable()
{
return $this->discoverable;
}
/**
* @param mixed $discoverable
* @return Actor
*/
public function setDiscoverable($discoverable)
{
$this->discoverable = $discoverable;
return $this;
}
/**
* @return mixed
*/
public function getManuallyApprovesFollowers()
{
return $this->manuallyApprovesFollowers;
}
/**
* @param mixed $manuallyApprovesFollowers
* @return Actor
*/
public function setManuallyApprovesFollowers($manuallyApprovesFollowers)
{
$this->manuallyApprovesFollowers = $manuallyApprovesFollowers;
return $this;
}
/**
* @return mixed
*/
public function getPreferredUsername()
{
return $this->preferredUsername;
}
/**
* @param mixed $preferredUsername
* @return Actor
*/
public function setPreferredUsername($preferredUsername)
{
$this->preferredUsername = $preferredUsername;
return $this;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
* @return Actor
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* @param mixed $type
* @return Actor
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return mixed
*/
public function getInbox()
{
return $this->inbox;
}
/**
* @param mixed $inbox
* @return Actor
*/
public function setInbox($inbox)
{
$this->inbox = $inbox;
return $this;
}
/**
* @return mixed
*/
public function getOutbox()
{
return $this->outbox;
}
/**
* @param mixed $outbox
* @return Actor
*/
public function setOutbox($outbox)
{
$this->outbox = $outbox;
return $this;
}
/**
* @return mixed
*/
public function getFollowers()
{
return $this->followers;
}
/**
* @param mixed $followers
* @return Actor
*/
public function setFollowers($followers)
{
$this->followers = $followers;
return $this;
}
/**
* @return mixed
*/
public function getFollowing()
{
return $this->following;
}
/**
* @param mixed $following
* @return Actor
*/
public function setFollowing($following)
{
$this->following = $following;
return $this;
}
/**
* @return mixed
*/
public function getEndpoints()
{
return $this->endpoints;
}
/**
* @param mixed $endpoints
* @return Actor
*/
public function setEndpoints($endpoints)
{
$this->endpoints = $endpoints;
return $this;
}
/**
* @return mixed
*/
public function getPublicKey()
{
return $this->publicKey;
}
/**
* @param mixed $publicKey
* @return Actor
*/
public function setPublicKey($publicKey)
{
$this->publicKey = $publicKey;
return $this;
}
/**
* @return mixed
*/
public function getWebfinger()
{
return $this->webfinger;
}
/**
* @param mixed $webfinger
* @return Actor
*/
public function setWebfinger($webfinger)
{
$this->webfinger = $webfinger;
return $this;
}
/**
* @return mixed
*/
public function getCanSearch()
{
return $this->canSearch;
}
/**
* @param mixed $canSearch
* @return Actor
*/
public function setCanSearch($canSearch)
{
$this->canSearch = $canSearch;
return $this;
}
/**
* @return mixed
*/
public function getIndexable()
{
return $this->indexable;
}
/**
* @param mixed $indexable
* @return Actor
*/
public function setIndexable($indexable)
{
$this->indexable = $indexable;
return $this;
}
/**
* @return mixed
*/
public function getAssertionMethod()
{
return $this->assertionMethod;
}
/**
* @param mixed $assertionMethod
* @return Actor
*/
public function setAssertionMethod($assertionMethod)
{
$this->assertionMethod = $assertionMethod;
return $this;
}
/**
* @return mixed
*/
public function getGateways()
{
return $this->gateways;
}
/**
* @param mixed $gateways
* @return Actor
*/
public function setGateways($gateways)
{
$this->gateways = $gateways;
return $this;
}
/**
* @return mixed
*/
public function getPermissions()
{
return $this->permissions;
}
/**
* @param mixed $permissions
* @return Actor
*/
public function setPermissions($permissions)
{
$this->permissions = $permissions;
return $this;
}
/**
* @return mixed
*/
public function getOpenwebauth()
{
return $this->openwebauth;
}
/**
* @param mixed $openwebauth
* @return Actor
*/
public function setOpenwebauth($openwebauth)
{
$this->openwebauth = $openwebauth;
return $this;
}
/**
* @return mixed
*/
public function getAuthredirect()
{
return $this->authredirect;
}
/**
* @param mixed $authredirect
* @return Actor
*/
public function setAuthredirect($authredirect)
{
$this->authredirect = $authredirect;
return $this;
}
}

View File

@@ -1,87 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class AssertionMethod extends ASObject
{
public $id;
public $type;
public $controller;
public $publicKeyMultibase;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
* @return AssertionMethod
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* @param mixed $type
* @return AssertionMethod
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return mixed
*/
public function getController()
{
return $this->controller;
}
/**
* @param mixed $controller
* @return AssertionMethod
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* @return mixed
*/
public function getPublicKeyMultibase()
{
return $this->publicKeyMultibase;
}
/**
* @param mixed $publicKeyMultibase
* @return AssertionMethod
*/
public function setPublicKeyMultibase($publicKeyMultibase)
{
$this->publicKeyMultibase = $publicKeyMultibase;
return $this;
}
}

View File

@@ -1,124 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Collection extends ASObject
{
public int $totalItems;
public string $current;
public string $first;
public string $last;
public array $items;
public mixed $collectionOf;
/**
* @return int
*/
public function getTotalItems(): int
{
return $this->totalItems;
}
/**
* @param mixed $totalItems
* @return Collection
*/
public function setTotalItems(mixed $totalItems): static
{
$this->totalItems = $totalItems;
return $this;
}
/**
* @return string
*/
public function getCurrent(): string
{
return $this->current;
}
/**
* @param mixed $current
* @return Collection
*/
public function setCurrent(mixed $current): static
{
$this->current = $current;
return $this;
}
/**
* @return string
*/
public function getFirst(): string
{
return $this->first;
}
/**
* @param mixed $first
* @return Collection
*/
public function setFirst(mixed $first): static
{
$this->first = $first;
return $this;
}
/**
* @return string
*/
public function getLast(): string
{
return $this->last;
}
/**
* @param mixed $last
* @return Collection
*/
public function setLast(mixed $last): static
{
$this->last = $last;
return $this;
}
/**
* @return array
*/
public function getItems(): array
{
return $this->items;
}
/**
* @param mixed $items
* @return Collection
*/
public function setItems(mixed $items): static
{
$this->items = $items;
return $this;
}
/**
* @return mixed
*/
public function getCollectionOf(): mixed
{
return $this->collectionOf;
}
/**
* @param mixed $collectionOf
* @return Collection
*/
public function setCollectionOf(mixed $collectionOf): static
{
$this->collectionOf = $collectionOf;
return $this;
}
}

View File

@@ -1,73 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class CollectionPage extends Collection
{
public $partOf;
public $next;
public $prev;
// startIndex only applies for OrderedCollectionPage. See
// https://www.w3.org/ns/activitystreams#OrderedCollectionPage
// It is provided here to avoid multiple inheritance
public $startIndex;
/**
* @return mixed
*/
public function getPartOf()
{
return $this->partOf;
}
/**
* @param mixed $partOf
* @return CollectionPage
*/
public function setPartOf($partOf)
{
$this->partOf = $partOf;
return $this;
}
/**
* @return mixed
*/
public function getNext()
{
return $this->next;
}
/**
* @param mixed $next
* @return CollectionPage
*/
public function setNext($next)
{
$this->next = $next;
return $this;
}
/**
* @return mixed
*/
public function getPrev()
{
return $this->prev;
}
/**
* @param mixed $prev
* @return CollectionPage
*/
public function setPrev($prev)
{
$this->prev = $prev;
return $this;
}
}

View File

@@ -1,104 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class IntransitiveActivity extends ASObject
{
public $actor;
public $target;
public $result;
public $origin;
public $instrument;
/**
* @return mixed
*/
public function getActor()
{
return $this->actor;
}
/**
* @param mixed $actor
* @return IntransitiveActivity
*/
public function setActor($actor)
{
$this->actor = $actor;
return $this;
}
/**
* @return mixed
*/
public function getTarget()
{
return $this->target;
}
/**
* @param mixed $target
* @return IntransitiveActivity
*/
public function setTarget($target)
{
$this->target = $target;
return $this;
}
/**
* @return mixed
*/
public function getResult()
{
return $this->result;
}
/**
* @param mixed $result
* @return IntransitiveActivity
*/
public function setResult($result)
{
$this->result = $result;
return $this;
}
/**
* @return mixed
*/
public function getOrigin()
{
return $this->origin;
}
/**
* @param mixed $origin
* @return IntransitiveActivity
*/
public function setOrigin($origin)
{
$this->origin = $origin;
return $this;
}
/**
* @return mixed
*/
public function getInstrument()
{
return $this->instrument;
}
/**
* @param mixed $instrument
* @return IntransitiveActivity
*/
public function setInstrument($instrument)
{
$this->instrument = $instrument;
return $this;
}
}

View File

@@ -1,183 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
use Zotlabs\Lib\BaseObject;
class Link extends BaseObject
{
public $type;
public $href;
public $rel;
public $mediaType;
public $name;
public $hreflang;
public $height;
public $width;
public $preview;
/**
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* @param mixed $type
* @return Link
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return mixed
*/
public function getHref()
{
return $this->href;
}
/**
* @param mixed $href
* @return Link
*/
public function setHref($href)
{
$this->href = $href;
return $this;
}
/**
* @return mixed
*/
public function getRel()
{
return $this->rel;
}
/**
* @param mixed $rel
* @return Link
*/
public function setRel($rel)
{
$this->rel = $rel;
return $this;
}
/**
* @return mixed
*/
public function getMediaType()
{
return $this->mediaType;
}
/**
* @param mixed $mediaType
* @return Link
*/
public function setMediaType($mediaType)
{
$this->mediaType = $mediaType;
return $this;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
* @return Link
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return mixed
*/
public function getHreflang()
{
return $this->hreflang;
}
/**
* @param mixed $hreflang
* @return Link
*/
public function setHreflang($hreflang)
{
$this->hreflang = $hreflang;
return $this;
}
/**
* @return mixed
*/
public function getHeight()
{
return $this->height;
}
/**
* @param mixed $height
* @return Link
*/
public function setHeight($height)
{
$this->height = $height;
return $this;
}
/**
* @return mixed
*/
public function getWidth()
{
return $this->width;
}
/**
* @param mixed $width
* @return Link
*/
public function setWidth($width)
{
$this->width = $width;
return $this;
}
/**
* @return mixed
*/
public function getPreview()
{
return $this->preview;
}
/**
* @param mixed $preview
* @return Link
*/
public function setPreview($preview)
{
$this->preview = $preview;
return $this;
}
}

View File

@@ -1,8 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class OrderedCollection extends Collection
{
}

View File

@@ -1,92 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
/**
* According to the specification, OrderedCollectionPage extends
* both OrderedCollection and CollectionPage, but PHP is still a bit awkward
* when it comes to multiple inheritance. Rather than try and do this with
* traits, we'll just include the CollectionPage elements here - as this only
* consists of three properties.
*/
class OrderedCollectionPage extends OrderedCollection
{
public $partOf;
public $next;
public $prev;
public $startIndex;
/**
* @return mixed
*/
public function getPartOf()
{
return $this->partOf;
}
/**
* @param mixed $partOf
* @return OrderedCollectionPage
*/
public function setPartOf($partOf)
{
$this->partOf = $partOf;
return $this;
}
/**
* @return mixed
*/
public function getNext()
{
return $this->next;
}
/**
* @param mixed $next
* @return OrderedCollectionPage
*/
public function setNext($next)
{
$this->next = $next;
return $this;
}
/**
* @return mixed
*/
public function getPrev()
{
return $this->prev;
}
/**
* @param mixed $prev
* @return OrderedCollectionPage
*/
public function setPrev($prev)
{
$this->prev = $prev;
return $this;
}
/**
* @return mixed
*/
public function getStartIndex()
{
return $this->startIndex;
}
/**
* @param mixed $startIndex
* @return OrderedCollectionPage
*/
public function setStartIndex($startIndex)
{
$this->startIndex = $startIndex;
return $this;
}
}

View File

@@ -1,125 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Place extends ASObject
{
public $accuracy;
public $altitude;
public $latitude;
public $longitude;
public $radius;
public $units;
/**
* @return mixed
*/
public function getAccuracy()
{
return $this->accuracy;
}
/**
* @param mixed $accuracy
* @return Place
*/
public function setAccuracy($accuracy)
{
$this->accuracy = $accuracy;
return $this;
}
/**
* @return mixed
*/
public function getAltitude()
{
return $this->altitude;
}
/**
* @param mixed $altitude
* @return Place
*/
public function setAltitude($altitude)
{
$this->altitude = $altitude;
return $this;
}
/**
* @return mixed
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* @param mixed $latitude
* @return Place
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
return $this;
}
/**
* @return mixed
*/
public function getLongitude()
{
return $this->longitude;
}
/**
* @param mixed $longitude
* @return Place
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
return $this;
}
/**
* @return mixed
*/
public function getRadius()
{
return $this->radius;
}
/**
* @param mixed $radius
* @return Place
*/
public function setRadius($radius)
{
$this->radius = $radius;
return $this;
}
/**
* @return mixed
*/
public function getUnits()
{
return $this->units;
}
/**
* @param mixed $units
* @return Place
*/
public function setUnits($units)
{
$this->units = $units;
return $this;
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Profile extends ASObject
{
public $describes;
/**
* @return mixed
*/
public function getDescribes()
{
return $this->describes;
}
/**
* @param mixed $describes
* @return Profile
*/
public function setDescribes($describes)
{
$this->describes = $describes;
return $this;
}
}

View File

@@ -1,68 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class PublicKey extends ASObject
{
public $owner;
public $signatureAlgorithm;
public $publicKeyPem;
/**
* @return mixed
*/
public function getOwner()
{
return $this->owner;
}
/**
* @param mixed $owner
* @return PublicKey
*/
public function setOwner($owner)
{
$this->owner = $owner;
return $this;
}
/**
* @return mixed
*/
public function getSignatureAlgorithm()
{
return $this->signatureAlgorithm;
}
/**
* @param mixed $signatureAlgorithm
* @return PublicKey
*/
public function setSignatureAlgorithm($signatureAlgorithm)
{
$this->signatureAlgorithm = $signatureAlgorithm;
return $this;
}
/**
* @return mixed
*/
public function getPublicKeyPem()
{
return $this->publicKeyPem;
}
/**
* @param mixed $publicKeyPem
* @return PublicKey
*/
public function setPublicKeyPem($publicKeyPem)
{
$this->publicKeyPem = $publicKeyPem;
return $this;
}
}

View File

@@ -1,67 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Question extends ASObject
{
public $oneOf;
public $anyOf;
public $closed;
/**
* @return mixed
*/
public function getOneOf()
{
return $this->oneOf;
}
/**
* @param mixed $oneOf
* @return Question
*/
public function setOneOf($oneOf)
{
$this->oneOf = $oneOf;
return $this;
}
/**
* @return mixed
*/
public function getAnyOf()
{
return $this->anyOf;
}
/**
* @param mixed $anyOf
* @return Question
*/
public function setAnyOf($anyOf)
{
$this->anyOf = $anyOf;
return $this;
}
/**
* @return mixed
*/
public function getClosed()
{
return $this->closed;
}
/**
* @param mixed $closed
* @return Question
*/
public function setClosed($closed)
{
$this->closed = $closed;
return $this;
}
}

View File

@@ -1,67 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Relationship extends ASObject
{
public $subject;
public $object;
public $relationship;
/**
* @return mixed
*/
public function getSubject()
{
return $this->subject;
}
/**
* @param mixed $subject
* @return Relationship
*/
public function setSubject($subject)
{
$this->subject = $subject;
return $this;
}
/**
* @return mixed
*/
public function getObject()
{
return $this->object;
}
/**
* @param mixed $object
* @return Relationship
*/
public function setObject($object)
{
$this->object = $object;
return $this;
}
/**
* @return mixed
*/
public function getRelationship()
{
return $this->relationship;
}
/**
* @param mixed $relationship
* @return Relationship
*/
public function setRelationship($relationship)
{
$this->relationship = $relationship;
return $this;
}
}

View File

@@ -1,65 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Signature extends ASObject
{
public $nonce;
public $creator;
public $signatureValue;
/**
* @return mixed
*/
public function getCreator()
{
return $this->creator;
}
/**
* @param mixed $creator
* @return Signature
*/
public function setCreator($creator)
{
$this->creator = $creator;
return $this;
}
/**
* @return mixed
*/
public function getSignatureValue()
{
return $this->signatureValue;
}
/**
* @param mixed $signatureValue
* @return Signature
*/
public function setSignatureValue($signatureValue)
{
$this->signatureValue = $signatureValue;
return $this;
}
/**
* @return mixed
*/
public function getNonce()
{
return $this->nonce;
}
/**
* @param mixed $nonce
* @return Signature
*/
public function setNonce($nonce)
{
$this->nonce = $nonce;
return $this;
}
}

View File

@@ -1,48 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class Tombstone extends ASObject
{
public $formerType;
public $deleted;
/**
* @return mixed
*/
public function getFormerType()
{
return $this->formerType;
}
/**
* @param mixed $formerType
* @return Tombstone
*/
public function setFormerType($formerType)
{
$this->formerType = $formerType;
return $this;
}
/**
* @return mixed
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* @param mixed $deleted
* @return Tombstone
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
}

View File

@@ -1,8 +0,0 @@
<?php
namespace Zotlabs\ActivityStreams;
class UnhandledElementException extends \Exception
{
}

View File

@@ -6,28 +6,23 @@ class Cache_embeds {
static public function run($argc,$argv) {
if(!$argc == 2) {
if(! $argc == 2)
return;
}
$c = q("select uid, aid, body, item_private from item where uuid = '%s'",
dbesc($argv[1])
$c = q("select body from item where id = %d ",
dbesc(intval($argv[1]))
);
if(!$c) {
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']);
// photocache addon hook to prefetch one copy of public item images for the sys channel
call_hooks('cache_prefetch_hook', $item);
bbcode($item['body']);
return;
}
}

View File

@@ -3,7 +3,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Cache;
use Zotlabs\Lib\Config;
class Cache_query {
@@ -12,17 +11,16 @@ class Cache_query {
if(! $argc == 3)
return;
$r = null;
$key = $argv[1];
$pid = Config::Get('procid', $key, false);
$pid = get_config('procid', $key, false);
if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) {
logger($key . ': procedure already run with pid ' . $pid, LOGGER_DEBUG);
return;
}
$pid = getmypid();
Config::Set('procid', $key, $pid);
set_config('procid', $key, $pid);
array_shift($argv);
array_shift($argv);
@@ -30,12 +28,10 @@ class Cache_query {
$arr = json_decode(base64_decode($argv[0]), true);
$r = call_user_func_array('q', $arr);
if(is_array($r)) {
if($r)
Cache::set($key, serialize($r));
}
Config::Delete('procid', $key);
del_config('procid', $key);
return;
}

View File

@@ -24,7 +24,7 @@ class Channel_purge {
);
if ($r) {
foreach ($r as $rv) {
drop_item($rv['id'], uid: $channel_id);
drop_item($rv['id'], false);
}
}
} while ($r);

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
require_once('include/hubloc.php');
class Checksites {
@@ -21,7 +19,7 @@ class Checksites {
if ($site_id)
$sql_options = " and site_url = '" . dbesc($argv[1]) . "' ";
$days = intval(Config::Get('system', 'sitecheckdays'));
$days = intval(get_config('system', 'sitecheckdays'));
if ($days < 1)
$days = 30;

View File

@@ -38,13 +38,7 @@ class Content_importer {
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512');
$redirects = 0;
$x = z_fetch_url(
$hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,
false,
$redirects,
[ 'headers' => $headers ]
);
$x = z_fetch_url($hz_server . '/api/z/1.0/item/export_page?f=&since=' . urlencode($since) . '&until=' . urlencode($until) . '&page=' . $page,false,$redirects,[ 'headers' => $headers ]);
// logger('item fetch: ' . print_r($x,true));
@@ -53,9 +47,9 @@ class Content_importer {
killme();
}
$j = json_decode($x['body'], true);
$j = json_decode($x['body'],true);
if($j && empty($j['item'])) {
if(! is_array($j['item']) || ! count($j['item'])) {
PConfig::Set($channel['channel_id'], 'import', 'content_completed', 1);
return;
}

View File

@@ -5,8 +5,6 @@ namespace Zotlabs\Daemon;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\ASCollection;
use Zotlabs\Lib\ASCache;
use Zotlabs\Lib\Config;
class Convo {
@@ -14,77 +12,51 @@ class Convo {
logger('convo invoked: ' . print_r($argv, true));
if ($argc < 4) {
if ($argc != 4) {
return;
}
$channels = explode(',', $argv[1]);
if (!$channels) {
$id = $argv[1];
$channel_id = intval($argv[2]);
$contact_hash = $argv[3];
$channel = channelx_by_n($channel_id);
if (!$channel) {
return;
}
$observer_hash = $argv[2];
if (!$observer_hash) {
$r = q("SELECT abook.*, xchan.* FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_xchan = '%s' LIMIT 1",
intval($channel_id),
dbesc($contact_hash)
);
if (!$r) {
return;
}
$mid = $argv[3];
if (!$mid) {
$contact = array_shift($r);
$obj = new ASCollection($id, $channel);
$messages = $obj->get();
if (!$messages) {
return;
}
$force = $argv[4] ?? false;
$interval = Config::Get('queueworker', 'queue_interval', 500000);
foreach ($channels as $channel_id) {
$channel = channelx_by_n($channel_id);
$obj = new ASCollection($mid, $channel);
$messages = $obj->get();
if (!$messages) {
continue;
foreach ($messages as $message) {
if (is_string($message)) {
$message = Activity::fetch($message, $channel);
}
foreach ($messages as $message) {
$network_fetch = false;
if (is_string($message)) {
$cached = ASCache::Get($message);
if ($cached) {
// logger('convo_cached: ' . $message);
$data = $cached;
}
else {
// logger('convo_fetching: ' . $message);
$network_fetch = true;
$data = Activity::fetch($message, $channel);
if ($data) {
ASCache::Set($message, $data);
}
}
}
else {
$data = $message;
}
if (!$network_fetch) {
// Add some delay so that the DB will not be overwhelmed
// Fetched from network will already have a slight delay
usleep($interval);
}
$AS = new ActivityStreams($data);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
$item['item_fetched'] = true;
Activity::store($channel, $observer_hash, $AS, $item, false, $force);
}
// set client flag because comments will probably just be objects and not full blown activities
// and that lets us use implied_create
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
Activity::store($channel, $contact['abook_xchan'], $AS, $item);
}
}
return;

View File

@@ -2,17 +2,13 @@
namespace Zotlabs\Daemon;
use DBA;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\ObjCache;
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\Libzotdir;
class Cron {
static public function run($argc, $argv) {
$maxsysload = intval(Config::Get('system', 'maxloadavg'));
$maxsysload = intval(get_config('system', 'maxloadavg'));
if ($maxsysload < 1)
$maxsysload = 50;
if (function_exists('sys_getloadavg')) {
@@ -27,7 +23,7 @@ class Cron {
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
$lockfile = 'store/[data]/cron';
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
&& (!Config::Get('system', 'override_cron_lockfile'))) {
&& (!get_config('system', 'override_cron_lockfile'))) {
logger("cron: Already running");
return;
}
@@ -39,17 +35,6 @@ class Cron {
logger('cron: start');
// If this is a directory server, request a sync with an upstream
// directory at least once a day, up to once every poll interval.
// Pull remote changes and push local changes.
// potential issue: how do we keep from creating an endless update loop?
$dirmode = Config::Get('system', 'directory_mode');
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
Libzotdir::sync_directories($dirmode);
}
// run queue delivery process in the background
Master::Summon(array('Queue'));
@@ -67,22 +52,22 @@ class Cron {
require_once('include/account.php');
remove_expired_registrations();
$interval = Config::Get('queueworker', 'queue_interval', 500000);
$interval = get_config('queueworker', 'queue_interval', 500000);
// expire any expired items
$r = q("select id, uid, item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s
$r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s
and item_deleted = 0 ",
db_utcnow()
);
if ($r) {
require_once('include/items.php');
foreach ($r as $rr) {
// pass uid of the message for permission check as we are running as a daemon process with no session.
drop_item($rr['id'], (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL), uid: intval($rr['uid']));
drop_item($rr['id'], false, (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL));
if ($rr['item_wall']) {
// The notifier isn't normally invoked unless item_drop is interactive.
Master::Summon(['Notifier', 'drop', $rr['id']]);
if ($interval) {
usleep($interval);
}
@@ -94,7 +79,7 @@ class Cron {
// delete expired access tokens
$r = q("select atoken_id from atoken where atoken_expires > '%s' and atoken_expires < %s",
dbesc(DBA::$dba->get_null_date()),
dbesc(NULL_DATE),
db_utcnow()
);
if ($r) {
@@ -127,15 +112,14 @@ class Cron {
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY')
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
);
if ($r) {
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval(Config::Get('system', 'default_expire_days', 30) . ' DAY')
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
);
foreach ($r as $rr) {
$file = dbunescbin($rr['content']);
if (is_file($file)) {
@@ -150,41 +134,52 @@ class Cron {
// (time travel posts). Restrict to items that have come of age in the last
// couple of days to limit the query to something reasonable.
$r = q("select * from item where item_delayed = 1 and created <= %s and created > '%s' ",
$r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ",
db_utcnow(),
dbesc(datetime_convert('UTC', 'UTC', 'now - 2 days'))
);
if ($r) {
xchan_query($r);
$items = fetch_post_tags($r);
foreach ($items as $item) {
$item['item_delayed'] = 0;
$post = item_store_update($item);
foreach ($r as $rr) {
$x = q("update item set item_delayed = 0 where id = %d",
intval($rr['id'])
);
if ($x) {
$z = q("select * from item where id = %d",
intval($rr['id'])
);
if ($z) {
xchan_query($z);
$sync_item = fetch_post_tags($z);
Libsync::build_sync_packet($sync_item[0]['uid'],
[
'item' => [encode_item($sync_item[0], true)]
]
);
}
Master::Summon(array('Notifier', 'wall-new', $rr['id']));
if($post['success']) {
Master::Summon(['Notifier', 'wall-new', $post['item_id']]);
if (!empty($post['approval_id'])) {
Master::Summon(['Notifier', 'wall-new', $post['approval_id']]);
if ($interval) {
usleep($interval);
}
}
if ($interval) {
usleep($interval);
}
}
}
}
}
require_once('include/attach.php');
attach_upgrade();
// once daily run birthday_updates and then expire in background
// FIXME: add birthday updates, both locally and for xprof for use
// by directory servers
$d1 = intval(Config::Get('system', 'last_expire_day'));
$d1 = intval(get_config('system', 'last_expire_day'));
$d2 = intval(datetime_convert('UTC', 'UTC', 'now', 'd'));
// Allow somebody to staggger daily activities if they have more than one site on their server,
// or if it happens at an inconvenient (busy) hour.
$h1 = intval(Config::Get('system', 'cron_hour'));
$h1 = intval(get_config('system', 'cron_hour'));
$h2 = intval(datetime_convert('UTC', 'UTC', 'now', 'G'));
@@ -218,7 +213,7 @@ class Cron {
// pull in some public posts if allowed
$disable_externals = Config::Get('system', 'disable_discover_tab') || Config::Get('system', 'disable_discover_tab') === false || Config::Get('system', 'site_firehose');
$disable_externals = get_config('system', 'disable_discover_tab') || get_config('system', 'disable_discover_tab') === false || get_config('system', 'site_firehose');
if (!$disable_externals)
Master::Summon(['Externals']);
@@ -238,74 +233,7 @@ class Cron {
if (!$restart)
Master::Summon(array('Cronhooks'));
// move as obj cache to fs
if (!Config::Get('system', 'as_objects_moved')) {
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'activitypub' and k = 'rawmsg' limit 300");
if ($results) {
foreach ($results as $result) {
if (is_string($result['v'])) {
if (str_starts_with($result['v'], '{')) {
$result['v'] = json_decode($result['v'], true);
}
elseif (str_starts_with($result['v'], 'json:')) {
$result['v'] = json_unserialize($result['v']);
}
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
}
}
if (is_array($result['v'])) {
ObjCache::Set($result['mid'], $result['v']);
}
q("delete from iconfig where id = %d",
intval($result['id'])
);
}
}
else {
Config::Set('system', 'as_objects_moved', 1);
}
}
// move diaspora obj cache to fs
if (!Config::Get('system', 'diaspora_objects_moved')) {
$results = dbq("select iconfig.*, item.mid from iconfig left join item on iid = item.id where cat = 'diaspora' and k = 'fields' limit 300");
if ($results) {
foreach ($results as $result) {
if (is_string($result['v'])) {
if (str_starts_with($result['v'], '{')) {
$result['v'] = json_decode($result['v'], true);
}
elseif (str_starts_with($result['v'], 'json:')) {
$result['v'] = json_unserialize($result['v']);
}
elseif (preg_match('|^a:[0-9]+:{.*}$|s', $result['v'])) {
$result['v'] = unserialize($result['v'], ['allowed_classes' => false]);
}
}
if (is_array($result['v'])) {
ObjCache::Set($result['mid'], $result['v'], 'diaspora');
}
q("delete from iconfig where id = %d",
intval($result['id'])
);
}
}
else {
Config::Set('system', 'diaspora_objects_moved', 1);
}
}
Config::Set('system', 'lastcron', datetime_convert());
set_config('system', 'lastcron', datetime_convert());
//All done - clear the lockfile
//@unlink($lockfile);

View File

@@ -2,7 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\Libzotdir;
class Cron_daily {
@@ -66,10 +65,10 @@ class Cron_daily {
}
}
// Clean up cache
// Clean up emdedded content cache
q("DELETE FROM cache WHERE updated < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval(Config::Get('system', 'cache_expire_days', 7) . ' DAY')
db_quoteinterval(get_config('system', 'active_expire_days', '30') . ' DAY')
);
//update statistics in config
@@ -83,7 +82,7 @@ class Cron_daily {
// expire old delivery reports
$keep_reports = intval(Config::Get('system', 'expire_delivery_reports'));
$keep_reports = intval(get_config('system', 'expire_delivery_reports'));
if ($keep_reports === 0)
$keep_reports = 10;
@@ -95,6 +94,17 @@ class Cron_daily {
// expire any expired accounts
downgrade_accounts();
// If this is a directory server, request a sync with an upstream
// directory at least once a day, up to once every poll interval.
// Pull remote changes and push local changes.
// potential issue: how do we keep from creating an endless update loop?
$dirmode = get_config('system', 'directory_mode');
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
Libzotdir::sync_directories($dirmode);
}
Master::Summon(array('Expire'));
Master::Summon(array('Cli_suggest'));
@@ -104,7 +114,7 @@ class Cron_daily {
$date = datetime_convert();
call_hooks('cron_daily', $date);
Config::Set('system', 'last_expire_day', intval(datetime_convert('UTC', 'UTC', 'now', 'd')));
set_config('system', 'last_expire_day', intval(datetime_convert('UTC', 'UTC', 'now', 'd')));
/**
* End Cron Daily

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
class Cron_weekly {
static public function run($argc, $argv) {
@@ -46,7 +44,7 @@ class Cron_weekly {
db_utcnow(), db_quoteinterval('14 DAY')
);
$dirmode = intval(Config::Get('system', 'directory_mode'));
$dirmode = intval(get_config('system', 'directory_mode'));
if ($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) {
logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())), true));
}

View File

@@ -2,7 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Libzotdir;
use Zotlabs\Lib\Queue;
@@ -26,7 +25,7 @@ class Directory {
logger('directory update', LOGGER_DEBUG);
$dirmode = Config::Get('system', 'directory_mode');
$dirmode = get_config('system', 'directory_mode');
if ($dirmode === false)
$dirmode = DIRECTORY_MODE_NORMAL;

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
require_once('include/items.php');
class Expire {
@@ -12,41 +10,42 @@ class Expire {
cli_startup();
$pid = Config::Get('procid', 'expire', false);
$pid = get_config('procid', 'expire', false);
if ($pid && (function_exists('posix_kill') ? posix_kill($pid, 0) : true)) {
logger('procedure already run with pid ' . $pid, LOGGER_DEBUG);
return;
}
$pid = getmypid();
Config::Set('procid', 'expire', $pid);
set_config('procid', 'expire', $pid);
// perform final cleanup on previously delete items
$r = q("select id, uid from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s",
$r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval('10 DAY')
);
if ($r) {
foreach ($r as $rr) {
drop_item($rr['id'], DROPITEM_PHASE2, uid: $rr['uid']);
drop_item($rr['id'], false, DROPITEM_PHASE2);
}
}
// physically remove anything that has been deleted for more than two months
/** @FIXME - this is a wretchedly inefficient query */
q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval('36 DAY')
);
if (intval(Config::Get('system', 'optimize_items')))
if (intval(get_config('system', 'optimize_items')))
q("optimize table item");
logger('expire: start with pid ' . $pid, LOGGER_DEBUG);
$site_expire = intval(Config::Get('system', 'default_expire_days', 30));
$commented_days = intval(Config::Get('system', 'active_expire_days', 7));
$site_expire = intval(get_config('system', 'default_expire_days'));
$commented_days = intval(get_config('system', 'active_expire_days'));
logger('site_expire: ' . $site_expire);
@@ -60,8 +59,8 @@ class Expire {
continue;
// service class default (if non-zero) over-rides the site default
$service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days');
$service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days');
if (intval($service_class_expire))
$channel_expire = $service_class_expire;
else
@@ -86,7 +85,8 @@ class Expire {
// this should probably just fetch the channel_expire_days from the sys channel,
// but there's no convenient way to set it.
$expire_days = Config::Get('system', 'sys_expire_days');
$expire_days = get_config('system', 'sys_expire_days');
if ($expire_days === false)
$expire_days = 30;
@@ -96,14 +96,13 @@ class Expire {
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
if ($expire_days) {
if ($expire_days)
item_expire($x['channel_id'], $expire_days, $commented_days);
}
logger('Expire: sys: done', LOGGER_DEBUG);
}
Config::Delete('procid', 'expire');
del_config('procid', 'expire');
return;
}

View File

@@ -74,8 +74,6 @@ class Externals {
}
}
$attempts++;
if (!$url) {
continue;
}
@@ -87,6 +85,7 @@ class Externals {
$blacklisted = true;
}
$attempts++;
// make sure we can eventually break out if somebody blacklists all known sites
@@ -144,7 +143,6 @@ class Externals {
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
$item['item_fetched'] = true;
Activity::store($importer, $contact['hubloc_hash'], $AS, $item);
$total++;
}

View File

@@ -1,44 +0,0 @@
<?php
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Activity;
class Fetchparents {
static public function run($argc, $argv) {
logger('Fetchparents invoked: ' . print_r($argv, true));
if ($argc < 4) {
return;
}
$channels = explode(',', $argv[1]);
if (!$channels) {
return;
}
$observer_hash = $argv[2];
if (!$observer_hash) {
return;
}
$mid = $argv[3];
if (!$mid) {
return;
}
$force = $argv[4] ?? false;
foreach ($channels as $channel_id) {
$channel = channelx_by_n($channel_id);
Activity::fetch_and_store_parents($channel, $observer_hash, $mid, null, $force);
}
Activity::init_background_fetch($observer_hash);
return;
}
}

View File

@@ -38,13 +38,7 @@ class File_importer {
$headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),true,'sha512');
// TODO: implement total count
$redirects = 0;
$x = z_fetch_url(
$hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page,
false,
$redirects,
[ 'headers' => $headers ]
);
$x = z_fetch_url($hz_server . '/api/z/1.0/file/export_page?f=records=1&page=' . $page, false, $redirects, [ 'headers' => $headers ]);
// logger('file fetch: ' . print_r($x,true));
if(! $x['success']) {

View File

@@ -11,21 +11,6 @@ class Importdoc {
self::update_docs_dir('doc/*');
$sys = get_sys_channel();
// remove old files that weren't updated (indicates they were most likely deleted).
$i = q("select id from item where uid = %d and item_type = 5 and edited < %s - INTERVAL %s",
intval($sys['channel_id']),
db_utcnow(),
db_quoteinterval('14 DAY')
);
if ($i) {
foreach ($i as $iv) {
drop_item($iv['id'], uid: $sys['channel_id']);
}
}
return;
}
@@ -33,13 +18,9 @@ class Importdoc {
static public function update_docs_dir($s) {
$f = basename($s);
$d = dirname($s);
if ($s === 'doc/html') {
if ($s === 'doc/html')
return;
}
$files = glob("$d/$f");
if ($files) {
foreach ($files as $fi) {
if ($fi === 'doc/html') {

View File

@@ -37,7 +37,7 @@ class Master {
return;
}
$phpbin = Config::Get('system', 'phpbin', 'php');
$phpbin = get_config('system', 'phpbin', 'php');
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', $arr);
*/
}

View File

@@ -2,12 +2,10 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\IConfig;
use Zotlabs\Lib\ObjCache;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\Queue;
use Zotlabs\Lib\LDSignatures;
require_once('include/html2plain.php');
require_once('include/conversation.php');
@@ -243,6 +241,11 @@ class Notifier {
$target_item = $r[0];
if (in_array($target_item['author']['xchan_network'], ['rss', 'anon', 'token'])) {
logger('notifier: target item author is not a fetchable actor', LOGGER_DEBUG);
return;
}
if (intval($target_item['item_deleted'])) {
logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG);
}
@@ -265,8 +268,23 @@ class Notifier {
}
if (!item_forwardable($target_item)) {
logger('notifier: target item not forwardable', LOGGER_DEBUG);
// Check for non published items, but allow an exclusion for transmitting hidden file activities
if (intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
intval($target_item['item_blocked']) ||
(intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) {
logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
return;
}
// follow/unfollow is for internal use only
if (in_array($target_item['verb'], [ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW])) {
logger('not fowarding follow/unfollow note activity');
return;
}
if (strpos($target_item['postopts'], 'nodeliver') !== false) {
logger('notifier: target item is undeliverable', LOGGER_DEBUG);
return;
}
@@ -282,11 +300,6 @@ class Notifier {
return;
}
if (in_array($target_item['verb'], [ACTIVITY_SHARE])) {
// Provide correct representation across the wire. Internally this is treated as a comment.
$target_item['parent_mid'] = $target_item['thr_parent'] = $target_item['mid'];
}
if ($target_item['mid'] === $target_item['parent_mid']) {
$parent_item = $target_item;
$top_level_post = true;
@@ -318,24 +331,20 @@ class Notifier {
return;
}
$m = ObjCache::Get($target_item['mid']);
if (!$m) {
$m = IConfig::Get($target_item, 'activitypub', 'rawmsg');
}
$m = get_iconfig($target_item, 'activitypub', 'signed_data');
// Re-use existing signature unless the activity type changed to a Tombstone, which won't verify.
if ($m && (!intval($target_item['item_deleted']))) {
self::$encoded_item = $m;
self::$encoded_item = json_decode($m, true);
}
else {
$activity = Activity::encode_activity($target_item);
if (!$activity) {
return;
}
self::$encoded_item = Activity::build_packet($activity, self::$channel, false);
self::$encoded_item = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], Activity::encode_activity($target_item)
);
self::$encoded_item['signature'] = LDSignatures::sign(self::$encoded_item, self::$channel);
}
logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG);
@@ -352,10 +361,6 @@ class Notifier {
$relay_to_owner = (!$top_level_post && intval($target_item['item_origin']) && comment_local_origin($target_item));
if (self::$channel['channel_hash'] === $target_item['owner_xchan']) {
$relay_to_owner = false;
}
// $cmd === 'relay' indicates the owner is sending it to the original recipients
// don't allow the item in the relay command to relay to owner under any circumstances, it will loop
@@ -386,7 +391,7 @@ class Notifier {
logger('normal (downstream) distribution', LOGGER_DEBUG);
}
if (($parent_item && $parent_item['item_private'] !== $target_item['item_private']) || (intval($target_item['item_restrict']) & 1)) {
if ($parent_item && $parent_item['item_private'] !== $target_item['item_private']) {
logger('conversation privacy mismatch - downstream delivery prevented');
return;
}
@@ -404,7 +409,9 @@ class Notifier {
self::$private = false;
self::$recipients = collect_recipients($parent_item, self::$private);
if ($top_level_post && intval($target_item['item_wall'])) {
// FIXME add any additional recipients such as mentions, etc.
if ($top_level_post) {
// remove clones who will receive the post via sync
self::$recipients = array_values(array_diff(self::$recipients, [$target_item['owner_xchan']]));
}
@@ -586,6 +593,8 @@ class Notifier {
foreach ($dhubs as $hub) {
logger('notifier_hub: ' . $hub['hubloc_url'], LOGGER_DEBUG);
if ($hub['hubloc_network'] !== 'zot6') {
$narr = [
'channel' => self::$channel,
@@ -664,7 +673,7 @@ class Notifier {
);
// only create delivery reports for normal undeleted items
if (is_array($target_item) && (!$target_item['item_deleted']) && (!Config::Get('system', 'disable_dreport'))) {
if (is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) {
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ",
dbesc($target_item['mid']),
@@ -694,7 +703,7 @@ class Notifier {
do_delivery(self::$deliveries);
}
if ($dead_hosts && is_array($target_item) && (!$target_item['item_deleted']) && (!Config::Get('system', 'disable_dreport'))) {
if ($dead_hosts && is_array($target_item) && (!$target_item['item_deleted']) && (!get_config('system', 'disable_dreport'))) {
foreach ($dead_hosts as $deceased_host) {
$r = q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan, dreport_queue )
values ( '%s', '%s','%s','%s','%s','%s','%s','%s' ) ",

View File

@@ -9,55 +9,67 @@ class Onedirsync {
static public function run($argc, $argv) {
if ($argc < 2 || is_int($argv[1]) === false) {
logger('onedirsync: no update id');
return;
}
logger('onedirsync: start ' . intval($argv[1]));
$update_id = intval($argv[1]);
if (($argc > 1) && (intval($argv[1])))
$update_id = intval($argv[1]);
if (!$update_id) {
logger('onedirsync: no update id');
logger('onedirsync: no update');
return;
}
$r = q("select * from updates where ud_id = %d",
$r = q("select * from updates where ud_id = %d limit 1",
intval($update_id)
);
if (!$r) {
logger('onedirsync: update id not found');
if (!$r)
return;
if (($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (!$r[0]['ud_addr']))
return;
// Have we probed this channel more recently than the other directory server
// (where we received this update from) ?
// If we have, we don't need to do anything except mark any older entries updated
$x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1",
dbesc($r[0]['ud_addr']),
dbesc($r[0]['ud_date']),
intval(UPDATE_FLAGS_UPDATED)
);
if ($x) {
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'",
intval(UPDATE_FLAGS_UPDATED),
dbesc($r[0]['ud_addr']),
intval(UPDATE_FLAGS_UPDATED),
dbesc($x[0]['ud_date'])
);
return;
}
// ignore doing an update if this ud_addr refers to a known dead hubloc
$h = q("select * from hubloc where hubloc_id_url = '%s' order by hubloc_id desc",
$h = q("select * from hubloc where hubloc_addr = '%s'",
dbesc($r[0]['ud_addr']),
);
$h = Libzot::zot_record_preferred($h);
if (($h) && (($h['hubloc_status'] & HUBLOC_OFFLINE) || $h['hubloc_deleted'] || $h['hubloc_error'])) {
// 2023-04-12: Try to update anyway since the info is not always correct
// This might change after all directory servers run the new code.
// q("update updates set ud_flags = 9 where ud_hash = '%s' and ud_flags != 9",
// dbesc($r[0]['ud_hash'])
//);
// return;
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ",
intval(UPDATE_FLAGS_DELETED),
dbesc($r[0]['ud_addr']),
intval(UPDATE_FLAGS_UPDATED)
);
return;
}
// we might have to pull this out some day, but for now update_directory_entry()
// runs zot_finger() and is kind of zot specific
if ($h && $h['hubloc_network'] !== 'zot6') {
if ($h && $h['hubloc_network'] !== 'zot6')
return;
}
Libzotdir::update_directory_entry($r[0]);

View File

@@ -2,11 +2,9 @@
namespace Zotlabs\Daemon;
use DBA;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\ASCollection;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\Libzot;
require_once('include/socgraph.php');
@@ -16,14 +14,10 @@ class Onepoll {
static public function run($argc, $argv) {
if ($argc < 2 || is_int($argv[1]) === false) {
logger('onepoll: no contact');
return;
}
logger('onepoll: start');
$contact_id = intval($argv[1]);
if (($argc > 1) && (intval($argv[1])))
$contact_id = intval($argv[1]);
if (!$contact_id) {
logger('onepoll: no contact');
@@ -31,15 +25,19 @@ class Onepoll {
}
$sql_extra = '';
$allow_feeds = Config::Get('system', 'feed_contacts');
$allow_feeds = get_config('system', 'feed_contacts');
if(!$allow_feeds) {
$sql_extra = ' and abook_feed = 0 ';
}
$contacts = q("SELECT abook.*, xchan.* FROM abook
LEFT JOIN xchan ON xchan_hash = abook_xchan
WHERE abook_id = %d",
$contact_id
$contacts = q("SELECT abook.*, xchan.*, account.*
FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan
where abook_id = %d $sql_extra
and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0
AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1",
intval($contact_id),
intval(ACCOUNT_OK),
intval(ACCOUNT_UNVERIFIED)
);
if (!$contacts) {
@@ -58,7 +56,7 @@ class Onepoll {
logger("onepoll: poll: ($contact_id) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}");
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= DBA::$dba->get_null_date()))
$last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] <= NULL_DATE))
? datetime_convert('UTC', 'UTC', 'now - 7 days')
: datetime_convert('UTC', 'UTC', $contact['abook_updated'] . ' - 2 days')
);
@@ -131,7 +129,7 @@ class Onepoll {
if ($fetch_feed) {
$max = intval(Config::Get('system', 'max_imported_posts', 30));
$max = intval(get_config('system', 'max_imported_posts', 30));
if (intval($max)) {
$cl = Activity::get_actor_collections($contact['abook_xchan']);
@@ -174,7 +172,6 @@ class Onepoll {
$AS = new ActivityStreams($message);
if ($AS->is_valid() && is_array($AS->obj)) {
$item = Activity::decode_note($AS);
$item['item_fetched'] = true;
Activity::store($importer, $contact['abook_xchan'], $AS, $item);
}
}

View File

@@ -2,14 +2,11 @@
namespace Zotlabs\Daemon;
use DBA;
use Zotlabs\Lib\Config;
class Poller {
static public function run($argc, $argv) {
$maxsysload = intval(Config::Get('system', 'maxloadavg'));
$maxsysload = intval(get_config('system', 'maxloadavg'));
if ($maxsysload < 1)
$maxsysload = 50;
if (function_exists('sys_getloadavg')) {
@@ -20,7 +17,25 @@ class Poller {
}
}
$interval = Config::Get('queueworker', 'queue_interval', 500000);
$interval = get_config('queueworker', 'queue_interval', 500000);
/*
if (!$interval) {
$interval = ((get_config('system', 'delivery_interval') === false) ? 3 : intval(get_config('system', 'delivery_interval')));
}
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
$lockfile = 'store/[data]/poller';
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))
&& (!get_config('system', 'override_poll_lockfile'))) {
logger("poller: Already running");
return;
}
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
$x = '';
file_put_contents($lockfile, $x);
*/
logger('poller: start');
@@ -46,17 +61,12 @@ class Poller {
reload_plugins();
// Only poll from those with suitable relationships
$abandon_days = intval(Config::Get('system', 'account_abandon_days', 0));
$abandon_days = intval(get_config('system', 'account_abandon_days', 0));
$abandon_sql = (($abandon_days)
? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days) . ' DAY'))
: ''
);
$allow_feeds = Config::Get('system', 'feed_contacts');
if(!$allow_feeds) {
$sql_extra .= ' and abook_feed = 0 ';
}
$randfunc = db_getfunc('RAND');
$contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed,
@@ -66,7 +76,7 @@ class Poller {
account.account_lastlog, account.account_flags
FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash
LEFT JOIN account on abook_account = account_id
where abook_self = 0 and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0
where abook_self = 0
$sql_extra
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
intval(ACCOUNT_OK),
@@ -84,7 +94,7 @@ class Poller {
if (intval($contact['abook_feed'])) {
$min = service_class_fetch($contact['abook_channel'], 'minimum_feedcheck_minutes');
if (!$min)
$min = intval(Config::Get('system', 'minimum_feedcheck_minutes'));
$min = intval(get_config('system', 'minimum_feedcheck_minutes'));
if (!$min)
$min = 60;
@@ -118,7 +128,7 @@ class Poller {
// if we've never connected with them, start the mark for death countdown from now
if ($c <= DBA::$dba->get_null_date()) {
if ($c <= NULL_DATE) {
q("update abook set abook_connected = '%s' where abook_id = %d",
dbesc(datetime_convert()),
intval($contact['abook_id'])
@@ -170,22 +180,21 @@ class Poller {
}
}
$dirmode = intval(Config::Get('system', 'directory_mode'));
$dirmode = intval(get_config('system', 'directory_mode'));
if ($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
$r = q("SELECT * FROM updates WHERE ud_update = 1 AND (ud_last = '%s' OR ud_last > %s - INTERVAL %s)",
dbesc(DBA::$dba->get_null_date()),
db_utcnow(),
db_quoteinterval('7 DAY')
$r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last <= '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ",
intval(UPDATE_FLAGS_UPDATED),
dbesc(NULL_DATE),
db_utcnow(), db_quoteinterval('7 DAY')
);
if ($r) {
foreach ($r as $rr) {
// If they didn't respond when we attempted before, back off to once a day
// After 7 days we won't bother anymore
if ($rr['ud_last'] > DBA::$dba->get_null_date())
if ($rr['ud_last'] > NULL_DATE)
if ($rr['ud_last'] > datetime_convert('UTC', 'UTC', 'now - 1 day'))
continue;
@@ -198,8 +207,12 @@ class Poller {
}
}
Config::Set('system', 'lastpoll', datetime_convert());
set_config('system', 'lastpoll', datetime_convert());
//All done - clear the lockfile
/*
@unlink($lockfile);
*/
return;
}
}

View File

@@ -7,59 +7,79 @@ use Zotlabs\Lib\Queue as LibQueue;
class Queue {
static public function run($argc, $argv) {
$queue_id = ($argc > 1) ? $argv[1] : '';
require_once('include/items.php');
require_once('include/bbcode.php');
if ($argc > 1)
$queue_id = $argv[1];
else
$queue_id = EMPTY_STR;
logger('queue: start');
// delete all queue items more than 3 days old
// but first mark these sites dead if we haven't heard from them in a month
$oldqItems = q("select outq_posturl, outq_hash from outq where outq_created < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval('3 DAY')
$r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s",
db_utcnow(), db_quoteinterval('3 DAY')
);
if ($oldqItems) {
foreach ($oldqItems as $qItem) {
$h = parse_url($qItem['outq_posturl']);
$site_url = $h['scheme'] . '://' . $h['host'] . ((!empty($h['port'])) ? ':' . $h['port'] : '');
if ($r) {
foreach ($r as $rr) {
$h = parse_url($rr['outq_posturl']);
$desturl = $h['scheme'] . '://' . $h['host'] . (isset($h['port']) ? ':' . $h['port'] : '');
q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s",
dbesc($site_url),
db_utcnow(),
db_quoteinterval('1 MONTH')
dbesc($desturl),
db_utcnow(), db_quoteinterval('1 MONTH')
);
}
$old_hashes = ids_to_querystr($oldqItems, 'outq_hash', true);
logger('Removing ' . count($oldqItems) . ' old queue entries');
dbq("DELETE FROM outq WHERE outq_hash IN ($old_hashes)");
}
$deliveries = [];
q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s",
db_utcnow(), db_quoteinterval('3 DAY')
);
if ($queue_id) {
$qItems = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
$r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1",
dbesc($queue_id)
);
logger('queue deliver: ' . $qItems[0]['outq_hash'] . ' to ' . $qItems[0]['outq_posturl'], LOGGER_DEBUG);
LibQueue::deliver($qItems[0]);
}
else {
$qItems = q("SELECT outq_hash FROM outq WHERE outq_scheduled < %s ",
// For the first 12 hours we'll try to deliver every 15 minutes
// After that, we'll only attempt delivery once per hour.
// This currently only handles the default queue drivers ('zot' or '') which we will group by posturl
// so that we don't start off a thousand deliveries for a couple of dead hubs.
// The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made).
// Other drivers will have to do something different here and may need their own query.
// Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the
// "every 15 minutes" category. We probably need to prioritise them when inserted into the queue
// or just prior to this query based on recent and long-term delivery history. If we have good reason to believe
// 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.
$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()
);
if ($qItems) {
foreach ($qItems as $qItem) {
$deliveries[] = $qItem['outq_hash'];
while ($r) {
foreach ($r as $rv) {
LibQueue::deliver($rv);
}
shuffle($deliveries);
do_delivery($deliveries, true);
$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)
return;
foreach ($r as $rv) {
LibQueue::deliver($rv);
}
return;
}
}

View File

@@ -2,7 +2,6 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Config;
class Thumbnail {
@@ -20,9 +19,9 @@ class Thumbnail {
$attach = $c[0];
$preview_style = intval(Config::Get('system', 'thumbnail_security', 0));
$preview_width = intval(Config::Get('system', 'thumbnail_width', 300));
$preview_height = intval(Config::Get('system', 'thumbnail_height', 300));
$preview_style = intval(get_config('system', 'thumbnail_security', 0));
$preview_width = intval(get_config('system', 'thumbnail_width', 300));
$preview_height = intval(get_config('system', 'thumbnail_height', 300));
$p = [
'attach' => $attach,

View File

@@ -1,37 +0,0 @@
<?php
/** @file */
namespace Zotlabs\Daemon;
class Xchan_photo {
static public function run($argc, $argv) {
if ($argc < 3) {
return;
}
$url = hex2bin($argv[1]);
$xchan = hex2bin($argv[2]);
$force = $argv[3];
$photos = import_xchan_photo($url, $xchan, false, $force);
if ($photos) {
$result = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
dbescdate(datetime_convert()),
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
dbesc($photos[3]),
dbesc($xchan)
);
if (! $result) {
logger("xchan photo update failed for $url");
}
}
return;
}
}

View File

@@ -1,37 +0,0 @@
<?php
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzot;
class Zotconvo {
static public function run($argc, $argv) {
logger('Zotconvo invoked: ' . print_r($argv, true));
if ($argc < 3) {
return;
}
$channels = explode(',', $argv[1]);
if (!$channels) {
return;
}
$mid = $argv[2];
if (!$mid) {
return;
}
$force = $argv[3] ?? false;
foreach ($channels as $channel_id) {
$channel = channelx_by_n($channel_id);
Libzot::fetch_conversation($channel, $mid, $force);
}
return;
}
}

View File

@@ -1,352 +0,0 @@
<?php
namespace Zotlabs\Entity;
use Zotlabs\Lib\BaseObject;
class Account extends BaseObject
{
public $account_id;
public $account_parent;
public $account_default_channel;
public $account_salt;
public $account_password;
public $account_email;
public $account_external;
public $account_language;
public $account_created;
public $account_lastlog;
public $account_flags;
public $account_roles;
public $account_reset;
public $account_expires;
public $account_expire_notified;
public $account_service_class;
public $account_level;
public $account_password_change;
/**
* @return mixed
*/
public function getId()
{
return $this->account_id;
}
/**
* @param mixed $account_id
* @return Account
*/
public function setId($account_id)
{
$this->account_id = $account_id;
return $this;
}
/**
* @return mixed
*/
public function getParent()
{
return $this->account_parent;
}
/**
* @param mixed $account_parent
* @return Account
*/
public function setParent($account_parent)
{
$this->account_parent = $account_parent;
return $this;
}
/**
* @return mixed
*/
public function getDefaultChannel()
{
return $this->account_default_channel;
}
/**
* @param mixed $account_default_channel
* @return Account
*/
public function setDefaultChannel($account_default_channel)
{
$this->account_default_channel = $account_default_channel;
return $this;
}
/**
* @return mixed
*/
public function getSalt()
{
return $this->account_salt;
}
/**
* @param mixed $account_salt
* @return Account
*/
public function setSalt($account_salt)
{
$this->account_salt = $account_salt;
return $this;
}
/**
* @return mixed
*/
public function getPassword()
{
return $this->account_password;
}
/**
* @param mixed $account_password
* @return Account
*/
public function setPassword($account_password)
{
$this->account_password = $account_password;
return $this;
}
/**
* @return mixed
*/
public function getEmail()
{
return $this->account_email;
}
/**
* @param mixed $account_email
* @return Account
*/
public function setEmail($account_email)
{
$this->account_email = $account_email;
return $this;
}
/**
* @return mixed
*/
public function getExternal()
{
return $this->account_external;
}
/**
* @param mixed $account_external
* @return Account
*/
public function setExternal($account_external)
{
$this->account_external = $account_external;
return $this;
}
/**
* @return mixed
*/
public function getLanguage()
{
return $this->account_language;
}
/**
* @param mixed $account_language
* @return Account
*/
public function setLanguage($account_language)
{
$this->account_language = $account_language;
return $this;
}
/**
* @return mixed
*/
public function getCreated()
{
return $this->account_created;
}
/**
* @param mixed $account_created
* @return Account
*/
public function setCreated($account_created)
{
$this->account_created = $account_created;
return $this;
}
/**
* @return mixed
*/
public function getLastlog()
{
return $this->account_lastlog;
}
/**
* @param mixed $account_lastlog
* @return Account
*/
public function setLastlog($account_lastlog)
{
$this->account_lastlog = $account_lastlog;
return $this;
}
/**
* @return mixed
*/
public function getFlags()
{
return $this->account_flags;
}
/**
* @param mixed $account_flags
* @return Account
*/
public function setFlags($account_flags)
{
$this->account_flags = $account_flags;
return $this;
}
/**
* @return mixed
*/
public function getRoles()
{
return $this->account_roles;
}
/**
* @param mixed $account_roles
* @return Account
*/
public function setRoles($account_roles)
{
$this->account_roles = $account_roles;
return $this;
}
/**
* @return mixed
*/
public function getReset()
{
return $this->account_reset;
}
/**
* @param mixed $account_reset
* @return Account
*/
public function setReset($account_reset)
{
$this->account_reset = $account_reset;
return $this;
}
/**
* @return mixed
*/
public function getExpires()
{
return $this->account_expires;
}
/**
* @param mixed $account_expires
* @return Account
*/
public function setExpires($account_expires)
{
$this->account_expires = $account_expires;
return $this;
}
/**
* @return mixed
*/
public function getExpireNotified()
{
return $this->account_expire_notified;
}
/**
* @param mixed $account_expire_notified
* @return Account
*/
public function setExpireNotified($account_expire_notified)
{
$this->account_expire_notified = $account_expire_notified;
return $this;
}
/**
* @return mixed
*/
public function getServiceClass()
{
return $this->account_service_class;
}
/**
* @param mixed $account_service_class
* @return Account
*/
public function setServiceClass($account_service_class)
{
$this->account_service_class = $account_service_class;
return $this;
}
/**
* @return mixed
*/
public function getLevel()
{
return $this->account_level;
}
/**
* @param mixed $account_level
* @return Account
*/
public function setLevel($account_level)
{
$this->account_level = $account_level;
return $this;
}
/**
* @return mixed
*/
public function getPasswordChange()
{
return $this->account_password_change;
}
/**
* @param mixed $account_password_change
* @return Account
*/
public function setPasswordChange($account_password_change)
{
$this->account_password_change = $account_password_change;
return $this;
}
}

View File

@@ -1,714 +0,0 @@
<?php
namespace Zotlabs\Entity;
use Zotlabs\Lib\BaseObject;
class Channel extends BaseObject
{
public $channel_id;
public $channel_account_id;
public $channel_primary;
public $channel_name;
public $channel_parent;
public $channel_address;
public $channel_guid;
public $channel_guid_sig;
public $channel_hash;
public $channel_timezone;
public $channel_location;
public $channel_theme;
public $channel_startpage;
public $channel_pubkey;
public $channel_prvkey;
public $channel_epubkey;
public $channel_eprvkey;
public $channel_notifyflags;
public $channel_pageflags;
public $channel_dirdate;
public $channel_lastpost;
public $channel_deleted;
public $channel_active;
public $channel_max_anon_mail;
public $channel_max_friend_req;
public $channel_expire_days;
public $channel_passwd_reset;
public $channel_default_group;
public $channel_allow_cid;
public $channel_allow_gid;
public $channel_deny_cid;
public $channel_deny_gid;
public $channel_removed;
public $channel_system;
public $channel_moved;
public $channel_password;
public $channel_salt;
/**
* @return mixed
*/
public function getId()
{
return $this->channel_id;
}
/**
* @param mixed $channel_id
* @return Channel
*/
public function setId($channel_id)
{
$this->channel_id = $channel_id;
return $this;
}
/**
* @return mixed
*/
public function getAccountId()
{
return $this->channel_account_id;
}
/**
* @param mixed $channel_account_id
* @return Channel
*/
public function setAccountId($channel_account_id)
{
$this->channel_account_id = $channel_account_id;
return $this;
}
/**
* @return mixed
*/
public function getPrimary()
{
return $this->channel_primary;
}
/**
* @param mixed $channel_primary
* @return Channel
*/
public function setPrimary($channel_primary)
{
$this->channel_primary = $channel_primary;
return $this;
}
/**
* @return mixed
*/
public function getName()
{
return $this->channel_name;
}
/**
* @param mixed $channel_name
* @return Channel
*/
public function setName($channel_name)
{
$this->channel_name = $channel_name;
return $this;
}
/**
* @return mixed
*/
public function getParent()
{
return $this->channel_parent;
}
/**
* @param mixed $channel_parent
* @return Channel
*/
public function setParent($channel_parent)
{
$this->channel_parent = $channel_parent;
return $this;
}
/**
* @return mixed
*/
public function getAddress()
{
return $this->channel_address;
}
/**
* @param mixed $channel_address
* @return Channel
*/
public function setAddress($channel_address)
{
$this->channel_address = $channel_address;
return $this;
}
/**
* @return mixed
*/
public function getGuid()
{
return $this->channel_guid;
}
/**
* @param mixed $channel_guid
* @return Channel
*/
public function setGuid($channel_guid)
{
$this->channel_guid = $channel_guid;
return $this;
}
/**
* @return mixed
*/
public function getGuidSig()
{
return $this->channel_guid_sig;
}
/**
* @param mixed $channel_guid_sig
* @return Channel
*/
public function setGuidSig($channel_guid_sig)
{
$this->channel_guid_sig = $channel_guid_sig;
return $this;
}
/**
* @return mixed
*/
public function getHash()
{
return $this->channel_hash;
}
/**
* @param mixed $channel_hash
* @return Channel
*/
public function setHash($channel_hash)
{
$this->channel_hash = $channel_hash;
return $this;
}
/**
* @return mixed
*/
public function getTimezone()
{
return $this->channel_timezone;
}
/**
* @param mixed $channel_timezone
* @return Channel
*/
public function setTimezone($channel_timezone)
{
$this->channel_timezone = $channel_timezone;
return $this;
}
/**
* @return mixed
*/
public function getLocation()
{
return $this->channel_location;
}
/**
* @param mixed $channel_location
* @return Channel
*/
public function setLocation($channel_location)
{
$this->channel_location = $channel_location;
return $this;
}
/**
* @return mixed
*/
public function getTheme()
{
return $this->channel_theme;
}
/**
* @param mixed $channel_theme
* @return Channel
*/
public function setTheme($channel_theme)
{
$this->channel_theme = $channel_theme;
return $this;
}
/**
* @return mixed
*/
public function getStartpage()
{
return $this->channel_startpage;
}
/**
* @param mixed $channel_startpage
* @return Channel
*/
public function setStartpage($channel_startpage)
{
$this->channel_startpage = $channel_startpage;
return $this;
}
/**
* @return mixed
*/
public function getPubkey()
{
return $this->channel_pubkey;
}
/**
* @param mixed $channel_pubkey
* @return Channel
*/
public function setPubkey($channel_pubkey)
{
$this->channel_pubkey = $channel_pubkey;
return $this;
}
/**
* @return mixed
*/
public function getPrvkey()
{
return $this->channel_prvkey;
}
/**
* @param mixed $channel_prvkey
* @return Channel
*/
public function setPrvkey($channel_prvkey)
{
$this->channel_prvkey = $channel_prvkey;
return $this;
}
/**
* @return mixed
*/
public function getEpubkey()
{
return $this->channel_epubkey;
}
/**
* @param mixed $channel_epubkey
* @return Channel
*/
public function setEpubkey($channel_epubkey)
{
$this->channel_epubkey = $channel_epubkey;
return $this;
}
/**
* @return mixed
*/
public function getEprvkey()
{
return $this->channel_eprvkey;
}
/**
* @param mixed $channel_eprvkey
* @return Channel
*/
public function setEprvkey($channel_eprvkey)
{
$this->channel_eprvkey = $channel_eprvkey;
return $this;
}
/**
* @return mixed
*/
public function getNotifyflags()
{
return $this->channel_notifyflags;
}
/**
* @param mixed $channel_notifyflags
* @return Channel
*/
public function setNotifyflags($channel_notifyflags)
{
$this->channel_notifyflags = $channel_notifyflags;
return $this;
}
/**
* @return mixed
*/
public function getPageflags()
{
return $this->channel_pageflags;
}
/**
* @param mixed $channel_pageflags
* @return Channel
*/
public function setPageflags($channel_pageflags)
{
$this->channel_pageflags = $channel_pageflags;
return $this;
}
/**
* @return mixed
*/
public function getDirdate()
{
return $this->channel_dirdate;
}
/**
* @param mixed $channel_dirdate
* @return Channel
*/
public function setDirdate($channel_dirdate)
{
$this->channel_dirdate = $channel_dirdate;
return $this;
}
/**
* @return mixed
*/
public function getLastpost()
{
return $this->channel_lastpost;
}
/**
* @param mixed $channel_lastpost
* @return Channel
*/
public function setLastpost($channel_lastpost)
{
$this->channel_lastpost = $channel_lastpost;
return $this;
}
/**
* @return mixed
*/
public function getDeleted()
{
return $this->channel_deleted;
}
/**
* @param mixed $channel_deleted
* @return Channel
*/
public function setDeleted($channel_deleted)
{
$this->channel_deleted = $channel_deleted;
return $this;
}
/**
* @return mixed
*/
public function getActive()
{
return $this->channel_active;
}
/**
* @param mixed $channel_active
* @return Channel
*/
public function setActive($channel_active)
{
$this->channel_active = $channel_active;
return $this;
}
/**
* @return mixed
*/
public function getMaxAnonMail()
{
return $this->channel_max_anon_mail;
}
/**
* @param mixed $channel_max_anon_mail
* @return Channel
*/
public function setMaxAnonMail($channel_max_anon_mail)
{
$this->channel_max_anon_mail = $channel_max_anon_mail;
return $this;
}
/**
* @return mixed
*/
public function getMaxFriendReq()
{
return $this->channel_max_friend_req;
}
/**
* @param mixed $channel_max_friend_req
* @return Channel
*/
public function setMaxFriendReq($channel_max_friend_req)
{
$this->channel_max_friend_req = $channel_max_friend_req;
return $this;
}
/**
* @return mixed
*/
public function getExpireDays()
{
return $this->channel_expire_days;
}
/**
* @param mixed $channel_expire_days
* @return Channel
*/
public function setExpireDays($channel_expire_days)
{
$this->channel_expire_days = $channel_expire_days;
return $this;
}
/**
* @return mixed
*/
public function getPasswdReset()
{
return $this->channel_passwd_reset;
}
/**
* @param mixed $channel_passwd_reset
* @return Channel
*/
public function setPasswdReset($channel_passwd_reset)
{
$this->channel_passwd_reset = $channel_passwd_reset;
return $this;
}
/**
* @return mixed
*/
public function getDefaultGroup()
{
return $this->channel_default_group;
}
/**
* @param mixed $channel_default_group
* @return Channel
*/
public function setDefaultGroup($channel_default_group)
{
$this->channel_default_group = $channel_default_group;
return $this;
}
/**
* @return mixed
*/
public function getAllowCid()
{
return $this->channel_allow_cid;
}
/**
* @param mixed $channel_allow_cid
* @return Channel
*/
public function setAllowCid($channel_allow_cid)
{
$this->channel_allow_cid = $channel_allow_cid;
return $this;
}
/**
* @return mixed
*/
public function getAllowGid()
{
return $this->channel_allow_gid;
}
/**
* @param mixed $channel_allow_gid
* @return Channel
*/
public function setAllowGid($channel_allow_gid)
{
$this->channel_allow_gid = $channel_allow_gid;
return $this;
}
/**
* @return mixed
*/
public function getDenyCid()
{
return $this->channel_deny_cid;
}
/**
* @param mixed $channel_deny_cid
* @return Channel
*/
public function setDenyCid($channel_deny_cid)
{
$this->channel_deny_cid = $channel_deny_cid;
return $this;
}
/**
* @return mixed
*/
public function getDenyGid()
{
return $this->channel_deny_gid;
}
/**
* @param mixed $channel_deny_gid
* @return Channel
*/
public function setDenyGid($channel_deny_gid)
{
$this->channel_deny_gid = $channel_deny_gid;
return $this;
}
/**
* @return mixed
*/
public function getRemoved()
{
return $this->channel_removed;
}
/**
* @param mixed $channel_removed
* @return Channel
*/
public function setRemoved($channel_removed)
{
$this->channel_removed = $channel_removed;
return $this;
}
/**
* @return mixed
*/
public function getSystem()
{
return $this->channel_system;
}
/**
* @param mixed $channel_system
* @return Channel
*/
public function setSystem($channel_system)
{
$this->channel_system = $channel_system;
return $this;
}
/**
* @return mixed
*/
public function getMoved()
{
return $this->channel_moved;
}
/**
* @param mixed $channel_moved
* @return Channel
*/
public function setMoved($channel_moved)
{
$this->channel_moved = $channel_moved;
return $this;
}
/**
* @return mixed
*/
public function getPassword()
{
return $this->channel_password;
}
/**
* @param mixed $channel_password
* @return Channel
*/
public function setPassword($channel_password)
{
$this->channel_password = $channel_password;
return $this;
}
/**
* @return mixed
*/
public function getSalt()
{
return $this->channel_salt;
}
/**
* @param mixed $channel_salt
* @return Channel
*/
public function setSalt($channel_salt)
{
$this->channel_salt = $channel_salt;
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,57 +1,15 @@
<?php
/*
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
* SPDX-FileContributor: redmatrix
* SPDX-FileContributor: Klaus Weidenbach
* SPDX-FileContributor: zotlabs
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Extend;
use App;
/**
* A class for hooking into Hubzilla.
* @brief Hook class.
*
* Hooks are functions that Hubzilla will invoke at certain points in the code
* during execution. An addon can register a callback handler that will be
* called whenever the specified hook is invoked. A callback handler is a
* function that takes a reference to an array containing the callback
* arguments as it's only argument.
*
* @see call_hooks
* @see load_hooks
*/
class Hook {
/**
* Register a callback handler for a hook.
*
* A callback handler is a function that takes a reference to an array
* containing the callback arguments as it's only argument.
*
* The contents and meaning of the array depends on the hook invoked. By
* modifying the contents of the array the hook can pass data back to the
* caller.
*
* Once the `Hook::register` function has been called, the callback may be
* invoked.
*
* @param string $hook The name of the hook to register a handler for.
* @param string $file The source file of the callback handler.
* @param string|array $function
* The function name of the callback handler, as a
* string or an array.
* @param int $version Hook interface version, allways 1.
* @param int $priority The priority of the callback handler, higher
* numbers takes precedence.
*
* @return true if the handler was already registered, otherwise the result
* from inserting the hook in the database.
*/
static public function register($hook,$file,$function,$version = 1,$priority = 0) {
if(is_array($function)) {
$function = serialize($function);
@@ -87,14 +45,6 @@ class Hook {
return $r;
}
/**
* Register an array of hook callback handlers.
*
* All of the handlers must be in the same source file.
*
* @param string $file The source file of the callback handlers.
* @param array $arr An array of `hookname => functionname` pairs.
*/
static public function register_array($file,$arr) {
if($arr) {
foreach($arr as $k => $v) {
@@ -104,20 +54,6 @@ class Hook {
}
/**
* Unregister a hook callback handler.
*
* @param string $hook The name of the hook to register a callback handler for.
* @param string $file The source file of the hook callback handler.
* @param string|array $function
* The function name of the callback handler, as a
* string or an array.
* @param int $version Hook interface version, allways 1.
* @param int $priority The priority of the callback handler, higher
* numbers takes precedence.
*
* @return The result of the database delete operation.
*/
static public function unregister($hook,$file,$function,$version = 1,$priority = 0) {
if(is_array($function)) {
$function = serialize($function);
@@ -134,13 +70,11 @@ class Hook {
}
/**
* Unregister all hooks handlers from a given source file.
* @brief Unregister all hooks with this file component.
*
* Useful for addon upgrades where you want to clean out old interfaces.
*
* @param string $file The source file where the hook handlers were defined.
*
* @return The result from the database delete operation.
* @param string $file
*/
static public function unregister_by_file($file) {
$r = q("DELETE FROM hook WHERE file = '%s' ",
@@ -151,20 +85,25 @@ class Hook {
}
/**
* Inserts a hook into a page request.
* @brief Inserts a hook into a page request.
*
* Insert a short-lived hook into the running page request. Hooks are
* normally persistent so that they can be called across asynchronous
* processes such as delivery and poll processes.
* Insert a short-lived hook into the running page request.
* Hooks are normally persistent so that they can be called
* across asynchronous processes such as delivery and poll
* processes.
*
* This function lets you attach a hook callback immediately which will not
* persist beyond the life of this page request or the current process.
* insert_hook lets you attach a hook callback immediately
* which will not persist beyond the life of this page request
* or the current process.
*
* @param string $hook Name of hook to attach callback.
* @param string|array $fn Name of callback handler as a string or array.
* @param int $version Hook interface version, allways 1.
* @param int $priority Currently not implemented in this function,
* would require the hook array to be resorted.
* @param string $hook
* name of hook to attach callback
* @param string $fn
* function name of callback handler
* @param int $version
* hook interface version, 0 uses two callback params, 1 uses one callback param
* @param int $priority
* currently not implemented in this function, would require the hook array to be resorted
*/
static public function insert($hook, $fn, $version = 0, $priority = 0) {
if(is_array($fn)) {
@@ -180,4 +119,4 @@ class Hook {
App::$hooks[$hook][] = array('', $fn, $priority, $version);
}
}
}

View File

@@ -1,106 +1,22 @@
<?php
/*
* SPDX-FileCopyrightText: 2018 The Hubzilla Community
* SPDX-FileContributor: Zotlabs
* SPDX-FileContributor: Mario <mario@mariovavti.com>
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Extend;
use Zotlabs\Lib\Config;
/**
* Class for managing routes.
*
* Routes connect a URL path to a module that will handle requests to that
* path.
*
* For example by registering a route like this:
*
* ```php
* Route::register(
* __DIR__ . '/Mod_Myroute.php',
* 'myroute'
* );
* ```
*
* Hubzilla will direct requests to the '/myroute' URL path to the 'Myroute'
* controller located in the '/Mod_Myroute.php' file in the same directory as
* the file this code was called from.
*
* Routes are stored persistently, so this function will typically be called from
* the `<addon>_load()` function if called from an addon. Accordingly, the route must
* be unregistered when no longer needed, like this:
*
* ```php
* Route::unregister(
* __DIR__ . '/Mod_Myroute.php',
* 'myroute'
* );
* ```
*
* This will typically be called from the `<addon>_unload()` function in an addon.
*/
class Route {
/**
* Register a new route.
*
* Example:
* ```php
* Route::register(
* __DIR__ . '/Mod_Myroute.php',
* 'myroute'
* );
* ```
*
* The route is stored persistently, and must be unregistered when no longer needed.
*
* @param string $file The file containing the controller for handling requests to this route.
* @param string $modname The name of the module (URL path).
*
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
*/
public static function register(string $file, string $modname): void {
static function register($file,$modname) {
$rt = self::get();
foreach ($rt as $r) {
if ($r[0] === $file && $r[1] === $modname) {
return;
}
}
$rt[] = [ $file, $modname ];
self::set($rt);
}
/**
* Unregister a route.
*
* Example:
* ```php
* Route::unregister(
* __DIR__ . '/Mod_Myroute.php',
* 'myroute'
* );
* ```
*
* @param string $file The file containing the controller for handling requests to this route.
* @param string $modname The name of the module (URL path).
*
* @see {@link Zotlabs::Extend::Route.register() register()}
* @see {@link Zotlabs::Extend::Route.unregister_by_file() unregister_by_file()}
*/
public static function unregister(string $file, string $modname): void {
static function unregister($file,$modname) {
$rt = self::get();
if($rt) {
$n = [];
foreach($rt as $r) {
if(!($r[0] === $file && $r[1] === $modname)) {
if($r[0] !== $file && $r[1] !== $modname) {
$n[] = $r;
}
}
@@ -108,23 +24,7 @@ class Route {
}
}
/**
* Unregister all routes by source file.
*
* Removes all persistently stored routes with hanclers in the
* given source file.
*
* Example:
* ```php
* Route::unregister_by_file(__DIR__ . '/Mod_Myroute.php');
* ```
*
* @param string $file The file containing the controllers to remove.
*
* @see {@link Zotlabs::Extend::Route.register() register()}
* @see {@link Zotlabs::Extend::Route.unregister() unregister()}
*/
public static function unregister_by_file(string $file): void {
static function unregister_by_file($file) {
$rt = self::get();
if($rt) {
$n = [];
@@ -137,19 +37,12 @@ class Route {
}
}
/**
* Get an array of all defined routes.
*
* @return An array of routes, where each entry is an array
* containing two elements, the file, and the module
* name.
*/
public static function get(): array {
return Config::Get('system','routes',[]);
static function get() {
return get_config('system','routes',[]);
}
private static function set(array $r): mixed {
return Config::Set('system','routes',$r);
static function set($r) {
return set_config('system','routes',$r);
}
}

View File

@@ -2,19 +2,11 @@
namespace Zotlabs\Extend;
use Zotlabs\Lib\Config;
class Widget {
static function register($file,$widget) {
$rt = self::get();
foreach ($rt as $r) {
if ($r[0] === $file && $r[1] === $widget) {
return;
}
}
$rt[] = [ $file, $widget ];
self::set($rt);
}
@@ -24,7 +16,7 @@ class Widget {
if($rt) {
$n = [];
foreach($rt as $r) {
if(!($r[0] === $file && $r[1] === $widget)) {
if($r[0] !== $file && $r[1] !== $widget) {
$n[] = $r;
}
}
@@ -46,10 +38,10 @@ class Widget {
}
static function get() {
return Config::Get('system','widgets',[]);
return get_config('system','widgets',[]);
}
static function set($r) {
return Config::Set('system','widgets',$r);
return set_config('system','widgets',$r);
}
}

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Identity;
use Zotlabs\Lib\Config;
class OAuth2Server extends \OAuth2\Server {
public function __construct(OAuth2Storage $storage, $config = null) {
@@ -26,8 +24,8 @@ class OAuth2Server extends \OAuth2\Server {
$keyStorage = new \OAuth2\Storage\Memory( [
'keys' => [
'public_key' => Config::Get('system', 'pubkey'),
'private_key' => Config::Get('system', 'prvkey')
'public_key' => get_config('system', 'pubkey'),
'private_key' => get_config('system', 'prvkey')
]
]);

View File

@@ -1,69 +0,0 @@
<?php /** @file */
namespace Zotlabs\Lib;
/**
* A wrapper for the cache api
*/
class ASCache {
public static function isEnabled()
{
return Config::Get('system', 'as_object_cache_enabled', true);
}
public static function getAge(): string
{
return Config::Get('system', 'as_object_cache_time', '10 MINUTE');
}
public static function Get(string $key): array
{
if (!self::isEnabled()) {
return [];
}
$ret = Cache::get($key, self::getAge());
if ($ret) {
return json_unserialize($ret);
}
return [];
}
public static function Set(string $key, array $obj): void
{
if (!self::isEnabled()) {
return;
}
if (!self::isCacheable($obj)) {
return;
}
Cache::set($key, json_serialize($obj));
}
public static function isCacheable(array $obj): bool
{
$to = [];
$cc = [];
if (isset($obj['to'])) {
$to = is_array($obj['to']) ? $obj['to'] : [$obj['to']];
}
if (isset($obj['cc'])) {
$cc = is_array($obj['cc']) ? $obj['cc'] : [$obj['cc']];
}
$receivers = array_merge($to, $cc);
if ($receivers && !in_array(ACTIVITY_PUBLIC_INBOX, $receivers)) {
return false;
}
return true;
}
}

View File

@@ -31,19 +31,7 @@ class ASCollection {
}
if (is_string($obj)) {
$cached = ASCache::Get($obj);
if ($cached) {
// logger('cached: ' . $obj);
$data = $cached;
}
else {
// logger('fetching: ' . $obj);
$data = Activity::fetch($obj, $channel);
if ($data) {
ASCache::Set($obj, $data);
}
}
$data = Activity::fetch($obj, $channel);
$this->history[] = $obj;
}
@@ -95,8 +83,6 @@ class ASCollection {
return false;
}
$data = null;
if (is_array($this->nextpage)) {
$data = $this->nextpage;
}
@@ -106,20 +92,7 @@ class ASCollection {
// recursion detected
return false;
}
$cached = ASCache::Get($this->nextpage);
if ($cached) {
// logger('cached: ' . $this->nextpage);
$data = $cached;
}
else {
$data = Activity::fetch($this->nextpage, $this->channel);
if ($data) {
// logger('fetching: ' . $this->nextpage);
ASCache::Set($this->nextpage, $data);
}
}
$data = Activity::fetch($this->nextpage, $this->channel);
$this->history[] = $this->nextpage;
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,6 @@ class ActivityStreams {
public $meta = null;
public $valid = false;
public $deleted = false;
public $portable_id = null;
public $id = '';
public $parent_id = '';
public $type = '';
@@ -24,11 +23,10 @@ class ActivityStreams {
public $origin = null;
public $owner = null;
public $signer = null;
public $sig = null;
public $ldsig = null;
public $sigok = false;
public $recips = null;
public $raw_recips = null;
public $saved_recips = null;
/**
* @brief Constructor for ActivityStreams.
@@ -37,13 +35,12 @@ class ActivityStreams {
*
* @param string $string
*/
function __construct($string, $portable_id = null) {
function __construct($string) {
if(!$string)
return;
$this->raw = $string;
$this->portable_id = $portable_id;
if (is_array($string)) {
$this->data = $string;
@@ -89,16 +86,7 @@ class ActivityStreams {
// Attempt to assemble an Activity from what we were given.
if ($this->is_valid()) {
$this->id = $this->get_property_obj('id');
if (!$this->id) {
logger('Data with mmissing id: ' . print_r($this->data, true));
return;
}
// cache for future use
ASCache::Set($this->id, $this->data);
$this->id = $this->get_property_obj('id');
$this->type = $this->get_primary_type();
$this->actor = $this->get_actor('actor', '', '');
$this->obj = $this->get_compound_property('object');
@@ -106,19 +94,11 @@ class ActivityStreams {
$this->origin = $this->get_compound_property('origin');
$this->recips = $this->collect_recips();
$this->sig = $this->get_compound_property('proof');
if ($this->sig) {
$this->checkEddsaSignature(); // will set signer and sigok if everything works out
}
// Try LDSignatures if edsig failed
if (!$this->sigok) {
$this->sig = $this->get_compound_property('signature');
if ($this->sig) {
$this->signer = $this->get_actor('creator', $this->sig);
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
$this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
}
$this->ldsig = $this->get_compound_property('signature');
if ($this->ldsig) {
$this->signer = $this->get_actor('creator', $this->ldsig);
if ($this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer) && is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']) {
$this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
}
}
@@ -130,34 +110,25 @@ class ActivityStreams {
}
}
// Fetch recursive or embedded activities
// fetch recursive or embedded activities
if ($this->obj && is_array($this->obj) && array_key_exists('object', $this->obj)) {
$this->obj['object'] = $this->get_compound_property('object', $this->obj);
$this->obj['object'] = $this->get_compound_property($this->obj['object']);
}
// Enumerate and store actors in referenced objects
if ($this->obj && is_array($this->obj) && isset($this->obj['actor'])) {
if ($this->obj && is_array($this->obj) && isset($this->obj['actor']))
$this->obj['actor'] = $this->get_actor('actor', $this->obj);
}
if ($this->tgt && is_array($this->tgt) && isset($this->tgt['actor'])) {
if ($this->tgt && is_array($this->tgt) && isset($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) && isset($this->obj['inReplyTo'])) {
$this->parent_id = $this->obj['inReplyTo'];
}
// Determine if this is a followup or response activity
$this->parent_id = ((is_array($this->get_property_obj('inReplyTo'))) ? $this->get_property_obj('inReplyTo')['id'] : $this->get_property_obj('inReplyTo'));
if (!$this->parent_id && isset($this->obj['inReplyTo'])) {
$this->parent_id = ((is_array($this->obj['inReplyTo'])) ? $this->obj['inReplyTo']['id'] : $this->obj['inReplyTo']);
}
if (!$this->parent_id && isset($this->obj['id'])) {
if ((!$this->parent_id) && is_array($this->obj) && isset($this->obj['id'])) {
$this->parent_id = $this->obj['id'];
}
}
}
@@ -174,59 +145,36 @@ class ActivityStreams {
$this->saved_recips = $arr;
}
/**
* @brief get single property from Activity object
*
* @param string $property
* @param mixed $default return value if property or object not set
* or object is a string id which could not be fetched.
* @return mixed
*/
public function objprop(string $property, mixed $default = false): mixed {
$x = $this->get_property_obj($property, $this->obj);
return (isset($x)) ? $x : $default;
}
/**
* @brief Collects all recipients.
*
* @param mixed $base
* @param string $base
* @param string $namespace (optional) default empty
* @return array
*/
public function collect_recips(mixed $base = '', string $namespace = ''): array {
$result = [];
$tmp = [];
function collect_recips($base = '', $namespace = '') {
$x = [];
$fields = ['to', 'cc', 'bto', 'bcc', 'audience'];
foreach ($fields as $field) {
// don't expand these yet
$values = $this->get_property_obj($field, $base, $namespace);
if ($values) {
$values = force_array($values);
$tmp[$field] = $values;
$result = array_values(array_unique(array_merge($result, $values)));
}
// Merge the object recipients if they exist.
$values = $this->objprop($field);
if ($values) {
$values = force_array($values);
$tmp[$field] = ((isset($tmp[$field])) ? array_merge($tmp[$field], $values) : $values);
$result = array_values(array_unique(array_merge($result, $values)));
}
// remove duplicates
if (isset($tmp[$field])) {
$tmp[$field] = array_values(array_unique($tmp[$field]));
foreach ($fields as $f) {
$y = $this->get_compound_property($f, $base, $namespace);
if ($y) {
if (!is_array($this->raw_recips)) {
$this->raw_recips = [];
}
if (!is_array($y)) {
$y = [$y];
}
$this->raw_recips[$f] = $y;
$x = array_merge($x, $y);
}
}
$this->raw_recips = $tmp;
// not yet ready for prime time
// $result = $this->expand($result,$base,$namespace);
return $result;
// not yet ready for prime time
// $x = $this->expand($x,$base,$namespace);
return $x;
}
function expand($arr, $base = '', $namespace = '') {
$ret = [];
@@ -330,27 +278,12 @@ class ActivityStreams {
* @return NULL|mixed
*/
function fetch_property($url, $channel = null) {
$x = null;
function fetch_property($url) {
return self::fetch($url);
}
if (str_starts_with($url, z_root() . '/item/')) {
$x = Activity::fetch_local($url, $this->portable_id ?? '');
logger('local: ' . print_r($x,true));
}
if (!$x) {
$x = Activity::fetch($url, $channel);
if ($x === null && strpos($url, '/channel/')) {
// look for other nomadic channels which might be alive
$zf = Zotfinger::exec($url, $channel);
if ($zf) {
$url = $zf['signature']['signer'];
$x = Activity::fetch($url, $channel);
}
}
}
return $x;
static function fetch($url, $channel = null) {
return Activity::fetch($url, $channel);
}
static function is_an_actor($s) {
@@ -361,7 +294,7 @@ class ActivityStreams {
if (!$s) {
return false;
}
return (in_array($s, ['Announce', 'Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
return (in_array($s, ['Like', 'Dislike', 'Flag', 'Block', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact']));
}
/**
@@ -377,7 +310,7 @@ class ActivityStreams {
$x = $this->get_property_obj($property, $base, $namespace);
if ($this->is_url($x)) {
$y = Activity::get_actor($x);
$y = Activity::get_cached_actor($x);
if ($y) {
return $y;
}
@@ -408,26 +341,13 @@ class ActivityStreams {
*/
function get_compound_property($property, $base = '', $namespace = '', $first = false) {
$x = $this->get_property_obj($property, $base, $namespace);
if ($this->is_url($x)) {
$cached = ASCache::Get($x);
if ($cached) {
// logger('AS cached: ' . $x);
$y = $cached;
}
else {
// logger('AS fetching: ' . $x);
$y = $this->fetch_property($x);
if ($y) {
ASCache::Set($x, $y);
}
}
$y = $this->fetch_property($x);
if (is_array($y)) {
$x = $y;
}
}
// verify and unpack JSalmon signature if present
if (is_array($x) && array_key_exists('signed', $x)) {
@@ -527,69 +447,4 @@ class ActivityStreams {
}
public function checkEddsaSignature() {
$publicKey = null;
$signer = $this->get_property_obj('verificationMethod', $this->sig);
if ($signer && str_starts_with($signer, 'did:key:')) {
$publicKey = str_replace('did:key:', '', $signer);
$this->signer = ['id' => $signer];
if (strpos($publicKey, '#') !== false) {
$publicKey = substr($publicKey,0, strpos($publicKey, '#'));
}
}
else {
$parseUrl = parse_url($signer);
if (isset($parseUrl['fragment'])) {
if (str_starts_with($parseUrl['fragment'], 'z6Mk')) {
$publicKey = $parseUrl['fragment'];
}
unset($parseUrl['fragment']);
}
if (isset($parseUrl['query'])) {
unset($parseUrl['query']);
}
$url = unparse_url($parseUrl);
$this->signer = ['id' => $url];
$hublocs = Activity::get_actor_hublocs($url);
$hasStoredKey = false;
if ($hublocs) {
foreach ($hublocs as $hubloc) {
if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
$hasStoredKey = true;
break;
}
}
}
if (!$hasStoredKey) {
$this->signer = Activity::get_actor($url);
if (isset($this->signer['assertionMethod'])) {
if (!isset($this->signer['assertionMethod'][0])) {
$this->signer['assertionMethod'] = [$this->signer['assertionMethod']];
}
foreach($this->signer['assertionMethod'] as $am) {
if ($url === $am['controller'] &&
$am['type'] === 'Multikey' &&
str_starts_with($am['publicKeyMultibase'], 'z6Mk')
) {
$publicKey = $am['publicKeyMultibase'];
}
}
}
}
}
if ($publicKey) {
$this->sigok = (new JcsEddsa2022)->verify($this->data, $publicKey);
}
}
}

View File

@@ -3,7 +3,6 @@
namespace Zotlabs\Lib;
use App;
use Zotlabs\Lib\Config;
require_once('include/plugin.php');
require_once('include/channel.php');
@@ -66,7 +65,7 @@ class Apps {
}
static public function get_base_apps() {
$x = Config::Get('system','base_apps',[
$x = get_config('system','base_apps',[
'Connections',
'Contact Roles',
'Network',
@@ -302,7 +301,7 @@ class Apps {
break;
default:
if($config)
$unset = ((Config::Get('system', $require[0]) == $require[1]) ? false : true);
$unset = ((get_config('system', $require[0]) == $require[1]) ? false : true);
else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset)
@@ -341,7 +340,7 @@ class Apps {
'Suggest Channels' => t('Suggest Channels'),
'Login' => t('Login'),
'Channel Manager' => t('Channel Manager'),
'Network' => t('Network'),
'Network' => t('Stream'),
'Settings' => t('Settings'),
'Files' => t('Files'),
'Webpages' => t('Webpages'),
@@ -353,6 +352,8 @@ class Apps {
'Directory' => t('Directory'),
'Help' => t('Help'),
'Mail' => t('Mail'),
'Mood' => t('Mood'),
'Poke' => t('Poke'),
'Chat' => t('Chat'),
'Search' => t('Search'),
'Probe' => t('Probe'),
@@ -418,28 +419,11 @@ class Apps {
static public function app_render($papp, $mode = 'view') {
$installed = false;
if(!$papp) {
if(! $papp)
return;
}
/**
* @hooks app_render_before
* Hook to manipulate the papp array before rendering
*/
$hookinfo = [
'name' => $papp['name'],
'photo' => $papp['photo']
];
call_hooks('app_render_manipulate_photo', $hookinfo);
// We will only allow to manipulate the photo
$papp['photo'] = $hookinfo['photo'];
if(!$papp['photo']) {
if(! $papp['photo'])
$papp['photo'] = 'icon:gear';
}
self::translate_system_apps($papp);
@@ -524,7 +508,7 @@ class Apps {
break;
default:
if($config)
$unset = ((Config::Get('system', $require[0]) === $require[1]) ? false : true);
$unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset)
@@ -961,7 +945,7 @@ class Apps {
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
$x = (($uid) ? get_pconfig($uid,'system',$conf) : Config::Get('system',$conf));
$x = (($uid) ? get_pconfig($uid,'system',$conf) : get_config('system',$conf));
if(($x) && (! is_array($x))) {
$y = explode(',',$x);
$y = array_map('trim',$y);
@@ -1028,7 +1012,12 @@ class Apps {
if(! $syslist)
return;
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
break;
}
}
if(! $position)
return;
@@ -1077,7 +1066,12 @@ class Apps {
if(! $syslist)
return;
$position = array_find_key($syslist, fn ($v) => $v['guid'] === $guid);
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
break;
}
}
if($position >= count($syslist) - 1)
return;

View File

@@ -1,80 +0,0 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\ActivityStreams\UnhandledElementException;
class BaseObject
{
public $string;
public $ldContext;
/**
* @param $input
* @param $strict
* @throws UnhandledElementException if $strict
*/
public function __construct($input = null, $strict = false)
{
if (isset($input)) {
if (is_string($input)) {
$this->string = $input;
}
elseif(is_array($input)) {
foreach ($input as $key => $value) {
$key = ($key === '@context') ? 'ldContext' : $key;
if ($strict && !property_exists($this, $key)) {
throw new UnhandledElementException("Unhandled element: $key");
}
$this->{$key} = $value;
}
}
}
return $this;
}
public function getDataType($element, $object = null)
{
$object = $object ?? $this;
$type = gettype($object[$element]);
if ($type === 'array' && array_is_list($object[$element])) {
return 'list';
}
return $type;
}
public function toArray()
{
if ($this->string) {
return $this->string;
}
$returnValue = [];
foreach ((array) $this as $key => $value) {
if (isset($value)) {
$key = ($key === 'ldContext') ? '@context' : $key;
$returnValue[$key] = (($value instanceof BaseObject) ? $value->toArray() : $value);
}
}
return $returnValue;
}
/**
* @return mixed
*/
public function getLdContext()
{
return $this->ldContext;
}
/**
* @param mixed $ldContext
* @return BaseObject
*/
public function setLdContext($ldContext)
{
$this->ldContext = $ldContext;
return $this;
}
}

View File

@@ -2,56 +2,53 @@
namespace Zotlabs\Lib;
use Zotlabs\Lib\Config;
/**
* cache api
*/
/**
* cache api
*/
class Cache {
/**
* @brief Returns cached content
*
*
* @param string $key
* @param string $age in SQL format, default is '30 DAY'
* @return string
*/
public static function get($key, $age = '') {
// $hash = hash('whirlpool',$key);
$hash = uuid_from_url($key);
$hash = hash('whirlpool',$key);
$r = q("SELECT v FROM cache WHERE k = '%s' AND updated > %s - INTERVAL %s LIMIT 1",
dbesc($hash),
db_utcnow(),
db_quoteinterval(($age ? $age : Config::Get('system','object_cache_days', '30') . ' DAY'))
db_quoteinterval(($age ? $age : get_config('system','object_cache_days', '30') . ' DAY'))
);
if ($r)
return $r[0]['v'];
return null;
}
public static function set($key,$value) {
// $hash = hash('whirlpool',$key);
$hash = uuid_from_url($key);
$r = q("SELECT * FROM cache WHERE k = '%s' LIMIT 1",
$hash = hash('whirlpool',$key);
$r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
dbesc($hash)
);
if($r) {
q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
dbesc($value),
dbesc(datetime_convert()),
dbesc($hash)
);
dbesc($hash));
}
else {
q("INSERT INTO cache (k, v, updated) VALUES ('%s', '%s', '%s')",
q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')",
dbesc($hash),
dbesc($value),
dbesc(datetime_convert())
);
dbesc(datetime_convert()));
}
}
}

View File

@@ -181,7 +181,7 @@ class Chatroom {
}
public static function leave($observer_xchan, $room_id, $client) {
function leave($observer_xchan, $room_id, $client) {
if(! $room_id || ! $observer_xchan)
return;

View File

@@ -2,7 +2,6 @@
namespace Zotlabs\Lib;
use App;
class Config {
@@ -15,41 +14,20 @@ class Config {
* @param string $family
* The category of the configuration value
*/
public static function Load($family, $recursionCounter = 0) {
if (! array_key_exists($family, App::$config)) {
App::$config[$family] = [];
}
static public function Load($family) {
if(! array_key_exists($family, \App::$config))
\App::$config[$family] = array();
// We typically continue when presented with minor DB issues,
// but loading the site configuration is more important.
// Check for query returning false and give it approx 30 seconds
// to recover if there's a problem. This is intended to fix a
// rare issue on Galera where temporary sync issues were causing
// the site encryption keys to be regenerated, which was causing
// communication issues for members.
// This code probably belongs at the database layer, but we don't
// necessarily want to shut the site down for problematic queries
// caused by bad data. That could be used in a denial of service
// attack. Those do need to be (and they are) logged.
if (! array_key_exists('config_loaded', App::$config[$family])) {
if(! array_key_exists('config_loaded', \App::$config[$family])) {
$r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family));
if ($r === false && !App::$install) {
sleep(3);
$recursionCounter ++;
if ($recursionCounter > 10) {
system_unavailable();
if($r !== false) {
if($r) {
foreach($r as $rr) {
$k = $rr['k'];
\App::$config[$family][$k] = $rr['v'];
}
}
self::Load($family, $recursionCounter);
}
elseif (is_array($r)) {
foreach ($r as $rr) {
$k = $rr['k'];
App::$config[$family][$k] = $rr['v'];
}
App::$config[$family]['config_loaded'] = true;
\App::$config[$family]['config_loaded'] = true;
}
}
}
@@ -68,19 +46,19 @@ class Config {
* @return mixed
* Return the set value, or false if the database update failed
*/
public static function Set($family, $key, $value) {
static public function Set($family, $key, $value) {
// manage array value
$dbvalue = ((is_array($value)) ? 'json:' . json_encode($value) : $value);
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
if (self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
if(self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
dbesc($family),
dbesc($key),
dbesc($dbvalue)
);
if ($ret) {
App::$config[$family][$key] = $value;
if($ret) {
\App::$config[$family][$key] = $value;
$ret = $value;
}
return $ret;
@@ -92,8 +70,8 @@ class Config {
dbesc($key)
);
if ($ret) {
App::$config[$family][$key] = $value;
if($ret) {
\App::$config[$family][$key] = $value;
$ret = $value;
}
@@ -115,37 +93,21 @@ class Config {
* The category of the configuration value
* @param string $key
* The configuration key to query
* @param mixed $default (optional) default false
* @param string $default (optional) default false
* @return mixed Return value or false on error or if not set
*/
public static function Get($family, $key, $default = false) {
if ((! array_key_exists($family, App::$config)) || (! array_key_exists('config_loaded', App::$config[$family]))) {
static public function Get($family, $key, $default = false) {
if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family])))
self::Load($family);
}
if (array_key_exists('config_loaded', App::$config[$family])) {
if (! array_key_exists($key, App::$config[$family])) {
if(array_key_exists('config_loaded', \App::$config[$family])) {
if(! array_key_exists($key, \App::$config[$family])) {
return $default;
}
$value = App::$config[$family][$key];
if (! is_array($value)) {
if (str_starts_with($value, 'json:')) {
return json_unserialize($value);
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $value)) {
// Unserialize in inherently unsafe. Try to mitigate by not
// allowing unserializing objects. Only kept for backwards
// compatibility. JSON serialization should be prefered.
return unserialize($value, array('allowed_classes' => false));
} else {
return $value;
}
}
else {
return $value;
}
return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key]))
? unserialize(\App::$config[$family][$key])
: \App::$config[$family][$key]
);
}
return $default;
@@ -163,13 +125,12 @@ class Config {
* The configuration key to delete
* @return mixed
*/
public static function Delete($family, $key) {
static public function Delete($family, $key) {
$ret = false;
if (array_key_exists($family, App::$config) && array_key_exists($key, App::$config[$family])) {
unset(App::$config[$family][$key]);
}
if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family]))
unset(\App::$config[$family][$key]);
$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
dbesc($family),
@@ -192,7 +153,7 @@ class Config {
* The configuration key to query
* @return mixed
*/
private static function get_from_storage($family, $key) {
static private function get_from_storage($family,$key) {
$ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
dbesc($family),
dbesc($key)
@@ -200,4 +161,5 @@ class Config {
return $ret;
}
}

View File

@@ -5,7 +5,8 @@ namespace Zotlabs\Lib;
use App;
use Zotlabs\Access\Permissions;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Config;
class Connect {
@@ -24,16 +25,10 @@ class Connect {
$uid = $channel['channel_id'];
// If we get just a channel name and it is not an URL turn it into a local webbie
if (!str_contains($url, '@') && strpos($url,'/') === false) {
if (strpos($url,'@') === false && strpos($url,'/') === false) {
$url = $url . '@' . App::get_hostname();
}
// Remove a possible leading @
if (str_starts_with($url, '@')) {
$url = ltrim($url, '@');
}
$result = [ 'success' => false, 'message' => '' ];
$my_perms = false;
@@ -74,8 +69,7 @@ class Connect {
$xchan_hash = '';
$sql_options = (($protocol) ? " and xchan_network = '" . dbesc($protocol) . "' " : '');
// We need both, the xchan and the hubloc here hence use JOIN instead of LEFT JOIN
$r = q("SELECT * FROM xchan JOIN hubloc ON xchan_hash = hubloc_hash WHERE ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ORDER BY hubloc_id DESC",
$r = q("SELECT * FROM xchan LEFT JOIN hubloc ON xchan_hash = hubloc_hash WHERE ( xchan_hash = '%s' or xchan_url = '%s' or xchan_addr = '%s') $sql_options ORDER BY hubloc_id DESC",
dbesc($url),
dbesc($url),
dbesc($url)
@@ -101,7 +95,7 @@ class Connect {
$wf = discover_by_webbie($url,$protocol);
if (! $wf) {
$feeds = Config::Get('system','feed_contacts');
$feeds = get_config('system','feed_contacts');
if (($feeds) && (in_array($protocol, [ '', 'feed', 'rss' ]))) {
$d = discover_by_url($url);

View File

@@ -3,7 +3,6 @@
namespace Zotlabs\Lib;
use Exception;
use Zotlabs\Lib\Config;
class Crypto {
@@ -45,7 +44,7 @@ class Crypto {
'encrypt_key' => false
];
$conf = Config::Get('system', 'openssl_conf_file');
$conf = get_config('system', 'openssl_conf_file');
if ($conf) {
$openssl_options['config'] = $conf;

View File

@@ -1,39 +1,21 @@
<?php
/**
* A class to handle database schema upgrades.
*
* SPDX-FileCopyrightText: 2024 Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use Zotlabs\Lib\Config;
/**
* Upgrade the database schema if necessary.
*
* Compares the currently active database schema version with the version
* required for this version of Hubzilla, and performs the upgrade if needed.
*
* If the difference consists of more than one revision of the schema, each of
* the intermediate upgrades are performed in turn.
*/
class DB_Upgrade {
/**
* Check the installed and required schema versions and perform the upgrade
* if necessary.
*
* @param int $db_version The required DB schema version.
*/
public static function run(int $db_revision): void {
public $config_name = '';
public $func_prefix = '';
$build = Config::Get('system', 'db_version', 0);
function __construct($db_revision) {
$this->config_name = 'db_version';
$this->func_prefix = '_';
$build = get_config('system', 'db_version', 0);
if(! intval($build))
$build = Config::Set('system', 'db_version', $db_revision);
$build = set_config('system', 'db_version', $db_revision);
if($build == $db_revision) {
// Nothing to be done.
@@ -45,7 +27,7 @@ class DB_Upgrade {
logger('Critical: check_config unable to determine database schema version');
return;
}
$current = intval($db_revision);
if($stored < $current) {
@@ -56,7 +38,7 @@ class DB_Upgrade {
for($x = $stored + 1; $x <= $current; $x ++) {
$s = '_' . $x;
$cls = '\\Zotlabs\Update\\' . $s ;
if(! class_exists($cls)) {
if(! class_exists($cls)) {
return;
}
@@ -70,10 +52,10 @@ class DB_Upgrade {
Config::Load('database');
if(Config::Get('database', $s))
if(get_config('database', $s))
break;
Config::Set('database',$s, '1');
set_config('database',$s, '1');
$c = new $cls();
@@ -83,10 +65,10 @@ class DB_Upgrade {
$source = t('Source code of failed update: ') . "\n\n" . @file_get_contents('Zotlabs/Update/' . $s . '.php');
// Prevent sending hundreds of thousands of emails by creating
// a lockfile.
// a lockfile.
$lockfile = 'store/[data]/mailsent';
@@ -95,7 +77,7 @@ class DB_Upgrade {
@unlink($lockfile);
//send the administrator an e-mail
file_put_contents($lockfile, $x);
$r = q("select account_language from account where account_email = '%s' limit 1",
dbesc(\App::$config['system']['admin_email'])
);
@@ -104,7 +86,7 @@ class DB_Upgrade {
[
'toEmail' => \App::$config['system']['admin_email'],
'messageSubject' => sprintf( t('Update Error at %s'), z_root()),
'textVersion' => replace_macros(get_intltext_template('update_fail_eml.tpl'),
'textVersion' => replace_macros(get_intltext_template('update_fail_eml.tpl'),
[
'$sitename' => \App::$config['system']['sitename'],
'$siteurl' => z_root(),
@@ -122,11 +104,11 @@ class DB_Upgrade {
pop_lang();
}
else {
Config::Set('database',$s, 'success');
set_config('database',$s, 'success');
}
}
}
Config::Set('system', 'db_version', $db_revision);
set_config('system', 'db_version', $db_revision);
}
}
}
}

View File

@@ -1,33 +1,28 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\Lib\Config;
class DReport {
private $location;
private $sender;
private $recipient;
private $name;
private $message_id;
private $message_uuid;
private $status;
private $date;
function __construct($location, $sender, $recipient, $message_id, $message_uuid = '', $status = 'deliver') {
$this->location = $location;
$this->sender = $sender;
$this->recipient = $recipient;
$this->name = EMPTY_STR;
$this->message_id = $message_id;
$this->message_uuid = $message_uuid;
$this->status = $status;
$this->date = datetime_convert();
function __construct($location,$sender,$recipient,$message_id,$status = 'deliver') {
$this->location = $location;
$this->sender = $sender;
$this->recipient = $recipient;
$this->name = EMPTY_STR;
$this->message_id = $message_id;
$this->status = $status;
$this->date = datetime_convert();
}
function update($status) {
$this->status = $status;
$this->date = datetime_convert();
$this->status = $status;
$this->date = datetime_convert();
}
function set_name($name) {
@@ -35,31 +30,29 @@ class DReport {
}
function addto_update($status) {
$this->status = $this->status . ', ' . $status;
$this->status = $this->status . ' ' . $status;
}
function set($arr) {
$this->location = $arr['location'];
$this->sender = $arr['sender'];
$this->recipient = $arr['recipient'];
$this->name = $arr['name'];
$this->message_id = $arr['message_id'];
$this->message_uuid = $arr['message_uuid'] ?? '';
$this->status = $arr['status'];
$this->date = $arr['date'];
$this->location = $arr['location'];
$this->sender = $arr['sender'];
$this->recipient = $arr['recipient'];
$this->name = $arr['name'];
$this->message_id = $arr['message_id'];
$this->status = $arr['status'];
$this->date = $arr['date'];
}
function get() {
return array(
'location' => $this->location,
'sender' => $this->sender,
'recipient' => $this->recipient,
'name' => $this->name,
'message_id' => $this->message_id,
'message_uuid' => $this->message_uuid,
'status' => $this->status,
'date' => $this->date
'location' => $this->location,
'sender' => $this->sender,
'recipient' => $this->recipient,
'name' => $this->name,
'message_id' => $this->message_id,
'status' => $this->status,
'date' => $this->date
);
}
@@ -72,7 +65,7 @@ class DReport {
static function is_storable($dr) {
if(Config::Get('system', 'disable_dreport'))
if(get_config('system', 'disable_dreport'))
return false;
/**
@@ -89,14 +82,8 @@ class DReport {
if(array_key_exists('reject',$dr) && intval($dr['reject']))
return false;
if (!$dr['sender']) {
if(! ($dr['sender']))
return false;
}
// do not store dismissed create activities
if ($dr['status'] === 'not a collection activity') {
return false;
}
// Is the sender one of our channels?
@@ -107,6 +94,19 @@ class DReport {
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');
@@ -117,7 +117,7 @@ class DReport {
// 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'] === $dr['recipient']) && ($dr['status'] === 'recipient not found'))
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
@@ -126,14 +126,14 @@ class DReport {
// have a channel on that site.
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'",
dbesc($dr['recipient']),
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($dr['recipient']),
dbesc($rxchan),
intval($c[0]['channel_id'])
);
if($r)

View File

@@ -1,43 +0,0 @@
<?php
/*
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use DBA;
/**
* Abstract class to obtain statistics from the database.
*
* This class should not be instantiated on it's own, but you can get
* a concrete class for the configured database type of this site by
* calling the `DbStats::getStats()` function.
*/
abstract class DbStats {
/**
* Get an object for getting statistics from the database.
*
* @return DbStats The concrete class for obtaining the statistics from
* this instances database.
*/
public static function getStats(): DbStats {
return DBA::$dba->is_postgres()
? new PostgresDbStats()
: new MySQLDbStats();
}
/**
* Return the number of queries recorded by the database.
*
* @return int Number of queries.
*/
public abstract function getQueries(): int;
// Prevent instantiation of this class
private function __construct() {}
}

View File

@@ -6,7 +6,6 @@ namespace Zotlabs\Lib;
* @brief File with functions and a class for generating system and email notifications.
*/
use Zotlabs\Lib\Config;
class Enotify {
@@ -62,7 +61,7 @@ class Enotify {
$product = t('$projectname'); // PLATFORM_NAME;
$siteurl = z_root();
$thanks = t('Thank You,');
$sitename = Config::Get('system','sitename');
$sitename = get_config('system','sitename');
$site_admin = sprintf( t('%s Administrator'), $sitename);
$opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), \App::get_hostname());
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
@@ -74,15 +73,15 @@ class Enotify {
// Do not translate 'noreply' as it must be a legal 7-bit email address
$reply_email = Config::Get('system', 'reply_address');
$reply_email = get_config('system', 'reply_address');
if(! $reply_email)
$reply_email = 'noreply' . '@' . $hostname;
$sender_email = Config::Get('system', 'from_email');
$sender_email = get_config('system', 'from_email');
if(! $sender_email)
$sender_email = 'Administrator' . '@' . $hostname;
$sender_name = Config::Get('system', 'from_email_name');
$sender_name = get_config('system', 'from_email_name');
if(! $sender_name)
$sender_name = \Zotlabs\Lib\System::get_site_name();
@@ -95,8 +94,8 @@ class Enotify {
if (array_key_exists('verb', $params['item'])) {
// localize_item() alters the original item so make a copy first
$i = $params['item'];
// logger('calling localize');
// localize_item($i);
logger('calling localize');
localize_item($i);
$title = $i['title'];
$body = $i['body'];
$private = (($i['item_private']) || intval($i['item_obscured']));
@@ -131,9 +130,9 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'), $sitename);
$preamble = sprintf( t('%1$s sent you a new private message at %2$s'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('%1$s sent you a new direct message at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'), '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]');
$sitelink = t('Please visit %s to view and/or reply to your private messages.');
$sitelink = t('Please visit %s to view and/or reply to your direct messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid']));
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/hq/' . gen_link_id($params['item']['mid']) . '">' . $sitename . '</a>');
$itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']);
@@ -146,11 +145,11 @@ class Enotify {
$itemlink = $params['link'];
$action = (($moderated) ? t('requested to post in') : t('posted in'));
$action = t('commented on');
if(array_key_exists('item',$params)) {
if(in_array($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE])) {
if(in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
@@ -158,14 +157,11 @@ class Enotify {
return;
}
if(activity_match($params['verb'], ['Like', ACTIVITY_LIKE]))
$action = (($moderated) ? t('requested to like') : t('liked'));
if(activity_match($params['verb'], ACTIVITY_LIKE))
$action = t('liked');
if(activity_match($params['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$action = (($moderated) ? t('requested to dislike') : t('disliked'));
if(activity_match($params['verb'], [ACTIVITY_SHARE]))
$action = (($moderated) ? t('requested to repeat') : t('repeated'));
if(activity_match($params['verb'], ACTIVITY_DISLIKE))
$action = t('disliked');
}
@@ -213,36 +209,28 @@ class Enotify {
//$possess_desc = str_replace('<!item_type!>',$possess_desc);
// "a post"
$dest_str = sprintf(
t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'),
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$item_post_type
);
$item_post_type);
// "George Bull's post"
if($p) {
$dest_str = sprintf(
t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
if($p)
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$parent_item['author']['xchan_name'],
$item_post_type
);
}
$p[0]['author']['xchan_name'],
$item_post_type);
// "your post"
if ($parent_item['owner']['xchan_hash'] === $recip['channel_hash'] && intval($parent_item['item_wall'])) {
$dest_str = sprintf(
t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$item_post_type
);
}
$item_post_type);
// Some mail softwares relies on subject field for threading.
// So, we cannot have different subjects for notifications of the same thread.
@@ -271,7 +259,7 @@ class Enotify {
$itemlink = $params['link'];
if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Announce']))) {
if (array_key_exists('item',$params) && (activity_match($params['item']['verb'], ACTIVITY_LIKE) || activity_match($params['item']['verb'], ACTIVITY_DISLIKE))) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE) || !feature_enabled($recip['channel_id'], 'dislike')) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
@@ -316,29 +304,19 @@ class Enotify {
$item_post_type = item_post_type($p[0]);
// $private = $p[0]['item_private'];
$parent_id = $p[0]['id'];
$parent_item = $p[0];
//$verb = ((activity_match($params['item']['verb'], ACTIVITY_DISLIKE)) ? t('disliked') : t('liked'));
$moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false);
if(activity_match($params['item']['verb'], ['Like', ACTIVITY_LIKE]))
$verb = (($moderated) ? t('requested to like') : t('liked'));
if(activity_match($params['item']['verb'], ['Dislike', ACTIVITY_DISLIKE]))
$verb = (($moderated) ? t('requested to dislike') : t('disliked'));
if(activity_match($params['item']['verb'], [ACTIVITY_SHARE]))
$verb = (($moderated) ? t('requested to repeat') : t('repeated'));
$verb = ((activity_match($params['item']['verb'], ACTIVITY_DISLIKE)) ? t('disliked') : t('liked'));
// "your post"
if ($parent_item['author']['xchan_hash'] === $recip['channel_hash']) {
if($p[0]['owner']['xchan_name'] === $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$verb,
$itemlink,
$item_post_type
);
}
else {
pop_lang();
return;
@@ -417,7 +395,6 @@ class Enotify {
}
elseif (isset($params['type']) && $params['type'] === NOTIFY_TAGSHARE) {
$itemlink = $params['link'];
$subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') ,
@@ -427,11 +404,12 @@ class Enotify {
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
$itemlink = $params['link'];
}
elseif (isset($params['type']) && $params['type'] === NOTIFY_INTRO) {
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
$preamble = sprintf( t('You\'ve received a new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
$siteurl . '/connections/ifpending',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
@@ -444,7 +422,6 @@ class Enotify {
}
elseif (isset($params['type']) && $params['type'] === NOTIFY_SUGGEST) {
$itemlink = $params['link'];
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
$preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'),
@@ -459,6 +436,7 @@ class Enotify {
$sitelink = t('Please visit %s to approve or reject the suggestion.');
$tsitelink = sprintf( $sitelink, $siteurl );
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
$itemlink = $params['link'];
}
elseif (isset($params['type']) && $params['type'] === NOTIFY_CONFIRM) {
@@ -498,8 +476,6 @@ class Enotify {
require_once('include/html2bbcode.php');
/*
do {
$dups = false;
$hash = random_string();
@@ -508,17 +484,10 @@ class Enotify {
if ($r)
$dups = true;
} while ($dups === true);
*/
$hash = ((in_array($params['verb'], ['Create', 'Update', 'Invite'])) ? $params['item']['uuid'] : $params['item']['thr_parent_uuid']);
if (!$hash) {
$hash = new_uuid();
}
$datarray = [];
$datarray['hash'] = $hash;
$datarray['hash'] = $hash;
$datarray['sender_hash'] = $sender['xchan_hash'];
$datarray['xname'] = $sender['xchan_name'];
$datarray['url'] = $sender['xchan_url'];
@@ -529,7 +498,7 @@ class Enotify {
$datarray['link'] = $itemlink;
$datarray['parent'] = $parent_mid;
$datarray['parent_item'] = $parent_item;
$datarray['ntype'] = $params['type'] ?? 0;
$datarray['ntype'] = $params['type'] ?? '';
$datarray['verb'] = $params['verb'] ?? '';
$datarray['otype'] = $params['otype'] ?? '';
$datarray['abort'] = false;
@@ -577,10 +546,8 @@ class Enotify {
dbesc($datarray['otype'])
);
$r = q("select id from notify where hash = '%s' and link = '%s' and ntype = %d and uid = %d limit 1",
dbesc($datarray['hash']),
dbesc($itemlink),
intval($datarray['ntype']),
$r = q("select id from notify where hash = '%s' and uid = %d limit 1",
dbesc($hash),
intval($recip['channel_id'])
);
if ($r) {
@@ -612,7 +579,7 @@ class Enotify {
// send email notification if notification preferences permit
require_once('include/bbcode.php');
require_once('bbcode.php');
if ((intval($recip['channel_notifyflags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) {
logger('notification: sending notification email');
@@ -857,10 +824,22 @@ class Enotify {
}
else {
$itemem_text = (($item['item_thread_top'])
? (($item['obj_type'] === 'Question') ? t('started a poll') : t('started a conversation'))
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('posted in %s\'s conversation'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
? (($item['obj_type'] === 'Question') ? t('created a new poll') : t('created a new post'))
: (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
);
if($item['verb'] === ACTIVITY_SHARE) {
$itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
}
if($item['verb'] === ACTIVITY_LIKE) {
$itemem_text = sprintf( t('liked %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
}
if($item['verb'] === ACTIVITY_DISLIKE) {
$itemem_text = sprintf( t('disliked %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
}
if(in_array($item['obj_type'], ['Document', 'Video', 'Audio', 'Image'])) {
$itemem_text = t('shared a file with you');
}
@@ -870,12 +849,18 @@ class Enotify {
if($item['edited'] > $item['created']) {
$edit = true;
$itemem_text = sprintf( t('edited a message dated %s'), relative_date($item['created']));
if($item['item_thread_top']) {
$itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created']));
}
else {
$itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created']));
}
}
// convert this logic into a json array just like the system notifications
$who = (($item['verb'] === ACTIVITY_SHARE) ? 'owner' : 'author');
$body = html2plain(bbcode($item['body'], ['drop_media' => true, 'tryoembed' => false]), 75, true);
if ($body) {
$body = htmlentities($body, ENT_QUOTES, 'UTF-8', false);
@@ -883,20 +868,19 @@ class Enotify {
$x = array(
'notify_link' => $item['llink'],
'name' => $item['author']['xchan_name'],
'addr' => $item['author']['xchan_addr'] ? $item['author']['xchan_addr'] : $item['author']['xchan_url'],
'url' => $item['author']['xchan_url'],
'photo' => $item['author']['xchan_photo_s'],
'name' => $item[$who]['xchan_name'],
'addr' => $item[$who]['xchan_addr'] ?? $item[$who]['xchan_url'],
'url' => $item[$who]['xchan_url'],
'photo' => $item[$who]['xchan_photo_s'],
'when' => (($edit) ? datetime_convert('UTC', date_default_timezone_get(), $item['edited']) : datetime_convert('UTC', date_default_timezone_get(), $item['created'])),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
// 'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
'b64mid' => ((in_array($item['verb'] , ['Like', 'Dislike', 'Announce']) && !empty($item['thr_parent_uuid'])) ? $item['thr_parent_uuid'] : $item['uuid'] ?? ''),
'b64mid' => (($item['mid']) ? gen_link_id($item['mid']) : ''),
//'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? gen_link_id($item['thr_parent']) : gen_link_id($item['mid'])),
'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => bbcode(escape_tags($itemem_text)),
'body' => $body,
// these are for the superblock addon
'hash' => $item['author']['xchan_hash'],
'hash' => $item[$who]['xchan_hash'],
'uid' => $item['uid'],
'display' => true
);
@@ -910,27 +894,33 @@ class Enotify {
}
static public function format_notify($tt) {
$message = trim(strip_tags(bbcode($tt['msg'])));
if(strpos($message, $tt['xname']) === 0)
$message = substr($message, strlen($tt['xname']) + 1);
$mid = basename($tt['link']);
$b64mid = gen_link_id($mid);
$x = [
'notify_link' => (($tt['ntype'] === NOTIFY_INTRO) ? z_root() . '/notify/view/' . $tt['id'] : $tt['link']),
'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
'name' => $tt['xname'],
'url' => $tt['url'],
'photo' => $tt['photo'],
'when' => datetime_convert('UTC', date_default_timezone_get(), $tt['created']),
'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'),
'b64mid' => (($tt['otype'] == 'item') ? $tt['hash'] : ''),
'b64mid' => (($tt['otype'] == 'item') ? $b64mid : ''),
'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : ''),
'message' => $message
];
return $x;
}
static public function format_intros($rr) {
return [
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
'name' => $rr['xchan_name'],

View File

@@ -13,8 +13,7 @@ class IConfig {
static public function Get(&$item, $family, $key, $default = false) {
$is_item = false;
$iid = null;
if(is_array($item)) {
$is_item = true;
if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig'])))
@@ -23,57 +22,32 @@ class IConfig {
if(array_key_exists('item_id',$item))
$iid = $item['item_id'];
else
$iid = $item['id'] ?? 0;
$iid = $item['id'];
}
elseif(intval($item))
$iid = $item;
if (!$iid)
if(! $iid)
return $default;
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
foreach($item['iconfig'] as $c) {
if (isset($c['iid']) && $c['iid'] == $iid && isset($c['cat']) && $c['cat'] == $family && isset($c['k']) && $c['k'] == $key) {
if (is_string($c['v'])) {
if (str_starts_with($c['v'], 'json:')) {
$c['v'] = json_unserialize($c['v']);
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $c['v'])) {
// Unserialize in inherently unsafe. Try to mitigate by not
// allowing unserializing objects. Only kept for backwards
// compatibility. JSON serialization should be prefered.
$c['v'] = unserialize($c['v'], ['allowed_classes' => false]);
}
}
if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key)
return $c['v'];
}
}
}
$r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1",
intval($iid),
dbesc($family),
dbesc($key)
);
if($r) {
if (str_starts_with($r[0]['v'], 'json:')) {
$r[0]['v'] = json_unserialize($r[0]['v']);
} else if (preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) {
// Unserialize in inherently unsafe. Try to mitigate by not
// allowing unserializing objects. Only kept for backwards
// compatibility. JSON serialization should be prefered.
$r[0]['v'] = unserialize($r[0]['v'], ['allowed_classes' => false]);
}
if ($is_item) {
$r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
if($is_item)
$item['iconfig'][] = $r[0];
}
return $r[0]['v'];
}
return $default;
}
@@ -89,15 +63,15 @@ class IConfig {
* $value - value of meta variable
* $sharing - boolean (default false); if true the meta information is propagated with the item
* to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered.
* If the meta information is added after delivery and you wish it to be shared, it may be necessary to
* alter the item edited timestamp and invoke the delivery process on the updated item. The edited
* If the meta information is added after delivery and you wish it to be shared, it may be necessary to
* alter the item edited timestamp and invoke the delivery process on the updated item. The edited
* timestamp needs to be altered in order to trigger an item_store_update() at the receiving end.
*/
static public function Set(&$item, $family, $key, $value, $sharing = false) {
$dbvalue = ((is_array($value)) ? json_serialize($value) : $value);
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
$is_item = false;
@@ -188,4 +162,4 @@ class IConfig {
}
}
}

View File

@@ -25,18 +25,18 @@ class Img_filesize {
static function getLocalFileSize($url) {
$fname = basename($url);
$resolution = 0;
if(strpos($fname,'.') !== false)
$fname = substr($fname,0,strpos($fname,'.'));
if(substr($fname,-2,1) == '-') {
$resolution = intval(substr($fname,-1,1));
$fname = substr($fname,0,-2);
}
$r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($fname),
intval($resolution)
@@ -116,6 +116,7 @@ function getRemoteFileSize($url)
curl_exec($ch);
curl_getinfo($ch);
curl_close($ch);
return $size;
}
}

View File

@@ -52,7 +52,7 @@ class JSalmon {
. base64url_encode($x['alg'],true);
$key = HTTPSig::get_key(EMPTY_STR,'zot6',base64url_decode($x['sigs']['key_id']));
logger('key: ' . print_r($key,true), LOGGER_DATA);
logger('key: ' . print_r($key,true));
if($key['portable_id'] && $key['public_key']) {
if(Crypto::verify($signed_data,base64url_decode($x['sigs']['value']),$key['public_key'])) {
logger('verified');

View File

@@ -1,109 +0,0 @@
<?php
namespace Zotlabs\Lib;
use Root23\JsonCanonicalizer\JsonCanonicalizer;
use StephenHill\Base58;
class JcsEddsa2022 {
/**
* Sign arbitrary data with the keys of the provided channel.
*
* @param $data The data to be signed.
* @param array $channel A channel as an array of key/value pairs.
*
* @return An array with the following fields:
* - `type`: The type of signature, always `DataIntegrityProof`.
* - `cryptosuite`: The cryptographic algorithm used, always `eddsa-jcs-2022`.
* - `created`: The UTC date and timestamp when the signature was created.
* - `verificationMethod`: The channel URL and the public key separated by a `#`.
* - `proofPurpose`: The purpose of the signature, always `assertionMethod`.
* - `proofValue`: The signature itself.
*
* @throws JcsEddsa2022SignatureException if the channel is missing, or
* don't have valid keys.
*/
public function sign($data, $channel): array {
if (!is_array($channel) || !isset($channel['channel_epubkey'], $channel['channel_eprvkey'])) {
throw new JcsEddsa2022SignException('Invalid or missing channel provided.');
}
$base58 = new Base58();
$pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
$options = [
'type' => 'DataIntegrityProof',
'cryptosuite' => 'eddsa-jcs-2022',
'created' => datetime_convert('UTC', 'UTC', 'now', ATOM_TIME),
'verificationMethod' => channel_url($channel) . '#' . $pubkey,
'proofPurpose' => 'assertionMethod',
];
$optionsHash = $this->hash($this->signableOptions($options), true);
$dataHash = $this->hash($this->signableData($data), true);
$options['proofValue'] = 'z' . $base58->encode(sodium_crypto_sign_detached($optionsHash . $dataHash,
sodium_base642bin($channel['channel_eprvkey'], SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING)));
return $options;
}
public function verify($data, $publicKey) {
$base58 = new Base58();
$encodedSignature = $data['proof']['proofValue'] ?? '';
if (!str_starts_with($encodedSignature,'z')) {
return false;
}
$encodedSignature = substr($encodedSignature, 1);
$optionsHash = $this->hash($this->signableOptions($data['proof']), true);
$dataHash = $this->hash($this->signableData($data),true);
try {
$result = sodium_crypto_sign_verify_detached($base58->decode($encodedSignature), $optionsHash . $dataHash,
(new Multibase())->decode($publicKey, true));
}
catch (\Exception $e) {
logger('verify exception:' . $e->getMessage());
}
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
return $result;
}
public function signableData($data) {
$signableData = [];
if ($data) {
foreach ($data as $k => $v) {
if (!in_array($k, ['proof', 'signature'])) {
$signableData[$k] = $v;
}
}
}
return $signableData;
}
public function signableOptions($options) {
$signableOptions = [];
if ($options) {
foreach ($options as $k => $v) {
if ($k !== 'proofValue') {
$signableOptions[$k] = $v;
}
}
}
return $signableOptions;
}
public function hash($obj, $binary = false) {
return hash('sha256', $this->canonicalize($obj), $binary);
}
public function canonicalize($data) {
$canonicalizer = new JsonCanonicalizer();
return $canonicalizer->canonicalize($data);
}
}

View File

@@ -1,15 +0,0 @@
<?php
/*
* SPDX-FileCopyrightText: 2025 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use Exception;
class JcsEddsa2022SignException extends Exception
{
}

View File

@@ -2,8 +2,8 @@
namespace Zotlabs\Lib;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
/**
* Keyutils
@@ -16,42 +16,41 @@ class Keyutils {
* @param string $e exponent
* @return string
*/
public static function meToPem(string $m, string $e): string
{
$parsedKey = PublicKeyLoader::load([
public static function meToPem($m, $e) {
$rsa = new RSA();
$rsa->loadKey([
'e' => new BigInteger($e, 256),
'n' => new BigInteger($m, 256)
]);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS8');
return $rsa->getPublicKey();
}
/**
* @param string key
* @return string
*/
public static function rsaToPem(string $key): string
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS8');
public static function rsaToPem($key) {
$rsa = new RSA();
$rsa->setPublicKey($key);
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
}
/**
* @param string key
* @return string
*/
public static function pemToRsa(string $key): string
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
return $parsedKey->toString('PKCS1');
public static function pemToRsa($key) {
$rsa = new RSA();
$rsa->setPublicKey($key);
return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
}
/**
@@ -59,28 +58,23 @@ class Keyutils {
* @param string $m reference modulo
* @param string $e reference exponent
*/
public static function pemToMe(string $key): array
{
$parsedKey = PublicKeyLoader::load($key);
if (method_exists($parsedKey, 'getPublicKey')) {
$parsedKey = $parsedKey->getPublicKey();
}
$raw = $parsedKey->toString('Raw');
public static function pemToMe($key, &$m, &$e) {
$m = $raw['n'];
$e = $raw['e'];
$rsa = new RSA();
$rsa->loadKey($key);
$rsa->setPublicKey();
$m = $rsa->modulus->toBytes();
$e = $rsa->exponent->toBytes();
return [$m->toBytes(), $e->toBytes()];
}
/**
* @param string $pubkey
* @return string
*/
public static function salmonKey(string $pubkey): string
{
[$m, $e] = self::pemToMe($pubkey);
/** @noinspection PhpRedundantOptionalArgumentInspection */
public static function salmonKey($pubkey) {
self::pemToMe($pubkey, $m, $e);
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true);
}
@@ -88,13 +82,11 @@ class Keyutils {
* @param string $key
* @return string
*/
public static function convertSalmonKey(string $key): string
{
if (str_contains($key, ',')) {
public static function convertSalmonKey($key) {
if (strstr($key, ','))
$rawkey = substr($key, strpos($key, ',') + 1);
} else {
else
$rawkey = substr($key, 5);
}
$key_info = explode('.', $rawkey);
@@ -104,4 +96,4 @@ class Keyutils {
return self::meToPem($m, $e);
}
}
}

View File

@@ -5,7 +5,6 @@ namespace Zotlabs\Lib;
use App;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Config;
class Libsync {
@@ -136,13 +135,13 @@ class Libsync {
$info['collection_members'] = $r;
}
$interval = Config::Get('queueworker', 'queue_interval', 500000);
$interval = get_config('queueworker', 'queue_interval', 500000);
logger('Packet: ' . print_r($info, true), LOGGER_DATA, LOG_DEBUG);
$total = count($synchubs);
foreach ($synchubs as $hub) {
$hash = new_uuid();
$hash = random_string();
$n = Libzot::build_packet($channel, 'sync', $env_recips, json_encode($info), 'hz', $hub['hubloc_sitekey'], $hub['site_crypto']);
Queue::insert([
'hash' => $hash,
@@ -158,7 +157,7 @@ class Libsync {
/*
$x = q("select count(outq_hash) as total from outq where outq_delivered = 0");
if (intval($x[0]['total']) > intval(Config::Get('system', 'force_queue_threshold', 3000))) {
if (intval($x[0]['total']) > intval(get_config('system', 'force_queue_threshold', 3000))) {
logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO);
Queue::update($hash);
continue;
@@ -199,7 +198,8 @@ class Libsync {
dbesc($sender)
);
$mid = $arr['item'][0]['message_id'] ?? 'sync';
$mid = 'sync';
$DR = new DReport(z_root(), $sender, $d, $mid);
@@ -266,7 +266,7 @@ class Libsync {
}
if ($cat !== 'hz_delpconfig') {
set_pconfig($channel['channel_id'], $cat, $k, $v, $pconfig_updated[$k]);
set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]);
}
}
}
@@ -305,8 +305,15 @@ class Libsync {
if (array_key_exists('item', $arr) && $arr['item']) {
sync_items($channel, $arr['item'], ((array_key_exists('relocate', $arr)) ? $arr['relocate'] : null));
$mid = $arr['item'][0]['message_id'] . '#sync';
}
// deprecated, maintaining for a few months for upward compatibility
// this should sync webpages, but the logic is a bit subtle
//if (array_key_exists('item_id', $arr) && $arr['item_id'])
// sync_items($channel, $arr['item_id']);
if (array_key_exists('menu', $arr) && $arr['menu'])
sync_menus($channel, $arr['menu']);
@@ -318,7 +325,10 @@ class Libsync {
if (array_key_exists('channel', $arr) && is_array($arr['channel']) && count($arr['channel'])) {
if (array_key_exists('channel_pageflags', $arr['channel'])) {
$remote_channel = $arr['channel'];
$remote_channel['channel_id'] = $channel['channel_id'];
if (array_key_exists('channel_pageflags', $arr['channel']) && intval($arr['channel']['channel_pageflags'])) {
// Several pageflags are site-specific and cannot be sync'd.
// Only allow those bits which are shareable from the remote and then
@@ -329,8 +339,6 @@ class Libsync {
}
$columns = db_columns('channel');
$disallowed = [
'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey',
'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted',
@@ -341,21 +349,16 @@ class Libsync {
'channel_a_delegate'
];
if (empty($channel['channel_epubkey']) && empty($channel['channel_eprvkey'])) {
$eckey = sodium_crypto_sign_keypair();
$channel['channel_epubkey'] = sodium_bin2base64(sodium_crypto_sign_publickey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
$channel['channel_eprvkey'] = sodium_bin2base64(sodium_crypto_sign_secretkey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
}
$clean = [];
foreach ($arr['channel'] as $k => $v) {
if (in_array($k, $disallowed)) {
if (in_array($k, $disallowed))
continue;
$clean[$k] = $v;
}
if (count($clean)) {
foreach ($clean as $k => $v) {
dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) . "' where channel_id = " . intval($channel['channel_id']));
}
if (!in_array($k, $columns)) {
continue;
}
$r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v)
. "' where channel_id = " . intval($channel['channel_id']));
}
}
@@ -749,11 +752,12 @@ class Libsync {
*/
call_hooks('process_channel_sync_delivery', $addon);
$DR->update('channel sync processed');
$DR = new DReport(z_root(), $d, $d, $mid, 'channel sync processed');
$DR->set_name($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
$result[] = $DR->get();
}
return $result;
}
@@ -762,17 +766,13 @@ class Libsync {
*
* @param array $sender
* @param array $arr
* @param boolean $absolute (optional) default false
* @return array
*/
static function sync_locations($sender, $arr) {
$ret = [
'change_message' => '',
'changed' => false,
'message' => ''
];
static function sync_locations($sender, $arr, $absolute = false) {
$ret = [];
$what = '';
$changed = false;
@@ -787,7 +787,10 @@ class Libsync {
if (isset($arr['locations']) && $arr['locations']) {
$xisting = q("select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
if ($absolute)
Libzot::check_location_move($sender['hash'], $arr['locations']);
$xisting = q("select * from hubloc where hubloc_hash = '%s'",
dbesc($sender['hash'])
);
@@ -876,7 +879,7 @@ class Libsync {
dbesc($t)
);
q("update hubloc set hubloc_error = 1, hubloc_deleted = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s' and hubloc_network = 'zot6'",
q("update hubloc set hubloc_error = 1, hubloc_deleted = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s'",
dbesc($r[0]['hubloc_url']),
dbesc($r[0]['hubloc_sitekey'])
);
@@ -930,7 +933,14 @@ class Libsync {
$what .= 'primary_hub ';
$changed = true;
}
elseif ($absolute) {
// Absolute sync - make sure the current primary is correctly reflected in the xchan
$pr = hubloc_change_primary($r[0]);
if ($pr) {
$what .= 'xchan_primary ';
$changed = true;
}
}
if (intval($r[0]['hubloc_deleted']) && (!intval($location['deleted']))) {
q("update hubloc set hubloc_deleted = 0, hubloc_updated = '%s' where hubloc_id_url = '%s'",
dbesc(datetime_convert()),
@@ -999,9 +1009,9 @@ class Libsync {
}
}
// get rid of any hublocs we have for this channel which weren't reported.
// get rid of any hubs we have for this channel which weren't reported.
if ($xisting) {
if ($absolute && $xisting) {
foreach ($xisting as $x) {
if (!array_key_exists('updated', $x)) {
logger('Deleting unreferenced hub location ' . $x['hubloc_addr']);

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,6 @@
namespace Zotlabs\Lib;
use DBA;
use Zotlabs\Lib\Config;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Zotfinger;
use Zotlabs\Lib\Webfinger;
@@ -22,7 +20,7 @@ class Libzotdir {
static function find_upstream_directory($dirmode) {
$preferred = Config::Get('system','directory_server');
$preferred = get_config('system','directory_server');
// Thwart attempts to use a private directory
@@ -49,17 +47,17 @@ class Libzotdir {
$directory_fallback_servers = get_directory_fallback_servers();
$dirmode = intval(Config::Get('system','directory_mode'));
$dirmode = intval(get_config('system','directory_mode'));
if ($dirmode == DIRECTORY_MODE_NORMAL) {
$toss = mt_rand(0,count($directory_fallback_servers));
$preferred = $directory_fallback_servers[$toss];
if(! $preferred) {
$preferred = DIRECTORY_FALLBACK_MASTER;
}
Config::Set('system','directory_server',$preferred);
set_config('system','directory_server',$preferred);
}
else {
Config::Set('system','directory_server',z_root());
set_config('system','directory_server',z_root());
}
}
if($preferred) {
@@ -79,7 +77,7 @@ class Libzotdir {
static function check_upstream_directory() {
$directory = Config::Get('system', 'directory_server');
$directory = get_config('system', 'directory_server');
// it's possible there is no directory server configured and the local hub is being used.
// If so, default to preserving the absence of a specific server setting.
@@ -96,7 +94,7 @@ class Libzotdir {
}
if (! $isadir)
Config::Set('system', 'directory_server', '');
set_config('system', 'directory_server', '');
}
@@ -108,7 +106,7 @@ class Libzotdir {
$ret = ((array_key_exists($setting,$_SESSION)) ? intval($_SESSION[$setting]) : false);
if($ret === false)
$ret = Config::Get('directory', $setting);
$ret = get_config('directory', $setting);
// 'safemode' is the default if there is no observer or no established preference.
@@ -116,7 +114,7 @@ class Libzotdir {
if($setting === 'safemode' && $ret === false)
$ret = 1;
if($setting === 'globaldir' && intval(Config::Get('system','localdir_hide')))
if($setting === 'globaldir' && intval(get_config('system','localdir_hide')))
$ret = 1;
return $ret;
@@ -135,7 +133,7 @@ class Libzotdir {
$globaldir = self::get_directory_setting($observer, 'globaldir');
$pubforums = self::get_directory_setting($observer, 'pubforums');
$hide_local = intval(Config::Get('system','localdir_hide'));
$hide_local = intval(get_config('system','localdir_hide'));
if($hide_local)
$globaldir = 1;
@@ -143,7 +141,7 @@ class Libzotdir {
// Build urls without order and pubforums so it's easy to tack on the changed value
// Probably there's an easier way to do this
$directory_sort_order = Config::Get('system','directory_sort_order');
$directory_sort_order = get_config('system','directory_sort_order');
if(! $directory_sort_order)
$directory_sort_order = 'date';
@@ -174,12 +172,13 @@ class Libzotdir {
}
/**
* @brief fetches updates from known directories
* @brief Checks the directory mode of this hub.
*
* Checks the directory mode of this hub to see if it is some form of directory server. If it is,
* get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
* a directory sync packet. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task.
* a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB.
* In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored
* directly if the rater's signature matches.
*
* @param int $dirmode;
*/
@@ -217,7 +216,7 @@ class Libzotdir {
[
'site_url' => DIRECTORY_FALLBACK_MASTER,
'site_flags' => DIRECTORY_MODE_PRIMARY,
'site_update' => DBA::$dba->get_null_date(),
'site_update' => NULL_DATE,
'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch',
'site_realm' => DIRECTORY_REALM,
'site_valid' => 1,
@@ -234,8 +233,6 @@ class Libzotdir {
if (! $r)
return;
$dir_trusted_hosts = array_merge(get_directory_fallback_servers(), Config::Get('system', 'trusted_directory_servers', []));
foreach ($r as $rr) {
if (! $rr['site_directory'])
continue;
@@ -246,9 +243,9 @@ class Libzotdir {
// It will take about a month for a new directory to obtain the full current repertoire of channels.
/** @FIXME Go back and pick up earlier ratings if this is a new directory server. These do not get refreshed. */
$token = Config::Get('system','realm_token');
$token = get_config('system','realm_token');
$syncdate = (($rr['site_sync'] <= DBA::$dba->get_null_date()) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
$syncdate = (($rr['site_sync'] <= NULL_DATE) ? datetime_convert('UTC','UTC','now - 2 days') : $rr['site_sync']);
$x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . (($token) ? '&t=' . $token : ''));
if (! $x['success'])
@@ -267,65 +264,31 @@ class Libzotdir {
if (is_array($j['transactions']) && count($j['transactions'])) {
foreach ($j['transactions'] as $t) {
if (empty($t['hash']) || empty($t['host']) || empty($t['address'])) {
if (empty($t['hash']) || empty($t['transaction_id']) || empty($t['address'])) {
continue;
}
$r = q("select * from updates where ud_hash = '%s' limit 1",
dbesc($t['hash'])
$r = q("select * from updates where ud_guid = '%s' limit 1",
dbesc($t['transaction_id'])
);
if($r)
continue;
if ($r) {
$update = 0;
$ud_flags = 0;
if (is_array($t['flags']) && in_array('deleted',$t['flags']))
$ud_flags |= UPDATE_FLAGS_DELETED;
if (is_array($t['flags']) && in_array('forced',$t['flags']))
$ud_flags |= UPDATE_FLAGS_FORCED;
// no need to look at updates that originated from our own site
if ($t['host'] === z_root()) {
continue;
}
// there is more recent xchan information
if ($r[0]['ud_date'] <= $t['timestamp']) {
$update = 1;
}
// the host is trusted and flags have changed - update flags immediately
if (in_array($t['host'], $dir_trusted_hosts) &&
$rr['site_url'] === $t['host'] &&
intval($r[0]['ud_flags']) !== intval($t['flags'])) {
q("UPDATE updates SET ud_update = %d, ud_flags = %d WHERE ud_id = %d",
intval($update),
intval($t['flags']),
dbesc($r[0]['ud_id'])
);
q("UPDATE xchan SET xchan_censored = %d WHERE xchan_hash = '%s'",
intval($t['flags']),
dbesc($r[0]['ud_hash'])
);
continue;
}
if (!$update) {
continue;
}
q("UPDATE updates SET ud_update = %d WHERE ud_id = %d",
intval($update),
dbesc($r[0]['ud_id'])
);
}
else {
q("insert into updates ( ud_hash, ud_host, ud_date, ud_addr, ud_update, ud_flags )
values ( '%s', '%s', '%s', '%s', 1, %d) ",
dbesc($t['hash']),
dbesc($t['host']),
dbesc($t['timestamp']),
dbesc($t['address']),
dbesc(in_array($t['host'], $dir_trusted_hosts) ? $t['flags'] : 0)
);
}
$z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )
values ( '%s', '%s', '%s', %d, '%s' ) ",
dbesc($t['hash']),
dbesc($t['transaction_id']),
dbesc($t['timestamp']),
intval($ud_flags),
dbesc($t['address'])
);
}
}
}
@@ -340,9 +303,8 @@ class Libzotdir {
*
* Ignore updating records marked as deleted.
*
* If successful, sets ud_updated in the DB to the current datetime for this
* If successful, sets ud_last in the DB to the current datetime for this
* reddress/webbie.
* Else update ud_last so we can stop trying after 7 days (Daemon/Poller.php)
*
* @param array $ud Entry from update table
*/
@@ -351,44 +313,32 @@ class Libzotdir {
logger('update_directory_entry: ' . print_r($ud,true), LOGGER_DATA);
// TODO: remove this check after all directory servers have version > 8.4
// ud_addr will always be the channel url at that time
$href = ((strpos($ud['ud_addr'], '://') === false) ? Webfinger::zot_url(punify($ud['ud_addr'])) : punify($ud['ud_addr']));
if($href) {
$zf = Zotfinger::exec($href);
if($zf && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
$xc = Libzot::import_xchan($zf['data']);
// xchan_hash mismatch - this can happen after a site re-install at the same url
if ($xc['success'] && $xc['hash'] !== $ud['ud_hash']) {
self::delete_by_hash($ud['ud_hash']);
}
// if the channel was deleted - delete the entry in updates
if (!empty($zf['data']['deleted_locally'])) {
self::delete_by_hash($ud['ud_hash']);
}
if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) {
$success = false;
$zf = [];
$href = Webfinger::zot_url(punify($ud['ud_addr']));
if($href) {
$zf = Zotfinger::exec($href);
}
if(array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
$xc = Libzot::import_xchan($zf['data'], 0, $ud);
// This is a workaround for a missing xchan_updated column
// TODO: implement xchan_updated in the xchan table and update this column instead
if(!empty($zf['data']['primary_location']['url'])) {
if($zf['data']['primary_location']['address'] && $zf['data']['primary_location']['url']) {
q("UPDATE hubloc SET hubloc_updated = '%s' WHERE hubloc_id_url = '%s' AND hubloc_primary = 1",
dbesc(datetime_convert()),
dbesc($zf['data']['primary_location']['url'])
);
}
return true;
}
else {
q("update updates set ud_last = '%s' where ud_addr = '%s'",
dbesc(datetime_convert()),
dbesc($ud['ud_addr'])
);
}
}
q("UPDATE updates SET ud_addr = '%s', ud_last = '%s' WHERE ud_hash = '%s'",
dbesc($href ? $href : $ud['ud_addr']),
dbesc(datetime_convert()),
dbesc($ud['ud_hash'])
);
return false;
}
@@ -403,78 +353,85 @@ class Libzotdir {
*/
static function local_dir_update($uid, $force) {
logger('local_dir_update uid: ' . $uid, LOGGER_DEBUG);
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.*, xchan.xchan_hidden, xchan.xchan_url from profile left join channel on channel_id = uid left join xchan on channel_hash = xchan_hash where profile.uid = %d and profile.is_default = 1",
logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG);
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
intval($uid)
);
if (!$p) {
logger('profile not found');
return;
}
$profile = [];
$profile = array();
$profile['encoding'] = 'zot';
$hash = $p[0]['channel_hash'];
if ($p) {
$hash = $p[0]['channel_hash'];
$profile['description'] = $p[0]['pdesc'];
$profile['birthday'] = $p[0]['dob'];
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
$profile['age'] = $age;
$profile['description'] = $p[0]['pdesc'];
$profile['birthday'] = $p[0]['dob'];
if ($age = age($p[0]['dob'],$p[0]['channel_timezone'],''))
$profile['age'] = $age;
$profile['gender'] = $p[0]['gender'];
$profile['marital'] = $p[0]['marital'];
$profile['sexual'] = $p[0]['sexual'];
$profile['locale'] = $p[0]['locality'];
$profile['region'] = $p[0]['region'];
$profile['postcode'] = $p[0]['postal_code'];
$profile['country'] = $p[0]['country_name'];
$profile['about'] = $p[0]['about'];
$profile['homepage'] = $p[0]['homepage'];
$profile['hometown'] = $p[0]['hometown'];
$profile['gender'] = $p[0]['gender'];
$profile['marital'] = $p[0]['marital'];
$profile['sexual'] = $p[0]['sexual'];
$profile['locale'] = $p[0]['locality'];
$profile['region'] = $p[0]['region'];
$profile['postcode'] = $p[0]['postal_code'];
$profile['country'] = $p[0]['country_name'];
$profile['about'] = $p[0]['about'];
$profile['homepage'] = $p[0]['homepage'];
$profile['hometown'] = $p[0]['hometown'];
if ($p[0]['keywords']) {
$tags = array();
$k = explode(' ', $p[0]['keywords']);
if ($k)
foreach ($k as $kk)
if (trim($kk))
$tags[] = trim($kk);
if ($p[0]['keywords']) {
$tags = array();
$k = explode(' ', $p[0]['keywords']);
if ($k)
foreach ($k as $kk)
if (trim($kk))
$tags[] = trim($kk);
if ($tags)
$profile['keywords'] = $tags;
}
if ($tags)
$profile['keywords'] = $tags;
}
$hidden = (1 - intval($p[0]['publish']));
$hidden = (1 - intval($p[0]['publish']));
logger('hidden: ' . $hidden);
logger('hidden: ' . $hidden);
if(intval($p[0]['xchan_hidden']) !== $hidden) {
q("update xchan set xchan_hidden = %d where xchan_hash = '%s'",
intval($hidden),
dbesc($hash)
$r = q("select xchan_hidden from xchan where xchan_hash = '%s'",
dbesc($p[0]['channel_hash'])
);
if(intval($r[0]['xchan_hidden']) != $hidden) {
$r = q("update xchan set xchan_hidden = %d where xchan_hash = '%s'",
intval($hidden),
dbesc($hash)
);
}
$arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ];
call_hooks('local_dir_update', $arr);
$address = channel_reddress($p[0]);
if (perm_is_allowed($uid, '', 'view_profile')) {
self::import_directory_profile($hash, $arr['profile'], $address, 0);
}
else {
// they may have made it private
q("delete from xprof where xprof_hash = '%s'",
dbesc($hash)
);
q("delete from xtag where xtag_hash = '%s'",
dbesc($hash)
);
}
}
$arr = [ 'channel_id' => $uid, 'hash' => $hash, 'profile' => $profile ];
call_hooks('local_dir_update', $arr);
if (perm_is_allowed($uid, '', 'view_profile')) {
self::import_directory_profile($hash, $arr['profile']);
}
else {
// they may have made it private
q("delete from xprof where xprof_hash = '%s'",
dbesc($hash)
);
q("delete from xtag where xtag_hash = '%s'",
dbesc($hash)
);
}
self::update($hash, $p[0]['xchan_url']);
$ud_hash = random_string() . '@' . \App::get_hostname();
self::update_modtime($hash, $ud_hash, channel_reddress($p[0]),(($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED));
}
@@ -484,10 +441,13 @@ class Libzotdir {
*
* @param string $hash
* @param array $profile
* @param string $addr
* @param number $ud_flags (optional) UPDATE_FLAGS_UPDATED
* @param number $suppress_update (optional) default 0
* @return boolean $updated if something changed
*/
static function import_directory_profile($hash, $profile) {
static function import_directory_profile($hash, $profile, $addr, $ud_flags = UPDATE_FLAGS_UPDATED, $suppress_update = 0) {
logger('import_directory_profile', LOGGER_DEBUG);
if (! $hash)
@@ -520,12 +480,7 @@ class Libzotdir {
$clean = array();
if (array_key_exists('keywords', $profile) and is_array($profile['keywords'])) {
self::import_directory_keywords($hash,$profile['keywords']);
foreach ($profile['keywords'] as $kw) {
if (in_array($kw, $clean)) {
continue;
}
$kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false));
$kw = trim($kw, ',');
$clean[] = $kw;
@@ -632,6 +587,9 @@ class Libzotdir {
*/
call_hooks('import_directory_profile', $d);
if (($d['update']) && (! $suppress_update))
self::update_modtime($arr['xprof_hash'],random_string() . '@' . \App::get_hostname(), $addr, $ud_flags);
return $d['update'];
}
@@ -649,10 +607,6 @@ class Libzotdir {
dbesc($hash)
);
$xchan = q("select xchan_censored from xchan where xchan_hash = '%s'",
dbesc($hash)
);
if($r) {
foreach($r as $rr)
$existing[] = $rr['xtag_term'];
@@ -660,10 +614,6 @@ class Libzotdir {
$clean = array();
foreach($keywords as $kw) {
if (in_array($kw, $clean)) {
continue;
}
$kw = trim(htmlspecialchars($kw,ENT_COMPAT, 'UTF-8', false));
$kw = trim($kw, ',');
$clean[] = $kw;
@@ -678,10 +628,9 @@ class Libzotdir {
}
foreach($clean as $x) {
if(! in_array($x, $existing)) {
$r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', %d )",
$r = q("insert into xtag ( xtag_hash, xtag_term, xtag_flags) values ( '%s' ,'%s', 0 )",
dbesc($hash),
dbesc($x),
intval($xchan[0]['xchan_censored'])
dbesc($x)
);
}
}
@@ -691,83 +640,40 @@ class Libzotdir {
/**
* @brief
*
* @param string $hash the channel hash
* @param string $addr the channel url
* @param bool $bump_date (optional) default true
* @param string $hash
* @param string $guid
* @param string $addr
* @param int $flags (optional) default 0
*/
static function update($hash, $addr, $bump_date = true, $flag = null) {
static function update_modtime($hash, $guid, $addr, $flags = 0) {
$dirmode = intval(Config::Get('system', 'directory_mode'));
$dirmode = intval(get_config('system', 'directory_mode'));
if($dirmode == DIRECTORY_MODE_NORMAL) {
return;
}
if (empty($hash) || empty($addr)) {
if (empty($hash) || empty($guid) || empty($addr)) {
return;
}
$u = q("SELECT * FROM updates WHERE ud_hash = '%s' LIMIT 1",
dbesc($hash)
);
$date_sql = '';
if ($bump_date) {
$date_sql = "ud_date = '" . dbesc(datetime_convert()) . "',";
}
$flag_sql = '';
if ($flag !== null) {
$flag_sql = "ud_flags = '" . intval($flag) . "',";
}
if ($u) {
$x = q("UPDATE updates SET $date_sql $flag_sql ud_last = '%s', ud_host = '%s', ud_addr = '%s', ud_update = 0 WHERE ud_id = %d",
dbesc(DBA::$dba->get_null_date()),
dbesc(z_root()),
dbesc($addr),
intval($u[0]['ud_id'])
if($flags) {
q("insert into updates (ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) values ( '%s', '%s', '%s', %d, '%s' )",
dbesc($hash),
dbesc($guid),
dbesc(datetime_convert()),
intval($flags),
dbesc($addr)
);
return;
}
q("INSERT INTO updates (ud_hash, ud_host, ud_date, ud_addr, ud_flags) VALUES ( '%s', '%s', '%s', '%s', %d )",
dbesc($hash),
dbesc(z_root()),
dbesc(datetime_convert()),
dbesc($addr),
intval($flag)
);
return;
}
/**
* @brief deletes a entry in updates by hash
*
* @param string $hash the channel hash
* @return boolean
*/
static function delete_by_hash($hash) {
if (!$hash) {
return false;
else {
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ",
intval(UPDATE_FLAGS_UPDATED),
dbesc($addr),
intval(UPDATE_FLAGS_UPDATED)
);
}
$x = q("DELETE FROM updates WHERE ud_hash = '%s'",
dbesc($hash)
);
if ($x) {
return true;
}
return false;
}
}

View File

@@ -1,86 +0,0 @@
<?php
/**
* Mailer class for sending emails from Hubzilla.
*
* SPDX-FileCopyrightText: 2024 Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use App;
/**
* A class for sending emails.
*
* Based on the previous `z_mail` function, but adaped and made more
* robust and usable as a class.
*/
class Mailer {
public function __construct(private array $params = []) {
}
public function deliver(): bool {
if(empty($this->params['fromEmail'])) {
$this->params['fromEmail'] = Config::Get('system','from_email');
if(empty($this->params['fromEmail'])) {
$this->params['fromEmail'] = 'Administrator@' . App::get_hostname();
}
}
if(empty($this->params['fromName'])) {
$this->params['fromName'] = Config::Get('system','from_email_name');
if(empty($this->params['fromName'])) {
$this->params['fromName'] = System::get_site_name();
}
}
if(empty($this->params['replyTo'])) {
$this->params['replyTo'] = Config::Get('system','reply_address');
if(empty($this->params['replyTo'])) {
$this->params['replyTo'] = 'noreply@' . App::get_hostname();
}
}
if (!isset($this->params['additionalMailHeader'])) {
$this->params['additionalMailHeader'] = '';
}
$this->params['sent'] = false;
$this->params['result'] = false;
/**
* @hooks email_send
* * \e params @see z_mail()
*/
call_hooks('email_send', $this->params);
if($this->params['sent']) {
logger('notification: z_mail returns ' . (($this->params['result']) ? 'success' : 'failure'), LOGGER_DEBUG);
return $this->params['result'];
}
$fromName = email_header_encode(html_entity_decode($this->params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
$messageSubject = email_header_encode(html_entity_decode($this->params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8');
$messageHeader =
$this->params['additionalMailHeader'] .
"From: $fromName <{$this->params['fromEmail']}>" . PHP_EOL .
"Reply-To: $fromName <{$this->params['replyTo']}>" . PHP_EOL .
"Content-Type: text/plain; charset=UTF-8";
// send the message
$res = mail(
$this->params['toEmail'], // send to address
$messageSubject, // subject
$this->params['textVersion'],
$messageHeader // message headers
);
logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG);
return $res;
}
}

View File

@@ -4,417 +4,242 @@ namespace Zotlabs\Lib;
require_once('include/html2plain.php');
class MessageFilter
{
protected $lastMatch = '';
protected $item = null;
protected $include = '';
protected $exclude = '';
protected $options = [];
protected $tags = null;
protected $language = '';
protected $text = '';
protected $excludeRules = [];
protected $includeRules = [];
class MessageFilter {
public function __construct($item, $include = '', $exclude = '', $options = [])
{
$this->item = $item;
$this->include = $include;
$this->exclude = $exclude;
$this->options = $options;
$this->setup();
}
public static function evaluate($item, $incl, $excl) {
protected function setup()
{
// Option: plaintext
// Improve language detection by providing a plaintext version of $item['body'] which has no markup constructs/tags.
$text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
if (array_key_exists('plaintext', $this->options)) {
$this->text = $this->options['plaintext'];
} else {
$this->text = $this->item['body'];
}
$lang = null;
$this->language = '';
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
$lang = detect_language($text);
}
// Language matching is a bit tricky, because the language can be ambiguous (detect_language() returns '').
// If the language is ambiguous, the message will pass (be accepted) regardless of language rules.
$tags = ((isset($item['term']) && is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
if (str_contains($this->include, 'lang=')
|| str_contains($this->exclude, 'lang=')
|| str_contains($this->include, 'lang!=')
|| str_contains($this->exclude, 'lang!=')) {
$this->language = detect_language($this->text);
}
// exclude always has priority
$this->tags = ((isset($this->item['term']) && is_array($this->item['term'])
&& count($this->item['term'])) ? $this->item['term'] : null);
$exclude = (($excl) ? explode("\n", $excl) : null);
$this->excludeRules = $this->parse($this->exclude);
$this->includeRules = $this->parse($this->include);
if ($exclude) {
foreach ($exclude as $word) {
$word = trim($word);
if (! $word) {
continue;
}
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
if (!strlen($lang)) {
// Result is ambiguous. As we are matching deny rules only at this time, continue tests.
// Any matching deny rule concludes testing.
continue;
}
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
return false;
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
return false;
}
}
elseif (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return false;
}
}
} elseif (substr($word, 0, 1) === '$' && $tags) {
foreach ($tags as $t) {
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return false;
}
}
} elseif (substr($word, 0, 2) === '?+') {
if (self::test_condition(substr($word, 2), $item['obj'])) {
return false;
}
} elseif (substr($word, 0, 1) === '?') {
if (self::test_condition(substr($word, 1), $item)) {
return false;
}
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
return false;
} elseif (stristr($text, $word) !== false) {
return false;
}
}
}
}
$include = (($incl) ? explode("\n", $incl) : null);
protected function parse($string): array
{
$rules = [];
if (! strlen($string)) {
return $rules;
}
if ($include) {
foreach ($include as $word) {
$word = trim($word);
if (! $word) {
continue;
}
if (isset($lang) && ((strpos($word, 'lang=') === 0) || (strpos($word, 'lang!=') === 0))) {
if (!strlen($lang)) {
// Result is ambiguous. However we are checking allow rules
// and an ambiguous language is always permitted.
return true;
}
if (strpos($word, 'lang=') === 0 && strcasecmp($lang, trim(substr($word, 5))) == 0) {
return true;
} elseif (strpos($word, 'lang!=') === 0 && strcasecmp($lang, trim(substr($word, 6))) != 0) {
return true;
}
}
elseif (substr($word, 0, 1) === '#' && $tags) {
foreach ($tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return true;
}
}
} elseif (substr($word, 0, 1) === '$' && $tags) {
foreach ($tags as $t) {
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($word, 1)) || (substr($word, 1) === '*'))) {
return true;
}
}
} elseif (substr($word, 0, 2) === '?+') {
if (self::test_condition(substr($word, 2), $item['obj'])) {
return true;
}
} elseif (substr($word, 0, 1) === '?') {
if (self::test_condition(substr($word, 1), $item)) {
return true;
}
} elseif ((strpos($word, '/') === 0) && preg_match($word, $text)) {
return true;
} elseif (stristr($text, $word) !== false) {
return true;
}
}
} else {
return true;
}
$phrases = preg_split("/(\s\|\|\s|\s&&\s|\n)/", $string, flags: PREG_SPLIT_DELIM_CAPTURE);
if (!$phrases) {
return $rules;
}
for ($index = 0; $index < count($phrases); $index ++) {
// Even indices are rules and odd indices are operations, linefeed is an implict OR.
if (!($index & 1)) {
$currentRule = ['operation' => '', 'rule' => $phrases[$index]];
if ($index && isset($phrases[$index - 1])) {
$currentRule['operation'] = $phrases[$index - 1];
if ($currentRule['operation'] === "\n") {
$currentRule['operation'] = ' || ';
}
$index++;
}
$rules[] = $currentRule;
}
}
return $rules;
}
return false;
}
public function evaluate(): bool
{
$previousResult = $newResult = null;
// exclude always has priority
$exclude = $this->excludeRules;
$include = $this->includeRules;
if ($exclude) {
foreach ($exclude as $rule) {
if (!strlen(trim($rule['rule']))) {
continue;
}
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
continue;
}
$result = $this->evaluateRule($rule['rule']);
switch ($rule['operation']) {
case '':
$previousResult = $newResult = $result;
break;
case ' || ':
$newResult = $previousResult || $result;
break;
case ' && ':
$newResult = $previousResult && $result;
break;
}
}
if ($newResult) {
return false;
}
}
$previousResult = $newResult = null;
if ($include) {
foreach ($include as $rule) {
if (!strlen(trim($rule['rule']))) {
continue;
}
if (!strlen($this->language) && ((str_starts_with($rule['rule'], 'lang=')) || (str_starts_with($rule['rule'], 'lang!=')))) {
continue;
}
$result = $this->evaluateRule($rule['rule']);
switch ($rule['operation']) {
case '':
$previousResult = $newResult = $result;
break;
case ' || ':
$newResult = $previousResult || $result;
break;
case ' && ':
$newResult = $previousResult && $result;
break;
}
}
}
return $newResult ?? true;
}
protected function evaluateRule($ruleText): bool
{
$ruleText = trim($ruleText);
if (($this->language) && ((str_starts_with($ruleText, 'lang=')) || (str_starts_with($ruleText, 'lang!=')))) {
if (str_starts_with($ruleText, 'lang=') && strcasecmp($this->language, trim(substr($ruleText, 5))) == 0) {
$this->lastMatch = $ruleText;
return true;
} elseif (str_starts_with($ruleText, 'lang!=') && strcasecmp($this->language, trim(substr($ruleText, 6))) != 0) {
$this->lastMatch = $ruleText;
return true;
}
} elseif (str_starts_with($ruleText, 'until=')) {
$until = strtotime(trim(substr($ruleText, 6)));
if ($until > strtotime($this->item['created'] . ' UTC')) {
$this->lastMatch = $ruleText;
return true;
}
} elseif (str_starts_with($ruleText, '#') && $this->tags) {
// #hashtag match
foreach ($this->tags as $t) {
if ((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (!strcasecmp($t['term'], substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
$this->lastMatch = $ruleText;
return true;
}
}
// hashtag count match
if (substr($ruleText, 1, 1) === '>') {
$hashtagLimit = (int)substr($ruleText, 2);
$hashtagCount = 0;
foreach ($this->tags as $t) {
if ($t['ttype'] == TERM_HASHTAG || $t['ttype'] == TERM_COMMUNITYTAG) {
$hashtagCount++;
}
}
if ($hashtagLimit && $hashtagCount > $hashtagLimit) {
$this->lastMatch = $ruleText;
return true;
}
}
} elseif (str_starts_with($ruleText, '@') && $this->tags) {
// @mention match
foreach ($this->tags as $t) {
if ((($t['ttype'] == TERM_MENTION && (!strcasecmp($t['term'], substr($ruleText, 1)))) || (substr($ruleText, 1) === '*'))) {
$this->lastMatch = $ruleText;
return true;
}
}
// mention count match
if (substr($ruleText, 1, 1) === '>') {
$mentionLimit = (int)substr($ruleText, 2);
$mentionCount = 0;
foreach ($this->tags as $t) {
if ($t['ttype'] == TERM_MENTION) {
$mentionCount++;
}
}
if ($mentionLimit && $mentionCount > $mentionLimit) {
$this->lastMatch = $ruleText;
return true;
}
}
} elseif (str_starts_with($ruleText, '$') && $this->tags) {
foreach ($this->tags as $t) {
if (($t['ttype'] == TERM_CATEGORY) && (($t['term'] === substr($ruleText, 1)) || (substr($ruleText, 1) === '*'))) {
$this->lastMatch = $ruleText;
return true;
}
}
} elseif (str_starts_with($ruleText, '?+') && is_array($this->item['obj'])) {
if ($this->test_condition(substr($ruleText, 2), $this->item['obj'])) {
$this->lastMatch = $ruleText;
return true;
}
} elseif (str_starts_with($ruleText, '?')) {
$this->item['ua'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (empty($this->item['app'])) {
$author_app = $this->item['author']['site_project'] ?? '';
if (!$author_app && isset($this->item['author'])) {
if (str_contains($this->item['author']['xchan_hash'], 'threads.net') || str_contains($this->item['author']['xchan_hash'], 'threads.com')) {
$author_app = 'threads';
}
}
$this->item['app'] = $author_app;
}
if ($this->test_condition(substr($ruleText, 1), $this->item)) {
unset($this->item['ua']);
$this->lastMatch = $ruleText;
return true;
}
unset($this->item['ua']);
} elseif ((str_starts_with($ruleText, '/')) && preg_match($ruleText, $this->item['body'])) {
$this->lastMatch = $ruleText;
return true;
} elseif (stristr($this->item['body'], $ruleText) !== false) {
$this->lastMatch = $ruleText;
return true;
}
return false;
}
public function getLastMatch(): string
{
return $this->lastMatch;
}
public function setLastMatch($string): MessageFilter
{
$this->lastMatch = $string;
return $this;
}
/**
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from src/Render/Comanche
*
* This is extensible. The first version of variable testing supports tests of the forms:
*
* - ?foo ~= baz will check if item.foo contains the string 'baz';
* - ?foo == baz will check if item.foo is the string 'baz';
* - ?foo != baz will check if item.foo is not the string 'baz';
* - ?foo // baz will check if item.foo matches the regular expression 'baz';
* - ?foo >= 3 will check if item.foo is greater than or equal to 3;
* - ?foo > 3 will check if item.foo is greater than 3;
* - ?foo <= 3 will check if item.foo is less than or equal to 3;
* - ?foo < 3 will check if item.foo is less than 3;
* - ?foo & 2 will check if item.foo has the second bit set.
* - ?foo !& 2 will check if item.foo does not have the second bit set.
*
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
* - ?foo which will check for a return of a true condition for item.foo;
/**
* @brief Test for Conditional Execution conditions. Shamelessly ripped off from Code/Render/Comanche
*
* This is extensible. The first version of variable testing supports tests of the forms:
*
* - ?foo ~= baz which will check if item.foo contains the string 'baz';
* - ?foo == baz which will check if item.foo is the string 'baz';
* - ?foo != baz which will check if item.foo is not the string 'baz';
* - ?foo >= 3 which will check if item.foo is greater than or equal to 3;
* - ?foo > 3 which will check if item.foo is greater than 3;
* - ?foo <= 3 which will check if item.foo is less than or equal to 3;
* - ?foo < 3 which will check if item.foo is less than 3;
*
* - ?foo {} baz which will check if 'baz' is an array element in item.foo
* - ?foo {*} baz which will check if 'baz' is an array key in item.foo
* - ?foo which will check for a return of a true condition for item.foo;
* - ?!foo which will check for a return of a false condition for item.foo;
*
* The values 0, '', an empty array, and an unset value will all evaluate to false.
*
* @param string $s
* @param array $item
* @return bool
*/
protected function test_condition($s,$item)
{
$s = trim($s);
* The values 0, '', an empty array, and an unset value will all evaluate to false.
*
* @param string $s
* @param array $item
* @return bool
*/
if (preg_match('/(.*?)\s&\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x & (int) trim($matches[2])) {
return true;
}
return false;
}
public static function test_condition($s,$item) {
if (preg_match('/(.*?)\s!&\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (!($x & (int) trim($matches[2]))) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\~\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (stripos($x, trim($matches[2])) !== false) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s~=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (stripos($x, trim($matches[2])) !== false) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\=\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x == trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s==\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x == trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\!\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x != trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s!=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x != trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x >= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\/\/\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (substr(trim($matches[2]),0,1) !== substr(trim($matches[2]),-1)) {
$matches[2] = '/' . trim($matches[2]) . '/';
}
if (preg_match(trim($matches[2]), $x)) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\<\=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x <= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s>=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x >= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x > trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s<=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x <= trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\>\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x < trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s>\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x > trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/[\$](.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && in_array(trim($matches[2]), $x)) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s<\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x < trim($matches[2])) {
return true;
}
return false;
}
if (preg_match('/(.*?)\s\{\*\}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
return true;
}
return false;
}
// Array contains value
if (preg_match('/(.*?)\s\{\}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && in_array(trim($matches[2]), $x)) {
return true;
}
return false;
}
// Ordering of this check (for falsiness) with relation to the following one (check for truthiness) is important.
if (preg_match('/\!(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (!$x) {
return true;
}
return false;
}
// Array contains key
if (preg_match('/(.*?)\s\{\*}\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (is_array($x) && array_key_exists(trim($matches[2]), $x)) {
return true;
}
return false;
}
if (preg_match('/(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x) {
return true;
}
return false;
}
// Ordering of this check (for falseness) with relation to the following one (check for truthiness) is important.
if (preg_match('/!(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (!$x) {
return true;
}
return false;
}
return false;
}
if (preg_match('/(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if ($x) {
return true;
}
return false;
}
return false;
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Zotlabs\Lib;
use StephenHill\Base58;
class Multibase {
protected $key = null;
public function __construct() {
return $this;
}
public function publicKey($key) {
$base58 = new Base58();
$raw = hex2bin('ed01') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
return 'z' . $base58->encode($raw);
}
public function secretKey($key) {
$base58 = new Base58();
$raw = hex2bin('8026') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
return 'z' . $base58->encode($raw);
}
public function decode($key, $binary = false) {
$base58 = new Base58();
$key = substr($key,1);
$raw = $base58->decode($key);
$binaryKey = substr($raw, 2);
return $binary ? $binaryKey : sodium_bin2base64($binaryKey, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING);
}
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use DBA;
use PDO;
/**
* Concrete implementation for getting stats from MySQL and MariaDB databases.
*/
class MySQLDbStats extends DbStats {
public function getQueries(): int {
//
// We can't use the regular Hubzilla db helper function here, as
// it will only return information from a `SELECT` statement.
//
// Use the basic PDO access instead.
//
$query = DBA::$dba->db->prepare('SHOW STATUS LIKE "Queries"');
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
logger(print_r($result, true));
if (!empty($result)) {
return $result['Value'] ?? -1;
}
return 0;
}
}

View File

@@ -1,40 +0,0 @@
<?php
namespace Zotlabs\Lib;
class ObjCache
{
public static function Get($path, $type = 'as')
{
if (!$path) {
return [];
}
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
if (file_exists($localpath)) {
return json_unserialize(file_get_contents($localpath));
}
return [];
}
public static function Set($path, $content, $type = 'as') {
if (!$path) {
return;
}
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
file_put_contents($localpath, json_serialize($content));
}
public static function Delete($path, $type = 'as') {
if (!$path) {
return;
}
$localpath = Hashpath::path($path, 'store/[data]/[obj]/' . $type, 2, alg: 'sha256');
if (file_exists($localpath)) {
unlink($localpath);
}
}
}

View File

@@ -117,7 +117,7 @@ class PermissionDescription {
}
/**
* Returns an icon css class name if an appropriate one is available, e.g. "bi-globe" for Public,
* Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public,
* otherwise returns empty string.
*
* @return string icon css class name (often FontAwesome)
@@ -125,12 +125,12 @@ class PermissionDescription {
public function get_permission_icon() {
switch($this->channel_perm) {
case 0:/* only me */ return 'bi-eye-slash';
case PERMS_PUBLIC: return 'bi-globe';
case PERMS_NETWORK: return 'bi-share'; // bi-share is very similiar to the hubzilla logo, but we should create our own logo class to use
case PERMS_SITE: return 'bi-geo';
case PERMS_CONTACTS: return 'bi-people';
case PERMS_SPECIFIC: return 'bi-list-ul';
case 0:/* only me */ return 'fa-eye-slash';
case PERMS_PUBLIC: return 'fa-globe';
case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use
case PERMS_SITE: return 'fa-sitemap';
case PERMS_CONTACTS: return 'fa-group';
case PERMS_SPECIFIC: return 'fa-list';
case PERMS_AUTHED: return '';
case PERMS_PENDING: return '';
default: return '';

View File

@@ -1,32 +0,0 @@
<?php
/*
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
use DBA;
/**
* Concrete implementation for getting stats from PostgreSQL databases.
*/
class PostgresDbStats extends DbStats {
public function getQueries(): int {
$sqlGetQps = <<<'SQL'
select (xact_commit + xact_rollback) as queries
from pg_stat_database
where datname='%s'
SQL;
$result = q($sqlGetQps, DBA::$dba->dbname);
if (!empty($result)) {
return $result[0]['queries'] ?? -1;
}
return 0;
}
}

View File

@@ -7,20 +7,6 @@ use Zotlabs\Zot6\Zot6Handler;
class Queue {
/**
* Get number of entries in the out queue.
*
* When delivery is successful, the item is removed from the out queue, so
* the number of items in the queue reflects the number of pending delivery
* attempts.
*
* @return int Number of items in the out queue.
*/
static function count(): int {
$r = dbq('select count(*) as total from outq');
return $r[0]['total'] ?? 0;
}
static function update($id, $add_priority = 0) {
logger('queue: requeue item ' . $id,LOGGER_DEBUG);
@@ -71,6 +57,7 @@ class Queue {
outq_priority = outq_priority + %d,
outq_scheduled = '%s'
WHERE outq_hash = '%s'",
dbesc(datetime_convert()),
intval($add_priority),
dbesc($next),
@@ -78,39 +65,17 @@ class Queue {
);
}
public static function remove($id, $channel_id = 0) {
logger('queue: remove queue item ' . $id, LOGGER_DEBUG);
static function remove($id,$channel_id = 0) {
logger('queue: remove queue item ' . $id,LOGGER_DEBUG);
$sql_extra = (($channel_id) ? " and outq_channel = " . intval($channel_id) . " " : '');
// figure out what endpoint it is going to.
$record = q("select outq_posturl from outq where outq_hash = '%s' $sql_extra",
q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
dbesc($id)
);
if ($record) {
q("DELETE FROM outq WHERE outq_hash = '%s' $sql_extra",
dbesc($id)
);
// If there's anything remaining in the queue for this site, move one of them to the next active
// queue run by setting outq_scheduled back to the present. We may be attempting to deliver it
// as a 'piled_up' delivery, but this ensures the site has an active queue entry as long as queued
// entries still exist for it. This fixes an issue where one immediate delivery left everything
// else for that site undeliverable since all the other entries had been pushed far into the future.
$r = q("SELECT outq_hash, outq_posturl FROM outq WHERE outq_posturl = '%s' LIMIT 1",
dbesc($record[0]['outq_posturl'])
);
if ($r) {
$hashes = ids_to_querystr($r, 'outq_hash', true);
$x = q("UPDATE outq SET outq_scheduled = '%s' WHERE outq_hash IN ($hashes)",
dbesc(datetime_convert())
);
}
}
}
static function remove_by_posturl($posturl) {
logger('queue: remove queue posturl ' . $posturl,LOGGER_DEBUG);
@@ -119,6 +84,8 @@ class Queue {
);
}
static function set_delivered($id,$channel = 0) {
logger('queue: set delivered ' . $id,LOGGER_DEBUG);
$sql_extra = (($channel['channel_id']) ? " and outq_channel = " . intval($channel['channel_id']) . " " : '');
@@ -185,19 +152,17 @@ class Queue {
$y = q("select site_update, site_dead from site where site_url = '%s' ",
dbesc($base)
);
if ($y) {
// Don't bother delivering if the site is dead.
// And if we haven't heard from the site in over a month - let them through but 3 strikes you're out.
if (intval($y[0]['site_dead']) || ($y[0]['site_update'] < datetime_convert('UTC', 'UTC', 'now - 1 month') && $outq['outq_priority'] > 20)) {
q("update dreport set dreport_result = '%s' where dreport_queue = '%s'",
dbesc('site dead'),
dbesc($outq['outq_hash'])
);
if($y) {
if(intval($y[0]['site_dead'])) {
self::remove_by_posturl($outq['outq_posturl']);
logger('dead site ignored ' . $base);
return;
}
if($y[0]['site_update'] < datetime_convert('UTC','UTC','now - 1 month')) {
self::update($outq['outq_hash'], 10);
logger('immediate delivery deferred for site ' . $base);
return;
}
}
else {
@@ -260,7 +225,7 @@ class Queue {
if($result['success']) {
logger('deliver: remote zot delivery succeeded to ' . $outq['outq_posturl']);
Libzot::process_response($outq['outq_posturl'], $result, $outq);
Libzot::process_response($outq['outq_posturl'],$result, $outq);
}
else {
logger('deliver: remote zot delivery failed to ' . $outq['outq_posturl']);

View File

@@ -4,9 +4,6 @@ namespace Zotlabs\Lib;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Zotlabs\Lib\Config;
require_once 'include/dba/dba_transaction.php';
class QueueWorker {
@@ -27,10 +24,21 @@ class QueueWorker {
// Exceptions for processtimeout ($workermaxage) value.
// Currently the value is overriden with 3600 seconds (1h).
public static $long_running_cmd = [
'Queue',
'Expire'
'Queue'
];
private static function qstart() {
q('START TRANSACTION');
}
private static function qcommit() {
q("COMMIT");
}
private static function qrollback() {
q("ROLLBACK");
}
public static function Summon($argv) {
if ($argv[0] !== 'Queueworker') {
@@ -54,28 +62,26 @@ class QueueWorker {
return;
}
logger('queueworker_stats_summon: cmd:' . $argv[0] . ' ' . 'timestamp:' . time());
$transaction = new \DbaTransaction(\DBA::$dba);
self::qstart();
$r = q("INSERT INTO workerq (workerq_priority, workerq_data, workerq_uuid, workerq_cmd) VALUES (%d, '%s', '%s', '%s')",
intval($priority),
dbesc($workinfo_json),
$workinfo_json,
dbesc($uuid),
dbesc($argv[0])
);
if (!$r) {
// Transaction is autmatically rolled back on return
self::qrollback();
logger("INSERT FAILED", LOGGER_DEBUG);
return;
}
$transaction->commit();
self::qcommit();
logger('INSERTED: ' . $workinfo_json, LOGGER_DEBUG);
}
$workers = self::GetWorkerCount();
if ($workers < self::$maxworkers) {
logger($workers . '/' . self::$maxworkers . ' workers active', LOGGER_DEBUG);
$phpbin = Config::Get('system', 'phpbin', 'php');
$phpbin = get_config('system', 'phpbin', 'php');
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', ['Queueworker']);
}
}
@@ -102,19 +108,19 @@ class QueueWorker {
return;
}
$transaction = new \DbaTransaction(\DBA::$dba);
self::qstart();
$r = q("INSERT INTO workerq (workerq_priority, workerq_data, workerq_uuid, workerq_cmd) VALUES (%d, '%s', '%s', '%s')",
intval($priority),
dbesc($workinfo_json),
$workinfo_json,
dbesc($uuid),
dbesc($argv[0])
);
if (!$r) {
// Transaction is automatically rolled back on return
self::qrollback();
logger("Insert failed: " . $workinfo_json, LOGGER_DEBUG);
return;
}
$transaction->commit();
self::qcommit();
logger('INSERTED: ' . $workinfo_json, LOGGER_DEBUG);
}
@@ -123,41 +129,21 @@ class QueueWorker {
public static function GetWorkerCount() {
if (self::$maxworkers == 0) {
self::$maxworkers = Config::Get('queueworker', 'max_queueworkers', 4);
self::$maxworkers = get_config('queueworker', 'max_queueworkers', 4);
self::$maxworkers = self::$maxworkers > 3 ? self::$maxworkers : 4;
}
if (self::$workermaxage == 0) {
self::$workermaxage = Config::Get('queueworker', 'max_queueworker_age');
self::$workermaxage = get_config('queueworker', 'max_queueworker_age');
self::$workermaxage = self::$workermaxage > 120 ? self::$workermaxage : 300;
}
$transaction = new \DbaTransaction(\DBA::$dba);
// skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
$sql_quirks = ((Config::Get('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
$r = q("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NOT NULL AND workerq_processtimeout < %s FOR UPDATE $sql_quirks",
q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s",
db_utcnow()
);
if ($r) {
// TODO: some long running services store their pid in config.procid.daemon
// we could possibly check if a pid exist and check if the process is still alive
// prior to reseting workerq_reservationid
$ids = ids_to_querystr($r, 'workerq_id');
$u = dbq("update workerq set workerq_reservationid = null where workerq_id in ($ids)");
}
$transaction->commit();
//q("update workerq set workerq_reservationid = null where workerq_reservationid is not null and workerq_processtimeout < %s",
//db_utcnow()
//);
//usleep(self::$workersleep);
$workers = dbq("select count(*) as total from workerq where workerq_reservationid is not null");
$workers = dbq("select count(distinct workerq_reservationid) as total from workerq where workerq_reservationid is not null");
logger("WORKERCOUNT: " . $workers[0]['total'], LOGGER_DEBUG);
return intval($workers[0]['total']);
@@ -170,7 +156,7 @@ class QueueWorker {
$wid = uniqid('', true);
//usleep(mt_rand(300000, 1000000)); //Sleep .3 - 1 seconds before creating a new worker.
usleep(mt_rand(300000, 1000000)); //Sleep .3 - 1 seconds before creating a new worker.
$workers = self::GetWorkerCount();
@@ -187,15 +173,15 @@ class QueueWorker {
private static function getWorkId() {
self::GetWorkerCount();
$transaction = new \DbaTransaction(\DBA::$dba);
self::qstart();
// skip locked is preferred but is not supported by mariadb < 10.6 which is still used a lot - hence make it optional
$sql_quirks = ((Config::Get('system', 'db_skip_locked_supported')) ? 'SKIP LOCKED' : 'NOWAIT');
// This is probably the better solution but is not supported by mariadb < 10.6 which is still used a lot.
// $work = dbq("SELECT workerq_id FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE SKIP LOCKED;");
$work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE $sql_quirks");
$work = dbq("SELECT workerq_id, workerq_cmd FROM workerq WHERE workerq_reservationid IS NULL ORDER BY workerq_priority DESC, workerq_id ASC LIMIT 1 FOR UPDATE;");
if (!$work) {
// Transaction automatically rolled back on return
self::qcommit();
return false;
}
@@ -215,24 +201,24 @@ class QueueWorker {
);
if (!$work) {
// Transaction automatically rolled back on return
self::qrollback();
logger("Could not update workerq.", LOGGER_DEBUG);
return false;
}
logger("GOTWORK: " . json_encode($work), LOGGER_DEBUG);
$transaction->commit();
self::qcommit();
return $id;
}
public static function Process() {
$sleep = intval(Config::Get('queueworker', 'queue_worker_sleep', 100));
$auto_queue_worker_sleep = Config::Get('queueworker', 'auto_queue_worker_sleep', 0);
$sleep = intval(get_config('queueworker', 'queue_worker_sleep', 100));
$auto_queue_worker_sleep = get_config('queueworker', 'auto_queue_worker_sleep', 0);
if (!self::GetWorkerID()) {
if ($auto_queue_worker_sleep) {
Config::Set('queueworker', 'queue_worker_sleep', $sleep + 100);
set_config('queueworker', 'queue_worker_sleep', $sleep + 100);
}
logger('Unable to get worker ID. Exiting.', LOGGER_DEBUG);
@@ -241,7 +227,7 @@ class QueueWorker {
if ($auto_queue_worker_sleep && $sleep > 100) {
$next_sleep = $sleep - 100;
Config::Set('queueworker', 'queue_worker_sleep', (($next_sleep < 100) ? 100 : $next_sleep));
set_config('queueworker', 'queue_worker_sleep', (($next_sleep < 100) ? 100 : $next_sleep));
}
$jobs = 0;
@@ -250,7 +236,7 @@ class QueueWorker {
self::$workersleep = $sleep;
self::$workersleep = ((intval(self::$workersleep) > 100) ? intval(self::$workersleep) : 100);
if (function_exists('sys_getloadavg') && Config::Get('queueworker', 'load_average_sleep')) {
if (function_exists('sys_getloadavg') && get_config('queueworker', 'load_average_sleep')) {
// very experimental!
$load_average_sleep = true;
}
@@ -278,7 +264,7 @@ class QueueWorker {
if ($workers < self::$maxworkers) {
logger($workers . '/' . self::$maxworkers . ' workers active', LOGGER_DEBUG);
$phpbin = Config::Get('system', 'phpbin', 'php');
$phpbin = get_config('system', 'phpbin', 'php');
proc_run($phpbin, 'Zotlabs/Daemon/Master.php', ['Queueworker']);
}
@@ -292,16 +278,12 @@ class QueueWorker {
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$argv = flatten_array_recursive($argv);
$argc = count($argv);
$rnd = random_string(16);
$rnd = random_string();
logger('PROCESSING: ' . $rnd . ' ' . print_r($argv[0], true));
$start_timestamp = microtime(true);
$cls::run($argc, $argv);
logger('logger_stats_data cmd:' . $argv[0] . ' start:' . $start_timestamp . ' ' . 'end:' . microtime(true) . ' meta:' . $rnd);
logger('COMPLETED: ' . $rnd);
// @FIXME: Right now we assume that if we get a return, everything is OK.

View File

@@ -1,28 +0,0 @@
<?php
/*
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Lib;
class QueueWorkerStats
{
public readonly int $size;
public readonly int $active;
public function __construct() {
$query = <<<'SQL'
select count(*) as total from workerq
union (select count(*) as qworkers from workerq where workerq_reservationid is not null)
SQL;
$result = dbq('select count(*) as total from workerq');
$this->size = !empty($result) ? $result[0]['total'] : -1;
$result = dbq('select count(*) as qworkers from workerq where workerq_reservationid is not null');
$this->active = !empty($result) ? $result[0]['qworkers'] : -1;
}
}

View File

@@ -112,34 +112,32 @@ class Share {
if(! $this->item)
return $bb;
$is_photo = ((in_array($this->item['obj_type'], ['Image', ACTIVITY_OBJ_PHOTO])) ? true : false);
$is_photo = (($this->item['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false);
if($is_photo) {
$object = json_decode($this->item['obj'],true);
$photo_bb = $object['body'];
}
if (!str_contains($this->item['body'], '[/share]')) {
$quote = in_array($this->item['author']['xchan_network'], ['zot6', 'activitypub']) ? "quote='true'" : '';
$bb .= "[share author='" . urlencode($this->item['author']['xchan_name']) . "'
profile='" . $this->item['author']['xchan_url'] . "'
avatar='" . $this->item['author']['xchan_photo_s'] . "'
link='" . $this->item['plink'] . "'
auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') . "'
posted='" . $this->item['created'] . "'
message_id='" . $this->item['mid'] . "'
$quote
]";
if ($this->item['title']) {
if (strpos($this->item['body'], "[/share]") !== false) {
$pos = strpos($this->item['body'], "[share");
$bb = substr($this->item['body'], $pos);
} else {
$bb = "[share author='".urlencode($this->item['author']['xchan_name']).
"' profile='" . $this->item['author']['xchan_url'] .
"' avatar='" . $this->item['author']['xchan_photo_s'] .
"' link='" . $this->item['plink'] .
"' auth='" . (($this->item['author']['xchan_network'] === 'zot6') ? 'true' : 'false') .
"' posted='" . $this->item['created'] .
"' message_id='" . $this->item['mid'] .
"']";
if($this->item['title'])
$bb .= '[h3][b]'.$this->item['title'].'[/b][/h3]'."\r\n";
}
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
$bb .= "[/share]";
}
return $bb;
}
}

127
Zotlabs/Lib/SuperCurl.php Normal file
View File

@@ -0,0 +1,127 @@
<?php
namespace Zotlabs\Lib;
/**
* @brief wrapper for z_fetch_url() which can be instantiated with several built-in parameters and
* these can be modified and re-used. Useful for CalDAV and other processes which need to authenticate
* and set lots of CURL options (many of which stay the same from one call to the next).
*/
class SuperCurl {
private $auth;
private $url;
private $curlopt = array();
private $headers = null;
public $filepos = 0;
public $filehandle = 0;
public $request_data = '';
private $request_method = 'GET';
private $upload = false;
private $cookies = false;
private function set_data($s) {
$this->request_data = $s;
$this->filepos = 0;
}
public function curl_read($ch,$fh,$size) {
if($this->filepos < 0) {
unset($fh);
return '';
}
$s = substr($this->request_data,$this->filepos,$size);
if(strlen($s) < $size)
$this->filepos = (-1);
else
$this->filepos = $this->filepos + $size;
return $s;
}
public function __construct($opts = array()) {
$this->set($opts);
}
private function set($opts = array()) {
if($opts) {
foreach($opts as $k => $v) {
switch($k) {
case 'http_auth':
$this->auth = $v;
break;
case 'magicauth':
// currently experimental
$this->magicauth = $v;
\Zotlabs\Daemon\Master::Summon([ 'CurlAuth', $v ]);
break;
case 'custom':
$this->request_method = $v;
break;
case 'url':
$this->url = $v;
break;
case 'data':
$this->set_data($v);
if($v) {
$this->upload = true;
}
else {
$this->upload = false;
}
break;
case 'headers':
$this->headers = $v;
break;
default:
$this->curlopts[$k] = $v;
break;
}
}
}
}
function exec() {
$opts = $this->curlopts;
$url = $this->url;
if($this->auth)
$opts['http_auth'] = $this->auth;
if($this->magicauth) {
$opts['cookiejar'] = 'store/[data]/cookie_' . $this->magicauth;
$opts['cookiefile'] = 'store/[data]/cookie_' . $this->magicauth;
$opts['cookie'] = 'PHPSESSID=' . trim(file_get_contents('store/[data]/cookien_' . $this->magicauth));
$c = channelx_by_n($this->magicauth);
if($c)
$url = zid($this->url,channel_reddress($c));
}
if($this->custom)
$opts['custom'] = $this->custom;
if($this->headers)
$opts['headers'] = $this->headers;
if($this->upload) {
$opts['upload'] = true;
$opts['infile'] = $this->filehandle;
$opts['infilesize'] = strlen($this->request_data);
$opts['readfunc'] = [ $this, 'curl_read' ] ;
}
$recurse = 0;
return z_fetch_url($this->url,true,$recurse,(($opts) ? $opts : null));
}
}

View File

@@ -1,33 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Text {
/**
* use this on "body" or "content" input where angle chars shouldn't be removed,
* and allow them to be safely displayed.
*
* @param string $string
*
* @return string
*/
public static function escape_tags(string $string): string {
if (!$string) {
return EMPTY_STR;
}
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
}
public static function rawurlencode_parts(string $string): string {
if (!$string) {
return EMPTY_STR;
}
return implode('/', array_map('rawurlencode', explode('/', $string)));
}
}

View File

@@ -3,7 +3,7 @@
namespace Zotlabs\Lib;
use App;
use DBA;
use Zotlabs\Lib\Apps;
use Zotlabs\Access\AccessList;
require_once('include/text.php');
@@ -19,13 +19,12 @@ class ThreadItem {
private $comment_box_template = 'comment_item.tpl';
private $commentable = false;
// list of supported reaction emojis - a site can over-ride this via config system.reactions
private $reactions = ['slightly_smiling_face','clapping_hands','bottle_with_popping_cork','kiss_mark','disappointed_face','red_heart','grinning_face','astonished_face','sleeping_face','winking_face_with_tongue','smiling_face_with_halo','smiling_face_with_horns'];
private $reactions = ['1f60a','1f44f','1f37e','1f48b','1f61e','2665','1f606','1f62e','1f634','1f61c','1f607','1f608'];
private $toplevel = false;
private $children = array();
private $parent = null;
private $conversation = null;
private $redirect_url = null;
private $owner_addr = '';
private $owner_url = '';
private $owner_photo = '';
private $owner_name = '';
@@ -36,15 +35,17 @@ class ThreadItem {
private $display_mode = 'normal';
private $reload = '';
public function __construct($data) {
$this->data = $data;
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
$this->threaded = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
$this->threaded = get_config('system','thread_allow');
$observer = \App::get_observer();
// Prepare the children
if(isset($data['children'])) {
foreach($data['children'] as $item) {
/*
@@ -55,6 +56,7 @@ class ThreadItem {
continue;
}
$child = new ThreadItem($item);
$this->add_child($child);
}
@@ -65,7 +67,7 @@ class ThreadItem {
// allow a site to configure the order and content of the reaction emoji list
if($this->toplevel) {
$x = Config::Get('system','reactions');
$x = get_config('system','reactions');
if($x && is_array($x) && count($x)) {
$this->reactions = $x;
}
@@ -80,7 +82,7 @@ class ThreadItem {
* _ false on failure
*/
public function get_template_data($thread_level=1, $conv_flags = []) {
public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
$result = [];
$item = $this->get_data();
@@ -89,7 +91,7 @@ class ThreadItem {
$buttons = '';
$dropping = false;
$star = false;
$isstarred = "unstarred bi-star";
$isstarred = "unstarred fa-star-o";
$is_comment = false;
$is_item = false;
$osparkle = '';
@@ -99,9 +101,7 @@ class ThreadItem {
$conv = $this->get_conversation();
$observer = $conv->get_observer();
$conv->mid_uuid_map[$item['mid']] = $item['uuid'];
$acl = new AccessList([]);
$acl = new AccessList(false);
$acl->set($item);
$lock = ((intval($item['item_private']) || ($item['uid'] == local_channel() && $acl->is_private()))
@@ -112,7 +112,7 @@ class ThreadItem {
$locktype = intval($item['item_private']);
if ($locktype === 2) {
$lock = t('Private message');
$lock = t('Direct message');
}
// 0 = limited based on public policy
@@ -121,14 +121,12 @@ class ThreadItem {
$locktype = 0;
}
$shareable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && (intval($item['item_private']) === 0) && !str_contains($item['body'], '[/share]'));
$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
if ($item['author']['xchan_network'] === 'rss')
if($item['author']['xchan_network'] === 'rss')
$shareable = true;
$repeatable = ((local_channel() && $conv->get_profile_owner() == local_channel()) && intval($item['item_private']) === 0 && in_array($item['author']['xchan_network'], ['zot6', 'activitypub']));
// @fixme
// Have recently added code to properly handle polls in group reshares by redirecting all of the poll responses to the group.
// Sharing a poll using a regular embedded share is harder because the poll will need to fork. This is due to comment permissions.
@@ -185,9 +183,9 @@ class ThreadItem {
$drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ];
}
$filer = (((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) ? t("Save to Folder") : false);
$filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
$profile_avatar = $item['author']['xchan_photo_s'];
$profile_avatar = $item['author']['xchan_photo_m'];
$profile_link = chanlink_hash($item['author_xchan']);
$profile_name = $item['author']['xchan_name'];
@@ -196,32 +194,60 @@ class ThreadItem {
$attend = null;
// process action responses - e.g. like/dislike/attend/agree/whatever
$response_verbs[] = 'like';
if(feature_enabled($conv->get_profile_owner(),'dislike')) {
$response_verbs = array('like');
if(feature_enabled($conv->get_profile_owner(),'dislike'))
$response_verbs[] = 'dislike';
}
if ($repeatable) {
$response_verbs[] = 'announce';
}
if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
$response_verbs[] = 'accept';
$response_verbs[] = 'reject';
$response_verbs[] = 'tentativeaccept';
if($item['obj_type'] === ACTIVITY_OBJ_EVENT) {
$response_verbs[] = 'attendyes';
$response_verbs[] = 'attendno';
$response_verbs[] = 'attendmaybe';
if($this->is_commentable() && $observer) {
$isevent = true;
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
}
}
if ($item['obj_type'] === 'Question') {
if($item['obj_type'] === 'Question') {
$response_verbs[] = 'answer';
}
$response_verbs[] = 'comment';
$responses = get_responses($response_verbs, $item);
if(! feature_enabled($conv->get_profile_owner(),'dislike'))
unset($conv_responses['dislike']);
$responses = get_responses($conv_responses,$response_verbs,$this,$item);
$my_responses = [];
foreach($response_verbs as $v) {
$my_responses[$v] = ((isset($conv_responses[$v][$item['mid'] . '-m'])) ? 1 : 0);
}
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
if (($like_list) && (count($like_list) > MAX_LIKERS)) {
$like_list_part = array_slice($like_list, 0, MAX_LIKERS);
array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else {
$like_list_part = '';
}
$like_button_label = tt('Like','Likes',$like_count,'noun');
$showdislike = '';
if (feature_enabled($conv->get_profile_owner(),'dislike')) {
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun');
if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) {
$dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else {
$dislike_list_part = '';
}
$showdislike = ((x($conv_responses['dislike'],$item['mid'])) ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : '');
}
$showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : '');
/*
* We should avoid doing this all the time, but it depends on the conversation mode
@@ -231,13 +257,7 @@ class ThreadItem {
$this->check_wall_to_wall();
$children = $this->get_children();
$children_count = count($children);
if($this->is_toplevel()) {
$conv->comments_total = $responses['comment']['count'] ?? 0;
$conv->comments_loaded = $children_count;
if((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) {
$star = [
'toggle' => t("Toggle Star Status"),
@@ -249,6 +269,7 @@ class ThreadItem {
$is_comment = true;
}
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
@@ -278,40 +299,41 @@ class ThreadItem {
}
$has_event = false;
if((in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) && $conv->get_profile_owner() == local_channel())
if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel())
$has_event = true;
$like = [];
$dislike = [];
$reply_to = [];
$reactions_allowed = false;
if($this->is_commentable()) {
$reply_to = array( t("Reply to this message"), t("reply"), t("Reply to"));
if ($observer) {
$reactions_allowed = true;
}
if($this->is_commentable() && $observer) {
$like = array( t("I like this \x28toggle\x29"), t("like"));
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
$reply_to = array( t("Reply on this comment"), t("reply"), t("Reply to"));
}
$share = [];
$embed = [];
if ($shareable) {
$embed = [t('Share'), t('share')];
}
if ($repeatable) {
$share = [t('Repeat'), t('repeat')];
// 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) {
//Not yet ready for primetime
//$share = array( t('Repeat This'), t('repeat'));
}
$embed = [t('Share This'), t('share')];
}
$dreport = '';
$keep_reports = intval(Config::Get('system','expire_delivery_reports'));
$keep_reports = intval(get_config('system','expire_delivery_reports'));
if($keep_reports === 0)
$keep_reports = 10;
$dreport_link = '';
if((intval($item['item_type']) == ITEM_TYPE_POST) && (! Config::Get('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) {
if((intval($item['item_type']) == ITEM_TYPE_POST) && (! 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_link = '?mid=' . $item['mid'];
$dreport_link = gen_link_id($item['mid']);
}
$is_new = false;
@@ -320,8 +342,7 @@ class ThreadItem {
localize_item($item);
$opts = (($item['resource_type'] === 'event') ? ['is_event_item' => true] : []);
$body = prepare_body($item, true, $opts);
$body = prepare_body($item,true);
// $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link
// since we can't depend on llink or plink pointing to the right local location.
@@ -331,9 +352,10 @@ class ThreadItem {
if($conv->get_mode() === 'channel')
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
$comment_count_txt = ['label' => sprintf(tt('%d comment', '%d comments', $total_children), $total_children), 'count' => $total_children];
$comment_count_txt = sprintf(tt('%d Comment', '%d Comments', $total_children), $total_children);
$list_unseen_txt = (($unseen_comments) ? sprintf(t('%d unseen'), $unseen_comments) : '');
$list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$children = $this->get_children();
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
@@ -341,13 +363,20 @@ class ThreadItem {
call_hooks('dropdown_extras',$dropdown_extras_arr);
$dropdown_extras = $dropdown_extras_arr['dropdown_extras'];
$midb64 = $item['uuid'];
$mids = [ $item['uuid'] ];
$midb64 = gen_link_id($item['mid']);
$mids = [ $midb64 ];
$response_mids = [];
foreach($response_verbs as $v) {
if(isset($conv_responses[$v]['mids'][$item['mid']])) {
$response_mids = array_merge($response_mids, $conv_responses[$v]['mids'][$item['mid']]);
}
}
$mids = array_merge($mids, $response_mids);
$json_mids = json_encode($mids);
// Pinned item processing
$allowed_type = (in_array($item['item_type'], Config::Get('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false);
$allowed_type = (in_array($item['item_type'], get_config('system', 'pin_types', [ ITEM_TYPE_POST ])) ? true : false);
$pinned_items = ($allowed_type ? get_pconfig($item['uid'], 'pinned', $item['item_type'], []) : []);
$pinned = ((!empty($pinned_items) && in_array($midb64, $pinned_items)) ? true : false);
@@ -357,26 +386,11 @@ class ThreadItem {
$contact = App::$contacts[$item['author_xchan']];
}
$blog_mode = $this->get_display_mode() === 'list';
$load_more = false;
$load_more_title = '';
$comments_total_percent = 0;
if (($conv->comments_total > $conv->comments_loaded) || ($blog_mode && $conv->comments_total > 3)) {
// provide a load more comments button
$load_more = true;
$load_more_title = sprintf(t('Load the next few of total %d comments'), $conv->comments_total);
$comments_total_percent = round(100 * 3 / $conv->comments_total);
}
$expand = '';
if ($this->threaded && !empty($item['comment_count'] && !$this->is_toplevel())) {
$expand = t('Expand Replies');
}
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
'item_type' => intval($item['item_type']),
//'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
'body' => $body['html'],
'tags' => $body['tags'],
'categories' => $body['categories'],
@@ -385,9 +399,9 @@ class ThreadItem {
'folders' => $body['folders'],
'text' => strip_tags($body['html']),
'id' => $this->get_id(),
'parent' => $item['parent'],
'mid' => $midb64,
'mids' => $json_mids,
'parent' => $item['parent'],
'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
'author_is_group_actor' => (($item['author']['xchan_pubforum']) ? t('Forum') : ''),
'isevent' => $isevent,
@@ -411,15 +425,16 @@ class ThreadItem {
'sparkle' => $sparkle,
'title' => $item['title'],
'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'),
//'ago' => relative_date($item['created']),
'app' => $item['app'],
'str_app' => sprintf( t('from %s'), $item['app']),
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'),
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created']),
'editedtime' => (($item['edited'] != $item['created']) ? sprintf(t('Last edited %s'), relative_time($item['edited'])) : ''),
'expiretime' => (($item['expires'] > DBA::$dba->get_null_date()) ? sprintf(t('Expires %s'), relative_time($item['expires'])) : ''),
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
'lock' => $lock,
'locktype' => $locktype,
'delayed' => (($item['item_delayed']) ? sprintf(t('Published %s'), relative_time($item['created'])) : ''),
'delayed' => $item['item_delayed'],
'privacy_warning' => $privacy_warning,
'verified' => $verified,
'unverified' => $unverified,
@@ -432,7 +447,6 @@ class ThreadItem {
'vote_title' => t('Voting Options'),
'is_comment' => $is_comment,
'is_new' => $is_new,
'owner_addr' => $this->get_owner_addr(),
'owner_url' => $this->get_owner_url(),
'owner_photo' => $this->get_owner_photo(),
'owner_name' => $this->get_owner_name(),
@@ -440,16 +454,17 @@ class ThreadItem {
'event' => $body['event'],
'has_tags' => $has_tags,
'reactions' => $this->reactions,
// Item toolbar buttons
// Item toolbar buttons
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
'reply_to' => ((feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'reply_to' => (((! $this->is_toplevel()) && feature_enabled($conv->get_profile_owner(),'reply_to')) ? $reply_to : ''),
'top_hint' => t("Go to previous comment"),
'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'],
'parent_mid' => $item['parent_mid'],
'plink' => get_plink($item),
'edpost' => $edpost,
'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
'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 : ''),
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
@@ -460,50 +475,36 @@ class ThreadItem {
'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
'drop' => $drop,
'dropdown_extras' => $dropdown_extras,
// end toolbar buttons
// end toolbar buttons
'unseen_comments' => $unseen_comments,
'comment_count' => $total_children,
'comment_count_txt' => $comment_count_txt,
'list_unseen_txt' => $list_unseen_txt,
'markseen' => t('Mark all comments seen'),
'markseen' => t('Mark all seen'),
'responses' => $responses,
// 'my_responses' => $my_responses,
'my_responses' => $my_responses,
'like_count' => $like_count,
'like_list' => $like_list,
'like_list_part' => $like_list_part,
'like_button_label' => $like_button_label,
'like_modal_title' => t('Likes','noun'),
'dislike_modal_title' => t('Dislikes','noun'),
'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''),
'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''),
'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''),
'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''),
'modal_dismiss' => t('Close'),
'showlike' => $showlike,
'showdislike' => $showdislike,
'comment' => ($item['item_delayed'] ? '' : $this->get_comment_box()),
'comment_hidden' => feature_enabled($conv->get_profile_owner(),'reply_to'),
'no_comment' => (($item['item_thread_top'] && $item['item_nocomment'])? t('Comments disabled') : ''),
'previewing' => ($conv->is_preview() ? true : false ),
'preview_lbl' => t('This is an unsaved preview'),
'wait' => t('Please wait'),
'thread_level' => $thread_level,
'settings' => $settings,
'thr_parent_uuid' => (($item['parent_mid'] !== $item['thr_parent'] && isset($conv->mid_uuid_map[$item['thr_parent']])) ? $conv->mid_uuid_map[$item['thr_parent']] : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : ''),
'moderate' => ($item['item_blocked'] == ITEM_MODERATED),
'moderate_approve' => t('Approve'),
'moderate_delete' => t('Delete'),
'rtl' => in_array($item['lang'], rtl_languages()),
'reactions_allowed' => $reactions_allowed,
'reaction_str' => [t('Add yours'), t('Remove yours')],
'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection'),
'observer_activity' => [
'like' => intval($item['observer_like_count'] ?? 0),
'dislike' => intval($item['observer_dislike_count'] ?? 0),
'announce' => intval($item['observer_announce_count'] ?? 0),
'comment' => intval($item['observer_comment_count'] ?? 0),
'accept' => intval($item['observer_accept_count'] ?? 0),
'reject' => intval($item['observer_reject_count'] ?? 0),
'tentativeaccept' => intval($item['observer_tentativeaccept_count'] ?? 0)
],
'threaded' => $this->threaded,
'blog_mode' => $blog_mode,
'collapse_comments' => t('show less'),
'expand_comments' => $this->threaded ? t('show more') : t('show all'),
'load_more' => $load_more,
'load_more_title' => $load_more_title,
'comments_total' => $conv->comments_total,
'comments_total_percent' => $comments_total_percent,
'expand' => $expand
'thr_parent' => (($item['parent_mid'] != $item['thr_parent']) ? gen_link_id($item['thr_parent']) : ''),
'contact_id' => (($contact) ? $contact['abook_id'] : '')
);
$arr = array('item' => $item, 'output' => $tmp_item);
@@ -512,19 +513,33 @@ class ThreadItem {
$result = $arr['output'];
$result['children'] = array();
$nb_children = count($children);
$visible_comments = 3; // Config::Get('system', 'expanded_comments', 3);
$visible_comments = get_config('system','expanded_comments');
if($visible_comments === false)
$visible_comments = 3;
if(($this->get_display_mode() === 'normal') && ($children_count > 0)) {
// needed for scroll to comment from notification but needs more work
// as we do not want to open all comments unless there is actually an #item_xx anchor
// and the url fragment is not sent to the server.
// if(in_array(\App::$module,['display','update_display']))
// $visible_comments = 99999;
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) {
$result['children'][] = $child->get_template_data($thread_level + 1, $conv_flags);
$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
}
// Collapse
if($thread_level === 1 && $children_count > $visible_comments) {
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
$result['children'][0]['comment_firstcollapsed'] = true;
$result['children'][0]['num_comments'] = $comment_count_txt['label'];
$result['children'][$children_count - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
$result['children'][0]['num_comments'] = $comment_count_txt;
$result['children'][0]['hide_text'] = sprintf( t('%s show all'), '<i class="fa fa-chevron-down"></i>');
if($thread_level > 1) {
$result['children'][$nb_children - 1]['comment_lastcollapsed'] = true;
}
else {
$result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
}
}
}
@@ -594,7 +609,7 @@ class ThreadItem {
* Only add what will be displayed
*/
if(activity_match($item->get_data_value('verb'), ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) {
return false;
}
@@ -777,7 +792,7 @@ class ThreadItem {
*/
private function get_comment_box() {
if(!$this->is_toplevel()) {
if(!$this->is_toplevel() && !get_config('system','thread_allow')) {
return '';
}
@@ -813,20 +828,19 @@ class ThreadItem {
'$submit' => t('Submit'),
'$edbold' => t('Bold'),
'$editalic' => t('Italic'),
'$edhighlighter' => t('Highlight selected text'),
'$eduline' => t('Underline'),
'$edquote' => t('Quote'),
'$edcode' => t('Code'),
'$edimg' => t('Embed (existing) photo from your photo albums'),
'$edimg' => t('Image'),
'$edatt' => t('Attach/Upload file'),
'$edurl' => t('Insert Link'),
'$edvideo' => t('Video'),
'$preview' => t('Preview'),
'$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
'$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()),
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
'$encrypt' => t('Encrypt text'),
'$cipher' => $conv->get_cipher(),
'$sourceapp' => App::$sourcename,
'$sourceapp' => \App::$sourcename,
'$observer' => get_observer_hash(),
'$anoncomments' => ((in_array($conv->get_mode(), ['channel', 'display', 'cards', 'articles']) && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
'$anonname' => [ 'anonname', t('Your full name (required)') ],
@@ -842,13 +856,12 @@ class ThreadItem {
}
/**
* Check if we are a wall to wall or announce item and set the relevant properties
* Check if we are a wall to wall item and set the relevant properties
*/
protected function check_wall_to_wall() {
$conv = $this->get_conversation();
$this->wall_to_wall = false;
$this->owner_url = '';
$this->owner_addr = '';
$this->owner_photo = '';
$this->owner_name = '';
@@ -857,18 +870,10 @@ class ThreadItem {
if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
$this->owner_addr = $this->data['owner']['xchan_addr'];
$this->owner_photo = $this->data['owner']['xchan_photo_s'];
$this->owner_photo = $this->data['owner']['xchan_photo_m'];
$this->owner_name = $this->data['owner']['xchan_name'];
$this->wall_to_wall = true;
}
elseif($this->is_toplevel() && $this->get_data_value('verb') === 'Announce' && isset($this->data['source'])) {
$this->owner_url = chanlink_hash($this->data['source']['xchan_hash']);
$this->owner_addr = $this->data['source']['xchan_addr'];
$this->owner_photo = $this->data['source']['xchan_photo_s'];
$this->owner_name = $this->data['source']['xchan_name'];
$this->wall_to_wall = true;
}
}
private function is_wall_to_wall() {
@@ -879,10 +884,6 @@ class ThreadItem {
return $this->owner_url;
}
private function get_owner_addr() {
return $this->owner_addr;
}
private function get_owner_photo() {
return $this->owner_photo;
}

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