]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/lib/discoveryhints.php
Merge commit 'refs/merge-requests/199' of git://gitorious.org/statusnet/mainline...
[quix0rs-gnu-social.git] / plugins / OStatus / lib / discoveryhints.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * Some utilities for generating hint data
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 class DiscoveryHints {
23     static function fromXRD(XML_XRD $xrd)
24     {
25         $hints = array();
26
27         foreach ($xrd->links as $link) {
28             switch ($link->rel) {
29             case WebFingerResource_Profile::PROFILEPAGE:
30                 $hints['profileurl'] = $link->href;
31                 break;
32             case Salmon::REL_SALMON:
33             case Salmon::NS_MENTIONS:   // XXX: deprecated, remove in the future
34             case Salmon::NS_REPLIES:    // XXX: deprecated, remove in the future
35                 $hints['salmon'] = $link->href;
36                 break;
37             case Discovery::UPDATESFROM:
38                 if (empty($link->type) || $link->type == 'application/atom+xml') {
39                     $hints['feedurl'] = $link->href;
40                 }
41                 break;
42             case Discovery::HCARD:
43                 $hints['hcardurl'] = $link->href;
44                 break;
45             default:
46                 break;
47             }
48         }
49
50         return $hints;
51     }
52
53     static function fromHcardUrl($url)
54     {
55         $client = new HTTPClient();
56         $client->setHeader('Accept', 'text/html,application/xhtml+xml');
57         $response = $client->get($url);
58
59         if (!$response->isOk()) {
60             return null;
61         }
62
63         return self::hcardHints($response->getBody(),
64                                 $response->getUrl());
65     }
66
67     static function hcardHints($body, $url)
68     {
69         $hcard = self::_hcard($body, $url);
70
71         if (empty($hcard)) {
72             return array();
73         }
74
75         $hints = array();
76
77         // XXX: don't copy stuff into an array and then copy it again
78
79         if (array_key_exists('nickname', $hcard)) {
80             $hints['nickname'] = $hcard['nickname'];
81         }
82
83         if (array_key_exists('fn', $hcard)) {
84             $hints['fullname'] = $hcard['fn'];
85         } else if (array_key_exists('n', $hcard)) {
86             $hints['fullname'] = implode(' ', $hcard['n']);
87         }
88
89         if (array_key_exists('photo', $hcard) && count($hcard['photo'])) {
90             $hints['avatar'] = $hcard['photo'][0];
91         }
92
93         if (array_key_exists('note', $hcard)) {
94             $hints['bio'] = $hcard['note'];
95         }
96
97         if (array_key_exists('adr', $hcard)) {
98             if (is_string($hcard['adr'])) {
99                 $hints['location'] = $hcard['adr'];
100             } else if (is_array($hcard['adr'])) {
101                 $hints['location'] = implode(' ', $hcard['adr']);
102             }
103         }
104
105         if (array_key_exists('url', $hcard)) {
106             if (is_string($hcard['url'])) {
107                 $hints['homepage'] = $hcard['url'];
108             } else if (is_array($hcard['url']) && !empty($hcard['url'])) {
109                 // HACK get the last one; that's how our hcards look
110                 $hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
111             }
112         }
113
114         return $hints;
115     }
116
117     static function _hcard($body, $url)
118     {
119         // DOMDocument::loadHTML may throw warnings on unrecognized elements,
120         // and notices on unrecognized namespaces.
121
122         $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
123
124         $doc = new DOMDocument();
125         $doc->loadHTML($body);
126
127         error_reporting($old);
128
129         $xp = new DOMXPath($doc);
130
131         $hcardNodes = self::_getChildrenByClass($doc->documentElement, 'vcard', $xp);
132
133         $hcards = array();
134
135         for ($i = 0; $i < $hcardNodes->length; $i++) {
136
137             $hcardNode = $hcardNodes->item($i);
138
139             $hcard = self::_hcardFromNode($hcardNode, $xp, $url);
140
141             $hcards[] = $hcard;
142         }
143
144         $repr = null;
145
146         foreach ($hcards as $hcard) {
147             if (in_array($url, $hcard['url'])) {
148                 $repr = $hcard;
149                 break;
150             }
151         }
152
153         if (!is_null($repr)) {
154             return $repr;
155         } else if (count($hcards) > 0) {
156             return $hcards[0];
157         } else {
158             return null;
159         }
160     }
161
162     function _getChildrenByClass($el, $cls, $xp)
163     {
164         // borrowed from hkit. Thanks dudes!
165
166         $qry = ".//*[contains(concat(' ',normalize-space(@class),' '),' $cls ')]";
167
168         $nodes = $xp->query($qry, $el);
169
170         return $nodes;
171     }
172
173     function _hcardFromNode($hcardNode, $xp, $base)
174     {
175         $hcard = array();
176
177         $hcard['url'] = array();
178
179         $urlNodes = self::_getChildrenByClass($hcardNode, 'url', $xp);
180
181         for ($j = 0; $j < $urlNodes->length; $j++) {
182
183             $urlNode = $urlNodes->item($j);
184
185             if ($urlNode->hasAttribute('href')) {
186                 $url = $urlNode->getAttribute('href');
187             } else {
188                 $url = $urlNode->textContent;
189             }
190
191             $hcard['url'][] = self::_rel2abs($url, $base);
192         }
193
194         $hcard['photo'] = array();
195
196         $photoNodes = self::_getChildrenByClass($hcardNode, 'photo', $xp);
197
198         for ($j = 0; $j < $photoNodes->length; $j++) {
199             $photoNode = $photoNodes->item($j);
200             if ($photoNode->hasAttribute('src')) {
201                 $url = $photoNode->getAttribute('src');
202             } else if ($photoNode->hasAttribute('href')) {
203                 $url = $photoNode->getAttribute('href');
204             } else {
205                 $url = $photoNode->textContent;
206             }
207             $hcard['photo'][] = self::_rel2abs($url, $base);
208         }
209
210         $singles = array('nickname', 'note', 'fn', 'n', 'adr');
211
212         foreach ($singles as $single) {
213
214             $nodes = self::_getChildrenByClass($hcardNode, $single, $xp);
215
216             if ($nodes->length > 0) {
217                 $node = $nodes->item(0);
218                 $hcard[$single] = $node->textContent;
219             }
220         }
221
222         return $hcard;
223     }
224
225     // XXX: this is a first pass; we probably need
226     // to handle things like ../ and ./ and so on
227
228     static function _rel2abs($rel, $wrt)
229     {
230         $parts = parse_url($rel);
231
232         if ($parts === false) {
233             return false;
234         }
235
236         // If it's got a scheme, use it
237
238         if (!empty($parts['scheme'])) {
239             return $rel;
240         }
241
242         $w = parse_url($wrt);
243
244         $base = $w['scheme'].'://'.$w['host'];
245
246         if ($rel[0] == '/') {
247             return $base.$rel;
248         }
249
250         $wp = explode('/', $w['path']);
251
252         array_pop($wp);
253
254         return $base.implode('/', $wp).'/'.$rel;
255     }
256 }