Merge branch 'support/3.2' into develop

This commit is contained in:
odain
2025-11-07 20:33:14 +01:00
1837 changed files with 33034 additions and 34549 deletions

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -25,7 +26,7 @@
use Combodo\iTop\Application\WebPage\WebPage;
define('SAFE_MINIMUM_MEMORY', 256*1024*1024);
define('SAFE_MINIMUM_MEMORY', 256 * 1024 * 1024);
require_once('../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
@@ -39,37 +40,28 @@ LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be
require_once(APPROOT.'/setup/setuppage.class.inc.php');
require_once(APPROOT.'/setup/xmldataloader.class.inc.php');
function SetMemoryLimit($oP)
{
$sMemoryLimit = trim(ini_get('memory_limit'));
if (empty($sMemoryLimit))
{
if (empty($sMemoryLimit)) {
// On some PHP installations, memory_limit does not exist as a PHP setting!
// (encountered on a 5.2.0 under Windows)
// In that case, ini_set will not work, let's keep track of this and proceed with the data load
$oP->p("No memory limit has been defined in this instance of PHP");
}
else
{
$oP->p("No memory limit has been defined in this instance of PHP");
} else {
// Check that the limit will allow us to load the data
//
$iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY))
{
if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE)
{
$oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
}
else
{
$oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY)) {
if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === false) {
$oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
} else {
$oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// Main
@@ -87,9 +79,7 @@ $sFileName = Utils::ReadParam('file', '', false, 'raw_data');
$oP = new WebPage("iTop - Backoffice data loader");
try
{
try {
// Note: the data model must be loaded first
$oDataLoader = new XMLDataLoader();
@@ -102,8 +92,7 @@ try
SetMemoryLimit($oP);
// The XMLDataLoader constructor has initialized the DB, let's start a transaction
// The XMLDataLoader constructor has initialized the DB, let's start a transaction
CMDBSource::Query('START TRANSACTION');
$oP->p("Starting data load.");
@@ -128,26 +117,21 @@ try
}
}
$aWarnings = $oDataLoader->GetWarnings();
if (count($aWarnings) > 0)
{
if (count($aWarnings) > 0) {
$oP->p('Warnings ('.count($aWarnings).')');
foreach ($aWarnings as $sMsg)
{
foreach ($aWarnings as $sMsg) {
$oP->p(' * '.$sMsg);
}
}
}
}
catch(Exception $e)
{
$oP->p("An error happened while loading the data: ".$e->getMessage());
} catch (Exception $e) {
$oP->p("An error happened while loading the data: ".$e->getMessage());
$oP->p("Aborting (no data written)...");
CMDBSource::Query('ROLLBACK');
}
if (function_exists('memory_get_peak_usage'))
{
if (function_exists('memory_get_peak_usage')) {
$oP->p("Information: memory peak usage: ".memory_get_peak_usage());
}

View File

@@ -41,12 +41,11 @@ require_once(APPROOT.'/application/startup.inc.php');
function GetSender($aHeaders)
{
$aResult = array('name' => '', 'email' => '');
$aResult = ['name' => '', 'email' => ''];
$aResult['name'] = $aHeaders['From'];
$aMatches = array();
if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches))
{
$aResult['email'] = $aMatches[1].'@'.$aMatches[2];
$aMatches = [];
if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches)) {
$aResult['email'] = $aMatches[1].'@'.$aMatches[2];
}
return $aResult;
}
@@ -61,16 +60,14 @@ function GetSender($aHeaders)
function CreateTicket($sSenderEmail, $sSubject, $sBody)
{
$oTicket = null;
try
{
try {
$oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
$oContactSearch->AddCondition('email', $sSenderEmail, '=');
$oSet = new DBObjectSet($oContactSearch);
if ($oSet->Count() == 1)
{
if ($oSet->Count() == 1) {
$oContact = $oSet->Fetch();
$oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
$oTicket = new UserRequest;
$oTicket = new UserRequest();
$oTicket->Set('title', $sSubject);
$oTicket->Set('description', $sBody);
$oTicket->Set('org_id', $oOrganization->GetKey());
@@ -86,14 +83,10 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
$sUserString = $oContact->GetName().', submitted by email';
CMDBObject::SetTrackInfo($sUserString);
$oTicket->DBInsert();
}
else
{
} else {
echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
}
}
catch(Exception $e)
{
} catch (Exception $e) {
echo "Error: exception ".$e->getMessage();
$oTicket = null;
}
@@ -111,15 +104,14 @@ $oPop3->login(MAILBOX_ACCOUNT, MAILBOX_PASSWORD);
// Note: it is expected that the sender of the email exists a valid contact as a 'Contact'
// in iTop (identified by her/his email address), otherwise the ticket creation will fail
$iNbMessages = $oPop3->numMsg();
for($index = 1; $index <= $iNbMessages; $index++)
{
for ($index = 1; $index <= $iNbMessages; $index++) {
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['crlf'] = "\r\n";
$aHeaders = $oPop3->getParsedHeaders($index);
$aSender = GetSender($aHeaders);
$oDecoder = new Mail_mimeDecode( $oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index) );
$oDecoder = new Mail_mimeDecode($oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index));
$oStructure = $oDecoder->decode($params);
$sSubject = $aHeaders['Subject'];
// Search for the text/plain body part
@@ -129,19 +121,14 @@ for($index = 1; $index <= $iNbMessages; $index++)
//echo "<pre>\n";
//print_r($oStructure);
//echo "</pre>\n";
if (!isset($oStructure->parts) || count($oStructure->parts) == 0)
{
if (!isset($oStructure->parts) || count($oStructure->parts) == 0) {
$sTextBody = $oStructure->body;
}
else
{
} else {
// Find the first "part" of the body which is in text/plain
while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
{
while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
//echo "<p>Reading part $iPartIndex</p>\n";
if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain') )
{
if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain')) {
$sTextBody = $oStructure->parts[$iPartIndex]->body;
$bFound = true;
//echo "<p>Plain text found ! ($sTextBody)</p>\n";
@@ -149,14 +136,11 @@ for($index = 1; $index <= $iNbMessages; $index++)
$iPartIndex++;
}
// Try again but this time look for an HTML part
if (!$bFound)
{
while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
{
if (!$bFound) {
while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
//echo "<p>Reading part $iPartIndex</p>\n";
if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
($oStructure->parts[$iPartIndex]->ctype_secondary == 'html') )
{
if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
($oStructure->parts[$iPartIndex]->ctype_secondary == 'html')) {
$sTextBody = $oStructure->parts[$iPartIndex]->body;
$bFound = true;
//echo "<p>HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")</p>\n";
@@ -171,11 +155,10 @@ for($index = 1; $index <= $iNbMessages; $index++)
// name => 'john foo <john.foo@combodo.com>
$oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
if ($oTicket != null)
{
if ($oTicket != null) {
// Ticket created, delete the email
$oPop3->deleteMsg($index);
echo "Ticket: ".$oTicket->GetName()." created.\n";
echo "Ticket: ".$oTicket->GetName()." created.\n";
}
}
$oPop3->disconnect();

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -27,8 +28,7 @@ require_once(__DIR__.'/../approot.inc.php');
const EXIT_CODE_ERROR = -1;
const EXIT_CODE_FATAL = -2;
// early exit
if (file_exists(READONLY_MODE_FILE))
{
if (file_exists(READONLY_MODE_FILE)) {
echo "iTop is read-only. Exiting...\n";
exit(EXIT_CODE_ERROR);
}
@@ -37,8 +37,7 @@ require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/core/background.inc.php');
$sConfigFile = APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE;
if (!file_exists($sConfigFile))
{
if (!file_exists($sConfigFile)) {
echo "iTop is not yet installed. Exiting...\n";
exit(EXIT_CODE_ERROR);
}
@@ -50,8 +49,7 @@ $oCtx = new ContextTag(ContextTag::TAG_CRON);
function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
$sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
if (is_null($sValue))
{
if (is_null($sValue)) {
$oP->p("ERROR: Missing argument '$sParam'\n");
UsageAndExit($oP);
}
@@ -63,13 +61,10 @@ function UsageAndExit($oP)
{
$bModeCLI = ($oP instanceof CLIPage);
if ($bModeCLI)
{
if ($bModeCLI) {
$oP->p("USAGE:\n");
$oP->p("php cron.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file>] [--verbose=1] [--debug=1] [--status_only=1]\n");
}
else
{
} else {
$oP->p("Optional parameters: verbose, param_file, status_only\n");
}
$oP->output();
@@ -93,7 +88,7 @@ function UsageAndExit($oP)
function RunTask(BackgroundTask $oTask, $iTimeLimit)
{
$TaskClass = $oTask->Get('class_name');
$oProcess = new $TaskClass;
$oProcess = new $TaskClass();
$oRefClass = new ReflectionClass(get_class($oProcess));
$oDateStarted = new DateTime();
$oDatePlanned = new DateTime($oTask->Get('next_run_date'));
@@ -102,8 +97,7 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$sMessage = '';
$oExceptionToThrow = null;
try
{
try {
// Record (when starting) that this task was started, just in case it crashes during the execution
$oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
// Record the current user running the cron
@@ -113,42 +107,30 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
// Time in seconds allowed to the task
$iCurrTimeLimit = $iTimeLimit;
// Compute allowed time
if ($oRefClass->implementsInterface('iScheduledProcess') === false)
{
if ($oRefClass->implementsInterface('iScheduledProcess') === false) {
// Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
$iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
$iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
// If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0)
{
if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0) {
$iCurrTimeLimit = $iTaskLimit;
}
}
$sMessage = $oProcess->Process($iCurrTimeLimit);
$oTask->Set('running', 0);
}
catch (MySQLHasGoneAwayException $e)
{
} catch (MySQLHasGoneAwayException $e) {
throw $e;
}
catch (ProcessFatalException $e)
{
} catch (ProcessFatalException $e) {
$oExceptionToThrow = $e;
}
catch (Exception $e) // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
{
if ($oTask->IsDebug())
{
$sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
}
else
{
$sMessage = 'Processing failed with message: '. $e->getMessage();
} catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
if ($oTask->IsDebug()) {
$sMessage = 'Processing failed with message: '.$e->getMessage().'. '.$e->getTraceAsString();
} else {
$sMessage = 'Processing failed with message: '.$e->getMessage();
}
}
$fDuration = microtime(true) - $fStart;
if ($oTask->Get('total_exec_count') == 0)
{
if ($oTask->Get('total_exec_count') == 0) {
// First execution
$oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
}
@@ -158,20 +140,16 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$oDateEnded = new DateTime();
$oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
if ($oRefClass->implementsInterface('iScheduledProcess'))
{
if ($oRefClass->implementsInterface('iScheduledProcess')) {
// Schedules process do repeat at specific moments
$oPlannedStart = $oProcess->GetNextOccurrence();
}
else
{
} else {
// Background processes do repeat periodically
$oPlannedStart = clone $oDatePlanned;
// Let's schedule from the previous planned date of execution to avoid shift
$oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
$oEnd = new DateTime();
while ($oPlannedStart->format('U') < $oEnd->format('U'))
{
while ($oPlannedStart->format('U') < $oEnd->format('U')) {
// Next planned start is already in the past, increase it again by a period
$oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
}
@@ -180,8 +158,7 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
$oTask->DBUpdate();
if ($oExceptionToThrow)
{
if ($oExceptionToThrow) {
throw $oExceptionToThrow;
}
@@ -207,23 +184,21 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
* @throws \OQLException
* @throws \ReflectionException
*/
function CronExec($oP, $bVerbose, $bDebug=false)
function CronExec($oP, $bVerbose, $bDebug = false)
{
$iStarted = time();
$iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
$iTimeLimit = $iStarted + $iMaxDuration;
$iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
if ($bVerbose)
{
if ($bVerbose) {
$oP->p("Planned duration = $iMaxDuration seconds");
$oP->p("Loop pause = $iCronSleep seconds");
}
ReSyncProcesses($oP, $bVerbose, $bDebug);
while (time() < $iTimeLimit)
{
while (time() < $iTimeLimit) {
CheckMaintenanceMode($oP);
$oNow = new DateTime();
@@ -234,23 +209,19 @@ function CronExec($oP, $bVerbose, $bDebug=false)
$oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
$bWorkDone = false;
if ($oTasks->CountExceeds(0))
{
if ($oTasks->CountExceeds(0)) {
$bWorkDone = true;
$aTasks = array();
if ($bVerbose)
{
$aTasks = [];
if ($bVerbose) {
$sCount = $oTasks->Count();
$oP->p("$sCount Tasks planned to run now ($sNow):");
$oP->p('+---------------------------+---------+---------------------+---------------------+');
$oP->p('| Task Class | Status | Last Run | Next Run |');
$oP->p('+---------------------------+---------+---------------------+---------------------+');
}
while ($oTask = $oTasks->Fetch())
{
while ($oTask = $oTasks->Fetch()) {
$aTasks[$oTask->Get('class_name')] = $oTask;
if ($bVerbose)
{
if ($bVerbose) {
$sTaskName = $oTask->Get('class_name');
$sStatus = $oTask->Get('status');
$sLastRunDate = $oTask->Get('latest_run_date');
@@ -258,13 +229,11 @@ function CronExec($oP, $bVerbose, $bDebug=false)
$oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
}
}
if ($bVerbose)
{
if ($bVerbose) {
$oP->p('+---------------------------+---------+---------------------+---------------------+');
}
$aRunTasks = [];
foreach ($aTasks as $oTask)
{
foreach ($aTasks as $oTask) {
$sTaskClass = $oTask->Get('class_name');
$aRunTasks[] = $sTaskClass;
@@ -273,66 +242,54 @@ function CronExec($oP, $bVerbose, $bDebug=false)
CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
// Run the task and record its next run time
if ($bVerbose)
{
if ($bVerbose) {
$oNow = new DateTime();
$oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
}
try
{
try {
$sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
} catch (MySQLHasGoneAwayException $e)
{
} catch (MySQLHasGoneAwayException $e) {
$oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
exit(EXIT_CODE_FATAL);
} catch (ProcessFatalException $e)
{
} catch (ProcessFatalException $e) {
$oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
}
if ($bVerbose)
{
if (!empty($sMessage))
{
if ($bVerbose) {
if (!empty($sMessage)) {
$oP->p("$sTaskClass: $sMessage");
}
$oEnd = new DateTime();
$sNextRunDate = $oTask->Get('next_run_date');
$oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
}
if (time() > $iTimeLimit)
{
if (time() > $iTimeLimit) {
break 2;
}
CheckMaintenanceMode($oP);
}
// Tasks to run later
if ($bVerbose)
{
if ($bVerbose) {
$oP->p('--');
$oSearch = new DBObjectSearch('BackgroundTask');
$oSearch->AddCondition('next_run_date', $sNow, '>');
$oSearch->AddCondition('status', 'active');
$oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
while ($oTask = $oTasks->Fetch())
{
if (!in_array($oTask->Get('class_name'), $aRunTasks))
{
while ($oTask = $oTasks->Fetch()) {
if (!in_array($oTask->Get('class_name'), $aRunTasks)) {
$oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
}
}
}
}
if ($bVerbose && $bWorkDone)
{
if ($bVerbose && $bWorkDone) {
$oP->p("Sleeping...\n");
}
sleep($iCronSleep);
}
if ($bVerbose)
{
if ($bVerbose) {
$oP->p('');
DisplayStatus($oP, ['next_run_date' => true]);
$oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
@@ -342,8 +299,9 @@ function CronExec($oP, $bVerbose, $bDebug=false)
/**
* @param WebPage $oP
*/
function CheckMaintenanceMode(Page $oP) {
// Verify files instead of reloading the full config each time
function CheckMaintenanceMode(Page $oP)
{
// Verify files instead of reloading the full config each time
if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
$oP->p("Maintenance detected, exiting");
exit(EXIT_CODE_ERROR);
@@ -367,16 +325,22 @@ function DisplayStatus($oP, $aTaskOrderBy = [])
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
$oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
while ($oTask = $oTasks->Fetch())
{
while ($oTask = $oTasks->Fetch()) {
$sTaskName = $oTask->Get('class_name');
$sStatus = $oTask->Get('status');
$sLastRunDate = $oTask->Get('latest_run_date');
$sNextRunDate = $oTask->Get('next_run_date');
$iNbRun = (int)$oTask->Get('total_exec_count');
$sAverageRunTime = $oTask->Get('average_run_duration');
$oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |', $sTaskName, $sStatus,
$sLastRunDate, $sNextRunDate, $iNbRun, $sAverageRunTime));
$oP->p(sprintf(
'| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |',
$sTaskName,
$sStatus,
$sLastRunDate,
$sNextRunDate,
$iNbRun,
$sAverageRunTime
));
}
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
}
@@ -401,22 +365,19 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
//
$oSearch = new DBObjectSearch('BackgroundTask');
$oTasks = new DBObjectSet($oSearch);
$aTasks = array();
while ($oTask = $oTasks->Fetch())
{
$aTasks = [];
while ($oTask = $oTasks->Fetch()) {
$aTasks[$oTask->Get('class_name')] = $oTask;
}
$oNow = new DateTime();
$aProcesses = array();
foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass)
{
$oProcess = new $sTaskClass;
$aProcesses = [];
foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass) {
$oProcess = new $sTaskClass();
$aProcesses[$sTaskClass] = $oProcess;
// Create missing entry if needed
if (!array_key_exists($sTaskClass, $aTasks))
{
if (!array_key_exists($sTaskClass, $aTasks)) {
// New entry, let's create a new BackgroundTask record, and plan the first execution
$oTask = new BackgroundTask();
$oTask->SetDebug($bDebug);
@@ -426,41 +387,32 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
$oTask->Set('max_run_duration', 0);
$oTask->Set('average_run_duration', 0);
$oRefClass = new ReflectionClass($sTaskClass);
if ($oRefClass->implementsInterface('iScheduledProcess'))
{
if ($oRefClass->implementsInterface('iScheduledProcess')) {
$oNextOcc = $oProcess->GetNextOccurrence();
$oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
}
else
{
} else {
// Background processes do start asap, i.e. "now"
$oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
}
if ($bVerbose)
{
if ($bVerbose) {
$oP->p('Creating record for: '.$sTaskClass);
$oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
}
$oTask->DBInsert();
}
else
{
} else {
/** @var \BackgroundTask $oTask */
$oTask = $aTasks[$sTaskClass];
if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00')
{
if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00') {
// check for rescheduled tasks
$oRefClass = new ReflectionClass($sTaskClass);
if ($oRefClass->implementsInterface('iScheduledProcess'))
{
if ($oRefClass->implementsInterface('iScheduledProcess')) {
$oNextOcc = $oProcess->GetNextOccurrence();
$oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
$oTask->DBUpdate();
}
}
// Reactivate task if necessary
if ($oTask->Get('status') == 'removed')
{
if ($oTask->Get('status') == 'removed') {
$oTask->Set('status', 'active');
$oTask->DBUpdate();
}
@@ -470,21 +422,17 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
}
// Remove all the tasks not having a valid class
foreach ($aTasks as $oTask)
{
foreach ($aTasks as $oTask) {
$sTaskClass = $oTask->Get('class_name');
if (!class_exists($sTaskClass))
{
if (!class_exists($sTaskClass)) {
$oTask->Set('status', 'removed');
$oTask->DBUpdate();
}
}
if ($bVerbose)
{
$aDisplayProcesses = array();
foreach ($aProcesses as $oExecInstance)
{
if ($bVerbose) {
$aDisplayProcesses = [];
foreach ($aProcesses as $oExecInstance) {
$aDisplayProcesses[] = get_class($oExecInstance);
}
$sDisplayProcesses = implode(', ', $aDisplayProcesses);
@@ -500,58 +448,45 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
set_time_limit(0); // Some background actions may really take long to finish (like backup)
$bIsModeCLI = utils::IsModeCLI();
if ($bIsModeCLI)
{
if ($bIsModeCLI) {
$oP = new CLIPage("iTop - cron");
SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
}
else
{
} else {
$oP = new WebPage("iTop - cron");
}
try
{
try {
utils::UseParamFile();
$bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
$bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
if ($bIsModeCLI)
{
if ($bIsModeCLI) {
// Next steps:
// specific arguments: 'csv file'
//
$sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
{
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
} else {
$oP->p("Access wrong credentials ('$sAuthUser')");
$oP->output();
exit(EXIT_CODE_ERROR);
}
}
else
{
} else {
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
if (!UserRights::IsAdministrator())
{
if (!UserRights::IsAdministrator()) {
$oP->p("Access restricted to administrators");
$oP->Output();
exit(EXIT_CODE_ERROR);
}
if (utils::ReadParam('status_only', false, true /* Allow CLI */))
{
if (utils::ReadParam('status_only', false, true /* Allow CLI */)) {
// Display status and exit
DisplayStatus($oP);
exit(0);
@@ -559,54 +494,36 @@ try
require_once(APPROOT.'core/mutex.class.inc.php');
$oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
}
catch (Exception $e)
{
} catch (Exception $e) {
$oP->p("Error: ".$e->GetMessage());
$oP->output();
exit(EXIT_CODE_FATAL);
}
try
{
try {
$oMutex = new iTopMutex('cron');
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
{
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
$oP->p("A maintenance is ongoing");
}
else
{
if ($oMutex->TryLock())
{
} else {
if ($oMutex->TryLock()) {
CronExec($oP, $bVerbose, $bDebug);
}
else
{
} else {
// Exit silently
$oP->p("Already running...");
}
}
}
catch (Exception $e)
{
} catch (Exception $e) {
$oP->p("ERROR: '".$e->getMessage()."'");
if ($bDebug)
{
if ($bDebug) {
// Might contain verb parameters such a password...
$oP->p($e->getTraceAsString());
}
}
finally
{
try
{
} finally {
try {
$oMutex->Unlock();
}
catch (Exception $e)
{
} catch (Exception $e) {
$oP->p("ERROR: '".$e->getMessage()."'");
if ($bDebug)
{
if ($bDebug) {
// Might contain verb parameters such a password...
$oP->p($e->getTraceAsString());
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -35,15 +36,12 @@ const EXIT_CODE_FATAL = -2;
function ReportErrorAndExit($sErrorMessage)
{
if (utils::IsModeCLI())
{
if (utils::IsModeCLI()) {
$oP = new CLIPage("iTop - Export");
$oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
$oP->output();
exit(EXIT_CODE_ERROR);
}
else
{
} else {
$oP = new WebPage("iTop - Export");
$oP->add_http_headers();
$oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
@@ -54,15 +52,13 @@ function ReportErrorAndExit($sErrorMessage)
function ReportErrorAndUsage($sErrorMessage)
{
if (utils::IsModeCLI())
{
if (utils::IsModeCLI()) {
$oP = new CLIPage("iTop - Export");
$oP->p('ERROR: '.$sErrorMessage);
Usage($oP);
$oP->output();
exit(EXIT_CODE_ERROR);
}
else {
} else {
$oP = new WebPage("iTop - Export");
$oP->add_http_headers();
$oP->p('ERROR: '.$sErrorMessage);
@@ -74,42 +70,32 @@ function ReportErrorAndUsage($sErrorMessage)
function Usage(Page $oP)
{
if (Utils::IsModeCLI())
{
if (Utils::IsModeCLI()) {
$oP->p('Usage: php '.basename(__FILE__).' --auth_user=<user> --auth_pwd=<password> --expression=<OQL Query> --query=<phrasebook_id> [--arg_xxx=<query_arguments>] [--no_localize=0|1] [--format=<format>] [--format-options...]');
$oP->p("Parameters:");
$oP->p(" * auth_user: the iTop user account for authentication");
$oP->p(" * auth_pwd: the password of the iTop user account");
}
else
{
} else {
$oP->p("Parameters:");
}
$oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
$oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
if (Utils::IsModeCLI())
{
if (Utils::IsModeCLI()) {
$oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
}
else
{
} else {
$oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
}
$oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
$aSupportedFormats = BulkExport::FindSupportedFormats();
$oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
foreach($aSupportedFormats as $sFormatCode => $sLabel)
{
foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
$oExporter = BulkExport::FindExporter($sFormatCode);
if ($oExporter !== null)
{
if (!Utils::IsModeCLI())
{
if ($oExporter !== null) {
if (!Utils::IsModeCLI()) {
$oP->add('<hr/>');
}
$oExporter->DisplayUsage($oP);
if (!Utils::IsModeCLI())
{
if (!Utils::IsModeCLI()) {
$oP->add('</div>');
}
}
@@ -248,19 +234,18 @@ EOF
$sExpressionError = '';
if (($sExpression === null) && ($sQueryId === null)) {
$bExpressionIsValid = false;
} else if ($sExpression !== '') {
} elseif ($sExpression !== '') {
try {
$oExportSearch = DBObjectSearch::FromOQL($sExpression);
$oExportSearch->UpdateContextFromUser();
}
catch (OQLException $e) {
} catch (OQLException $e) {
$bExpressionIsValid = false;
$sExpressionError = $e->getMessage();
}
}
if (!$bExpressionIsValid) {
DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError,$oForm);
DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError, $oForm);
return;
}
@@ -275,13 +260,12 @@ EOF
$oExportSearch->UpdateContextFromUser();
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
}
$aFormPartsByFormat = array();
$aAllFormParts = array();
$aFormPartsByFormat = [];
$aAllFormParts = [];
if ($sFormat == null) {
// No specific format chosen
$sDefaultFormat = utils::ReadParam('format', 'xlsx');
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
$oSelect->SetIsLabelBefore(true);
$oForm->AddSubBlock($oSelect);
@@ -390,7 +374,7 @@ EOF
if ($sExpression === null) {
// No expression supplied, let's check if phrasebook entry is given
if ($sQueryId !== null) {
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -412,7 +396,6 @@ EOF
}
}
if ($sFormat !== null) {
$oExporter = BulkExport::FindExporter($sFormat);
if ($oExporter === null) {
@@ -452,7 +435,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
// Either $sExpression or $sQueryId must be specified
if ($sExpression === null) {
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -470,7 +453,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
try {
$oSearch = DBObjectSearch::FromOQL($sExpression);
$oSearch->UpdateContextFromUser();
$aArgs = array();
$aArgs = [];
foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
if (!is_null($value)) {
@@ -483,30 +466,23 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
$sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
$oExporter = BulkExport::FindExporter($sFormat, $oSearch);
if ($oExporter == null)
{
if ($oExporter == null) {
$aSupportedFormats = BulkExport::FindSupportedFormats();
ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
}
}
catch(MissingQueryArgument $e)
{
} catch (MissingQueryArgument $e) {
$oSearch = null;
ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
}
catch(OQLException $e)
{
} catch (OQLException $e) {
$oSearch = null;
ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
}
catch(Exception $e)
{
} catch (Exception $e) {
$oSearch = null;
ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
}
// update last export information if check parameters ok
if($oQuery != null){
if ($oQuery != null) {
$oQuery->UpdateLastExportInformation();
}
@@ -522,24 +498,18 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
{
$oExporter->SetHttpHeaders($oP);
$exportResult = $oExporter->GetHeader();
$aStatus = array();
do
{
$aStatus = [];
do {
$exportResult .= $oExporter->GetNextChunk($aStatus);
}
while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
} while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
if ($aStatus['code'] == 'error')
{
if ($aStatus['code'] == 'error') {
$oExporter->Cleanup();
ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
}
else
{
} else {
$exportResult .= $oExporter->GetFooter();
$sMimeType = $oExporter->GetMimeType();
if (substr($sMimeType, 0, 5) == 'text/')
{
if (substr($sMimeType, 0, 5) == 'text/') {
$sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
}
$oP->SetContentType($sMimeType);
@@ -549,7 +519,6 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Command Line mode
@@ -567,8 +536,7 @@ if (utils::IsModeCLI()) {
try {
// Do this before loging, in order to allow setting user credentials from within the file
utils::UseParamFile();
}
catch (Exception $e) {
} catch (Exception $e) {
echo "Error: ".utils::HtmlEntities($e->getMessage())."<br/>\n";
exit(EXIT_CODE_FATAL);
}
@@ -600,7 +568,7 @@ if (utils::IsModeCLI()) {
}
if ($sExpression === null) {
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -613,7 +581,7 @@ if (utils::IsModeCLI()) {
try {
$oSearch = DBObjectSearch::FromOQL($sExpression);
$oSearch->UpdateContextFromUser();
$aArgs = array();
$aArgs = [];
foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
if (!is_null($value)) {
@@ -626,8 +594,7 @@ if (utils::IsModeCLI()) {
$sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
$oExporter = BulkExport::FindExporter($sFormat);
if ($oExporter == null)
{
if ($oExporter == null) {
$aSupportedFormats = BulkExport::FindSupportedFormats();
ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
}
@@ -638,36 +605,25 @@ if (utils::IsModeCLI()) {
$oExporter->ReadParameters();
$exportResult = $oExporter->GetHeader();
$aStatus = array();
$aStatus = [];
do
{
do {
$exportResult .= $oExporter->GetNextChunk($aStatus);
}
while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
} while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
if ($aStatus['code'] == 'error')
{
if ($aStatus['code'] == 'error') {
ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
}
else
{
} else {
$exportResult .= $oExporter->GetFooter();
echo $exportResult;
}
$oExporter->Cleanup();
}
catch(MissingQueryArgument $e)
{
} catch (MissingQueryArgument $e) {
ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
}
catch(OQLException $e)
{
} catch (OQLException $e) {
ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
}
catch(Exception $e)
{
} catch (Exception $e) {
ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
}
@@ -680,8 +636,7 @@ if (utils::IsModeCLI()) {
//
/////////////////////////////////////////////////////////////////////////////
try
{
try {
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
// Main parameters
@@ -722,14 +677,12 @@ try
DoExport($oP, $oExporter, false);
$oP->output();
}
}
catch (BulkExportMissingParameterException $e) {
} catch (BulkExportMissingParameterException $e) {
$oP = new AjaxPage('iTop Export');
$oP->add(utils::HtmlEntities($e->getMessage()));
Usage($oP);
$oP->output();
}
catch (Exception $e) {
} catch (Exception $e) {
$oP = new WebPage('iTop Export');
$oP->add_http_headers();
$oP->add('Error: '.utils::HtmlEntities($e->getMessage()));

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -20,12 +21,10 @@
if (isset($_REQUEST['debug'])) {
if ($_REQUEST['debug'] == 'text') {
header('Content-Type: text/plain; charset=UTF-8');
}
else {
} else {
header('Content-Type: application/xml; charset=UTF-8');
}
}
else {
} else {
// This is to make sure that the client will accept it....
//
header('Content-Type: application/xml; charset=UTF-8');
@@ -47,8 +46,7 @@ require_once(APPROOT.'webservices/webservices.basic.php');
$sServiceCategory = utils::ReadParam('service_category');
if (!empty($sServiceCategory)) {
$sRawFile = WebServicesBase::GetWSDLContents($sServiceCategory);
}
else {
} else {
$sRawFile = WebServicesBase::GetWSDLContents();
}
@@ -64,4 +62,3 @@ $sFinalFile = str_replace(
);
echo $sFinalFile;
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,9 +17,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Shows a usage of the SOAP queries
* Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -36,29 +36,26 @@
* @return bool|false|string
* @throws \Exception
*/
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = [])
{
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
if (function_exists('curl_init'))
{
if (function_exists('curl_init')) {
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
// For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
// by setting the SSLVERSION to 3 as done below.
$aHTTPHeaders = array();
if ($sOptionnalHeaders !== null)
{
$aHTTPHeaders = [];
if ($sOptionnalHeaders !== null) {
$aHeaders = explode("\n", $sOptionnalHeaders);
// N°3267 - Webservices: Fix optional headers not being taken into account
// See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
$aHTTPHeaders = array();
foreach($aHeaders as $sHeaderString)
{
$aHTTPHeaders = [];
foreach ($aHeaders as $sHeaderString) {
$aHTTPHeaders[] = trim($sHeaderString);
}
}
// Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
$aOptions = array(
$aOptions = [
CURLOPT_RETURNTRANSFER => true, // return the content of the request
CURLOPT_HEADER => false, // don't return the headers in the output
CURLOPT_FOLLOWLOCATION => true, // follow redirects
@@ -76,74 +73,58 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
CURLOPT_POST => count($aData),
CURLOPT_POSTFIELDS => http_build_query($aData),
CURLOPT_HTTPHEADER => $aHTTPHeaders,
);
];
$aAllOptions = $aCurlOptions + $aOptions;
$ch = curl_init($sUrl);
curl_setopt_array($ch, $aAllOptions);
$response = curl_exec($ch);
$iErr = curl_errno($ch);
$sErrMsg = curl_error( $ch );
if ($iErr !== 0)
{
$sErrMsg = curl_error($ch);
if ($iErr !== 0) {
throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
}
if (is_array($aResponseHeaders))
{
if (is_array($aResponseHeaders)) {
$aHeaders = curl_getinfo($ch);
foreach($aHeaders as $sCode => $sValue)
{
$sName = str_replace(' ' , '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
foreach ($aHeaders as $sCode => $sValue) {
$sName = str_replace(' ', '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
$aResponseHeaders[$sName] = $sValue;
}
}
curl_close( $ch );
}
else
{
curl_close($ch);
} else {
// cURL is not available let's try with streams and fopen...
$sData = http_build_query($aData);
$aParams = array('http' => array(
$aParams = ['http' => [
'method' => 'POST',
'content' => $sData,
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
));
if ($sOptionnalHeaders !== null)
{
'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
]];
if ($sOptionnalHeaders !== null) {
$aParams['http']['header'] .= $sOptionnalHeaders;
}
$ctx = stream_context_create($aParams);
$fp = @fopen($sUrl, 'rb', false, $ctx);
if (!$fp)
{
if (!$fp) {
global $php_errormsg;
if (isset($php_errormsg))
{
if (isset($php_errormsg)) {
throw new Exception("Wrong URL: $sUrl, $php_errormsg");
}
elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl'))
{
} elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl')) {
throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
}
else
{
} else {
throw new Exception("Wrong URL: $sUrl");
}
}
$response = @stream_get_contents($fp);
if ($response === false)
{
if ($response === false) {
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
}
if (is_array($aResponseHeaders))
{
if (is_array($aResponseHeaders)) {
$aMeta = stream_get_meta_data($fp);
$aHeaders = $aMeta['wrapper_data'];
foreach($aHeaders as $sHeaderString)
{
if(preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches))
{
foreach ($aHeaders as $sHeaderString) {
if (preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches)) {
$aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
}
}
@@ -161,208 +142,203 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
// Define the operations to perform (one operation per call the rest service)
//
$aOperations = array(
array(
$aOperations = [
[
'operation' => 'list_operations', // operation code
),
array(
],
[
'operation' => 'core/create', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'fields' => [
'org_id' => "SELECT Organization WHERE name = 'Demo'",
'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
'title' => 'issue blah',
'description' => 'something happened'
),
),
array(
'description' => 'something happened',
],
],
[
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE id=1',
'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'fields' => [
'title' => 'Issue #'.rand(0, 100),
'contacts_list' => array(
array(
'contacts_list' => [
[
'role' => 'fireman #'.rand(0, 100),
'contact_id' => array('finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'),
),
),
),
),
'contact_id' => ['finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'],
],
],
],
],
// Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
array(
[
'operation' => 'core/update',
'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE id=1',
'output_fields' => 'id, friendlyname, title',
'fields' => array(
'public_log' => array(
'items' => array(
0 => array(
'fields' => [
'public_log' => [
'items' => [
0 => [
'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
'user_login' => 'Alarm monitoring', //Free text
'user_id' => 0, //0 is required for the user_login to be taken into account
'message' => 'This is 1st entry as an <b>HTML</b> formatted<br>text',
),
1 => array(
],
1 => [
'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
'message' => 'Second entry in text format:
with new line, but format not specified, so treated as HTML!, user_id=0 missing, so user_login ignored',
),
),
),
),
),
],
],
],
],
],
// Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
array(
[
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 1, // object id or OQL
'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
// Example of adding an entry into a CaseLog on an existing UserRequest
'fields' => array(
'public_log' => array(
'add_item' => array(
'fields' => [
'public_log' => [
'add_item' => [
'user_login' => 'New Entry', //Free text
'user_id' => 0, //0 is required for the user_login to be taken into account
'format' => 'text', //If ommitted, source is expected to be HTML
'message' => 'This text is not HTML formatted with 3 lines:
new line
3rd and last line',
),
),
),
),
array(
],
],
],
],
[
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest',
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
),
array(
],
[
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE org_id = 2',
'simulate' => true,
),
array(
],
[
'operation' => 'core/apply_stimulus', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 1,
'stimulus' => 'ev_assign',
// Values to set
'fields' => array(
'fields' => [
'team_id' => 15, // Helpdesk
'agent_id' => 9 // Jules Verne
),
'agent_id' => 9, // Jules Verne
],
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
),
array(
],
[
'operation' => 'core/get_related', // operation code
'class' => 'Server',
'key' => 'SELECT Server',
'relation' => 'impacts', // relation code
'depth' => 4, // max recursion depth
),
);
$aOperations = array(
array(
],
];
$aOperations = [
[
'operation' => 'core/create', // operation code
'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
'class' => 'Attachment',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'fields' => [
'item_class' => 'UserRequest',
'item_id' => 1,
'item_org_id' => 3,
'contents' => array(
'contents' => [
'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
'filename' => 'myself.png',
'mimetype' => 'image/png'
),
),
),
array(
'mimetype' => 'image/png',
],
],
],
[
'operation' => 'core/get', // operation code
'class' => 'Attachment',
'key' => 'SELECT Attachment',
'output_fields' => '*',
)
);
$aOperations = array(
array(
],
];
$aOperations = [
[
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server WHERE name="Server1"',
'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'fields' => [
'description' => 'Issue #'.time(),
),
),
);
$aOperations = array(
array(
],
],
];
$aOperations = [
[
'operation' => 'core/create', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'fields' => [
'org_id' => "SELECT Organization WHERE name = 'Demo'",
'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
'title' => 'issue blah',
'description' => 'something happened'
),
),
);
$aXXXOperations = array(
array(
'description' => 'something happened',
],
],
];
$aXXXOperations = [
[
'operation' => 'core/check_credentials', // operation code
'user' => 'admin',
'password' => 'admin',
),
);
$aDeleteOperations = array(
array(
],
];
$aDeleteOperations = [
[
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server',
'simulate' => false,
),
);
],
];
if (false)
{
if (false) {
echo "Please edit the sample script and configure the server URL";
exit;
}
else
{
} else {
$sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
}
$aData = array();
$aData = [];
$aData['auth_user'] = 'rest';
$aData['auth_pwd'] = 'rest';
foreach ($aOperations as $iOp => $aOperation)
{
foreach ($aOperations as $iOp => $aOperation) {
echo "======================================\n";
echo "Operation #$iOp: ".$aOperation['operation']."\n";
$aData['json_data'] = json_encode($aOperation);
@@ -371,25 +347,18 @@ foreach ($aOperations as $iOp => $aOperation)
echo "Input:\n";
print_r($aOperation);
$aResults = null;
try
{
try {
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
}
catch (Exception $e)
{
} catch (Exception $e) {
$response = $e->getMessage();
}
if ($aResults)
{
if ($aResults) {
echo "--------------------------------------\n";
echo "Reply:\n";
print_r($aResults);
}
else
{
} else {
echo "ERROR rest.php replied:\n";
echo $response;
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,9 +17,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Shows a usage of the SOAP queries
* Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -31,17 +31,16 @@ $sWsdlUri = $sItopRoot.'/webservices/itop.wsdl.php';
$aSOAPMapping = SOAPMapping::GetMapping();
ini_set("soap.wsdl_cache_enabled","0");
ini_set("soap.wsdl_cache_enabled", "0");
$oSoapClient = new SoapClient(
$sWsdlUri,
array(
[
'trace' => 1,
'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
)
]
);
try
{
try {
// The most simple service, returning a string
//
$sServerVersion = $oSoapClient->GetVersion();
@@ -50,30 +49,29 @@ try
// More complex ones, returning a SOAPResult structure
// (run the page to know more about the returned data)
//
$oRes = $oSoapClient->CreateIncidentTicket
(
$oRes = $oSoapClient->CreateIncidentTicket(
'admin', /* login */
'admin', /* password */
'Email server down', /* title */
'HW found shutdown', /* description */
null, /* caller */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* customer */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW Management'))), /* service */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Troubleshooting'))), /* service subcategory */
new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Demo')]), /* customer */
new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW Management')]), /* service */
new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Troubleshooting')]), /* service subcategory */
'', /* product */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW support'))), /* workgroup */
array(
new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW support')]), /* workgroup */
[
new SOAPLinkCreationSpec(
'Device',
array(new SOAPSearchCondition('name', 'switch01')),
array()
[new SOAPSearchCondition('name', 'switch01')],
[]
),
new SOAPLinkCreationSpec(
'Server',
array(new SOAPSearchCondition('name', 'dbserver1.demo.com')),
array()
[new SOAPSearchCondition('name', 'dbserver1.demo.com')],
[]
),
), /* impacted cis */
], /* impacted cis */
'1', /* impact */
'1' /* urgency */
);
@@ -84,44 +82,36 @@ try
echo "</pre>\n";
echo "</p>\n";
$oRes = $oSoapClient->SearchObjects
(
$oRes = $oSoapClient->SearchObjects(
'admin', /* login */
'admin', /* password */
'SELECT URP_Profiles' /* oql */
);
echo "<p>SearchObjects() returned:\n";
if ($oRes->status)
{
if ($oRes->status) {
$aResults = $oRes->result;
echo "<table>\n";
// Header made after the first line
echo "<tr>\n";
foreach ($aResults[0]->values as $aKeyValuePair)
{
foreach ($aResults[0]->values as $aKeyValuePair) {
echo " <th>".$aKeyValuePair->key."</th>\n";
}
echo "</tr>\n";
foreach ($aResults as $iRow => $aData)
{
foreach ($aResults as $iRow => $aData) {
echo "<tr>\n";
foreach ($aData->values as $aKeyValuePair)
{
foreach ($aData->values as $aKeyValuePair) {
echo " <td>".$aKeyValuePair->value."</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
}
else
{
$aErrors = array();
foreach ($oRes->errors->messages as $oMessage)
{
} else {
$aErrors = [];
foreach ($oRes->errors->messages as $oMessage) {
$aErrors[] = $oMessage->text;
}
$sErrorMsg = implode(', ', $aErrors);
@@ -131,15 +121,12 @@ try
//echo "</pre>\n";
}
echo "</p>\n";
}
catch(SoapFault $e)
{
echo "<h1>SoapFault Exception: {$e->getMessage()}</h1>\n";
echo "<h2>Request</h2>\n";
echo "<pre>\n";
echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
echo "</pre>";
} catch (SoapFault $e) {
echo "<h1>SoapFault Exception: {$e->getMessage()}</h1>\n";
echo "<h2>Request</h2>\n";
echo "<pre>\n";
echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
echo "</pre>";
echo "<h2>Response</h2>";
echo $oSoapClient->__getLastResponse()."\n";
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Declarations required for the WSDL
*
@@ -24,7 +24,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Note: the attributes must have the same names (case sensitive) as in the WSDL specification
//
@@ -40,7 +39,6 @@ class SOAPSearchCondition
}
}
class SOAPExternalKeySearch
{
public $conditions; // array of SOAPSearchCondition
@@ -52,12 +50,15 @@ class SOAPExternalKeySearch
public function IsVoid()
{
if (is_null($this->conditions)) return true;
if (count($this->conditions) == 0) return true;
if (is_null($this->conditions)) {
return true;
}
if (count($this->conditions) == 0) {
return true;
}
}
}
class SOAPAttributeValue
{
public $attcode; // string
@@ -70,7 +71,6 @@ class SOAPAttributeValue
}
}
class SOAPLinkCreationSpec
{
public $class;
@@ -85,7 +85,6 @@ class SOAPLinkCreationSpec
}
}
class SOAPLogMessage
{
public $text; // string
@@ -96,7 +95,6 @@ class SOAPLogMessage
}
}
class SOAPResultLog
{
public $messages; // array of SOAPLogMessage
@@ -107,7 +105,6 @@ class SOAPResultLog
}
}
class SOAPKeyValue
{
public $key; // string
@@ -132,7 +129,6 @@ class SOAPResultMessage
}
}
class SOAPResult
{
public $status; // boolean
@@ -163,12 +159,11 @@ class SOAPSimpleResult
}
}
class SOAPMapping
{
static function GetMapping()
public static function GetMapping()
{
$aSOAPMapping = array(
$aSOAPMapping = [
'SearchCondition' => 'SOAPSearchCondition',
'ExternalKeySearch' => 'SOAPExternalKeySearch',
'AttributeValue' => 'SOAPAttributeValue',
@@ -180,9 +175,7 @@ class SOAPMapping
'ResultMessage' => 'SOAPResultMessage',
'Result' => 'SOAPResult',
'SimpleResult' => 'SOAPSimpleResult',
);
];
return $aSOAPMapping;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -27,10 +28,9 @@ require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'core/restservices.class.inc.php');
/**
* Result structure that is specific to the hardcoded verb 'list_operations'
*/
*/
class RestResultListOperations extends RestResult
{
public $version;
@@ -38,24 +38,25 @@ class RestResultListOperations extends RestResult
public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
{
$this->operations[] = array(
$this->operations[] = [
'verb' => $sVerb,
'description' => $sDescription,
'extension' => $sServiceProviderClass,
);
];
}
}
if (!function_exists('json_last_error_msg')) {
function json_last_error_msg() {
static $ERRORS = array(
function json_last_error_msg()
{
static $ERRORS = [
JSON_ERROR_NONE => 'No error',
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
JSON_ERROR_SYNTAX => 'Syntax error',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
);
];
$error = json_last_error();
return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
@@ -74,15 +75,14 @@ $sOperation = utils::ReadParam('operation', null);
//read json_data parameter via as a string (standard behaviour)
$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
if (empty($sJsonString)){
if (empty($sJsonString)) {
//N °3455: read json_data parameter via a file passed by http protocol
if(isset($_FILES['json_data']['tmp_name']))
{
if (isset($_FILES['json_data']['tmp_name'])) {
$sTmpFilePath = $_FILES['json_data']['tmp_name'];
if (is_file($sTmpFilePath)){
if (is_file($sTmpFilePath)) {
$sValue = file_get_contents($sTmpFilePath);
unlink($sTmpFilePath);
if (! empty($sValue)){
if (! empty($sValue)) {
$sJsonString = utils::Sanitize($sValue, null, 'raw_data');
}
}
@@ -92,136 +92,111 @@ if (empty($sJsonString)){
$sProvider = '';
$oKPI = new ExecutionKPI();
try
{
try {
utils::UseParamFile();
$oKPI->ComputeAndReport('Data model loaded');
// N°6358 - force credentials for REST calls
LoginWebPage::ResetSession(true);
// N°6358 - force credentials for REST calls
LoginWebPage::ResetSession(true);
$iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
$oKPI->ComputeAndReport('User login');
$oKPI->ComputeAndReport('User login');
if ($iRet == LoginWebPage::EXIT_CODE_OK)
{
if ($iRet == LoginWebPage::EXIT_CODE_OK) {
// Extra validation of the profile
if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User'))
{
if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User')) {
// Web services access is limited to the users with the profile REST Web Services
$iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
}
}
if ($iRet != LoginWebPage::EXIT_CODE_OK)
{
switch($iRet)
{
if ($iRet != LoginWebPage::EXIT_CODE_OK) {
switch ($iRet) {
case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
break;
throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
break;
case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
break;
throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
break;
case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
break;
throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
break;
case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
break;
throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
break;
case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
break;
throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
break;
default:
throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
}
}
if ($sVersion == null)
{
if ($sVersion == null) {
throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
}
if ($sJsonString == null)
{
if ($sJsonString == null) {
throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
}
if (is_string($sJsonString))
{
$aJsonData = @json_decode($sJsonString);
}
elseif(is_array($sJsonString))
{
$aJsonData = (object) $sJsonString;
$sJsonString = json_encode($aJsonData);
}
else
{
$aJsonData = null;
}
if (is_string($sJsonString)) {
$aJsonData = @json_decode($sJsonString);
} elseif (is_array($sJsonString)) {
$aJsonData = (object) $sJsonString;
$sJsonString = json_encode($aJsonData);
} else {
$aJsonData = null;
}
if ($aJsonData == null)
{
throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
}
if ($aJsonData == null) {
throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
}
$oKPI->ComputeAndReport('Parameters validated');
/** @var iRestServiceProvider[] $aProviders */
$oKPI = new ExecutionKPI();
$aProviders = array();
foreach(get_declared_classes() as $sPHPClass)
{
$aProviders = [];
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->implementsInterface('iRestServiceProvider'))
{
$aProviders[] = new $sPHPClass;
if ($oRefClass->implementsInterface('iRestServiceProvider')) {
$aProviders[] = new $sPHPClass();
}
}
$aOpToRestService = array(); // verb => $oRestServiceProvider
$aOpToRestService = []; // verb => $oRestServiceProvider
/** @var iRestServiceProvider $oRestSP */
foreach ($aProviders as $oRestSP)
{
foreach ($aProviders as $oRestSP) {
$aOperations = $oRestSP->ListOperations($sVersion);
foreach ($aOperations as $aOpData)
{
$aOpToRestService[$aOpData['verb']] = array
(
foreach ($aOperations as $aOpData) {
$aOpToRestService[$aOpData['verb']] =
[
'service_provider' => $oRestSP,
'description' => $aOpData['description'],
);
];
}
}
$oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
if (count($aOpToRestService) == 0)
{
if (count($aOpToRestService) == 0) {
throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
}
$sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
if ($sOperation == 'list_operations')
{
if ($sOperation == 'list_operations') {
$oResult = new RestResultListOperations();
$oResult->message = "Operations: ".count($aOpToRestService);
$oResult->version = $sVersion;
foreach ($aOpToRestService as $sVerb => $aOpData)
{
foreach ($aOpToRestService as $sVerb => $aOpData) {
$oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
}
}
else
{
if (!array_key_exists($sOperation, $aOpToRestService))
{
} else {
if (!array_key_exists($sOperation, $aOpToRestService)) {
throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
}
/** @var iRestServiceProvider $oRS */
@@ -236,16 +211,11 @@ try
$oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
}
$oKPI->ComputeAndReport('Operation finished');
}
catch(Exception $e)
{
} catch (Exception $e) {
$oResult = new RestResult();
if ($e->GetCode() == 0)
{
if ($e->GetCode() == 0) {
$oResult->code = RestResult::INTERNAL_ERROR;
}
else
{
} else {
$oResult->code = $e->GetCode();
}
$oResult->message = "Error: ".$e->GetMessage();
@@ -256,23 +226,17 @@ catch(Exception $e)
//
$sResponse = json_encode($oResult);
if ($sResponse === false)
{
if ($sResponse === false) {
$oJsonIssue = new RestResult();
$oJsonIssue->code = RestResult::INTERNAL_ERROR;
$oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
$sResponse = json_encode($oJsonIssue);
}
$sCallback = utils::ReadParam('callback', null);
if ($sCallback == null)
{
if ($sCallback == null) {
$oP = new JsonPage();
}
else
{
} else {
$oP = new JsonPPage($sCallback);
}
$oP->add_header('Access-Control-Allow-Origin: *');
@@ -283,18 +247,16 @@ ExecutionKPI::ReportStats();
// Log usage
//
if (MetaModel::GetConfig()->Get('log_rest_service'))
{
if (MetaModel::GetConfig()->Get('log_rest_service')) {
$oLog = new EventRestService();
$oLog->SetTrim('userinfo', UserRights::GetUser());
$oLog->Set('version', $sVersion);
$oLog->Set('operation', $sOperation);
$oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
$oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
$oLog->Set('provider', $sProvider);
$sMessage = $oResult->message;
if (empty($oResult->message))
{
if (empty($oResult->message)) {
$sMessage = 'Ok';
}
$oLog->SetTrim('message', $sMessage);
@@ -303,7 +265,7 @@ if (MetaModel::GetConfig()->Get('log_rest_service'))
$iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
$sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
$sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
!StringFitsInLogField( $sJsonOuputWithPrettyPrinting) ?
!StringFitsInLogField($sJsonOuputWithPrettyPrinting) ?
$oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
$oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
@@ -316,4 +278,4 @@ if (MetaModel::GetConfig()->Get('log_rest_service'))
function StringFitsInLogField(string $sLog): bool
{
return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -27,59 +28,46 @@ require_once(APPROOT.'/application/startup.inc.php');
// this file is generated dynamically with location = here
$sWsdlUri = utils::GetAbsoluteUrlAppRoot().'webservices/itop.wsdl.php';
$sServiceCategory = utils::ReadParam('service_category');
if (!empty($sServiceCategory))
{
if (!empty($sServiceCategory)) {
$sWsdlUri .= "?service_category=".$sServiceCategory;
}
ini_set("soap.wsdl_cache_enabled","0");
ini_set("soap.wsdl_cache_enabled", "0");
$aSOAPMapping = SOAPMapping::GetMapping();
$oSoapServer = new SoapServer
(
$oSoapServer = new SoapServer(
$sWsdlUri,
array(
'classmap' => $aSOAPMapping
)
[
'classmap' => $aSOAPMapping,
]
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
if (!empty($sServiceCategory))
{
if (!empty($sServiceCategory)) {
$sServiceClass = $sServiceCategory;
if (!class_exists($sServiceClass))
{
if (!class_exists($sServiceClass)) {
// not a valid class name (not a PHP class at all)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
}
elseif (!is_subclass_of($sServiceClass, 'WebServicesBase'))
{
} elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) {
// not a valid class name (not deriving from WebServicesBase)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
}
else
{
} else {
$oSoapServer->setClass($sServiceClass, null);
}
}
else
{
} else {
$oSoapServer->setClass('BasicServices', null);
}
if ($_SERVER["REQUEST_METHOD"] == "POST")
{
if ($_SERVER["REQUEST_METHOD"] == "POST") {
CMDBObject::SetTrackOrigin('webservice-soap');
$oSoapServer->handle();
}
else
{
} else {
echo "This SOAP server can handle the following functions: ";
$aFunctions = $oSoapServer->getFunctions();
echo "<ul>\n";
foreach($aFunctions as $sFunc)
{
if ($sFunc == 'GetWSDLContents') continue;
foreach ($aFunctions as $sFunc) {
if ($sFunc == 'GetWSDLContents') {
continue;
}
echo "<li>$sFunc</li>\n";
}
@@ -88,10 +76,8 @@ else
echo "You may also want to try the following service categories: ";
echo "<ul>\n";
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'WebServicesBase'))
{
foreach (get_declared_classes() as $sPHPClass) {
if (is_subclass_of($sPHPClass, 'WebServicesBase')) {
$sServiceCategory = $sPHPClass;
$sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
$sSoapServerUri .= "?service_category=$sServiceCategory";
@@ -100,4 +86,3 @@ else
}
echo "</ul>\n";
}
?>

View File

@@ -6,21 +6,18 @@ require_once(__DIR__.'/../approot.inc.php');
use Combodo\iTop\Application\Status\Status;
//Do check Status
try
{
new Status();
$aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
}
catch (Exception $e)
{
$iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
$aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
http_response_code(500);
try {
new Status();
$aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
} catch (Exception $e) {
$iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
$aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
http_response_code(500);
}
//Set headers, based on webservices/rest.php
$sContentType = 'application/json';
header('Content-type: ' . $sContentType);
header('Content-type: '.$sContentType);
header('Access-Control-Allow-Origin: *');
//Output result

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Implementation of iTop SOAP services
*
@@ -26,10 +26,9 @@
require_once(APPROOT.'/webservices/webservices.class.inc.php');
class BasicServices extends WebServicesBase
{
static protected function GetWSDLFilePath()
protected static function GetWSDLFilePath()
{
return APPROOT.'/webservices/itop.wsdl.tpl';
}
@@ -39,14 +38,11 @@ class BasicServices extends WebServicesBase
*
* @return string WebServiceResult
*/
static public function GetVersion()
public static function GetVersion()
{
if (ITOP_REVISION == 'svn')
{
if (ITOP_REVISION == 'svn') {
$sVersionString = ITOP_VERSION.' [dev]';
}
else
{
} else {
// This is a build made from SVN, let display the full information
$sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
}
@@ -56,8 +52,7 @@ class BasicServices extends WebServicesBase
public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -71,15 +66,15 @@ class BasicServices extends WebServicesBase
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
$aImpactedCIs = array();
if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
foreach($aSOAPImpactedCIs as $oImpactedCIs)
{
$aImpactedCIs = [];
if (is_null($aSOAPImpactedCIs)) {
$aSOAPImpactedCIs = [];
}
foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
$oRes = $this->_CreateResponseTicket
(
$oRes = $this->_CreateResponseTicket(
'UserRequest',
$sTitle,
$sDescription,
@@ -98,8 +93,7 @@ class BasicServices extends WebServicesBase
public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -107,29 +101,27 @@ class BasicServices extends WebServicesBase
}
UserRights::Login($sLogin);
if (!class_exists('Incident'))
{
if (!class_exists('Incident')) {
$oRes = new WebServiceResult();
$oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
return $oRes->ToSoapStructure();
}
$aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
$aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
$aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
$aImpactedCIs = array();
if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
foreach($aSOAPImpactedCIs as $oImpactedCIs)
{
$aImpactedCIs = [];
if (is_null($aSOAPImpactedCIs)) {
$aSOAPImpactedCIs = [];
}
foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
$oRes = $this->_CreateResponseTicket
(
$oRes = $this->_CreateResponseTicket(
'Incident',
$sTitle,
$sDescription,
@@ -145,11 +137,11 @@ class BasicServices extends WebServicesBase
);
return $oRes->ToSoapStructure();
}
/**
* Create an ResponseTicket (Incident or UserRequest) from an external system
* Some CIs might be specified (by their name/IP)
*
*
* @param string sClass The class of the ticket: Incident or UserRequest
* @param string sTitle
* @param string sDescription
@@ -170,8 +162,7 @@ class BasicServices extends WebServicesBase
$oRes = new WebServiceResult();
try
{
try {
CMDBObject::SetTrackInfo('Administrator');
$oNewTicket = MetaModel::NewObject($sClass);
@@ -180,48 +171,36 @@ class BasicServices extends WebServicesBase
$this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
if (!array_key_exists('service_id', $aServiceSubcategoryDesc))
{
if (!array_key_exists('service_id', $aServiceSubcategoryDesc)) {
$aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
}
$this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
if (MetaModel::IsValidAttCode($sClass, 'product'))
{
if (MetaModel::IsValidAttCode($sClass, 'product')) {
// 1.x data models
$this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
}
if (MetaModel::IsValidAttCode($sClass, 'workgroup_id'))
{
if (MetaModel::IsValidAttCode($sClass, 'workgroup_id')) {
// 1.x data models
$this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
}
else if (MetaModel::IsValidAttCode($sClass, 'team_id'))
{
} elseif (MetaModel::IsValidAttCode($sClass, 'team_id')) {
// 2.x data models
$this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
}
if (MetaModel::IsValidAttCode($sClass, 'ci_list'))
{
if (MetaModel::IsValidAttCode($sClass, 'ci_list')) {
// 1.x data models
$aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
}
else if (MetaModel::IsValidAttCode($sClass, 'functionalcis_list'))
{
} elseif (MetaModel::IsValidAttCode($sClass, 'functionalcis_list')) {
// 2.x data models
$aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
}
if (count($aDevicesNotFound) > 0)
{
if (count($aDevicesNotFound) > 0) {
$this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
}
else
{
} else {
$this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
}
@@ -229,13 +208,9 @@ class BasicServices extends WebServicesBase
$this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
$this->MyObjectInsert($oNewTicket, 'created', $oRes);
}
catch (CoreException $e)
{
} catch (CoreException $e) {
$oRes->LogError($e->getMessage());
}
catch (Exception $e)
{
} catch (Exception $e) {
$oRes->LogError($e->getMessage());
}
@@ -245,13 +220,12 @@ class BasicServices extends WebServicesBase
/**
* Given an OQL, returns a set of objects (several objects could be on the same row)
*
*
* @param string sOQL
*/
*/
public function SearchObjects($sLogin, $sPassword, $sOQL)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -266,22 +240,16 @@ class BasicServices extends WebServicesBase
protected function _SearchObjects($sOQL)
{
$oRes = new WebServiceResult();
try
{
try {
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch);
$aData = $oSet->ToArrayOfValues();
foreach($aData as $iRow => $aRow)
{
foreach ($aData as $iRow => $aRow) {
$oRes->AddResultRow("row_$iRow", $aRow);
}
}
catch (CoreException $e)
{
} catch (CoreException $e) {
$oRes->LogError($e->getMessage());
}
catch (Exception $e)
{
} catch (Exception $e) {
$oRes->LogError($e->getMessage());
}
@@ -289,4 +257,3 @@ class BasicServices extends WebServicesBase
return $oRes;
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Implementation of iTop SOAP services
*
@@ -24,7 +24,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/webservices/itopsoaptypes.class.inc.php');
/**
@@ -34,7 +33,6 @@ require_once(APPROOT.'/webservices/itopsoaptypes.class.inc.php');
*/
class WebServiceResult
{
/**
* Overall status
*
@@ -71,37 +69,32 @@ class WebServiceResult
public function __construct()
{
$this->m_bStatus = true;
$this->m_aResult = array();
$this->m_aErrors = array();
$this->m_aWarnings = array();
$this->m_aInfos = array();
$this->m_aResult = [];
$this->m_aErrors = [];
$this->m_aWarnings = [];
$this->m_aInfos = [];
}
public function ToSoapStructure()
{
$aResults = array();
foreach($this->m_aResult as $sLabel => $aData)
{
$aValues = array();
foreach($aData as $sKey => $value)
{
$aResults = [];
foreach ($this->m_aResult as $sLabel => $aData) {
$aValues = [];
foreach ($aData as $sKey => $value) {
$aValues[] = new SOAPKeyValue($sKey, $value);
}
$aResults[] = new SoapResultMessage($sLabel, $aValues);
}
$aInfos = array();
foreach($this->m_aInfos as $sMessage)
{
$aInfos = [];
foreach ($this->m_aInfos as $sMessage) {
$aInfos[] = new SoapLogMessage($sMessage);
}
$aWarnings = array();
foreach($this->m_aWarnings as $sMessage)
{
$aWarnings = [];
foreach ($this->m_aWarnings as $sMessage) {
$aWarnings[] = new SoapLogMessage($sMessage);
}
$aErrors = array();
foreach($this->m_aErrors as $sMessage)
{
$aErrors = [];
foreach ($this->m_aErrors as $sMessage) {
$aErrors[] = new SoapLogMessage($sMessage);
}
@@ -135,11 +128,11 @@ class WebServiceResult
public function AddResultObject($sLabel, $oObject)
{
$oAppContext = new ApplicationContext();
$this->m_aResult[$sLabel] = array(
$this->m_aResult[$sLabel] = [
'id' => $oObject->GetKey(),
'name' => $oObject->GetRawName(),
'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
);
];
}
/**
@@ -183,8 +176,11 @@ class WebServiceResult
*/
public function LogIssue($sDescription, $bIsStopper = true)
{
if ($bIsStopper) $this->LogError($sDescription);
else $this->LogWarning($sDescription);
if ($bIsStopper) {
$this->LogError($sDescription);
} else {
$this->LogWarning($sDescription);
}
}
/**
@@ -220,8 +216,7 @@ class WebServiceResult
public function GetReturnedDataAsText()
{
$sRet = '';
foreach ($this->m_aResult as $sKey => $value)
{
foreach ($this->m_aResult as $sKey => $value) {
$sRet .= "===== $sKey =====\n";
$sRet .= print_r($value, true);
}
@@ -229,7 +224,6 @@ class WebServiceResult
}
}
/**
* Generic response of iTop SOAP services - failed login
*
@@ -251,13 +245,12 @@ class WebServiceResultFailedLogin extends WebServiceResult
*/
abstract class WebServicesBase
{
static public function GetWSDLContents($sServiceCategory = '')
public static function GetWSDLContents($sServiceCategory = '')
{
if ($sServiceCategory == '')
{
if ($sServiceCategory == '') {
$sServiceCategory = 'BasicServices';
}
$sWsdlFilePath = call_user_func(array($sServiceCategory, 'GetWSDLFilePath'));
$sWsdlFilePath = call_user_func([$sServiceCategory, 'GetWSDLFilePath']);
return file_get_contents($sWsdlFilePath);
}
@@ -271,15 +264,14 @@ abstract class WebServicesBase
*/
protected function LogUsage($sVerb, $oRes)
{
if (!MetaModel::IsLogEnabledWebService()) return;
if (!MetaModel::IsLogEnabledWebService()) {
return;
}
$oLog = new EventWebService();
if ($oRes->IsOk())
{
if ($oRes->IsOk()) {
$oLog->Set('message', $sVerb.' was successfully invoked');
}
else
{
} else {
$oLog->Set('message', $sVerb.' returned errors');
}
$oLog->Set('userinfo', UserRights::GetUser());
@@ -291,12 +283,11 @@ abstract class WebServicesBase
$this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
$oLog->DBInsertNoReload();
}
protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
if (is_object($oAttDef))
{
if (is_object($oAttDef)) {
$iMaxSize = $oAttDef->GetMaxSize();
if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
$sValue = mb_substr($sValue, 0, $iMaxSize);
@@ -317,12 +308,9 @@ abstract class WebServicesBase
protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
{
$res = $oTargetObj->CheckValue($sAttCode, $value);
if ($res === true)
{
if ($res === true) {
$oTargetObj->Set($sAttCode, $value);
}
else
{
} else {
// $res contains the error description
$oRes->LogError("Unexpected value for parameter $sParamName: $res");
}
@@ -343,31 +331,24 @@ abstract class WebServicesBase
$bIsMandatory = !$oExtKey->IsNullAllowed();
if (is_null($aExtKeyDesc))
{
if ($bIsMandatory)
{
if (is_null($aExtKeyDesc)) {
if ($bIsMandatory) {
$oRes->LogError("Parameter $sParamName: found null for a mandatory key");
}
else
{
} else {
// skip silently
return;
}
}
if (count($aExtKeyDesc) == 0)
{
if (count($aExtKeyDesc) == 0) {
$oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
return;
}
$sKeyClass = $oExtKey->GetTargetClass();
$oReconFilter = new DBObjectSearch($sKeyClass);
foreach ($aExtKeyDesc as $sForeignAttCode => $value)
{
if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode))
{
foreach ($aExtKeyDesc as $sForeignAttCode => $value) {
if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) {
$aCodes = MetaModel::GetFiltersList($sKeyClass);
$sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
$oRes->LogIssue($sMsg, $bIsMandatory);
@@ -376,26 +357,24 @@ abstract class WebServicesBase
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
switch($oExtObjects->Count())
{
case 0:
$sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
$oRes->LogIssue($sMsg, $bIsMandatory);
break;
case 1:
// Do change the external key attribute
$oForeignObj = $oExtObjects->Fetch();
$oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
switch ($oExtObjects->Count()) {
case 0:
$sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
$oRes->LogIssue($sMsg, $bIsMandatory);
break;
case 1:
// Do change the external key attribute
$oForeignObj = $oExtObjects->Fetch();
$oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
// Report it (no need to report if the object already had this value
if (array_key_exists($sAttCode, $oTargetObj->ListChanges()))
{
$oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
}
break;
default:
$sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
$oRes->LogIssue($sMsg, $bIsMandatory);
// Report it (no need to report if the object already had this value
if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) {
$oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
}
break;
default:
$sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
$oRes->LogIssue($sMsg, $bIsMandatory);
}
}
@@ -416,34 +395,29 @@ abstract class WebServicesBase
$sLinkClass = $oLinkAtt->GetLinkedClass();
$sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
$aItemsFound = array();
$aItemsNotFound = array();
if (is_null($aLinkList))
{
$aItemsFound = [];
$aItemsNotFound = [];
if (is_null($aLinkList)) {
return $aItemsNotFound;
}
foreach ($aLinkList as $aItemData)
{
if (!array_key_exists('class', $aItemData))
{
foreach ($aLinkList as $aItemData) {
if (!array_key_exists('class', $aItemData)) {
$oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
continue; // skip
}
$sTargetClass = $aItemData['class'];
if (!MetaModel::IsValidClass($sTargetClass))
{
if (!MetaModel::IsValidClass($sTargetClass)) {
$oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
continue; // skip
}
if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass))
{
if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
$oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
continue; // skip
}
$oReconFilter = new DBObjectSearch($sTargetClass);
$aCIStringDesc = array();
$aCIStringDesc = [];
foreach ($aItemData['search'] as $sAttCode => $value) {
if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
$aCodes = MetaModel::GetFiltersList($sTargetClass);
@@ -455,52 +429,42 @@ abstract class WebServicesBase
// The attribute is one of our reconciliation key
$oReconFilter->AddCondition($sAttCode, $value, '=');
}
if (count($aCIStringDesc) == 1)
{
if (count($aCIStringDesc) == 1) {
// take the last and unique value to describe the object
$sItemDesc = $value;
}
else
{
} else {
// describe the object by the given keys
$sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
switch($oExtObjects->Count())
{
case 0:
$oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
$aItemsNotFound[] = $sItemDesc;
break;
case 1:
$aItemsFound[] = array (
'object' => $oExtObjects->Fetch(),
'link_values' => @$aItemData['link_values'],
'desc' => $sItemDesc,
);
break;
default:
$oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
$aItemsNotFound[] = $sItemDesc;
switch ($oExtObjects->Count()) {
case 0:
$oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
$aItemsNotFound[] = $sItemDesc;
break;
case 1:
$aItemsFound[] = [
'object' => $oExtObjects->Fetch(),
'link_values' => @$aItemData['link_values'],
'desc' => $sItemDesc,
];
break;
default:
$oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
$aItemsNotFound[] = $sItemDesc;
}
}
if (count($aItemsFound) > 0)
{
$aLinks = array();
foreach($aItemsFound as $aItemData)
{
if (count($aItemsFound) > 0) {
$aLinks = [];
foreach ($aItemsFound as $aItemData) {
$oLink = MetaModel::NewObject($sLinkClass);
$oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
foreach($aItemData['link_values'] as $sKey => $value)
{
if(!MetaModel::IsValidAttCode($sLinkClass, $sKey))
{
foreach ($aItemData['link_values'] as $sKey => $value) {
if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
$oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
}
else
{
} else {
$oLink->Set($sKey, $value);
}
}
@@ -529,77 +493,71 @@ abstract class WebServicesBase
*/
protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
{
if ($oRes->IsOk())
{
if ($oRes->IsOk()) {
list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
if ($bRes)
{
if ($bRes) {
$iId = $oTargetObj->DBInsertNoReload();
$oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
$oRes->AddResultObject($sResultLabel, $oTargetObj);
}
else
{
} else {
$oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
foreach($aIssues as $iIssue => $sIssue)
{
foreach ($aIssues as $iIssue => $sIssue) {
$oRes->LogError("Issue #$iIssue: $sIssue");
}
}
}
}
static protected function SoapStructToExternalKeySearch($oExternalKeySearch)
protected static function SoapStructToExternalKeySearch($oExternalKeySearch)
{
if (is_null($oExternalKeySearch)) return null;
if ($oExternalKeySearch->IsVoid()) return null;
if (is_null($oExternalKeySearch)) {
return null;
}
if ($oExternalKeySearch->IsVoid()) {
return null;
}
$aRes = array();
foreach($oExternalKeySearch->conditions as $oSearchCondition)
{
$aRes = [];
foreach ($oExternalKeySearch->conditions as $oSearchCondition) {
$aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
}
return $aRes;
}
static protected function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
protected static function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
{
$aRes = array
(
$aRes =
[
'class' => $oLinkCreationSpec->class,
'search' => array(),
'link_values' => array(),
);
'search' => [],
'link_values' => [],
];
foreach($oLinkCreationSpec->conditions as $oSearchCondition)
{
foreach ($oLinkCreationSpec->conditions as $oSearchCondition) {
$aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
}
foreach($oLinkCreationSpec->attributes as $oAttributeValue)
{
foreach ($oLinkCreationSpec->attributes as $oAttributeValue) {
$aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
}
return $aRes;
}
static protected function SoapStructToAssociativeArray($aArrayOfAssocArray)
protected static function SoapStructToAssociativeArray($aArrayOfAssocArray)
{
if (is_null($aArrayOfAssocArray)) return array();
if (is_null($aArrayOfAssocArray)) {
return [];
}
$aRes = array();
foreach($aArrayOfAssocArray as $aAssocArray)
{
$aRow = array();
foreach ($aAssocArray as $oKeyValuePair)
{
$aRes = [];
foreach ($aArrayOfAssocArray as $aAssocArray) {
$aRow = [];
foreach ($aAssocArray as $oKeyValuePair) {
$aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
}
$aRes[] = $aRow;
$aRes[] = $aRow;
}
return $aRes;
}
}
?>