<?php
// $Header: /cvsroot/phptsmadmin/phpTSMadmin/lib/ds_tsm.php,v 1.8 2009/04/19 04:03:05 wurley Exp $

/**
 * @package phpTSMadmin
 * @author Deon George (c) 2006
 */

class tsm extends DS {
	public function __construct($index) {
		$this->index = $index;
		$this->type = 'tsm';

		# Additional values that can go in our config.php
		$this->custom = new StdClass;
		$this->default = new StdClass;

		# TSM Server Stanza Name
		$this->default->server['stanza'] = array(
			'desc'=>'TSM Server Stanza Name',
			'default'=>null);

		$this->default->system['dsmadmc'] = array(
			'desc'=>'Path to dsmadmc command.',
			'default'=>'/opt/tivoli/tsm/client/ba/bin/dsmadmc');
		$this->default->system['errorlog'] = array(
			'desc'=>'Set errorlog filename',
			'default'=>'/tmp/pta-tsm-errorlog.log');
		$this->default->system['tapeage'] = array(
			'desc'=>'Age of tapes to alert for rotation',
			'default'=>180);

		# TSM Parameters
		$this->default->tsmparm['msgFormat'] = array(
			'desc'=>'Format of TSM message codes.',
			'default'=>'[A-Z]{3}[0-9]{4}[I|E|W|S]');

		$this->default->tsmparm['queryStartMsg'] = array(
			'desc'=>'TSM code depicting START of queries results.',
			'default'=>'ANS8000I');

		$this->default->tsmparm['queryEndMsg'] = array(
			'desc'=>'TSM code depicting END of queries results.',
			'default'=>'ANS8002I');

		$this->default->tsmparm['loginFail'] = array(
			'desc'=>'Known TSM "failed login" message codes.',
			'default'=>array(
				'ANS8023E',	//
				'ANS1017E',	// TCP/IP connection rejected.
				'ANS1217E',	// Server name not found in System Options File
				'ANS1355E',	// Sessions disabled.
				'ANS8034E'	// Your administrator ID is not recognized by this server.
			));
		$this->default->tsmparm['loginFailIgnore'] = array(
			'desc'=>'Known TSM "ignore" message codes during login.',
			'default'=>array(
				'ANS0110E',	// LogMsg: Unable to open error log file '%s' for output.
				'ANS8002I'	// Highest return code was %s.
			));
		$this->default->tsmparm['ignoremsg'] = array(
			'desc'=>'Known TSM "ignore" message codes.',
			'default'=>array(
				'ANS8001I',	// Return code %s.
				'ANS0102W'	// Unable to open the message repository %s. The American English repository will be used instead.
			));
		$this->default->tsmparm['nodatamsg'] = array(
			'desc'=>'Known TSM "select returned no data" message codes.',
			'default'=>array(
				'ANR2034E'	// SELECT: No match found using this criteria.
			));
	}

	/**
	 * Required ABSTRACT functions
	 */
	/**
	 * Connect and Bind to the Database
	 *
	 * @param method This enables to have multiple different types of connects, ie: read only, readwrite, etc
	 * @return resource|null Connection resource if successful, null if not.
	 */
	protected function connect($method,$debug=false) {
		$method = $this->getMethod($method);

		if (! $this->isLoggedIn($method))
			system_message(array('title'=>'Not Logged In',
				'body'=>sprintf('You are not logged into server %s',$this->getValue('server','name')),
				'type'=>'warn'),'index.php');

		if (! file_exists($this->getValue('system','dsmadmc'))) {
			system_message(array('title'=>'Cant find DSMADMC',
				'body'=>sprintf('Unable to find the dsmadmc at <b>%s</b>',$this->getValue('system','dsmadmc')),
				'type'=>'error'));
			return false;
		}

		if ($this->getValue('system','errorlog'))
			$errorlog = sprintf('-errorlogname=%s',$this->getValue('system','errorlog'));

		if (is_null($this->getValue('server','stanza')) || ! trim($this->getValue('server','stanza')))
			$TSMcmd = sprintf('%s -id=%s -password=%s -displ=list %s',
				$this->getValue('system','dsmadmc'),$this->getLogin($method),$this->getPassword($method),
				isset($errorlog) ? $errorlog : '');
		else
			$TSMcmd = sprintf('%s -se=%s -id=%s -password=%s -displ=list %s',
				$this->getValue('system','dsmadmc'),
				$this->getValue('server','stanza'),$this->getLogin($method),$this->getPassword($method),
				isset($errorlog) ? $errorlog : '');

		return $TSMcmd;
	}

	/**
	 * Login to the database with the application user/password
	 *
	 * @param method This enables to have multiple different types of connects, ie: read only, readwrite, etc
	 * @return boolean true|false for successful login.
	 */
	public function login($user=null,$pass=null,$method=null) {
		# Set our user and password.
		$this->setLogin($user,$pass,$method);

		# Test that it works.
		$result = $this->query('SELECT platform,version,release,level,sublevel FROM status',$method);

		if (! $result)
			$this->logout($method);
		else
			return true;

		return false;
	}

	/**
	 * Perform a query to the Database
	 *
	 * @param string SQL query to perform
	 * @param string Index items according to this key
	 * @return array|null Results of query.
	 */
	public function query($query,$method,$index=null,$debug=false) {
		$TSMcmd = sprintf('%s "%s"',$this->connect($method,$debug),$query);
		$TSMcmdOutput = exec($TSMcmd,$OutputLines,$return);

		if ($debug)
			debug_dump(array('cmd'=>$TSMcmd,'TSMcmdOutput'=>$TSMcmdOutput,'return'=>$return));

		# Parse the ouput for key:value pairs.
		$outindex = 0;
		$isQueryResult = 0;
		$isKeyValue = 0;
		$haveHelp = false;
		$QueryResult = false;
		$tsmCommands = array();
		foreach ($OutputLines as $OutputLine ) {
			$OutputLine = preg_replace('/\s+$/','',$OutputLine);
			if ($debug)
				debug_dump("($isQueryResult/$isKeyValue): WORKING line [$OutputLine]");

			# See if we got a message number.
			if (preg_match("/^(".$this->getValue('tsmparm','msgFormat').")\s+/",$OutputLine,$matches)) {
				$msgnum = $matches[1];

				if ($debug)
					debug_dump("($isQueryResult/$isKeyValue): Got SYSTEM Msg [$OutputLine]");

				# Filter through the output and check if we got our start message.
				if (preg_match("/^".$this->getValue('tsmparm','queryStartMsg')."\s+/",$OutputLine)) {
					if ($debug)
						debug_dump("($isQueryResult/$isKeyValue): Got START Msg [$OutputLine]");
					$isQueryResult = 1;
					continue;

				# Break out of loop when we get our end message.
				} elseif (preg_match("/^".$this->getValue('tsmparm','queryEndMsg')."\s+/",$OutputLine)) {
					if ($debug)
						debug_dump("($isQueryResult/$isKeyValue): Got END Msg [$OutputLine]");
					break;
				}

				if (in_array($msgnum,$this->getValue('tsmparm','nodatamsg')))
					continue;

				if (! in_array($msgnum,$this->getValue('tsmparm','ignoremsg')))
					system_message(array('title'=>'TSM Message','body'=>$OutputLine));
			}

			# If we the line is a queryResult line
			if ($isQueryResult) {

				# See if we got a key:value pair, otherwise go to next line.
				if (preg_match('/:/',$OutputLine) && ! (preg_match('/^help/',$query))) {
					$OutputLine = preg_replace('/\s*:\s+/',':',$OutputLine);
					list($key,$value) = explode(':',$OutputLine,2);
					$isKeyValue = 1;

				# Not key:value, so check if we need to increase the index.
				} else {

					# Is this a show slots command, so there is more info
					if (preg_match('/^show slots/',$query)) {

						if (preg_match('/^Slot /',$OutputLine)) {

							foreach ((preg_split('/,\s*/',$OutputLine,-1)) as $slotkey => $val) {

								if (preg_match('/^element number\s+/',$val)) {
									$key = preg_replace('/^element number\s+/','',$val);

								} elseif (preg_match('/^Slot\s+/',$val)) {
									$slots['slot'] = preg_replace('/^Slot\s+/','',$val);

								} elseif (preg_match('/^status\s+/',$val)) {
									$slots['status'] = preg_replace('/^status\s+/','',$val);

								} elseif (preg_match('/^barcode value </',$val)) {
									$slots['barcodelabel'] = preg_replace('/^barcode value <(.*)>/',"$1",$val);

								} elseif (preg_match('/^barcode\s+/',$val)) {
									$slots['barcode'] = preg_replace('/^barcode /','',$val);
								}
							}

							$QueryResult[$outindex]['SlotUsage'][$key] = $slots;

						} elseif (preg_match('/busy.$/',$OutputLine)) {
							system_message(array('title'=>'Library is Busy',
								'body'=>sprintf('The library appears busy at the moment.<br />%s',$OutputLine),
								'type'=>'info'),
								sprintf('index.php',get_request('cmd','REQUEST') ? sprintf('cmd=%s',get_request('cmd','REQUEST')) : ''));
						}

					# Is this a help command, so there is more info
					} elseif (preg_match('/^help$/',$query)) {
						if (preg_match('/^\s*([0-9]+) - Commands /',$OutputLine)) {
							$helpnum = preg_replace('/^\s*([0-9]+) - Commands .*/','$1',$OutputLine);

							$tsmCommands = array_unique(array_merge($tsmCommands,$this->query("help $helpnum",null)));
						}

					} elseif (preg_match('/^help\s+[0-9]+/',$query)) {
						if (! $OutputLine)
							continue;

						if (! $haveHelp && preg_match('/^\s*Command Name/',$OutputLine)) {
							$haveHelp = true;
							$helpCommands = array();
							continue;
						}

						if ($haveHelp) {
							# Filter out the beginning spaces.
							$OutputLine = preg_replace('/^\s*/','',$OutputLine);

							# Filter out the "(See Note)" messages.
							$OutputLine = preg_replace('/\s\(.*\)/','',$OutputLine);

							# Join the two columns together
							$OutputLine = preg_replace('/\ \s+/','|',$OutputLine);

							# We should now just have commands and they should be in uppercase.
							if (preg_match('/[a-z0-9]/',$OutputLine))
								continue;

							#debug_dump("Im finally here with [$OutputLine].");

							foreach (explode('|',$OutputLine) as $helpCMD)
								array_push($helpCommands,$helpCMD);
						}

					} elseif (preg_match('/^help\s+[A-Z]+/',$query)) {
						# Sometimes QueryResult gets data because of colons - we need to capture that.
						if (isset($QueryResult) && is_array($QueryResult)) {
							foreach ($QueryResult as $lineDetail)
								foreach ($lineDetail as $key => $ldindex)
									$helpText[] = $key.$ldindex;
							$QueryResult = array();
						}

						$helpText[] = $OutputLine;
						//debug_dump(array("Im finally here with [$OutputLine]."=>$QueryResult));

					# If this is a blank line
					} elseif (!$OutputLine) {

						# Only increment index if the previous iteration was a key/value pair.
						# Reset isKeyValue so we dont increase index for 2 non key:value lines in a row.
						if ($isKeyValue) {
							if (!preg_match('/^show/',$query))
								$outindex++;
							$isKeyValue = 0;
						}

					} // if
					continue;

				} // if

				$key = preg_replace('/^\s+/','',$key);
				$value = preg_replace('/^\s+/','',$value);
				$QueryResult[$outindex][$key] = $value;

			} // if
		} // foreach

		if (is_array($tsmCommands) && preg_match('/^help$/',$query)) {
			asort($tsmCommands);
			$QueryResult = $tsmCommands;
		}

		if (isset($helpCommands) && preg_match('/^help\s+[0-9]+/',$query))
			$QueryResult = $helpCommands;

		if (isset($helpText) && preg_match('/^help\s+[A-Z]+/',$query))
			$QueryResult = $helpText;

		if ($debug)
			debug_dump($QueryResult,0);

		if (! $isQueryResult && $debug)
			Error(sprintf('The query <BR>%s<BR>didnt return anything [%s]',preg_replace('/-password=(.*)\ -/','-password=x -',$TSMcmd),serialize($OutputLines)));

		if ($index && is_array($QueryResult)) {
			foreach ($QueryResult as $result => $val) {
				if (isset($val[$index]))
					$IndexQueryResult[$val[$index]] = $QueryResult[$result];
			}

			return (isset($IndexQueryResult) ? $IndexQueryResult : false);
		} elseif (is_array($QueryResult)) {
			return $QueryResult;
		}
		return array();
	}

	/**
	 * Get the last error string
	 */
	protected function getErrorMessage() {
		return null;
	}

	/**
	 * Get the last error number
	 */
	protected function getErrorNum() {
		return null;
	}

	/**
	 * Return if anonymous bind is allowed in the configuration
	 */
	public function isAnonBindAllowed() {
		return false;
	}

	/**
	 * Query the database for particular attributes values.
	 */
	public function queryAttr($table,$attrs,$key,$where=null,$orderby=null) {
		return array();
	}

	/**
	 * Register user to the application
	 */
	public function register($user,$pass,$other=array()) {
		return null;
	}

	/**
	 * Query TSM and get Database information
	 */
	function QueryDBDetail() {
		$query = $this->query('select * from DB',null);
		$this->CACHE['db'] = $query[0];
		$this->CACHE['db']['dbvols'] = $this->query('select * from DBVOLUMES',null);
		$this->CACHE['cache']['db'] = time();
		$this->CACHE['cache']['log'] = time();

		$query = $this->query('select * from LOG',null);
		$this->CACHE['log'] = $query[0];
		$this->CACHE['log']['logvols'] = $this->query('select * from LOGVOLUMES',null);

		$_SESSION['tsm'][$this->index] = $this->CACHE;
	}

	/**
	 * Return the DB information.
	 * @param string attribute
	 * @return string value for attribute
	 */
	function GetDBDetail($attribute) {
		if (! isset($this->CACHE['db'][$attribute]))
			$this->QueryDBDetail();

		if (! isset($this->CACHE['db'][$attribute]))
			Error(sprintf(_('ERROR: A request to get DB attribute "%" didnt return a value'),$attribute));

		return $this->CACHE['db'][$attribute];
	}

	/**
	 * Return the LOG information.
	 * @param string attribute
	 * @return string value for attribute
	 */
	function GetLogDetail($attribute) {
		if (! isset($this->CACHE['log'][$attribute]))
			$this->QueryDBDetail();

		if (! isset($this->CACHE['log'][$attribute]))
			Error(sprintf(_('ERROR: A request to get LOG attribute "%" didnt return a value'),$attribute));

		return $this->CACHE['log'][$attribute];
	}

	/**
	 * Return the VOLUMES information
	 * @return array
	 */
	function GetVolumes() {
		if (isset($this->CACHE['volumes']))
			return $this->CACHE['volumes'];

		$this->CACHE['volumes'] = $this->query('select * from VOLUMES',null,'VOLUME_NAME');
		$this->CACHE['cache']['volumes'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['volumes'];
	}

	/**
	 * Return the LIBVOLUMES information
	 * @return array
	 */
	function GetLibVolumes() {
		if (isset($this->CACHE['libvols']))
			return $this->CACHE['libvols'];

		$this->CACHE['libvols'] = $this->query('select * from LIBVOLUMES',null,'HOME_ELEMENT');
		$this->CACHE['cache']['libvols'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['libvols'];
	}

	/**
	 * Return the DB Backup Volumes
	 * @return array
	 */
	function GetDBBackupInfo() {
		if (isset($this->CACHE['dbbackup']))
			return $this->CACHE['dbbackup'];

		$this->CACHE['dbbackup']['vols'] = $this->query("select * from VOLHISTORY where TYPE in ('BACKUPFULL','BACKUPINCR','DBSNAPSHOT') order by BACKUP_SERIES,BACKUP_OPERATION,VOLUME_SEQ",
			null,'VOLUME_NAME');

		# Now check that a backup series is valid - since it should be in order.
		$lastSeries = -1;
		$backupfirst = '';
		$backuplast = '';
		$backupcount = 0;
		if (is_array($this->CACHE['dbbackup']['vols'])) {
			foreach ($this->CACHE['dbbackup']['vols'] as $volume => $voldetails) {
				if ($lastSeries != $voldetails['BACKUP_SERIES']) {
					$lastSeries = $voldetails['BACKUP_SERIES'];
					$lastOperation = -1;
					$lastSeq = 0;

					if (! $backupfirst | $voldetails['DATE_TIME'] < $backupfirst)
						$backupfirst = $voldetails['DATE_TIME'];
					if (! $backuplast | $voldetails['DATE_TIME'] > $backuplast)
						$backuplast = $voldetails['DATE_TIME'];
					$backupcount++;
				}

				# No point trying this series if it is invalid.
				if (isset($status[$voldetails['BACKUP_SERIES']]['status']) &&
					($status[$voldetails['BACKUP_SERIES']]['status'] == 'InValid'))

					continue;

				$this->CACHE['dbbackup']['vols'][$volume]['STATUS'] = 'Valid';

				if (($voldetails['BACKUP_OPERATION'] == $lastOperation) || ($voldetails['BACKUP_OPERATION']-1 == $lastOperation)) {
					$status[$voldetails['BACKUP_SERIES']]['status'] = 'Valid';
					$lastOperation = $voldetails['BACKUP_OPERATION'];

				} else {
					$status[$voldetails['BACKUP_SERIES']]['status'] = 'InValid';
					$status[$voldetails['BACKUP_SERIES']]['statusreason'] = sprintf(_('Missing SERIES volumes, expected [%s], found [%s].'),
						$lastOperation+1,$voldetails['BACKUP_OPERATION']);
					continue;
				}

				if (($voldetails['VOLUME_SEQ'] == $lastSeq) || $voldetails['VOLUME_SEQ']-1 == $lastSeq) {
					$status[$voldetails['BACKUP_SERIES']]['status'] = 'Valid';
					$lastSeq = $voldetails['VOLUME_SEQ'];

				} else {
					$status[$voldetails['BACKUP_SERIES']]['status'] = 'InValid';
					$status[$voldetails['BACKUP_SERIES']]['statusreason'] = sprintf(_('Missing SERIES volumes, expected [%s], found [%s].'),$lastSeq+1,$voldetails['VOLUME_SEQ']);
				}

				$lastSeq = $voldetails['VOLUME_SEQ'];
			}

			# Now we know which series are invalid, mark all the volumes.
			foreach ($this->CACHE['dbbackup']['vols'] as $volume => $voldetails) {
				$this->CACHE['dbbackup']['vols'][$volume]['STATUS'] = $status[$voldetails['BACKUP_SERIES']]['status'];
				if (isset($status[$voldetails['BACKUP_SERIES']]['statusreason']))
					$this->CACHE['dbbackup']['vols'][$volume]['STATUSREASON'] = $status[$voldetails['BACKUP_SERIES']]['statusreason'];
			}
		}

		# Grab backup information
		$history = $this->query('select DATE_TIME,MSGNO,MESSAGE from ACTLOG where MSGNO in (4550,4551) order by DATE_TIME',null);

		if (! isset($history[0]['ANR2034E SELECT']))
			foreach ($history as $index => $historydetail) {
				$this->CACHE['dbbackup']['history'][$index]['date'] = $historydetail['DATE_TIME'];

				switch ($historydetail['MSGNO']) {
					case 4550:
						if (preg_match('/([0-9]+) pages copied./',$historydetail['MESSAGE'],$matchall)) {
							$this->CACHE['dbbackup']['history'][$index]['size'] = $matchall[1];
							$this->CACHE['dbbackup']['history'][$index]['type'] = 'FULL';
						}
						break;

					case 4551:
						if (preg_match('/([0-9]+) pages copied./',$historydetail['MESSAGE'],$matchall)) {
							$this->CACHE['dbbackup']['history'][$index]['size'] = $matchall[1];
							$this->CACHE['dbbackup']['history'][$index]['type'] = 'INCR';
						}
						break;

					default:
						printf('Message %s not catered for [%s]...',$historydetail['MSGNO'],$historydetail['MESSAGE']);
						die();
				}
			}

		# Get DBBackup Trigger information.
		$select = $this->query('select * from DBBACKUPTRIGGER',null);
		$result = array_shift($select);

		if (isset($result['ANR2034E SELECT']))
			$this->CACHE['dbbackup']['trigger'] = false;
		else
			$this->CACHE['dbbackup']['trigger'] = $result;

		$this->CACHE['dbbackup']['first'] = $backupfirst;
		$this->CACHE['dbbackup']['last'] = $backuplast;
		$this->CACHE['dbbackup']['count'] = $backupcount;

		$this->CACHE['cache']['dbbackup'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['dbbackup'];
	}

	function GetDBBackupDetail($detail) {
		if (isset($this->CACHE['dbbackup'][$detail]))
			return $this->CACHE['dbbackup'][$detail];

		$this->GetDBBackupInfo();

		if (isset($this->CACHE['dbbackup'][$detail]))
			return $this->CACHE['dbbackup'][$detail];
		else
			return null;
	}

	/**
	 * Return the Status Information
	 * @return array
	 */
	function GetStatus() {
		if (isset($this->CACHE['status']))
			return $this->CACHE['status'];

		$select = $this->query('select * from STATUS',null);
		$this->CACHE['status'] = array_shift($select);

		$this->CACHE['cache']['status'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['status'];
	}

	function GetStatusDetail($detail) {
		if (isset($this->CACHE['status'][$detail]))
			return $this->CACHE['status'][$detail];

		$this->GetStatus();

		if (isset($this->CACHE['status'][$detail]))
			return $this->CACHE['status'][$detail];
		else
			return null;
	}

	/**
	 * Get TSM event information
	 * @param string Start date for report
	 * @param string End date for report
	 * @param string order by key.
	 * @return array Query result
	 */
	function GetEvents($reportStart='',$reportEnd='',$orderby='SCHEDULED_START') {
		if (isset($this->CACHE['events']) && $reportStart == $this->CACHE['events']['start'] &&
			$reportEnd == $this->CACHE['events']['end'] && $orderby == $this->CACHE['events']['orderby'])

			return $this->CACHE['events'];

		$TSMQuery = 'select * from EVENTS where DOMAIN_NAME is not null';

		if (($reportStart) && ($reportEnd))
			$TSMQuery .= sprintf(" and (SCHEDULED_START between '%s' and '%s')",$reportStart,$reportEnd);
		else if ($reportStart)
			$TSMQuery .= sprintf(" and SCHEDULED_START \> '%s'",$reportStart);
		else if ($reportEnd)
			$TSMQuery .= sprintf(" and SCHEDULED_START <= '%s'",$reportEnd);

		$TSMQuery .= sprintf(' order by %s',$orderby);

		$result = $this->query($TSMQuery,null);

		if (isset($result[0]['ANR2034E SELECT'])) {
			$this->CACHE['events'] = false;

		} else {
			$this->CACHE['events']['detail'] = $result;
			$this->CACHE['events']['start'] = $reportStart;
			$this->CACHE['events']['end'] = $reportEnd;
			$this->CACHE['events']['orderby'] = $orderby;
		}

		$this->CACHE['cache']['events'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['events'];
	}

	/**
	 * Get TSM Activity Log Client backup information.
	 * @param string Start date for report
	 * @param string End date for report
	 * @return array Query result
	 */
	function GetActlogBackupSummary($reportStart='',$reportEnd='') {
		if (isset($this->CACHE['actlogbackup']) && $reportStart == $this->CACHE['actlogbackup']['start'] &&
			$reportEnd == $this->CACHE['actlogbackup']['end'])

			return $this->CACHE['actlogbackup'];

		$tsmMSG['4952'] = 'Inspected';
		$tsmMSG['4954'] = 'Backup';
		$tsmMSG['4958'] = 'Update';
		$tsmMSG['4957'] = 'Delete';
		$tsmMSG['4959'] = 'Failed';
		$tsmMSG['4960'] = 'Rebound';
		$tsmMSG['4961'] = 'Transfer';
		$tsmMSG['4964'] = 'ProcTime';
		$tsmMSG['4967'] = 'AggRate';
		$tsmMSG['4968'] = 'Compress';
		$tsmMSG['4970'] = 'Expire';

		$TSMQuery = sprintf('select MSGNO,NODENAME,SESSID,MESSAGE,SCHEDNAME,DOMAINNAME from ACTLOG where MSGNO in (%s)',
			implode(',',array_keys($tsmMSG)));

		if (($reportStart) && ($reportEnd))
			$TSMQuery .= sprintf(" and (DATE_TIME between '%s' and '%s')",$reportStart,$reportEnd);
		else if ($reportStart)
			$TSMQuery .= sprintf(" and DATE_TIME \> '%s'",$reportStart);
		else if ($reportEnd)
			$TSMQuery .= sprintf(" and DATE_TIME <= '%s'",$reportEnd);

		$TSMQuery .= ' order by sessid';

		foreach ($this->query($TSMQuery,null) as $tsmBackup => $tsmSession) {
			# TSM on AIX adds a "SESSION" at the end of each line - maybe others do as well.
			if (isset($tsmSession['MESSAGE']))
				$tsmSession['MESSAGE'] = preg_replace('/\(SESSION:.*\)/','',$tsmSession['MESSAGE']);

			$tsmBackupSummary['detail'][$tsmSession['NODENAME']][$tsmSession['SESSID']][$tsmMSG[$tsmSession['MSGNO']]] = preg_replace('/^.*:\s*/U','',$tsmSession['MESSAGE']);
		}

		$tsmBackupSummary['start'] = $reportStart;
		$tsmBackupSummary['end'] = $reportEnd;

		$this->CACHE['actlogbackup'] = $tsmBackupSummary;
		$this->CACHE['cache']['actlogbackup'] = time();
		$_SESSION['tsm'][$this->index] = $this->CACHE;
		return $this->CACHE['actlogbackup'];
	}

	function GetVolLocation($vol) {
		$volumes = $this->GetVolumes();

		if ($this->GetLibVolumes())
			foreach ($this->GetLibVolumes() as $slot => $details) {
				if ($details['VOLUME_NAME'] == $vol)
					return sprintf('**%s %s**',_('LIBRARY'),$details['LIBRARY_NAME']);
			}

		if (isset($volumes[$vol]))
			return $volumes[$vol]['LOCATION'];

		# If we get here, we dont know where the volume is.
		return null;
	}
}
?>