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