]> git.mxchange.org Git - friendica.git/blob - src/Model/Attach.php
Merge pull request #10969 from MrPetovan/task/remove-private-contacts
[friendica.git] / src / Model / Attach.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, 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;
23
24 use Friendica\Core\System;
25 use Friendica\Database\DBA;
26 use Friendica\Database\DBStructure;
27 use Friendica\DI;
28 use Friendica\Core\Storage\Exception\InvalidClassStorageException;
29 use Friendica\Core\Storage\Exception\ReferenceStorageException;
30 use Friendica\Object\Image;
31 use Friendica\Util\DateTimeFormat;
32 use Friendica\Util\Mimetype;
33 use Friendica\Security\Security;
34
35 /**
36  * Class to handle attach dabatase table
37  */
38 class Attach
39 {
40
41         /**
42          * Return a list of fields that are associated with the attach table
43          *
44          * @return array field list
45          * @throws \Exception
46          */
47         private static function getFields()
48         {
49                 $allfields = DBStructure::definition(DI::app()->getBasePath(), false);
50                 $fields = array_keys($allfields['attach']['fields']);
51                 array_splice($fields, array_search('data', $fields), 1);
52                 return $fields;
53         }
54
55         /**
56          * Select rows from the attach table and return them as array
57          *
58          * @param array $fields     Array of selected fields, empty for all
59          * @param array $conditions Array of fields for conditions
60          * @param array $params     Array of several parameters
61          *
62          * @return array
63          *
64          * @throws \Exception
65          * @see   \Friendica\Database\DBA::selectToArray
66          */
67         public static function selectToArray(array $fields = [], array $conditions = [], array $params = [])
68         {
69                 if (empty($fields)) {
70                         $fields = self::getFields();
71                 }
72
73                 return DBA::selectToArray('attach', $fields, $conditions, $params);
74         }
75
76         /**
77          * Retrieve a single record from the attach table
78          *
79          * @param array $fields     Array of selected fields, empty for all
80          * @param array $conditions Array of fields for conditions
81          * @param array $params     Array of several parameters
82          *
83          * @return bool|array
84          *
85          * @throws \Exception
86          * @see   \Friendica\Database\DBA::select
87          */
88         public static function selectFirst(array $fields = [], array $conditions = [], array $params = [])
89         {
90                 if (empty($fields)) {
91                         $fields = self::getFields();
92                 }
93
94                 return DBA::selectFirst('attach', $fields, $conditions, $params);
95         }
96
97         /**
98          * Check if attachment with given conditions exists
99          *
100          * @param array $conditions Array of extra conditions
101          *
102          * @return boolean
103          * @throws \Exception
104          */
105         public static function exists(array $conditions)
106         {
107                 return DBA::exists('attach', $conditions);
108         }
109
110         /**
111          * Retrive a single record given the ID
112          *
113          * @param int $id Row id of the record
114          *
115          * @return bool|array
116          *
117          * @throws \Exception
118          * @see   \Friendica\Database\DBA::select
119          */
120         public static function getById($id)
121         {
122                 return self::selectFirst([], ['id' => $id]);
123         }
124
125         /**
126          * Retrive a single record given the ID
127          *
128          * @param int $id Row id of the record
129          *
130          * @return bool|array
131          *
132          * @throws \Exception
133          * @see   \Friendica\Database\DBA::select
134          */
135         public static function getByIdWithPermission($id)
136         {
137                 $r = self::selectFirst(['uid'], ['id' => $id]);
138                 if ($r === false) {
139                         return false;
140                 }
141
142                 $sql_acl = Security::getPermissionsSQLByUserId($r['uid']);
143
144                 $conditions = [
145                         '`id` = ?' . $sql_acl,
146                         $id
147                 ];
148
149                 $item = self::selectFirst([], $conditions);
150
151                 return $item;
152         }
153
154         /**
155          * Get file data for given row id. null if row id does not exist
156          *
157          * @param array $item Attachment data. Needs at least 'id', 'backend-class', 'backend-ref'
158          *
159          * @return string  file data
160          * @throws \Exception
161          */
162         public static function getData($item)
163         {
164                 if (!empty($item['data'])) {
165                         return $item['data'];
166                 }
167
168                 try {
169                         $backendClass = DI::storageManager()->getByName($item['backend-class'] ?? '');
170                         $backendRef   = $item['backend-ref'];
171                         return $backendClass->get($backendRef);
172                 } catch (InvalidClassStorageException $storageException) {
173                         // legacy data storage in 'data' column
174                         $i = self::selectFirst(['data'], ['id' => $item['id']]);
175                         if ($i === false) {
176                                 return null;
177                         }
178                         return $i['data'];
179                 } catch (ReferenceStorageException $referenceStorageException) {
180                         DI::logger()->debug('No data found for item', ['item' => $item, 'exception' => $referenceStorageException]);
181                         return '';
182                 }
183         }
184
185         /**
186          * Store new file metadata in db and binary in default backend
187          *
188          * @param string  $data      Binary data
189          * @param integer $uid       User ID
190          * @param string  $filename  Filename
191          * @param string  $filetype  Mimetype. optional, default = ''
192          * @param integer $filesize  File size in bytes. optional, default = null
193          * @param string  $allow_cid Permissions, allowed contacts. optional, default = ''
194          * @param string  $allow_gid Permissions, allowed groups. optional, default = ''
195          * @param string  $deny_cid  Permissions, denied contacts.optional, default = ''
196          * @param string  $deny_gid  Permissions, denied greoup.optional, default = ''
197          *
198          * @return boolean/integer Row id on success, False on errors
199          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
200          */
201         public static function store($data, $uid, $filename, $filetype = '' , $filesize = null, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '')
202         {
203                 if ($filetype === '') {
204                         $filetype = Mimetype::getContentType($filename);
205                 }
206
207                 if (is_null($filesize)) {
208                         $filesize = strlen($data);
209                 }
210
211                 $backend_ref = DI::storage()->put($data);
212                 $data = '';
213
214                 $hash = System::createGUID(64);
215                 $created = DateTimeFormat::utcNow();
216
217                 $fields = [
218                         'uid' => $uid,
219                         'hash' => $hash,
220                         'filename' => $filename,
221                         'filetype' => $filetype,
222                         'filesize' => $filesize,
223                         'data' => $data,
224                         'created' => $created,
225                         'edited' => $created,
226                         'allow_cid' => $allow_cid,
227                         'allow_gid' => $allow_gid,
228                         'deny_cid' => $deny_cid,
229                         'deny_gid' => $deny_gid,
230                         'backend-class' => (string)DI::storage(),
231                         'backend-ref' => $backend_ref
232                 ];
233
234                 $r = DBA::insert('attach', $fields);
235                 if ($r === true) {
236                         return DBA::lastInsertId();
237                 }
238                 return $r;
239         }
240
241         /**
242          * Store new file metadata in db and binary in default backend from existing file
243          *
244          * @param        $src
245          * @param        $uid
246          * @param string $filename
247          * @param string $allow_cid
248          * @param string $allow_gid
249          * @param string $deny_cid
250          * @param string $deny_gid
251          * @return boolean True on success
252          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
253          */
254         public static function storeFile($src, $uid, $filename = '', $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '')
255         {
256                 if ($filename === '') {
257                         $filename = basename($src);
258                 }
259
260                 $data = @file_get_contents($src);
261
262                 return self::store($data, $uid, $filename, '', null, $allow_cid, $allow_gid,  $deny_cid, $deny_gid);
263         }
264
265
266         /**
267          * Update an attached file
268          *
269          * @param array         $fields     Contains the fields that are updated
270          * @param array         $conditions Condition array with the key values
271          * @param Image         $img        Image data to update. Optional, default null.
272          * @param array|boolean $old_fields Array with the old field values that are about to be replaced (true = update on duplicate)
273          *
274          * @return boolean  Was the update successful?
275          *
276          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
277          * @see   \Friendica\Database\DBA::update
278          */
279         public static function update($fields, $conditions, Image $img = null, array $old_fields = [])
280         {
281                 if (!is_null($img)) {
282                         // get items to update
283                         $items = self::selectToArray(['backend-class','backend-ref'], $conditions);
284
285                         foreach($items as $item) {
286                                 try {
287                                         $backend_class         = DI::storageManager()->getWritableStorageByName($item['backend-class'] ?? '');
288                                         $fields['backend-ref'] = $backend_class->put($img->asString(), $item['backend-ref'] ?? '');
289                                 } catch (InvalidClassStorageException $storageException) {
290                                         DI::logger()->debug('Storage class not found.', ['conditions' => $conditions, 'exception' => $storageException]);
291                                 } catch (ReferenceStorageException $referenceStorageException) {
292                                         DI::logger()->debug('Item doesn\'t exist.', ['conditions' => $conditions, 'exception' => $referenceStorageException]);
293                                 }
294                         }
295                 }
296
297                 $fields['edited'] = DateTimeFormat::utcNow();
298
299                 return DBA::update('attach', $fields, $conditions, $old_fields);
300         }
301
302
303         /**
304          * Delete info from table and data from storage
305          *
306          * @param array $conditions Field condition(s)
307          * @param array $options    Options array, Optional
308          *
309          * @return boolean
310          *
311          * @throws \Exception
312          * @see   \Friendica\Database\DBA::delete
313          */
314         public static function delete(array $conditions, array $options = [])
315         {
316                 // get items to delete data info
317                 $items = self::selectToArray(['backend-class','backend-ref'], $conditions);
318
319                 foreach($items as $item) {
320                         try {
321                                 $backend_class = DI::storageManager()->getWritableStorageByName($item['backend-class'] ?? '');
322                                 $backend_class->delete($item['backend-ref'] ?? '');
323                         } catch (InvalidClassStorageException $storageException) {
324                                 DI::logger()->debug('Storage class not found.', ['conditions' => $conditions, 'exception' => $storageException]);
325                         } catch (ReferenceStorageException $referenceStorageException) {
326                                 DI::logger()->debug('Item doesn\'t exist.', ['conditions' => $conditions, 'exception' => $referenceStorageException]);
327                         }
328                 }
329
330                 return DBA::delete('attach', $conditions, $options);
331         }
332 }