X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2FScrape.php;h=52405ae2dd556e5ee1ce434acfbca1b75f15500a;hb=808b886a3e18b07c63171c4833334e2cf77ddf71;hp=05a53e53866c443a796cec0c06908cabc32fbe7c;hpb=7cc5a9bba9ee892bac41fe3af4ae00da846ca96a;p=friendica.git diff --git a/include/Scrape.php b/include/Scrape.php old mode 100644 new mode 100755 index 05a53e5386..52405ae2dd --- a/include/Scrape.php +++ b/include/Scrape.php @@ -1,6 +1,7 @@ getAttribute('rel'); if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) $ret['feed_atom'] = $item->getAttribute('href'); - if(substr($x,0,5) == "dfrn-") + if(substr($x,0,5) == "dfrn-") { $ret[$x] = $item->getAttribute('href'); + } if($x === 'lrdd') { $decoded = urldecode($item->getAttribute('href')); if(preg_match('/acct:([^@]*)@/',$decoded,$matches)) @@ -54,17 +59,28 @@ function scrape_dfrn($url) { // 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('class'),'fn')) + if(attribute_contains($x->getAttribute('class'),'fn')) { $ret['fn'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'photo')) - $ret['photo'] = $x->getAttribute('src'); - if(attribute_contains($x->getAttribute('class'),'key')) + } + if((attribute_contains($x->getAttribute('class'),'photo')) + || (attribute_contains($x->getAttribute('class'),'avatar'))) { + $size = intval($x->getAttribute('width')); + // dfrn prefers 175, so if we find this, we set largest_size so it can't be topped. + if(($size > $largest_photo) || ($size == 175) || (! $largest_photo)) { + $ret['photo'] = $x->getAttribute('src'); + $largest_photo = (($size == 175) ? 9999 : $size); + } + } + if(attribute_contains($x->getAttribute('class'),'key')) { $ret['key'] = $x->textContent; + } } } } @@ -119,9 +135,11 @@ function scrape_meta($url) { } } - - - $dom = HTML5_Parser::parse($s); + try { + $dom = HTML5_Parser::parse($s); + } catch (DOMException $e) { + logger('scrape_meta: parse error: ' . $e); + } if(! $dom) return $ret; @@ -164,13 +182,19 @@ function scrape_vcard($url) { } } - $dom = HTML5_Parser::parse($s); + 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')) { @@ -179,11 +203,17 @@ function scrape_vcard($url) { if(attribute_contains($x->getAttribute('class'),'fn')) $ret['fn'] = $x->textContent; if((attribute_contains($x->getAttribute('class'),'photo')) - || (attribute_contains($x->getAttribute('class'),'avatar'))) - $ret['photo'] = $x->getAttribute('src'); + || (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; + } + } if((attribute_contains($x->getAttribute('class'),'nickname')) - || (attribute_contains($x->getAttribute('class'),'uid'))) + || (attribute_contains($x->getAttribute('class'),'uid'))) { $ret['nick'] = $x->textContent; + } } } } @@ -222,24 +252,25 @@ function scrape_feed($url) { } } - $dom = HTML5_Parser::parse($s); + try { + $dom = HTML5_Parser::parse($s); + } catch (DOMException $e) { + logger('scrape_feed: parse error: ' . $e); + } if(! $dom) return $ret; - $items = $dom->getElementsByTagName('img'); - - // get img elements (twitter) - - if($items) { - foreach($items as $item) { - $x = $item->getAttribute('id'); - if($x === 'profile-image') { - $ret['photo'] = $item->getAttribute('src'); - } + $head = $dom->getElementsByTagName('base'); + if($head) { + foreach($head as $head0) { + $basename = $head0->getAttribute('href'); + break; } } + if(! $basename) + $basename = substr($url,0,strrpos($url,'/')) . '/'; $items = $dom->getElementsByTagName('link'); @@ -259,11 +290,40 @@ function scrape_feed($url) { } } + // Drupal and perhaps others only provide relative URL's. Turn them into absolute. + + if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://'))) + $ret['feed_atom'] = $basename . $ret['feed_atom']; + if(x($ret,'feed_rss') && (! strstr($ret['feed_rss'],'://'))) + $ret['feed_rss'] = $basename . $ret['feed_rss']; + return $ret; }} -function probe_url($url) { +/** + * + * Probe a network address to discover what kind of protocols we need to communicate with it. + * + * Warning: this function is a bit touchy and there are some subtle dependencies within the logic flow. + * Edit with care. + * + */ + +/** + * + * PROBE_DIASPORA has a bias towards returning Diaspora information + * while PROBE_NORMAL has a bias towards dfrn/zot - in the case where + * an address (such as a Friendica address) supports more than one type + * of network. + * + */ + + +define ( 'PROBE_NORMAL', 0); +define ( 'PROBE_DIASPORA', 1); + +function probe_url($url, $mode = PROBE_NORMAL) { require_once('include/email.php'); $result = array(); @@ -271,14 +331,34 @@ function probe_url($url) { if(! $url) return $result; - $diaspora = false; + $network = null; + $diaspora = false; + $diaspora_base = ''; + $diaspora_guid = ''; + $diaspora_key = ''; + $has_lrdd = false; $email_conversant = false; - if($url) { - $links = lrdd($url); + $twitter = ((strpos($url,'twitter.com') !== false) ? true : false); + + $at_addr = ((strpos($url,'@') !== false) ? true : false); + + if(! $twitter) { + + if(strpos($url,'mailto:') !== false && $at_addr) { + $url = str_replace('mailto:','',$url); + $links = array(); + } + else + $links = lrdd($url); if(count($links)) { + $has_lrdd = true; + + logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA); foreach($links as $link) { + if($link['@attributes']['rel'] === NAMESPACE_ZOT) + $zot = unamp($link['@attributes']['href']); if($link['@attributes']['rel'] === NAMESPACE_DFRN) $dfrn = unamp($link['@attributes']['href']); if($link['@attributes']['rel'] === 'salmon') @@ -289,8 +369,21 @@ function probe_url($url) { $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://joindiaspora.com/seed_location') + 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'])); + $pubkey = rsatopem($diaspora_key); $diaspora = true; + } } // Status.Net can have more than one profile URL. We need to match the profile URL @@ -313,7 +406,7 @@ function probe_url($url) { } } } - else { + elseif($mode == PROBE_NORMAL) { // Check email @@ -322,7 +415,7 @@ function probe_url($url) { $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", intval(local_user()) ); - $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1", + $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", intval(local_user()) ); if(count($x) && count($r)) { @@ -338,16 +431,27 @@ function probe_url($url) { $addr = $orig_url; $network = NETWORK_MAIL; $name = substr($url,0,strpos($url,'@')); - $profile = 'http://' . substr($url,strpos($url,'@')+1); + $phost = substr($url,strpos($url,'@')+1); + $profile = 'http://' . $phost; // fix nick character range $vcard = array('fn' => $name, 'nick' => $name, 'photo' => gravatar_img($url)); - $notify = 'smtp'; - $poll = 'email'; + $notify = 'smtp ' . random_string(); + $poll = 'email ' . random_string(); $priority = 0; $x = email_msg_meta($mbox,$msgs[0]); - $adr = imap_rfc822_parse_adrlist($x->from,''); - if(strlen($adr[0]->personal)) - $vcard['fn'] = notags($adr[0]->personal); + if(stristr($x->from,$orig_url)) + $adr = imap_rfc822_parse_adrlist($x->from,''); + elseif(stristr($x->to,$orig_url)) + $adr = imap_rfc822_parse_adrlist($x->to,''); + if(isset($adr)) { + foreach($adr as $feadr) { + if((strcasecmp($feadr->mailbox,$name) == 0) + &&(strcasecmp($feadr->host,$phost) == 0) + && (strlen($feadr->personal))) { + $vcard['fn'] = notags($feadr->personal); + } + } + } } imap_close($mbox); } @@ -355,22 +459,60 @@ function probe_url($url) { } } - if(strlen($dfrn)) { - $ret = scrape_dfrn($dfrn); - if(is_array($ret) && x($ret,'dfrn-request')) { - $network = NETWORK_DFRN; - $request = $ret['dfrn-request']; - $confirm = $ret['dfrn-confirm']; - $notify = $ret['dfrn-notify']; - $poll = $ret['dfrn-poll']; + if($mode == PROBE_NORMAL) { + if(strlen($zot)) { + $s = fetch_url($zot); + if($s) { + $j = json_decode($s); + if($j) { + $network = NETWORK_ZOT; + $vcard = array( + 'fn' => $j->fullname, + 'nick' => $j->nickname, + 'photo' => $j->photo + ); + $profile = $j->url; + $notify = $j->post; + $pubkey = $j->pubkey; + $poll = 'N/A'; + } + } + } + + if(strlen($dfrn)) { + $ret = scrape_dfrn(($hcard) ? $hcard : $dfrn); + if(is_array($ret) && x($ret,'dfrn-request')) { + $network = NETWORK_DFRN; + $request = $ret['dfrn-request']; + $confirm = $ret['dfrn-confirm']; + $notify = $ret['dfrn-notify']; + $poll = $ret['dfrn-poll']; + + $vcard = array(); + $vcard['fn'] = $ret['fn']; + $vcard['nick'] = $ret['nick']; + $vcard['photo'] = $ret['photo']; + } } } - if($network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { - $network = NETWORK_OSTATUS; + if($diaspora && $diaspora_base && $diaspora_guid) { + if($mode == PROBE_DIASPORA || ! $notify) { + $notify = $diaspora_base . 'receive/users/' . $diaspora_guid; + $batch = $diaspora_base . 'receive/public' ; + } + if(strpos($url,'@')) + $addr = str_replace('acct:', '', $url); + } + + if($network !== NETWORK_ZOT && $network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { + if($diaspora) + $network = NETWORK_DIASPORA; + elseif($has_lrdd) + $network = NETWORK_OSTATUS; $priority = 0; - if($hcard) { + if($hcard && ! $vcard) { $vcard = scrape_vcard($hcard); // Google doesn't use absolute url in profile photos @@ -384,83 +526,138 @@ function probe_url($url) { logger('probe_url: scrape_vcard: ' . print_r($vcard,true), LOGGER_DATA); } - if(! $profile) { - if($diaspora) - $profile = $hcard; + if($twitter) { + logger('twitter: setup'); + $tid = basename($url); + $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss'; + if(intval($tid)) + $poll = $tapi . '?user_id=' . $tid; else - $profile = $url; + $poll = $tapi . '?screen_name=' . $tid; + $profile = 'http://twitter.com/#!/' . $tid; + $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid; + $vcard['nick'] = $tid; + $vcard['fn'] = $tid . '@twitter'; } if(! x($vcard,'fn')) if(x($vcard,'nick')) $vcard['fn'] = $vcard['nick']; - if((! isset($vcard)) && (! $poll)) { + $check_feed = false; - $ret = scrape_feed($url); - logger('probe_url: scrape_feed returns: ' . print_r($ret,true), LOGGER_DATA); - if(count($ret) && ($ret['feed_atom'] || $ret['feed_rss'])) { - $poll = ((x($ret,'feed_atom')) ? unamp($ret['feed_atom']) : unamp($ret['feed_rss'])); - $vcard = array(); - if(x($ret,'photo')) - $vcard['photo'] = $ret['photo']; - require_once('simplepie/simplepie.inc'); - $feed = new SimplePie(); - $xml = fetch_url($poll); - - $feed->set_raw_data($xml); - - $feed->init(); - - if(! x($vcard,'photo')) - $vcard['photo'] = $feed->get_image_url(); - $author = $feed->get_author(); - if($author) { - $vcard['fn'] = unxmlify(trim($author->get_name())); - if(! $vcard['fn']) - $vcard['fn'] = trim(unxmlify($author->get_email())); - if(strpos($vcard['fn'],'@') !== false) - $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); - $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); - if(strpos($vcard['nick'],' ')) - $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); - $email = unxmlify($author->get_email()); + if($twitter || ! $poll) + $check_feed = true; + if((! isset($vcard)) || (! x($vcard,'fn')) || (! $profile)) + $check_feed = true; + if(($at_addr) && (! count($links))) + $check_feed = false; + + if($check_feed) { + + $feedret = scrape_feed(($poll) ? $poll : $url); + logger('probe_url: scrape_feed returns: ' . print_r($feedret,true), LOGGER_DATA); + if(count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) { + $poll = ((x($feedret,'feed_atom')) ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss'])); + if(! x($vcard)) + $vcard = array(); + } + + if(x($feedret,'photo') && (! x($vcard,'photo'))) + $vcard['photo'] = $feedret['photo']; + require_once('library/simplepie/simplepie.inc'); + $feed = new SimplePie(); + $xml = fetch_url($poll); + + logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA); + $a = get_app(); + + logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), $LOGGER_DATA); + + $feed->set_raw_data($xml); + + $feed->init(); + if($feed->error()) + logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error()); + + + if(! x($vcard,'photo')) + $vcard['photo'] = $feed->get_image_url(); + $author = $feed->get_author(); + + if($author) { + $vcard['fn'] = unxmlify(trim($author->get_name())); + if(! $vcard['fn']) + $vcard['fn'] = trim(unxmlify($author->get_email())); + if(strpos($vcard['fn'],'@') !== false) + $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); + $email = unxmlify($author->get_email()); + if(! $profile && $author->get_link()) + $profile = trim(unxmlify($author->get_link())); + if(! $vcard['photo']) { + $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + if($rawtags) { + $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; + if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo')) + $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; + } } - else { - $item = $feed->get_item(0); - if($item) { - $author = $item->get_author(); - if($author) { - $vcard['fn'] = trim(unxmlify($author->get_name())); - if(! $vcard['fn']) - $vcard['fn'] = trim(unxmlify($author->get_email())); - if(strpos($vcard['fn'],'@') !== false) - $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); - $vcard['nick'] = strtolower(unxmlify($vcard['fn'])); - if(strpos($vcard['nick'],' ')) - $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); - $email = unxmlify($author->get_email()); - } - if(! $vcard['photo']) { - $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail'); - if($rawmedia && $rawmedia[0]['attribs']['']['url']) - $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); - } + } + else { + $item = $feed->get_item(0); + if($item) { + $author = $item->get_author(); + if($author) { + $vcard['fn'] = trim(unxmlify($author->get_name())); + if(! $vcard['fn']) + $vcard['fn'] = trim(unxmlify($author->get_email())); + if(strpos($vcard['fn'],'@') !== false) + $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); + $email = unxmlify($author->get_email()); + if(! $profile && $author->get_link()) + $profile = trim(unxmlify($author->get_link())); + } + if(! $vcard['photo']) { + $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail'); + if($rawmedia && $rawmedia[0]['attribs']['']['url']) + $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); + } + if(! $vcard['photo']) { + $rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); + if($rawtags) { + $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; + if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo')) + $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; + } } } - if((! $vcard['photo']) && strlen($email)) - $vcard['photo'] = gravatar_img($email); - if($poll === $profile) - $lnk = $feed->get_permalink(); - if(isset($lnk) && strlen($lnk)) - $profile = $lnk; - if(! (x($vcard,'fn'))) - $vcard['fn'] = notags($feed->get_title()); - if(! (x($vcard,'fn'))) - $vcard['fn'] = notags($feed->get_description()); - $network = 'feed'; - $priority = 2; } + + if((! $vcard['photo']) && strlen($email)) + $vcard['photo'] = gravatar_img($email); + if($poll === $profile) + $lnk = $feed->get_permalink(); + if(isset($lnk) && strlen($lnk)) + $profile = $lnk; + + if(! (x($vcard,'fn'))) + $vcard['fn'] = notags($feed->get_title()); + if(! (x($vcard,'fn'))) + $vcard['fn'] = notags($feed->get_description()); + + if(strpos($vcard['fn'],'Twitter / ') !== false) { + $vcard['fn'] = substr($vcard['fn'],strpos($vcard['fn'],'/')+1); + $vcard['fn'] = trim($vcard['fn']); + } + if(! x($vcard,'nick')) { + $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); + if(strpos($vcard['nick'],' ')) + $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); + } + if(! $network) + $network = NETWORK_FEED; + if(! $priority) + $priority = 2; } } @@ -468,22 +665,33 @@ function probe_url($url) { $a = get_app(); $vcard['photo'] = $a->get_baseurl() . '/images/default-profile.jpg' ; } - $vcard['fn'] = notags($vcard['fn']); - $vcard['nick'] = notags($vcard['nick']); + if(! $profile) + $profile = $url; + // No human could be associated with this link, use the URL as the contact name + + if(($network === NETWORK_FEED) && ($poll) && (! x($vcard,'fn'))) + $vcard['fn'] = $url; + + $vcard['fn'] = notags($vcard['fn']); + $vcard['nick'] = str_replace(' ','',notags($vcard['nick'])); + $result['name'] = $vcard['fn']; $result['nick'] = $vcard['nick']; $result['url'] = $profile; $result['addr'] = $addr; + $result['batch'] = $batch; $result['notify'] = $notify; $result['poll'] = $poll; $result['request'] = $request; $result['confirm'] = $confirm; + $result['poco'] = $poco; $result['photo'] = $vcard['photo']; $result['priority'] = $priority; $result['network'] = $network; $result['alias'] = $alias; + $result['pubkey'] = $pubkey; logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG);