Completed
Push — master ( 4f070d...a1881f )
by El
03:48
created

lib/Model/Paste.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1
11
 */
12
13
namespace PrivateBin\Model;
14
15
use Exception;
16
use PrivateBin\Persistence\ServerSalt;
17
use PrivateBin\PrivateBin;
18
use PrivateBin\Sjcl;
19
20
/**
21
 * Paste
22
 *
23
 * Model of a PrivateBin paste.
24
 */
25
class Paste extends AbstractModel
26
{
27
    /**
28
     * Get paste data.
29
     *
30
     * @access public
31
     * @throws Exception
32
     * @return stdClass
33
     */
34 40
    public function get()
35
    {
36 40
        $data = $this->_store->read($this->getId());
37 40
        if ($data === false) {
38 1
            throw new Exception(PrivateBin::GENERIC_ERROR, 64);
39
        }
40
41
        // check if paste has expired and delete it if neccessary.
42 39
        if (property_exists($data->meta, 'expire_date')) {
43 5
            if ($data->meta->expire_date < time()) {
44 4
                $this->delete();
45 4
                throw new Exception(PrivateBin::GENERIC_ERROR, 63);
46
            }
47
            // We kindly provide the remaining time before expiration (in seconds)
48 1
            $data->meta->remaining_time = $data->meta->expire_date - time();
49
        }
50
51
        // check if non-expired burn after reading paste needs to be deleted
52 35
        if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading && $this->_conf->getKey('instantburnafterreading')) {
53 2
            $this->delete();
54
        }
55
56
        // set formatter for for the view.
57 35
        if (!property_exists($data->meta, 'formatter')) {
58
            // support < 0.21 syntax highlighting
59 6
            if (property_exists($data->meta, 'syntaxcoloring') && $data->meta->syntaxcoloring === true) {
60 2
                $data->meta->formatter = 'syntaxhighlighting';
61
            } else {
62 4
                $data->meta->formatter = $this->_conf->getKey('defaultformatter');
63
            }
64
        }
65
66
        // support old paste format with server wide salt
67 35
        if (!property_exists($data->meta, 'salt')) {
68 4
            $data->meta->salt = ServerSalt::get();
69
        }
70 35
        $data->comments       = array_values($this->getComments());
71 35
        $data->comment_count  = count($data->comments);
72 35
        $data->comment_offset = 0;
73 35
        $data->{'@context'}   = 'js/paste.jsonld';
74 35
        $this->_data          = $data;
75 35
        return $this->_data;
76
    }
77
78
    /**
79
     * Store the paste's data.
80
     *
81
     * @access public
82
     * @throws Exception
83
     */
84 35
    public function store()
85
    {
86
        // Check for improbable collision.
87 35
        if ($this->exists()) {
88 3
            throw new Exception('You are unlucky. Try again.', 75);
89
        }
90
91 33
        $this->_data->meta->postdate = time();
92 33
        $this->_data->meta->salt     = serversalt::generate();
93
94
        // store paste
95
        if (
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96 33
            $this->_store->create(
97 33
                $this->getId(),
98 33
                json_decode(json_encode($this->_data), true)
99 33
            ) === false
100
        ) {
101
            throw new Exception('Error saving paste. Sorry.', 76);
102
        }
103 33
    }
104
105
    /**
106
     * Delete the paste.
107
     *
108
     * @access public
109
     * @throws Exception
110
     */
111 26
    public function delete()
112
    {
113 26
        $this->_store->delete($this->getId());
114 26
    }
115
116
    /**
117
     * Test if paste exists in store.
118
     *
119
     * @access public
120
     * @return bool
121
     */
122 84
    public function exists()
123
    {
124 84
        return $this->_store->exists($this->getId());
125
    }
126
127
    /**
128
     * Get a comment, optionally a specific instance.
129
     *
130
     * @access public
131
     * @param string $parentId
132
     * @param string $commentId
133
     * @throws Exception
134
     * @return Comment
135
     */
136 20
    public function getComment($parentId, $commentId = null)
137
    {
138 20
        if (!$this->exists()) {
139 1
            throw new Exception('Invalid data.', 62);
140
        }
141 19
        $comment = new Comment($this->_conf, $this->_store);
142 19
        $comment->setPaste($this);
143 19
        $comment->setParentId($parentId);
144 17
        if ($commentId !== null) {
145 5
            $comment->setId($commentId);
146
        }
147 17
        return $comment;
148
    }
149
150
    /**
151
     * Get all comments, if any.
152
     *
153
     * @access public
154
     * @return array
155
     */
156 35
    public function getComments()
157
    {
158 35
        return $this->_store->readComments($this->getId());
159
    }
160
161
    /**
162
     * Generate the "delete" token.
163
     *
164
     * The token is the hmac of the pastes ID signed with the server salt.
165
     * The paste can be deleted by calling:
166
     * http://example.com/privatebin/?pasteid=<pasteid>&deletetoken=<deletetoken>
167
     *
168
     * @access public
169
     * @return string
170
     */
171 32
    public function getDeleteToken()
172
    {
173 32
        if (!property_exists($this->_data->meta, 'salt')) {
174
            $this->get();
175
        }
176 32
        return hash_hmac(
177 32
            $this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256',
178 32
            $this->getId(),
179 32
            $this->_data->meta->salt
180
        );
181
    }
182
183
    /**
184
     * Set paste's attachment.
185
     *
186
     * @access public
187
     * @param string $attachment
188
     * @throws Exception
189
     */
190 2 View Code Duplication
    public function setAttachment($attachment)
191
    {
192 2
        if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachment)) {
193
            throw new Exception('Invalid attachment.', 71);
194
        }
195 2
        $this->_data->meta->attachment = $attachment;
196 2
    }
197
198
    /**
199
     * Set paste's attachment name.
200
     *
201
     * @access public
202
     * @param string $attachmentname
203
     * @throws Exception
204
     */
205 2 View Code Duplication
    public function setAttachmentName($attachmentname)
206
    {
207 2
        if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachmentname)) {
208
            throw new Exception('Invalid attachment.', 72);
209
        }
210 2
        $this->_data->meta->attachmentname = $attachmentname;
211 2
    }
212
213
    /**
214
     * Set paste expiration.
215
     *
216
     * @access public
217
     * @param string $expiration
218
     */
219 7
    public function setExpiration($expiration)
220
    {
221 7
        $expire_options = $this->_conf->getSection('expire_options');
222 7
        if (array_key_exists($expiration, $expire_options)) {
223 5
            $expire = $expire_options[$expiration];
224
        } else {
225
            // using getKey() to ensure a default value is present
226 2
            $expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
227
        }
228 7
        if ($expire > 0) {
229 7
            $this->_data->meta->expire_date = time() + $expire;
230
        }
231 7
    }
232
233
    /**
234
     * Set paste's burn-after-reading type.
235
     *
236
     * @access public
237
     * @param string $burnafterreading
238
     * @throws Exception
239
     */
240 3
    public function setBurnafterreading($burnafterreading = '1')
241
    {
242 3
        if ($burnafterreading === '0') {
243 1
            $this->_data->meta->burnafterreading = false;
244
        } else {
245 3
            if ($burnafterreading !== '1') {
246 2
                throw new Exception('Invalid data.', 73);
247
            }
248 1
            $this->_data->meta->burnafterreading = true;
249 1
            $this->_data->meta->opendiscussion   = false;
250
        }
251 1
    }
252
253
    /**
254
     * Set paste's discussion state.
255
     *
256
     * @access public
257
     * @param string $opendiscussion
258
     * @throws Exception
259
     */
260 11
    public function setOpendiscussion($opendiscussion = '1')
261
    {
262
        if (
263 11
            !$this->_conf->getKey('discussion') ||
264 11
            $this->isBurnafterreading() ||
265 11
            $opendiscussion === '0'
266
        ) {
267 1
            $this->_data->meta->opendiscussion = false;
268
        } else {
269 11
            if ($opendiscussion !== '1') {
270 2
                throw new Exception('Invalid data.', 74);
271
            }
272 9
            $this->_data->meta->opendiscussion = true;
273
        }
274 9
    }
275
276
    /**
277
     * Set paste's format.
278
     *
279
     * @access public
280
     * @param string $format
281
     * @throws Exception
282
     */
283 8
    public function setFormatter($format)
284
    {
285 8
        if (!array_key_exists($format, $this->_conf->getSection('formatter_options'))) {
286 2
            $format = $this->_conf->getKey('defaultformatter');
287
        }
288 8
        $this->_data->meta->formatter = $format;
289 8
    }
290
291
    /**
292
     * Check if paste is of burn-after-reading type.
293
     *
294
     * @access public
295
     * @throws Exception
296
     * @return bool
297
     */
298 25 View Code Duplication
    public function isBurnafterreading()
299
    {
300 25
        if (!property_exists($this->_data, 'data')) {
301 14
            $this->get();
302
        }
303 23
        return property_exists($this->_data->meta, 'burnafterreading') &&
304 23
               $this->_data->meta->burnafterreading === true;
305
    }
306
307
    /**
308
     * Check if paste has discussions enabled.
309
     *
310
     * @access public
311
     * @throws Exception
312
     * @return bool
313
     */
314 13 View Code Duplication
    public function isOpendiscussion()
315
    {
316 13
        if (!property_exists($this->_data, 'data')) {
317 8
            $this->get();
318
        }
319 13
        return property_exists($this->_data->meta, 'opendiscussion') &&
320 13
               $this->_data->meta->opendiscussion === true;
321
    }
322
}
323