]> git.mxchange.org Git - friendica.git/blobdiff - src/Util/Network.php
Merge pull request #5386 from annando/dfrn-unknown-owner
[friendica.git] / src / Util / Network.php
index f25d9c9a265981bac26d1e6d86be986dd26dfbc7..0e65145b78340caca37bc49f583a9d62c441df0e 100644 (file)
@@ -12,18 +12,19 @@ use Friendica\Core\Config;
 use Friendica\Network\Probe;
 use Friendica\Object\Image;
 use Friendica\Util\XML;
-
-require_once 'library/slinky.php';
+use DOMDocument;
+use DomXPath;
 
 class Network
 {
        /**
-        * @brief Curl wrapper
+        * Curl wrapper
         *
         * If binary flag is true, return binary results.
         * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt")
         * to preserve cookies from one request to the next.
         *
+        * @brief Curl wrapper
         * @param string  $url            URL to fetch
         * @param boolean $binary         default false
         *                                TRUE if asked to return binary results (file download)
@@ -34,9 +35,33 @@ class Network
         *
         * @return string The fetched content
         */
-       public static function fetchURL($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0)
+       public static function fetchUrl($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0)
+       {
+               $ret = self::fetchUrlFull($url, $binary, $redirects, $timeout, $accept_content, $cookiejar);
+
+               return $ret['body'];
+       }
+
+       /**
+        * Curl wrapper with array of return values.
+        *
+        * Inner workings and parameters are the same as @ref fetchUrl but returns an array with
+        * all the information collected during the fetch.
+        *
+        * @brief Curl wrapper with array of return values.
+        * @param string  $url            URL to fetch
+        * @param boolean $binary         default false
+        *                                TRUE if asked to return binary results (file download)
+        * @param integer $redirects      The recursion counter for internal use - default 0
+        * @param integer $timeout        Timeout in seconds, default system config value or 60 seconds
+        * @param string  $accept_content supply Accept: header with 'accept_content' as the value
+        * @param string  $cookiejar      Path to cookie jar file
+        *
+        * @return array With all relevant information, 'body' contains the actual fetched content.
+        */
+       public static function fetchUrlFull($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0)
        {
-               $ret = self::zFetchURL(
+               return self::curl(
                        $url,
                        $binary,
                        $redirects,
@@ -45,8 +70,6 @@ class Network
                        'cookiejar'=>$cookiejar
                        ]
                );
-
-               return($ret['body']);
        }
 
        /**
@@ -71,7 +94,7 @@ class Network
         *    string 'header' => HTTP headers
         *    string 'body' => fetched content
         */
-       public static function zFetchURL($url, $binary = false, &$redirects = 0, $opts = [])
+       public static function curl($url, $binary = false, &$redirects = 0, $opts = [])
        {
                $ret = ['return_code' => 0, 'success' => false, 'header' => '', 'info' => '', 'body' => ''];
 
@@ -79,8 +102,20 @@ class Network
 
                $a = get_app();
 
-               if (blocked_url($url)) {
-                       logger('z_fetch_url: domain of ' . $url . ' is blocked', LOGGER_DATA);
+               $parts = parse_url($url);
+               $path_parts = explode('/', defaults($parts, 'path', ''));
+               foreach ($path_parts as $part) {
+                       if (strlen($part) <> mb_strlen($part)) {
+                               $parts2[] = rawurlencode($part);
+                       } else {
+                               $parts2[] = $part;
+                       }
+               }
+               $parts['path'] =  implode('/', $parts2);
+               $url = self::unparseURL($parts);
+
+               if (self::isUrlBlocked($url)) {
+                       logger('domain of ' . $url . ' is blocked', LOGGER_DATA);
                        return $ret;
                }
 
@@ -185,7 +220,7 @@ class Network
                }
 
                if (curl_errno($ch) !== CURLE_OK) {
-                       logger('fetch_url error fetching ' . $url . ': ' . curl_error($ch), LOGGER_NORMAL);
+                       logger('error fetching ' . $url . ': ' . curl_error($ch), LOGGER_NORMAL);
                }
 
                $ret['errno'] = curl_errno($ch);
@@ -195,7 +230,7 @@ class Network
 
                $http_code = $curl_info['http_code'];
 
-               logger('fetch_url ' . $url . ': ' . $http_code . " " . $s, LOGGER_DATA);
+               logger($url . ': ' . $http_code . " " . $s, LOGGER_DATA);
                $header = '';
 
                // Pull out multiple headers, e.g. proxy and continuation headers
@@ -217,7 +252,7 @@ class Network
 
                        $newurl = $curl_info['redirect_url'];
 
-                       if (($new_location_info['path'] == '') && ( $new_location_info['host'] != '')) {
+                       if (empty($new_location_info['path']) && !empty($new_location_info['host'])) {
                                $newurl = $new_location_info['scheme'] . '://' . $new_location_info['host'] . $old_location_info['path'];
                        }
 
@@ -229,11 +264,16 @@ class Network
                        if (strpos($newurl, '/') === 0) {
                                $newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl;
                        }
+                       $old_location_query = @parse_url($url, PHP_URL_QUERY);
+
+                       if ($old_location_query != '') {
+                               $newurl .= '?' . $old_location_query;
+                       }
 
                        if (filter_var($newurl, FILTER_VALIDATE_URL)) {
                                $redirects++;
                                @curl_close($ch);
-                               return z_fetch_url($newurl, $binary, $redirects, $opts);
+                               return self::curl($newurl, $binary, $redirects, $opts);
                        }
                }
 
@@ -248,8 +288,8 @@ class Network
                if (!$ret['success']) {
                        $ret['error'] = curl_error($ch);
                        $ret['debug'] = $curl_info;
-                       logger('z_fetch_url: error: '.$url.': '.$ret['return_code'].' - '.$ret['error'], LOGGER_DEBUG);
-                       logger('z_fetch_url: debug: '.print_r($curl_info, true), LOGGER_DATA);
+                       logger('error: '.$url.': '.$ret['return_code'].' - '.$ret['error'], LOGGER_DEBUG);
+                       logger('debug: '.print_r($curl_info, true), LOGGER_DATA);
                }
 
                $ret['body'] = substr($s, strlen($header));
@@ -277,11 +317,11 @@ class Network
         *
         * @return string The content
         */
-       public static function postURL($url, $params, $headers = null, &$redirects = 0, $timeout = 0)
+       public static function post($url, $params, $headers = null, &$redirects = 0, $timeout = 0)
        {
                $stamp1 = microtime(true);
 
-               if (blocked_url($url)) {
+               if (self::isUrlBlocked($url)) {
                        logger('post_url: domain of ' . $url . ' is blocked', LOGGER_DATA);
                        return false;
                }
@@ -370,6 +410,9 @@ class Network
 
                if ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) {
                        $matches = [];
+                       $new_location_info = @parse_url($curl_info['redirect_url']);
+                       $old_location_info = @parse_url($curl_info['url']);
+
                        preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
                        $newurl = trim(array_pop($matches));
 
@@ -380,7 +423,7 @@ class Network
                        if (filter_var($newurl, FILTER_VALIDATE_URL)) {
                                $redirects++;
                                logger('post_url: redirect ' . $url . ' to ' . $newurl);
-                               return post_url($newurl, $params, $headers, $redirects, $timeout);
+                               return self::post($newurl, $params, $headers, $redirects, $timeout);
                        }
                }
 
@@ -400,71 +443,7 @@ class Network
        }
 
        /**
-        * 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.
-        */
-       public static function xmlStatus($st, $message = '')
-       {
-               $result = ['status' => $st];
-
-               if ($message != '') {
-                       $result['message'] = $message;
-               }
-
-               if ($st) {
-                       logger('xml_status returning non_zero: ' . $st . " message=" . $message);
-               }
-
-               header("Content-type: text/xml");
-
-               $xmldata = ["result" => $result];
-
-               echo XML::fromArray($xmldata, $xml);
-
-               killme();
-       }
-
-       /**
-        * @brief Send HTTP status header and exit.
-        *
-        * @param integer $val         HTTP status result value
-        * @param array   $description optional message
-        *                             'title' => header title
-        *                             'description' => optional message
-        */
-       public static function httpStatusExit($val, $description = [])
-       {
-               $err = '';
-               if ($val >= 400) {
-                       $err = 'Error';
-                       if (!isset($description["title"])) {
-                               $description["title"] = $err." ".$val;
-                       }
-               }
-
-               if ($val >= 200 && $val < 300) {
-                       $err = 'OK';
-               }
-
-               logger('http_status_exit ' . $val);
-               header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err);
-
-               if (isset($description["title"])) {
-                       $tpl = get_markup_template('http_status.tpl');
-                       echo replace_macros(
-                               $tpl,
-                               [
-                                       '$title' => $description["title"],
-                                       '$description' => $description["description"]]
-                       );
-               }
-
-               killme();
-       }
-
-       /**
-        * @brief Check URL to se if ts's real
+        * @brief Check URL to see if it's real
         *
         * 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)
@@ -472,7 +451,7 @@ class Network
         * @param string $url The URL to be validated
         * @return string|boolean The actual working URL, false else
         */
-       public static function validateURL($url)
+       public static function isUrlValid($url)
        {
                if (Config::get('system', 'disable_url_validation')) {
                        return $url;
@@ -490,7 +469,7 @@ class Network
                /// @TODO Really suppress function outcomes? Why not find them + debug them?
                $h = @parse_url($url);
 
-               if ((is_array($h)) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
+               if ((is_array($h)) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
                        return $url;
                }
 
@@ -503,7 +482,7 @@ class Network
         * @param string $addr The email address
         * @return boolean True if it's a valid email address, false if it's not
         */
-       public static function validateEmail($addr)
+       public static function isEmailDomainValid($addr)
        {
                if (Config::get('system', 'disable_email_validation')) {
                        return true;
@@ -515,7 +494,10 @@ class Network
 
                $h = substr($addr, strpos($addr, '@') + 1);
 
-               if (($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
+               if (($h) && (dns_get_record($h, DNS_A + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
+                       return true;
+               }
+               if (($h) && dns_get_record($h, DNS_CNAME + DNS_MX)) {
                        return true;
                }
                return false;
@@ -530,7 +512,7 @@ class Network
         * @param string $url URL which get tested
         * @return boolean True if url is allowed otherwise return false
         */
-       public static function allowedURL($url)
+       public static function isUrlAllowed($url)
        {
                $h = @parse_url($url);
 
@@ -575,7 +557,7 @@ class Network
         *
         * @return boolean
         */
-       public static function blockedURL($url)
+       public static function isUrlBlocked($url)
        {
                $h = @parse_url($url);
 
@@ -608,7 +590,7 @@ class Network
         * @return boolean False if not allowed, true if allowed
         *    or if allowed list is not configured
         */
-       public static function allowedEmail($email)
+       public static function isEmailDomainAllowed($email)
        {
                $domain = strtolower(substr($email, strpos($email, '@') + 1));
                if (!$domain) {
@@ -622,7 +604,7 @@ class Network
 
                $allowed = explode(',', $str_allowed);
 
-               return allowed_domain($domain, $allowed);
+               return self::isDomainAllowed($domain, $allowed);
        }
 
        /**
@@ -633,7 +615,7 @@ class Network
         * @param array  $domain_list
         * @return boolean
         */
-       public static function allowedDomain($domain, array $domain_list)
+       public static function isDomainAllowed($domain, array $domain_list)
        {
                $found = false;
 
@@ -648,7 +630,7 @@ class Network
                return $found;
        }
 
-       public static function avatarImg($email)
+       public static function lookupAvatarByEmail($email)
        {
                $avatar['size'] = 175;
                $avatar['email'] = $email;
@@ -665,125 +647,6 @@ class Network
                return $avatar['url'];
        }
 
-       public static function parseXmlString($s, $strict = true)
-       {
-               // the "strict" parameter is deactivated
-
-               /// @todo Move this function to the xml class
-               libxml_use_internal_errors(true);
-
-               $x = @simplexml_load_string($s);
-               if (!$x) {
-                       logger('libxml: parse: error: ' . $s, 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;
-       }
-
-       public static function scaleExternalImages($srctext, $include_link = true, $scale_replace = false)
-       {
-               // Suppress "view full size"
-               if (intval(Config::get('system', 'no_view_full_size'))) {
-                       $include_link = false;
-               }
-
-               // Picture addresses can contain special characters
-               $s = htmlspecialchars_decode($srctext);
-
-               $matches = null;
-               $c = preg_match_all('/\[img.*?\](.*?)\[\/img\]/ism', $s, $matches, PREG_SET_ORDER);
-               if ($c) {
-                       foreach ($matches as $mtch) {
-                               logger('scale_external_image: ' . $mtch[1]);
-
-                               $hostname = str_replace('www.', '', substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3));
-                               if (stristr($mtch[1], $hostname)) {
-                                       continue;
-                               }
-
-                               // $scale_replace, if passed, is an array of two elements. The
-                               // first is the name of the full-size image. The second is the
-                               // name of a remote, scaled-down version of the full size image.
-                               // This allows Friendica to display the smaller remote image if
-                               // one exists, while still linking to the full-size image
-                               if ($scale_replace) {
-                                       $scaled = str_replace($scale_replace[0], $scale_replace[1], $mtch[1]);
-                               } else {
-                                       $scaled = $mtch[1];
-                               }
-                               $i = fetch_url($scaled);
-                               if (! $i) {
-                                       return $srctext;
-                               }
-
-                               // guess mimetype from headers or filename
-                               $type = Image::guessType($mtch[1], true);
-
-                               if ($i) {
-                                       $Image = new Image($i, $type);
-                                       if ($Image->isValid()) {
-                                               $orig_width = $Image->getWidth();
-                                               $orig_height = $Image->getHeight();
-
-                                               if ($orig_width > 640 || $orig_height > 640) {
-                                                       $Image->scaleDown(640);
-                                                       $new_width = $Image->getWidth();
-                                                       $new_height = $Image->getHeight();
-                                                       logger('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG);
-                                                       $s = str_replace(
-                                                               $mtch[0],
-                                                               '[img=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/img]'
-                                                               . "\n" . (($include_link)
-                                                                       ? '[url=' . $mtch[1] . ']' . L10n::t('view full size') . '[/url]' . "\n"
-                                                                       : ''),
-                                                               $s
-                                                       );
-                                                       logger('scale_external_images: new string: ' . $s, LOGGER_DEBUG);
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // replace the special char encoding
-               $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
-               return $s;
-       }
-
-       public static function fixContactSslPolicy(&$contact, $new_policy)
-       {
-               $ssl_changed = false;
-               if ((intval($new_policy) == SSL_POLICY_SELFSIGN || $new_policy === 'self') && strstr($contact['url'], 'https:')) {
-                       $ssl_changed = true;
-                       $contact['url']     =   str_replace('https:', 'http:', $contact['url']);
-                       $contact['request'] =   str_replace('https:', 'http:', $contact['request']);
-                       $contact['notify']  =   str_replace('https:', 'http:', $contact['notify']);
-                       $contact['poll']    =   str_replace('https:', 'http:', $contact['poll']);
-                       $contact['confirm'] =   str_replace('https:', 'http:', $contact['confirm']);
-                       $contact['poco']    =   str_replace('https:', 'http:', $contact['poco']);
-               }
-
-               if ((intval($new_policy) == SSL_POLICY_FULL || $new_policy === 'full') && strstr($contact['url'], 'http:')) {
-                       $ssl_changed = true;
-                       $contact['url']     =   str_replace('http:', 'https:', $contact['url']);
-                       $contact['request'] =   str_replace('http:', 'https:', $contact['request']);
-                       $contact['notify']  =   str_replace('http:', 'https:', $contact['notify']);
-                       $contact['poll']    =   str_replace('http:', 'https:', $contact['poll']);
-                       $contact['confirm'] =   str_replace('http:', 'https:', $contact['confirm']);
-                       $contact['poco']    =   str_replace('http:', 'https:', $contact['poco']);
-               }
-
-               if ($ssl_changed) {
-                       $fields = ['url' => $contact['url'], 'request' => $contact['request'],
-                                       'notify' => $contact['notify'], 'poll' => $contact['poll'],
-                                       'confirm' => $contact['confirm'], 'poco' => $contact['poco']];
-                       dba::update('contact', $fields, ['id' => $contact['id']]);
-               }
-       }
-
        /**
         * @brief Remove Google Analytics and other tracking platforms params from URL
         *
@@ -793,7 +656,7 @@ class Network
        public static function stripTrackingQueryParams($url)
        {
                $urldata = parse_url($url);
-               if (is_string($urldata["query"])) {
+               if (!empty($urldata["query"])) {
                        $query = $urldata["query"];
                        parse_str($query, $querydata);
 
@@ -848,11 +711,11 @@ class Network
         * @param bool   $fetchbody Wether to fetch the body or not after the HEAD requests
         * @return string A canonical URL
         */
-       public static function originalURL($url, $depth = 1, $fetchbody = false)
+       public static function finalUrl($url, $depth = 1, $fetchbody = false)
        {
                $a = get_app();
 
-               $url = strip_tracking_query_params($url);
+               $url = self::stripTrackingQueryParams($url);
 
                if ($depth > 10) {
                        return($url);
@@ -885,15 +748,15 @@ class Network
                        && (($curl_info['redirect_url'] != "") || ($curl_info['location'] != ""))
                ) {
                        if ($curl_info['redirect_url'] != "") {
-                               return(original_url($curl_info['redirect_url'], ++$depth, $fetchbody));
+                               return(self::finalUrl($curl_info['redirect_url'], ++$depth, $fetchbody));
                        } else {
-                               return(original_url($curl_info['location'], ++$depth, $fetchbody));
+                               return(self::finalUrl($curl_info['location'], ++$depth, $fetchbody));
                        }
                }
 
                // Check for redirects in the meta elements of the body if there are no redirects in the header.
                if (!$fetchbody) {
-                       return(original_url($url, ++$depth, true));
+                       return(self::finalUrl($url, ++$depth, true));
                }
 
                // if the file is too large then exit
@@ -945,7 +808,7 @@ class Network
                                $pathinfo = explode(";", $path);
                                foreach ($pathinfo as $value) {
                                        if (substr(strtolower($value), 0, 4) == "url=") {
-                                               return(original_url(substr($value, 4), ++$depth));
+                                               return(self::finalUrl(substr($value, 4), ++$depth));
                                        }
                                }
                        }
@@ -954,45 +817,6 @@ class Network
                return $url;
        }
 
-       public static function shortLink($url)
-       {
-               $slinky = new Slinky($url);
-               $yourls_url = Config::get('yourls', 'url1');
-               if ($yourls_url) {
-                       $yourls_username = Config::get('yourls', 'username1');
-                       $yourls_password = Config::get('yourls', 'password1');
-                       $yourls_ssl = Config::get('yourls', 'ssl1');
-                       $yourls = new Slinky_YourLS();
-                       $yourls->set('username', $yourls_username);
-                       $yourls->set('password', $yourls_password);
-                       $yourls->set('ssl', $yourls_ssl);
-                       $yourls->set('yourls-url', $yourls_url);
-                       $slinky->set_cascade([$yourls, new Slinky_Ur1ca(), new Slinky_TinyURL()]);
-               } else {
-                       // setup a cascade of shortening services
-                       // try to get a short link from these services
-                       // in the order ur1.ca, tinyurl
-                       $slinky->set_cascade([new Slinky_Ur1ca(), new Slinky_TinyURL()]);
-               }
-               return $slinky->short();
-       }
-
-       /**
-        * @brief Encodes content to json
-        *
-        * This function encodes an array to json format
-        * and adds an application/json HTTP header to the output.
-        * After finishing the process is getting killed.
-        *
-        * @param array $x The input content
-        */
-       public static function jsonReturnAndDie($x)
-       {
-               header("content-type: application/json");
-               echo json_encode($x);
-               killme();
-       }
-
        /**
         * @brief Find the matching part between two url
         *
@@ -1000,7 +824,7 @@ class Network
         * @param string $url2
         * @return string The matching part
         */
-       public static function matchingURL($url1, $url2)
+       public static function getUrlMatch($url1, $url2)
        {
                if (($url1 == "") || ($url2 == "")) {
                        return "";
@@ -1016,14 +840,35 @@ class Network
                        return "";
                }
 
+               if (empty($parts1["scheme"])) {
+                       $parts1["scheme"] = '';
+               }
+               if (empty($parts2["scheme"])) {
+                       $parts2["scheme"] = '';
+               }
+
                if ($parts1["scheme"] != $parts2["scheme"]) {
                        return "";
                }
 
+               if (empty($parts1["host"])) {
+                       $parts1["host"] = '';
+               }
+               if (empty($parts2["host"])) {
+                       $parts2["host"] = '';
+               }
+
                if ($parts1["host"] != $parts2["host"]) {
                        return "";
                }
 
+               if (empty($parts1["port"])) {
+                       $parts1["port"] = '';
+               }
+               if (empty($parts2["port"])) {
+                       $parts2["port"] = '';
+               }
+
                if ($parts1["port"] != $parts2["port"]) {
                        return "";
                }
@@ -1034,14 +879,21 @@ class Network
                        $match .= ":".$parts1["port"];
                }
 
+               if (empty($parts1["path"])) {
+                       $parts1["path"] = '';
+               }
+               if (empty($parts2["path"])) {
+                       $parts2["path"] = '';
+               }
+
                $pathparts1 = explode("/", $parts1["path"]);
                $pathparts2 = explode("/", $parts2["path"]);
 
                $i = 0;
                $path = "";
                do {
-                       $path1 = $pathparts1[$i];
-                       $path2 = $pathparts2[$i];
+                       $path1 = defaults($pathparts1, $i, '');
+                       $path2 = defaults($pathparts2, $i, '');
 
                        if ($path1 == $path2) {
                                $path .= $path1."/";
@@ -1060,7 +912,7 @@ class Network
         *
         * @return string The glued URL
         */
-       public static function unParseURL($parsed)
+       public static function unparseURL($parsed)
        {
                $get = function ($key) use ($parsed) {
                        return isset($parsed[$key]) ? $parsed[$key] : null;