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.
8 function sortFilesListByName($a, $b) {
9 $firstLeft = substr($a['name'], 0, 1);
10 $firstRight = substr($b['name'], 0, 1);
11 if ($firstLeft == '.' && $firstRight != '.') {
13 } else if ($firstRight == '.' && $firstLeft != '.') {
16 return strcasecmp($a['name'], $b['name']);
19 class Miscellaneous extends TModule {
21 const LICENCE_FILE = 'LICENSE';
23 const RPATH_PATTERN = '/^b2\d+$/';
25 public $job_types = array(
38 private $jobLevels = array(
41 'D' => 'Differential',
46 'O' => 'VolumeToCatalog',
47 'd' => 'DiskToCatalog'
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)')
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');
84 private $runningJobStates = array('C', 'R');
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')
93 private $replace_opts = array(
102 * Getting the licence from file.
105 * @return string licence text
107 public function getLicence() {
108 return nl2br(htmlspecialchars(file_get_contents(self::LICENCE_FILE)));
111 public function getJobLevels() {
112 return $this->jobLevels;
115 public function getJobState($jobStateLetter = null) {
117 if(is_null($jobStateLetter)) {
118 $state = $this->jobStates;
120 $state = array_key_exists($jobStateLetter, $this->jobStates) ? $this->jobStates[$jobStateLetter] : null;
125 public function getRunningJobStates() {
126 return $this->runningJobStates;
129 public function getComponents() {
130 $components = array_keys($this->components);
133 public function getMainComponentResource($type) {
135 if (array_key_exists($type, $this->components)) {
136 $resource = $this->components[$type]['main_resource'];
141 public function getComponentFullName($type) {
143 if (array_key_exists($type, $this->components)) {
144 $name = $this->components[$type]['full_name'];
149 public function getJobStatesByType($type) {
150 $statesByType = array();
154 $states = $this->jobStatesOK;
157 $states = $this->jobStatesWarning;
160 $states = $this->jobStatesError;
163 $states = $this->jobStatesCancel;
166 $states = $this->jobStatesRunning;
170 for ($i = 0; $i < count($states); $i++) {
171 $statesByType[$states[$i]] = $this->getJobState($states[$i]);
174 return $statesByType;
178 * @TODO: Move it to separate validation module.
180 public function isValidJobLevel($jobLevel) {
181 return array_key_exists($jobLevel, $this->getJobLevels());
184 public function isValidName($name) {
185 return (preg_match('/^[\w:\.\-\s]{1,127}$/', $name) === 1);
188 public function isValidState($state) {
189 return (preg_match('/^\w+$/', $state) === 1);
192 public function isValidNumber($num) {
193 return (preg_match('/^\d+$/', $num) === 1);
196 public function isValidBoolean($val) {
197 return (preg_match('/^(yes|no|0|1|true|false)$/', $val) === 1);
200 public function isValidId($id) {
201 return (preg_match('/^\d+$/', $id) === 1);
204 public function isValidPath($path) {
205 return (preg_match('/^[\p{L}\p{N}\p{Z}\[\]\(\)\-\+\/\\\:\.#~_,{}!]{0,10000}$/u', $path) === 1);
208 public function isValidReplace($replace) {
209 return in_array($replace, $this->replace_opts);
212 public function isValidIdsList($list) {
213 return (preg_match('/^[\d,]+$/', $list) === 1);
216 public function isValidBvfsPath($path) {
217 return (preg_match('/^b2\d+$/', $path) === 1);
221 * Writing INI-style configuration file.
223 * Functions has been got from StackOverflow.com service (http://stackoverflow.com/questions/4082626/save-ini-file-with-comments).
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
230 public function writeINIFile($file, array $options){
232 foreach($options as $section => $values){
233 $tmp .= "[$section]\n";
234 foreach($values as $key => $val){
236 foreach($val as $k => $v) {
237 $v = $this->escapeINIVal($v);
238 $tmp .= "{$key}[$k] = \"$v\"\n";
241 $val = $this->escapeINIVal($val);
242 $tmp .= "$key = \"$val\"\n";
247 $old_umask = umask(0);
249 $result = file_put_contents($file, $tmp);
255 * Escape text written to INI-style file.
258 * @param string $value text to escape
259 * @return string escaped text
261 private function escapeINIVal($value) {
262 $esc_value = str_replace('"', '\"', $value);
267 * Parse INI-style configuration file.
270 * @param string $file file localization
271 * @return array data of configuration file
273 public static function parseINIFile($file) {
275 if (file_exists($file)) {
276 $content = parse_ini_file($file, true);
277 if (!is_array($content)) {
286 * This method is copied from http://stackoverflow.com/questions/4345554/convert-php-object-to-associative-array
288 public function objectToArray($data) {
289 if (is_array($data) || is_object($data)) {
291 foreach ($data as $key => $value) {
292 $result[$key] = $this->objectToArray($value);
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
307 } elseif ($lstat_len > 16) {
308 // cut off unknown fields
309 array_splice($lstat_fields, 16);
330 $encoded_values = array(
339 'blocksize' => $blocksize,
350 foreach($encoded_values as $key => $val) {
355 if(substr($val, 0, 1) === '-') {
360 for($i = $start; $i < strlen($val); $i++) {
361 $result = bcmul($result, bcpow(2,6));
362 $result += strpos($base64, substr($val, $i , 1));
364 $ret[$key] = ($is_minus === true) ? -$result : $result;
369 public function parseBvfsList($list) {
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'] == '.') {
375 } elseif($match['name'] != '..') {
376 $match['name'] .= '/';
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'],
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'] == '.') {
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'],
402 usort($elements, 'sortFilesListByName');
406 public function parseFileVersions($filename, $list) {
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(
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'],
427 public function findJobIdStartedJob($output) {
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'];
440 * Get (pseudo)random string.
442 * Useful for log out user from HTTP Basic auth by providing random password.
445 * @return string random 62 characters string from range [a-zA-Z0-9]
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);
457 * Get encrypted password to use in HTTP Basic auth.
460 * @param string $password plain text password
461 * @return string encrypted password
463 public function getCryptedPassword($password) {
464 $enc_pwd = crypt($password, base64_encode($password));