Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

697 lines
19 KiB

  1. <?php
  2. require_once('library/HTML5/Parser.php');
  3. require_once('include/crypto.php');
  4. if(! function_exists('scrape_dfrn')) {
  5. function scrape_dfrn($url) {
  6. $a = get_app();
  7. $ret = array();
  8. logger('scrape_dfrn: url=' . $url);
  9. $s = fetch_url($url);
  10. if(! $s)
  11. return $ret;
  12. $headers = $a->get_curl_headers();
  13. logger('scrape_dfrn: headers=' . $headers, LOGGER_DEBUG);
  14. $lines = explode("\n",$headers);
  15. if(count($lines)) {
  16. foreach($lines as $line) {
  17. // don't try and run feeds through the html5 parser
  18. if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml'))))
  19. return ret;
  20. }
  21. }
  22. try {
  23. $dom = HTML5_Parser::parse($s);
  24. } catch (DOMException $e) {
  25. logger('scrape_dfrn: parse error: ' . $e);
  26. }
  27. if(! $dom)
  28. return $ret;
  29. $items = $dom->getElementsByTagName('link');
  30. // get DFRN link elements
  31. foreach($items as $item) {
  32. $x = $item->getAttribute('rel');
  33. if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml'))
  34. $ret['feed_atom'] = $item->getAttribute('href');
  35. if(substr($x,0,5) == "dfrn-") {
  36. $ret[$x] = $item->getAttribute('href');
  37. }
  38. if($x === 'lrdd') {
  39. $decoded = urldecode($item->getAttribute('href'));
  40. if(preg_match('/acct:([^@]*)@/',$decoded,$matches))
  41. $ret['nick'] = $matches[1];
  42. }
  43. }
  44. // Pull out hCard profile elements
  45. $largest_photo = 0;
  46. $items = $dom->getElementsByTagName('*');
  47. foreach($items as $item) {
  48. if(attribute_contains($item->getAttribute('class'), 'vcard')) {
  49. $level2 = $item->getElementsByTagName('*');
  50. foreach($level2 as $x) {
  51. if(attribute_contains($x->getAttribute('class'),'fn')) {
  52. $ret['fn'] = $x->textContent;
  53. }
  54. if((attribute_contains($x->getAttribute('class'),'photo'))
  55. || (attribute_contains($x->getAttribute('class'),'avatar'))) {
  56. $size = intval($x->getAttribute('width'));
  57. // dfrn prefers 175, so if we find this, we set largest_size so it can't be topped.
  58. if(($size > $largest_photo) || ($size == 175) || (! $largest_photo)) {
  59. $ret['photo'] = $x->getAttribute('src');
  60. $largest_photo = (($size == 175) ? 9999 : $size);
  61. }
  62. }
  63. if(attribute_contains($x->getAttribute('class'),'key')) {
  64. $ret['key'] = $x->textContent;
  65. }
  66. }
  67. }
  68. }
  69. return $ret;
  70. }}
  71. if(! function_exists('validate_dfrn')) {
  72. function validate_dfrn($a) {
  73. $errors = 0;
  74. if(! x($a,'key'))
  75. $errors ++;
  76. if(! x($a,'dfrn-request'))
  77. $errors ++;
  78. if(! x($a,'dfrn-confirm'))
  79. $errors ++;
  80. if(! x($a,'dfrn-notify'))
  81. $errors ++;
  82. if(! x($a,'dfrn-poll'))
  83. $errors ++;
  84. return $errors;
  85. }}
  86. if(! function_exists('scrape_meta')) {
  87. function scrape_meta($url) {
  88. $a = get_app();
  89. $ret = array();
  90. logger('scrape_meta: url=' . $url);
  91. $s = fetch_url($url);
  92. if(! $s)
  93. return $ret;
  94. $headers = $a->get_curl_headers();
  95. logger('scrape_meta: headers=' . $headers, LOGGER_DEBUG);
  96. $lines = explode("\n",$headers);
  97. if(count($lines)) {
  98. foreach($lines as $line) {
  99. // don't try and run feeds through the html5 parser
  100. if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml'))))
  101. return ret;
  102. }
  103. }
  104. try {
  105. $dom = HTML5_Parser::parse($s);
  106. } catch (DOMException $e) {
  107. logger('scrape_meta: parse error: ' . $e);
  108. }
  109. if(! $dom)
  110. return $ret;
  111. $items = $dom->getElementsByTagName('meta');
  112. // get DFRN link elements
  113. foreach($items as $item) {
  114. $x = $item->getAttribute('name');
  115. if(substr($x,0,5) == "dfrn-")
  116. $ret[$x] = $item->getAttribute('content');
  117. }
  118. return $ret;
  119. }}
  120. if(! function_exists('scrape_vcard')) {
  121. function scrape_vcard($url) {
  122. $a = get_app();
  123. $ret = array();
  124. logger('scrape_vcard: url=' . $url);
  125. $s = fetch_url($url);
  126. if(! $s)
  127. return $ret;
  128. $headers = $a->get_curl_headers();
  129. $lines = explode("\n",$headers);
  130. if(count($lines)) {
  131. foreach($lines as $line) {
  132. // don't try and run feeds through the html5 parser
  133. if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml'))))
  134. return ret;
  135. }
  136. }
  137. try {
  138. $dom = HTML5_Parser::parse($s);
  139. } catch (DOMException $e) {
  140. logger('scrape_vcard: parse error: ' . $e);
  141. }
  142. if(! $dom)
  143. return $ret;
  144. // Pull out hCard profile elements
  145. $largest_photo = 0;
  146. $items = $dom->getElementsByTagName('*');
  147. foreach($items as $item) {
  148. if(attribute_contains($item->getAttribute('class'), 'vcard')) {
  149. $level2 = $item->getElementsByTagName('*');
  150. foreach($level2 as $x) {
  151. if(attribute_contains($x->getAttribute('class'),'fn'))
  152. $ret['fn'] = $x->textContent;
  153. if((attribute_contains($x->getAttribute('class'),'photo'))
  154. || (attribute_contains($x->getAttribute('class'),'avatar'))) {
  155. $size = intval($x->getAttribute('width'));
  156. if(($size > $largest_photo) || (! $largest_photo)) {
  157. $ret['photo'] = $x->getAttribute('src');
  158. $largest_photo = $size;
  159. }
  160. }
  161. if((attribute_contains($x->getAttribute('class'),'nickname'))
  162. || (attribute_contains($x->getAttribute('class'),'uid'))) {
  163. $ret['nick'] = $x->textContent;
  164. }
  165. }
  166. }
  167. }
  168. return $ret;
  169. }}
  170. if(! function_exists('scrape_feed')) {
  171. function scrape_feed($url) {
  172. $a = get_app();
  173. $ret = array();
  174. $s = fetch_url($url);
  175. if(! $s)
  176. return $ret;
  177. $headers = $a->get_curl_headers();
  178. logger('scrape_feed: headers=' . $headers, LOGGER_DEBUG);
  179. $lines = explode("\n",$headers);
  180. if(count($lines)) {
  181. foreach($lines as $line) {
  182. if(stristr($line,'content-type:')) {
  183. if(stristr($line,'application/atom+xml') || stristr($s,'<feed')) {
  184. $ret['feed_atom'] = $url;
  185. return $ret;
  186. }
  187. if(stristr($line,'application/rss+xml') || stristr($s,'<rss')) {
  188. $ret['feed_rss'] = $url;
  189. return $ret;
  190. }
  191. }
  192. }
  193. }
  194. try {
  195. $dom = HTML5_Parser::parse($s);
  196. } catch (DOMException $e) {
  197. logger('scrape_feed: parse error: ' . $e);
  198. }
  199. if(! $dom)
  200. return $ret;
  201. $head = $dom->getElementsByTagName('base');
  202. if($head) {
  203. foreach($head as $head0) {
  204. $basename = $head0->getAttribute('href');
  205. break;
  206. }
  207. }
  208. if(! $basename)
  209. $basename = substr($url,0,strrpos($url,'/')) . '/';
  210. $items = $dom->getElementsByTagName('link');
  211. // get Atom/RSS link elements, take the first one of either.
  212. if($items) {
  213. foreach($items as $item) {
  214. $x = $item->getAttribute('rel');
  215. if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) {
  216. if(! x($ret,'feed_atom'))
  217. $ret['feed_atom'] = $item->getAttribute('href');
  218. }
  219. if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) {
  220. if(! x($ret,'feed_rss'))
  221. $ret['feed_rss'] = $item->getAttribute('href');
  222. }
  223. }
  224. }
  225. // Drupal and perhaps others only provide relative URL's. Turn them into absolute.
  226. if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://')))
  227. $ret['feed_atom'] = $basename . $ret['feed_atom'];
  228. if(x($ret,'feed_rss') && (! strstr($ret['feed_rss'],'://')))
  229. $ret['feed_rss'] = $basename . $ret['feed_rss'];
  230. return $ret;
  231. }}
  232. /**
  233. *
  234. * Probe a network address to discover what kind of protocols we need to communicate with it.
  235. *
  236. * Warning: this function is a bit touchy and there are some subtle dependencies within the logic flow.
  237. * Edit with care.
  238. *
  239. */
  240. /**
  241. *
  242. * PROBE_DIASPORA has a bias towards returning Diaspora information
  243. * while PROBE_NORMAL has a bias towards dfrn/zot - in the case where
  244. * an address (such as a Friendika address) supports more than one type
  245. * of network.
  246. *
  247. */
  248. define ( 'PROBE_NORMAL', 0);
  249. define ( 'PROBE_DIASPORA', 1);
  250. function probe_url($url, $mode = PROBE_NORMAL) {
  251. require_once('include/email.php');
  252. $result = array();
  253. if(! $url)
  254. return $result;
  255. $network = null;
  256. $diaspora = false;
  257. $diaspora_base = '';
  258. $diaspora_guid = '';
  259. $diaspora_key = '';
  260. $has_lrdd = false;
  261. $email_conversant = false;
  262. $twitter = ((strpos($url,'twitter.com') !== false) ? true : false);
  263. $at_addr = ((strpos($url,'@') !== false) ? true : false);
  264. if(! $twitter) {
  265. if(strpos($url,'mailto:') !== false && $at_addr) {
  266. $url = str_replace('mailto:','',$url);
  267. $links = array();
  268. }
  269. else
  270. $links = lrdd($url);
  271. if(count($links)) {
  272. $has_lrdd = true;
  273. logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA);
  274. foreach($links as $link) {
  275. if($link['@attributes']['rel'] === NAMESPACE_ZOT)
  276. $zot = unamp($link['@attributes']['href']);
  277. if($link['@attributes']['rel'] === NAMESPACE_DFRN)
  278. $dfrn = unamp($link['@attributes']['href']);
  279. if($link['@attributes']['rel'] === 'salmon')
  280. $notify = unamp($link['@attributes']['href']);
  281. if($link['@attributes']['rel'] === NAMESPACE_FEED)
  282. $poll = unamp($link['@attributes']['href']);
  283. if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard')
  284. $hcard = unamp($link['@attributes']['href']);
  285. if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page')
  286. $profile = unamp($link['@attributes']['href']);
  287. if($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') {
  288. $diaspora_base = unamp($link['@attributes']['href']);
  289. $diaspora = true;
  290. }
  291. if($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') {
  292. $diaspora_guid = unamp($link['@attributes']['href']);
  293. $diaspora = true;
  294. }
  295. if($link['@attributes']['rel'] === 'diaspora-public-key') {
  296. $diaspora_key = base64_decode(unamp($link['@attributes']['href']));
  297. $pubkey = rsatopem($diaspora_key);
  298. $diaspora = true;
  299. }
  300. }
  301. // Status.Net can have more than one profile URL. We need to match the profile URL
  302. // to a contact on incoming messages to prevent spam, and we won't know which one
  303. // to match. So in case of two, one of them is stored as an alias. Only store URL's
  304. // and not webfinger user@host aliases. If they've got more than two non-email style
  305. // aliases, let's hope we're lucky and get one that matches the feed author-uri because
  306. // otherwise we're screwed.
  307. foreach($links as $link) {
  308. if($link['@attributes']['rel'] === 'alias') {
  309. if(strpos($link['@attributes']['href'],'@') === false) {
  310. if(isset($profile)) {
  311. if($link['@attributes']['href'] !== $profile)
  312. $alias = unamp($link['@attributes']['href']);
  313. }
  314. else
  315. $profile = unamp($link['@attributes']['href']);
  316. }
  317. }
  318. }
  319. }
  320. elseif($mode == PROBE_NORMAL) {
  321. // Check email
  322. $orig_url = $url;
  323. if((strpos($orig_url,'@')) && validate_email($orig_url)) {
  324. $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1",
  325. intval(local_user())
  326. );
  327. $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1",
  328. intval(local_user())
  329. );
  330. if(count($x) && count($r)) {
  331. $mailbox = construct_mailbox_name($r[0]);
  332. $password = '';
  333. openssl_private_decrypt(hex2bin($r[0]['pass']),$password,$x[0]['prvkey']);
  334. $mbox = email_connect($mailbox,$r[0]['user'],$password);
  335. unset($password);
  336. }
  337. if($mbox) {
  338. $msgs = email_poll($mbox,$orig_url);
  339. if(count($msgs)) {
  340. $addr = $orig_url;
  341. $network = NETWORK_MAIL;
  342. $name = substr($url,0,strpos($url,'@'));
  343. $phost = substr($url,strpos($url,'@')+1);
  344. $profile = 'http://' . $phost;
  345. // fix nick character range
  346. $vcard = array('fn' => $name, 'nick' => $name, 'photo' => gravatar_img($url));
  347. $notify = 'smtp ' . random_string();
  348. $poll = 'email ' . random_string();
  349. $priority = 0;
  350. $x = email_msg_meta($mbox,$msgs[0]);
  351. if(stristr($x->from,$orig_url))
  352. $adr = imap_rfc822_parse_adrlist($x->from,'');
  353. elseif(stristr($x->to,$orig_url))
  354. $adr = imap_rfc822_parse_adrlist($x->to,'');
  355. if(isset($adr)) {
  356. foreach($adr as $feadr) {
  357. if((strcasecmp($feadr->mailbox,$name) == 0)
  358. &&(strcasecmp($feadr->host,$phost) == 0)
  359. && (strlen($feadr->personal))) {
  360. $vcard['fn'] = notags($feadr->personal);
  361. }
  362. }
  363. }
  364. }
  365. imap_close($mbox);
  366. }
  367. }
  368. }
  369. }
  370. if($mode == PROBE_NORMAL) {
  371. if(strlen($zot)) {
  372. $s = fetch_url($zot);
  373. if($s) {
  374. $j = json_decode($s);
  375. if($j) {
  376. $network = NETWORK_ZOT;
  377. $vcard = array(
  378. 'fn' => $j->fullname,
  379. 'nick' => $j->nickname,
  380. 'photo' => $j->photo
  381. );
  382. $profile = $j->url;
  383. $notify = $j->post;
  384. $pubkey = $j->pubkey;
  385. $poll = 'N/A';
  386. }
  387. }
  388. }
  389. if(strlen($dfrn)) {
  390. $ret = scrape_dfrn(($hcard) ? $hcard : $dfrn);
  391. if(is_array($ret) && x($ret,'dfrn-request')) {
  392. $network = NETWORK_DFRN;
  393. $request = $ret['dfrn-request'];
  394. $confirm = $ret['dfrn-confirm'];
  395. $notify = $ret['dfrn-notify'];
  396. $poll = $ret['dfrn-poll'];
  397. $vcard = array();
  398. $vcard['fn'] = $ret['fn'];
  399. $vcard['nick'] = $ret['nick'];
  400. $vcard['photo'] = $ret['photo'];
  401. }
  402. }
  403. }
  404. if($diaspora && $diaspora_base && $diaspora_guid) {
  405. if($mode == PROBE_DIASPORA || ! $notify) {
  406. $notify = $diaspora_base . 'receive/users/' . $diaspora_guid;
  407. $batch = $diaspora_base . 'receive/public' ;
  408. }
  409. if(strpos($url,'@'))
  410. $addr = str_replace('acct:', '', $url);
  411. }
  412. if($network !== NETWORK_ZOT && $network !== NETWORK_DFRN && $network !== NETWORK_MAIL) {
  413. if($diaspora)
  414. $network = NETWORK_DIASPORA;
  415. elseif($has_lrdd)
  416. $network = NETWORK_OSTATUS;
  417. $priority = 0;
  418. if($hcard && ! $vcard) {
  419. $vcard = scrape_vcard($hcard);
  420. // Google doesn't use absolute url in profile photos
  421. if((x($vcard,'photo')) && substr($vcard['photo'],0,1) == '/') {
  422. $h = @parse_url($hcard);
  423. if($h)
  424. $vcard['photo'] = $h['scheme'] . '://' . $h['host'] . $vcard['photo'];
  425. }
  426. logger('probe_url: scrape_vcard: ' . print_r($vcard,true), LOGGER_DATA);
  427. }
  428. if($twitter) {
  429. logger('twitter: setup');
  430. $tid = basename($url);
  431. $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss';
  432. if(intval($tid))
  433. $poll = $tapi . '?user_id=' . $tid;
  434. else
  435. $poll = $tapi . '?screen_name=' . $tid;
  436. $profile = 'http://twitter.com/#!/' . $tid;
  437. $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid;
  438. $vcard['nick'] = $tid;
  439. $vcard['fn'] = $tid . '@twitter';
  440. }
  441. if(! x($vcard,'fn'))
  442. if(x($vcard,'nick'))
  443. $vcard['fn'] = $vcard['nick'];
  444. $check_feed = false;
  445. if($twitter || ! $poll)
  446. $check_feed = true;
  447. if((! isset($vcard)) || (! x($vcard,'fn')) || (! $profile))
  448. $check_feed = true;
  449. if(($at_addr) && (! count($links)))
  450. $check_feed = false;
  451. if($check_feed) {
  452. $feedret = scrape_feed(($poll) ? $poll : $url);
  453. logger('probe_url: scrape_feed returns: ' . print_r($feedret,true), LOGGER_DATA);
  454. if(count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) {
  455. $poll = ((x($feedret,'feed_atom')) ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss']));
  456. if(! x($vcard))
  457. $vcard = array();
  458. }
  459. if(x($feedret,'photo') && (! x($vcard,'photo')))
  460. $vcard['photo'] = $feedret['photo'];
  461. require_once('library/simplepie/simplepie.inc');
  462. $feed = new SimplePie();
  463. $xml = fetch_url($poll);
  464. logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA);
  465. $a = get_app();
  466. logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), $LOGGER_DATA);
  467. $feed->set_raw_data($xml);
  468. $feed->init();
  469. if($feed->error())
  470. logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error());
  471. if(! x($vcard,'photo'))
  472. $vcard['photo'] = $feed->get_image_url();
  473. $author = $feed->get_author();
  474. if($author) {
  475. $vcard['fn'] = unxmlify(trim($author->get_name()));
  476. if(! $vcard['fn'])
  477. $vcard['fn'] = trim(unxmlify($author->get_email()));
  478. if(strpos($vcard['fn'],'@') !== false)
  479. $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
  480. $email = unxmlify($author->get_email());
  481. if(! $profile && $author->get_link())
  482. $profile = trim(unxmlify($author->get_link()));
  483. if(! $vcard['photo']) {
  484. $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
  485. if($rawtags) {
  486. $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
  487. if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
  488. $vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
  489. }
  490. }
  491. }
  492. else {
  493. $item = $feed->get_item(0);
  494. if($item) {
  495. $author = $item->get_author();
  496. if($author) {
  497. $vcard['fn'] = trim(unxmlify($author->get_name()));
  498. if(! $vcard['fn'])
  499. $vcard['fn'] = trim(unxmlify($author->get_email()));
  500. if(strpos($vcard['fn'],'@') !== false)
  501. $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
  502. $email = unxmlify($author->get_email());
  503. if(! $profile && $author->get_link())
  504. $profile = trim(unxmlify($author->get_link()));
  505. }
  506. if(! $vcard['photo']) {
  507. $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail');
  508. if($rawmedia && $rawmedia[0]['attribs']['']['url'])
  509. $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']);
  510. }
  511. if(! $vcard['photo']) {
  512. $rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
  513. if($rawtags) {
  514. $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
  515. if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
  516. $vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
  517. }
  518. }
  519. }
  520. }
  521. if((! $vcard['photo']) && strlen($email))
  522. $vcard['photo'] = gravatar_img($email);
  523. if($poll === $profile)
  524. $lnk = $feed->get_permalink();
  525. if(isset($lnk) && strlen($lnk))
  526. $profile = $lnk;
  527. if(! (x($vcard,'fn')))
  528. $vcard['fn'] = notags($feed->get_title());
  529. if(! (x($vcard,'fn')))
  530. $vcard['fn'] = notags($feed->get_description());
  531. if(strpos($vcard['fn'],'Twitter / ') !== false) {
  532. $vcard['fn'] = substr($vcard['fn'],strpos($vcard['fn'],'/')+1);
  533. $vcard['fn'] = trim($vcard['fn']);
  534. }
  535. if(! x($vcard,'nick')) {
  536. $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn'])));
  537. if(strpos($vcard['nick'],' '))
  538. $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' ')));
  539. }
  540. if(! $network)
  541. $network = NETWORK_FEED;
  542. if(! $priority)
  543. $priority = 2;
  544. }
  545. }
  546. if(! x($vcard,'photo')) {
  547. $a = get_app();
  548. $vcard['photo'] = $a->get_baseurl() . '/images/default-profile.jpg' ;
  549. }
  550. if(! $profile)
  551. $profile = $url;
  552. // No human could be associated with this link, use the URL as the contact name
  553. if(($network === NETWORK_FEED) && ($poll) && (! x($vcard,'fn')))
  554. $vcard['fn'] = $url;
  555. $vcard['fn'] = notags($vcard['fn']);
  556. $vcard['nick'] = str_replace(' ','',notags($vcard['nick']));
  557. $result['name'] = $vcard['fn'];
  558. $result['nick'] = $vcard['nick'];
  559. $result['url'] = $profile;
  560. $result['addr'] = $addr;
  561. $result['batch'] = $batch;
  562. $result['notify'] = $notify;
  563. $result['poll'] = $poll;
  564. $result['request'] = $request;
  565. $result['confirm'] = $confirm;
  566. $result['photo'] = $vcard['photo'];
  567. $result['priority'] = $priority;
  568. $result['network'] = $network;
  569. $result['alias'] = $alias;
  570. $result['pubkey'] = $pubkey;
  571. logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG);
  572. return $result;
  573. }