]> git.mxchange.org Git - friendica.git/blob - src/Model/Post/Link.php
spelling: failure
[friendica.git] / src / Model / Post / Link.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, 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  */
21
22 namespace Friendica\Model\Post;
23
24 use Friendica\Core\Logger;
25 use Friendica\Core\System;
26 use Friendica\Database\Database;
27 use Friendica\Database\DBA;
28 use Friendica\DI;
29 use Friendica\Network\HTTPClient\Client\HttpClientAccept;
30 use Friendica\Network\HTTPClient\Client\HttpClientOptions;
31 use Friendica\Util\HTTPSignature;
32 use Friendica\Util\Images;
33 use Friendica\Util\Proxy;
34 use Friendica\Object\Image;
35
36 /**
37  * Class Link
38  *
39  * This Model class handles post related external links
40  */
41 class Link
42 {
43         /**
44          * Check if the link is stored
45          *
46          * @param int $uriId
47          * @param string $url URL
48          * @return bool Whether record has been found
49          */
50         public static function exists(int $uriId, string $url): bool
51         {
52                 return DBA::exists('post-link', ['uri-id' => $uriId, 'url' => $url]);
53         }
54
55         /**
56          * Returns URL by URI id and other URL
57          *
58          * @param int $uriId
59          * @param string $url
60          * @param string $size
61          * @return string Found link URL + id on success, $url on failure
62          */
63         public static function getByLink(int $uriId, string $url, string $size = ''): string
64         {
65                 if (empty($uriId) || empty($url) || Proxy::isLocalImage($url)) {
66                         return $url;
67                 }
68
69                 if (!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https'])) {
70                         Logger::info('Bad URL, quitting', ['uri-id' => $uriId, 'url' => $url, 'callstack' => System::callstack(20)]);
71                         return $url;
72                 }
73
74                 $link = DBA::selectFirst('post-link', ['id'], ['uri-id' => $uriId, 'url' => $url]);
75                 if (!empty($link['id'])) {
76                         $id = $link['id'];
77                         Logger::info('Found', ['id' => $id, 'uri-id' => $uriId, 'url' => $url]);
78                 } else {
79                         $fields = self::fetchMimeType($url);
80                         $fields['uri-id'] = $uriId;
81                         $fields['url'] = $url;
82
83                         DBA::insert('post-link', $fields, Database::INSERT_IGNORE);
84                         $id = DBA::lastInsertId();
85                         Logger::info('Inserted', $fields);
86                 }
87
88                 if (empty($id)) {
89                         return $url;
90                 }
91
92                 $url = DI::baseUrl() . '/photo/link/';
93                 switch ($size) {
94                         case Proxy::SIZE_MICRO:
95                                 $url .= Proxy::PIXEL_MICRO . '/';
96                                 break;
97
98                         case Proxy::SIZE_THUMB:
99                                 $url .= Proxy::PIXEL_THUMB . '/';
100                                 break;
101
102                         case Proxy::SIZE_SMALL:
103                                 $url .= Proxy::PIXEL_SMALL . '/';
104                                 break;
105
106                         case Proxy::SIZE_MEDIUM:
107                                 $url .= Proxy::PIXEL_MEDIUM . '/';
108                                 break;
109
110                         case Proxy::SIZE_LARGE:
111                                 $url .= Proxy::PIXEL_LARGE . '/';
112                                 break;
113                 }
114                 return $url . $id;
115         }
116
117         /**
118          * Fetches MIME type by URL and Accept: header
119          *
120          * @param string $url URL to fetch
121          * @param string $accept Comma-separated list of expected response MIME type(s)
122          * @return array Discovered MIME type and blurhash or empty array on failure
123          */
124         private static function fetchMimeType(string $url, string $accept = HttpClientAccept::DEFAULT): array
125         {
126                 $timeout = DI::config()->get('system', 'xrd_timeout');
127
128                 try {
129                         $curlResult = HTTPSignature::fetchRaw($url, 0, [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::ACCEPT_CONTENT => $accept]);
130                         if (empty($curlResult) || !$curlResult->isSuccess()) {
131                                 return [];
132                         }
133                 } catch (\Exception $exception) {
134                         Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]);
135                         return [];
136                 }
137                 $fields = ['mimetype' => $curlResult->getHeader('Content-Type')[0]];
138
139                 $img_str = $curlResult->getBody();
140                 $image = new Image($img_str, Images::getMimeTypeByData($img_str));
141                 if ($image->isValid()) {
142                         $fields['mimetype'] = $image->getType();
143                         $fields['width']    = $image->getWidth();
144                         $fields['height']   = $image->getHeight();
145                         $fields['blurhash'] = $image->getBlurHash();
146                 }
147
148                 return $fields;
149         }
150
151         /**
152          * Add external links and replace them in the body
153          *
154          * @param integer $uriId
155          * @param string $body Item body formatted with BBCodes
156          * @return string Body with replaced links
157          */
158         public static function insertFromBody(int $uriId, string $body): string
159         {
160                 if (preg_match_all("/\[img\=([0-9]*)x([0-9]*)\](http.*?)\[\/img\]/ism", $body, $pictures, PREG_SET_ORDER)) {
161                         foreach ($pictures as $picture) {
162                                 $body = str_replace($picture[3], self::getByLink($uriId, $picture[3]), $body);
163                         }
164                 }
165
166                 if (preg_match_all("/\[img=(http[^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) {
167                         foreach ($pictures as $picture) {
168                                 $body = str_replace($picture[1], self::getByLink($uriId, $picture[1]), $body);
169                         }
170                 }
171
172                 if (preg_match_all("/\[img\](http[^\[\]]*)\[\/img\]/ism", $body, $pictures, PREG_SET_ORDER)) {
173                         foreach ($pictures as $picture) {
174                                 $body = str_replace($picture[1], self::getByLink($uriId, $picture[1]), $body);
175                         }
176                 }
177
178                 return trim($body);
179         }
180 }