diff options
Diffstat (limited to 'include/network.php')
-rw-r--r-- | include/network.php | 1319 |
1 files changed, 476 insertions, 843 deletions
diff --git a/include/network.php b/include/network.php index 66716ef9e..7e2dbf4cf 100644 --- a/include/network.php +++ b/include/network.php @@ -22,8 +22,8 @@ function get_capath() { * @param int $redirects default 0 * internal use, recursion counter * @param array $opts (optional parameters) associative array with: - * * \b accept_content => supply Accept: header with 'accept_content' as the value * * \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 @@ -31,6 +31,7 @@ function get_capath() { * * \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 * * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure @@ -74,8 +75,21 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { if(x($opts,'readfunc')) @curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']); - if(x($opts,'headers')) - @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); + // 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); + if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); @@ -91,6 +105,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } + if(x($opts,'http_auth')) { // "username" . ':' . "password" @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); @@ -186,7 +201,6 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { * @param int $redirects = 0 * internal use, recursion counter * @param array $opts (optional parameters) - * 'accept_content' => supply Accept: header with 'accept_content' as the value * '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 @@ -229,9 +243,16 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_HEADER, false); } - if(x($opts,'headers')) { - @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); + $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']); @@ -322,7 +343,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if (isset($url_parsed)) { curl_close($ch); if($http_code == 303) { - return z_fetch_url($newurl,false,$redirects++,$opts); + return z_fetch_url($newurl,false,++$redirects,$opts); } else { return z_post_url($newurl,$params,++$redirects,$opts); } @@ -376,36 +397,13 @@ function json_return_and_die($x, $content_type = 'application/json') { killme(); } - - -// Generic XML return -// Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable -// of $st and an optional text <message> of $message and terminates the current process. - - -function xml_status($st, $message = '') { - - $xml_message = ((strlen($message)) ? "\t<message>" . xmlify($message) . "</message>\r\n" : ''); - - if($st) - logger('xml_status returning non_zero: ' . $st . " message=" . $message); - - header( "Content-type: text/xml" ); - echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n"; - echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n"; - killme(); -} - - - /** - * @brief Send HTTP status header + * @brief Send HTTP status header. * * @param int $val * integer HTTP status result value * @param string $msg * optional message - * @returns nil */ function http_status($val, $msg = '') { if ($val >= 400) @@ -413,12 +411,11 @@ function http_status($val, $msg = '') { if ($val >= 200 && $val < 300) $msg = (($msg) ? $msg : 'OK'); - logger('http_status_exit ' . $val . ' ' . $msg); + logger(\App::$query_string . ':' . $val . ' ' . $msg); header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg); } - /** * @brief Send HTTP status header and exit. * @@ -426,58 +423,57 @@ function http_status($val, $msg = '') { * integer HTTP status result value * @param string $msg * optional message - * @returns (does not return, process is terminated) + * @return does not return, process is terminated */ function http_status_exit($val, $msg = '') { http_status($val, $msg); killme(); } - - -// convert an XML document to a normalised, case-corrected array -// used by webfinger - - +/** + * @brief convert an XML document to a normalised, case-corrected array used by webfinger. + * + * @param string|array|SimpleXMLElement $xml_element + * @param int $recursion_depth[in,out] + * @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 we're getting too deep, bail out + if ($recursion_depth > 512) { + return(null); + } - if (is_array($xml_element)) { - $result_array = array(); - if (count($xml_element) <= 0) { - return (trim(strval($xml_element_copy))); - } + 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); + } - foreach($xml_element as $key=>$value) { + if (is_array($xml_element)) { + $result_array = array(); + if (count($xml_element) <= 0) { + return (trim(strval($xml_element_copy))); + } - $recursion_depth++; - $result_array[strtolower($key)] = + 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))); + $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))); + } } @@ -491,7 +487,7 @@ function z_dns_check($h,$check_mx = 0) { if(is_array(\App::$config) && array_key_exists('system',\App::$config) && is_array(\App::$config['system']) - && array_key_exists('do_not_check_dns',\App::$config['system']) + && array_key_exists('do_not_check_dns',\App::$config['system']) && \App::$config['system']['do_not_check_dns']) return true; @@ -499,56 +495,71 @@ function z_dns_check($h,$check_mx = 0) { //$opts = DNS_A + DNS_CNAME + DNS_PTR; //if($check_mx) // $opts += DNS_MX; - // Specific record type flags are unreliable on FreeBSD and Mac, - // so now we'll ignore these and just check for the existence of any DNS record. + // Specific record type flags are unreliable on FreeBSD and Mac, + // so now we'll ignore these and just check for the existence of any DNS record. return((@dns_get_record($h) || filter_var($h, FILTER_VALIDATE_IP)) ? true : false); - } -// 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) -// return true if it's OK, false if something is wrong with it - - +/** + * @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 string $url[in,out] 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) + if(strpos($url, '.') === false && strpos($url, '/localhost/') === false) return false; - if(substr($url,0,4) != 'http') + + if(substr($url, 0, 4) != 'http') $url = 'http://' . $url; + $h = @parse_url($url); if(($h) && z_dns_check($h['host'])) { return true; } + return false; } -// checks that email is an actual resolvable internet address - - +/** + * @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')) + if(get_config('system', 'disable_email_validation')) return true; - if(! strpos($addr,'@')) + if(! strpos($addr, '@')) return false; - $h = substr($addr,strpos($addr,'@') + 1); - if(($h) && z_dns_check($h,true)) { + $h = substr($addr, strpos($addr, '@') + 1); + + if(($h) && z_dns_check($h, true)) { return true; } + return false; } -// Check $url against our list of allowed sites, -// wildcards allowed. If allowed_sites is unset return true; -// If url is allowed, return true. -// otherwise, return false - - +/** + * @brief Check $url against our list of allowed sites. + * + * Wildcards allowed. If allowed_sites is unset return true. + * + * @param string $url + * @return boolean Return true if url is allowed, otherwise return false + */ function allowed_url($url) { $h = @parse_url($url); @@ -557,7 +568,7 @@ function allowed_url($url) { return false; } - $str_allowed = get_config('system','allowed_sites'); + $str_allowed = get_config('system', 'allowed_sites'); if(! $str_allowed) return true; @@ -585,21 +596,23 @@ function allowed_url($url) { return $found; } -// check if email address is allowed to register here. -// Compare against our list (wildcards allowed). -// Returns false if not allowed, true if allowed or if -// allowed list is not configured. - - +/** + * @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)); + $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'); + $str_allowed = get_config('system', 'allowed_email'); + $str_not_allowed = get_config('system', 'not_allowed_email'); if(! $str_allowed && ! $str_not_allowed) return true; @@ -610,7 +623,7 @@ function allowed_email($email) { $fnmatch = function_exists('fnmatch'); - $allowed = explode(',',$str_allowed); + $allowed = explode(',', $str_allowed); if(count($allowed)) { foreach($allowed as $a) { @@ -622,7 +635,7 @@ function allowed_email($email) { } } - $not_allowed = explode(',',$str_not_allowed); + $not_allowed = explode(',', $str_not_allowed); if(count($not_allowed)) { foreach($not_allowed as $na) { @@ -639,6 +652,7 @@ function allowed_email($email) { } elseif (!$str_allowed && !$found_not_allowed) { $return = true; } + return $return; } @@ -648,19 +662,24 @@ 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); - if(! $x) { + if($x === false) { logger('libxml: parse: error: ' . $s2, LOGGER_DATA); - foreach(libxml_get_errors() as $err) - logger('libxml: parse: ' . $err->code." at ".$err->line.":".$err->column." : ".$err->message, 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 $x; } @@ -676,7 +695,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) require_once('include/photo/photo_driver.php'); foreach($matches as $mtch) { - logger('scale_external_image: ' . $mtch[2] . ' ' . $mtch[3]); + logger('data: ' . $mtch[2] . ' ' . $mtch[3]); if(substr($mtch[1],0,1) == '=') { $owidth = intval(substr($mtch[2],1)); @@ -727,12 +746,12 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) $ph->scaleImage(1024); $new_width = $ph->getWidth(); $new_height = $ph->getHeight(); - logger('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG); + logger('data: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG); $s = str_replace($mtch[0],'[' . $tag . '=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/' . $tag . ']' . "\n" . (($include_link) ? '[zrl=' . $mtch[2] . ']' . t('view full size') . '[/zrl]' . "\n" : ''),$s); - logger('scale_external_images: new string: ' . $s, LOGGER_DEBUG); + logger('new string: ' . $s, LOGGER_DEBUG); } } } @@ -747,27 +766,31 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) } /** - * xml2array() will convert the given XML text to an array in the XML structure. + * @brief xml2array() will convert the given XML text to an array in the XML structure. + * * Link: http://www.bin-co.com/php/scripts/xml2array/ - * Portions significantly re-written by mike@macgirvin.com for Friendica (namespaces, lowercase tags, get_attribute default changed, more...) - * Arguments : $contents - The XML text - * $namespaces - true or false include namespace information in the returned array as array elements. - * $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. - * $priority - Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance. - * Return: The parsed XML in an array form. Use print_r() to see the resulting array structure. + * Portions significantly re-written by mike@macgirvin.com for Friendica + * (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')); + * $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 array(); + if(!$contents) + return array(); if(!function_exists('xml_parser_create')) { logger('xml2array: parser function missing'); return array(); } - libxml_use_internal_errors(true); libxml_clear_errors(); @@ -793,6 +816,7 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = foreach(libxml_get_errors() as $err) logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA); libxml_clear_errors(); + return; } @@ -859,7 +883,6 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = $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]; @@ -870,7 +893,8 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 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; + 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... @@ -882,13 +906,11 @@ function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = $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']); } @@ -961,53 +983,26 @@ function email_header_encode($in_str, $charset = 'UTF-8') { return $out_str; } -function email_send($addr, $subject, $headers, $item) { - //$headers .= 'MIME-Version: 1.0' . "\n"; - //$headers .= 'Content-Type: text/html; charset=UTF-8' . "\n"; - //$headers .= 'Content-Type: text/plain; charset=UTF-8' . "\n"; - //$headers .= 'Content-Transfer-Encoding: 8bit' . "\n\n"; - - $part = uniqid("", true); - - $html = prepare_body($item); - - $headers .= "Mime-Version: 1.0\n"; - $headers .= 'Content-Type: multipart/alternative; boundary="=_'.$part.'"'."\n\n"; - - $body = "\n--=_".$part."\n"; - $body .= "Content-Transfer-Encoding: 8bit\n"; - $body .= "Content-Type: text/plain; charset=utf-8; format=flowed\n\n"; - - $body .= html2plain($html)."\n"; - - $body .= "--=_".$part."\n"; - $body .= "Content-Transfer-Encoding: 8bit\n"; - $body .= "Content-Type: text/html; charset=utf-8\n\n"; - - $body .= '<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">'.$html."</body></html>\n"; - - $body .= "--=_".$part."--"; - - //$message = '<html><body>' . $html . '</body></html>'; - //$message = html2plain($html); - logger('notifier: email delivery to ' . $addr); - mail($addr, $subject, $body, $headers); -} - - - -function discover_by_url($url,$arr = null) { - require_once('library/HTML5/Parser.php'); +/** + * @brief Creates an xchan entry for URL. + * + * @param string $url URL to discover + * @param array $arr fallback values if scrape_feed() is empty + * + * @return boolean + */ +function discover_by_url($url, $arr = null) { $x = scrape_feed($url); if(! $x) { if(! $arr) return false; + $network = (($arr['network']) ? $arr['network'] : 'unknown'); - $name = (($arr['name']) ? $arr['name'] : 'unknown'); - $photo = (($arr['photo']) ? $arr['photo'] : ''); - $addr = (($arr['addr']) ? $arr['addr'] : ''); - $guid = $url; + $name = (($arr['name']) ? $arr['name'] : 'unknown'); + $photo = (($arr['photo']) ? $arr['photo'] : ''); + $addr = (($arr['addr']) ? $arr['addr'] : ''); + $guid = $url; } $profile = $url; @@ -1025,27 +1020,26 @@ function discover_by_url($url,$arr = null) { // try and discover stuff from the feeed - require_once('library/simplepie/simplepie.inc'); $feed = new SimplePie(); $level = 0; - $x = z_fetch_url($guid,false,$level,array('novalidate' => true)); + $x = z_fetch_url($guid, false, $level, array('novalidate' => true)); if(! $x['success']) { - logger('probe_url: feed fetch failed for ' . $poll); + logger('Feed fetch failed for ' . $guid); return false; } $xml = $x['body']; - logger('probe_url: fetch feed: ' . $guid . ' returns: ' . $xml, LOGGER_DATA); - logger('probe_url: scrape_feed: headers: ' . $x['header'], LOGGER_DATA); + logger('Fetch feed: ' . $guid . ' returns: ' . $xml, LOGGER_DATA); + logger('scrape_feed: headers: ' . $x['header'], LOGGER_DATA); // Don't try and parse an empty string $feed->set_raw_data(($xml) ? $xml : '<?xml version="1.0" encoding="utf-8" ?><xml></xml>'); $feed->init(); if($feed->error()) - logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error()); + logger('scrape_feed: Error parsing XML: ' . $feed->error()); - $name = unxmlify(trim($feed->get_title())); - $photo = $feed->get_image_url(); + $name = unxmlify(trim($feed->get_title())); + $photo = $feed->get_image_url(); $author = $feed->get_author(); if($author) { @@ -1083,7 +1077,7 @@ function discover_by_url($url,$arr = null) { $profile = trim(unxmlify($author->get_link())); } if(! $photo) { - $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail'); + $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/', 'thumbnail'); if($rawmedia && $rawmedia[0]['attribs']['']['url']) $photo = unxmlify($rawmedia[0]['attribs']['']['url']); } @@ -1097,7 +1091,7 @@ function discover_by_url($url,$arr = null) { } } } - if($poll === $profile) + if($guid === $profile) $lnk = $feed->get_permalink(); if(isset($lnk) && strlen($lnk)) $profile = $lnk; @@ -1109,9 +1103,6 @@ function discover_by_url($url,$arr = null) { if(! $name) $name = notags($feed->get_description()); - if(! $guid) - return false; - $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($guid) ); @@ -1125,7 +1116,6 @@ function discover_by_url($url,$arr = null) { [ 'xchan_hash' => $guid, 'xchan_guid' => $guid, - 'xchan_pubkey' => $pubkey, 'xchan_addr' => $addr, 'xchan_url' => $profile, 'xchan_name' => $name, @@ -1143,30 +1133,17 @@ function discover_by_url($url,$arr = null) { dbesc($photos[3]), dbesc($guid) ); - return true; + return true; } +function discover_by_webbie($webbie,$protocol = '') { -function discover_by_webbie($webbie) { - require_once('library/HTML5/Parser.php'); - - $result = array(); + $result = []; $network = null; - $diaspora = false; - $gnusoc = false; - $dfrn = false; - - $has_salmon = false; - $salmon_key = false; - $atom_feed = false; - $diaspora_base = ''; - $diaspora_guid = ''; - $diaspora_key = ''; - - $webbie = strtolower($webbie); +// $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie,true); if($x && array_key_exists('links',$x) && $x['links']) { @@ -1176,7 +1153,7 @@ function discover_by_webbie($webbie) { // If we discover zot - don't search further; grab the info and get out of // here. - if($link['rel'] === PROTOCOL_ZOT) { + if($link['rel'] === PROTOCOL_ZOT && ((! $protocol) || (strtolower($protocol) === 'zot'))) { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); if(array_key_exists('zot',$x) && $x['zot']['success']) { $i = import_xchan($x['zot']); @@ -1191,317 +1168,23 @@ function discover_by_webbie($webbie) { } } } - if($link['rel'] == NAMESPACE_DFRN) { - $dfrn = $link['href']; - } - if($link['rel'] == 'magic-public-key') { - if(substr($link['href'],0,5) === 'data:') { - $salmon_key = convert_salmon_key($link['href']); - } - } - if($link['rel'] == 'salmon') { - $has_salmon = true; - $salmon = $link['href']; - } - if($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') { - $atom_feed = $link['href']; - } } } } logger('webfinger: ' . print_r($x,true), LOGGER_DATA, LOG_INFO); - $arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x); + $arr = array('address' => $webbie, 'protocol' => $protocol, 'success' => false, 'webfinger' => $x); call_hooks('discover_channel_webfinger', $arr); if($arr['success']) return true; - $aliases = array(); - - // Now let's make some decisions on what we may need - // to obtain further info - - $probe_atom = false; - $probe_old = false; - $probe_hcard = false; - - $address = ''; - $location = ''; - $nickname = ''; - $fullname = ''; - $avatar = ''; - $pubkey = ''; - - if(is_array($x)) { - if(array_key_exists('address',$x)) - $address = $x['address']; - if(array_key_exists('location',$x)) - $location = $x['location']; - if(array_key_exists('nickname',$x)) - $nickname = $x['nickname']; - } - - if(! $x) - $probe_old = true; - - - if((! $dfrn) && (! $has_salmon)) - $probe_old = true; - - if($probe_old) { - $y = old_webfinger($webbie); - if($y) { - logger('old_webfinger: ' . print_r($x,true)); - foreach($y as $link) { - if($link['@attributes']['rel'] === NAMESPACE_DFRN) - $dfrn = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'salmon') - $notify = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === NAMESPACE_FEED) - $poll = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') - $hcard = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') - $profile = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') - $poco = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { - $diaspora_base = unamp($link['@attributes']['href']); - $diaspora = true; - } - if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { - $diaspora_guid = unamp($link['@attributes']['href']); - $diaspora = true; - } - if($link['@attributes']['rel'] === 'diaspora-public-key') { - $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); - if(strstr($diaspora_key,'RSA ')) - $pubkey = rsatopem($diaspora_key); - else - $pubkey = $diaspora_key; - $diaspora = true; - } - if($link['@attributes']['rel'] == 'magic-public-key') { - if(substr($link['@attributes']['href'],0,5) === 'data:') { - $salmon_key = convert_salmon_key($link['@attributes']['href']); - } - } - if($link['@attributes']['rel'] == 'salmon') { - $has_salmon = true; - $salmon = $link['@attributes']['href']; - } - - if($link['@attributes']['rel'] == 'http://schemas.google.com/g/2010#updates-from') { - $atom_feed = $link['@attributes']['href']; - } - if($link['@attributes']['rel'] === 'alias') { - $aliases[] = $link['@attributes']['href']; - } - if($link['@attributes']['rel'] === 'subject') { - $subject = $link['@attributes']['href']; - } - } - } - } - - if($subject || $aliases) { - if(strpos($webbie,'@')) { - $rhs = substr($webbie,strpos($webbie,'@')+1); - } - else { - $m = parse_url($webbie); - if($m) { - $rhs = $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } - } - - $v = array('subject' => $subject,'aliases' => $aliases); - $address = find_webfinger_address($v,$rhs); - $location = find_webfinger_location($v,$rhs); - if($address) - $nickname = substr($address,0,strpos($address,'@')); - - } - - if($salmon_key && $has_salmon && $atom_feed && (! $dfrn) && (! $diaspora)) { - $gnusoc = true; - $probe_atom = true; - } - - if(! $pubkey) - $pubkey = $salmon_key; - - if(($dfrn || $diaspora) && $hcard) - $probe_hcard = true; - - if(! $fullname) - $fullname = $nickname; - - if($probe_atom) { - $k = z_fetch_url($atom_feed); - if($k['success']) - $feed_meta = feed_meta($k['body']); - if($feed_meta) { - - // stash any discovered pubsubhubbub hubs in case we need to follow them - // this will save an expensive lookup later - - if($feed_meta['hubs'] && $address) { - set_xconfig($address,'system','push_hubs',$feed_meta['hubs']); - set_xconfig($address,'system','feed_url',$atom_feed); - } - if($feed_meta['author']['author_name']) { - $fullname = $feed_meta['author']['author_name']; - } - if(! $avatar) { - if($feed_meta['author']['author_photo']) - $avatar = $feed_meta['author']['author_photo']; - } - - // for GNU-social over-ride any url aliases we may have picked up in webfinger - // The author.uri element in the feed is likely to be more accurate - - if($gnusoc && $feed_meta['author']['author_uri']) - $location = $feed_meta['author']['author_uri']; - } - } - else { - if($probe_hcard) { - $vcard = scrape_vcard($hcard); - if($vcard) { - logger('vcard: ' . print_r($vcard,true), LOGGER_DATA); - if($vcard['fn']) - $fullname = $vcard['fn']; - if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) - $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; - if(($vcard['public_key']) && (! $pubkey)) { - $diaspora_key = $vcard['public_key']; - if(strstr($diaspora_key,'RSA ')) - $pubkey = rsatopem($diaspora_key); - else - $pubkey = $diaspora_key; - } - if(! $avatar) - $avatar = $vcard['photo']; - if($diaspora) { - if(($vcard['uid']) && (! $diaspora_guid)) - $diaspora_guid = $vcard['uid']; - if(($vcard['url']) && (! $diaspora_base)) - $diaspora_base = $vcard['url']; - - - - - } - - } - } - } - - if(($profile) && (! $location)) - $location = $profile; - - if($location) { - $m = parse_url($location); - $base = $m['scheme'] . '://' . $m['host']; - $host = $m['host']; - } - - - if($diaspora && $diaspora_base && $diaspora_guid) { - if($dfrn) - $network = 'friendica-over-diaspora'; - else - $network = 'diaspora'; - - $base = trim($diaspora_base,'/'); - $notify = $base . '/receive'; - - } - else { - if($gnusoc) { - $network = 'gnusoc'; - $notify = $salmon; - } - } - - - logger('network: ' . $network); - logger('address: ' . $address); - logger('fullname: ' . $fullname); - logger('pubkey: ' . $pubkey); - logger('location: ' . $location); - - - - // if we have everything we need, let's create the records - - if($network && $address && $fullname && $pubkey && $location) { - $r = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($address) - ); - if($r) { - $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s'", - dbesc($fullname), - dbesc($network), - dbesc(datetime_convert()), - dbesc($address) - ); - } - else { - $r = xchan_store_lowlevel( - [ - 'xchan_hash' => $address, - 'xchan_guid' => (($diaspora_guid) ? $diaspora_guid : $location), - 'xchan_pubkey' => $pubkey, - 'xchan_addr' => $address, - 'xchan_url' => $location, - 'xchan_name' => $fullname, - 'xchan_name_date' => datetime_convert(), - 'xchan_network' => $network - ] - ); - } - - $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", - dbesc($address) - ); - - if(! $r) { - $r = hubloc_store_lowlevel( - [ - 'hubloc_guid' => (($diaspora_guid) ? $diaspora_guid : $location), - 'hubloc_hash' => $address, - 'hubloc_addr' => $address, - 'hubloc_network' => $network, - 'hubloc_url' => $base, - 'hubloc_host' => $host, - 'hubloc_callback' => $notify, - 'hubloc_updated' => datetime_convert(), - 'hubloc_primary' => 1 - ] - ); - } - $photos = import_xchan_photo($avatar,$address); - $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", - dbescdate(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($address) - ); - return true; - } return false; -} - +} function webfinger_rfc7033($webbie,$zot = false) { - if(strpos($webbie,'@')) { $lhs = substr($webbie,0,strpos($webbie,'@')); $rhs = substr($webbie,strpos($webbie,'@')+1); @@ -1520,92 +1203,26 @@ function webfinger_rfc7033($webbie,$zot = false) { } logger('fetching url from resource: ' . $rhs . ':' . $webbie); - $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : '')); + // The default curl Accept: header is */*, which is incorrectly handled by Mastodon servers + // and results in a 406 (Not Acceptable) response, and will also incorrectly produce an XML + // document if you use 'application/jrd+json, */*'. We could set this to application/jrd+json, + // but some test webfinger servers may not explicitly set the content type and they would be + // blocked. The best compromise until Mastodon is fixed is to remove the Accept header which is + // accomplished by setting it to nothing. + + $counter = 0; + $s = z_fetch_url('https://' . $rhs . '/.well-known/webfinger?f=&resource=' . $resource . (($zot) ? '&zot=1' : ''), + false, $counter, [ 'headers' => [ 'Accept:' ] ]); if($s['success']) { $j = json_decode($s['body'],true); - - // We could have a number of URL aliases and webbies - // make an executive decision about the most likely "best" of each - // by comparing against some examples from known networks we're likely to encounter. - // Otherwise we have to store every alias that we may ever encounter and - // validate every URL we ever find against every possible alias - - // @fixme pump.io is going to be a real bugger since it doesn't return subject or aliases - // or provide lookup by url - - $j['address'] = find_webfinger_address($j,$rhs); - $j['location'] = find_webfinger_location($j,$rhs); - if($j['address']) - $j['nickname'] = substr($j['address'],0,strpos($j['address'],'@')); - } - else - return false; - - return($j); -} - -function find_webfinger_address($j,$rhs) { - if(is_array($j) && ($j)) { - if(strpos($j['subject'],'acct:') !== false && strpos($j['subject'],'@' . $rhs)) - return str_replace('acct:','',$j['subject']); - if($j['aliases']) { - foreach($j['aliases'] as $alias) { - if(strpos($alias,'acct:') !== false && strpos($alias,'@' . $rhs)) { - return str_replace('acct:','',$alias); - } - } - } + return($j); } - return ''; -} - - -function find_webfinger_location($j,$rhs) { - if(is_array($j) && ($j)) { - if(strpos($j['subject'],'http') === 0) { - $x = match_webfinger_location($j['subject'],$rhs); - if($x) - return $x; - } - if($j['aliases']) { - foreach($j['aliases'] as $alias) { - if(strpos($alias,'http') === 0) { - $x = match_webfinger_location($alias,$rhs); - if($x) - return($x); - } - } - } - } - return ''; -} - -function match_webfinger_location($s,$h) { - // GNU-social and the older StatusNet - the $host/user/123 form doesn't work - if(preg_match('|' . $h . '/index.php/user/([0-9]*?)$|',$s)) - return $s; - // Redmatrix / hubzilla - if(preg_match('|' . $h . '/channel/|',$s)) - return $s; - // Friendica - if(preg_match('|' . $h . '/profile/|',$s)) - return $s; + return false; - $arr = array('test' => $s, 'host' => $h, 'success' => false); - call_hooks('match_webfinger_location',$arr); - if($arr['success']) - return $s; - return ''; } - - - - - - function old_webfinger($webbie) { $host = ''; @@ -1652,14 +1269,14 @@ function fetch_lrdd_template($host) { } if(! strpos($tpl,'{uri}')) $tpl = ''; - return $tpl; + return $tpl; } function fetch_xrd_links($url) { - logger('fetch_xrd_links: ' . $url, LOGGER_DEBUG); + logger('url: ' . $url, LOGGER_DEBUG); $redirects = 0; $x = z_fetch_url($url,false,$redirects,array('timeout' => 20)); @@ -1668,16 +1285,13 @@ function fetch_xrd_links($url) { return array(); $xml = $x['body']; - logger('fetch_xrd_links: ' . $xml, LOGGER_DATA); + logger('data: ' . $xml, LOGGER_DATA); if ((! $xml) || (! stristr($xml,'<xrd'))) return array(); - // fix diaspora's bad xml - $xml = str_replace(array('href="','"/>'),array('href="','"/>'),$xml); - $h = parse_xml_string($xml); - if(! $h) + if($h === false) return array(); $arr = convert_xml_element_to_array($h); @@ -1709,92 +1323,21 @@ function fetch_xrd_links($url) { $links[]['@attributes'] = array('rel' => 'subject' , 'href' => $arr['xrd']['subject']); } - logger('fetch_xrd_links: ' . print_r($links,true), LOGGER_DATA); + logger('data: ' . print_r($links, true), LOGGER_DATA); return $links; } -function scrape_vcard($url) { - - $ret = array(); - - logger('scrape_vcard: url=' . $url); - - $x = z_fetch_url($url); - if(! $x['success']) - return $ret; - - $s = $x['body']; - - if(! $s) - return $ret; - - $headers = $x['header']; - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('scrape_vcard: parse error: ' . $e); - } - - if(! $dom) - return $ret; - - // Pull out hCard profile elements - - $largest_photo = 0; - - $items = $dom->getElementsByTagName('*'); - foreach($items as $item) { - if(attribute_contains($item->getAttribute('class'), 'vcard')) { - $level2 = $item->getElementsByTagName('*'); - foreach($level2 as $x) { - if(attribute_contains($x->getAttribute('id'),'pod_location')) - $ret['pod_location'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'fn')) - $ret['fn'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'uid')) - $ret['uid'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'nickname')) - $ret['nick'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'searchable')) - $ret['searchable'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'key')) - $ret['public_key'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'given_name')) - $ret['given_name'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'family_name')) - $ret['family_name'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'url')) - $ret['url'] = $x->textContent; - - if((attribute_contains($x->getAttribute('class'),'photo')) - || (attribute_contains($x->getAttribute('class'),'avatar'))) { - $size = intval($x->getAttribute('width')); - if(($size > $largest_photo) || (! $largest_photo)) { - $ret['photo'] = $x->getAttribute('src'); - $largest_photo = $size; - } - } - } - } - } - - return $ret; -} - - +/** + * @brief + * + * @param string $url The URL to scrape + * @return array + */ function scrape_feed($url) { + require_once('library/HTML5/Parser.php'); $ret = array(); $level = 0; @@ -1807,15 +1350,14 @@ function scrape_feed($url) { $code = $x['return_code']; $s = $x['body']; - logger('scrape_feed: returns: ' . $code . ' headers=' . $headers, LOGGER_DEBUG); + logger('returns: ' . $code . ' headers=' . $headers, LOGGER_DEBUG); if(! $s) { - logger('scrape_feed: no data returned for ' . $url); + logger('No data returned for ' . $url); return $ret; } - - $lines = explode("\n",$headers); + $lines = explode("\n", $headers); if(count($lines)) { foreach($lines as $line) { if(stristr($line,'content-type:')) { @@ -1839,15 +1381,14 @@ function scrape_feed($url) { try { $dom = HTML5_Parser::parse($s); } catch (DOMException $e) { - logger('scrape_feed: parse error: ' . $e); + logger('Parse error: ' . $e); } if(! $dom) { - logger('scrape_feed: failed to parse.'); + logger('Failed to parse.'); return $ret; } - $head = $dom->getElementsByTagName('base'); if($head) { foreach($head as $head0) { @@ -1888,112 +1429,6 @@ function scrape_feed($url) { -function service_plink($contact, $guid) { - - $plink = ''; - - $m = parse_url($contact['xchan_url']); - if($m) { - $url = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); - } - else - $url = 'https://' . substr($contact['xchan_addr'],strpos($contact['xchan_addr'],'@')+1); - - $handle = substr($contact['xchan_addr'], 0, strpos($contact['xchan_addr'],'@')); - - if($contact['xchan_network'] === 'diaspora') - $plink = $url . '/posts/' . $guid; - if($contact['xchan_network'] === 'friendica-over-diaspora') - $plink = $url . '/display/' . $handle . '/' . $guid; - if($contact['xchan_network'] === 'zot') - $plink = $url . '/channel/' . $handle . '?f=&mid=' . $guid; - - return $plink; -} - - -function format_and_send_email($sender,$xchan,$item) { - - $title = $item['title']; - $body = $item['body']; - - $textversion = strip_tags(html_entity_decode(bbcode(str_replace(array("\\r", "\\n"), array( "", "\n"), $body)),ENT_QUOTES,'UTF-8')); - - $htmlversion = bbcode(str_replace(array("\\r","\\n"), array("","<br />\n"),$body)); - - $banner = t('$Projectname Notification'); - $product = t('$projectname'); // PLATFORM_NAME; - $siteurl = z_root(); - $thanks = t('Thank You,'); - $sitename = get_config('system','sitename'); - $site_admin = sprintf( t('%s Administrator'), $sitename); - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_html.tpl'); - $email_html_body = replace_macros($tpl,array( - '$banner' => $banner, - '$notify_icon' => Zotlabs\Lib\System::get_notify_icon(), - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, - '$source_name' => $sender['xchan_name'], - '$source_link' => $sender['xchan_url'], - '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, - '$site_admin' => $site_admin, - '$title' => $title, - '$htmlversion' => $htmlversion, - )); - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_text.tpl'); - $email_text_body = replace_macros($tpl, array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, - '$source_name' => $sender['xchan_name'], - '$source_link' => $sender['xchan_url'], - '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, - '$site_admin' => $site_admin, - '$title' => $title, - '$textversion' => $textversion - )); - - $sender_name = t('Administrator'); - - $hostname = App::get_hostname(); - if(strpos($hostname,':')) - $hostname = substr($hostname,0,strpos($hostname,':')); - $sender_email = get_config('system','reply_address'); - if(! $sender_email) - $sender_email = 'noreply' . '@' . $hostname; - - // use the EmailNotification library to send the message - - Zotlabs\Lib\Enotify::send(array( - 'fromName' => $product, - 'fromEmail' => $sender_email, - 'replyTo' => $sender_email, - 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), - 'messageSubject' => (($title) ? $title : t('No Subject')), - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, - 'additionalMailHeader' => '', - )); - -} - - function do_delivery($deliveries) { if(! (is_array($deliveries) && count($deliveries))) @@ -2028,8 +1463,6 @@ function do_delivery($deliveries) { if($deliver) Zotlabs\Daemon\Master::Summon(array('Deliver',$deliver)); - - } @@ -2086,7 +1519,7 @@ function get_site_info() { $commit = ''; } else { - $version = $commit = ''; + $version = $commit = ''; } //Statistics @@ -2103,49 +1536,53 @@ function get_site_info() { foreach(App::$config['feature_lock'] as $k => $v) { if($k === 'config_loaded') continue; + $locked_features[$k] = intval($v); } } - - $data = Array( - 'version' => $version, - 'version_tag' => $tag, - 'server_role' => Zotlabs\Lib\System::get_server_role(), - 'commit' => $commit, - 'url' => z_root(), - 'plugins' => $visible_plugins, - 'register_policy' => $register_policy[get_config('system','register_policy')], - 'invitation_only' => intval(get_config('system','invitation_only')), - 'directory_mode' => $directory_mode[get_config('system','directory_mode')], - 'language' => get_config('system','language'), - 'rss_connections' => intval(get_config('system','feed_contacts')), - 'expiration' => $site_expire, + $data = [ + 'url' => z_root(), + 'platform' => Zotlabs\Lib\System::get_platform_name(), + 'site_name' => (($site_name) ? $site_name : ''), + 'version' => $version, + 'version_tag' => $tag, + 'server_role' => Zotlabs\Lib\System::get_server_role(), + 'commit' => $commit, + 'plugins' => $visible_plugins, + 'register_policy' => $register_policy[get_config('system','register_policy')], + 'invitation_only' => intval(get_config('system','invitation_only')), + 'directory_mode' => $directory_mode[get_config('system','directory_mode')], + 'language' => get_config('system','language'), + 'rss_connections' => intval(get_config('system','feed_contacts')), + 'expiration' => $site_expire, 'default_service_restrictions' => $service_class, - 'locked_features' => $locked_features, - 'admin' => $admin, - 'site_name' => (($site_name) ? $site_name : ''), - 'platform' => Zotlabs\Lib\System::get_platform_name(), - 'dbdriver' => DBA::$dba->getdriver(), - 'lastpoll' => get_config('system','lastpoll'), - 'info' => (($site_info) ? $site_info : ''), - 'channels_total' => $channels_total_stat, - 'channels_active_halfyear' => $channels_active_halfyear_stat, - 'channels_active_monthly' => $channels_active_monthly_stat, - 'local_posts' => $local_posts_stat, - 'hide_in_statistics' => $hide_in_statistics - ); + 'locked_features' => $locked_features, + 'admin' => $admin, + 'dbdriver' => DBA::$dba->getdriver(), + 'lastpoll' => get_config('system','lastpoll'), + 'info' => (($site_info) ? $site_info : ''), + 'channels_total' => $channels_total_stat, + 'channels_active_halfyear' => $channels_active_halfyear_stat, + 'channels_active_monthly' => $channels_active_monthly_stat, + 'local_posts' => $local_posts_stat, + 'hide_in_statistics' => $hide_in_statistics + ]; + return $data; } - - +/** + * @brief + * + * @param string $url + * @return boolean + */ function check_siteallowed($url) { $retvalue = true; - $arr = array('url' => $url); call_hooks('check_siteallowed',$arr); @@ -2171,9 +1608,16 @@ function check_siteallowed($url) { } } } + return $retvalue; } +/** + * @brief + * + * @param string $hash + * @return boolean + */ function check_channelallowed($hash) { $retvalue = true; @@ -2203,13 +1647,22 @@ function check_channelallowed($hash) { } } } + 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['xchan_hash']) + dbesc($xchan_hash) ); if($r) { if(! $r[0]['abook_instance']) @@ -2233,24 +1686,30 @@ function get_repository_version($branch = 'master') { 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_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'), + 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'), ); call_hooks('network_to_name', $nets); @@ -2258,27 +1717,24 @@ function network_to_name($s) { $search = array_keys($nets); $replace = array_values($nets); - return str_replace($search,$replace,$s); - + return str_replace($search, $replace, $s); } - +/** + * @brief Send a text email message. + * + * @param array $params an assoziative 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) { - /** - * @brief Send a text email message - * - * @param array $params an assoziative 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 - */ - if(! $params['fromEmail']) { $params['fromEmail'] = get_config('system','from_email'); if(! $params['fromEmail']) @@ -2324,8 +1780,13 @@ function z_mail($params) { return $res; } -// discover the best API path available for redmatrix/hubzilla servers +/** + * @brief Discover the best API path available for redmatrix/hubzilla servers. + * + * @param string $host + * @return string + */ function probe_api_path($host) { $schemes = ['https', 'http' ]; @@ -2335,10 +1796,182 @@ function probe_api_path($host) { foreach($paths as $path) { $curpath = $scheme . '://' . $host . $path; $x = z_fetch_url($curpath); - if($x['success'] && ! strlen($x['body'],'not implemented')) - return str_replace('version','',$curpath); + if($x['success'] && ! strlen($x['body'], 'not implemented')) + return str_replace('version', '', $curpath); } } return ''; } + + +function scrape_vcard($url) { + + require_once('library/HTML5/Parser.php'); + + $ret = array(); + + logger('url=' . $url); + + $x = z_fetch_url($url); + if(! $x['success']) + return $ret; + + $s = $x['body']; + + if(! $s) + return $ret; + + $headers = $x['header']; + $lines = explode("\n",$headers); + if(count($lines)) { + foreach($lines as $line) { + // don't try and run feeds through the html5 parser + if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) + return ret; + } + } + + try { + $dom = HTML5_Parser::parse($s); + } catch (DOMException $e) { + logger('Parse error: ' . $e); + } + + if(! $dom) + return $ret; + + // Pull out hCard profile elements + + $largest_photo = 0; + + $items = $dom->getElementsByTagName('*'); + foreach($items as $item) { + if(attribute_contains($item->getAttribute('class'), 'vcard')) { + $level2 = $item->getElementsByTagName('*'); + foreach($level2 as $x) { + if(attribute_contains($x->getAttribute('id'),'pod_location')) + $ret['pod_location'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'fn')) + $ret['fn'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'uid')) + $ret['uid'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'nickname')) + $ret['nick'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'searchable')) + $ret['searchable'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'key')) + $ret['public_key'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'given_name')) + $ret['given_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'family_name')) + $ret['family_name'] = $x->textContent; + if(attribute_contains($x->getAttribute('class'),'url')) + $ret['url'] = $x->textContent; + + if((attribute_contains($x->getAttribute('class'),'photo')) + || (attribute_contains($x->getAttribute('class'),'avatar'))) { + $size = intval($x->getAttribute('width')); + if(($size > $largest_photo) || (! $largest_photo)) { + $ret['photo'] = $x->getAttribute('src'); + $largest_photo = $size; + } + } + } + } + } + + return $ret; +} + +function service_plink($contact, $guid) { + + $plink = ''; + + $m = parse_url($contact['xchan_url']); + if($m) { + $url = $m['scheme'] . '://' . $m['host'] . (($m['port']) ? ':' . $m['port'] : ''); + } + 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; + + $x = [ 'xchan' => $contact, 'guid' => $guid, 'url' => $url, 'plink' => $plink ]; + call_hooks('service_plink', $x); + + return $x['plink']; +} + +function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false) { + // Values will be stored in this array + + if($acceptedTypes === false) + $acceptedTypes = $_SERVER['HTTP_ACCEPT']; + + $AcceptTypes = Array (); + + // Accept header is case insensitive, and whitespace isn’t 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 isn’t supported! + $AcceptTypes[$a] = $q; + } + arsort($AcceptTypes); + + // if no parameter was passed, just return parsed data + if (!$mimeTypes) return $AcceptTypes; + + $mimeTypes = array_map('strtolower', (array)$mimeTypes); + + // let’s check our supported types: + foreach ($AcceptTypes as $mime => $q) { + if ($q && in_array($mime, $mimeTypes)) return $mime; + } + // no mime-type found + return null; +} + + +function jsonld_document_loader($url) { + + // perform caching for jsonld normaliser + + require_once('library/jsonld/jsonld.php'); + + $cachepath = 'store/[data]/ldcache'; + if(! is_dir($cachepath)) + os_mkdir($cachepath,STORAGE_DEFAULT_PERMISSIONS,true); + + $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)); + return $r; + } + + logger('not found'); + if(file_exists($filename)) { + return json_decode(file_get_contents($filename)); + } + + return []; + +}
\ No newline at end of file |