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