/var/www/(Del)hsihk.com/wp-content/plugins/backupbuddy/classes/fileoptions.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<?php
/* Class pb_backupbuddy_fileoptions
 *
 * @author Dustin Bolton
 * @date April, 2013
 *
 * Uses the filesystem for storing options data. Data is serialized & base64 encoded.
 * By default uses a locking mechanism to lock out accessing the options from another instance
 * while open with this instance. Lock automatically removed on class destruction.
 *
 * After construction check is_ok() function to verify === true. If not true returns error message.
 *
 * Example usage:
 * $backup_options = new pb_backupbuddy_fileoptions( $filename );
 * if ( $backup_options->is_ok() ) {
 *     $backup_options->options = array( 'hello' => 'world' );
 *     $backup_options->save(); // Optional force save now. If omitted destructor will hopefully save.
 * }
 
 Another in-use example:
 
pb_backupbuddy::status( 'details', 'About to load fileoptions data.' );
require_once( pb_backupbuddy::plugin_path() . '/classes/fileoptions.php' );
$fileoptions_obj = new pb_backupbuddy_fileoptions( backupbuddy_core::getLogDirectory() . 'fileoptions/send-' . $send_id . '.txt', $read_only = true, $ignore_lock = true, $create_file = false );
if ( true !== ( $result = $fileoptions_obj->is_ok() ) ) {
    pb_backupbuddy::status( 'error', __('Fatal Error #9034.2344848. Unable to access fileoptions data.', 'it-l10n-backupbuddy' ) . ' Error: ' . $result );
    return false;
}
pb_backupbuddy::status( 'details', 'Fileoptions data loaded.' );
$fileoptions = &$fileoptions_obj->options;

 *
 */

class pb_backupbuddy_fileoptions {
    
    public 
$options ''// Current options.
    
private $_options_hash ''// Hold hash of options so we don't perform file operations needlessly if things have not changed.
    
private $_file// Filename options are stored in.
    
private $_is_ok 'UNKNOWN'// true on error; error message otherwise
    
private $_read_only false;
    private 
$_loaded false// Has file been succesfully loaded yet?
    
    /* __construct()
     *
     * Reads and creates file lock. If file does not exist, creates it. Places options into this class's $options.
     *
     * @param    string        $file            Full filename to save fileoptions into.
     * @param    bool        $read_only        true read only mode; false writable.
     * @param    bool        $ignore_lock    When true ignore file locking. default: false
     * @param    bool        $create_file    Create file if it does not yet exist and mark is_ok value to true.
     * @return    null
     *
     */
    
function __construct$file$read_only false$ignore_lock false$create_file false ) {
        
$this->_file $file;
        
$this->_read_only $read_only;
        
        
// If read-only then ignore locks is forced.
        
if ( $read_only === true ) {
            
$ignore_lock true;
        }
        
        if ( ! 
file_existsdirname$file ) ) ) { // Directory exist?
            
pb_backupbuddy::anti_directory_browsingdirname$file ), $die_on_fail false$deny_all true );
        }
        
        
/*
        if ( ! file_exists( $file ) ) { // File exist?
            //$this->save();
        }
        */
        
        
$this->load$ignore_lock$create_file );
        
    } 
// End __construct().
    
    
    
    /* __destruct()
     *
     * Saves options on destruction.
     *
     * @return null
     *
     */
    
function __destruct() {
        
        
// IMPORTANT: We can NOT rely on any outside classes from here on out such as the framework status method. Pass true to unlock to insure it does not perform any status logging.
        
$this->unlock$destructorCalled true );
        
    } 
// End __destruct().
    
    
    
    /* is_ok()
     *
     * Determine whether options was loaded correctly and is ok.
     *
     * @return true\string        True on valid, else returns error message string.
     *
     */
    
public function is_ok() {
        
        return 
$this->_is_ok;
        
    } 
// End is_ok().
    
    
    
    /* load()
     *
     * Load options from file. Use is_ok() to verify integrity. If is_ok() !== true, returns error message.
     *
     * @param    bool        $ignore_lock    Whether or not to ignore the file being locked.
     * @param    bool        $create_file    Create file if it does not yet exist and mark is_ok value to true.
     * @param    int            $retryCount        If ERROR_EMPTY_FILE_NON_CREATE_MODE error then we will retry a couple of times after a slight pause in case there was a race condition while another process was updating the file.
     * @return    bool        true on load success, else false.
     *
     */
    
public function load$ignore_lock false$create_file false$retryCount ) {
        
        
// Handle locked file.
        
if ( ( false === $ignore_lock ) && ( true === $this->is_locked() ) ) {
            
pb_backupbuddy::status'warning''Warning #54555. Unable to read fileoptions file `' $this->_file '` as it is currently locked. Lock file ID: ' $this->_last_seen_lock_id '.' );
            
$this->_is_ok 'ERROR_LOCKED';
            return 
false;
        }
        
        
// Get options and decode into usable format.
        
if ( file_exists$this->_file ) ) {
            
$options = @file_get_contents$this->_file );
        } else {
            if ( 
true !== $create_file ) {
                
pb_backupbuddy::status'warning''Fileoptions file `' $this->_file '` not found and NOT in create mode. Verify file exists & check permissions.' );
                
$this->_is_ok 'ERROR_FILE_MISSING_NON_CREATE_MODE';
            }
            
$options '';
        }
        
        if ( 
false === $options ) {
            
pb_backupbuddy::status'error''Unable to read fileoptions file `' $this->_file '`. Verify permissions on this directory.' );
            
$this->_is_ok 'ERROR_READ';
            return 
false;
        }
        if ( 
false === ( $options base64_decode$options ) ) ) {
            
pb_backupbuddy::status'error''Unable to base64 decode data from fileoptions file `' $this->_file '`.' );
            
$this->_is_ok 'ERROR_BASE64_DECODE';
            return 
false;
        }
        if ( 
false === ( $options maybe_unserialize$options ) ) ) {
            
pb_backupbuddy::status'error''Unable to unserialize data from fileoptions file `' $this->_file '`.' );
            
$this->_is_ok 'ERROR_UNSERIALIZE';
            return 
false;
        }
        
        if ( 
false === $this->_read_only ) { // Only lock when not in read-only mode.
            
if ( false === $this->_lock() ) { // If lock fails (possibly due to existing lock file) then fail load.
                
$this->_is_ok 'ERROR_UNABLE_TO_LOCK';
                return 
false;
            }
        }
        
        if ( 
true === $create_file ) {
            
$this->_is_ok true;
        } elseif ( 
'' != $options ) {
            
$this->_is_ok true;
        } else {
            
$this->_is_ok 'ERROR_EMPTY_FILE_NON_CREATE_MODE';
            if ( 
$retryCount ) { // Give it one more chance by sleeping then loading once more. Return whatever result that gives.
                
$this->unlock();
                
$retryCount++;
                
pb_backupbuddy::status'details''Fileoptions file was EMPTY. Unlocking & sleeping momentarily and then trying again. Attempt #1' $retryCount );
                
sleep);
                return 
$this->load$ignore_lock$create_file$retryCount );
            }
        }
        
$this->options $options;
        
$this->_loaded true;
        
$this->_options_hash md5serialize$options ) );
        
        return 
true;
    } 
// End load();
    
    
    
    /* save()
     *
     * Save the options into file now without removing lock.
     *
     * @param        bool    $remove_lock    When true the lock will be removed as well. default: false
     * @return        bool                    true on save success, else false.
     *
     */
    
public function save$remove_lock false ) {
        
        if ( 
'' == $this->_file ) { // No file set yet. Just return.
            
return true;
        }
        
        if ( 
true === $this->_read_only ) {
            
pb_backupbuddy::status'error''Attempted to write to fileoptions while in readonly mode; denied.' );
            return 
false;
        }
        
        if ( 
false === $this->_loaded ) { // Skip saving if we have not successfully loaded yet to prevent overwriting data.
            
return false;
        }
        
        
        
/*
        if ( true === $this->is_locked() ) {
            error_log( 'saveislocked' );
            pb_backupbuddy::status( 'details', 'Unable to write to fileoptions as file is currently locked: `' . $this->_file . '`.' );
            return false;
        }
        */
        
        
$serialized serialize$this->options );
        
$options_hash md5$serialized );
        
        if ( 
$options_hash == $this->_options_hash ) { // Only update if options has changed so if equal then no change so return.
            
if ( true === $remove_lock ) {
                
$this->unlock();
            }
            return 
true;
        }
        
        
$options base64_encode$serialized );
        
        if ( 
false === ( $bytesWritten file_put_contents$this->_file$options ) ) ) { // unable to write.
            
pb_backupbuddy::status'error''Unable to write fileoptions file `' $this->_file '`. Verify permissions.' );
            if ( 
true === $remove_lock ) {
                
$this->unlock();
            }
            return 
false;
        } else { 
// wrote to file.
            
pb_backupbuddy::status'details''Fileoptions saved. ' $bytesWritten ' bytes written.' );
            
$this->_options_hash $options_hash;
            if ( 
true === $remove_lock ) {
                
$this->unlock();
            }
            return 
true;
        }
        
    } 
// End save().
    
    
    
    /* _lock()
     *
     * Lock file.
     *
     * @return        bool    true on lock success, else false.
     *
     */
    
private function _lock() {
        
        
$lockFile $this->_file '.lock';
        
        if ( 
true === $this->_read_only ) {
            
pb_backupbuddy::status'error''Attempted to lock fileoptions while in readonly mode; denied.' );
            return 
false;
        }
        
        
$handle = @fopen$lockFile'x' );
        if ( 
false === $handle ) { // Failed creating file.
            
if ( file_exists$lockFile ) ) {
                
$this->_last_seen_lock_id = @file_get_contents$lockFile );
                
pb_backupbuddy::status'error''Unable to create fileoptions lock file as it already exists: `' $lockFile '`. Lock file ID: ' $this->_last_seen_lock_id '.' );
            } else {
                
pb_backupbuddy::status'error''Unable to create fileoptions lock file `' $lockFile '`. Verify permissions on this directory.' );
            }
            return 
false;
        } else { 
// Created file.
            
$lockID uniqid''true );
            if ( 
false === @fwrite$handle$lockID ) ) {
                
pb_backupbuddy::status'warning''Unable to write unique lock ID `' $lockID '` to lock file `' $lockFile '`.' );
            } else {
                
pb_backupbuddy::status'details''Created fileoptions lock file `' $lockFile '` with ID: ' $lockID '.' );
            }
            @
fclose$handle );
        }
        
    }
    
    
    
    
/* unlock()
     *
     * Unlock file.
     * WARNING!!! IMPORTANT!!! We cannot reliably call pb_backupbuddy::status() here _IF_ calling via destructor, $destructorCalled = true.
     *
     * @param        bool    $destructorCalled        Whether or not this function was called via the destructor. See warning in comments above.
     * @return        bool    true on unlock success, else false.
     *
     */
    
public function unlock$destructorCalled false ) {
        
        
$lockFile $this->_file '.lock';
        
        if ( 
file_exists$lockFile ) ) { // Locked; continue to unlock;
            
$this->_last_seen_lock_id = @file_get_contents$lockFile );
            
$result unlink(  $lockFile );
            if ( 
true === $result ) {
                if ( 
false === $destructorCalled ) {
                    
pb_backupbuddy::status'details''Unlocked fileoptions lock file `' $lockFile '` with lock ID `' $this->_last_seen_lock_id '`.' );
                }
                return 
true;
            } else {
                if ( 
class_exists'pb_backupbuddy' ) ) {
                    if ( 
false === $destructorCalled ) {
                        
pb_backupbuddy::status'error''Unable to delete fileoptions lock file `' $lockFile '` with lock ID `' $this->_last_seen_lock_id '`. Verify permissions on this file / directory.' );
                    }
                }
                return 
false;
            }
        } else { 
// File already unlocked.
            
return true;
        }
        
    } 
// End unlock().
    
    
    
    /* is_locked()
     *
     * Is this file locked / in use?
     *
     * @return        bool        Whether or not file is currenty locked.
     *
     */
    
public function is_locked() {
        
        
$lockFile $this->_file '.lock';
        
        if ( 
file_exists$lockFile ) ) {
            
$this->_last_seen_lock_id = @file_get_contents$lockFile );
            return 
true;
        } else {
            return 
false;
        }
        
    } 
// End is_locked().
    
    
    
// End class pb_backupbuddy_fileoptions.