streams/include/network.php

1824 lines
51 KiB
PHP
Raw Normal View History

<?php
2018-08-03 23:03:16 +00:00
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Zotfinger;
2019-07-19 03:38:36 +00:00
use Zotlabs\Lib\Webfinger;
2018-08-29 01:37:21 +00:00
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Activity;
2018-09-17 05:05:04 +00:00
use Zotlabs\Lib\ActivityPub;
2018-11-12 01:44:01 +00:00
use Zotlabs\Lib\Queue;
use Zotlabs\Lib\System;
use Zotlabs\Lib\Keyutils;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Daemon\Run;
2018-08-03 23:03:16 +00:00
/**
* @file include/network.php
* @brief Network related functions.
*/
/**
* @brief Returns path to CA file.
*
* @return string
*/
function get_capath() {
return appdirpath() . '/library/cacert.pem';
}
/**
* @brief fetches an URL.
*
* @param string $url
* URL to fetch
* @param boolean $binary default false
* TRUE if asked to return binary results (file download)
* @param int $redirects default 0
* internal use, recursion counter
2016-06-16 04:25:26 +00:00
* @param array $opts (optional parameters) associative array with:
* * \b timeout => int seconds, default system config value or 60 seconds
* * \b headers => array of additional header fields
* * \b http_auth => username:password
* * \b novalidate => do not validate SSL certs, default is to validate using our CA list
* * \b nobody => only return the header
* * \b filep => stream resource to write body to. header and body are not returned when using this option.
2016-06-16 04:25:26 +00:00
* * \b custom => custom request method: e.g. 'PUT', 'DELETE'
* * \b cookiejar => cookie file (write)
* * \b cookiefile => cookie file (read)
* * \b session => boolean; append session cookie *if* $url is our own site
*
2016-06-16 04:25:26 +00:00
* @return array an associative array with:
* * \e int \b return_code => HTTP return code or 0 if timeout or failure
* * \e boolean \b success => boolean true (if HTTP 2xx result) or false
* * \e string \b header => HTTP headers
* * \e string \b body => fetched content
*/
function z_fetch_url($url, $binary = false, $redirects = 0, $opts = []) {
2012-08-10 03:31:06 +00:00
$ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => "");
$passthru = false;
2012-08-10 03:31:06 +00:00
$ch = @curl_init($url);
if(($redirects > 8) || (! $ch))
return $ret;
2012-08-10 03:31:06 +00:00
2018-10-11 00:58:51 +00:00
if(! array_key_exists('request_target',$opts)) {
$opts['request_target'] = 'get ' . get_request_string($url);
}
2012-08-10 03:31:06 +00:00
@curl_setopt($ch, CURLOPT_HEADER, true);
@curl_setopt($ch, CURLINFO_HEADER_OUT, true);
@curl_setopt($ch, CURLOPT_CAINFO, get_capath());
2013-02-15 01:39:16 +00:00
@curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
@curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
2020-05-03 23:52:44 +00:00
@curl_setopt($ch, CURLOPT_ENCODING, '');
$ciphers = @get_config('system','curl_ssl_ciphers');
if($ciphers)
@curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers);
if(x($opts,'filep')) {
@curl_setopt($ch, CURLOPT_FILE, $opts['filep']);
2016-12-16 00:02:52 +00:00
@curl_setopt($ch, CURLOPT_HEADER, false);
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$passthru = true;
}
if (x($opts['useragent'])) {
@curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']);
}
else {
@curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)");
}
if(x($opts,'upload'))
@curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']);
if(x($opts,'infile'))
@curl_setopt($ch, CURLOPT_INFILE, $opts['infile']);
if(x($opts,'infilesize'))
@curl_setopt($ch, CURLOPT_INFILESIZE, $opts['infilesize']);
if(x($opts,'readfunc'))
@curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']);
// When using the session option and fetching from our own site,
// append the PHPSESSID cookie to any existing headers.
// Don't add to $opts['headers'] so that the cookie does not get
// sent to other sites via redirects
$instance_headers = ((array_key_exists('headers',$opts) && is_array($opts['headers'])) ? $opts['headers'] : []);
if(x($opts,'session')) {
if(strpos($url,z_root()) === 0) {
$instance_headers[] = 'Cookie: PHPSESSID=' . session_id();
}
}
if($instance_headers)
@curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers);
2012-08-10 03:31:06 +00:00
if(x($opts,'nobody'))
@curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']);
2016-06-16 04:25:26 +00:00
if(x($opts,'custom'))
@curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']);
2013-02-15 01:39:16 +00:00
if(x($opts,'timeout') && intval($opts['timeout'])) {
2017-01-25 03:44:24 +00:00
@curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout']));
2012-08-10 03:31:06 +00:00
}
else {
2019-03-08 04:13:33 +00:00
$curl_time = intval(@get_config('system','curl_timeout',60));
2019-03-08 04:15:03 +00:00
@curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time);
2019-03-08 04:13:33 +00:00
}
if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) {
@curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout']));
}
else {
$curl_contime = intval(@get_config('system','curl_connecttimeout',30));
2019-03-08 04:15:03 +00:00
@curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime);
2012-08-10 03:31:06 +00:00
}
2013-02-15 01:39:16 +00:00
if(x($opts,'http_auth')) {
// "username" . ':' . "password"
@curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']);
}
2012-08-10 03:31:06 +00:00
if(x($opts,'cookiejar'))
@curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']);
if(x($opts,'cookiefile'))
@curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']);
if(x($opts,'cookie'))
@curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']);
@curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,
2013-02-26 23:49:37 +00:00
((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true));
2016-12-16 00:02:52 +00:00
$prx = @get_config('system','proxy');
2012-08-10 03:31:06 +00:00
if(strlen($prx)) {
@curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
@curl_setopt($ch, CURLOPT_PROXY, $prx);
$prxusr = @get_config('system','proxyuser');
if(strlen($prxusr))
@curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
}
if($binary)
@curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
// don't let curl abort the entire application'
2012-08-10 03:31:06 +00:00
// if it throws any errors.
$s = @curl_exec($ch);
$base = $s;
$curl_info = @curl_getinfo($ch);
$http_code = $curl_info['http_code'];
//logger('fetch_url:' . $http_code . ' data: ' . $s);
2012-08-10 03:31:06 +00:00
$header = '';
// For file redirects, set curl to follow location (indicated by $passthru).
// Then just return success or failure.
if ($passthru) {
if ($http_code >= 200 && $http_code < 300) {
$ret['success'] = true;
}
$ret['return_code'] = $http_code;
@curl_close($ch);
return $ret;
}
2012-08-10 03:31:06 +00:00
// Pull out multiple headers, e.g. proxy and continuation headers
// allow for HTTP/2.x without fixing code
2019-12-27 21:55:50 +00:00
while(preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/',$base)) {
2012-08-10 03:31:06 +00:00
$chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
$header .= $chunk;
$base = substr($base,strlen($chunk));
}
if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) {
$matches = [];
2018-09-10 13:13:28 +00:00
preg_match('/(Location:|URI:)(.*?)\n/i', $header, $matches);
2012-08-10 03:31:06 +00:00
$newurl = trim(array_pop($matches));
2020-08-13 06:58:09 +00:00
if(strpos($newurl,'/') === 0) {
// We received a redirect to a relative path.
// Find the base component of the original url and re-assemble it with the new location
$base = @parse_url($url);
if ($base) {
unset($base['path']); unset($base['query']); unset($base['fragment']);
$newurl = unparse_url($base) . $newurl;
}
}
if ($newurl) {
@curl_close($ch);
return z_fetch_url($newurl,$binary,++$redirects,$opts);
2012-08-10 03:31:06 +00:00
}
}
$rc = intval($http_code);
$ret['return_code'] = $rc;
$ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false);
if(! $ret['success']) {
$ret['error'] = curl_error($ch);
$ret['debug'] = $curl_info;
$dbg = [
'url' => $ret['debug']['url'],
'content_type' => $ret['debug']['content_type'],
'error_code' => $ret['debug']['error_code'],
'request_header' => $ret['debug']['request_header']
];
logger('z_fetch_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG);
logger('z_fetch_url: debug: ' . print_r($dbg,true), LOGGER_DATA);
}
2012-08-10 03:31:06 +00:00
$ret['body'] = substr($s,strlen($header));
$ret['header'] = $header;
2018-10-11 00:58:51 +00:00
$ret['request_target'] = $opts['request_target'];
if(x($opts,'debug')) {
$ret['debug'] = $curl_info;
}
2012-08-10 03:31:06 +00:00
@curl_close($ch);
return($ret);
2013-02-26 23:49:37 +00:00
}
/**
* @brief Does a curl post request.
*
* @param string $url
2014-09-09 11:10:01 +00:00
* URL to post
* @param mixed $params
* The full data to post in a HTTP "POST" operation. This parameter can
* either be passed as a urlencoded string like 'para1=val1&para2=val2&...'
* or as an array with the field name as key and field data as value. If value
* is an array, the Content-Type header will be set to multipart/form-data.
* @param int $redirects = 0
* internal use, recursion counter
* @param array $opts (optional parameters)
* 'timeout' => int seconds, default system config value or 60 seconds
* 'http_auth' => username:password
* 'novalidate' => do not validate SSL certs, default is to validate using our CA list
* 'filep' => stream resource to write body to. header and body are not returned when using this option.
2016-06-16 04:25:26 +00:00
* 'custom' => custom request method: e.g. 'PUT', 'DELETE'
*
* @return array an associative array with:
* * \e int \b return_code => HTTP return code or 0 if timeout or failure
* * \e boolean \b success => boolean true (if HTTP 2xx result) or false
* * \e string \b header => HTTP headers
* * \e string \b body => content
* * \e string \b debug => from curl_info()
*/
function z_post_url($url, $params, $redirects = 0, $opts = []) {
2016-06-20 01:57:56 +00:00
// logger('url: ' . $url);
// logger('params: ' . print_r($params,true));
// logger('opts: ' . print_r($opts,true));
$ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => "");
$ch = curl_init($url);
if(($redirects > 8) || (! $ch))
return $ret;
2018-10-11 00:58:51 +00:00
if(! array_key_exists('request_target',$opts)) {
$opts['request_target'] = 'post ' . get_request_string($url);
}
@curl_setopt($ch, CURLOPT_HEADER, true);
@curl_setopt($ch, CURLINFO_HEADER_OUT, true);
@curl_setopt($ch, CURLOPT_CAINFO, get_capath());
@curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
@curl_setopt($ch, CURLOPT_POST,1);
@curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
2020-05-03 23:52:44 +00:00
@curl_setopt($ch, CURLOPT_ENCODING, '');
$ciphers = @get_config('system','curl_ssl_ciphers');
if($ciphers)
@curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, $ciphers);
if(x($opts,'filep')) {
@curl_setopt($ch, CURLOPT_FILE, $opts['filep']);
2016-12-16 00:02:52 +00:00
@curl_setopt($ch, CURLOPT_HEADER, false);
}
if (x($opts['useragent'])) {
@curl_setopt($ch, CURLOPT_USERAGENT, $opts['useragent']);
}
else {
@curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; zot)");
}
$instance_headers = ((array_key_exists('headers',$opts) && is_array($opts['headers'])) ? $opts['headers'] : []);
if(x($opts,'session')) {
if(strpos($url,z_root()) === 0) {
$instance_headers[] = 'Cookie: PHPSESSID=' . session_id();
}
}
if($instance_headers)
@curl_setopt($ch, CURLOPT_HTTPHEADER, $instance_headers);
if(x($opts,'nobody'))
@curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']);
2016-06-20 01:57:56 +00:00
if(x($opts,'custom')) {
2016-06-16 04:25:26 +00:00
@curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']);
2016-06-20 01:57:56 +00:00
@curl_setopt($ch, CURLOPT_POST,0);
}
2016-06-16 04:25:26 +00:00
if(x($opts,'timeout') && intval($opts['timeout'])) {
2019-03-08 04:13:33 +00:00
@curl_setopt($ch, CURLOPT_TIMEOUT, intval($opts['timeout']));
}
else {
$curl_time = intval(@get_config('system','curl_timeout',60));
2019-03-08 04:15:03 +00:00
@curl_setopt($ch, CURLOPT_TIMEOUT, $curl_time);
2019-03-08 04:13:33 +00:00
}
if(x($opts,'connecttimeout') && intval($opts['connecttimeout'])) {
@curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($opts['connecttimeout']));
}
else {
2019-03-08 04:13:33 +00:00
$curl_contime = intval(@get_config('system','curl_connecttimeout',30));
2019-03-08 04:15:03 +00:00
@curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $curl_contime);
}
if(x($opts,'http_auth')) {
// "username" . ':' . "password"
@curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']);
}
if(x($opts,'cookiejar'))
@curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']);
if(x($opts,'cookiefile'))
@curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']);
if(x($opts,'cookie'))
@curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']);
@curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,
((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true));
$prx = get_config('system','proxy');
if(strlen($prx)) {
@curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
@curl_setopt($ch, CURLOPT_PROXY, $prx);
$prxusr = get_config('system','proxyuser');
if(strlen($prxusr))
@curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
}
// don't let curl abort the entire application
// if it throws any errors.
$s = @curl_exec($ch);
$base = $s;
$curl_info = @curl_getinfo($ch);
$http_code = $curl_info['http_code'];
$header = '';
// Pull out multiple headers, e.g. proxy and continuation headers
// allow for HTTP/2.x without fixing code
2019-12-27 21:55:50 +00:00
while(preg_match('/^HTTP\/[1-3][\.0-9]* [1-5][0-9][0-9]/',$base)) {
$chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
$header .= $chunk;
$base = substr($base,strlen($chunk));
}
// would somebody take lighttpd and just shoot it?
if($http_code == 417) {
curl_close($ch);
if($opts) {
if($opts['headers'])
$opts['headers'][] = 'Expect:';
else
$opts['headers'] = array('Expect:');
}
else
$opts = array('headers' => array('Expect:'));
return z_post_url($url,$params,++$redirects,$opts);
}
if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307 || $http_code == 308) {
$matches = [];
2012-12-26 01:17:43 +00:00
preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
$newurl = trim(array_pop($matches));
2020-08-13 06:58:09 +00:00
if(strpos($newurl,'/') === 0) {
// We received a redirect to a relative path.
// Find the base component of the original url and re-assemble it with the new location
$base = @parse_url($url);
if ($base) {
unset($base['path']); unset($base['query']); unset($base['fragment']);
$newurl = unparse_url($base) . $newurl;
}
}
if ($newurl) {
curl_close($ch);
if($http_code == 303) {
2017-05-08 09:00:00 +00:00
return z_fetch_url($newurl,false,++$redirects,$opts);
2020-08-13 06:58:09 +00:00
}
else {
return z_post_url($newurl,$params,++$redirects,$opts);
}
2012-12-26 01:17:43 +00:00
}
}
$rc = intval($http_code);
$ret['return_code'] = $rc;
$ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false);
if(! $ret['success']) {
$ret['error'] = curl_error($ch);
$ret['debug'] = $curl_info;
$dbg = [
'url' => $ret['debug']['url'],
'content_type' => $ret['debug']['content_type'],
'error_code' => $ret['debug']['error_code'],
'request_header' => $ret['debug']['request_header']
];
logger('z_post_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG);
logger('z_post_url: debug: ' . print_r($dbg,true), LOGGER_DATA);
}
$ret['body'] = substr($s, strlen($header));
$ret['header'] = $header;
2018-10-11 00:58:51 +00:00
$ret['request_target'] = $opts['request_target'];
if(x($opts,'debug')) {
$ret['debug'] = $curl_info;
}
curl_close($ch);
return($ret);
2013-02-26 23:49:37 +00:00
}
2020-11-14 19:57:28 +00:00
function json_return_and_die($x, $content_type = 'application/json', $debug = false) {
2016-02-19 08:06:10 +00:00
header("Content-type: $content_type");
2020-11-14 19:57:28 +00:00
if ($debug) {
logger('returned_json: ' . json_encode($x,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES), LOGGER_DATA);
}
2012-08-09 01:50:04 +00:00
echo json_encode($x);
killme();
}
function as_return_and_die($obj,$channel) {
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
2021-01-23 00:59:56 +00:00
Activity::ap_schema()
]], $obj );
$headers = [];
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
$x['signature'] = LDSignatures::sign($x,$channel);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
logger('data: ' . jindent($ret), LOGGER_DATA);
$headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
HTTPSig::set_headers($h);
echo $ret;
killme();
}
/**
* @brief Send HTTP status header.
*
* @param int $val
* integer HTTP status result value
* @param string $msg
* optional message
*/
function http_status($val, $msg = '') {
if ($val >= 400)
$msg = (($msg) ? $msg : 'Error');
if ($val >= 200 && $val < 300)
$msg = (($msg) ? $msg : 'OK');
2011-08-10 01:55:46 +00:00
logger(\App::$query_string . ':' . $val . ' ' . $msg);
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg);
}
/**
* @brief Send HTTP status header and exit.
*
* @param int $val
* integer HTTP status result value
* @param string $msg
* optional message
* @return void does not return, process is terminated
*/
function http_status_exit($val, $msg = '') {
http_status($val, $msg);
2011-08-10 01:55:46 +00:00
killme();
2013-02-26 23:49:37 +00:00
}
2011-08-10 01:55:46 +00:00
2020-08-13 06:58:09 +00:00
/*
*
* Takes the output of parse_url and builds a URL from it
*
*/
function unparse_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
2021-03-09 23:36:07 +00:00
$port = ((isset($parsed_url['port']) && intval($parsed_url['port'])) ? ':' . intval($parsed_url['port']) : '');
2020-08-13 06:58:09 +00:00
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
/**
* @brief Convert an XML document to a normalised, case-corrected array used by webfinger.
*
* @param string|array|SimpleXMLElement $xml_element
* @param[in,out] int $recursion_depth
* @return NULL|string|array
*/
function convert_xml_element_to_array($xml_element, &$recursion_depth=0) {
// If we're getting too deep, bail out
if ($recursion_depth > 512) {
return(null);
}
if (!is_string($xml_element) &&
!is_array($xml_element) &&
(get_class($xml_element) == 'SimpleXMLElement')) {
$xml_element_copy = $xml_element;
$xml_element = get_object_vars($xml_element);
}
if (is_array($xml_element)) {
$result_array = [];
if (count($xml_element) <= 0) {
return (trim(strval($xml_element_copy)));
}
foreach($xml_element as $key=>$value) {
$recursion_depth++;
$result_array[strtolower($key)] =
convert_xml_element_to_array($value, $recursion_depth);
$recursion_depth--;
}
if ($recursion_depth == 0) {
$temp_array = $result_array;
$result_array = array(
strtolower($xml_element_copy->getName()) => $temp_array,
);
}
return ($result_array);
} else {
return (trim(strval($xml_element)));
}
2013-02-26 23:49:37 +00:00
}
function z_dns_check($h,$check_mx = 0) {
// dns_get_record() has issues on some platforms
// so allow somebody to ignore it completely
// Use config values from memory as this can be called during setup
// before a database or even any config structure exists.
if(is_array(App::$config) && array_path_exists('system/do_not_check_dns', App::$config) && App::$config['system']['do_not_check_dns'])
return true;
2018-05-24 23:32:21 +00:00
// This will match either Windows or Mac ('Darwin')
if(stripos(PHP_OS,'win') !== false)
return true;
// BSD variants have dns_get_record() but it only works reliably without any options
if(stripos(PHP_OS,'bsd') !== false)
return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false);
// Otherwise we will assume dns_get_record() works as documented
$opts = DNS_A + DNS_CNAME + DNS_AAAA;
2018-05-24 23:32:21 +00:00
if($check_mx)
$opts += DNS_MX;
2018-05-24 23:32:21 +00:00
return((@dns_get_record($h,$opts) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false);
}
/**
* @brief Validates a given URL.
*
* Take a URL from the wild, prepend http:// if necessary and check DNS to see
* if it's real (or check if is a valid IP address).
*
* @see z_dns_check()
*
* @param[in,out] string $url URL to check
* @return boolean Return true if it's OK, false if something is wrong with it
*/
function validate_url(&$url) {
// no naked subdomains (allow localhost for tests)
if(strpos($url, '.') === false && strpos($url, '/localhost/') === false)
2012-02-12 22:18:32 +00:00
return false;
if(substr($url, 0, 4) != 'http')
$url = 'http://' . $url;
$h = @parse_url($url);
if(($h) && z_dns_check($h['host'])) {
return true;
}
return false;
2013-02-26 23:49:37 +00:00
}
/**
* @brief Checks that email is an actual resolvable internet address.
*
* @param string $addr
* @return boolean
*/
function validate_email($addr) {
if(get_config('system', 'disable_email_validation'))
2012-07-02 01:56:00 +00:00
return true;
if(! strpos($addr, '@'))
return false;
$h = substr($addr, strpos($addr, '@') + 1);
if(($h) && z_dns_check($h, true)) {
return true;
}
return false;
2013-02-26 23:49:37 +00:00
}
/**
* @brief Check if email address is allowed to register here.
*
* Compare against our list (wildcards allowed).
*
* @param string $email
* @return boolean Returns false if not allowed, true if allowed or if allowed list is
* not configured.
*/
function allowed_email($email) {
$domain = strtolower(substr($email, strpos($email, '@') + 1));
if(! $domain)
return false;
$str_allowed = get_config('system', 'allowed_email');
$str_not_allowed = get_config('system', 'not_allowed_email');
if(! $str_allowed && ! $str_not_allowed)
return true;
$return = false;
$found_allowed = false;
$found_not_allowed = false;
$fnmatch = function_exists('fnmatch');
$allowed = explode(',', $str_allowed);
if(count($allowed)) {
foreach($allowed as $a) {
$pat = strtolower(trim($a));
if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) {
$found_allowed = true;
break;
}
}
}
$not_allowed = explode(',', $str_not_allowed);
if(count($not_allowed)) {
foreach($not_allowed as $na) {
$pat = strtolower(trim($na));
if(($fnmatch && fnmatch($pat,$email)) || ($pat == $domain)) {
$found_not_allowed = true;
break;
}
}
}
if ($found_allowed) {
$return = true;
} elseif (!$str_allowed && !$found_not_allowed) {
$return = true;
}
return $return;
2013-02-26 23:49:37 +00:00
}
function parse_xml_string($s, $strict = true) {
if($strict) {
if(! strstr($s,'<?xml'))
return false;
$s2 = substr($s,strpos($s,'<?xml'));
}
else
$s2 = $s;
libxml_use_internal_errors(true);
$x = @simplexml_load_string($s2);
2017-08-10 00:35:03 +00:00
if($x === false) {
logger('libxml: parse: error: ' . $s2, LOGGER_DATA);
2017-08-10 00:35:03 +00:00
foreach(libxml_get_errors() as $err) {
logger('libxml: parse: ' . $err->code . ' at ' . $err->line
. ':' . $err->column . ' : ' . $err->message, LOGGER_DATA);
}
libxml_clear_errors();
}
return $x;
2013-02-26 23:49:37 +00:00
}
2011-08-18 11:20:30 +00:00
function sxml2array ( $xmlObject, $out = array () )
{
foreach ( (array) $xmlObject as $index => $node )
$out[$index] = ( is_object ( $node ) ) ? sxml2array ( $node ) : $node;
return $out;
}
/**
* @brief xml2[] will convert the given XML text to an array in the XML structure.
*
* Link: http://www.bin-co.com/php/scripts/xml2array/
2018-04-28 21:13:56 +00:00
* Portions significantly re-written by mike@macgirvin.com
* (namespaces, lowercase tags, get_attribute default changed, more...)
*
* Examples: $array = xml2array(file_get_contents('feed.xml'));
* $array = xml2array(file_get_contents('feed.xml', true, 1, 'attribute'));
*
* @param string $contents The XML text
* @param boolean $namespaces true or false include namespace information in the returned array as array elements
* @param int $get_attributes 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value.
* @param string $priority Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance.
*
* @return array The parsed XML in an array form. Use print_r() to see the resulting array structure.
*/
function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') {
if(!$contents)
return [];
if(!function_exists('xml_parser_create')) {
logger('xml2array: parser function missing');
return [];
}
libxml_use_internal_errors(true);
libxml_clear_errors();
if($namespaces)
$parser = @xml_parser_create_ns("UTF-8",':');
else
$parser = @xml_parser_create();
if(! $parser) {
logger('xml2array: xml_parser_create: no resource');
return [];
}
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
// http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
@xml_parse_into_struct($parser, trim($contents), $xml_values);
@xml_parser_free($parser);
if(! $xml_values) {
logger('xml2array: libxml: parse error: ' . $contents, LOGGER_DATA);
foreach(libxml_get_errors() as $err)
logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA);
libxml_clear_errors();
return;
}
//Initializations
$xml_array = [];
$parents = [];
$opened_tags = [];
$arr = [];
$current = &$xml_array; // Reference
// Go through the tags.
$repeated_tag_index = []; // Multiple tags with same name will be turned into an array
foreach($xml_values as $data) {
unset($attributes,$value); // Remove existing values, or there will be trouble
// This command will extract these variables into the foreach scope
// tag(string), type(string), level(int), attributes(array).
extract($data); // We could use the array by itself, but this cooler.
$result = [];
$attributes_data = [];
if(isset($value)) {
if($priority == 'tag') $result = $value;
else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
}
//Set the attributes too.
if(isset($attributes) and $get_attributes) {
foreach($attributes as $attr => $val) {
if($priority == 'tag') $attributes_data[$attr] = $val;
else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
}
}
// See tag status and do the needed.
if($namespaces && strpos($tag,':')) {
$namespc = substr($tag,0,strrpos($tag,':'));
$tag = strtolower(substr($tag,strlen($namespc)+1));
$result['@namespace'] = $namespc;
}
$tag = strtolower($tag);
if($type == "open") { // The starting of the tag '<tag>'
$parent[$level-1] = &$current;
if(!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag
$current[$tag] = $result;
if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
$repeated_tag_index[$tag.'_'.$level] = 1;
$current = &$current[$tag];
} else { // There was another element with the same tag name
if(isset($current[$tag][0])) { // If there is a 0th element it is already an array
$current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
$repeated_tag_index[$tag.'_'.$level]++;
} else { // This section will make the value an array if multiple tags with the same name appear together
$current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array
$repeated_tag_index[$tag.'_'.$level] = 2;
if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
$current[$tag]['0_attr'] = $current[$tag.'_attr'];
unset($current[$tag.'_attr']);
}
}
$last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
$current = &$current[$tag][$last_item_index];
}
} elseif($type == "complete") { // Tags that ends in 1 line '<tag />'
//See if the key is already taken.
if(!isset($current[$tag])) { //New Key
$current[$tag] = $result;
$repeated_tag_index[$tag.'_'.$level] = 1;
if($priority == 'tag' and $attributes_data)
$current[$tag. '_attr'] = $attributes_data;
} else { // If taken, put all things inside a list(array)
if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
// ...push the new element into that array.
$current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
if($priority == 'tag' and $get_attributes and $attributes_data) {
$current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
}
$repeated_tag_index[$tag.'_'.$level]++;
} else { // If it is not an array...
$current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
$repeated_tag_index[$tag.'_'.$level] = 1;
if($priority == 'tag' and $get_attributes) {
if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
$current[$tag]['0_attr'] = $current[$tag.'_attr'];
unset($current[$tag.'_attr']);
}
if($attributes_data) {
$current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
}
}
$repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 indexes are already taken
}
}
} elseif($type == 'close') { // End of tag '</tag>'
$current = &$parent[$level-1];
}
}
return($xml_array);
}
2012-10-23 02:46:18 +00:00
function email_header_encode($in_str, $charset = 'UTF-8', $header = 'Subject') {
$out_str = $in_str;
2012-10-23 02:46:18 +00:00
$need_to_convert = false;
for($x = 0; $x < strlen($in_str); $x ++) {
if((ord($in_str[$x]) == 0) || ((ord($in_str[$x]) > 128))) {
$need_to_convert = true;
break;
2012-10-23 02:46:18 +00:00
}
}
if(! $need_to_convert)
return $in_str;
if ($out_str && $charset) {
// define start delimimter, end delimiter and spacer
$end = "?=";
$start = "=?" . $charset . "?B?";
2019-03-17 23:32:21 +00:00
$spacer = $end . PHP_EOL . " " . $start;
// determine length of encoded text within chunks
// and ensure length is even
$length = 75 - strlen($start) - strlen($end) - (strlen($header) + 2);
/*
[EDIT BY danbrown AT php DOT net: The following
is a bugfix provided by (gardan AT gmx DOT de)
on 31-MAR-2005 with the following note:
"This means: $length should not be even,
but divisible by 4. The reason is that in
base64-encoding 3 8-bit-chars are represented
by 4 6-bit-chars. These 4 chars must not be
split between two encoded words, according
to RFC-2047.
*/
$length = $length - ($length % 4);
// encode the string and split it into chunks
// with spacers after each chunk
$out_str = base64_encode($out_str);
$out_str = chunk_split($out_str, $length, $spacer);
// remove trailing spacer and
// add start and end delimiters
$spacer = preg_quote($spacer,'/');
$out_str = preg_replace("/" . $spacer . "$/", "", $out_str);
$out_str = $start . $out_str . $end;
}
return $out_str;
2012-10-23 02:46:18 +00:00
}
/**
* @brief
*
* @param string $webbie
* @param string $protocol (optional) default empty
2020-07-16 02:28:01 +00:00
* @param boolean $verify (optional) default true, verify HTTP signatures on Zot discovery packets.
* @return boolean
*/
2020-07-16 02:28:01 +00:00
function discover_by_webbie($webbie, $protocol = '', $verify = true) {
2014-08-20 02:38:42 +00:00
2017-03-09 19:51:21 +00:00
$result = [];
2016-03-23 02:58:59 +00:00
$network = null;
2019-07-19 03:38:36 +00:00
$x = Webfinger::exec($webbie);
2018-08-23 06:04:37 +00:00
$address = EMPTY_STR;
if($x && array_key_exists('subject',$x) && strpos($x['subject'],'acct:') === 0)
$address = str_replace('acct:','',$x['subject']);
if($x && array_key_exists('aliases',$x) && count($x['aliases'])) {
foreach($x['aliases'] as $a) {
if(strpos($a,'acct:') === 0) {
$address = str_replace('acct:','',$a);
break;
}
}
}
2020-07-22 23:40:58 +00:00
if($x && array_key_exists('links',$x) && is_array($x['links'])) {
2014-08-20 02:38:42 +00:00
foreach($x['links'] as $link) {
if(array_key_exists('rel',$link)) {
2016-03-23 02:58:59 +00:00
2018-08-23 06:04:37 +00:00
$apurl = null;
2016-03-23 02:58:59 +00:00
// If we discover zot - don't search further; grab the info and get out of
// here.
2016-03-23 02:58:59 +00:00
2018-08-03 23:03:16 +00:00
if($link['rel'] === PROTOCOL_ZOT6 && ((! $protocol) || (strtolower($protocol) === 'zot6'))) {
logger('zot6 found for ' . $webbie, LOGGER_DEBUG);
$record = Zotfinger::exec($link['href'], null, $verify);
2018-08-03 23:03:16 +00:00
// Check the HTTP signature
2020-07-16 02:28:01 +00:00
if ($verify) {
2018-08-03 23:03:16 +00:00
2020-07-16 02:28:01 +00:00
$hsig = $record['signature'];
if($hsig && ($hsig['signer'] === $url || $hsig['signer'] === $link['href']) && $hsig['header_valid'] === true && $hsig['content_valid'] === true) {
$hsig_valid = true;
}
if(! $hsig_valid) {
logger('http signature not valid: ' . print_r($hsig,true));
continue;
}
}
2018-08-03 23:03:16 +00:00
$x = Libzot::import_xchan($record['data']);
if($x['success']) {
2018-09-15 23:13:44 +00:00
return $x['hash'];
}
}
2018-08-29 01:37:21 +00:00
if($link['rel'] === 'self' && ($link['type'] === 'application/activity+json' || strpos($link['type'],'ld+json') !== false) && ((! $protocol) || (strtolower($protocol) === 'activitypub'))) {
2018-09-17 04:42:41 +00:00
$ap = ActivityPub::discover($link['href']);
if($ap) {
return $ap;
2018-08-23 06:04:37 +00:00
}
}
2014-08-20 02:38:42 +00:00
}
}
}
2018-09-17 05:05:04 +00:00
if(strpos($webbie,'http') === 0) {
$ap = ActivityPub::discover($webbie);
2018-09-17 04:42:41 +00:00
if($ap) {
return $ap;
}
}
2016-03-23 02:58:59 +00:00
logger('webfinger: ' . print_r($x,true), LOGGER_DATA, LOG_INFO);
$arr = [
'address' => $webbie,
'protocol' => $protocol,
'success' => false,
'xchan' => '',
'webfinger' => $x
];
/**
* @hooks discover_channel_webfinger
* Called when performing a webfinger lookup.
* * \e string \b address - The webbie
* * \e string \b protocol
* * \e array \b webfinger - The result from webfinger_rfc7033()
* * \e boolean \b success - The return value, default false
*/
call_hooks('discover_channel_webfinger', $arr);
if($arr['success'])
return $arr['xchan'];
2016-03-23 02:58:59 +00:00
return false;
2017-03-09 19:51:21 +00:00
}
2016-03-23 02:58:59 +00:00
/**
* @brief Fetch and return a webfinger for a webbie.
2019-07-19 03:38:36 +00:00
* No longer used - see Zotlabs/Lib/Webfinger.php
*
2018-05-18 10:09:38 +00:00
* @param string $webbie - The resource
* @return boolean|string false or associative array from result JSON
*/
2018-05-18 10:09:38 +00:00
function webfinger_rfc7033($webbie) {
2014-08-20 02:38:42 +00:00
if(strpos($webbie,'@')) {
$lhs = substr($webbie,0,strpos($webbie,'@'));
$rhs = substr($webbie,strpos($webbie,'@')+1);
$resource = urlencode('acct:' . $webbie);
}
else {
$m = parse_url($webbie);
if($m) {
if($m['scheme'] !== 'https')
return false;
2021-03-09 23:36:07 +00:00
$rhs = $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : '');
$resource = urlencode($webbie);
}
else
return false;
}
2016-03-15 05:14:17 +00:00
logger('fetching url from resource: ' . $rhs . ':' . $webbie);
2014-08-20 02:38:42 +00:00
$counter = 0;
$s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''),
false, $counter, [ 'headers' => [ 'Accept: application/jrd+json, application/json, */*' ] ]);
2014-08-20 02:38:42 +00:00
if($s['success']) {
$j = json_decode($s['body'], true);
2017-03-09 19:51:21 +00:00
return($j);
}
2017-03-09 19:51:21 +00:00
return false;
}
2017-11-02 22:43:14 +00:00
function do_delivery($deliveries, $force = false) {
// $force is set if a site that wasn't responding suddenly returns to life.
// Try and shove through everything going to that site while it's responding.
2015-06-29 23:56:18 +00:00
if(! (is_array($deliveries) && count($deliveries)))
return;
2015-06-29 23:56:18 +00:00
$x = q("select count(outq_hash) as total from outq where outq_delivered = 0");
2018-07-03 05:43:41 +00:00
if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',3000)) && (! $force)) {
logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO);
foreach($deliveries as $d) {
2018-11-12 01:44:01 +00:00
Queue::update($d);
}
return;
}
$interval = ((get_config('system','delivery_interval') !== false)
? intval(get_config('system','delivery_interval')) : 2 );
2015-06-29 23:56:18 +00:00
$deliveries_per_process = intval(get_config('system','delivery_batch_count'));
2015-06-29 23:56:18 +00:00
if($deliveries_per_process <= 0)
$deliveries_per_process = 1;
2015-06-29 23:56:18 +00:00
$deliver = [];
foreach($deliveries as $d) {
2015-06-29 23:56:18 +00:00
2016-05-20 03:48:40 +00:00
if(! $d)
continue;
$deliver[] = $d;
if(count($deliver) >= $deliveries_per_process) {
2021-04-13 03:49:58 +00:00
Run::Summon( [ 'Deliver',$deliver ] );
$deliver = [];
if($interval)
@time_sleep_until(microtime(true) + (float) $interval);
}
}
// catch any stragglers
2018-11-12 01:44:01 +00:00
if($deliver) {
2021-04-13 03:49:58 +00:00
Run::Summon( [ 'Deliver',$deliver ] );
2018-11-12 01:44:01 +00:00
}
2015-02-09 02:31:51 +00:00
}
function get_site_info() {
$register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN');
2016-02-12 22:02:50 +00:00
$directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE');
$sql_extra = '';
2018-08-02 08:56:34 +00:00
$r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 ) > 0 and account_default_channel = channel_id");
if($r) {
$admin = [];
foreach($r as $rr) {
if($rr['channel_pageflags'] & PAGE_HUBADMIN)
$admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
}
if(! $admin) {
foreach($r as $rr) {
$admin[] = array( 'name' => $rr['channel_name'], 'address' => channel_reddress($rr), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
}
}
}
else {
$admin = false;
}
$def_service_class = get_config('system','default_service_class');
if($def_service_class)
$service_class = get_config('service_class',$def_service_class);
else
$service_class = false;
2016-06-21 03:34:19 +00:00
$visible_plugins = visible_plugin_list();
if(@is_dir('.git') && function_exists('shell_exec'))
$commit = trim(@shell_exec('git log -1 --format="%h"'));
if(! isset($commit) || strlen($commit) > 16)
$commit = '';
$site_info = get_config('system','info');
$site_name = get_config('system','sitename');
if(! get_config('system','hidden_version_siteinfo')) {
2018-11-12 01:44:01 +00:00
$version = System::get_project_version();
if(@is_dir('.git') && function_exists('shell_exec')) {
$commit = trim( @shell_exec('git log -1 --format="%h"'));
}
2016-01-18 00:29:32 +00:00
if(! isset($commit) || strlen($commit) > 16)
$commit = '';
}
else {
$version = $commit = '';
}
//Statistics
2018-08-02 08:56:34 +00:00
// $channels_total_stat = intval(get_config('system','channels_total_stat'));
// $channels_active_halfyear_stat = intval(get_config('system','channels_active_halfyear_stat'));
// $channels_active_monthly_stat = intval(get_config('system','channels_active_monthly_stat'));
// $local_posts_stat = intval(get_config('system','local_posts_stat'));
// $local_comments_stat = intval(get_config('system','local_comments_stat'));
// $hide_in_statistics = intval(get_config('system','hide_in_statistics'));
$site_expire = intval(get_config('system', 'default_expire_days'));
load_config('feature_lock');
$locked_features = [];
2016-03-31 23:06:03 +00:00
if(is_array(App::$config['feature_lock']) && count(App::$config['feature_lock'])) {
foreach(App::$config['feature_lock'] as $k => $v) {
if($k === 'config_loaded')
continue;
$locked_features[$k] = intval($v);
}
}
2019-10-05 21:42:38 +00:00
$protocols = [ 'zot' ];
if (get_config('system','activitypub', ACTIVITYPUB_ENABLED)) {
2019-10-05 21:42:38 +00:00
$protocols[] = 'activitypub';
}
2017-03-13 03:54:48 +00:00
$data = [
'url' => z_root(),
2018-11-12 01:44:01 +00:00
'platform' => System::get_platform_name(),
2017-03-13 03:54:48 +00:00
'site_name' => (($site_name) ? $site_name : ''),
'version' => $version,
2020-12-22 23:30:52 +00:00
// 'version_tag' => $tag,
2019-08-16 06:53:02 +00:00
'addon_version' => defined('ADDON_VERSION') ? ADDON_VERSION : 'unknown',
2018-11-12 01:44:01 +00:00
'server_role' => System::get_server_role(),
2017-03-13 03:54:48 +00:00
'commit' => $commit,
2019-10-05 21:42:38 +00:00
'protocols' => $protocols,
2017-03-13 03:54:48 +00:00
'plugins' => $visible_plugins,
'register_policy' => $register_policy[get_config('system','register_policy')],
'invitation_only' => (bool) intval(get_config('system','invitation_only')),
2017-03-13 03:54:48 +00:00
'directory_mode' => $directory_mode[get_config('system','directory_mode')],
2020-12-22 23:30:52 +00:00
// 'directory_server' => get_config('system','directory_server'),
2017-03-13 03:54:48 +00:00
'language' => get_config('system','language'),
// 'rss_connections' => (bool) intval(get_config('system','feed_contacts')),
2017-03-13 03:54:48 +00:00
'expiration' => $site_expire,
'default_service_restrictions' => $service_class,
2017-03-13 03:54:48 +00:00
'locked_features' => $locked_features,
'admin' => $admin,
2018-01-23 04:07:08 +00:00
'dbdriver' => DBA::$dba->getdriver() . ' ' . ((ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? 'postgres' : 'mysql'),
2017-03-13 03:54:48 +00:00
'lastpoll' => get_config('system','lastpoll'),
2018-12-13 22:28:38 +00:00
'ebs' => System::ebs(),
2018-08-02 08:56:34 +00:00
'info' => (($site_info) ? $site_info : '')
2017-03-13 03:54:48 +00:00
];
return $data;
}
/**
* @brief
*
* @param string $url
* @return boolean
*/
function check_siteallowed($url) {
$retvalue = true;
2019-09-11 02:31:08 +00:00
$arr = [ 'url' => $url ];
/**
* @hooks check_siteallowed
* Used to over-ride or bypass the site black/white block lists.
* * \e string \b url
* * \e boolean \b allowed - optional return value set in hook
*/
call_hooks('check_siteallowed', $arr);
2019-09-11 02:31:08 +00:00
if (array_key_exists('allowed',$arr)) {
return $arr['allowed'];
2019-09-11 02:31:08 +00:00
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','allowed_sites');
2019-09-11 02:31:08 +00:00
if (is_array($bl1) && $bl1) {
foreach ($bl1 as $bl) {
if ($bl === '*') {
$retvalue = true;
2019-09-11 02:31:08 +00:00
}
if ($bl && strpos($url,$bl) !== false)
return true;
}
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','denied_sites');
2019-09-11 02:31:08 +00:00
if (is_array($bl1) && $bl1) {
foreach ($bl1 as $bl) {
if ($bl === '*') {
$retvalue = false;
2019-09-11 02:31:08 +00:00
}
if ($bl && strpos($url,$bl) !== false) {
return false;
}
}
}
return $retvalue;
}
/**
* @brief
*
* @param string $url
* @return boolean
*/
function check_pubstream_siteallowed($url) {
$retvalue = true;
$arr = array('url' => $url);
/**
* @hooks check_siteallowed
* Used to over-ride or bypass the site black/white block lists.
* * \e string \b url
* * \e boolean \b allowed - optional return value set in hook
*/
call_hooks('pubstream_check_siteallowed', $arr);
if(array_key_exists('allowed',$arr))
return $arr['allowed'];
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','pubstream_allowed_sites');
if(is_array($bl1) && $bl1) {
foreach($bl1 as $bl) {
if($bl === '*')
$retvalue = true;
if($bl && strpos($url,$bl) !== false)
return true;
}
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','pubstream_denied_sites');
if(is_array($bl1) && $bl1) {
foreach($bl1 as $bl) {
if($bl === '*')
$retvalue = false;
if($bl && strpos($url,$bl) !== false) {
return false;
}
}
}
return $retvalue;
}
/**
* @brief
*
* @param string $hash
* @return boolean
*/
function check_channelallowed($hash) {
$retvalue = true;
2019-09-11 02:31:08 +00:00
$arr = [ 'hash' => $hash ];
/**
* @hooks check_channelallowed
* Used to over-ride or bypass the channel black/white block lists.
* * \e string \b hash
* * \e boolean \b allowed - optional return value set in hook
*/
call_hooks('check_channelallowed', $arr);
2019-09-11 02:31:08 +00:00
if (array_key_exists('allowed',$arr)) {
return $arr['allowed'];
2019-09-11 02:31:08 +00:00
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','allowed_channels');
2019-09-11 02:31:08 +00:00
if (is_array($bl1) && $bl1) {
foreach ($bl1 as $bl) {
if ($bl === '*') {
$retvalue = true;
2019-09-11 02:31:08 +00:00
}
if ($bl && strpos($hash,$bl) !== false) {
return true;
2019-09-11 02:31:08 +00:00
}
}
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','denied_channels');
2019-09-11 02:31:08 +00:00
if (is_array($bl1) && $bl1) {
foreach ($bl1 as $bl) {
if ($bl === '*') {
$retvalue = false;
2019-09-11 02:31:08 +00:00
}
if ($bl && strpos($hash,$bl) !== false) {
return false;
}
}
}
return $retvalue;
}
/**
* @brief
*
* @param string $hash
* @return boolean
*/
function check_pubstream_channelallowed($hash) {
$retvalue = true;
$arr = array('hash' => $hash);
/**
* @hooks check_channelallowed
* Used to over-ride or bypass the channel black/white block lists.
* * \e string \b hash
* * \e boolean \b allowed - optional return value set in hook
*/
call_hooks('check_pubstream_channelallowed', $arr);
if(array_key_exists('allowed',$arr))
return $arr['allowed'];
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','pubstream_allowed_channels');
if(is_array($bl1) && $bl1) {
foreach($bl1 as $bl) {
if($bl === '*')
$retvalue = true;
if($bl && strpos($hash,$bl) !== false)
return true;
}
}
2020-06-14 11:58:27 +00:00
$bl1 = get_config('system','pubstream_denied_channels');
if(is_array($bl1) && $bl1) {
foreach($bl1 as $bl) {
if($bl === '*')
$retvalue = false;
if($bl && strpos($hash,$bl) !== false) {
return false;
}
}
}
return $retvalue;
}
function deliverable_singleton($channel_id,$xchan) {
if(array_key_exists('xchan_hash',$xchan))
$xchan_hash = $xchan['xchan_hash'];
elseif(array_key_exists('hubloc_hash',$xchan))
$xchan_hash = $xchan['hubloc_hash'];
else
return true;
$r = q("select abook_instance from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval($channel_id),
dbesc($xchan_hash)
2015-12-10 02:30:30 +00:00
);
if($r) {
if(! $r[0]['abook_instance'])
return true;
if(strpos($r[0]['abook_instance'],z_root()) !== false)
return true;
}
return false;
}
2020-06-16 03:54:54 +00:00
function get_repository_version($branch = 'release') {
2020-10-16 10:42:55 +00:00
$path = "https://codeberg.org/zot/" . ((PLATFORM_NAME === 'mistpark') ? 'misty' : PLATFORM_NAME) . "/raw/$branch/boot.php";
$x = z_fetch_url($path);
if($x['success']) {
$y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/',$x['body'],$matches);
if($y)
return $matches[3];
}
return '?.?';
}
/**
* @brief Get translated network name.
*
* @param string $s Network string, see boot.php
* @return string Translated name of the network
*/
function network_to_name($s) {
$nets = array(
NETWORK_DFRN => t('Friendica'),
NETWORK_FRND => t('Friendica'),
NETWORK_OSTATUS => t('OStatus'),
NETWORK_GNUSOCIAL => t('GNU-Social'),
NETWORK_FEED => t('RSS/Atom'),
NETWORK_ACTIVITYPUB => t('ActivityPub'),
NETWORK_MAIL => t('Email'),
NETWORK_DIASPORA => t('Diaspora'),
NETWORK_FACEBOOK => t('Facebook'),
NETWORK_ZOT => t('Zot'),
NETWORK_LINKEDIN => t('LinkedIn'),
NETWORK_XMPP => t('XMPP/IM'),
NETWORK_MYSPACE => t('MySpace'),
);
/**
* @hooks network_to_name
* @deprecated
*/
call_hooks('network_to_name', $nets);
$search = array_keys($nets);
$replace = array_values($nets);
return str_replace($search, $replace, $s);
}
/**
* @brief Send a text email message.
*
* @param array $params an associative array with:
* * \e string \b fromName name of the sender
* * \e string \b fromEmail email of the sender
* * \e string \b replyTo replyTo address to direct responses
* * \e string \b toEmail destination email address
* * \e string \b messageSubject subject of the message
* * \e string \b htmlVersion html version of the message
* * \e string \b textVersion text only version of the message
* * \e string \b additionalMailHeader additions to the smtp mail header
*/
function z_mail($params) {
if(! $params['fromEmail']) {
$params['fromEmail'] = get_config('system','from_email');
if(! $params['fromEmail'])
$params['fromEmail'] = 'Administrator' . '@' . App::get_hostname();
}
if(! $params['fromName']) {
$params['fromName'] = get_config('system','from_email_name');
if(! $params['fromName'])
2018-11-12 01:44:01 +00:00
$params['fromName'] = System::get_site_name();
}
if(! $params['replyTo']) {
$params['replyTo'] = get_config('system','reply_address');
if(! $params['replyTo'])
$params['replyTo'] = 'noreply' . '@' . App::get_hostname();
}
$params['sent'] = false;
$params['result'] = false;
/**
* @hooks email_send
* * \e params @see z_mail()
*/
2016-10-01 22:15:14 +00:00
call_hooks('email_send', $params);
if($params['sent']) {
logger('notification: z_mail returns ' . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG);
return $params['result'];
}
$fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8');
$messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8');
$messageHeader =
$params['additionalMailHeader'] .
2019-03-17 23:34:54 +00:00
2019-03-17 23:32:21 +00:00
"From: $fromName <{$params['fromEmail']}>" .PHP_EOL .
"Reply-To: $fromName <{$params['replyTo']}>" . PHP_EOL .
"Content-Type: text/plain; charset=UTF-8";
// send the message
$res = mail(
$params['toEmail'], // send to address
$messageSubject, // subject
$params['textVersion'],
$messageHeader // message headers
);
logger('notification: z_mail returns ' . (($res) ? 'success' : 'failure'), LOGGER_DEBUG);
return $res;
}
/**
* @brief Discover the best API path available for redmatrix/hubzilla servers.
*
* @param string $host
* @return string
*/
function probe_api_path($host) {
$schemes = ['https', 'http' ];
$paths = ['/api/z/1.0/version', '/api/red/version' ];
foreach($schemes as $scheme) {
foreach($paths as $path) {
$curpath = $scheme . '://' . $host . $path;
$x = z_fetch_url($curpath);
2018-03-31 20:22:12 +00:00
if($x['success'] && ! strpos($x['body'], 'not implemented'))
return str_replace('version', '', $curpath);
}
}
return '';
}
2017-05-05 04:55:56 +00:00
function service_plink($contact, $guid) {
$plink = '';
$m = parse_url($contact['xchan_url']);
if($m) {
2021-03-09 23:36:07 +00:00
$url = $m['scheme'] . '://' . $m['host'] . ((isset($m['port']) && intval($m['port'])) ? ':' . intval($m['port']) : '');
2017-05-05 04:55:56 +00:00
}
else {
$url = 'https://' . substr($contact['xchan_addr'],strpos($contact['xchan_addr'],'@')+1);
}
$handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'],'@'));
$plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid;
2017-07-05 10:32:46 +00:00
$x = [ 'xchan' => $contact, 'guid' => $guid, 'url' => $url, 'plink' => $plink ];
/**
* @hooks service_plink
* * \e array \b xchan
* * \e string \b guid
* * \e string \b url
* * \e string \b plink will get returned
*/
2017-05-05 04:55:56 +00:00
call_hooks('service_plink', $x);
return $x['plink'];
}
/**
* @brief
*
* @param array $mimeTypes
* @param string $acceptedTypes by default false will use $_SERVER['HTTP_ACCEPT']
* @return array|NULL
*/
function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) {
// Values will be stored in this array
$AcceptTypes = [];
2021-03-12 01:17:40 +00:00
if($acceptedTypes === false) {
$acceptedTypes = ((isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : EMPTY_STR);
}
// Accept header is case insensitive, and whitespace isnt important
$accept = strtolower(str_replace(' ', '', $acceptedTypes));
// divide it into parts in the place of a ","
$accept = explode(',', $accept);
foreach ($accept as $a) {
// the default quality is 1.
$q = 1;
// check if there is a different quality
if (strpos($a, ';q=')) {
// divide "mime/type;q=X" into two parts: "mime/type" i "X"
list($a, $q) = explode(';q=', $a);
}
// mime-type $a is accepted with the quality $q
// WARNING: $q == 0 means, that mime-type isnt supported!
$AcceptTypes[$a] = $q;
}
arsort($AcceptTypes);
2017-09-20 02:15:15 +00:00
// if no parameter was passed, just return parsed data
if (!$mimeTypes) return $AcceptTypes;
2017-09-20 02:15:15 +00:00
$mimeTypes = array_map('strtolower', (array)$mimeTypes);
2017-09-20 02:15:15 +00:00
// lets check our supported types:
foreach ($AcceptTypes as $mime => $q) {
if ($q && in_array($mime, $mimeTypes)) return $mime;
}
// no mime-type found
return null;
}
/**
* @brief Perform caching for jsonld normaliser.
*
* @param string $url
* @return mixed|boolean|array
*/
function jsonld_document_loader($url) {
2017-09-20 02:15:15 +00:00
require_once('library/jsonld/jsonld.php');
$recursion = 0;
$x = debug_backtrace();
if($x) {
foreach($x as $n) {
if($n['function'] === __FUNCTION__) {
$recursion ++;
}
}
}
if($recursion > 5) {
logger('jsonld bomb detected at: ' . $url);
killme();
}
2020-03-04 02:24:26 +00:00
$cachepath = 'cache/ldcache';
2017-09-20 02:15:15 +00:00
if(! is_dir($cachepath))
os_mkdir($cachepath, STORAGE_DEFAULT_PERMISSIONS, true);
2017-09-20 02:15:15 +00:00
$filename = $cachepath . '/' . urlencode($url);
if(file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) {
return json_decode(file_get_contents($filename));
}
$r = jsonld_default_document_loader($url);
if($r) {
file_put_contents($filename, json_encode($r));
2017-09-20 02:15:15 +00:00
return $r;
}
logger('not found');
if(file_exists($filename)) {
return json_decode(file_get_contents($filename));
}
return [];
2018-04-23 02:49:26 +00:00
}
2018-05-23 06:20:29 +00:00
function is_https_request() {
2018-05-23 06:20:29 +00:00
$https = false;
if (array_key_exists('HTTPS',$_SERVER) && $_SERVER['HTTPS']) {
2018-05-23 06:20:29 +00:00
$https = true;
}
elseif (array_key_exists('SERVER_PORT',$_SERVER) && intval($_SERVER['SERVER_PORT']) === 443) {
2018-05-23 06:20:29 +00:00
$https = true;
}
elseif (array_key_exists('HTTP_X_FORWARDED_PROTO',$_SERVER) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$https = true; // technically this is not true, but pretending it is will allow reverse proxies to work
}
2018-05-23 06:20:29 +00:00
return $https;
}
2018-10-11 00:58:51 +00:00
/**
* @brief Given a URL, return everything after the host portion.
* example https://foobar.com/gravy?g=5&y=6
* returns /gravy?g=5&y=6
* result always returns the leading slash
*/
function get_request_string($url) {
$a = explode('/',$url,4);
return '/' . ((count($a) > 3) ? $a[3] : EMPTY_STR);
}