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