You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
597 lines
16 KiB
597 lines
16 KiB
<?php
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Class containing methods for operations with images.
|
|
*/
|
|
class CImage extends CApiService {
|
|
|
|
public const ACCESS_RULES = [
|
|
'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
|
|
'create' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'update' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'delete' => ['min_user_type' => USER_TYPE_SUPER_ADMIN]
|
|
];
|
|
|
|
protected $tableName = 'images';
|
|
protected $tableAlias = 'i';
|
|
protected $sortColumns = ['imageid', 'name'];
|
|
|
|
/**
|
|
* Get images data
|
|
*
|
|
* @param array $options
|
|
* @param array $options['imageids']
|
|
* @param array $options['sysmapids']
|
|
* @param array $options['filter']
|
|
* @param array $options['search']
|
|
* @param bool $options['searchByAny']
|
|
* @param bool $options['startSearch']
|
|
* @param bool $options['excludeSearch']
|
|
* @param bool $options['searchWildcardsEnabled']
|
|
* @param array $options['output']
|
|
* @param int $options['select_image']
|
|
* @param bool $options['editable']
|
|
* @param bool $options['countOutput']
|
|
* @param bool $options['preservekeys']
|
|
* @param string $options['sortfield']
|
|
* @param string $options['sortorder']
|
|
* @param int $options['limit']
|
|
*
|
|
* @return array|boolean image data as array or false if error
|
|
*/
|
|
public function get($options = []) {
|
|
$result = [];
|
|
|
|
$sqlParts = [
|
|
'select' => ['images' => 'i.imageid'],
|
|
'from' => ['images' => 'images i'],
|
|
'where' => [],
|
|
'order' => [],
|
|
'limit' => null
|
|
];
|
|
|
|
$defOptions = [
|
|
'imageids' => null,
|
|
'sysmapids' => null,
|
|
// filter
|
|
'filter' => null,
|
|
'search' => null,
|
|
'searchByAny' => null,
|
|
'startSearch' => false,
|
|
'excludeSearch' => false,
|
|
'searchWildcardsEnabled' => null,
|
|
// output
|
|
'output' => API_OUTPUT_EXTEND,
|
|
'select_image' => null,
|
|
'editable' => false,
|
|
'countOutput' => false,
|
|
'preservekeys' => false,
|
|
'sortfield' => '',
|
|
'sortorder' => '',
|
|
'limit' => null
|
|
];
|
|
$options = zbx_array_merge($defOptions, $options);
|
|
|
|
// editable + PERMISSION CHECK
|
|
if ($options['editable'] && self::$userData['type'] < USER_TYPE_ZABBIX_ADMIN) {
|
|
return [];
|
|
}
|
|
|
|
// imageids
|
|
if (!is_null($options['imageids'])) {
|
|
zbx_value2array($options['imageids']);
|
|
$sqlParts['where']['imageid'] = dbConditionInt('i.imageid', $options['imageids']);
|
|
}
|
|
|
|
// sysmapids
|
|
if (!is_null($options['sysmapids'])) {
|
|
zbx_value2array($options['sysmapids']);
|
|
|
|
$sqlParts['from']['sysmaps'] = 'sysmaps sm';
|
|
$sqlParts['from']['sysmaps_elements'] = 'sysmaps_elements se';
|
|
$sqlParts['where']['sm'] = dbConditionInt('sm.sysmapid', $options['sysmapids']);
|
|
$sqlParts['where']['smse_or_bg'] = '('.
|
|
'sm.backgroundid=i.imageid'.
|
|
' OR ('.
|
|
'sm.sysmapid=se.sysmapid'.
|
|
' AND ('.
|
|
'se.iconid_off=i.imageid'.
|
|
' OR se.iconid_on=i.imageid'.
|
|
' OR se.iconid_disabled=i.imageid'.
|
|
' OR se.iconid_maintenance=i.imageid'.
|
|
')'.
|
|
')'.
|
|
')';
|
|
}
|
|
|
|
// filter
|
|
if (is_array($options['filter'])) {
|
|
$this->dbFilter('images i', $options, $sqlParts);
|
|
}
|
|
|
|
// search
|
|
if (is_array($options['search'])) {
|
|
zbx_db_search('images i', $options, $sqlParts);
|
|
}
|
|
|
|
// limit
|
|
if (zbx_ctype_digit($options['limit']) && $options['limit']) {
|
|
$sqlParts['limit'] = $options['limit'];
|
|
}
|
|
|
|
$imageids = [];
|
|
$sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
|
|
$sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
|
|
$res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']);
|
|
while ($image = DBfetch($res)) {
|
|
if ($options['countOutput']) {
|
|
return $image['rowscount'];
|
|
}
|
|
else {
|
|
$imageids[$image['imageid']] = $image['imageid'];
|
|
|
|
$result[$image['imageid']] = $image;
|
|
}
|
|
}
|
|
|
|
// adding objects
|
|
if (!is_null($options['select_image'])) {
|
|
$dbImg = DBselect('SELECT i.imageid,i.image FROM images i WHERE '.dbConditionInt('i.imageid', $imageids));
|
|
while ($img = DBfetch($dbImg)) {
|
|
$result[$img['imageid']]['image'] = base64_encode($img['image']);
|
|
}
|
|
}
|
|
|
|
if (!$options['preservekeys']) {
|
|
$result = zbx_cleanHashes($result);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Add images.
|
|
*
|
|
* @param array $images ['name' => string, 'image' => string, 'imagetype' => int]
|
|
*
|
|
* @return array
|
|
*/
|
|
public function create($images) {
|
|
global $DB;
|
|
|
|
self::validateCreate($images);
|
|
|
|
if ($DB['TYPE'] === ZBX_DB_ORACLE) {
|
|
$upd_images_data = [];
|
|
|
|
foreach ($images as $index => &$image) {
|
|
$upd_images_data[$index]['image'] = $image['image'];
|
|
unset($image['image']);
|
|
}
|
|
unset($image);
|
|
}
|
|
|
|
$imageids = DB::insert('images', $images);
|
|
|
|
foreach ($images as $index => &$image) {
|
|
if ($DB['TYPE'] === ZBX_DB_ORACLE) {
|
|
$upd_images_data[$index]['imageid'] = $imageids[$index];
|
|
$image['image'] = $upd_images_data[$index]['image'];
|
|
}
|
|
$image['imageid'] = $imageids[$index];
|
|
}
|
|
unset($image);
|
|
|
|
if ($DB['TYPE'] === ZBX_DB_ORACLE) {
|
|
self::updateOracleImagesData($upd_images_data);
|
|
}
|
|
|
|
self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_IMAGE, $images);
|
|
|
|
return ['imageids' => array_column($images, 'imageid')];
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $images
|
|
*
|
|
* @throws APIException if the input is invalid
|
|
*/
|
|
private static function validateCreate(array &$images): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
|
|
'imagetype' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => IMAGE_TYPE_ICON.','.IMAGE_TYPE_BACKGROUND],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('images', 'name')],
|
|
'image' => ['type' => API_IMAGE, 'flags' => API_REQUIRED | API_NOT_EMPTY]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $images, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
self::checkDuplicates($images);
|
|
self::prepareImages($images);
|
|
}
|
|
|
|
/**
|
|
* Update images.
|
|
*
|
|
* @param array $images
|
|
*
|
|
* @return array (updated images)
|
|
*/
|
|
public function update($images) {
|
|
global $DB;
|
|
|
|
self::validateUpdate($images, $db_images);
|
|
|
|
if ($DB['TYPE'] === ZBX_DB_ORACLE) {
|
|
$upd_images_data = [];
|
|
|
|
foreach ($images as $index => &$image) {
|
|
if (array_key_exists('image', $image)) {
|
|
$upd_images_data[$index] = array_intersect_key($image, array_flip(['imageid', 'image']));
|
|
unset($image['image']);
|
|
}
|
|
}
|
|
unset($image);
|
|
}
|
|
|
|
$upd_images = [];
|
|
|
|
foreach ($images as $image) {
|
|
$upd_image = DB::getUpdatedValues('images', $image, $db_images[$image['imageid']]);
|
|
|
|
if ($upd_image) {
|
|
$upd_images[] = [
|
|
'values' => $upd_image,
|
|
'where' => ['imageid' => $image['imageid']]
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($upd_images) {
|
|
DB::update('images', $upd_images);
|
|
}
|
|
|
|
if ($DB['TYPE'] === ZBX_DB_ORACLE) {
|
|
foreach ($images as $index => &$image) {
|
|
if (array_key_exists($index, $upd_images_data)) {
|
|
$image['image'] = $upd_images_data[$index]['image'];
|
|
}
|
|
}
|
|
unset($image);
|
|
|
|
if ($upd_images_data) {
|
|
self::updateOracleImagesData($upd_images_data, $db_images);
|
|
}
|
|
}
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_IMAGE, $images, $db_images);
|
|
|
|
return ['imageids' => array_column($images, 'imageid')];
|
|
}
|
|
|
|
/**
|
|
* Saving image data to ORACLE database.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $images
|
|
* @param string $images[]['image']
|
|
* @param array|null $db_images
|
|
*/
|
|
private static function updateOracleImagesData(array $images, array $db_images = null): void {
|
|
global $DB;
|
|
|
|
foreach ($images as $image) {
|
|
if ($db_images !== null && $image['image'] === $db_images[$image['imageid']]['image']) {
|
|
continue;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['image'],
|
|
'imageids' => $image['imageid']
|
|
];
|
|
|
|
if (!$stmt = oci_parse($DB['DB'], DB::makeSql('images', $options).' FOR UPDATE')) {
|
|
$e = oci_error($DB['DB']);
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, 'SQL error ['.$e['message'].'] in ['.$e['sqltext'].']');
|
|
}
|
|
|
|
if (!oci_execute($stmt, OCI_DEFAULT)) {
|
|
$e = oci_error($stmt);
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, 'SQL error ['.$e['message'].'] in ['.$e['sqltext'].']');
|
|
}
|
|
|
|
if (false === ($row = oci_fetch_assoc($stmt))) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, 'DBerror');
|
|
}
|
|
|
|
$row['IMAGE']->truncate();
|
|
$row['IMAGE']->save($image['image']);
|
|
$row['IMAGE']->free();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $images
|
|
* @param array|null $db_images
|
|
*
|
|
* @throws APIException if the input is invalid
|
|
*/
|
|
private static function validateUpdate(array &$images, array &$db_images = null): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['imageid'], ['name']], 'fields' => [
|
|
'imageid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('images', 'name')],
|
|
'image' => ['type' => API_IMAGE, 'flags' => API_NOT_EMPTY]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $images, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_images = DB::select('images', [
|
|
'output' => ['imageid', 'name', 'image'],
|
|
'imageids' => array_column($images, 'imageid'),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_images) != count($images)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
self::checkDuplicates($images, $db_images);
|
|
self::prepareImages($images);
|
|
}
|
|
|
|
/**
|
|
* Delete images.
|
|
*
|
|
* @param array $imageids
|
|
*
|
|
* @return array
|
|
*/
|
|
public function delete(array $imageids) {
|
|
self::validateDelete($imageids, $db_images);
|
|
|
|
DB::update('sysmaps_elements', ['values' => ['iconid_off' => 0], 'where' => ['iconid_off' => $imageids]]);
|
|
DB::update('sysmaps_elements', ['values' => ['iconid_on' => 0], 'where' => ['iconid_on' => $imageids]]);
|
|
DB::update('sysmaps_elements', ['values' => ['iconid_disabled' => 0], 'where' => ['iconid_disabled' => $imageids]]);
|
|
DB::update('sysmaps_elements', ['values' => ['iconid_maintenance' => 0], 'where' => ['iconid_maintenance' => $imageids]]);
|
|
|
|
DB::delete('images', ['imageid' => $imageids]);
|
|
|
|
self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_IMAGE, $db_images);
|
|
|
|
return ['imageids' => $imageids];
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
*
|
|
* @param array $imageids
|
|
* @param array|null $db_images
|
|
*
|
|
* @throws APIException if the input is invalid
|
|
*/
|
|
private static function validateDelete(array &$imageids, array &$db_images = null): void {
|
|
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $imageids, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_images = DB::select('images', [
|
|
'output' => ['imageid', 'name'],
|
|
'imageids' => $imageids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_images) != count($imageids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
self::checkUsedIconMaps($imageids);
|
|
self::checkUsedSysMaps($imageids);
|
|
}
|
|
|
|
/**
|
|
* Unset "image" field from the output.
|
|
*
|
|
* @param string $table_name
|
|
* @param string $table_alias
|
|
* @param array $options
|
|
* @param array $sql_parts
|
|
*
|
|
* @return array The resulting SQL parts array.
|
|
*/
|
|
protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts) {
|
|
if (!$options['countOutput']) {
|
|
if ($options['output'] == API_OUTPUT_EXTEND) {
|
|
$options['output'] = ['imageid', 'imagetype', 'name'];
|
|
}
|
|
elseif (is_array($options['output']) && in_array('image', $options['output'])) {
|
|
foreach ($options['output'] as $idx => $field) {
|
|
if ($field === 'image') {
|
|
unset($options['output'][$idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
|
|
}
|
|
|
|
/**
|
|
* Convert image body to PNG.
|
|
*
|
|
* @static
|
|
*
|
|
* @param string $image Base64 encoded body of image.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected static function convertToPng($image): string {
|
|
$image = imagecreatefromstring($image);
|
|
|
|
ob_start();
|
|
imagealphablending($image, false);
|
|
imagesavealpha($image, true);
|
|
imagepng($image);
|
|
imagedestroy($image);
|
|
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Check for unique image names.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $images
|
|
* @param array|null $db_images
|
|
*
|
|
* @throws APIException if image names are not unique.
|
|
*/
|
|
private static function checkDuplicates(array $images, array $db_images = null): void {
|
|
$names = [];
|
|
|
|
foreach ($images as $image) {
|
|
if (!array_key_exists('name', $image)) {
|
|
continue;
|
|
}
|
|
|
|
if ($db_images === null || $image['name'] !== $db_images[$image['imageid']]['name']) {
|
|
$names[] = $image['name'];
|
|
}
|
|
}
|
|
|
|
if (!$names) {
|
|
return;
|
|
}
|
|
|
|
$duplicates = DB::select('images', [
|
|
'output' => ['name'],
|
|
'filter' => ['name' => $names],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($duplicates) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Image "%1$s" already exists.', $duplicates[0]['name']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Preparing images before saving to the DB.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $images
|
|
* @param string $images[]['image'] (optional)
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function prepareImages(array &$images): void {
|
|
foreach ($images as &$image) {
|
|
if (!array_key_exists('image', $image)) {
|
|
continue;
|
|
}
|
|
|
|
list(,, $img_type) = getimagesizefromstring($image['image']);
|
|
|
|
// Converting to PNG all images except PNG, JPEG and GIF
|
|
if (!in_array($img_type, [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG])) {
|
|
$image['image'] = self::convertToPng($image['image']);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate image used in icon mapping.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $imageids
|
|
*
|
|
* @throws APIException if image used in icon mapping.
|
|
*/
|
|
private static function checkUsedIconMaps(array $imageids): void {
|
|
$used = [];
|
|
|
|
$db_iconmaps = DBselect(
|
|
'SELECT DISTINCT im.name'.
|
|
' FROM icon_map im,icon_mapping imp'.
|
|
' WHERE im.iconmapid=imp.iconmapid'.
|
|
' AND ('.dbConditionInt('im.default_iconid', $imageids).
|
|
' OR '.dbConditionInt('imp.iconid', $imageids).')'
|
|
);
|
|
|
|
while ($db_iconmap = DBfetch($db_iconmaps)) {
|
|
$used[] = $db_iconmap['name'];
|
|
}
|
|
|
|
if ($used) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _n('The image is used in icon map %1$s.',
|
|
'The image is used in icon maps %1$s.', '"'.implode('", "', $used).'"', count($used)
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate image used in maps.
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $imageids
|
|
*
|
|
* @throws APIException if image used in map.
|
|
*/
|
|
private static function checkUsedSysMaps(array $imageids): void {
|
|
$used = [];
|
|
|
|
$db_sysmaps = DBselect(
|
|
'SELECT DISTINCT sm.sysmapid,sm.name'.
|
|
' FROM sysmaps_elements se,sysmaps sm'.
|
|
' WHERE sm.sysmapid=se.sysmapid'.
|
|
' AND (sm.iconmapid IS NULL'.
|
|
' OR se.use_iconmap='.SYSMAP_ELEMENT_USE_ICONMAP_OFF.')'.
|
|
' AND ('.dbConditionInt('se.iconid_off', $imageids).
|
|
' OR '.dbConditionInt('se.iconid_on', $imageids).
|
|
' OR '.dbConditionInt('se.iconid_disabled', $imageids).
|
|
' OR '.dbConditionInt('se.iconid_maintenance', $imageids).')'.
|
|
' OR '.dbConditionInt('sm.backgroundid', $imageids)
|
|
);
|
|
|
|
while ($db_sysmap = DBfetch($db_sysmaps)) {
|
|
$used[] = $db_sysmap['name'];
|
|
}
|
|
|
|
if ($used) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _n('The image is used in map %1$s.',
|
|
'The image is used in maps %1$s.', '"'.implode('", "', $used).'"', count($used)
|
|
));
|
|
}
|
|
}
|
|
}
|