Completed
Branch master (79509a)
by El
16:32 queued 06:47
created

lib/privatebin/data.php (2 issues)

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   http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   0.22
11
 */
12
13
/**
14
 * privatebin_data
15
 *
16
 * Model for data access, implemented as a singleton.
17
 */
18
class privatebin_data extends privatebin_abstract
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
{
20
    /**
21
     * directory where data is stored
22
     *
23
     * @access private
24
     * @static
25
     * @var string
26
     */
27
    private static $_dir = 'data/';
28
29
    /**
30
     * get instance of singleton
31
     *
32
     * @access public
33
     * @static
34
     * @param  array $options
35
     * @return privatebin_data
36
     */
37 3
    public static function getInstance($options = null)
38
    {
39
        // if given update the data directory
40
        if (
41 3
            is_array($options) &&
42 3
            array_key_exists('dir', $options)
43 3
        ) self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR;
44
        // if needed initialize the singleton
45 3
        if(!(self::$_instance instanceof privatebin_data)) {
46 1
            self::$_instance = new self;
47 1
            self::_init();
48 1
        }
49 3
        return self::$_instance;
50
    }
51
52
    /**
53
     * Create a paste.
54
     *
55
     * @access public
56
     * @param  string $pasteid
57
     * @param  array  $paste
58
     * @return bool
59
     */
60 2
    public function create($pasteid, $paste)
61
    {
62 2
        $storagedir = self::_dataid2path($pasteid);
63 2
        if (is_file($storagedir . $pasteid)) return false;
64 2
        if (!is_dir($storagedir)) mkdir($storagedir, 0705, true);
65 2
        return (bool) @file_put_contents($storagedir . $pasteid, json_encode($paste));
66
    }
67
68
    /**
69
     * Read a paste.
70
     *
71
     * @access public
72
     * @param  string $pasteid
73
     * @return stdClass|false
74
     */
75 2
    public function read($pasteid)
76
    {
77 2
        if(!$this->exists($pasteid)) return false;
78 2
        $paste = json_decode(
79 2
            file_get_contents(self::_dataid2path($pasteid) . $pasteid)
80 2
        );
81 2
        if (property_exists($paste->meta, 'attachment'))
82 2
        {
83 1
            $paste->attachment = $paste->meta->attachment;
84 1
            unset($paste->meta->attachment);
85 1
            if (property_exists($paste->meta, 'attachmentname'))
86 1
            {
87 1
                $paste->attachmentname = $paste->meta->attachmentname;
88 1
                unset($paste->meta->attachmentname);
89 1
            }
90 1
        }
91 2
        return $paste;
92
    }
93
94
    /**
95
     * Delete a paste and its discussion.
96
     *
97
     * @access public
98
     * @param  string $pasteid
99
     * @return void
100
     */
101 2
    public function delete($pasteid)
102
    {
103
        // Delete the paste itself.
104 2
        @unlink(self::_dataid2path($pasteid) . $pasteid);
105
106
        // Delete discussion if it exists.
107 2
        $discdir = self::_dataid2discussionpath($pasteid);
108 2
        if (is_dir($discdir))
109 2
        {
110
            // Delete all files in discussion directory
111 1
            $dir = dir($discdir);
112 1
            while (false !== ($filename = $dir->read()))
113
            {
114 1
                if (is_file($discdir.$filename)) @unlink($discdir.$filename);
115 1
            }
116 1
            $dir->close();
117
118
            // Delete the discussion directory.
119 1
            @rmdir($discdir);
120 1
        }
121 2
    }
122
123
    /**
124
     * Test if a paste exists.
125
     *
126
     * @access public
127
     * @param  string $dataid
128
     * @return void
129
     */
130 3
    public function exists($pasteid)
131
    {
132 3
        return is_file(self::_dataid2path($pasteid) . $pasteid);
133
    }
134
135
    /**
136
     * Create a comment in a paste.
137
     *
138
     * @access public
139
     * @param  string $pasteid
140
     * @param  string $parentid
141
     * @param  string $commentid
142
     * @param  array  $comment
143
     * @return bool
144
     */
145 1
    public function createComment($pasteid, $parentid, $commentid, $comment)
146
    {
147 1
        $storagedir = self::_dataid2discussionpath($pasteid);
148 1
        $filename = $pasteid . '.' . $commentid . '.' . $parentid;
149 1
        if (is_file($storagedir . $filename)) return false;
150 1
        if (!is_dir($storagedir)) mkdir($storagedir, 0705, true);
151 1
        return (bool) @file_put_contents($storagedir . $filename, json_encode($comment));
152
    }
153
154
    /**
155
     * Read all comments of paste.
156
     *
157
     * @access public
158
     * @param  string $pasteid
159
     * @return array
160
     */
161 1
    public function readComments($pasteid)
162
    {
163 1
        $comments = array();
164 1
        $discdir = self::_dataid2discussionpath($pasteid);
165 1
        if (is_dir($discdir))
166 1
        {
167
            // Delete all files in discussion directory
168 1
            $dir = dir($discdir);
169 1
            while (false !== ($filename = $dir->read()))
170
            {
171
                // Filename is in the form pasteid.commentid.parentid:
172
                // - pasteid is the paste this reply belongs to.
173
                // - commentid is the comment identifier itself.
174
                // - parentid is the comment this comment replies to (It can be pasteid)
175 1
                if (is_file($discdir . $filename))
176 1
                {
177 1
                    $comment = json_decode(file_get_contents($discdir . $filename));
178 1
                    $items = explode('.', $filename);
179
                    // Add some meta information not contained in file.
180 1
                    $comment->id = $items[1];
181 1
                    $comment->parentid = $items[2];
182
183
                    // Store in array
184 1
                    $key = $this->getOpenSlot($comments, (int) $comment->meta->postdate);
0 ignored issues
show
Are you sure the assignment to $key is correct as $this->getOpenSlot($comm...omment->meta->postdate) (which targets privatebin_abstract::getOpenSlot()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
185 1
                    $comments[$key] = $comment;
186 1
                }
187 1
            }
188 1
            $dir->close();
189
190
            // Sort comments by date, oldest first.
191 1
            ksort($comments);
192 1
        }
193 1
        return $comments;
194
    }
195
196
    /**
197
     * Test if a comment exists.
198
     *
199
     * @access public
200
     * @param  string $dataid
201
     * @param  string $parentid
202
     * @param  string $commentid
203
     * @return void
204
     */
205 1
    public function existsComment($pasteid, $parentid, $commentid)
206
    {
207 1
        return is_file(
208 1
            self::_dataid2discussionpath($pasteid) .
209 1
            $pasteid . '.' . $commentid . '.' . $parentid
210 1
        );
211
    }
212
213
    /**
214
     * initialize privatebin
215
     *
216
     * @access private
217
     * @static
218
     * @return void
219
     */
220 1
    private static function _init()
221
    {
222
        // Create storage directory if it does not exist.
223 1
        if (!is_dir(self::$_dir)) mkdir(self::$_dir, 0705);
224
        // Create .htaccess file if it does not exist.
225 1
        if (!is_file(self::$_dir . '.htaccess'))
226 1
        {
227
            file_put_contents(
228
                self::$_dir . '.htaccess',
229
                'Allow from none' . PHP_EOL .
230
                'Deny from all'. PHP_EOL
231
            );
232
        }
233 1
    }
234
235
    /**
236
     * Convert paste id to storage path.
237
     *
238
     * The idea is to creates subdirectories in order to limit the number of files per directory.
239
     * (A high number of files in a single directory can slow things down.)
240
     * eg. "f468483c313401e8" will be stored in "data/f4/68/f468483c313401e8"
241
     * High-trafic websites may want to deepen the directory structure (like Squid does).
242
     *
243
     * eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/'
244
     *
245
     * @access private
246
     * @static
247
     * @param  string $dataid
248
     * @return void
249
     */
250 3
    private static function _dataid2path($dataid)
251
    {
252 3
        return self::$_dir . substr($dataid,0,2) . '/' . substr($dataid,2,2) . '/';
253
    }
254
255
    /**
256
     * Convert paste id to discussion storage path.
257
     *
258
     * eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/e3570978f9e4aa90.discussion/'
259
     *
260
     * @access private
261
     * @static
262
     * @param  string $dataid
263
     * @return void
264
     */
265 2
    private static function _dataid2discussionpath($dataid)
266
    {
267 2
        return self::_dataid2path($dataid) . $dataid . '.discussion/';
268
    }
269
}
270