]> git.mxchange.org Git - friendica.git/blobdiff - include/network.php
Merge pull request #803 from annando/master
[friendica.git] / include / network.php
index 27a45ec407ddea095b070c59d7b63d4705ee8ca0..3bf2626424ca546f1f3dd65fe060716709f6bb7a 100644 (file)
@@ -4,25 +4,39 @@
 // 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.
 if(! function_exists('fetch_url')) {
-function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_content=Null) {
+function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_content=Null, $cookiejar = 0) {
+
+       $stamp1 = microtime(true);
 
        $a = get_app();
 
        $ch = @curl_init($url);
-       if(($redirects > 8) || (! $ch)) 
+       if(($redirects > 8) || (! $ch))
                return false;
 
        @curl_setopt($ch, CURLOPT_HEADER, true);
-       
+
+       if($cookiejar) {
+               curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiejar);
+               curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiejar);
+       }
+
+//  These settings aren't needed. We're following the location already. 
+//     @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+//     @curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
+
        if (!is_null($accept_content)){
                curl_setopt($ch,CURLOPT_HTTPHEADER, array (
                        "Accept: " . $accept_content
                ));
        }
-       
+
        @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
-       @curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
+       //@curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
+       @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Friendica)");
 
 
        if(intval($timeout)) {
@@ -59,7 +73,6 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_
        $base = $s;
        $curl_info = @curl_getinfo($ch);
        $http_code = $curl_info['http_code'];
-
 //     logger('fetch_url:' . $http_code . ' data: ' . $s);
        $header = '';
 
@@ -73,25 +86,35 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_
        }
 
        if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) {
-        $matches = array();
-        preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
-        $newurl = trim(array_pop($matches));
+               $new_location_info = @parse_url($curl_info["redirect_url"]);
+               $old_location_info = @parse_url($curl_info["url"]);
+
+               $newurl = $curl_info["redirect_url"];
+
+               if (($new_location_info["path"] == "") AND ($new_location_info["host"] != ""))
+                       $newurl = $new_location_info["scheme"]."://".$new_location_info["host"].$old_location_info["path"];
+
+               $matches = array();
+               if (preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches)) {
+                       $newurl = trim(array_pop($matches));
+               }
                if(strpos($newurl,'/') === 0)
-                       $newurl = $url . $newurl;
-        $url_parsed = @parse_url($newurl);
-        if (isset($url_parsed)) {
-            $redirects++;
-            return fetch_url($newurl,$binary,$redirects,$timeout);
-        }
-    }
+                       $newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl;
+               if (filter_var($newurl, FILTER_VALIDATE_URL)) {
+                       $redirects++;
+                       return fetch_url($newurl,$binary,$redirects,$timeout,$accept_content,$cookiejar);
+               }
+       }
 
        $a->set_curl_code($http_code);
+       $a->set_curl_content_type($curl_info['content_type']);
 
        $body = substr($s,strlen($header));
-
        $a->set_curl_headers($header);
-
        @curl_close($ch);
+
+       $a->save_timestamp($stamp1, "network");
+
        return($body);
 }}
 
@@ -99,9 +122,12 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_
 
 if(! function_exists('post_url')) {
 function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) {
+
+       $stamp1 = microtime(true);
+
        $a = get_app();
        $ch = curl_init($url);
-       if(($redirects > 8) || (! $ch)) 
+       if(($redirects > 8) || (! $ch))
                return false;
 
        curl_setopt($ch, CURLOPT_HEADER, true);
@@ -168,9 +194,8 @@ function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0)
         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
         $newurl = trim(array_pop($matches));
                if(strpos($newurl,'/') === 0)
-                       $newurl = $url . $newurl;
-        $url_parsed = @parse_url($newurl);
-        if (isset($url_parsed)) {
+                       $newurl = $old_location_info["scheme"] . "://" . $old_location_info["host"] . $newurl;
+        if (filter_var($newurl, FILTER_VALIDATE_URL)) {
             $redirects++;
             return fetch_url($newurl,false,$redirects,$timeout);
         }
@@ -181,6 +206,9 @@ function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0)
        $a->set_curl_headers($header);
 
        curl_close($ch);
+
+       $a->save_timestamp($stamp1, "network");
+
        return($body);
 }}
 
@@ -290,9 +318,9 @@ function webfinger_dfrn($s,&$hcard) {
                        if($link['@attributes']['rel'] === NAMESPACE_DFRN)
                                $profile_link = $link['@attributes']['href'];
                        if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
-                               $profile_link = 'stat:' . $link['@attributes']['template'];     
+                               $profile_link = 'stat:' . $link['@attributes']['template'];
                        if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard')
-                               $hcard = $link['@attributes']['href'];                          
+                               $hcard = $link['@attributes']['href'];
                }
        }
        return $profile_link;
@@ -358,6 +386,7 @@ function lrdd($uri, $debug = false) {
        logger('lrdd: constructed url: ' . $url);
 
        $xml = fetch_url($url);
+
        $headers = $a->get_curl_headers();
 
        if (! $xml)
@@ -407,7 +436,7 @@ function lrdd($uri, $debug = false) {
                                elseif(x($link['@attributes'],'href'))
                                        $href = $link['@attributes']['href'];
                        }
-               }               
+               }
        }
 
        if((! isset($tpl)) || (! strpos($tpl,'{uri}')))
@@ -426,7 +455,7 @@ function lrdd($uri, $debug = false) {
 
                $lines = explode("\n",$headers);
                if(count($lines)) {
-                       foreach($lines as $line) {                              
+                       foreach($lines as $line) {
                                if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) {
                                        return(fetch_xrd_links($matches[1]));
                                        break;
@@ -472,7 +501,7 @@ function lrdd($uri, $debug = false) {
 
        $lines = explode("\n",$headers);
        if(count($lines)) {
-               foreach($lines as $line) {                              
+               foreach($lines as $line) {
                        // TODO alter the following regex to support multiple relations (space separated)
                        if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) {
                                $pagelink = $matches[1];
@@ -588,14 +617,14 @@ function fetch_xrd_links($url) {
 
 if(! function_exists('validate_url')) {
 function validate_url(&$url) {
-       
+
        // no naked subdomains (allow localhost for tests)
        if(strpos($url,'.') === false && strpos($url,'/localhost/') === false)
                return false;
        if(substr($url,0,4) != 'http')
                $url = 'http://' . $url;
        $h = @parse_url($url);
-       
+
        if(($h) && (dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
                return true;
        }
@@ -607,6 +636,9 @@ function validate_url(&$url) {
 if(! function_exists('validate_email')) {
 function validate_email($addr) {
 
+       if(get_config('system','disable_email_validation'))
+               return true;
+
        if(! strpos($addr,'@'))
                return false;
        $h = substr($addr,strpos($addr,'@') + 1);
@@ -751,7 +783,7 @@ function add_fcontact($arr,$update = false) {
                        `alias` = '%s',
                        `pubkey` = '%s',
                        `updated` = '%s'
-                       WHERE `url` = '%s' AND `network` = '%s' LIMIT 1", 
+                       WHERE `url` = '%s' AND `network` = '%s'",
                        dbesc($arr['name']),
                        dbesc($arr['photo']),
                        dbesc($arr['request']),
@@ -793,22 +825,49 @@ function add_fcontact($arr,$update = false) {
 }
 
 
-function scale_external_images($s,$include_link = true) {
+function scale_external_images($srctext, $include_link = true, $scale_replace = false) {
 
        $a = get_app();
 
+       // Picture addresses can contain special characters
+       $s = htmlspecialchars_decode($srctext);
+
        $matches = null;
-       $c = preg_match_all('/\[img\](.*?)\[\/img\]/ism',$s,$matches,PREG_SET_ORDER);
+       $c = preg_match_all('/\[img.*?\](.*?)\[\/img\]/ism',$s,$matches,PREG_SET_ORDER);
        if($c) {
                require_once('include/Photo.php');
                foreach($matches as $mtch) {
                        logger('scale_external_image: ' . $mtch[1]);
+
                        $hostname = str_replace('www.','',substr($a->get_baseurl(),strpos($a->get_baseurl(),'://')+3));
                        if(stristr($mtch[1],$hostname))
                                continue;
-                       $i = fetch_url($mtch[1]);
+
+                       // $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;
+
+                       $cachefile = get_cachefile(hash("md5", $scaled));
+                       if ($cachefile != '') {
+                               $stamp1 = microtime(true);
+                               file_put_contents($cachefile, $i);
+                               $a->save_timestamp($stamp1, "file");
+                       }
+
+                       // guess mimetype from headers or filename
+                       $type = guess_image_type($mtch[1],true);
+
                        if($i) {
-                               $ph = new Photo($i);
+                               $ph = new Photo($i, $type);
                                if($ph->is_valid()) {
                                        $orig_width = $ph->getWidth();
                                        $orig_height = $ph->getHeight();
@@ -819,7 +878,7 @@ function scale_external_images($s,$include_link = true) {
                                                $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);
-                                               $s = str_replace($mtch[0],'[img=' . $new_width . 'x' . $new_height. ']' . $mtch[1] . '[/img]'
+                                               $s = str_replace($mtch[0],'[img=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/img]'
                                                        . "\n" . (($include_link) 
                                                                ? '[url=' . $mtch[1] . ']' . t('view full size') . '[/url]' . "\n"
                                                                : ''),$s);
@@ -829,6 +888,9 @@ function scale_external_images($s,$include_link = true) {
                        }
                }
        }
+
+       // replace the special char encoding
+       $s = htmlspecialchars($s,ENT_NOQUOTES,'UTF-8');
        return $s;
 }
 
@@ -876,3 +938,167 @@ function fix_contact_ssl_policy(&$contact,$new_policy) {
        }
 }
 
+
+
+/**
+ * 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.
+ * Examples: $array =  xml2array(file_get_contents('feed.xml'));
+ *              $array =  xml2array(file_get_contents('feed.xml', true, 1, 'attribute'));
+ */
+
+function xml2array($contents, $namespaces = true, $get_attributes=1, $priority = 'attribute') {
+    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();
+
+       if($namespaces)
+           $parser = @xml_parser_create_ns("UTF-8",':');
+       else
+           $parser = @xml_parser_create();
+
+       if(! $parser) {
+               logger('xml2array: xml_parser_create: no resource');
+               return array();
+       }
+
+    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 = array();
+    $parents = array();
+    $opened_tags = array();
+    $arr = array();
+
+    $current = &$xml_array; // Reference
+
+    // Go through the tags.
+    $repeated_tag_index = array(); // 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 = array();
+        $attributes_data = array();
+
+        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);
+}