<?php /* * StatusNet - the distributed open-source microblogging tool * Copyright (C) 2010, StatusNet, Inc. * * Some utilities for generating hint data * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ class DiscoveryHints { static function fromXRD($xrd) { $hints = array(); foreach ($xrd->links as $link) { switch ($link['rel']) { case Discovery::PROFILEPAGE: $hints['profileurl'] = $link['href']; break; case Salmon::NS_MENTIONS: case Salmon::NS_REPLIES: $hints['salmon'] = $link['href']; break; case Discovery::UPDATESFROM: $hints['feedurl'] = $link['href']; break; case Discovery::HCARD: $hints['hcardurl'] = $link['href']; break; default: break; } } return $hints; } static function fromHcardUrl($url) { $client = new HTTPClient(); $client->setHeader('Accept', 'text/html,application/xhtml+xml'); $response = $client->get($url); if (!$response->isOk()) { return null; } return self::hcardHints($response->getBody(), $response->getUrl()); } static function hcardHints($body, $url) { $hcard = self::_hcard($body, $url); if (empty($hcard)) { return array(); } $hints = array(); // XXX: don't copy stuff into an array and then copy it again if (array_key_exists('nickname', $hcard)) { $hints['nickname'] = $hcard['nickname']; } if (array_key_exists('fn', $hcard)) { $hints['fullname'] = $hcard['fn']; } else if (array_key_exists('n', $hcard)) { $hints['fullname'] = implode(' ', $hcard['n']); } if (array_key_exists('photo', $hcard) && count($hcard['photo'])) { $hints['avatar'] = $hcard['photo'][0]; } if (array_key_exists('note', $hcard)) { $hints['bio'] = $hcard['note']; } if (array_key_exists('adr', $hcard)) { if (is_string($hcard['adr'])) { $hints['location'] = $hcard['adr']; } else if (is_array($hcard['adr'])) { $hints['location'] = implode(' ', $hcard['adr']); } } if (array_key_exists('url', $hcard)) { if (is_string($hcard['url'])) { $hints['homepage'] = $hcard['url']; } else if (is_array($hcard['url']) && !empty($hcard['url'])) { // HACK get the last one; that's how our hcards look $hints['homepage'] = $hcard['url'][count($hcard['url'])-1]; } } return $hints; } static function _hcard($body, $url) { // DOMDocument::loadHTML may throw warnings on unrecognized elements, // and notices on unrecognized namespaces. $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE)); $doc = new DOMDocument(); $doc->loadHTML($body); error_reporting($old); $xp = new DOMXPath($doc); $hcardNodes = self::_getChildrenByClass($doc->documentElement, 'vcard', $xp); $hcards = array(); for ($i = 0; $i < $hcardNodes->length; $i++) { $hcardNode = $hcardNodes->item($i); $hcard = self::_hcardFromNode($hcardNode, $xp, $url); $hcards[] = $hcard; } $repr = null; foreach ($hcards as $hcard) { if (in_array($url, $hcard['url'])) { $repr = $hcard; break; } } if (!is_null($repr)) { return $repr; } else if (count($hcards) > 0) { return $hcards[0]; } else { return null; } } function _getChildrenByClass($el, $cls, $xp) { // borrowed from hkit. Thanks dudes! $qry = ".//*[contains(concat(' ',normalize-space(@class),' '),' $cls ')]"; $nodes = $xp->query($qry, $el); return $nodes; } function _hcardFromNode($hcardNode, $xp, $base) { $hcard = array(); $hcard['url'] = array(); $urlNodes = self::_getChildrenByClass($hcardNode, 'url', $xp); for ($j = 0; $j < $urlNodes->length; $j++) { $urlNode = $urlNodes->item($j); if ($urlNode->hasAttribute('href')) { $url = $urlNode->getAttribute('href'); } else { $url = $urlNode->textContent; } $hcard['url'][] = self::_rel2abs($url, $base); } $hcard['photo'] = array(); $photoNodes = self::_getChildrenByClass($hcardNode, 'photo', $xp); for ($j = 0; $j < $photoNodes->length; $j++) { $photoNode = $photoNodes->item($j); if ($photoNode->hasAttribute('src')) { $url = $photoNode->getAttribute('src'); } else if ($photoNode->hasAttribute('href')) { $url = $photoNode->getAttribute('href'); } else { $url = $photoNode->textContent; } $hcard['photo'][] = self::_rel2abs($url, $base); } $singles = array('nickname', 'note', 'fn', 'n', 'adr'); foreach ($singles as $single) { $nodes = self::_getChildrenByClass($hcardNode, $single, $xp); if ($nodes->length > 0) { $node = $nodes->item(0); $hcard[$single] = $node->textContent; } } return $hcard; } // XXX: this is a first pass; we probably need // to handle things like ../ and ./ and so on static function _rel2abs($rel, $wrt) { $parts = parse_url($rel); if ($parts === false) { return false; } // If it's got a scheme, use it if (!empty($parts['scheme'])) { return $rel; } $w = parse_url($wrt); $base = $w['scheme'].'://'.$w['host']; if ($rel[0] == '/') { return $base.$rel; } $wp = explode('/', $w['path']); array_pop($wp); return $base.implode('/', $wp).'/'.$rel; } }