]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/lib/discoveryhints.php
Merge commit 'origin/master' into testing
[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
24     static function fromXRD($xrd)
25     {
26         $hints = array();
27
28         foreach ($xrd->links as $link) {
29             switch ($link['rel']) {
30             case Discovery::PROFILEPAGE:
31                 $hints['profileurl'] = $link['href'];
32                 break;
33             case Salmon::NS_REPLIES:
34                 $hints['salmon'] = $link['href'];
35                 break;
36             case Discovery::UPDATESFROM:
37                 $hints['feedurl'] = $link['href'];
38                 break;
39             case Discovery::HCARD:
40                 $hints['hcardurl'] = $link['href'];
41                 break;
42             default:
43                 break;
44             }
45         }
46
47         return $hints;
48     }
49
50     static function fromHcardUrl($url)
51     {
52         $client = new HTTPClient();
53         $client->setHeader('Accept', 'text/html,application/xhtml+xml');
54         $response = $client->get($url);
55
56         if (!$response->isOk()) {
57             return null;
58         }
59
60         return self::hcardHints($response->getBody(),
61                                 $response->getUrl());
62     }
63
64     static function hcardHints($body, $url)
65     {
66         $hcard = self::_hcard($body, $url);
67
68         if (empty($hcard)) {
69             return array();
70         }
71
72         $hints = array();
73
74         // XXX: don't copy stuff into an array and then copy it again
75
76         if (array_key_exists('nickname', $hcard)) {
77             $hints['nickname'] = $hcard['nickname'];
78         }
79
80         if (array_key_exists('fn', $hcard)) {
81             $hints['fullname'] = $hcard['fn'];
82         } else if (array_key_exists('n', $hcard)) {
83             $hints['fullname'] = implode(' ', $hcard['n']);
84         }
85
86         if (array_key_exists('photo', $hcard)) {
87             $hints['avatar'] = $hcard['photo'][0];
88         }
89
90         if (array_key_exists('note', $hcard)) {
91             $hints['bio'] = $hcard['note'];
92         }
93
94         if (array_key_exists('adr', $hcard)) {
95             if (is_string($hcard['adr'])) {
96                 $hints['location'] = $hcard['adr'];
97             } else if (is_array($hcard['adr'])) {
98                 $hints['location'] = implode(' ', $hcard['adr']);
99             }
100         }
101
102         if (array_key_exists('url', $hcard)) {
103             if (is_string($hcard['url'])) {
104                 $hints['homepage'] = $hcard['url'];
105             } else if (is_array($hcard['url']) && !empty($hcard['url'])) {
106                 // HACK get the last one; that's how our hcards look
107                 $hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
108             }
109         }
110
111         return $hints;
112     }
113
114     static function _hcard($body, $url)
115     {
116         // DOMDocument::loadHTML may throw warnings on unrecognized elements.
117
118         $old = error_reporting(error_reporting() & ~E_WARNING);
119
120         $doc = new DOMDocument();
121         $doc->loadHTML($body);
122
123         error_reporting($old);
124
125         $xp = new DOMXPath($doc);
126
127         $hcardNodes = self::_getChildrenByClass($doc->documentElement, 'vcard', $xp);
128
129         $hcards = array();
130
131         for ($i = 0; $i < $hcardNodes->length; $i++) {
132
133             $hcardNode = $hcardNodes->item($i);
134
135             $hcard = self::_hcardFromNode($hcardNode, $xp, $url);
136
137             $hcards[] = $hcard;
138         }
139
140         $repr = null;
141
142         foreach ($hcards as $hcard) {
143             if (in_array($url, $hcard['url'])) {
144                 $repr = $hcard;
145                 break;
146             }
147         }
148
149         if (!is_null($repr)) {
150             return $repr;
151         } else if (count($hcards) > 0) {
152             return $hcards[0];
153         } else {
154             return null;
155         }
156     }
157
158     function _getChildrenByClass($el, $cls, $xp)
159     {
160         // borrowed from hkit. Thanks dudes!
161
162         $qry = ".//*[contains(concat(' ',normalize-space(@class),' '),' $cls ')]";
163
164         $nodes = $xp->query($qry, $el);
165
166         return $nodes;
167     }
168
169     function _hcardFromNode($hcardNode, $xp, $base)
170     {
171         $hcard = array();
172
173         $hcard['url'] = array();
174
175         $urlNodes = self::_getChildrenByClass($hcardNode, 'url', $xp);
176
177         for ($j = 0; $j < $urlNodes->length; $j++) {
178
179             $urlNode = $urlNodes->item($j);
180
181             if ($urlNode->hasAttribute('href')) {
182                 $url = $urlNode->getAttribute('href');
183             } else {
184                 $url = $urlNode->textContent;
185             }
186
187             $hcard['url'][] = self::_rel2abs($url, $base);
188         }
189
190         $hcard['photo'] = array();
191
192         $photoNodes = self::_getChildrenByClass($hcardNode, 'photo', $xp);
193
194         for ($j = 0; $j < $photoNodes->length; $j++) {
195             $photoNode = $photoNodes->item($j);
196             if ($photoNode->hasAttribute('src')) {
197                 $url = $photoNode->getAttribute('src');
198             } else if ($photoNode->hasAttribute('href')) {
199                 $url = $photoNode->getAttribute('href');
200             } else {
201                 $url = $photoNode->textContent;
202             }
203             $hcard['photo'][] = self::_rel2abs($url, $base);
204         }
205
206         $singles = array('nickname', 'note', 'fn', 'n', 'adr');
207
208         foreach ($singles as $single) {
209
210             $nodes = self::_getChildrenByClass($hcardNode, $single, $xp);
211
212             if ($nodes->length > 0) {
213                 $node = $nodes->item(0);
214                 $hcard[$single] = $node->textContent;
215             }
216         }
217
218         return $hcard;
219     }
220
221     // XXX: this is a first pass; we probably need
222     // to handle things like ../ and ./ and so on
223
224     static function _rel2abs($rel, $wrt)
225     {
226         $parts = parse_url($rel);
227
228         if ($parts === false) {
229             return false;
230         }
231
232         // If it's got a scheme, use it
233
234         if (!empty($parts['scheme'])) {
235             return $rel;
236         }
237
238         $w = parse_url($wrt);
239
240         $base = $w['scheme'].'://'.$w['host'];
241
242         if ($rel[0] == '/') {
243             return $base.$rel;
244         }
245
246         $wp = explode('/', $w['path']);
247
248         array_pop($wp);
249
250         return $base.implode('/', $wp).'/'.$rel;
251     }
252 }