]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/protected/Common/Class/Miscellaneous.php
baculum: Add state, number, boolean and id validators
[bacula/bacula] / gui / baculum / protected / Common / Class / Miscellaneous.php
1 <?php
2
3 /*
4  * Small sorting callback function to sort files and directories by name.
5  * Function keeps '.' and '..' names always in the beginning of array.
6  * Used to sort files and directories from Bvfs.
7  */
8 function sortFilesListByName($a, $b) {
9         $firstLeft = substr($a['name'], 0, 1);
10         $firstRight = substr($b['name'], 0, 1);
11         if ($firstLeft == '.' && $firstRight != '.') {
12                 return -1;
13         } else if ($firstRight == '.' && $firstLeft != '.') {
14                 return 1;
15         }
16         return strcasecmp($a['name'], $b['name']);
17 }
18
19 class Miscellaneous extends TModule {
20
21         const LICENCE_FILE = 'LICENSE';
22
23         const RPATH_PATTERN = '/^b2\d+$/';
24
25         public $job_types = array(
26                 'B' => 'Backup',
27                 'M' => 'Migrated',
28                 'V' => 'Verify',
29                 'R' => 'Restore',
30                 'I' => 'Internal',
31                 'D' => 'Admin',
32                 'A' => 'Archive',
33                 'C' => 'Copy',
34                 'c' => 'Copy Job',
35                 'g' => 'Migration'
36         );
37
38         private $jobLevels = array(
39                 'F' => 'Full',
40                 'I' => 'Incremental',
41                 'D' => 'Differential',
42                 'B' => 'Base',
43                 'f' => 'VirtualFull',
44                 'V' => 'InitCatalog',
45                 'C' => 'Catalog',
46                 'O' => 'VolumeToCatalog',
47                 'd' => 'DiskToCatalog'
48         );
49
50         public $jobStates =  array(
51                 'C' => array('value' => 'Created', 'description' =>'Created but not yet running'),
52                 'R' => array('value' => 'Running', 'description' => 'Running'),
53                 'B' => array('value' => 'Blocked', 'description' => 'Blocked'),
54                 'T' => array('value' => 'Terminated', 'description' =>'Terminated normally'),
55                 'W' => array('value' => 'Terminated', 'description' =>'Terminated normally with warnings'),
56                 'E' => array('value' => 'Error', 'description' =>'Terminated in Error'),
57                 'e' => array('value' => 'Non-fatal error', 'description' =>'Non-fatal error'),
58                 'f' => array('value' => 'Fatal error', 'description' =>'Fatal error'),
59                 'D' => array('value' => 'Verify Diff.', 'description' =>'Verify Differences'),
60                 'A' => array('value' => 'Canceled', 'description' =>'Canceled by the user'),
61                 'I' => array('value' => 'Incomplete', 'description' =>'Incomplete Job'),
62                 'F' => array('value' => 'Waiting on FD', 'description' =>'Waiting on the File daemon'),
63                 'S' => array('value' => 'Waiting on SD', 'description' =>'Waiting on the Storage daemon'),
64                 'm' => array('value' => 'Waiting for new vol.', 'description' =>'Waiting for a new Volume to be mounted'),
65                 'M' => array('value' => 'Waiting for mount', 'description' =>'Waiting for a Mount'),
66                 's' => array('value' => 'Waiting for storage', 'description' =>'Waiting for Storage resource'),
67                 'j' => array('value' => 'Waiting for job', 'description' =>'Waiting for Job resource'),
68                 'c' => array('value' => 'Waiting for client', 'description' =>'Waiting for Client resource'),
69                 'd' => array('value' => 'Waiting for Max. jobs', 'description' =>'Wating for Maximum jobs'),
70                 't' => array('value' => 'Waiting for start', 'description' =>'Waiting for Start Time'),
71                 'p' => array('value' => 'Waiting for higher priority', 'description' =>'Waiting for higher priority job to finish'),
72                 'i' => array('value' => 'Batch insert', 'description' =>'Doing batch insert file records'),
73                 'a' => array('value' => 'Despooling attributes', 'description' =>'SD despooling attributes'),
74                 'l' => array('value' => 'Data despooling', 'description' =>'Doing data despooling'),
75                 'L' => array('value' => 'Commiting data', 'description' =>'Committing data (last despool)')
76         );
77
78         private $jobStatesOK = array('T', 'D');
79         private $jobStatesWarning = array('W');
80         private $jobStatesError = array('E', 'e', 'f', 'I');
81         private $jobStatesCancel = array('A');
82         private $jobStatesRunning = array('C', 'R', 'B', 'F', 'S', 'm', 'M', 's', 'j', 'c', 'd','t', 'p', 'i', 'a', 'l', 'L');
83
84         private $runningJobStates = array('C', 'R');
85
86         private $components = array(
87                 'dir' => array('full_name' => 'Director', 'main_resource' => 'Director'),
88                 'sd' => array('full_name' => 'Storage Daemon', 'main_resource' => 'Storage'),
89                 'fd' => array('full_name' => 'File Daemon', 'main_resource' => 'FileDaemon'),
90                 'bcons' => array('full_name' => 'Console', 'main_resource' => 'Director')
91         );
92
93         private $replace_opts = array(
94                 'always',
95                 'ifnewer',
96                 'ifolder',
97                 'never'
98         );
99
100
101         /**
102          * Getting the licence from file.
103          * 
104          * @access public
105          * @return string licence text
106          */
107         public function getLicence() {
108                 return nl2br(htmlspecialchars(file_get_contents(self::LICENCE_FILE)));
109         }
110
111         public function getJobLevels() {
112                 return $this->jobLevels;
113         }
114
115         public function getJobState($jobStateLetter = null) {
116                 $state;
117                 if(is_null($jobStateLetter)) {
118                         $state = $this->jobStates;
119                 } else {
120                         $state = array_key_exists($jobStateLetter, $this->jobStates) ? $this->jobStates[$jobStateLetter] : null;
121                 }
122                 return $state;
123         }
124
125         public function getRunningJobStates() {
126                 return $this->runningJobStates;
127         }
128
129         public function getComponents() {
130                 $components = array_keys($this->components);
131         }
132
133         public function getMainComponentResource($type) {
134                 $resource = null;
135                 if (array_key_exists($type, $this->components)) {
136                         $resource = $this->components[$type]['main_resource'];
137                 }
138                 return $resource;
139         }
140
141         public function getComponentFullName($type) {
142                 $name = '';
143                 if (array_key_exists($type, $this->components)) {
144                         $name = $this->components[$type]['full_name'];
145                 }
146                 return $name;
147         }
148
149         public function getJobStatesByType($type) {
150                 $statesByType = array();
151                 $states = array();
152                 switch($type) {
153                         case 'ok':
154                                 $states = $this->jobStatesOK;
155                                 break;
156                         case 'warning':
157                                 $states = $this->jobStatesWarning;
158                                 break;
159                         case 'error':
160                                 $states = $this->jobStatesError;
161                                 break;
162                         case 'cancel':
163                                 $states = $this->jobStatesCancel;
164                                 break;
165                         case 'running':
166                                 $states = $this->jobStatesRunning;
167                                 break;
168                 }
169
170                 for ($i = 0; $i < count($states); $i++) {
171                         $statesByType[$states[$i]] = $this->getJobState($states[$i]);
172                 }
173
174                 return $statesByType;
175         }
176
177         /*
178          * @TODO: Move it to separate validation module.
179          */
180         public function isValidJobLevel($jobLevel) {
181                 return array_key_exists($jobLevel, $this->getJobLevels());
182         }
183
184         public function isValidName($name) {
185                 return (preg_match('/^[\w:\.\-\s]{1,127}$/', $name) === 1);
186         }
187
188         public function isValidState($state) {
189                 return (preg_match('/^\w+$/', $state) === 1);
190         }
191
192         public function isValidNumber($num) {
193                 return (preg_match('/^\d+$/', $num) === 1);
194         }
195
196         public function isValidBoolean($val) {
197                 return (preg_match('/^(yes|no|0|1|true|false)$/', $val) === 1);
198         }
199
200         public function isValidId($id) {
201                 return (preg_match('/^\d+$/', $id) === 1);
202         }
203
204         public function isValidPath($path) {
205                 return (preg_match('/^[\p{L}\p{N}\p{Z}\[\]\(\)\-\+\/\\\:\.#~_,{}!]{0,1000}$/', $path) === 1);
206         }
207
208         public function isValidReplace($replace) {
209                 return in_array($replace, $this->replace_opts);
210         }
211
212         public function isValidIdsList($list) {
213                 return (preg_match('/^[\d,]+$/', $list) === 1);
214         }
215
216         public function isValidBvfsPath($path) {
217                 return (preg_match('/^b2\d+$/', $path) === 1);
218         }
219
220         /**
221          * Writing INI-style configuration file.
222          * 
223          * Functions has been got from StackOverflow.com service (http://stackoverflow.com/questions/4082626/save-ini-file-with-comments).
224          * 
225          * @access public
226          * @param string $file file localization
227          * @param array $options structure of config file params
228          * @return mixed if success then returns the number of bytes that were written to the file as the integer type, if failure then returns false
229          */
230         public function writeINIFile($file, array $options){
231                 $tmp = '';
232                 foreach($options as $section => $values){
233                         $tmp .= "[$section]\n";
234                                 foreach($values as $key => $val){
235                                         if(is_array($val)){
236                                                 foreach($val as $k => $v) {
237                                                         $v = $this->escapeINIVal($v);
238                                                         $tmp .= "{$key}[$k] = \"$v\"\n";
239                                                 }
240                                         } else {
241                                                 $val = $this->escapeINIVal($val);
242                                                 $tmp .= "$key = \"$val\"\n";
243                                         }
244                                 }
245                         $tmp .= "\n";
246                 }
247                 $old_umask = umask(0);
248                 umask(0077);
249                 $result = file_put_contents($file, $tmp);
250                 umask($old_umask);
251                 return $result;
252         }
253
254         /**
255          * Escape text written to INI-style file.
256          *
257          * @access private
258          * @param string $value text to escape
259          * @return string escaped text
260          */
261         private function escapeINIVal($value) {
262                 $esc_value = str_replace('"', '\"', $value);
263                 return $esc_value;
264         }
265
266         /**
267          * Parse INI-style configuration file.
268          * 
269          * @access public
270          * @param string $file file localization
271          * @return array data of configuration file
272          */
273         public static function parseINIFile($file) {
274                 $content = array();
275                 if (file_exists($file)) {
276                         $content = parse_ini_file($file, true);
277                         if (!is_array($content)) {
278                                 $content = array();
279                         }
280                 }
281                 return $content;
282         }
283
284
285         /**
286          * This method is copied from http://stackoverflow.com/questions/4345554/convert-php-object-to-associative-array
287          */
288         public function objectToArray($data) {
289                 if (is_array($data) || is_object($data)) {
290                         $result = array();
291                         foreach ($data as $key => $value) {
292                                 $result[$key] = $this->objectToArray($value);
293                         }
294                         return $result;
295                 }
296                 return $data;
297         }
298
299         public function decode_bacula_lstat($lstat) {
300                 $base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
301                 $lstat = trim($lstat);
302                 $lstat_fields = explode(' ', $lstat);
303                 $lstat_len = count($lstat_fields);
304                 if ($lstat_len < 16) {
305                         // not known or empty lstat value
306                         return;
307                 } elseif ($lstat_len > 16) {
308                         // cut off unknown fields
309                         array_splice($lstat_fields, 16);
310                 }
311
312                 list(
313                         $dev,
314                         $inode,
315                         $mode,
316                         $nlink,
317                         $uid,
318                         $gid,
319                         $rdev,
320                         $size,
321                         $blocksize,
322                         $blocks,
323                         $atime,
324                         $mtime,
325                         $ctime,
326                         $linkfi,
327                         $flags,
328                         $data
329                 ) = $lstat_fields;
330                 $encoded_values = array(
331                         'dev' => $dev,
332                         'inode' => $inode,
333                         'mode' => $mode,
334                         'nlink' => $nlink,
335                         'uid' => $uid,
336                         'gid' => $gid,
337                         'rdev' => $rdev,
338                         'size' => $size,
339                         'blocksize' => $blocksize,
340                         'blocks' => $blocks,
341                         'atime' => $atime,
342                         'mtime' => $mtime,
343                         'ctime' => $ctime,
344                         'linkfi' => $linkfi,
345                         'flags' => $flags,
346                         'data' => $data
347                 );
348
349                 $ret = array();
350                 foreach($encoded_values as $key => $val) {
351                         $result = 0;
352                         $is_minus = false;
353                         $start = 0;
354
355                         if(substr($val, 0, 1) === '-') {
356                                 $is_minus = true;
357                                 $start++;
358                         }
359
360                         for($i = $start; $i < strlen($val); $i++) {
361                                 $result = bcmul($result, bcpow(2,6));
362                                 $result +=  strpos($base64, substr($val, $i , 1));
363                         }
364                         $ret[$key] = ($is_minus === true) ? -$result : $result;
365                 }
366                 return $ret;
367         }
368
369         public function parseBvfsList($list) {
370                 $elements = array();
371                 for($i = 0; $i < count($list); $i++) {
372                         if(preg_match('/^(?P<pathid>\d+)\t(?P<filenameid>\d+)\t(?P<fileid>\d+)\t(?P<jobid>\d+)\t(?P<lstat>[a-zA-z0-9\+\/\ ]+)\t(?P<name>.*)\/$/', $list[$i], $match) == 1 || preg_match('/^(?P<pathid>\d+)\t(?P<filenameid>\d+)\t(?P<fileid>\d+)\t(?P<jobid>\d+)\t(?P<lstat>[a-zA-z0-9\+\/\ ]+)\t(?P<name>\.{2})$/', $list[$i], $match) == 1) {
373                                 if($match['name'] == '.') {
374                                         continue;
375                                 } elseif($match['name'] != '..') {
376                                         $match['name'] .= '/';
377                                 }
378                                 $elements[] = array(
379                                         'pathid' => $match['pathid'],
380                                         'filenameid' => $match['filenameid'],
381                                         'fileid' => $match['fileid'],
382                                         'jobid' => $match['jobid'],
383                                         'lstat' => $this->decode_bacula_lstat($match['lstat']),
384                                         'name' => $match['name'],
385                                         'type' => 'dir'
386                                 );
387                         } elseif(preg_match('/^(?P<pathid>\d+)\t(?P<filenameid>\d+)\t(?P<fileid>\d+)\t(?P<jobid>\d+)\t(?P<lstat>[a-zA-z0-9\+\/\ ]+)\t(?P<name>[^\/]+)$/', $list[$i], $match) == 1) {
388                                 if($match['name'] == '.') {
389                                         continue;
390                                 }
391                                 $elements[] = array(
392                                         'pathid' => $match['pathid'],
393                                         'filenameid' => $match['filenameid'],
394                                         'fileid' => $match['fileid'],
395                                         'jobid' => $match['jobid'],
396                                         'lstat' => $this->decode_bacula_lstat($match['lstat']),
397                                         'name' => $match['name'],
398                                         'type' => 'file'
399                                 );
400                         }
401                 }
402                 usort($elements, 'sortFilesListByName');
403                 return $elements;
404         }
405
406         public function parseFileVersions($filename, $list) {
407                 $elements = array();
408                 for($i = 0; $i < count($list); $i++) {
409                         if(preg_match('/^(?P<pathid>\d+)\t(?P<filenameid>\d+)\t(?P<fileid>\d+)\t(?P<jobid>\d+)\t(?P<lstat>[a-zA-Z0-9\+\/\ ]+)\t(?P<md5>.+)\t(?P<volname>.+)\t(?P<inchanger>\d+)$/', $list[$i], $match) == 1) {
410                                 $elements[$match['fileid']] = array(
411                                         'name' => $filename,
412                                         'pathid' => $match['pathid'],
413                                         'filenameid' => $match['filenameid'],
414                                         'fileid' => $match['fileid'],
415                                         'jobid' => $match['jobid'],
416                                         'lstat' => $this->decode_bacula_lstat($match['lstat']),
417                                         'md5' => $match['md5'],
418                                         'volname' => $match['volname'],
419                                         'inchanger' => $match['inchanger'],
420                                         'type' => 'file'
421                                 );
422                         }
423                 }
424                 return $elements;
425         }
426
427         public function findJobIdStartedJob($output) {
428                 $jobid = null;
429                 $output = array_reverse($output); // jobid is ussually at the end of output
430                 for ($i = 0; $i < count($output); $i++) {
431                         if (preg_match('/^Job queued\.\sJobId=(?P<jobid>\d+)$/', $output[$i], $match) === 1) {
432                                 $jobid = $match['jobid'];
433                                 break;
434                         }
435                 }
436                 return $jobid;
437         }
438
439         /**
440          * Get (pseudo)random string.
441          *
442          * Useful for log out user from HTTP Basic auth by providing random password.
443          *
444          * @access public
445          * @return string random 62 characters string from range [a-zA-Z0-9]
446          */
447         public function getRandomString($length = null) {
448                 $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
449                 $rand_string = str_shuffle($characters);
450                 if (is_int($length) && $length <= 62) {
451                         $rand_string = substr($rand_string, 0, $length);
452                 }
453                 return $rand_string;
454         }
455
456         /**
457          * Get encrypted password to use in HTTP Basic auth.
458          *
459          * @access public
460          * @param string $password plain text password
461          * @return string encrypted password
462          */
463         public function getCryptedPassword($password) {
464                 $enc_pwd = crypt($password, base64_encode($password));
465                 return $enc_pwd;
466         }
467 }
468 ?>