]> git.mxchange.org Git - friendica.git/blob - src/Module/User/PortableContacts.php
9f98ca7ebe0849fb1e97042cb0456a82a42fcf29
[friendica.git] / src / Module / User / PortableContacts.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  * @see https://web.archive.org/web/20160405005550/http://portablecontacts.net/draft-spec.html
21  */
22
23 namespace Friendica\Module\User;
24
25 use Friendica\App;
26 use Friendica\BaseModule;
27 use Friendica\Content\Text\BBCode;
28 use Friendica\Core\Cache\Capability\ICanCache;
29 use Friendica\Core\Config\Capability\IManageConfigValues;
30 use Friendica\Core\L10n;
31 use Friendica\Core\Protocol;
32 use Friendica\Core\System;
33 use Friendica\Database\Database;
34 use Friendica\Module\Response;
35 use Friendica\Network\HTTPException;
36 use Friendica\Util\DateTimeFormat;
37 use Friendica\Util\Profiler;
38 use Psr\Log\LoggerInterface;
39
40 /**
41  * Minimal implementation of the Portable Contacts protocol
42  * @see https://portablecontacts.github.io
43  */
44 class PortableContacts extends BaseModule
45 {
46         /** @var IManageConfigValues */
47         private $config;
48         /** @var Database */
49         private $database;
50         /** @var ICanCache */
51         private $cache;
52
53         public function __construct(ICanCache $cache, Database $database, IManageConfigValues $config, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
54         {
55                 parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
56
57                 $this->config   = $config;
58                 $this->database = $database;
59                 $this->cache    = $cache;
60         }
61
62         protected function rawContent(array $request = [])
63         {
64                 if ($this->config->get('system', 'block_public') || $this->config->get('system', 'block_local_dir')) {
65                         throw new HTTPException\ForbiddenException();
66                 }
67
68                 $format = $request['format'] ?? 'json';
69                 if ($format !== 'json') {
70                         throw new HTTPException\UnsupportedMediaTypeException();
71                 }
72
73                 $totalResults = $this->database->count('profile', ['net-publish' => true]);
74                 if (!$totalResults) {
75                         throw new HTTPException\ForbiddenException();
76                 }
77
78                 if (!empty($request['startIndex']) && is_numeric($request['startIndex'])) {
79                         $startIndex = intval($request['startIndex']);
80                 } else {
81                         $startIndex = 0;
82                 }
83
84                 $itemsPerPage = !empty($request['count']) && is_numeric($request['count']) ? intval($request['count']) : $totalResults;
85
86                 $this->logger->info('Start system mode query');
87                 $contacts = $this->database->selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
88                 $this->logger->info('Query done');
89
90                 $return = [];
91                 if (!empty($request['sorted'])) {
92                         $return['sorted'] = false;
93                 }
94
95                 if (!empty($request['filtered'])) {
96                         $return['filtered'] = false;
97                 }
98
99                 if (!empty($request['updatedSince'])) {
100                         $return['updatedSince'] = false;
101                 }
102
103                 $return['startIndex']   = $startIndex;
104                 $return['itemsPerPage'] = $itemsPerPage;
105                 $return['totalResults'] = $totalResults;
106
107                 $return['entry'] = [];
108
109                 $selectedFields = [
110                         'id'                => false,
111                         'displayName'       => false,
112                         'urls'              => false,
113                         'updated'           => false,
114                         'preferredUsername' => false,
115                         'photos'            => false,
116                         'aboutMe'           => false,
117                         'currentLocation'   => false,
118                         'network'           => false,
119                         'tags'              => false,
120                         'address'           => false,
121                         'contactType'       => false,
122                         'generation'        => false
123                 ];
124
125                 if (empty($request['fields']) || $request['fields'] == '@all') {
126                         foreach ($selectedFields as $k => $v) {
127                                 $selectedFields[$k] = true;
128                         }
129                 } else {
130                         $fields_req = explode(',', $request['fields']);
131                         foreach ($fields_req as $f) {
132                                 $selectedFields[trim($f)] = true;
133                         }
134                 }
135
136                 if (!$contacts) {
137                         $return['entry'][] = [];
138                 }
139
140                 foreach ($contacts as $contact) {
141                         if (!isset($contact['updated'])) {
142                                 $contact['updated'] = '';
143                         }
144
145                         if (!isset($contact['generation'])) {
146                                 $contact['generation'] = 1;
147                         }
148
149                         if (empty($contact['keywords']) && isset($contact['pub_keywords'])) {
150                                 $contact['keywords'] = $contact['pub_keywords'];
151                         }
152
153                         if (isset($contact['account-type'])) {
154                                 $contact['contact-type'] = $contact['account-type'];
155                         }
156
157                         $cacheKey = 'about:' . $contact['nick'] . ':' . DateTimeFormat::utc($contact['updated'], DateTimeFormat::ATOM);
158                         $about    = $this->cache->get($cacheKey);
159                         if (is_null($about)) {
160                                 $about = BBCode::convertForUriId($contact['uri-id'], $contact['about']);
161                                 $this->cache->set($cacheKey, $about);
162                         }
163
164                         // Non connected persons can only see the keywords of a Diaspora account
165                         if ($contact['network'] == Protocol::DIASPORA) {
166                                 $contact['location'] = '';
167                                 $about               = '';
168                         }
169
170                         $entry = [];
171                         if ($selectedFields['id']) {
172                                 $entry['id'] = (int)$contact['id'];
173                         }
174
175                         if ($selectedFields['displayName']) {
176                                 $entry['displayName'] = $contact['name'];
177                         }
178
179                         if ($selectedFields['aboutMe']) {
180                                 $entry['aboutMe'] = $about;
181                         }
182
183                         if ($selectedFields['currentLocation']) {
184                                 $entry['currentLocation'] = $contact['location'];
185                         }
186
187                         if ($selectedFields['generation']) {
188                                 $entry['generation'] = (int)$contact['generation'];
189                         }
190
191                         if ($selectedFields['urls']) {
192                                 $entry['urls'] = [['value' => $contact['url'], 'type' => 'profile']];
193                                 if ($contact['addr'] && ($contact['network'] !== Protocol::MAIL)) {
194                                         $entry['urls'][] = ['value' => 'acct:' . $contact['addr'], 'type' => 'webfinger'];
195                                 }
196                         }
197
198                         if ($selectedFields['preferredUsername']) {
199                                 $entry['preferredUsername'] = $contact['nick'];
200                         }
201
202                         if ($selectedFields['updated']) {
203                                 $entry['updated'] = $contact['success_update'];
204
205                                 if ($contact['name-date'] > $entry['updated']) {
206                                         $entry['updated'] = $contact['name-date'];
207                                 }
208
209                                 if ($contact['uri-date'] > $entry['updated']) {
210                                         $entry['updated'] = $contact['uri-date'];
211                                 }
212
213                                 if ($contact['avatar-date'] > $entry['updated']) {
214                                         $entry['updated'] = $contact['avatar-date'];
215                                 }
216
217                                 $entry['updated'] = date('c', strtotime($entry['updated']));
218                         }
219
220                         if ($selectedFields['photos']) {
221                                 $entry['photos'] = [['value' => $contact['photo'], 'type' => 'profile']];
222                         }
223
224                         if ($selectedFields['network']) {
225                                 $entry['network'] = $contact['network'];
226                                 if (($entry['network'] == '') && ($contact['self'])) {
227                                         $entry['network'] = Protocol::DFRN;
228                                 }
229                         }
230
231                         if ($selectedFields['tags']) {
232                                 $tags = str_replace(',', ' ', $contact['keywords'] ?? '');
233                                 $tags = explode(' ', $tags);
234
235                                 $cleaned = [];
236                                 foreach ($tags as $tag) {
237                                         $tag = trim(strtolower($tag));
238                                         if ($tag != '') {
239                                                 $cleaned[] = $tag;
240                                         }
241                                 }
242
243                                 $entry['tags'] = [$cleaned];
244                         }
245
246                         if ($selectedFields['address']) {
247                                 $entry['address'] = [];
248
249                                 if (isset($contact['locality'])) {
250                                         $entry['address']['locality'] = $contact['locality'];
251                                 }
252
253                                 if (isset($contact['region'])) {
254                                         $entry['address']['region'] = $contact['region'];
255                                 }
256
257                                 if (isset($contact['country'])) {
258                                         $entry['address']['country'] = $contact['country'];
259                                 }
260                         }
261
262                         if ($selectedFields['contactType']) {
263                                 $entry['contactType'] = intval($contact['contact-type']);
264                         }
265
266                         $return['entry'][] = $entry;
267                 }
268
269                 $this->logger->info('End of poco');
270
271                 System::jsonExit($return);
272         }
273 }