Browse Source

Merge branch 'master' of https://framagit.org/hubzilla/core into disroot_master

disroot_master
Muppeth 6 months ago
parent
commit
85f80362ce
100 changed files with 10000 additions and 21606 deletions
  1. +50
    -60
      .homeinstall/README.md
  2. +52
    -18
      .homeinstall/hubzilla-setup.sh
  3. +69
    -0
      CHANGELOG
  4. +8
    -9
      Zotlabs/Daemon/Cron.php
  5. +5
    -0
      Zotlabs/Daemon/Cron_daily.php
  6. +3
    -3
      Zotlabs/Daemon/CurlAuth.php
  7. +1
    -1
      Zotlabs/Daemon/Master.php
  8. +15
    -2
      Zotlabs/Daemon/Notifier.php
  9. +7
    -5
      Zotlabs/Daemon/Onepoll.php
  10. +1
    -1
      Zotlabs/Daemon/Poller.php
  11. +169
    -34
      Zotlabs/Lib/Activity.php
  12. +4
    -9
      Zotlabs/Lib/Cache.php
  13. +1
    -1
      Zotlabs/Lib/LDSignatures.php
  14. +52
    -1
      Zotlabs/Lib/Libzot.php
  15. +1
    -1
      Zotlabs/Lib/Queue.php
  16. +150
    -0
      Zotlabs/Lib/SvgSanitizer.php
  17. +1
    -4
      Zotlabs/Lib/ThreadItem.php
  18. +16
    -15
      Zotlabs/Module/Admin/Addons.php
  19. +11
    -3
      Zotlabs/Module/Admin/Security.php
  20. +0
    -3
      Zotlabs/Module/Admin/Site.php
  21. +2
    -3
      Zotlabs/Module/Article_edit.php
  22. +8
    -4
      Zotlabs/Module/Articles.php
  23. +0
    -2
      Zotlabs/Module/Cdav.php
  24. +17
    -15
      Zotlabs/Module/Channel.php
  25. +0
    -7
      Zotlabs/Module/Cloud.php
  26. +4
    -1
      Zotlabs/Module/Connections.php
  27. +2
    -0
      Zotlabs/Module/Dav.php
  28. +2
    -2
      Zotlabs/Module/Directory.php
  29. +1
    -12
      Zotlabs/Module/Item.php
  30. +3
    -2
      Zotlabs/Module/Menu.php
  31. +11
    -14
      Zotlabs/Module/Photo.php
  32. +1
    -3
      Zotlabs/Module/Photos.php
  33. +18
    -3
      Zotlabs/Module/Wall_attach.php
  34. +12
    -0
      Zotlabs/Module/Well_known.php
  35. +1
    -1
      Zotlabs/Module/Zotfeed.php
  36. +6
    -3
      Zotlabs/Storage/Directory.php
  37. +1
    -1
      Zotlabs/Web/Router.php
  38. +6
    -1
      Zotlabs/Web/SessionHandler.php
  39. +3
    -2
      boot.php
  40. +4
    -4
      composer.json
  41. +109
    -109
      composer.lock
  42. +9
    -0
      doc/database.bb
  43. +26
    -8
      doc/hidden_configs.bb
  44. +1
    -0
      doc/hook/activity_decode_mapper.bb
  45. +1
    -0
      doc/hook/activity_mapper.bb
  46. +1
    -0
      doc/hook/activity_obj_decode_mapper.bb
  47. +1
    -0
      doc/hook/activity_obj_mapper.bb
  48. +11
    -0
      doc/hook/comments_are_now_closed.bb
  49. +1
    -0
      doc/hook/encode_object.bb
  50. +1
    -0
      doc/hook/fetch_and_store.bb
  51. +21
    -0
      doc/hooklist.bb
  52. +2
    -1
      doc/toc.html
  53. +24
    -2
      include/bbcode.php
  54. +5
    -5
      include/channel.php
  55. +5
    -0
      include/connections.php
  56. +6
    -0
      include/event.php
  57. +16
    -0
      include/feedutils.php
  58. +1
    -1
      include/follow.php
  59. +20
    -3
      include/import.php
  60. +23
    -17
      include/items.php
  61. +6
    -0
      include/markdown.php
  62. +1
    -1
      include/nav.php
  63. +1
    -1
      include/oembed.php
  64. +72
    -0
      include/opengraph.php
  65. +1
    -1
      include/queue_fn.php
  66. +32
    -5
      include/text.php
  67. +3
    -7
      include/zot.php
  68. +2
    -3
      install/schema_mysql.sql
  69. +0
    -1
      install/schema_postgres.sql
  70. +0
    -2
      library/jquery.i18n/.gitignore
  71. +0
    -19
      library/jquery.i18n/LICENSE
  72. +0
    -152
      library/jquery.i18n/README.markdown
  73. +0
    -1
      library/jquery.i18n/VERSION
  74. +0
    -55
      library/jquery.i18n/build.xml
  75. +0
    -78
      library/jquery.i18n/examples/index.html
  76. +0
    -6240
      library/jquery.i18n/examples/jquery-1.4.2.js
  77. +0
    -154
      library/jquery.i18n/jquery.i18n.js
  78. +0
    -13
      library/jquery.i18n/jquery.i18n.min.js
  79. +0
    -154
      library/jquery.i18n/src/jquery.i18n.js
  80. +0
    -4
      library/jquery_ac/README
  81. +0
    -400
      library/jquery_ac/friendica.complete.js
  82. +0
    -19
      library/jquery_ac/jquery-1.3.2.min.js
  83. +0
    -11
      library/jquery_ac/jquery.autocomplete-min.js
  84. +0
    -390
      library/jquery_ac/jquery.autocomplete.js
  85. BIN
      library/jquery_ac/shadow.png
  86. +0
    -6
      library/jquery_ac/styles.css
  87. +0
    -535
      library/moment/CHANGELOG.md
  88. +0
    -22
      library/moment/LICENSE
  89. +0
    -58
      library/moment/README.md
  90. +0
    -4040
      library/moment/moment.js
  91. +0
    -7
      library/moment/moment.min.js
  92. +3
    -3
      tests/unit/includes/LanguageTest.php
  93. +8666
    -8624
      util/hmessages.po
  94. +1
    -1
      util/po2php.php
  95. +1
    -1
      util/thumbrepair
  96. +7
    -0
      util/zotsh/README.txt
  97. BIN
      util/zotsh/easywebdav/__init__.pyc
  98. BIN
      util/zotsh/easywebdav/__version__.pyc
  99. +202
    -202
      util/zotsh/easywebdav/client.py
  100. BIN
      util/zotsh/easywebdav/client.pyc

+ 50
- 60
.homeinstall/README.md View File

@@ -1,50 +1,10 @@
# Hubzilla at Home next to your Router

This readme will show you how to install and run Hubzilla (or Zap) at home.

The installation is done by a script.

What the script will do for you...

+ install everything required by Hubzilla, 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
- backup to external disk (certificates, database, /var/www/)
- renew certfificate (letsencrypt)
- update of Hubzilla
- update of Debian
- restart
+ create cron jobs for
- DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes
- Master.php for Zap/Hubzilla every 10 minutes
- daily maintenance script every day at 05:30

The script is known to work without adjustments with

+ Hardware
- Mini-PC with Debian 10 (stretch), or
- Rapberry 3 with Raspbian, Debian 10
+ DynDNS
- selfHOST.de
- freedns.afraid.org

The script can install both [Hubzilla](https://zotlabs.org/page/hubzilla/hubzilla-project) and [Zap](https://zotlabs.com/zap/). Make sure to use the correct GIT repositories.

+ Hubzilla
- core: git clone https://framagit.org/hubzilla/core.git html (in this readme)
- addons: util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons (in hubzilla-setup.sh)
+ Zap
- core: git clone https://framagit.org/zot/zap.git html (in this readme)
- addons: util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons (in hubzilla-setup.sh)
# How to use

## Disclaimers

- This script does work with Debian 10 only.
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation).

# Step-by-Step Overwiew

## Preconditions

Hardware
@@ -58,7 +18,7 @@ Software
+ Fresh installation of Debian 10 (Stretch)
+ Router with open ports 80 and 443 for your web server

## The basic steps (quick overview)
## 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
@@ -76,31 +36,68 @@ Software
- ... wait, wait, wait until the script is finised
+ Open your domain with a browser and step throught the initial configuration of hubzilla.

## Troubleshooting
## Optional - Set path to imagemagick

If the check of the mail address fails when you try to register the very first user in the browser. Do...
In Admin settings of hubzilla or via terminal

cd /var/www/html
util/config system.do_not_check_dns 1
util/config system.imagick_convert_path /usr/bin/convert

## Optional - Set path to imagemagick
## Optional - Switch verification of email on/off

Do this just befor you register the user.

In Admin settings of hubzilla or via terminal

cd /var/www/html
util/config system.imagick_convert_path /usr/bin/convert

# Step-by-Step in Detail
Check the current setting

util/config system verify_email

Switch the verification on/off (1/0)

util/config system verify_email 0

## What the script will do for you...

+ install everything required by Hubzilla, 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
- backup to external disk (certificates, database, /var/www/)
- renew certfificate (letsencrypt)
- update of Hubzilla
- update of Debian
- restart
+ create cron jobs for
- DynDNS (selfHOST.de or freedns.afraid.org) every 5 minutes
- Master.php for Zap/Hubzilla every 10 minutes
- daily maintenance script every day at 05:30

The script is known to work without adjustments with

+ Hardware
- Mini-PC with Debian 10 (stretch), or
- Rapberry 3 with Raspbian, Debian 10
+ DynDNS
- selfHOST.de
- freedns.afraid.org

The script can install both [Hubzilla](https://zotlabs.org/page/hubzilla/hubzilla-project) and [Zap](https://zotlabs.com/zap/). Make sure to use the correct GIT repositories.

## Preparations Software
+ Hubzilla
- core: git clone https://framagit.org/hubzilla/core.git html (in this readme)
- addons: util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons (in hubzilla-setup.sh)
+ Zap
- core: git clone https://framagit.org/zot/zap.git html (in this readme)
- addons: util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons (in hubzilla-setup.sh)

## Install Debian 9

Provided you use a Raspberry Pi 3...

Download the OS Raspbian from https://www.raspberrypi.org/downloads/raspbian/
# Step-by-Step - some Details

Follow the installation instruction there.
## Preparations

## Configure your Router

@@ -146,12 +143,5 @@ to boot the Rapsi to the client console.

DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!

On a Raspian Stretch (Debian 10) the validation of the mail address fails for the very first user.
This used to happen on some *bsd distros but there was some work to fix that a year ago (2017).

So if your system isn't registered in DNS or DNS isn't active do

cd /var/www/html
util/config system.do_not_check_dns 1



+ 52
- 18
.homeinstall/hubzilla-setup.sh View File

@@ -28,14 +28,13 @@
# * php,
# * mariadb - the database for hubzilla,
# * adminer,
# * git to download and update hubzilla addon
# - download hubzilla core and addons
# * git to download and update addons
# - configure cron
# * "Master.php" for regular background prozesses of hubzilla
# * "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
# * backup hubzillas database and files (rsync)
# - letsencrypt
# - run letsencrypt to create, register and use a certifacte for https
#
#
# Discussion
@@ -56,7 +55,7 @@
# - creates a daily cron that runs the hubzilla-daily.sh
#
# hubzilla-daily.sh makes a (daily) backup of all relevant files
# - /var/lib/mysql/ > hubzilla database
# - /var/lib/mysql/ > database
# - /var/www/ > hubzilla/zap from github
# - /etc/letsencrypt/ > certificates
#
@@ -223,6 +222,11 @@ function install_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"
@@ -269,7 +273,19 @@ function install_adminer {
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
}

@@ -407,10 +423,9 @@ function install_letsencrypt {
then
die "Failed to install let's encrypt: 'le_domain' is empty in $configfile"
fi
# check if user gave mail address
if [ -z "$le_email" ]
then
die "Failed to install let's encrypt: 'le_domain' is empty in $configfile"
die "Failed to install let's encrypt: 'le_email' is empty in $configfile"
fi
nocheck_install "certbot python-certbot-apache"
print_info "run certbot ..."
@@ -431,12 +446,19 @@ function check_https {
}

function install_hubzilla {
print_info "installing hubzilla addons..."
print_info "installing addons..."
cd /var/www/html/
# if you install Hubzilla
# util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons
# if you install ZAP
util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons
if git remote -v | grep -i "origin.*hubzilla.*core"
then
print_info "hubzilla"
util/add_addon_repo https://framagit.org/hubzilla/addons hzaddons
elif git remote -v | grep -i "origin.*zap.*core"
then
print_info "zap"
util/add_addon_repo https://framagit.org/zot/zap-addons.git zaddons
else
die "neither zap nor hubzilla repository > did not install addons or zap/hubzilla"
fi
mkdir -p "store/[data]/smarty3"
chmod -R 777 store
touch .htconfig.php
@@ -446,7 +468,7 @@ function install_hubzilla {
chown root:www-data /var/www/html/
chown root:www-data /var/www/html/.htaccess
chmod 0644 /var/www/html/.htaccess
print_info "installed hubzilla"
print_info "installed addons"
}

function install_rsync {
@@ -585,6 +607,7 @@ check_config
stop_hubzilla
update_upgrade
install_curl
install_wget
install_sendmail
install_apache
install_imagemagick
@@ -600,23 +623,34 @@ configure_cron_selfhost

if [ "$le_domain" != "localhost" ]
then
install_letsencrypt
check_https
install_letsencrypt
configure_apache_for_https
check_https
else
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
print_info "is localhost - skipped installation of letsencrypt and configuration of apache for https"
fi

install_hubzilla

if [ "$le_domain" != "localhost" ]
then
rewrite_to_https
install_rsnapshot
else
print_info "is localhost - skipped rewrite to https and installation of rsnapshot"
fi

configure_cron_daily

if [ "$le_domain" != "localhost" ]
then
install_rsync
install_cryptosetup
install_cryptosetup
write_uninstall_script
else
print_info "is localhost - skipped installation of cryptosetup"
print_info "is localhost - skipped installation of cryptosetup"
fi


#set +x # stop debugging from here



+ 69
- 0
CHANGELOG View File

@@ -1,3 +1,72 @@
Hubzilla 4.6 (2019-12-04)
- Improve opengraph support for channels
- Add opengraph support for articles
- Update abook_connected for RSS feeds only if handle_feed() returned success
- Do not embed PDF files by default but allow to enabled this feature in security options
- Check if file exists before we include it in the router
- Update jquery to version 3.4.1
- Update composer libraries
- Remove old and unused javascript libraries
- Improved BBcode to Markdown conversion
- Introduce inline SVG support via BBcode
- Sanitize title on Atom/RSS feed import
- Improved HTTP headers cache support for photos
- Add date headers to signed headers
- Add check if item['tag'] is an array
- Add hook comments_are_now_closed for addons to override date based comment closure
- Change mysql schema for item.llink and item.plink for new installs from char(191) to text
- Improved photo cache expiration
- Improved plural function processing on translation strings creation from .po file with util/po2php utlility
- Improved support for CDN/Infrastructure caching (especially profile images)
- New japanese translation
- Add connect button for non-zot networks not connected in current location
- Allow to send forum channels wall2wall or sent by mentions post to external sites via addons
- Allow addons to process forum posts published through mentions
- Improved internal routing for ActivityPub messages
- Improved admin documentation
- Add ITEM_TYPE_CUSTOM and hooks to permit addons to create and distribute custom item types
- Support "comment policy" in Zot6 communications
- Add selected text as quote on reply if comment button is used
- Add more nofollow tags to links to discourage backlink farmers
- Improved conversion of emoji reactions from zot to zot6
- Add CardDAV/CalDAV autodiscovery
- Label source project of zotfeed since it is not completely compatible across projects
- Update homeinstall script

Bugfixes
- Fix once cached embedded content is used and stored forever
- Fix wildcard tag issue
- Fix duplicate attachment in jot fileupload
- Fix regression with audio file upload
- Fix can not edit menu name or title (#1402)
- Fix pagination encoding issue for some server setups
- Fix Zap->Hubzilla event title compatibility
- Fix event timezones for Zot6
- Fix missing summary in mod article_edit
- Fix PHP warning failed to write session data using user defined save handler
- Fix possible thumbnails distortion on rebuild with util/thumbrepair utility
- Fix issues with image import to zot6
- Fix attachment permissions on clonned channels sync
- Fix entries without sitekey returned from DB in queue_deliver() and Lib/Queue

Addons
- Twitter: send tweet even if attached image uploading was unsuccessful
- Livejournal: add link to original post option
- Flashcards: update to version 2.08
- Pubcrawl: compatibility changes to support pixelfed
- Cart: update paypal button to API v2
- Photocache: rework for speed and lower memory consumption
- Photocache: etag support for cached photos
- Photocache: purge cache on addon uninstall
- Openstreetmap: fix regression if no default values set
- Livejournal: allow send posts from non channel owner
- Pubcrawl: fix event timezones
- Pubcrawl: better ActivityPub channel URL detection
- Pubcrawl: fix comments delivery for other channels on the same hub
- New addon "workflow" with initial basic "issue tracker" capability



Hubzilla 4.4.1 (2019-08-16)
- Fix wrong profile photo displayed when previewing and editing profiles
- Fix regression from 4.4 which prevented encrypted signatures from being used for encrypted messages


+ 8
- 9
Zotlabs/Daemon/Cron.php View File

@@ -97,13 +97,17 @@ class Cron {
// Clean expired photos from cache
$age = get_config('system','active_expire_days', '30');
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
db_utcnow(),
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(get_config('system','active_expire_days', '30') . ' DAY')
);
foreach($r as $rr) {
$file = dbunescbin($rr['content']);
if(is_file($file)) {
@@ -113,11 +117,6 @@ class Cron {
}
}
}
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
);

// publish any applicable items that were set to be published in the future
// (time travel posts). Restrict to items that have come of age in the last
@@ -215,7 +214,7 @@ class Cron {
$restart = true;
$generation = intval($argv[2]);
if(! $generation)
killme();
return;
}

reload_plugins();


+ 5
- 0
Zotlabs/Daemon/Cron_daily.php View File

@@ -44,6 +44,11 @@ class Cron_daily {
db_utcnow(), db_quoteinterval('1 YEAR')
);

// Clean up emdedded content cache
q("DELETE FROM cache WHERE updated < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval(get_config('system','active_expire_days', '30') . ' DAY')
);

//update statistics in config
require_once('include/statistics_fns.php');


+ 3
- 3
Zotlabs/Daemon/CurlAuth.php View File

@@ -13,7 +13,7 @@ class CurlAuth {
static public function run($argc,$argv) {

if($argc != 2)
killme();
return;

\App::$session->start();

@@ -50,6 +50,6 @@ class CurlAuth {

file_put_contents($c,$x);

killme();
return;
}
}
}

+ 1
- 1
Zotlabs/Daemon/Master.php View File

@@ -9,7 +9,7 @@ if(array_search( __file__ , get_included_files()) === 0) {

if($argc)
Master::Release($argc,$argv);
killme();
return;
}




+ 15
- 2
Zotlabs/Daemon/Notifier.php View File

@@ -285,8 +285,21 @@ class Notifier {
}

if(! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST ] )) {
logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
return;
$hookinfo=[
'targetitem'=>$target_item,
'deliver'=>false
];
if (intval($target_item['item_type'] == ITEM_TYPE_CUSTOM)) {
call_hooks('customitem_deliver',$hookinfo);
}

if (!$hookinfo['deliver']) {
logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
return;
}

$target_item = $hookinfo['targetitem'];

}

// Check for non published items, but allow an exclusion for transmitting hidden file activities


+ 7
- 5
Zotlabs/Daemon/Onepoll.php View File

@@ -61,11 +61,13 @@ class Onepoll {

if($contact['xchan_network'] === 'rss') {
logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG);
handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']);
q("update abook set abook_connected = '%s' where abook_id = %d",
dbesc(datetime_convert()),
intval($contact['abook_id'])
);
$alive = handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']);
if ($alive) {
q("update abook set abook_connected = '%s' where abook_id = %d",
dbesc(datetime_convert()),
intval($contact['abook_id'])
);
}
return;
}


+ 1
- 1
Zotlabs/Daemon/Poller.php View File

@@ -47,7 +47,7 @@ class Poller {
$restart = true;
$generation = intval($argv[2]);
if(! $generation)
killme();
return;
}

if(($argc > 1) && intval($argv[1])) {


+ 169
- 34
Zotlabs/Lib/Activity.php View File

@@ -2,10 +2,12 @@

namespace Zotlabs\Lib;

use Zotlabs\Access\PermissionLimits;
use Zotlabs\Daemon\Master;
use Zotlabs\Web\HTTPSig;

require_once('include/event.php');
require_once('include/html2plain.php');

class Activity {

@@ -40,6 +42,8 @@ class Activity {
if($x['type'] === ACTIVITY_OBJ_PHOTO) {
return self::fetch_image($x);
}

call_hooks('encode_object',$x);
}

return $x;
@@ -63,12 +67,32 @@ class Activity {
}
else {
$m = parse_url($url);

// handle bearcaps
if ($m['scheme'] === 'bear') {
$params = explode('&',$m['query']);
if ($params) {
foreach ($params as $p) {
if (substr($p,0,2) === 'u=') {
$url = substr($p,2);
}
if (substr($p,0,2) === 't=') {
$token = substr($p,2);
}
}
$m = parse_url($url);
}
}

$headers = [
'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'Host' => $m['host'],
'(request-target)' => 'get ' . get_request_string($url),
'Date' => datetime_convert('UTC','UTC','now','D, d M Y H:i:s') . ' UTC'
'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'),
'(request-target)' => 'get ' . get_request_string($url)
];
if (isset($token)) {
$headers['Authorization'] = 'Bearer ' . $token;
}
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
$x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] );
}
@@ -178,6 +202,11 @@ class Activity {
$ev = bbtoevent($x['content']);
if($ev) {


if (! $ev['timezone']) {
$ev['timezone'] = 'UTC';
}

$actor = null;
if(array_key_exists('author',$x) && array_key_exists('link',$x['author'])) {
$actor = $x['author']['link'][0]['href'];
@@ -185,16 +214,17 @@ class Activity {
$y = [
'type' => 'Event',
'id' => z_root() . '/event/' . $ev['event_hash'],
'summary' => bbcode($ev['summary'], [ 'cache' => true ]),
'name' => $ev['summary'],
// 'summary' => bbcode($ev['summary'], [ 'cache' => true ]),
// RFC3339 Section 4.3
'startTime' => (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')),
'startTime' => (($ev['adjust']) ? datetime_convert($ev['timezone'],'UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')),
'content' => bbcode($ev['description'], [ 'cache' => true ]),
'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location'], [ 'cache' => true ]) ],
'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ],
'actor' => $actor,
];
if(! $ev['nofinish']) {
$y['endTime'] = (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtend'],'Y-m-d\\TH:i:s-00:00'));
$y['endTime'] = (($ev['adjust']) ? datetime_convert($ev['timezone'],'UTC',$ev['dtend'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtend'],'Y-m-d\\TH:i:s-00:00'));
}
// copy attachments from the passed object - these are already formatted for ActivityStreams
@@ -274,8 +304,14 @@ class Activity {

$ret = [];

$objtype = self::activity_obj_mapper($i['obj_type']);

if($i['verb'] === ACTIVITY_FRIEND) {
// Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
$objtype = 'Note';
}
else {
$objtype = self::activity_obj_mapper($i['obj_type']);
}
if(intval($i['item_deleted'])) {
$ret['type'] = 'Tombstone';
$ret['formerType'] = $objtype;
@@ -312,10 +348,21 @@ class Activity {
}
}

if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) {
$ret['commentPolicy'] = map_scope(PermissionLimits::Get($i['uid'],'post_comments'));
}

if (intval($i['item_private']) === 2) {
$ret['directMessage'] = true;
}

if (array_key_exists('comments_closed',$i) && $i['comments_closed'] !== EMPTY_STR && $i['comments_closed'] !== NULL_DATE) {
if($ret['commentPolicy']) {
$ret['commentPolicy'] .= ' ';
}
$ret['commentPolicy'] .= 'until=' . datetime_convert('UTC','UTC',$i['comments_closed'],ATOM_TIME);
}

$ret['attributedTo'] = $i['author']['xchan_url'];

if($i['id'] != $i['parent']) {
@@ -354,26 +401,30 @@ class Activity {

$ret = [];

if($item['tag']) {
foreach($item['tag'] as $t) {
if(! array_key_exists('type',$t))
if ($item['tag'] && is_array($item['tag'])) {
$ptr = $item['tag'];
if (! array_key_exists(0,$ptr)) {
$ptr = [ $ptr ];
}
foreach ($ptr as $t) {
if (! array_key_exists('type',$t))
$t['type'] = 'Hashtag';

switch($t['type']) {
case 'Hashtag':
$ret[] = [ 'ttype' => TERM_HASHTAG, 'url' => ((isset($t['href'])) ? $t['href'] : $t['id']), 'term' => escape_tags((substr($t['name'],0,1) === '#') ? substr($t['name'],1) : $t['name']) ];
$ret[] = [ 'ttype' => TERM_HASHTAG, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '#') ? substr($t['name'],1) : $t['name']) ];
break;

case 'Mention':
$mention_type = substr($t['name'],0,1);
if($mention_type === '!') {
if ($mention_type === '!') {
$ret[] = [ 'ttype' => TERM_FORUM, 'url' => $t['href'], 'term' => escape_tags(substr($t['name'],1)) ];
}
else {
$ret[] = [ 'ttype' => TERM_MENTION, 'url' => $t['href'], 'term' => escape_tags((substr($t['name'],0,1) === '@') ? substr($t['name'],1) : $t['name']) ];
}
break;
default:
break;
}
@@ -384,6 +435,7 @@ class Activity {
}



static function encode_taxonomy($item) {

$ret = [];
@@ -467,6 +519,12 @@ class Activity {
$ret = [];
$reply = false;


if($i['verb'] === ACTIVITY_FRIEND) {
// Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
$ret['obj'] = [];
}

if(intval($i['item_deleted'])) {
$ret['type'] = 'Tombstone';
$ret['formerType'] = self::activity_obj_mapper($i['obj_type']);
@@ -479,11 +537,6 @@ class Activity {
return $ret;
}

if($i['verb'] === ACTIVITY_FRIEND) {
// Hubzilla 'make-friend' activity, no direct mapping from AS1 to AS2 - make it a note
$ret['obj_type'] = ACTIVITY_OBJ_NOTE;
$ret['obj'] = [];
}

$ret['type'] = self::activity_mapper($i['verb']);

@@ -497,6 +550,25 @@ class Activity {
xchan_query($p,true);
$p = fetch_post_tags($p,true);
$i['obj'] = self::encode_item($p[0]);

// convert to zot6 emoji reaction encoding which uses the target object to indicate the
// specific emoji instead of overloading the verb or type.
$im = explode('#',$i['verb']);
if($im && count($im) > 1)
$emoji = $im[1];
if(preg_match("/\[img(.*?)\](.*?)\[\/img\]/ism", $i['body'], $match)) {
$ln = $match[2];
}

$i['tgt_type'] = 'Image';
$i['target'] = [
'type' => 'Image',
'name' => $emoji,
'url' => (($ln) ? $ln : z_root() . '/images/emoji/' . $emoji . '.png')
];
}
}

@@ -537,9 +609,15 @@ class Activity {
}

if($i['id'] != $i['parent']) {
$ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
$reply = true;

// inReplyTo needs to be set in the activity for followup actiions (Like, Dislike, Attend, Announce, etc.),
// but *not* for comments, where it should only be present in the object

if (! in_array($ret['type'],[ 'Create','Update' ])) {
$ret['inReplyTo'] = ((strpos($i['thr_parent'],'http') === 0) ? $i['thr_parent'] : z_root() . '/item/' . urlencode($i['thr_parent']));
}

if($i['item_private']) {
$d = q("select xchan_url, xchan_addr, xchan_name from item left join xchan on xchan_hash = author_xchan where id = %d limit 1",
intval($i['parent'])
@@ -577,7 +655,7 @@ class Activity {
$i['obj'] = json_decode($i['obj'],true);
}
if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) {
$i['obj']['id'] = $i['id'];
$i['obj']['id'] = $i['mid'];
}

$obj = self::encode_object($i['obj']);
@@ -668,8 +746,24 @@ class Activity {
}
$ret = [];

$c = ((array_key_exists('channel_id',$p)) ? $p : channelx_by_hash($p['xchan_hash']));

$ret['type'] = 'Person';
$ret['id'] = $p['xchan_url'];

if ($c) {
$role = get_pconfig($c['channel_id'],'system','permissions_role');
if (strpos($role,'forum') !== false) {
$ret['type'] = 'Group';
}
}

if ($c) {
$ret['id'] = channel_url($c);
}
else {
$ret['id'] = ((strpos($p['xchan_hash'],'http') === 0) ? $p['xchan_hash'] : $p['xchan_url']);
}

if($p['xchan_addr'] && strpos($p['xchan_addr'],'@'))
$ret['preferredUsername'] = substr($p['xchan_addr'],0,strpos($p['xchan_addr'],'@'));
$ret['name'] = $p['xchan_name'];
@@ -731,6 +825,7 @@ class Activity {
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
];

call_hooks('activity_mapper',$acts);

if(array_key_exists($verb,$acts) && $acts[$verb]) {
return $acts[$verb];
@@ -743,6 +838,9 @@ class Activity {
if(strpos($verb,ACTIVITY_MOOD) !== false)
return 'Create';

if(strpos($verb,ACTIVITY_FRIEND) !== false)
return 'Create';

if(strpos($verb,ACTIVITY_POKE) !== false)
return 'Activity';

@@ -773,6 +871,7 @@ class Activity {
'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept'
];

call_hooks('activity_decode_mapper',$acts);

foreach($acts as $k => $v) {
if($verb === $v) {
@@ -806,6 +905,8 @@ class Activity {
];

call_hooks('activity_obj_decode_mapper',$objs);

foreach($objs as $k => $v) {
if($obj === $v) {
return $k;
@@ -843,6 +944,8 @@ class Activity {
];

call_hooks('activity_obj_mapper',$objs);

if(array_key_exists($obj,$objs)) {
return $objs[$obj];
}
@@ -1601,11 +1704,12 @@ class Activity {
}

if($act->obj['type'] === 'Event') {

$s['obj'] = [];
$s['obj']['asld'] = $act->obj;
$s['obj']['type'] = ACTIVITY_OBJ_EVENT;
$s['obj']['id'] = $act->obj['id'];
$s['obj']['title'] = $act->obj['summary'];
$s['obj']['title'] = $act->obj['name'];

if(strpos($act->obj['startTime'],'Z'))
$s['obj']['adjust'] = true;
@@ -1863,6 +1967,15 @@ class Activity {
set_iconfig($s,'activitypub','rawmsg',$act->raw,1);
}

$hookinfo = [
'act' => $act,
's' => $s
];

call_hooks('decode_note',$hookinfo);

$s = $hookinfo['s'];

return $s;

}
@@ -2052,16 +2165,25 @@ class Activity {
break;

}
if(! $item) {
break;
}

array_unshift($p,[ $a, $item, $replies]);
$hookinfo = [
'a' => $a,
'item' => $item
];

call_hooks('fetch_and_store',$hookinfo);

if($item['parent_mid'] === $item['mid'] || count($p) > 20) {
break;
}
$item = $hookinfo['item'];

if($item) {

array_unshift($p,[ $a, $item, $replies]);
if($item['parent_mid'] === $item['mid'] || count($p) > 20) {
break;
}

}
$current_act = $a;
$current_item = $item;
}
@@ -2110,11 +2232,19 @@ class Activity {
default:
break;
}
if(! $item) {
break;
}

array_unshift($p,[ $a, $item ]);
$hookinfo = [
'a' => $a,
'item' => $item
];

call_hooks('fetch_and_store',$hookinfo);

$item = $hookinfo['item'];

if($item) {
array_unshift($p,[ $a, $item ]);
}

}

@@ -2495,7 +2625,12 @@ class Activity {
}

if($event) {
$event['summary'] = html2bbcode($content['summary']);
$event['summary'] = $content['name'];
if(! $event['summary']) {
if($content['summary']) {
$event['summary'] = html2plain($content['summary']);
}
}
$event['description'] = html2bbcode($content['content']);
if($event['summary'] && $event['dtstart']) {
$content['event'] = $event;


+ 4
- 9
Zotlabs/Lib/Cache.php View File

@@ -11,8 +11,10 @@ class Cache {

$hash = hash('whirlpool',$key);

$r = q("SELECT v FROM cache WHERE k = '%s' limit 1",
dbesc($hash)
$r = q("SELECT v FROM cache WHERE k = '%s' AND updated > %s - INTERVAL %s LIMIT 1",
dbesc($hash),
db_utcnow(),
db_quoteinterval(get_config('system','object_cache_days', '30') . ' DAY')
);
if ($r)
@@ -40,12 +42,5 @@ class Cache {
dbesc(datetime_convert()));
}
}

public static function clear() {
q("DELETE FROM cache WHERE updated < '%s'",
dbesc(datetime_convert('UTC','UTC',"now - 30 days")));
}
}

+ 1
- 1
Zotlabs/Lib/LDSignatures.php View File

@@ -30,7 +30,7 @@ class LDSignatures {
'type' => 'RsaSignature2017',
'nonce' => random_string(64),
'creator' => z_root() . '/channel/' . $channel['channel_address'],
'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\Th:i:s\Z')
'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\TH:i:s\Z')
];

$ohash = self::hash(self::signable_options($options));


+ 52
- 1
Zotlabs/Lib/Libzot.php View File

@@ -1223,9 +1223,39 @@ class Libzot {
if($private) {
$arr['item_private'] = true;
}

if ($arr['mid'] === $arr['parent_mid']) {
if (is_array($AS->obj) && array_key_exists('commentPolicy',$AS->obj)) {
$p = strstr($AS->obj['commentPolicy'],'until=');
if($p !== false) {
$arr['comments_closed'] = datetime_convert('UTC','UTC', substr($p,6));
$arr['comment_policy'] = trim(str_replace($p,'',$AS->obj['commentPolicy']));
}
else {
$arr['comment_policy'] = $AS->obj['commentPolicy'];
}
}
}


/// @FIXME - spoofable
if($AS->data['hubloc']) {
$arr['item_verified'] = true;

if (! array_key_exists('comment_policy',$arr)) {
// set comment policy depending on source hub. Unknown or osada is ActivityPub.
// Anything else we'll say is zot - which could have a range of project names
$s = q("select site_project from site where site_url = '%s' limit 1",
dbesc($r[0]['hubloc_url'])
);

if ((! $s) || (in_array($s[0]['site_project'],[ '', 'osada' ]))) {
$arr['comment_policy'] = 'authenticated';
}
else {
$arr['comment_policy'] = 'contacts';
}
}
}
if($AS->data['signed_data']) {
IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
@@ -1734,7 +1764,7 @@ class Libzot {
// if it's a sourced post, call the post_local hooks as if it were
// posted locally so that crosspost connectors will be triggered.

if(check_item_source($arr['uid'], $arr)) {
if(check_item_source($arr['uid'], $arr) || ($channel['xchan_pubforum'] == 1)) {
/**
* @hooks post_local
* Called when an item has been posted on this machine via mod/item.php (also via API).
@@ -1819,6 +1849,10 @@ class Libzot {

$ret = [];

$signer = q("select hubloc_hash, hubloc_url from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($a['signature']['signer'])
);

foreach($a['data']['orderedItems'] as $activity) {

$AS = new ActivityStreams($activity);
@@ -1877,6 +1911,23 @@ class Libzot {
if($AS->data['hubloc']) {
$arr['item_verified'] = true;
}

// set comment policy depending on source hub. Unknown or osada is ActivityPub.
// Anything else we'll say is zot - which could have a range of project names

if ($signer) {
$s = q("select site_project from site where site_url = '%s' limit 1",
dbesc($signer[0]['hubloc_url'])
);
if ((! $s) || (in_array($s[0]['site_project'],[ '', 'osada' ]))) {
$arr['comment_policy'] = 'authenticated';
}
else {
$arr['comment_policy'] = 'contacts';
}
}


if($AS->data['signed_data']) {
IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
}


+ 1
- 1
Zotlabs/Lib/Queue.php View File

@@ -250,7 +250,7 @@ class Queue {
$host_crypto = null;

if($channel && $base) {
$h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' order by hubloc_id desc limit 1",
$h = q("select hubloc_sitekey, site_crypto from hubloc left join site on hubloc_url = site_url where site_url = '%s' and hubloc_sitekey != '' order by hubloc_id desc limit 1",
dbesc($base)
);
if($h) {


+ 150
- 0
Zotlabs/Lib/SvgSanitizer.php View File

@@ -0,0 +1,150 @@
<?php

namespace Zotlabs\Lib;
use DomDocument;

/**
* SVGSantiizer
*
* Whitelist-based PHP SVG sanitizer.
*
* @link https://github.com/alister-/SVG-Sanitizer}
* @author Alister Norris
* @copyright Copyright (c) 2013 Alister Norris
* @license http://opensource.org/licenses/mit-license.php The MIT License
* @package svgsanitizer
*/

class SvgSanitizer {
private $xmlDoc; // PHP XML DOMDocument

private $removedattrs = [];

private static $allowed_functions = [ 'matrix', 'url', 'translate', 'rgb' ];

// defines the whitelist of elements and attributes allowed.
private static $whitelist = [
'a' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'href', 'xlink:href', 'xlink:title' ],
'circle' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
'clipPath' => [ 'class', 'clipPathUnits', 'id' ],
'defs' => [ ],
'style' => [ 'type' ],
'desc' => [ ],
'ellipse' => [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
'feGaussianBlur' => [ 'class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation' ],
'filter' => [ 'class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y' ],
'foreignObject' => [ 'class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y' ],
'g' => [ 'class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor' ],
'image' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
'line' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2' ],
'linearGradient' => [ 'class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
'marker' => [ 'id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ],
'mask' => [ 'class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
'metadata' => [ 'class', 'id' ],
'path' => [ 'class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
'pattern' => [ 'class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y' ],
'polygon' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
'polyline' => [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
'radialGradient' => [ 'class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href' ],
'rect' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y' ],
'stop' => [ 'class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage' ],
'svg' => [ 'class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y' ],
'switch' => [ 'class', 'id', 'requiredFeatures', 'systemLanguage' ],
'symbol' => [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox' ],
'text' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y' ],
'textPath' => [ 'class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href' ],
'title' => [ ],
'tspan' => [ 'class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y' ],
'use' => [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y' ],
];

function __construct() {
$this->xmlDoc = new DOMDocument('1.0','UTF-8');
$this->xmlDoc->preserveWhiteSpace = false;
libxml_use_internal_errors(true);
}

// load XML SVG
function load($file) {
$this->xmlDoc->load($file);
}

function loadXML($str) {
if (! $this->xmlDoc->loadXML($str)) {
logger('loadxml: ' . print_r(libxml_get_errors(),true), LOGGER_DEBUG);
return false;
}
return true;
}

function sanitize()
{
// all elements in xml doc
$allElements = $this->xmlDoc->getElementsByTagName('*');

// loop through all elements
for($i = 0; $i < $allElements->length; $i++)
{
$this->removedattrs = [];
$currentNode = $allElements->item($i);

// logger('current_node: ' . print_r($currentNode,true));

// array of allowed attributes in specific element
$whitelist_attr_arr = self::$whitelist[$currentNode->tagName];

// does element exist in whitelist?
if(isset($whitelist_attr_arr)) {
$total = $currentNode->attributes->length;
for($x = 0; $x < $total; $x++) {

// get attributes name
$attrName = $currentNode->attributes->item($x)->nodeName;

// logger('checking: ' . print_r($currentNode->attributes->item($x),true));
$matches = false;
// check if attribute isn't in whitelist
if(! in_array($attrName, $whitelist_attr_arr)) {
$this->removedattrs[] = $attrName;
}
// check for disallowed functions
elseif (preg_match_all('/([a-zA-Z0-9]+)[\s]*\(/',
$currentNode->attributes->item($x)->textContent,$matches,PREG_SET_ORDER)) {
if ($attrName === 'text') {
continue;
}
foreach ($matches as $match) {
if(! in_array($match[1],self::$allowed_functions)) {
logger('queue_remove_function: ' . $match[1],LOGGER_DEBUG);
$this->removedattrs[] = $attrName;
}
}
}
}
if ($this->removedattrs) {
foreach ($this->removedattrs as $attr) {
$currentNode->removeAttribute($attr);
logger('removed: ' . $attr, LOGGER_DEBUG);
}
}

}

// else remove element
else {
logger('remove_node: ' . print_r($currentNode,true));
$currentNode->parentNode->removeChild($currentNode);
}
}
return true;
}

function saveSVG() {
$this->xmlDoc->formatOutput = true;
return($this->xmlDoc->saveXML());
}
}

+ 1
- 4
Zotlabs/Lib/ThreadItem.php View File

@@ -778,8 +778,6 @@ class ThreadItem {
call_hooks('comment_buttons',$arr);
$comment_buttons = $arr['comment_buttons'];
$feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false");

$comment_box = replace_macros($template,array(
'$return_path' => '',
'$threaded' => $this->is_threaded(),
@@ -814,8 +812,7 @@ class ThreadItem {
'$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
'$anonname' => [ 'anonname', t('Your full name (required)') ],
'$anonmail' => [ 'anonmail', t('Your email address (required)') ],
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ],
'$auto_save_draft' => $feature_auto_save_draft
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ]
));

return $comment_box;


+ 16
- 15
Zotlabs/Module/Admin/Addons.php View File

@@ -2,6 +2,7 @@

namespace Zotlabs\Module\Admin;

use App;
use \Zotlabs\Storage\GitRepo;
use \Michelf\MarkdownExtra;

@@ -253,14 +254,14 @@ class Addons {
* Single plugin
*/

if (\App::$argc == 3){
$plugin = \App::$argv[2];
if (App::$argc == 3){
$plugin = App::$argv[2];
if (!is_file("addon/$plugin/$plugin.php")){
notice( t("Item not found.") );
return '';
}

$enabled = in_array($plugin,\App::$plugins);
$enabled = in_array($plugin,App::$plugins);
$info = get_plugin_info($plugin);
$x = check_plugin_versions($info);

@@ -268,11 +269,11 @@ class Addons {

if($enabled && ! $x) {
$enabled = false;
$idz = array_search($plugin, \App::$plugins);
$idz = array_search($plugin, App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
unset(App::$plugins[$idz]);
uninstall_plugin($plugin);
set_config("system","addon", implode(", ",\App::$plugins));
set_config("system","addon", implode(", ",App::$plugins));
}
}
$info['disabled'] = 1-intval($x);
@@ -281,19 +282,19 @@ class Addons {
check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't');
$pinstalled = false;
// Toggle plugin status
$idx = array_search($plugin, \App::$plugins);
$idx = array_search($plugin, App::$plugins);
if ($idx !== false){
unset(\App::$plugins[$idx]);
unset(App::$plugins[$idx]);
uninstall_plugin($plugin);
$pinstalled = false;
info( sprintf( t("Plugin %s disabled."), $plugin ) );
} else {
\App::$plugins[] = $plugin;
App::$plugins[] = $plugin;
install_plugin($plugin);
$pinstalled = true;
info( sprintf( t("Plugin %s enabled."), $plugin ) );
}
set_config("system","addon", implode(", ",\App::$plugins));
set_config("system","addon", implode(", ",App::$plugins));

if($pinstalled) {
@require_once("addon/$plugin/$plugin.php");
@@ -305,7 +306,7 @@ class Addons {

// display plugin details

if (in_array($plugin, \App::$plugins)){
if (in_array($plugin, App::$plugins)){
$status = 'on';
$action = t('Disable');
} else {
@@ -380,18 +381,18 @@ class Addons {

list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins);
$enabled = in_array($id,App::$plugins);
$x = check_plugin_versions($info);

// disable plugins which are installed but incompatible versions

if($enabled && ! $x) {
$enabled = false;
$idz = array_search($id, \App::$plugins);
$idz = array_search($id, App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
unset(App::$plugins[$idz]);
uninstall_plugin($id);
set_config("system","addon", implode(", ",\App::$plugins));
set_config("system","addon", implode(", ",App::$plugins));
}
}
$info['disabled'] = 1-intval($x);


+ 11
- 3
Zotlabs/Module/Admin/Security.php View File

@@ -43,6 +43,12 @@ class Security {
$be = $this->trim_array_elems(explode("\n",$_POST['embed_deny']));
set_config('system','embed_deny',$be);

$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
set_config('system', 'thumbnail_security' , $thumbnail_security);

$inline_pdf = ((x($_POST,'inline_pdf')) ? intval($_POST['inline_pdf']) : 0);
set_config('system', 'inline_pdf' , $inline_pdf);
$ts = ((x($_POST,'transport_security')) ? True : False);
set_config('system','transport_security_header',$ts);
@@ -86,7 +92,7 @@ class Security {
$embedhelp2 = t("The recommended setting is to only allow unfiltered HTML from the following sites:");
$embedhelp3 = t("https://youtube.com/<br />https://www.youtube.com/<br />https://youtu.be/<br />https://vimeo.com/<br />https://soundcloud.com/<br />");
$embedhelp4 = t("All other embedded content will be filtered, <strong>unless</strong> embedded content from that site is explicitly blocked.");
$t = get_markup_template('admin_security.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
@@ -106,7 +112,9 @@ class Security {
'$embed_sslonly' => array('embed_sslonly',t('Only allow embeds from secure (SSL) websites and links.'), intval(get_config('system','embed_sslonly')),''),
'$embed_allow' => array('embed_allow', t('Allow unfiltered embedded HTML content only from these domains'), $whiteembeds_str, t('One site per line. By default embedded content is filtered.')),
'$embed_deny' => array('embed_deny', t('Block embedded HTML from these domains'), $blackembeds_str, ''),
'$thumbnail_security' => [ 'thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.") ],
'$inline_pdf' => [ 'inline_pdf', t("Allow embedded (inline) PDF files"), get_config('system','inline_pdf',0), '' ],

// '$embed_coop' => array('embed_coop', t('Cooperative embed security'), $embed_coop, t('Enable to share embed security with other compatible sites/hubs')),

'$submit' => t('Submit')
@@ -128,4 +136,4 @@ class Security {
}
}
}

+ 0
- 3
Zotlabs/Module/Admin/Site.php View File

@@ -73,7 +73,6 @@ class Site {
$feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0);
$verify_email = ((x($_POST,'verify_email')) ? 1 : 0);
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000);
$pub_incl = escape_tags(trim($_POST['pub_incl']));
$pub_excl = escape_tags(trim($_POST['pub_excl']));
@@ -100,7 +99,6 @@ class Site {
set_config('system', 'from_email', $from_email);
set_config('system', 'from_email_name' , $from_email_name);
set_config('system', 'imagick_convert_path' , $imagick_path);
set_config('system', 'thumbnail_security' , $thumbnail_security);
set_config('system', 'default_permissions_role', $permissions_role);
set_config('system', 'pubstream_incl',$pub_incl);
set_config('system', 'pubstream_excl',$pub_excl);
@@ -341,7 +339,6 @@ class Site {
'$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',3000), t("Always defer immediate delivery if queue contains more than this number of entries.")),
'$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
'$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")),
'$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")),
'$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
'$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')),
'$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''),


+ 2
- 3
Zotlabs/Module/Article_edit.php View File

@@ -85,10 +85,9 @@ class Article_edit extends \Zotlabs\Web\Controller {

$mimetype = $itm[0]['mimetype'];

$summary = (($itm[0]['summary']) ? '[summary]' . $itm[0]['summary'] . '[/summary]' . "\r\n" : '');
$content = $itm[0]['body'];



$rp = 'articles/' . $channel['channel_address'];

$x = array(
@@ -110,7 +109,7 @@ class Article_edit extends \Zotlabs\Web\Controller {
'ptyp' => $itm[0]['type'],
'mimeselect' => false,
'mimetype' => $itm[0]['mimetype'],
'body' => undo_post_tagging($content),
'body' => $summary . undo_post_tagging($content),
'post_id' => $post_id,
'visitor' => true,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),


+ 8
- 4
Zotlabs/Module/Articles.php View File

@@ -9,6 +9,7 @@ use Zotlabs\Lib\PermissionDescription;
require_once('include/channel.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
require_once('include/opengraph.php');


class Articles extends Controller {
@@ -192,7 +193,7 @@ class Articles extends Controller {

$parents_str = ids_to_querystr($r,'id');

$items = q("SELECT item.*, item.id AS item_id
$r = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
@@ -200,15 +201,18 @@ class Articles extends Controller {
intval(App::$profile['profile_uid']),
dbesc($parents_str)
);
if($items) {
xchan_query($items);
$items = fetch_post_tags($items, true);
if($r) {
xchan_query($r);
$items = fetch_post_tags($r, true);
$items = conv_sort($items,'updated');
}
else
$items = [];
}

// Add Opengraph markup
opengraph_add_meta((! empty($items) ? $r[0] : []), $channel);

$mode = 'articles';
if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card))


+ 0
- 2
Zotlabs/Module/Cdav.php View File

@@ -910,8 +910,6 @@ class Cdav extends Controller {

require_once 'vendor/autoload.php';

head_add_css('cdav.css');

if(!cdav_principal($principalUri)) {
$this->activate($pdo, $channel);
if(!cdav_principal($principalUri)) {


+ 17
- 15
Zotlabs/Module/Channel.php View File

@@ -13,6 +13,7 @@ require_once('include/items.php');
require_once('include/security.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
require_once('include/opengraph.php');


/**
@@ -109,19 +110,20 @@ class Channel extends Controller {

// Run profile_load() here to make sure the theme is set before
// we start loading content

profile_load($which,$profile);

App::$page['htmlhead'] .= '<meta property="og:title" content="' . htmlspecialchars($channel['channel_name']) . '">' . "\r\n";
App::$page['htmlhead'] .= '<meta property="og:image" content="' . $channel['xchan_photo_l'] . '">' . "\r\n";

if(App::$profile['about'] && perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_profile')) {
App::$page['htmlhead'] .= '<meta property="og:description" content="' . htmlspecialchars(App::$profile['about']) . '">' . "\r\n";
}
else {
App::$page['htmlhead'] .= '<meta property="og:description" content="' . htmlspecialchars(sprintf( t('This is the home page of %s.'), $channel['channel_name'])) . '">' . "\r\n";
}

// Add Opengraph markup
$mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : '');
if(strpos($mid,'b64.') === 0)
$mid = @base64url_decode(substr($mid,4));
if($mid)
$r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d AND item_private = 0 LIMIT 1",
dbesc($mid),
intval($channel['channel_id'])
);
opengraph_add_meta($r ? $r[0] : [], $channel);
}

function get($update = 0, $load = false) {
@@ -362,7 +364,7 @@ class Channel extends Controller {

$parents_str = ids_to_querystr($r,'item_id');

$items = q("SELECT item.*, item.id AS item_id
$r = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
@@ -371,8 +373,8 @@ class Channel extends Controller {
dbesc($parents_str)
);

xchan_query($items);
$items = fetch_post_tags($items, true);
xchan_query($r);
$items = fetch_post_tags($r, true);
$items = conv_sort($items,$ordering);

if($load && $mid && (! count($items))) {


+ 0
- 7
Zotlabs/Module/Cloud.php View File

@@ -35,13 +35,6 @@ class Cloud extends \Zotlabs\Web\Controller {
if (argc() > 1)
$which = argv(1);


if (argc() < 2 && intval(get_config('system','cloud_disable_siteroot'))) {
notice( t('Permission denied.') . EOL);
construct_page();
killme();
}

$profile = 0;

if ($which)


+ 4
- 1
Zotlabs/Module/Connections.php View File

@@ -322,7 +322,10 @@ class Connections extends \Zotlabs\Web\Controller {
'ignore' => ((! $rr['abook_ignored']) ? t('Ignore') : false),
'recent_label' => t('Recent activity'),
'recentlink' => z_root() . '/network/?f=&cid=' . intval($rr['abook_id']) . '&name=' . $rr['xchan_name'],
'oneway' => $oneway
'oneway' => $oneway,
'connect' => (intval($rr['abook_not_here']) ? t('Connect') : ''),
'follow' => z_root() . '/follow/?f=&url=' . urlencode($rr['xchan_hash']) . '&interactive=0',
'connect_hover' => t('Connect at this location')
);
}
}


+ 2
- 0
Zotlabs/Module/Dav.php View File

@@ -95,6 +95,8 @@ class Dav extends \Zotlabs\Web\Controller {


$auth = new \Zotlabs\Storage\BasicAuth();
$auth->observer = get_observer_hash();

$auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . ' ' . 'WebDAV');

$rootDirectory = new \Zotlabs\Storage\Directory('/', $auth);


+ 2
- 2
Zotlabs/Module/Directory.php View File

@@ -287,7 +287,7 @@ class Directory extends \Zotlabs\Web\Controller {
$hometown = ((x($profile,'hometown') == 1) ? $profile['hometown'] : False);
$about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'])) : False);
$about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'], ['tryoembed' => false])) : False);
$keywords = ((x($profile,'keywords')) ? $profile['keywords'] : '');
@@ -345,7 +345,7 @@ class Directory extends \Zotlabs\Web\Controller {
'pdesc_label' => t('Description:'),
'marital' => $marital,
'homepage' => $homepage,
'homepageurl' => linkify($homepageurl),
'homepageurl' => linkify($homepageurl, true),
'hometown' => $hometown,
'hometown_label' => t('Hometown:'),
'about' => $about,


+ 1
- 12
Zotlabs/Module/Item.php View File

@@ -817,11 +817,6 @@ class Item extends Controller {
'revision' => $r['data']['revision']
);
}
$ext = substr($r['data']['filename'],strrpos($r['data']['filename'],'.'));
if(strpos($r['data']['filetype'],'audio/') !== false)
$attach_link = '[audio]' . z_root() . '/attach/' . $r['data']['hash'] . '/' . $r['data']['revision'] . (($ext) ? $ext : '') . '[/audio]';
elseif(strpos($r['data']['filetype'],'video/') !== false)
$attach_link = '[video]' . z_root() . '/attach/' . $r['data']['hash'] . '/' . $r['data']['revision'] . (($ext) ? $ext : '') . '[/video]';
$body = str_replace($match[1][$i],$attach_link,$body);
$i++;
}
@@ -1232,13 +1227,7 @@ class Item extends Controller {
killme();
}
if(($parent) && ($parent != $post_id)) {
// Store the comment signature information in case we need to relay to Diaspora
//$ditem = $datarray;
//$ditem['author'] = $observer;
//store_diaspora_comment_sig($ditem,$channel,$parent_item, $post_id, (($walltowall_comment) ? 1 : 0));
}
else {
if(($parent == $post_id) || ($datarray['item_private'] == 1)) {
$r = q("select * from item where id = %d",
intval($post_id)
);


+ 3
- 2
Zotlabs/Module/Menu.php View File

@@ -54,9 +54,10 @@ class Menu extends \Zotlabs\Web\Controller {
if($_REQUEST['menu_system'])
$_REQUEST['menu_flags'] |= MENU_SYSTEM;
$menu_id = ((argc() > 1) ? intval(argv(1)) : 0);
$menu_id = ((argc() > 2) ? intval(argv(2)) : 0);

if($menu_id) {
$_REQUEST['menu_id'] = intval(argv(1));
$_REQUEST['menu_id'] = $menu_id;
$r = menu_edit($_REQUEST);
if($r) {
menu_sync_packet($uid,get_observer_hash(),$menu_id);


+ 11
- 14
Zotlabs/Module/Photo.php View File

@@ -31,12 +31,7 @@ class Photo extends \Zotlabs\Web\Controller {
// NOTREACHED
}

$cache_mode = array(
'on' => false,
'age' => 86400,
'exp' => true,
'leak' => false
);
$cache_mode = [ 'on' => false, 'age' => 86400, 'exp' => true, 'leak' => false ];
call_hooks('cache_mode_hook', $cache_mode);
$observer_xchan = get_observer_hash();
@@ -144,7 +139,7 @@ class Photo extends \Zotlabs\Web\Controller {
$resolution = 1;
}
$r = q("SELECT uid, photo_usage, display_path FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
$r = q("SELECT * FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo),
intval($resolution)
);
@@ -163,13 +158,10 @@ class Photo extends \Zotlabs\Web\Controller {
if($u === PHOTO_CACHE) {
// Validate cache
if($cache_mode['on']) {
$cache = array(
'resid' => $photo,
'status' => false
);
$cache = [ 'status' => false, 'item' => $r[0] ];
call_hooks('cache_url_hook', $cache);
if(! $cache['status']) {
$url = html_entity_decode($r[0]['display_path'], ENT_QUOTES);
$url = html_entity_decode($cache['item']['display_path'], ENT_QUOTES);
// SSLify if needed
if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false)
$url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url);
@@ -229,7 +221,7 @@ class Photo extends \Zotlabs\Web\Controller {

header_remove('Pragma');

if($_SERVER['HTTP_IF_NONE_MATCH'] === $etag || $_SERVER['HTTP_IF_MODIFIED_SINCE'] === gmdate("D, d M Y H:i:s", $modified) . " GMT") {
if((isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag) || (!isset($_SERVER['HTTP_IF_NONE_MATCH']) && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $_SERVER['HTTP_IF_MODIFIED_SINCE'] === gmdate("D, d M Y H:i:s", $modified) . " GMT")) {
header_remove('Expires');
header_remove('Cache-Control');
header_remove('Set-Cookie');
@@ -272,7 +264,12 @@ class Photo extends \Zotlabs\Web\Controller {
$maxage = $expires - time();
header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
header("Cache-Control: max-age=" . $maxage . $cachecontrol);

// set CDN/Infrastructure caching much lower than maxage
// in the event that infrastructure caching is present.
$smaxage = intval($maxage/12);

header("Cache-Control: s-maxage=" . $smaxage . ", max-age=" . $maxage . $cachecontrol);
}



+ 1
- 3
Zotlabs/Module/Photos.php View File

@@ -1080,7 +1080,6 @@ class Photos extends \Zotlabs\Web\Controller {
$comments = '';
if(! $r) {
if($observer && ($can_post || $can_comment)) {
$feature_auto_save_draft = ((feature_enabled($owner_uid, 'auto_save_draft')) ? "true" : "false");
$commentbox = replace_macros($cmnt_tpl,array(
'$return_path' => '',
'$mode' => 'photos',
@@ -1096,8 +1095,7 @@ class Photos extends \Zotlabs\Web\Controller {
'$submit' => t('Submit'),
'$preview' => t('Preview'),
'$ww' => '',
'$feature_encrypt' => false,
'$auto_save_draft' => $feature_auto_save_draft
'$feature_encrypt' => false
));
}
}


+ 18
- 3
Zotlabs/Module/Wall_attach.php View File

@@ -86,7 +86,7 @@ class Wall_attach extends \Zotlabs\Web\Controller {
$def_attach = get_pconfig($channel['channel_id'],'system','attach_path');
$r = attach_store($channel,(($observer) ? $observer['xchan_hash'] : ''),'', array('source' => 'editor', 'visible' => 0, 'album' => $def_album, 'directory' => $def_attach, 'allow_cid' => '<' . $channel['channel_hash'] . '>'));
if(! $r['success']) {
notice( $r['message'] . EOL);
killme();
@@ -111,9 +111,24 @@ class Wall_attach extends \Zotlabs\Web\Controller {
}
if(strpos($r['data']['filetype'],'audio') === 0) {
$url = z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path'];
echo "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n";
$s = "\n\n" . '[zaudio]' . $url . '[/zaudio]' . "\n\n";
}
if ($r['data']['filetype'] === 'image/svg+xml') {
$x = @file_get_contents('store/' . $channel['channel_address'] . '/' . $r['data']['os_path']);
if ($x) {
$bb = svg2bb($x);
if ($bb) {
$s .= "\n\n" . $bb;
}
else {
logger('empty return from svgbb');
}
}
else {
logger('unable to read svg data file: ' . 'store/' . $channel['channel_address'] . '/' . $r['data']['os_path']);
}
}
$s .= "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n";
}



+ 12
- 0
Zotlabs/Module/Well_known.php View File

@@ -63,6 +63,18 @@ class Well_known extends \Zotlabs\Web\Controller {
case 'dnt-policy.txt':
echo file_get_contents('doc/dnt-policy.txt');
killme();
case 'caldav':
if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
http_status('301', 'moved permanently');
goaway(z_root() . '/cdav');
};
case 'carddav':
if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
http_status('301', 'moved permanently');
goaway(z_root() . '